@wordpress/eslint-plugin 24.0.1-next.v.0 → 24.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/configs/custom.js +2 -0
- package/package.json +5 -4
- package/rules/__tests__/components-no-missing-40px-size-prop.js +353 -0
- package/rules/__tests__/components-no-unsafe-button-disabled.js +235 -0
- package/rules/__tests__/dependency-group.js +33 -0
- package/rules/__tests__/no-setting-ds-tokens.js +46 -0
- package/rules/__tests__/no-unknown-ds-tokens.js +60 -0
- package/rules/components-no-missing-40px-size-prop.js +327 -0
- package/rules/components-no-unsafe-button-disabled.js +126 -0
- package/rules/dependency-group.js +64 -1
- package/rules/no-setting-ds-tokens.js +27 -0
- package/rules/no-unknown-ds-tokens.js +101 -0
- package/utils/has-truthy-jsx-attribute.js +50 -0
- package/utils/index.js +2 -0
package/README.md
CHANGED
|
@@ -82,8 +82,10 @@ The granular rulesets will not define any environment globals. As such, if they
|
|
|
82
82
|
| [i18n-text-domain](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/i18n-text-domain.md) | Enforce passing valid text domains. | ✓ |
|
|
83
83
|
| [i18n-translator-comments](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/i18n-translator-comments.md) | Enforce adding translator comments. | ✓ |
|
|
84
84
|
| [no-base-control-with-label-without-id](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-base-control-with-label-without-id.md) | Disallow the usage of BaseControl component with a label prop set but omitting the id property. | ✓ |
|
|
85
|
+
| [components-no-missing-40px-size-prop](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/components-no-missing-40px-size-prop.md) | Disallow missing `__next40pxDefaultSize` prop on `@wordpress/components` components. | ✓ |
|
|
86
|
+
| [components-no-unsafe-button-disabled](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/components-no-unsafe-button-disabled.md) | Disallow using `disabled` on Button without `accessibleWhenDisabled`. | ✓ |
|
|
85
87
|
| [no-unguarded-get-range-at](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unguarded-get-range-at.md) | Disallow the usage of unguarded `getRangeAt` calls. | ✓ |
|
|
86
|
-
| [no-unsafe-wp-apis](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unsafe-wp-apis.md) | Disallow the usage of unsafe APIs from `@wordpress/*`
|
|
88
|
+
| [no-unsafe-wp-apis](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unsafe-wp-apis.md) | Disallow the usage of unsafe APIs from `@wordpress/*` packages | ✓ |
|
|
87
89
|
| [no-unused-vars-before-return](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md) | Disallow assigning variable values if unused before a return. | ✓ |
|
|
88
90
|
| [no-wp-process-env](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-wp-process-env.md) | Disallow legacy usage of WordPress variables via `process.env` like `process.env.SCRIPT_DEBUG`. | ✓ |
|
|
89
91
|
| [react-no-unsafe-timeout](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/react-no-unsafe-timeout.md) | Disallow unsafe `setTimeout` in component. | |
|
package/configs/custom.js
CHANGED
|
@@ -6,6 +6,8 @@ module.exports = {
|
|
|
6
6
|
'@wordpress/no-unguarded-get-range-at': 'error',
|
|
7
7
|
'@wordpress/no-global-active-element': 'error',
|
|
8
8
|
'@wordpress/no-global-get-selection': 'error',
|
|
9
|
+
'@wordpress/no-setting-ds-tokens': 'error',
|
|
10
|
+
'@wordpress/no-unknown-ds-tokens': 'error',
|
|
9
11
|
'@wordpress/no-unsafe-wp-apis': 'error',
|
|
10
12
|
'@wordpress/no-wp-process-env': 'error',
|
|
11
13
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/eslint-plugin",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.1.0",
|
|
4
4
|
"description": "ESLint plugin for WordPress development.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
"@babel/eslint-parser": "7.25.7",
|
|
41
41
|
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
|
42
42
|
"@typescript-eslint/parser": "^6.4.1",
|
|
43
|
-
"@wordpress/babel-preset-default": "^8.
|
|
44
|
-
"@wordpress/prettier-config": "^4.
|
|
43
|
+
"@wordpress/babel-preset-default": "^8.39.0",
|
|
44
|
+
"@wordpress/prettier-config": "^4.39.0",
|
|
45
|
+
"@wordpress/theme": "^0.6.0",
|
|
45
46
|
"cosmiconfig": "^7.0.0",
|
|
46
47
|
"eslint-config-prettier": "^8.3.0",
|
|
47
48
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
@@ -77,5 +78,5 @@
|
|
|
77
78
|
"publishConfig": {
|
|
78
79
|
"access": "public"
|
|
79
80
|
},
|
|
80
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "eee1cfb1472f11183e40fb77465a5f13145df7ad"
|
|
81
82
|
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import rule from '../components-no-missing-40px-size-prop';
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester( {
|
|
5
|
+
parserOptions: {
|
|
6
|
+
sourceType: 'module',
|
|
7
|
+
ecmaVersion: 6,
|
|
8
|
+
ecmaFeatures: {
|
|
9
|
+
jsx: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
|
|
15
|
+
valid: [
|
|
16
|
+
// Component with __next40pxDefaultSize (boolean attribute)
|
|
17
|
+
{
|
|
18
|
+
code: `
|
|
19
|
+
import { Button } from '@wordpress/components';
|
|
20
|
+
<Button __next40pxDefaultSize />
|
|
21
|
+
`,
|
|
22
|
+
},
|
|
23
|
+
// Component with __next40pxDefaultSize={true}
|
|
24
|
+
{
|
|
25
|
+
code: `
|
|
26
|
+
import { InputControl } from '@wordpress/components';
|
|
27
|
+
<InputControl __next40pxDefaultSize={true} />
|
|
28
|
+
`,
|
|
29
|
+
},
|
|
30
|
+
// Component with non-default size prop
|
|
31
|
+
{
|
|
32
|
+
code: `
|
|
33
|
+
import { Button } from '@wordpress/components';
|
|
34
|
+
<Button size="small" />
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
// Component with size="compact"
|
|
38
|
+
{
|
|
39
|
+
code: `
|
|
40
|
+
import { SelectControl } from '@wordpress/components';
|
|
41
|
+
<SelectControl size="compact" />
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
// Component from @wordpress/ui (should not be checked)
|
|
45
|
+
{
|
|
46
|
+
code: `
|
|
47
|
+
import { Button } from '@wordpress/ui';
|
|
48
|
+
<Button />
|
|
49
|
+
`,
|
|
50
|
+
},
|
|
51
|
+
// Local component (should not be checked)
|
|
52
|
+
{
|
|
53
|
+
code: `
|
|
54
|
+
const Button = () => <button />;
|
|
55
|
+
<Button />
|
|
56
|
+
`,
|
|
57
|
+
},
|
|
58
|
+
// Component from another package (should not be checked)
|
|
59
|
+
{
|
|
60
|
+
code: `
|
|
61
|
+
import { Button } from 'some-other-package';
|
|
62
|
+
<Button />
|
|
63
|
+
`,
|
|
64
|
+
},
|
|
65
|
+
// Aliased import with correct prop
|
|
66
|
+
{
|
|
67
|
+
code: `
|
|
68
|
+
import { Button as WPButton } from '@wordpress/components';
|
|
69
|
+
<WPButton __next40pxDefaultSize />
|
|
70
|
+
`,
|
|
71
|
+
},
|
|
72
|
+
// FormFileUpload with render prop (special case)
|
|
73
|
+
{
|
|
74
|
+
code: `
|
|
75
|
+
import { FormFileUpload } from '@wordpress/components';
|
|
76
|
+
<FormFileUpload render={({ open }) => <button onClick={open}>Upload</button>} />
|
|
77
|
+
`,
|
|
78
|
+
},
|
|
79
|
+
// FormFileUpload with __next40pxDefaultSize
|
|
80
|
+
{
|
|
81
|
+
code: `
|
|
82
|
+
import { FormFileUpload } from '@wordpress/components';
|
|
83
|
+
<FormFileUpload __next40pxDefaultSize />
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
// Component with dynamic size prop (assumes it could be non-default)
|
|
87
|
+
{
|
|
88
|
+
code: `
|
|
89
|
+
import { Button } from '@wordpress/components';
|
|
90
|
+
<Button size={buttonSize} />
|
|
91
|
+
`,
|
|
92
|
+
},
|
|
93
|
+
// Button with variant="link" (doesn't need __next40pxDefaultSize)
|
|
94
|
+
{
|
|
95
|
+
code: `
|
|
96
|
+
import { Button } from '@wordpress/components';
|
|
97
|
+
<Button variant="link" />
|
|
98
|
+
`,
|
|
99
|
+
},
|
|
100
|
+
// Non-targeted component (should not be checked)
|
|
101
|
+
{
|
|
102
|
+
code: `
|
|
103
|
+
import { Modal } from '@wordpress/components';
|
|
104
|
+
<Modal />
|
|
105
|
+
`,
|
|
106
|
+
},
|
|
107
|
+
// All targeted components with __next40pxDefaultSize
|
|
108
|
+
{
|
|
109
|
+
code: `
|
|
110
|
+
import {
|
|
111
|
+
BorderBoxControl,
|
|
112
|
+
BorderControl,
|
|
113
|
+
BoxControl,
|
|
114
|
+
ComboboxControl,
|
|
115
|
+
CustomSelectControl,
|
|
116
|
+
FontAppearanceControl,
|
|
117
|
+
FontFamilyControl,
|
|
118
|
+
FontSizePicker,
|
|
119
|
+
FormTokenField,
|
|
120
|
+
InputControl,
|
|
121
|
+
LetterSpacingControl,
|
|
122
|
+
LineHeightControl,
|
|
123
|
+
NumberControl,
|
|
124
|
+
RangeControl,
|
|
125
|
+
SelectControl,
|
|
126
|
+
TextControl,
|
|
127
|
+
ToggleGroupControl,
|
|
128
|
+
UnitControl,
|
|
129
|
+
} from '@wordpress/components';
|
|
130
|
+
<>
|
|
131
|
+
<BorderBoxControl __next40pxDefaultSize />
|
|
132
|
+
<BorderControl __next40pxDefaultSize />
|
|
133
|
+
<BoxControl __next40pxDefaultSize />
|
|
134
|
+
<ComboboxControl __next40pxDefaultSize />
|
|
135
|
+
<CustomSelectControl __next40pxDefaultSize />
|
|
136
|
+
<FontAppearanceControl __next40pxDefaultSize />
|
|
137
|
+
<FontFamilyControl __next40pxDefaultSize />
|
|
138
|
+
<FontSizePicker __next40pxDefaultSize />
|
|
139
|
+
<FormTokenField __next40pxDefaultSize />
|
|
140
|
+
<InputControl __next40pxDefaultSize />
|
|
141
|
+
<LetterSpacingControl __next40pxDefaultSize />
|
|
142
|
+
<LineHeightControl __next40pxDefaultSize />
|
|
143
|
+
<NumberControl __next40pxDefaultSize />
|
|
144
|
+
<RangeControl __next40pxDefaultSize />
|
|
145
|
+
<SelectControl __next40pxDefaultSize />
|
|
146
|
+
<TextControl __next40pxDefaultSize />
|
|
147
|
+
<ToggleGroupControl __next40pxDefaultSize />
|
|
148
|
+
<UnitControl __next40pxDefaultSize />
|
|
149
|
+
</>
|
|
150
|
+
`,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
invalid: [
|
|
154
|
+
// Button without __next40pxDefaultSize
|
|
155
|
+
{
|
|
156
|
+
code: `
|
|
157
|
+
import { Button } from '@wordpress/components';
|
|
158
|
+
<Button />
|
|
159
|
+
`,
|
|
160
|
+
errors: [
|
|
161
|
+
{
|
|
162
|
+
messageId: 'missingProp',
|
|
163
|
+
data: { component: 'Button' },
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
// InputControl without __next40pxDefaultSize
|
|
168
|
+
{
|
|
169
|
+
code: `
|
|
170
|
+
import { InputControl } from '@wordpress/components';
|
|
171
|
+
<InputControl value={value} onChange={onChange} />
|
|
172
|
+
`,
|
|
173
|
+
errors: [
|
|
174
|
+
{
|
|
175
|
+
messageId: 'missingProp',
|
|
176
|
+
data: { component: 'InputControl' },
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
// Component with __next40pxDefaultSize={false}
|
|
181
|
+
{
|
|
182
|
+
code: `
|
|
183
|
+
import { SelectControl } from '@wordpress/components';
|
|
184
|
+
<SelectControl __next40pxDefaultSize={false} />
|
|
185
|
+
`,
|
|
186
|
+
errors: [
|
|
187
|
+
{
|
|
188
|
+
messageId: 'missingProp',
|
|
189
|
+
data: { component: 'SelectControl' },
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
// Component with size="default" (should still require __next40pxDefaultSize)
|
|
194
|
+
{
|
|
195
|
+
code: `
|
|
196
|
+
import { Button } from '@wordpress/components';
|
|
197
|
+
<Button size="default" />
|
|
198
|
+
`,
|
|
199
|
+
errors: [
|
|
200
|
+
{
|
|
201
|
+
messageId: 'missingProp',
|
|
202
|
+
data: { component: 'Button' },
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
// Aliased import without __next40pxDefaultSize
|
|
207
|
+
{
|
|
208
|
+
code: `
|
|
209
|
+
import { TextControl as MyTextControl } from '@wordpress/components';
|
|
210
|
+
<MyTextControl />
|
|
211
|
+
`,
|
|
212
|
+
errors: [
|
|
213
|
+
{
|
|
214
|
+
messageId: 'missingProp',
|
|
215
|
+
data: { component: 'TextControl' },
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
// FormFileUpload without __next40pxDefaultSize or render
|
|
220
|
+
{
|
|
221
|
+
code: `
|
|
222
|
+
import { FormFileUpload } from '@wordpress/components';
|
|
223
|
+
<FormFileUpload onChange={handleChange} />
|
|
224
|
+
`,
|
|
225
|
+
errors: [
|
|
226
|
+
{
|
|
227
|
+
messageId: 'missingPropFormFileUpload',
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
// Multiple components, some invalid
|
|
232
|
+
{
|
|
233
|
+
code: `
|
|
234
|
+
import { Button, InputControl } from '@wordpress/components';
|
|
235
|
+
<>
|
|
236
|
+
<Button __next40pxDefaultSize />
|
|
237
|
+
<InputControl />
|
|
238
|
+
</>
|
|
239
|
+
`,
|
|
240
|
+
errors: [
|
|
241
|
+
{
|
|
242
|
+
messageId: 'missingProp',
|
|
243
|
+
data: { component: 'InputControl' },
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
// Multiple invalid components
|
|
248
|
+
{
|
|
249
|
+
code: `
|
|
250
|
+
import { Button, SelectControl } from '@wordpress/components';
|
|
251
|
+
<>
|
|
252
|
+
<Button />
|
|
253
|
+
<SelectControl />
|
|
254
|
+
</>
|
|
255
|
+
`,
|
|
256
|
+
errors: [
|
|
257
|
+
{
|
|
258
|
+
messageId: 'missingProp',
|
|
259
|
+
data: { component: 'Button' },
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
messageId: 'missingProp',
|
|
263
|
+
data: { component: 'SelectControl' },
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
// Relative import with checkLocalImports enabled
|
|
268
|
+
{
|
|
269
|
+
code: `
|
|
270
|
+
import { Button } from '../button';
|
|
271
|
+
<Button />
|
|
272
|
+
`,
|
|
273
|
+
options: [ { checkLocalImports: true } ],
|
|
274
|
+
errors: [
|
|
275
|
+
{
|
|
276
|
+
messageId: 'missingProp',
|
|
277
|
+
data: { component: 'Button' },
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
// Default import from input-control path with checkLocalImports enabled
|
|
282
|
+
{
|
|
283
|
+
code: `
|
|
284
|
+
import InputControl from '../input-control';
|
|
285
|
+
<InputControl />
|
|
286
|
+
`,
|
|
287
|
+
options: [ { checkLocalImports: true } ],
|
|
288
|
+
errors: [
|
|
289
|
+
{
|
|
290
|
+
messageId: 'missingProp',
|
|
291
|
+
data: { component: 'InputControl' },
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
} );
|
|
297
|
+
|
|
298
|
+
// Additional tests for checkLocalImports option
|
|
299
|
+
ruleTester.run(
|
|
300
|
+
'components-no-missing-40px-size-prop (checkLocalImports)',
|
|
301
|
+
rule,
|
|
302
|
+
{
|
|
303
|
+
valid: [
|
|
304
|
+
// Relative import with correct props
|
|
305
|
+
{
|
|
306
|
+
code: `
|
|
307
|
+
import { Button } from '../button';
|
|
308
|
+
<Button __next40pxDefaultSize />
|
|
309
|
+
`,
|
|
310
|
+
options: [ { checkLocalImports: true } ],
|
|
311
|
+
},
|
|
312
|
+
// Default import with correct props
|
|
313
|
+
{
|
|
314
|
+
code: `
|
|
315
|
+
import InputControl from './input-control';
|
|
316
|
+
<InputControl __next40pxDefaultSize />
|
|
317
|
+
`,
|
|
318
|
+
options: [ { checkLocalImports: true } ],
|
|
319
|
+
},
|
|
320
|
+
// Relative import with non-default size
|
|
321
|
+
{
|
|
322
|
+
code: `
|
|
323
|
+
import { Button } from '../button';
|
|
324
|
+
<Button size="small" />
|
|
325
|
+
`,
|
|
326
|
+
options: [ { checkLocalImports: true } ],
|
|
327
|
+
},
|
|
328
|
+
// Relative import without checkLocalImports (should not be checked)
|
|
329
|
+
{
|
|
330
|
+
code: `
|
|
331
|
+
import { Button } from '../button';
|
|
332
|
+
<Button />
|
|
333
|
+
`,
|
|
334
|
+
},
|
|
335
|
+
// Default import without checkLocalImports (should not be checked)
|
|
336
|
+
{
|
|
337
|
+
code: `
|
|
338
|
+
import InputControl from '../input-control';
|
|
339
|
+
<InputControl />
|
|
340
|
+
`,
|
|
341
|
+
},
|
|
342
|
+
// FormFileUpload relative import with render prop
|
|
343
|
+
{
|
|
344
|
+
code: `
|
|
345
|
+
import { FormFileUpload } from '../form-file-upload';
|
|
346
|
+
<FormFileUpload render={({ open }) => <button onClick={open}>Upload</button>} />
|
|
347
|
+
`,
|
|
348
|
+
options: [ { checkLocalImports: true } ],
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
invalid: [],
|
|
352
|
+
}
|
|
353
|
+
);
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import rule from '../components-no-unsafe-button-disabled';
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester( {
|
|
5
|
+
parserOptions: {
|
|
6
|
+
sourceType: 'module',
|
|
7
|
+
ecmaVersion: 6,
|
|
8
|
+
ecmaFeatures: {
|
|
9
|
+
jsx: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
ruleTester.run( 'components-no-unsafe-button-disabled', rule, {
|
|
15
|
+
valid: [
|
|
16
|
+
// Button with both disabled and accessibleWhenDisabled
|
|
17
|
+
{
|
|
18
|
+
code: `
|
|
19
|
+
import { Button } from '@wordpress/components';
|
|
20
|
+
<Button disabled accessibleWhenDisabled />
|
|
21
|
+
`,
|
|
22
|
+
},
|
|
23
|
+
// Button with accessibleWhenDisabled={true}
|
|
24
|
+
{
|
|
25
|
+
code: `
|
|
26
|
+
import { Button } from '@wordpress/components';
|
|
27
|
+
<Button disabled accessibleWhenDisabled={true} />
|
|
28
|
+
`,
|
|
29
|
+
},
|
|
30
|
+
// Button with accessibleWhenDisabled={false}
|
|
31
|
+
{
|
|
32
|
+
code: `
|
|
33
|
+
import { Button } from '@wordpress/components';
|
|
34
|
+
<Button disabled accessibleWhenDisabled={false} />
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
// Button with accessibleWhenDisabled={someVar}
|
|
38
|
+
{
|
|
39
|
+
code: `
|
|
40
|
+
import { Button } from '@wordpress/components';
|
|
41
|
+
<Button disabled accessibleWhenDisabled={someVar} />
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
// Button with accessibleWhenDisabled={false} should error (handled in invalid)
|
|
45
|
+
// Button with disabled={false} should not require accessibleWhenDisabled
|
|
46
|
+
{
|
|
47
|
+
code: `
|
|
48
|
+
import { Button } from '@wordpress/components';
|
|
49
|
+
<Button disabled={false} />
|
|
50
|
+
`,
|
|
51
|
+
},
|
|
52
|
+
// Button with disabled={someVar} and accessibleWhenDisabled={someVar}
|
|
53
|
+
{
|
|
54
|
+
code: `
|
|
55
|
+
import { Button } from '@wordpress/components';
|
|
56
|
+
<Button disabled={isDisabled} accessibleWhenDisabled={someVar} />
|
|
57
|
+
`,
|
|
58
|
+
},
|
|
59
|
+
// Button without disabled prop
|
|
60
|
+
{
|
|
61
|
+
code: `
|
|
62
|
+
import { Button } from '@wordpress/components';
|
|
63
|
+
<Button onClick={handleClick} />
|
|
64
|
+
`,
|
|
65
|
+
},
|
|
66
|
+
// Button from @wordpress/ui (should not be checked)
|
|
67
|
+
{
|
|
68
|
+
code: `
|
|
69
|
+
import { Button } from '@wordpress/ui';
|
|
70
|
+
<Button disabled />
|
|
71
|
+
`,
|
|
72
|
+
},
|
|
73
|
+
// Local Button component (should not be checked)
|
|
74
|
+
{
|
|
75
|
+
code: `
|
|
76
|
+
const Button = () => <button />;
|
|
77
|
+
<Button disabled />
|
|
78
|
+
`,
|
|
79
|
+
},
|
|
80
|
+
// Button from another package (should not be checked)
|
|
81
|
+
{
|
|
82
|
+
code: `
|
|
83
|
+
import { Button } from 'some-other-package';
|
|
84
|
+
<Button disabled />
|
|
85
|
+
`,
|
|
86
|
+
},
|
|
87
|
+
// Aliased import with correct props
|
|
88
|
+
{
|
|
89
|
+
code: `
|
|
90
|
+
import { Button as WPButton } from '@wordpress/components';
|
|
91
|
+
<WPButton disabled accessibleWhenDisabled />
|
|
92
|
+
`,
|
|
93
|
+
},
|
|
94
|
+
// Non-Button component with disabled (should not be checked)
|
|
95
|
+
{
|
|
96
|
+
code: `
|
|
97
|
+
import { TextControl } from '@wordpress/components';
|
|
98
|
+
<TextControl disabled />
|
|
99
|
+
`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
invalid: [
|
|
103
|
+
// Button with disabled but no accessibleWhenDisabled
|
|
104
|
+
{
|
|
105
|
+
code: `
|
|
106
|
+
import { Button } from '@wordpress/components';
|
|
107
|
+
<Button disabled />
|
|
108
|
+
`,
|
|
109
|
+
errors: [
|
|
110
|
+
{
|
|
111
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
// Button with disabled={someVar} but no accessibleWhenDisabled
|
|
116
|
+
{
|
|
117
|
+
code: `
|
|
118
|
+
import { Button } from '@wordpress/components';
|
|
119
|
+
<Button disabled={isDisabled} />
|
|
120
|
+
`,
|
|
121
|
+
errors: [
|
|
122
|
+
{
|
|
123
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
// Button with disabled={true} but no accessibleWhenDisabled
|
|
128
|
+
{
|
|
129
|
+
code: `
|
|
130
|
+
import { Button } from '@wordpress/components';
|
|
131
|
+
<Button disabled={true} />
|
|
132
|
+
`,
|
|
133
|
+
errors: [
|
|
134
|
+
{
|
|
135
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
// Aliased import without accessibleWhenDisabled
|
|
140
|
+
{
|
|
141
|
+
code: `
|
|
142
|
+
import { Button as MyButton } from '@wordpress/components';
|
|
143
|
+
<MyButton disabled />
|
|
144
|
+
`,
|
|
145
|
+
errors: [
|
|
146
|
+
{
|
|
147
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
// Multiple Buttons, one invalid
|
|
152
|
+
{
|
|
153
|
+
code: `
|
|
154
|
+
import { Button } from '@wordpress/components';
|
|
155
|
+
<>
|
|
156
|
+
<Button disabled accessibleWhenDisabled />
|
|
157
|
+
<Button disabled />
|
|
158
|
+
</>
|
|
159
|
+
`,
|
|
160
|
+
errors: [
|
|
161
|
+
{
|
|
162
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
} );
|
|
168
|
+
|
|
169
|
+
// Additional tests for checkLocalImports option
|
|
170
|
+
ruleTester.run(
|
|
171
|
+
'components-no-unsafe-button-disabled (checkLocalImports)',
|
|
172
|
+
rule,
|
|
173
|
+
{
|
|
174
|
+
valid: [
|
|
175
|
+
// Relative import with correct props
|
|
176
|
+
{
|
|
177
|
+
code: `
|
|
178
|
+
import { Button } from '../button';
|
|
179
|
+
<Button disabled accessibleWhenDisabled />
|
|
180
|
+
`,
|
|
181
|
+
options: [ { checkLocalImports: true } ],
|
|
182
|
+
},
|
|
183
|
+
// Default import with correct props
|
|
184
|
+
{
|
|
185
|
+
code: `
|
|
186
|
+
import Button from './button';
|
|
187
|
+
<Button disabled accessibleWhenDisabled />
|
|
188
|
+
`,
|
|
189
|
+
options: [ { checkLocalImports: true } ],
|
|
190
|
+
},
|
|
191
|
+
// Relative import without checkLocalImports (should not be checked)
|
|
192
|
+
{
|
|
193
|
+
code: `
|
|
194
|
+
import { Button } from '../button';
|
|
195
|
+
<Button disabled />
|
|
196
|
+
`,
|
|
197
|
+
},
|
|
198
|
+
// Default import without checkLocalImports (should not be checked)
|
|
199
|
+
{
|
|
200
|
+
code: `
|
|
201
|
+
import Button from '../button';
|
|
202
|
+
<Button disabled />
|
|
203
|
+
`,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
invalid: [
|
|
207
|
+
// Relative import with checkLocalImports enabled
|
|
208
|
+
{
|
|
209
|
+
code: `
|
|
210
|
+
import { Button } from '../button';
|
|
211
|
+
<Button disabled />
|
|
212
|
+
`,
|
|
213
|
+
options: [ { checkLocalImports: true } ],
|
|
214
|
+
errors: [
|
|
215
|
+
{
|
|
216
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
// Default import from button path with checkLocalImports enabled
|
|
221
|
+
{
|
|
222
|
+
code: `
|
|
223
|
+
import Button from '../button';
|
|
224
|
+
<Button disabled />
|
|
225
|
+
`,
|
|
226
|
+
options: [ { checkLocalImports: true } ],
|
|
227
|
+
errors: [
|
|
228
|
+
{
|
|
229
|
+
messageId: 'missingAccessibleWhenDisabled',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
}
|
|
235
|
+
);
|
|
@@ -53,6 +53,14 @@ const { Component } = require( '@wordpress/element' );
|
|
|
53
53
|
*/
|
|
54
54
|
const edit = require( './edit' );`,
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
code: `
|
|
58
|
+
import { camelCase } from 'change-case';
|
|
59
|
+
import clsx from 'clsx';
|
|
60
|
+
import { Component } from '@wordpress/element';
|
|
61
|
+
import edit from './edit';`,
|
|
62
|
+
options: [ 'never' ],
|
|
63
|
+
},
|
|
56
64
|
],
|
|
57
65
|
invalid: [
|
|
58
66
|
{
|
|
@@ -131,5 +139,30 @@ const { Component } = require( '@wordpress/element' );
|
|
|
131
139
|
*/
|
|
132
140
|
const edit = require( './edit' );`,
|
|
133
141
|
},
|
|
142
|
+
{
|
|
143
|
+
code: `
|
|
144
|
+
/**
|
|
145
|
+
* External dependencies
|
|
146
|
+
*/
|
|
147
|
+
import { camelCase } from 'change-case';
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* WordPress dependencies
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
import { Component } from '@wordpress/element';`,
|
|
154
|
+
options: [ 'never' ],
|
|
155
|
+
errors: [
|
|
156
|
+
{
|
|
157
|
+
message: 'Unexpected dependency group comment block',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
message: 'Unexpected dependency group comment block',
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
output: `
|
|
164
|
+
import { camelCase } from 'change-case';
|
|
165
|
+
import { Component } from '@wordpress/element';`,
|
|
166
|
+
},
|
|
134
167
|
],
|
|
135
168
|
} );
|