Skip to content

Commit 573ff38

Browse files
authored
Merge pull request #337 from codesnippetspro/fix/beta-issues
2 parents 59db52d + 2e57771 commit 573ff38

51 files changed

Lines changed: 2911 additions & 282 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/instructions/code-review.instructions.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ Severity labels used in this file:
1515
- **MUST** — Flag as a blocking issue; must be resolved before merge.
1616
- **SHOULD** — Flag as a recommendation; resolve before merge unless a risk-aware rationale is provided.
1717

18+
## Practical Reviewer Checks
19+
20+
- Don't silently widen types or drop generics when refactoring. If typing gets weaker, require a clear reason. [MUST]
21+
- In namespaced PHP, call WordPress globals with `\function_name()` and do not assume pluggable/core functions exist on very early execution paths. If a callback can run during early bootstrap, guard availability appropriately. [MUST]
22+
- Ensure hook callbacks that respond to options/actions are tightly gated to the intended option/action. Be suspicious of inverted or overly broad conditionals that can be triggered by other plugins. [MUST]
23+
- Avoid no-op abstractions (pass-through helpers, one-liner wrappers) unless they materially improve readability, reuse, or testability. [SHOULD]
24+
- Inline single-use extractions that do not clarify intent (local functions/variables used once). [SHOULD]
25+
- Prefer `undefined` for absent optional values in TypeScript/React unless `null` has explicit semantics in that API. [SHOULD]
26+
- For conditional class names, prefer the repository `classnames.classnames` helper over manual array filtering and joining. [SHOULD]
27+
- Prefer JSX for React markup. If a hook/util needs to render elements, suggest moving the markup into a `.tsx` component instead of using `createElement` in a `.ts` file. [SHOULD]
28+
- For simple key-to-value parsing/transforms, prefer a literal object/record map over a loop + `switch` when it improves clarity. [SHOULD]
29+
- Do not stack redundant `catch` blocks (e.g., `ParseError` plus `Throwable`) unless the handlers differ. [SHOULD]
30+
- When a screen is React-driven, question heavy PHP view logic. If PHP is used due to WordPress admin primitives (e.g., Screen Options, non-REST file streaming), require a short rationale. [SHOULD]
31+
1832
## Scope and Diff Hygiene
1933

2034
- Flag PRs that mix behavior changes with refactors, renames, or formatting-only edits. Each change should be single-purpose. [MUST]

eslint.config.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export default eslintTs.config(
2626
rules: reactHooks.configs.recommended.rules,
2727
},
2828
{
29-
ignores: ['bundle/*', 'src/dist/*', 'src/vendor/*', 'svn/*', '*.config.mjs', '*.config.js', '.*/*', 'tmp/*']
29+
ignores: [
30+
'bundle/*', 'src/dist/*', 'src/vendor/*', 'svn/*',
31+
'*.config.mjs', '*.config.js',
32+
'.*/*', 'tmp/*', 'playwright-report/*'
33+
]
3034
},
3135
{
3236
languageOptions: {

src/css/common/_notices.scss

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.code-snippets-notice {
2+
.notice-dismiss {
3+
position: absolute;
4+
transform: initial;
5+
inset-inline-end: 0;
6+
inset-block-start: 0;
7+
}
8+
9+
details {
10+
margin: .5em 0;
11+
padding: 2px;
12+
13+
summary {
14+
cursor: pointer;
15+
}
16+
17+
pre {
18+
max-inline-size: 100%;
19+
overflow: auto hidden;
20+
white-space: pre;
21+
}
22+
23+
.stack-trace-hint {
24+
opacity: 0.5;
25+
}
26+
}
27+
}

src/css/edit.scss

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
@use 'common/select';
1010
@use 'common/tooltips';
1111
@use 'common/modal';
12+
@use 'common/notices';
1213
@use 'common/upsell';
1314
@use 'common/toolbar';
1415
@use 'edit/form';
@@ -28,6 +29,10 @@
2829
margin: 0;
2930
}
3031

32+
#adminmenu a.code-snippets-edit-menu-link {
33+
cursor: pointer;
34+
}
35+
3136
.snippet-description-container {
3237
.wp-editor-tools {
3338
padding-block-start: 5px;
@@ -96,13 +101,3 @@
96101
form.condition-snippet .snippet-code-container {
97102
display: none;
98103
}
99-
100-
.cs-back {
101-
cursor: pointer;
102-
103-
&::before {
104-
content: '<';
105-
color: #2271b1;
106-
margin-inline-end: 3px;
107-
}
108-
}

src/css/manage.scss

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@use 'common/tooltips';
55
@use 'common/direction';
66
@use 'common/select';
7+
@use 'common/notices';
78
@use 'common/upsell';
89
@use 'common/toolbar';
910
@use 'prism';
@@ -61,10 +62,6 @@
6162
}
6263
}
6364

