eslint-plugin-smarthr 0.3.25 → 0.3.27
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/CHANGELOG.md +14 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/rules/a11y-heading-in-sectioning-content/index.js +1 -0
- package/rules/a11y-input-in-form-control/index.js +22 -5
- package/rules/a11y-prohibit-useless-sectioning-fragment/index.js +2 -2
- package/test/a11y-heading-in-sectioning-content.js +2 -0
- package/test/a11y-input-in-form-control.js +24 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.3.27](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.26...v0.3.27) (2024-01-28)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* a11y系コンポーネントで入力要素の拡張コンポーネントの判定をfunctionを使っている場合にも正しく判定できるように修正 ([#108](https://github.com/kufu/eslint-plugin-smarthr/issues/108)) ([c929760](https://github.com/kufu/eslint-plugin-smarthr/commit/c929760b3d8e166e7e3f7befcf048fa28cc48042))
|
|
11
|
+
|
|
12
|
+
### [0.3.26](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.25...v0.3.26) (2024-01-24)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* a11y-prohibit-useless-sectioning-fragmentの属性チェックのバグを修正 ([#107](https://github.com/kufu/eslint-plugin-smarthr/issues/107)) ([b42bdd9](https://github.com/kufu/eslint-plugin-smarthr/commit/b42bdd9e11258a6a32e13647c0c764065b5bac64))
|
|
18
|
+
|
|
5
19
|
### [0.3.25](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.24...v0.3.25) (2024-01-23)
|
|
6
20
|
|
|
7
21
|
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- [a11y-input-has-name-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-input-has-name-attribute)
|
|
9
9
|
- [a11y-input-in-form-control](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-input-in-form-control)
|
|
10
10
|
- [a11y-prohibit-input-placeholder](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-input-placeholder)
|
|
11
|
+
- [a11y-prohibit-useless-sectioning-fragment](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-useless-sectioning-fragment)
|
|
11
12
|
- [a11y-trigger-has-button](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-trigger-has-button)
|
|
12
13
|
- [best-practice-for-date](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/best-practice-for-date)
|
|
13
14
|
- [best-practice-for-remote-trigger-dialog](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/best-practice-for-remote-trigger-dialog)
|
package/package.json
CHANGED
|
@@ -112,6 +112,7 @@ const searchBubbleUp = (node) => {
|
|
|
112
112
|
if (
|
|
113
113
|
// Headingコンポーネントの拡張なので対象外
|
|
114
114
|
node.type === 'VariableDeclarator' && ignoreHeadingCheckParentType.includes(node.parent.parent?.type) && node.id.name.match(declaratorHeadingRegex) ||
|
|
115
|
+
node.type === 'FunctionDeclaration' && ignoreHeadingCheckParentType.includes(node.parent.type) && node.id.name.match(declaratorHeadingRegex) ||
|
|
115
116
|
// ModelessDialogのheaderにHeadingを設定している場合も対象外
|
|
116
117
|
node.type === 'JSXAttribute' && node.name.name === 'header' && node.parent.name.name.match(modelessDialogRegex)
|
|
117
118
|
) {
|
|
@@ -33,6 +33,7 @@ const EXPECTED_NAMES = {
|
|
|
33
33
|
'Reel$': '(Reel)$',
|
|
34
34
|
'Sidebar$': '(Sidebar)$',
|
|
35
35
|
'Stack$': '(Stack)$',
|
|
36
|
+
'(L|^l)abel$': '(Label)$',
|
|
36
37
|
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -179,15 +180,20 @@ module.exports = {
|
|
|
179
180
|
}
|
|
180
181
|
// HINT: 擬似的にラベルが設定されている場合、無視する
|
|
181
182
|
} else if (!isRadio && !isCheckbox && !isPseudoLabel) {
|
|
183
|
+
const isSelect = nodeName.match(SELECT_REGEX)
|
|
184
|
+
|
|
182
185
|
context.report({
|
|
183
186
|
node: n,
|
|
184
187
|
message: `${name} が ラベルを持たない入力要素(${nodeName})を含んでいます。入力要素が何であるかを正しく伝えるため、以下の方法のいずれかで修正してください。
|
|
185
188
|
- 方法1: ${name} を smarthr-ui/FormControl、もしくはそれを拡張したコンポーネントに変更してください
|
|
186
189
|
- 方法2: ${nodeName} がlabel要素を含むコンポーネントである場合、名称を${FORM_CONTROL_REGEX}にマッチするものに変更してください
|
|
187
|
-
|
|
190
|
+
- smarthr-ui/FormControl、smarthr-ui/FormGroup はlabel要素を内包しています
|
|
188
191
|
- 方法3: ${nodeName} がRadioButton、もしくはCheckboxを表すコンポーネントの場合、名称を${LABELED_INPUTS_REGEX}にマッチするものに変更してください
|
|
189
|
-
|
|
190
|
-
- 方法4: ${name} が smarthr-ui/Fieldset、もしくはそれを拡張しているコンポーネントではない場合、名称を ${FIELDSET_REGEX}
|
|
192
|
+
- smarthr-ui/RadioButton、smarthr-ui/RadioButtonPanel、smarthr-ui/Checkbox はlabel要素を内包しています
|
|
193
|
+
- 方法4: ${name} が smarthr-ui/Fieldset、もしくはそれを拡張しているコンポーネントではない場合、名称を ${FIELDSET_REGEX} にマッチしないものに変更してください
|
|
194
|
+
- 方法5: 別途label要素が存在し、それらと紐づけたい場合はlabel要素のhtmlFor属性、${nodeName}のid属性に同じ文字列を指定してください。この文字列はhtml内で一意である必要があります
|
|
195
|
+
- 方法6: 上記のいずれの方法も適切ではない場合、${nodeName}のtitle属性に "どんな値を${isSelect ? '選択' : '入力'}すれば良いのか" の説明を設定してください
|
|
196
|
+
- 例: <${nodeName} title="${isSelect ? '検索対象を選択してください' : '姓を全角カタカナのみで入力してください'}" />`,
|
|
191
197
|
});
|
|
192
198
|
}
|
|
193
199
|
|
|
@@ -209,7 +215,8 @@ module.exports = {
|
|
|
209
215
|
- 方法1: ${actualName} を${wrapComponentName}、もしくはそれを拡張したコンポーネントに変更してください
|
|
210
216
|
- ${actualName} 内のHeading要素は${wrapComponentName}のtitle属性に変更してください
|
|
211
217
|
- 方法2: ${actualName} と ${nodeName} の間に ${wrapComponentName} が存在するようにマークアップを変更してください${isRadio ? '' : `
|
|
212
|
-
- 方法3:
|
|
218
|
+
- 方法3: 別途label要素が存在し、それらと紐づけたい場合はlabel要素のhtmlFor属性、${nodeName}のid属性に同じ文字列を指定してください。この文字列はhtml内で一意である必要があります
|
|
219
|
+
- 方法4: 上記のいずれの方法も適切ではない場合、${nodeName}のtitle属性に "どんな値を${isSelect ? '選択' : '入力'}すれば良いのか" の説明を設定してください
|
|
213
220
|
- 例: <${nodeName} title="${isSelect ? '検索対象を選択してください' : '姓を全角カタカナのみで入力してください'}" />`}`,
|
|
214
221
|
});
|
|
215
222
|
}
|
|
@@ -233,6 +240,16 @@ module.exports = {
|
|
|
233
240
|
|
|
234
241
|
break
|
|
235
242
|
}
|
|
243
|
+
case 'FunctionDeclaration': {
|
|
244
|
+
if (n.parent.type.match(IGNORE_INPUT_CHECK_PARENT_TYPE)) {
|
|
245
|
+
const name = n.id.name
|
|
246
|
+
|
|
247
|
+
// 入力要素系コンポーネントの拡張なので対象外
|
|
248
|
+
if (name.match(FORM_CONTROL_INPUTS_REGEX) || checkAdditionalMultiInputComponents(name) || checkAdditionalInputComponents(name)) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
236
253
|
case 'Program': {
|
|
237
254
|
// HINT: smarthr-ui/CheckBoxはlabelを単独で持つため、FormControl系でラップをする必要はない
|
|
238
255
|
// HINT: 擬似的にラベルが設定されている場合、無視する
|
|
@@ -246,7 +263,7 @@ module.exports = {
|
|
|
246
263
|
- FieldsetでRadioButtonを囲むことでグループ化された入力要素に対して適切なタイトル・説明を追加出来ます` : ``}
|
|
247
264
|
- ${nodeName}が入力要素とラベル・タイトル・説明など含む概念を表示するコンポーネントの場合、コンポーネント名を${FROM_CONTROLS_REGEX}とマッチするように修正してください
|
|
248
265
|
- ${nodeName}が入力要素自体を表現するコンポーネントの一部である場合、ルートとなるコンポーネントの名称を${FORM_CONTROL_INPUTS_REGEX}とマッチするように修正してください${isRadio ? '' : `
|
|
249
|
-
-
|
|
266
|
+
- 上記のいずれの方法も適切ではない場合、${nodeName}のtitle属性に "どんな値を${isSelect ? '選択' : '入力'}すれば良いのか" の説明を設定してください
|
|
250
267
|
- 例: <${nodeName} title="${isSelect ? '検索対象を選択してください' : '姓を全角カタカナのみで入力してください'}" />`}`,
|
|
251
268
|
});
|
|
252
269
|
}
|
|
@@ -19,12 +19,12 @@ const SECTIONING_FRAGMENT_REGEX = /^SectioningFragment$/
|
|
|
19
19
|
const LAYOUT_REGEX = /((C(ent|lust)er)|Reel|Sidebar|Stack)$/
|
|
20
20
|
const AS_REGEX = /^(as|forwardedAs)$/
|
|
21
21
|
|
|
22
|
-
const includeSectioningAsAttr = (a) => a.name?.name
|
|
22
|
+
const includeSectioningAsAttr = (a) => a.name?.name?.match(AS_REGEX) && a.value.value?.match(BARE_SECTIONING_TAG_REGEX)
|
|
23
23
|
|
|
24
24
|
const searchSectioningFragment = (node) => {
|
|
25
25
|
switch (node.type) {
|
|
26
26
|
case 'JSXElement':
|
|
27
|
-
return node.openingElement.name?.name
|
|
27
|
+
return node.openingElement.name?.name?.match(SECTIONING_FRAGMENT_REGEX) ? node.openingElement : null
|
|
28
28
|
case 'Program':
|
|
29
29
|
return null
|
|
30
30
|
}
|
|
@@ -55,6 +55,8 @@ ruleTester.run('a11y-heading-in-sectioning-content', rule, {
|
|
|
55
55
|
{ code: '<><Section><Heading>hoge</Heading></Section><Section><Heading>fuga</Heading></Section></>' },
|
|
56
56
|
{ code: 'const HogeHeading = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>' },
|
|
57
57
|
{ code: 'export const HogeHeading = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>' },
|
|
58
|
+
{ code: 'function FugaHeading() { return <PiyoHeading anyArg={abc}>hoge</PiyoHeading> }' },
|
|
59
|
+
{ code: 'export function FugaHeading() { return <PiyoHeading anyArg={abc}>hoge</PiyoHeading> }' },
|
|
58
60
|
{ code: '<Center as="section"><div><Heading>hoge</Heading></div></Center>' },
|
|
59
61
|
{ code: '<Cluster as="section"><div><Heading>hoge</Heading></div></Cluster>' },
|
|
60
62
|
{ code: '<Reel as="aside"><div><Heading>hoge</Heading></div></Reel>' },
|
|
@@ -16,13 +16,13 @@ const noLabeledInput = (name) => `${name} を、smarthr-ui/FormControl もしく
|
|
|
16
16
|
- FormControlで入力要素を囲むことでラベルと入力要素が適切に紐づき、操作性が高まります
|
|
17
17
|
- ${name}が入力要素とラベル・タイトル・説明など含む概念を表示するコンポーネントの場合、コンポーネント名を/((FormGroup)$|(FormControl)$|((F|^f)ieldset)$)/とマッチするように修正してください
|
|
18
18
|
- ${name}が入力要素自体を表現するコンポーネントの一部である場合、ルートとなるコンポーネントの名称を/((I|^i)nput$|SearchInput$|(T|^t)extarea$|(S|^s)elect$|InputFile$|Combo(b|B)ox$|DatePicker$|RadioButton$|RadioButtonPanel$|Check(B|b)ox$)/とマッチするように修正してください
|
|
19
|
-
-
|
|
19
|
+
- 上記のいずれの方法も適切ではない場合、${name}のtitle属性に "どんな値を入力すれば良いのか" の説明を設定してください
|
|
20
20
|
- 例: <${name} title="姓を全角カタカナのみで入力してください" />`
|
|
21
21
|
const noLabeledSelect = (name) => `${name} を、smarthr-ui/FormControl もしくはそれを拡張したコンポーネントが囲むようマークアップを変更してください。
|
|
22
22
|
- FormControlで入力要素を囲むことでラベルと入力要素が適切に紐づき、操作性が高まります
|
|
23
23
|
- ${name}が入力要素とラベル・タイトル・説明など含む概念を表示するコンポーネントの場合、コンポーネント名を/((FormGroup)$|(FormControl)$|((F|^f)ieldset)$)/とマッチするように修正してください
|
|
24
24
|
- ${name}が入力要素自体を表現するコンポーネントの一部である場合、ルートとなるコンポーネントの名称を/((I|^i)nput$|SearchInput$|(T|^t)extarea$|(S|^s)elect$|InputFile$|Combo(b|B)ox$|DatePicker$|RadioButton$|RadioButtonPanel$|Check(B|b)ox$)/とマッチするように修正してください
|
|
25
|
-
-
|
|
25
|
+
- 上記のいずれの方法も適切ではない場合、${name}のtitle属性に "どんな値を選択すれば良いのか" の説明を設定してください
|
|
26
26
|
- 例: <${name} title="検索対象を選択してください" />`
|
|
27
27
|
const invalidPureCheckboxInFormControl = (name) => `HogeFormControl が ${name} を含んでいます。smarthr-ui/FormControl を smarthr-ui/Fieldset に変更し、正しくグルーピングされるように修正してください。
|
|
28
28
|
- 可能なら${name}はsmarthr-ui/Checkboxへの変更を検討してください。難しい場合は ${name} と結びつくlabel要素が必ず存在するよう、マークアップする必要があることに注意してください。`
|
|
@@ -41,15 +41,29 @@ const invalidMultiInputsInFormControl = () => `HogeFormControl が複数の入
|
|
|
41
41
|
const noLabeledInputInFieldset = (name) => `HogeFieldset が ラベルを持たない入力要素(${name})を含んでいます。入力要素が何であるかを正しく伝えるため、以下の方法のいずれかで修正してください。
|
|
42
42
|
- 方法1: HogeFieldset を smarthr-ui/FormControl、もしくはそれを拡張したコンポーネントに変更してください
|
|
43
43
|
- 方法2: ${name} がlabel要素を含むコンポーネントである場合、名称を/(Form(Control|Group))$/にマッチするものに変更してください
|
|
44
|
-
|
|
44
|
+
- smarthr-ui/FormControl、smarthr-ui/FormGroup はlabel要素を内包しています
|
|
45
45
|
- 方法3: ${name} がRadioButton、もしくはCheckboxを表すコンポーネントの場合、名称を/(RadioButton$|RadioButtonPanel$|Check(B|b)ox$)/にマッチするものに変更してください
|
|
46
|
-
|
|
47
|
-
- 方法4: HogeFieldset が smarthr-ui/Fieldset、もしくはそれを拡張しているコンポーネントではない場合、名称を /Fieldset$/
|
|
46
|
+
- smarthr-ui/RadioButton、smarthr-ui/RadioButtonPanel、smarthr-ui/Checkbox はlabel要素を内包しています
|
|
47
|
+
- 方法4: HogeFieldset が smarthr-ui/Fieldset、もしくはそれを拡張しているコンポーネントではない場合、名称を /Fieldset$/ にマッチしないものに変更してください
|
|
48
|
+
- 方法5: 別途label要素が存在し、それらと紐づけたい場合はlabel要素のhtmlFor属性、${name}のid属性に同じ文字列を指定してください。この文字列はhtml内で一意である必要があります
|
|
49
|
+
- 方法6: 上記のいずれの方法も適切ではない場合、${name}のtitle属性に "どんな値を入力すれば良いのか" の説明を設定してください
|
|
50
|
+
- 例: <${name} title="姓を全角カタカナのみで入力してください" />`
|
|
51
|
+
const noLabeledInputInFieldsetWithSelect = (name) => `HogeFieldset が ラベルを持たない入力要素(${name})を含んでいます。入力要素が何であるかを正しく伝えるため、以下の方法のいずれかで修正してください。
|
|
52
|
+
- 方法1: HogeFieldset を smarthr-ui/FormControl、もしくはそれを拡張したコンポーネントに変更してください
|
|
53
|
+
- 方法2: ${name} がlabel要素を含むコンポーネントである場合、名称を/(Form(Control|Group))$/にマッチするものに変更してください
|
|
54
|
+
- smarthr-ui/FormControl、smarthr-ui/FormGroup はlabel要素を内包しています
|
|
55
|
+
- 方法3: ${name} がRadioButton、もしくはCheckboxを表すコンポーネントの場合、名称を/(RadioButton$|RadioButtonPanel$|Check(B|b)ox$)/にマッチするものに変更してください
|
|
56
|
+
- smarthr-ui/RadioButton、smarthr-ui/RadioButtonPanel、smarthr-ui/Checkbox はlabel要素を内包しています
|
|
57
|
+
- 方法4: HogeFieldset が smarthr-ui/Fieldset、もしくはそれを拡張しているコンポーネントではない場合、名称を /Fieldset$/ にマッチしないものに変更してください
|
|
58
|
+
- 方法5: 別途label要素が存在し、それらと紐づけたい場合はlabel要素のhtmlFor属性、${name}のid属性に同じ文字列を指定してください。この文字列はhtml内で一意である必要があります
|
|
59
|
+
- 方法6: 上記のいずれの方法も適切ではない場合、${name}のtitle属性に "どんな値を選択すれば良いのか" の説明を設定してください
|
|
60
|
+
- 例: <${name} title="検索対象を選択してください" />`
|
|
48
61
|
const useFormControlInsteadOfSection = (name, section) => `${name}は${section}より先に、smarthr-ui/FormControlが入力要素を囲むようマークアップを以下のいずれかの方法で変更してください。
|
|
49
62
|
- 方法1: ${section} をFormControl、もしくはそれを拡張したコンポーネントに変更してください
|
|
50
63
|
- ${section} 内のHeading要素はFormControlのtitle属性に変更してください
|
|
51
64
|
- 方法2: ${section} と ${name} の間に FormControl が存在するようにマークアップを変更してください
|
|
52
|
-
- 方法3:
|
|
65
|
+
- 方法3: 別途label要素が存在し、それらと紐づけたい場合はlabel要素のhtmlFor属性、${name}のid属性に同じ文字列を指定してください。この文字列はhtml内で一意である必要があります
|
|
66
|
+
- 方法4: 上記のいずれの方法も適切ではない場合、${name}のtitle属性に "どんな値を入力すれば良いのか" の説明を設定してください
|
|
53
67
|
- 例: <${name} title="姓を全角カタカナのみで入力してください" />`
|
|
54
68
|
const useFormControlInsteadOfSectionInRadio = (name, section) => `${name}は${section}より先に、smarthr-ui/Fieldsetが入力要素を囲むようマークアップを以下のいずれかの方法で変更してください。
|
|
55
69
|
- 方法1: ${section} をFieldset、もしくはそれを拡張したコンポーネントに変更してください
|
|
@@ -105,6 +119,9 @@ ruleTester.run('a11y-input-in-form-control', rule, {
|
|
|
105
119
|
{ code: '<FugaSection><HogeFormControl><HogeInput /></HogeFormControl></FugaSection>' },
|
|
106
120
|
{ code: '<Stack as="section"><HogeFormControl><HogeInput /></HogeFormControl></Stack>' },
|
|
107
121
|
{ code: `const AnyComboBox = () => <input />` },
|
|
122
|
+
{ code: `export const AnyComboBox = () => <input />` },
|
|
123
|
+
{ code: `function AnySingleCombobox() { return <SingleCombobox /> }` },
|
|
124
|
+
{ code: `export function AnySingleCombobox() { return <SingleCombobox /> }` },
|
|
108
125
|
{ code: `<Fieldset><HogeFieldset /><HogeFormControl /></Fieldset>` },
|
|
109
126
|
{ code: '<HogeFieldset><HogeCheckBox /><HogeInput id="any" /></HogeFieldset>' },
|
|
110
127
|
{ code: '<FugaSection><HogeInput id="any" /></FugaSection>' },
|
|
@@ -141,6 +158,7 @@ ruleTester.run('a11y-input-in-form-control', rule, {
|
|
|
141
158
|
{ code: '<HogeFormControl><RadioButton /></HogeFormControl>', errors: [ { message: invalidRadioInFormControl('RadioButton') } ] },
|
|
142
159
|
{ code: '<HogeFormControl><HogeRadioButtonPanel /></HogeFormControl>', errors: [ { message: invalidRadioInFormControl('HogeRadioButtonPanel') } ] },
|
|
143
160
|
{ code: '<HogeFieldset><HogeCheckBox /><HogeInput /></HogeFieldset>', errors: [ { message: noLabeledInputInFieldset('HogeInput') } ] },
|
|
161
|
+
{ code: '<HogeFieldset><HogeCheckBox /><HogeSelect /></HogeFieldset>', errors: [ { message: noLabeledInputInFieldsetWithSelect('HogeSelect') } ] },
|
|
144
162
|
{ code: '<FugaSection><HogeInput /></FugaSection>', errors: [ { message: useFormControlInsteadOfSection('HogeInput', 'FugaSection') } ] },
|
|
145
163
|
{ code: '<Stack as="section"><HogeInput /></Stack>', errors: [ { message: useFormControlInsteadOfSection('HogeInput', '<Stack as="section">') } ] },
|
|
146
164
|
{ code: '<Center forwardedAs="aside"><HogeInput /></Center>', errors: [ { message: useFormControlInsteadOfSection('HogeInput', '<Center forwardedAs="aside">') } ] },
|