eslint-plugin-smarthr 0.4.2 → 0.5.1
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 +19 -1
- package/README.md +1 -0
- package/index.js +20 -1
- package/package.json +1 -1
- package/rules/a11y-anchor-has-href-attribute/README.md +3 -3
- package/rules/a11y-anchor-has-href-attribute/index.js +2 -2
- package/rules/a11y-delegate-element-has-role-presentation/README.md +11 -11
- package/rules/a11y-form-control-in-form/README.md +67 -0
- package/rules/a11y-form-control-in-form/index.js +94 -0
- package/rules/a11y-heading-in-sectioning-content/index.js +1 -1
- package/rules/a11y-image-has-alt-attribute/README.md +3 -3
- package/rules/a11y-image-has-alt-attribute/index.js +2 -2
- package/rules/a11y-input-has-name-attribute/README.md +3 -3
- package/rules/a11y-input-has-name-attribute/index.js +2 -2
- package/rules/a11y-prohibit-input-placeholder/index.js +1 -1
- package/test/a11y-anchor-has-href-attribute.js +1 -1
- package/test/a11y-form-control-in-form.js +37 -0
- package/test/a11y-image-has-alt-attribute.js +1 -1
- package/test/a11y-input-has-name-attribute.js +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
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.5.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.5.0...v0.5.1) (2024-03-17)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* a11y-form-control-in-formルールを追加 ([#121](https://github.com/kufu/eslint-plugin-smarthr/issues/121)) ([a6270f9](https://github.com/kufu/eslint-plugin-smarthr/commit/a6270f9f22832a8ccaeef3b63e35da84b6e13e68))
|
|
11
|
+
|
|
12
|
+
## [0.5.0](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.2...v0.5.0) (2024-03-11)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### ⚠ BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
* spread attributesが指定されている場合、ruleをcorrectにする smartr オプションを allow-spread-attributes オプションにリネームする (#119)
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* spread attributesが指定されている場合、ruleをcorrectにする smartr オプションを allow-spread-attributes オプションにリネームする ([#119](https://github.com/kufu/eslint-plugin-smarthr/issues/119)) ([25752bb](https://github.com/kufu/eslint-plugin-smarthr/commit/25752bb8e77cf170779de3746b9dbbc3997be09d))
|
|
22
|
+
|
|
5
23
|
### [0.4.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.1...v0.4.2) (2024-03-03)
|
|
6
24
|
|
|
7
25
|
|
|
@@ -333,7 +351,7 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
333
351
|
### [0.2.14](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.13...v0.2.14) (2022-12-13)
|
|
334
352
|
|
|
335
353
|
### Features
|
|
336
|
-
* trim-propsルールを追加 ([#44](https://github.com/kufu/eslint-plugin-smarthr/pull/44))
|
|
354
|
+
* trim-propsルールを追加 ([#44](https://github.com/kufu/eslint-plugin-smarthr/pull/44))
|
|
337
355
|
|
|
338
356
|
### [0.2.13](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.12...v0.2.13) (2022-12-07)
|
|
339
357
|
|
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
- [a11y-anchor-has-href-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-anchor-has-href-attribute)
|
|
4
4
|
- [a11y-clickable-element-has-text](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-clickable-element-has-text)
|
|
5
5
|
- [a11y-delegate-element-has-role-presentation](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-delegate-element-has-role-presentation)
|
|
6
|
+
- [a11y-form-control-in-form](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-form-control-in-form)
|
|
6
7
|
- [a11y-heading-in-sectioning-content](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-heading-in-sectioning-content)
|
|
7
8
|
- [a11y-image-has-alt-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-image-has-alt-attribute)
|
|
8
9
|
- [a11y-input-has-name-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-input-has-name-attribute)
|
package/index.js
CHANGED
|
@@ -23,6 +23,23 @@ function generateRulesMap() {
|
|
|
23
23
|
return rulesMap;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
const DISAPPROVE_RULE_NAMES = [
|
|
27
|
+
'a11y-form-control-in-form', // formを使用することの是非について議論中のため
|
|
28
|
+
|
|
29
|
+
// ルールが動作するために設定が必要なものはrecommendedに含めない
|
|
30
|
+
'format-import-path',
|
|
31
|
+
'format-translate-component',
|
|
32
|
+
'jsx-start-with-spread-attributes',
|
|
33
|
+
'no-import-other-domain',
|
|
34
|
+
'prohibit-file-name',
|
|
35
|
+
'prohibit-import',
|
|
36
|
+
'prohibit-path-within-template-literal',
|
|
37
|
+
'require-declaration',
|
|
38
|
+
'require-export',
|
|
39
|
+
'require-import',
|
|
40
|
+
]
|
|
41
|
+
const DISAPPROVE_RULE_NAMES_REGEX = new RegExp(`^(${DISAPPROVE_RULE_NAMES.join('|')})$`)
|
|
42
|
+
|
|
26
43
|
function generateRecommendedConfig(rules) {
|
|
27
44
|
let config = {
|
|
28
45
|
plugins: ['smarthr'],
|
|
@@ -30,7 +47,9 @@ function generateRecommendedConfig(rules) {
|
|
|
30
47
|
};
|
|
31
48
|
|
|
32
49
|
for (let ruleName of Object.keys(rules)) {
|
|
33
|
-
|
|
50
|
+
if (!DISAPPROVE_RULE_NAMES_REGEX.test(ruleName)) {
|
|
51
|
+
config.rules[`smarthr/${ruleName}`] = 'off';
|
|
52
|
+
}
|
|
34
53
|
}
|
|
35
54
|
|
|
36
55
|
return config;
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- URL遷移を行う場合、hrefが設定されていないとキーボード操作やコンテキストメニューからの遷移ができなくなります
|
|
7
7
|
- これらの操作は href属性を参照します
|
|
8
8
|
- 無効化されたリンクであることを表したい場合 `href={undefined}` を設定してください
|
|
9
|
-
- checkTypeオプションに '
|
|
9
|
+
- checkTypeオプションに 'allow-spread-attributes' を指定することで spread attributeが設定されている場合はcorrectに出来ます
|
|
10
10
|
- react-router-dom packageを利用している場合、a要素にto属性が指定されている場合、href属性が指定されているものとして許容します
|
|
11
11
|
- next/link コンポーネント直下のa要素にhref属性が指定されていないことを許容します
|
|
12
12
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
rules: {
|
|
18
18
|
'smarthr/a11y-anchor-has-href-attribute': [
|
|
19
19
|
'error', // 'warn', 'off'
|
|
20
|
-
// { checkType: 'always' } /* 'always' || '
|
|
20
|
+
// { checkType: 'always' } /* 'always' || 'allow-spread-attributes' */
|
|
21
21
|
]
|
|
22
22
|
},
|
|
23
23
|
}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
// react-router-domを利用している場合
|
|
50
50
|
<Link to={hoge}>any</Link>
|
|
51
51
|
|
|
52
|
-
// checkType: '
|
|
52
|
+
// checkType: 'allow-spread-attributes'
|
|
53
53
|
<XxxAnchor {...args} />
|
|
54
54
|
<XxxLink {...args} any="any" />
|
|
55
55
|
```
|
|
@@ -45,7 +45,7 @@ const baseCheck = (node, checkType) => {
|
|
|
45
45
|
if (
|
|
46
46
|
nodeName.match(REGEX_TARGET) &&
|
|
47
47
|
checkExistAttribute(node, findHrefAttribute) &&
|
|
48
|
-
(checkType !== '
|
|
48
|
+
(checkType !== 'allow-spread-attributes' || !node.attributes.some(findSpreadAttr))
|
|
49
49
|
) {
|
|
50
50
|
return nodeName
|
|
51
51
|
}
|
|
@@ -87,7 +87,7 @@ const SCHEMA = [
|
|
|
87
87
|
{
|
|
88
88
|
type: 'object',
|
|
89
89
|
properties: {
|
|
90
|
-
checkType: { type: 'string', enum: ['always', '
|
|
90
|
+
checkType: { type: 'string', enum: ['always', 'allow-spread-attributes'], default: 'always' },
|
|
91
91
|
},
|
|
92
92
|
additionalProperties: false,
|
|
93
93
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# smarthr/a11y-delegate-element-has-role-
|
|
1
|
+
# smarthr/a11y-delegate-element-has-role-presentation
|
|
2
2
|
|
|
3
|
-
- 'role="
|
|
4
|
-
- インタラクティブな要素に対して'role="
|
|
3
|
+
- 'role="presentation"'を適切に設定することを促すルールです
|
|
4
|
+
- インタラクティブな要素に対して'role="presentation"'が設定されている場合、エラーになります
|
|
5
5
|
- インタラクティブな要素とは form, inputなどの入力要素、button, a などのクリッカブルな要素を指します
|
|
6
|
-
- インタラクティブな要素から発生するイベントを親要素でキャッチする場合、親要素に 'role="
|
|
7
|
-
- インタラクティブではない要素でイベントをキャッチしており、かつ'role="
|
|
6
|
+
- インタラクティブな要素から発生するイベントを親要素でキャッチする場合、親要素に 'role="presentation"' を設定することを促します
|
|
7
|
+
- インタラクティブではない要素でイベントをキャッチしており、かつ'role="presentation"'を設定しているにも関わらず、子要素にインタラクティブな要素がない場合はエラーになります
|
|
8
8
|
- additionalInteractiveComponentRegexオプションに独自コンポーネントの名称を正規表現で設定することで、インタラクティブな要素として判定することが可能です
|
|
9
9
|
|
|
10
10
|
## rules
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
```js
|
|
13
13
|
{
|
|
14
14
|
rules: {
|
|
15
|
-
'smarthr/a11y-delegate-element-has-role-
|
|
15
|
+
'smarthr/a11y-delegate-element-has-role-presentation': [
|
|
16
16
|
'error', // 'warn', 'off'
|
|
17
17
|
// { additionalInteractiveComponentRegex: ['^InteractiveComponent%'] }
|
|
18
18
|
]
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
## ❌ Incorrect
|
|
24
24
|
|
|
25
25
|
```jsx
|
|
26
|
-
// インタラクティブな要素に対して role="
|
|
27
|
-
<Button role="
|
|
28
|
-
<input type="text" role="
|
|
26
|
+
// インタラクティブな要素に対して role="presentation" は設定できない
|
|
27
|
+
<Button role="presentation">text.</Button>
|
|
28
|
+
<input type="text" role="presentation" />
|
|
29
29
|
|
|
30
30
|
// インタラクティブな要素で発生するイベントを非インタラクティブな要素でキャッチする場合
|
|
31
|
-
// role="
|
|
31
|
+
// role="presentation" を設定する必要がある
|
|
32
32
|
<div onClick={hoge}>
|
|
33
33
|
<Button>text.</Button>
|
|
34
34
|
</div>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
```jsx
|
|
46
46
|
// インタラクティブな要素で発生するイベントを非インタラクティブな要素でキャッチする場合
|
|
47
|
-
// role="
|
|
47
|
+
// role="presentation" を設定する
|
|
48
48
|
<div onClick={hoge} role="presentation">
|
|
49
49
|
<Button>text.</Button>
|
|
50
50
|
</div>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# smarthr/a11y-form-control-in-form
|
|
2
|
+
|
|
3
|
+
- fieldset, Fieldset, FormControl を利用する場合、form要素で囲むことを促すルールです
|
|
4
|
+
- form要素で囲むことで以下のようなメリットがあります
|
|
5
|
+
- 適切にマークアップできるようになり、フォームの範囲などがスクリーンリーダーに正しく伝わる
|
|
6
|
+
- 入力要素にfocusした状態でEnterを押せばフォームをsubmitできる
|
|
7
|
+
- inputのrequired属性、pattern属性を利用した入力チェックをブラウザの機能として実行できる
|
|
8
|
+
- smarthr/a11y-input-in-form-control と組み合わせることでより厳密なフォームのマークアップを行えます
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## rules
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
{
|
|
15
|
+
rules: {
|
|
16
|
+
'smarthr/a11y-form-control-in-form': 'error', // 'warn', 'off'
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## ❌ Incorrect
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
// formで囲まれていないためNG
|
|
25
|
+
const AnyComponent = <>
|
|
26
|
+
<FormControl />
|
|
27
|
+
<HogeFieldset />
|
|
28
|
+
<fieldset />
|
|
29
|
+
</>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## ✅ Correct
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
// formで囲まれているためOK
|
|
36
|
+
const AnyComponent = <StyledForm>
|
|
37
|
+
<FormControl />
|
|
38
|
+
<HogeFieldset />
|
|
39
|
+
<fieldset />
|
|
40
|
+
</StyledForm>
|
|
41
|
+
const AnyComponent = <Hoge as="form">
|
|
42
|
+
<FormControl />
|
|
43
|
+
<HogeFieldset />
|
|
44
|
+
<fieldset />
|
|
45
|
+
</Hoge>
|
|
46
|
+
const AnyComponent = <Hoge forwardedAs="form">
|
|
47
|
+
<FormControl />
|
|
48
|
+
<HogeFieldset />
|
|
49
|
+
<fieldset />
|
|
50
|
+
</Hoge>
|
|
51
|
+
|
|
52
|
+
// Dialogの場合、FormDialog・RemoteTriggerFormDialogで囲めばOK
|
|
53
|
+
const AnyComponent = <FormDialog>
|
|
54
|
+
<FugaFormControl />
|
|
55
|
+
</FormDialog>
|
|
56
|
+
const AnyComponent = <RemoteTriggerAnyFormDialog>
|
|
57
|
+
<FugaFormControl />
|
|
58
|
+
</RemoteTriggerAnyFormDialog>
|
|
59
|
+
|
|
60
|
+
// 対象のFormControl、Fieldsetがコンポーネントの一要素であり、その親コンポーネント名がFormControl、もしくはFieldsetの場合OK
|
|
61
|
+
const AnyFormControl = <>
|
|
62
|
+
<StyledFormControl />
|
|
63
|
+
</>
|
|
64
|
+
const AnyFieldset = <>
|
|
65
|
+
<StyledFieldset />
|
|
66
|
+
</>
|
|
67
|
+
```
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const { generateTagFormatter } = require('../../libs/format_styled_components')
|
|
2
|
+
|
|
3
|
+
const FIELDSET_EXPECTED_NAMES = {
|
|
4
|
+
'((F|^f)ieldset)$': '(Fieldset)$',
|
|
5
|
+
'(Fieldsets)$': '(Fieldsets)$',
|
|
6
|
+
}
|
|
7
|
+
const FORM_CONTROL_EXPECTED_NAMES = {
|
|
8
|
+
...FIELDSET_EXPECTED_NAMES,
|
|
9
|
+
'(FormGroup)$': '(FormGroup)$',
|
|
10
|
+
'(FormControl)$': '(FormControl)$',
|
|
11
|
+
'(FormControls)$': '(FormControls)$',
|
|
12
|
+
}
|
|
13
|
+
const FORM_EXPECTED_NAMES = {
|
|
14
|
+
'((F|^f)orm)$': '(Form)$',
|
|
15
|
+
'(FormDialog)$': '(FormDialog)$',
|
|
16
|
+
'RemoteTrigger(.*)FormDialog$': 'RemoteTrigger(.*)FormDialog$',
|
|
17
|
+
}
|
|
18
|
+
const EXPECTED_NAMES = {
|
|
19
|
+
...FORM_CONTROL_EXPECTED_NAMES,
|
|
20
|
+
...FORM_EXPECTED_NAMES,
|
|
21
|
+
}
|
|
22
|
+
const UNEXPECTED_NAMES = EXPECTED_NAMES
|
|
23
|
+
|
|
24
|
+
const targetRegex = new RegExp(`(${Object.keys(FORM_CONTROL_EXPECTED_NAMES).join('|')})`)
|
|
25
|
+
const wrapperRegex = new RegExp(`(${Object.keys(EXPECTED_NAMES).join('|')})`)
|
|
26
|
+
const ignoreCheckParentTypeRegex = /^(Program|ExportNamedDeclaration)$/
|
|
27
|
+
const messageFieldset = `(${Object.values(FORM_CONTROL_EXPECTED_NAMES).join('|')})`
|
|
28
|
+
const declaratorTargetRegex = new RegExp(messageFieldset)
|
|
29
|
+
const asRegex = /^(as|forwardedAs)$/
|
|
30
|
+
const bareTagRegex = /^(form|fieldset)$/
|
|
31
|
+
|
|
32
|
+
const includeAsAttrFormOrFieldset = (a) => a.name?.name.match(asRegex) && a.value.value.match(bareTagRegex)
|
|
33
|
+
|
|
34
|
+
const searchBubbleUp = (node) => {
|
|
35
|
+
switch (node.type) {
|
|
36
|
+
case 'Program':
|
|
37
|
+
// rootまで検索した場合は確定でエラーにする
|
|
38
|
+
return null
|
|
39
|
+
case 'JSXElement':
|
|
40
|
+
// formかFieldsetでラップされていればOK
|
|
41
|
+
if (node.openingElement.name.name && (wrapperRegex.test(node.openingElement.name.name) || node.openingElement.attributes.some(includeAsAttrFormOrFieldset))) {
|
|
42
|
+
return node
|
|
43
|
+
}
|
|
44
|
+
break
|
|
45
|
+
case 'VariableDeclarator':
|
|
46
|
+
// FormControl系コンポーネントの拡張の場合は対象外
|
|
47
|
+
if (ignoreCheckParentTypeRegex.test(node.parent.parent?.type) && declaratorTargetRegex.test(node.id.name)) {
|
|
48
|
+
return node
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
break
|
|
52
|
+
case 'FunctionDeclaration':
|
|
53
|
+
case 'ClassDeclaration':
|
|
54
|
+
// FormControl系コンポーネントの拡張の場合は対象外
|
|
55
|
+
if (ignoreCheckParentTypeRegex.test(node.parent.type) && declaratorTargetRegex.test(node.id.name)) {
|
|
56
|
+
return node
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return searchBubbleUp(node.parent)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
meta: {
|
|
67
|
+
type: 'problem',
|
|
68
|
+
schema: [],
|
|
69
|
+
},
|
|
70
|
+
create(context) {
|
|
71
|
+
return {
|
|
72
|
+
...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
|
|
73
|
+
JSXOpeningElement: (node) => {
|
|
74
|
+
const elementName = node.name.name
|
|
75
|
+
|
|
76
|
+
if (elementName && targetRegex.test(elementName)) {
|
|
77
|
+
const result = searchBubbleUp(node.parent.parent)
|
|
78
|
+
|
|
79
|
+
if (!result) {
|
|
80
|
+
context.report({
|
|
81
|
+
node,
|
|
82
|
+
message: `${elementName}をform要素で囲むようにマークアップしてください。
|
|
83
|
+
- form要素で囲むことでスクリーンリーダーに入力フォームであることが正しく伝わる、入力要素にfocusした状態でEnterを押せばsubmitできる、inputのpattern属性を利用できるなどのメリットがあります
|
|
84
|
+
- 以下のいずれかの方法で修正をおこなってください
|
|
85
|
+
- 方法1: form要素で ${elementName} を囲んでください。smarthr-ui/ActionDialog、もしくはsmarthr-ui/RemoteTriggerActionDialogを利用している場合、smarthr-ui/FormDialog、smarthr-ui/RemoteTriggerFormDialogに置き換えてください
|
|
86
|
+
- 方法2: ${elementName} がコンポーネント内の一要素であり、かつその親コンポーネントがFormControl、もしくはFieldsetを表現するものである場合、親コンポーネント名を "${messageFieldset}" とマッチするものに変更してください`,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
module.exports.schema = []
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# smarthr/a11y-image-has-alt-attribute
|
|
2
2
|
|
|
3
3
|
- 画像やアイコンにalt属性を設定することを強制するルールです
|
|
4
|
-
- checkTypeオプションに '
|
|
4
|
+
- checkTypeオプションに 'allow-spread-attributes' を指定することで spread attributeが設定されている場合はcorrectに出来ます。
|
|
5
5
|
|
|
6
6
|
## rules
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
rules: {
|
|
11
11
|
'smarthr/a11y-image-has-alt-attribute': [
|
|
12
12
|
'error', // 'warn', 'off'
|
|
13
|
-
// { checkType: 'always' } /* 'always' || '
|
|
13
|
+
// { checkType: 'always' } /* 'always' || 'allow-spread-attributes' */
|
|
14
14
|
]
|
|
15
15
|
},
|
|
16
16
|
}
|
|
@@ -56,7 +56,7 @@ const StyledPiyo = styled(Icon)``
|
|
|
56
56
|
<Icon alt="message" />
|
|
57
57
|
```
|
|
58
58
|
```jsx
|
|
59
|
-
// checkType: '
|
|
59
|
+
// checkType: 'allow-spread-attributes'
|
|
60
60
|
<XxxImage {...args} />
|
|
61
61
|
<YyyIcon {...args} any="any" />
|
|
62
62
|
```
|
|
@@ -42,7 +42,7 @@ const SCHEMA = [
|
|
|
42
42
|
{
|
|
43
43
|
type: 'object',
|
|
44
44
|
properties: {
|
|
45
|
-
checkType: { type: 'string', enum: ['always', '
|
|
45
|
+
checkType: { type: 'string', enum: ['always', 'allow-spread-attributes'], default: 'always' },
|
|
46
46
|
},
|
|
47
47
|
additionalProperties: false,
|
|
48
48
|
}
|
|
@@ -76,7 +76,7 @@ module.exports = {
|
|
|
76
76
|
!isWithinSvgJsxElement(node.parent)
|
|
77
77
|
) &&
|
|
78
78
|
(
|
|
79
|
-
checkType !== '
|
|
79
|
+
checkType !== 'allow-spread-attributes' ||
|
|
80
80
|
!node.attributes.some(findSpreadAttr)
|
|
81
81
|
)
|
|
82
82
|
) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- 入力要素は name を設定することでブラウザの補完機能が有効になる可能性が高まります。
|
|
5
5
|
- 補完機能はブラウザによって異なるため、補完される可能性が上がるよう、name には半角英数の小文字・大文字と一部記号(`_ , [, ]`)のみ利用可能です。
|
|
6
6
|
- input[type="radio"] は name を適切に設定することでラジオグループが確立され、キーボード操作しやすくなる等のメリットがあります。
|
|
7
|
-
- checkTypeオプションに '
|
|
7
|
+
- checkTypeオプションに 'allow-spread-attributes' を指定することで spread attributeが設定されている場合はcorrectに出来ます。
|
|
8
8
|
|
|
9
9
|
## rules
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
rules: {
|
|
14
14
|
'smarthr/a11y-input-has-name-attribute': [
|
|
15
15
|
'error', // 'warn', 'off'
|
|
16
|
-
// { checkType: 'always' } /* 'always' || '
|
|
16
|
+
// { checkType: 'always' } /* 'always' || 'allow-spread-attributes' */
|
|
17
17
|
]
|
|
18
18
|
},
|
|
19
19
|
}
|
|
@@ -51,7 +51,7 @@ const StyledPiyo = styled(RadioButton)``;
|
|
|
51
51
|
<Textarea name="some" />
|
|
52
52
|
<Select name="piyo" />
|
|
53
53
|
|
|
54
|
-
// checkType: '
|
|
54
|
+
// checkType: 'allow-spread-attributes'
|
|
55
55
|
<AnyInput {...args} />
|
|
56
56
|
<AnyInput {...args} any="any" />
|
|
57
57
|
```
|
|
@@ -34,7 +34,7 @@ const SCHEMA = [
|
|
|
34
34
|
{
|
|
35
35
|
type: 'object',
|
|
36
36
|
properties: {
|
|
37
|
-
checkType: { type: 'string', enum: ['always', '
|
|
37
|
+
checkType: { type: 'string', enum: ['always', 'allow-spread-attributes'], default: 'always' },
|
|
38
38
|
},
|
|
39
39
|
additionalProperties: false,
|
|
40
40
|
}
|
|
@@ -60,7 +60,7 @@ module.exports = {
|
|
|
60
60
|
if (!nameAttr) {
|
|
61
61
|
if (
|
|
62
62
|
node.attributes.length === 0 ||
|
|
63
|
-
checkType !== '
|
|
63
|
+
checkType !== 'allow-spread-attributes' ||
|
|
64
64
|
!node.attributes.some(findSpreadAttr)
|
|
65
65
|
) {
|
|
66
66
|
const isRadio =
|
|
@@ -37,7 +37,7 @@ ruleTester.run('a11y-anchor-has-href-attribute', rule, {
|
|
|
37
37
|
{ code: `<HogeAnchor href={hoge}>ほげ</HogeAnchor>` },
|
|
38
38
|
{ code: `<Link href="hoge">ほげ</Link>` },
|
|
39
39
|
{ code: `<Link href="#fuga">ほげ</Link>` },
|
|
40
|
-
{ code: '<AnyAnchor {...args1} />', options: [{ checkType: '
|
|
40
|
+
{ code: '<AnyAnchor {...args1} />', options: [{ checkType: 'allow-spread-attributes' }] },
|
|
41
41
|
],
|
|
42
42
|
invalid: [
|
|
43
43
|
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const rule = require('../rules/a11y-form-control-in-form')
|
|
2
|
+
const RuleTester = require('eslint').RuleTester
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester({
|
|
5
|
+
parserOptions: {
|
|
6
|
+
ecmaVersion: 2018,
|
|
7
|
+
ecmaFeatures: {
|
|
8
|
+
experimentalObjectRestSpread: true,
|
|
9
|
+
jsx: true,
|
|
10
|
+
},
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const generateErrorText = (elementName) => `${elementName}をform要素で囲むようにマークアップしてください。
|
|
16
|
+
- form要素で囲むことでスクリーンリーダーに入力フォームであることが正しく伝わる、入力要素にfocusした状態でEnterを押せばsubmitできる、inputのpattern属性を利用できるなどのメリットがあります
|
|
17
|
+
- 以下のいずれかの方法で修正をおこなってください
|
|
18
|
+
- 方法1: form要素で ${elementName} を囲んでください。smarthr-ui/ActionDialog、もしくはsmarthr-ui/RemoteTriggerActionDialogを利用している場合、smarthr-ui/FormDialog、smarthr-ui/RemoteTriggerFormDialogに置き換えてください
|
|
19
|
+
- 方法2: ${elementName} がコンポーネント内の一要素であり、かつその親コンポーネントがFormControl、もしくはFieldsetを表現するものである場合、親コンポーネント名を "((Fieldset)$|(Fieldsets)$|(FormGroup)$|(FormControl)$|(FormControls)$)" とマッチするものに変更してください`
|
|
20
|
+
|
|
21
|
+
ruleTester.run('a11y-form-control-in-form', rule, {
|
|
22
|
+
valid: [
|
|
23
|
+
{ code: '<input />' },
|
|
24
|
+
{ code: '<Select />' },
|
|
25
|
+
{ code: '<form><FormControl /></form>' },
|
|
26
|
+
{ code: '<Form><fieldset /></Form>' },
|
|
27
|
+
{ code: '<StyledForm><AnyFieldset /></StyledForm>' },
|
|
28
|
+
{ code: 'const HogeFormControl = <><AnyFormControl /></>' },
|
|
29
|
+
{ code: 'const HogeFieldset = <><AnyFieldset /></>' },
|
|
30
|
+
],
|
|
31
|
+
invalid: [
|
|
32
|
+
{ code: '<FormControl />', errors: [ { message: generateErrorText('FormControl') } ] },
|
|
33
|
+
{ code: '<fieldset />', errors: [ { message: generateErrorText('fieldset') } ] },
|
|
34
|
+
{ code: '<AnyFieldset />', errors: [ { message: generateErrorText('AnyFieldset') } ] },
|
|
35
|
+
{ code: 'const Hoge = <><AnyFormControl /></>', errors: [ { message: generateErrorText('AnyFormControl') } ] },
|
|
36
|
+
]
|
|
37
|
+
})
|
|
@@ -45,7 +45,7 @@ ruleTester.run('a11y-image-has-alt-attribute', rule, {
|
|
|
45
45
|
{ code: '<HogeImage aria-describedby="hoge" />' },
|
|
46
46
|
{ code: '<HogeImage aria-describedby="hoge" alt="fuga" />' },
|
|
47
47
|
{ code: '<svg><image /></svg>' },
|
|
48
|
-
{ code: '<AnyImg {...hoge} />', options: [{ checkType: '
|
|
48
|
+
{ code: '<AnyImg {...hoge} />', options: [{ checkType: 'allow-spread-attributes' }] },
|
|
49
49
|
],
|
|
50
50
|
invalid: [
|
|
51
51
|
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
|
|
@@ -46,9 +46,9 @@ ruleTester.run('a11y-input-has-name-attribute', rule, {
|
|
|
46
46
|
{ code: '<HogeTextarea name="hoge" />' },
|
|
47
47
|
{ code: '<select name="hoge" />' },
|
|
48
48
|
{ code: '<Select name="hoge[0][Fuga]" />' },
|
|
49
|
-
{ code: '<Input {...hoge} />', options: [{ checkType: '
|
|
50
|
-
{ code: '<Input {...args1} {...args2} />', options: [{ checkType: '
|
|
51
|
-
{ code: '<Input {...args} hoge="fuga" />', options: [{ checkType: '
|
|
49
|
+
{ code: '<Input {...hoge} />', options: [{ checkType: 'allow-spread-attributes' }] },
|
|
50
|
+
{ code: '<Input {...args1} {...args2} />', options: [{ checkType: 'allow-spread-attributes' }] },
|
|
51
|
+
{ code: '<Input {...args} hoge="fuga" />', options: [{ checkType: 'allow-spread-attributes' }] },
|
|
52
52
|
],
|
|
53
53
|
invalid: [
|
|
54
54
|
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
|