64-
.code-snippets-notice a.notice-dismiss {
65-
text-decoration: none;
66-
}
67-
6865
.refresh-button-container {
6966
display: flex;
7067
align-items: center;

src/css/manage/_cloud-community.scss

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,61 @@
11
@use '../common/theme';
22
@use '../common/banners';
33

4+
.cloud-search-form {
5+
display: flex;
6+
gap: 8px;
7+
margin-block: 31px 47px;
8+
block-size: 54px;
9+
10+
> select {
11+
flex: 0 0 250px;
12+
}
13+
14+
.button {
15+
flex: 0 0 165px;
16+
}
17+
18+
.cloud-search-query {
19+
flex: 1;
20+
position: relative;
21+
22+
input {
23+
inline-size: 100%;
24+
block-size: 100%;
25+
}
26+
27+
> .components-spinner {
28+
position: absolute;
29+
inset-inline-end: 1.5em;
30+
inset-block-start: 25%;
31+
}
32+
}
33+
}
34+
435
.cloud-search {
536
@include banners.banners;
637

738
.banner {
839
justify-content: center;
940
}
41+
42+
.tablenav.top {
43+
display: flex;
44+
gap: 20px;
45+
block-size: 40px;
46+
margin-block-end: 31px;
47+
align-items: center;
48+
49+
select {
50+
inline-size: 245px;
51+
block-size: 100%;
52+
}
53+
54+
.tablenav-pages {
55+
margin: 0;
56+
margin-inline-start: auto;
57+
}
58+
}
1059
}
1160

1261
.cloud-search-results {
@@ -79,10 +128,6 @@
79128
padding-block: 12px;
80129
align-items: center;
81130

82-
.components-spinner {
83-
margin: 0;
84-
}
85-
86131
.dashicons-warning {
87132
color: #b32d2e;
88133
}
@@ -100,51 +145,6 @@
100145
}
101146
}
102147

103-
.cloud-search-form {
104-
display: flex;
105-
gap: 8px;
106-
margin-block: 31px 47px;
107-
block-size: 54px;
108-
109-
select {
110-
flex: 0 0 250px;
111-
}
112-
113-
.button {
114-
flex: 0 0 165px;
115-
}
116-
117-
.cloud-search-query {
118-
flex: 1;
119-
position: relative;
120-
121-
input {
122-
inline-size: 100%;
123-
block-size: 100%;
124-
}
125-
126-
.components-spinner {
127-
position: absolute;
128-
inset-inline-end: 1.5em;
129-
inset-block-start: 25%;
130-
}
131-
}
132-
}
133-
134-
.tablenav.top {
135-
display: flex;
136-
gap: 20px;
137-
block-size: 40px;
138-
margin-block-end: 31px;
139-
align-items: center;
140-
141-
select {
142-
inline-size: 245px;
143-
block-size: 100%;
144-
}
145-
146-
.tablenav-pages {
147-
margin: 0;
148-
margin-inline-start: auto;
149-
}
148+
.cloud-search-results .cloud-search-result footer > .components-spinner {
149+
margin: 0;
150150
}

src/css/manage/_snippets-table.scss

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,31 @@
166166
min-inline-size: 130px;
167167
max-inline-size: 130px;
168168
text-align: end;
169-
padding-inline: 8px;
170169
overflow: hidden;
171170
text-overflow: ellipsis;
172171
}
172+
173+
td.column-date .modified-column-content {
174+
display: block;
175+
text-align: start;
176+
}
177+
}
178+
179+
.wp-list-table.truncate-row-values {
180+
td.column-name > .snippet-name,
181+
td.column-desc .snippet-description-content {
182+
display: block;
183+
overflow: hidden;
184+
text-overflow: ellipsis;
185+
}
186+
187+
td.column-name > .snippet-name {
188+
max-inline-size: min(15rem, 30vw);
189+
}
190+
191+
td.column-desc .snippet-description-content {
192+
max-inline-size: min(25rem, 45vw);
193+
}
173194
}
174195

175196
.wp-core-ui .button.clear-filters {
Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,118 @@
1-
import React from 'react'
1+
import React, { useEffect } from 'react'
22
import { SnippetForm } from './SnippetForm'
33

4-
export const EditMenu = () =>
5-
<SnippetForm />
4+
const EVENT_NAME = 'code_snippets_focus_editor'
5+
6+
interface EditMenuLinkBinding {
7+
menuLink: HTMLAnchorElement
8+
originalHref: string | undefined
9+
originalRole: string | undefined
10+
originalTabIndex: string | undefined
11+
handleClick: (event: MouseEvent) => void
12+
handleKeyDown: (event: KeyboardEvent) => void
13+
}
14+
15+
const focusCodeEditor = () => {
16+
window.dispatchEvent(new CustomEvent(EVENT_NAME))
17+
}
18+
19+
const restoreAttribute = (menuLink: HTMLAnchorElement, name: string, value: string | undefined) => {
20+
if (undefined !== value) {
21+
menuLink.setAttribute(name, value)
22+
return
23+
}
24+
25+
menuLink.removeAttribute(name)
26+
}
27+
28+
const getEditPage = (): string | undefined => {
29+
const editUrl = window.CODE_SNIPPETS?.urls.edit
30+
31+
return editUrl ? new URL(editUrl, window.location.origin).searchParams.get('page') ?? undefined : undefined
32+
}
33+
34+
const bindEditMenuLink = (menuLink: HTMLAnchorElement, page: string): EditMenuLinkBinding | undefined => {
35+
const menuUrl = new URL(menuLink.href, window.location.origin)
36+
37+
if (page !== menuUrl.searchParams.get('page')) {
38+
return undefined
39+
}
40+
41+
const handleClick = (event: MouseEvent) => {
42+
event.preventDefault()
43+
focusCodeEditor()
44+
}
45+
46+
const handleKeyDown = (event: KeyboardEvent) => {
47+
if ('Enter' !== event.key && ' ' !== event.key) {
48+
return
49+
}
50+
51+
event.preventDefault()
52+
focusCodeEditor()
53+
}
54+
55+
const binding = {
56+
menuLink,
57+
originalHref: menuLink.getAttribute('href') ?? undefined,
58+
originalRole: menuLink.getAttribute('role') ?? undefined,
59+
originalTabIndex: menuLink.getAttribute('tabindex') ?? undefined,
60+
handleClick,
61+
handleKeyDown
62+
}
63+
64+
menuLink.dataset.codeSnippetsDisabled = 'true'
65+
menuLink.setAttribute('role', 'button')
66+
menuLink.setAttribute('tabindex', '0')
67+
menuLink.classList.add('code-snippets-edit-menu-link')
68+
menuLink.removeAttribute('href')
69+
menuLink.addEventListener('click', handleClick)
70+
menuLink.addEventListener('keydown', handleKeyDown)
71+
72+
return binding
73+
}
74+
75+
const unbindEditMenuLink = ({
76+
menuLink,
77+
originalHref,
78+
originalRole,
79+
originalTabIndex,
80+
handleClick,
81+
handleKeyDown
82+
}: EditMenuLinkBinding) => {
83+
menuLink.removeEventListener('click', handleClick)
84+
menuLink.removeEventListener('keydown', handleKeyDown)
85+
menuLink.classList.remove('code-snippets-edit-menu-link')
86+
delete menuLink.dataset.codeSnippetsDisabled
87+
88+
restoreAttribute(menuLink, 'href', originalHref)
89+
restoreAttribute(menuLink, 'role', originalRole)
90+
restoreAttribute(menuLink, 'tabindex', originalTabIndex)
91+
}
92+
93+
const useEditMenuLinkFocus = () => {
94+
useEffect(() => {
95+
const page = getEditPage()
96+
97+
if (!page) {
98+
return
99+
}
100+
101+
const editMenuLinks = Array.from(document.querySelectorAll<HTMLAnchorElement>('#adminmenu a[href]'))
102+
.flatMap(menuLink => {
103+
const binding = bindEditMenuLink(menuLink, page)
104+
105+
return binding ? [binding] : []
106+
})
107+
108+
return () => {
109+
editMenuLinks.forEach(unbindEditMenuLink)
110+
}
111+
}, [])
112+
}
113+
114+
export const EditMenu = () => {
115+
useEditMenuLinkFocus()
116+
117+
return <SnippetForm />
118+
}

0 commit comments

Comments
 (0)