eslint-plugin-smarthr 4.0.2 → 6.0.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/CHANGELOG.md +23 -0
- package/README.md +2 -2
- package/package.json +2 -2
- package/rules/a11y-anchor-has-href-attribute/README.md +68 -15
- package/rules/a11y-clickable-element-has-text/README.md +76 -31
- package/rules/a11y-form-control-in-form/README.md +184 -41
- package/rules/best-practice-for-interactive-element/README.md +178 -0
- package/rules/best-practice-for-interactive-element/index.js +82 -0
- package/rules/best-practice-for-spread-syntax/README.md +5 -5
- package/test/best-practice-for-interactive-element.js +71 -0
- package/rules/a11y-delegate-element-has-role-presentation/README.md +0 -55
- package/rules/a11y-delegate-element-has-role-presentation/index.js +0 -225
- package/test/a11y-delegate-element-has-role-presentation.js +0 -76
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
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
|
+
## [6.0.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v5.0.0...eslint-plugin-smarthr-v6.0.0) (2026-01-28)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
* **a11y-delegate-element-has-role-presentation:** ルールを削除します ([#1015](https://github.com/kufu/tamatebako/issues/1015))
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* **a11y-delegate-element-has-role-presentation:** ルールを削除します ([#1015](https://github.com/kufu/tamatebako/issues/1015)) ([4c5c7ab](https://github.com/kufu/tamatebako/commit/4c5c7ab43ab7a52f749963aa7b7cb359e6f623d5))
|
|
15
|
+
* best-practice-for-interactive-elementを追加 ([#1014](https://github.com/kufu/tamatebako/issues/1014)) ([e0a171b](https://github.com/kufu/tamatebako/commit/e0a171b056d8dbe775e3697633136e60c84afe5d))
|
|
16
|
+
|
|
17
|
+
## [5.0.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v4.0.2...eslint-plugin-smarthr-v5.0.0) (2026-01-08)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### ⚠ BREAKING CHANGES
|
|
21
|
+
|
|
22
|
+
* **best-practice-for-spread-syntax:** best-practice-for-spread-syntaxのチェック対象をObject内のスプレッド構文に広げ、autofixするように修正 ([#1009](https://github.com/kufu/tamatebako/issues/1009))
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* **best-practice-for-spread-syntax:** best-practice-for-spread-syntaxのチェック対象をObject内のスプレッド構文に広げ、autofixするように修正 ([#1009](https://github.com/kufu/tamatebako/issues/1009)) ([d334970](https://github.com/kufu/tamatebako/commit/d33497002710301b738f7e16382540a5ba35d36f))
|
|
27
|
+
|
|
5
28
|
## [4.0.2](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v4.0.1...eslint-plugin-smarthr-v4.0.2) (2026-01-07)
|
|
6
29
|
|
|
7
30
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
- [a11y-anchor-has-href-attribute](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-anchor-has-href-attribute)
|
|
4
4
|
- [a11y-clickable-element-has-text](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-clickable-element-has-text)
|
|
5
|
-
- [a11y-delegate-element-has-role-presentation](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-delegate-element-has-role-presentation)
|
|
6
5
|
- [a11y-form-control-in-form](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-form-control-in-form)
|
|
7
6
|
- [a11y-heading-in-sectioning-content](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-heading-in-sectioning-content)
|
|
8
7
|
- [a11y-help-link-with-support-href](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-help-link-with-support-href)
|
|
@@ -19,19 +18,20 @@
|
|
|
19
18
|
- [best-practice-for-button-element](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-button-element)
|
|
20
19
|
- [best-practice-for-data-test-attribute](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-data-test-attribute)
|
|
21
20
|
- [best-practice-for-date](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-date)
|
|
21
|
+
- [best-practice-for-interactive-element](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-interactive-element)
|
|
22
22
|
- [best-practice-for-layouts](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts)
|
|
23
23
|
- [best-practice-for-nested-attributes-array-index](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-nested-attributes-array-index)
|
|
24
24
|
- [best-practice-for-optional-chaining](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-optional-chaining)
|
|
25
25
|
- [best-practice-for-prohibit-import-smarthr-ui-local](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-prohibit-import-smarthr-ui-local)
|
|
26
26
|
- [best-practice-for-remote-trigger-dialog](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-remote-trigger-dialog)
|
|
27
27
|
- [best-practice-for-rest-parameters](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-rest-parameters)
|
|
28
|
+
- [best-practice-for-spread-syntax](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-spread-syntax)
|
|
28
29
|
- [best-practice-for-tailwind-prohibit-root-margin](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-prohibit-root-margin)
|
|
29
30
|
- [best-practice-for-tailwind-variants](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-variants)
|
|
30
31
|
- [best-practice-for-unnesessary-early-return](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-unnesessary-early-return)
|
|
31
32
|
- [design-system-guideline-prohibit-double-icons](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/design-system-guideline-prohibit-double-icons)
|
|
32
33
|
- [format-import-path](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/format-import-path)
|
|
33
34
|
- [format-translate-component](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/format-translate-component)
|
|
34
|
-
- [best-practice-for-spread-syntax](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-spread-syntax)
|
|
35
35
|
- [no-import-other-domain](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/no-import-other-domain)
|
|
36
36
|
- [prohibit-export-array-type](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/prohibit-export-array-type)
|
|
37
37
|
- [prohibit-file-name](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/prohibit-file-name)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-smarthr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"author": "SmartHR",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A sharable ESLint plugin for SmartHR",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"eslintplugin",
|
|
38
38
|
"smarthr"
|
|
39
39
|
],
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "d9b333ac3b44127f7db54408dfa84ce49682a59e"
|
|
41
41
|
}
|
|
@@ -1,14 +1,56 @@
|
|
|
1
1
|
# smarthr/a11y-anchor-has-href-attribute
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
a, Anchor, Link コンポーネントに href 属性を設定することを促すルールです
|
|
4
|
+
|
|
5
|
+
## なぜa要素にhref属性を設定するべきなのか
|
|
6
|
+
|
|
7
|
+
href属性が設定されていないa要素は**遷移先が存在しない無効化されたリンク**という扱いになります。
|
|
8
|
+
これはbutton要素で例えるなら**disalbed属性が設定された状態のbutton要素**と同等です。
|
|
9
|
+
|
|
10
|
+
またhref属性がないa要素ではtab移動の対象にならない・コンテキストメニュー(右クリックメニュー)から別タブで開く機能が使えない、などのデメリットが発生します。
|
|
11
|
+
これらの機能は **href属性が存在する == 有効なリンクである** ことから有効になります。
|
|
12
|
+
|
|
13
|
+
逆説的に**設定したいhref属性が存在しない場合、a要素ではなくbutton要素を利用する**ように心がけてください。
|
|
14
|
+
a要素はあくまでURL遷移を表現するための要素であるため、それ以外の処理のトリガーとして利用するならばbutton要素のほうが適切です。
|
|
15
|
+
|
|
16
|
+
また**遷移先が存在しない無効化されたリンク**を表現したい場合、明示的に`href={undefined}`を設定することを推奨しています。
|
|
17
|
+
|
|
18
|
+
## react-router, next/Linkを利用している場合
|
|
19
|
+
|
|
20
|
+
react-router, next/Linkを利用している場合、href属性と同等の機能を提供する別属性が存在するため、自動的にチェック方法が切り替わります。
|
|
21
|
+
利用しているか否かの判定にはpackage.jsonが利用され、dependenciesに `react-router` もしくは `next` が存在するかどうかで判断されます。
|
|
22
|
+
|
|
23
|
+
### react-routerを利用している場合
|
|
24
|
+
|
|
25
|
+
a要素にto属性が指定されている場合、href属性が指定されているものとして許容します。
|
|
26
|
+
|
|
27
|
+
```jsx
|
|
28
|
+
// react-routerを利用している場合、かつto属性を設定しているためOK
|
|
29
|
+
<Link to={hoge}>any</Link>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### next/Linkを利用している場合
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
next/link コンポーネント直下のa要素にhref属性が指定されていないことを許容します。
|
|
36
|
+
|
|
37
|
+
```jsx
|
|
38
|
+
// next/Linkを利用している場合、子のaにhref属性がなくてもOK
|
|
39
|
+
<Link href={hoge}><a>any</a></Link>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### spread-attributesが設定されているなら許容したい場合
|
|
43
|
+
|
|
44
|
+
下記の様にspread attributesが設定されていれば、href属性が設定されている扱いにしたい場合、lintのoptionとして `checkType` に `allow-spread-attributes` を設定してください。
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
// checkType: 'allow-spread-attributes'
|
|
48
|
+
<XxxAnchor {...args} />
|
|
49
|
+
<XxxLink {...args} any="any" />
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
便利な設定ではありますが、**href属性が実際に設定されているかは判定出来ていないため、チェック漏れが発生する可能性があります。**
|
|
53
|
+
設定される場合は慎重に検討してください。
|
|
12
54
|
|
|
13
55
|
## rules
|
|
14
56
|
|
|
@@ -26,12 +68,15 @@
|
|
|
26
68
|
## ❌ Incorrect
|
|
27
69
|
|
|
28
70
|
```jsx
|
|
71
|
+
// a要素と思われるコンポーネントにhref属性が設定されていないためNG
|
|
29
72
|
<a>any</a>
|
|
30
73
|
<XxxAnchor>any</XxxAnchor>
|
|
31
74
|
<XxxLink>any</XxxLink>
|
|
32
75
|
<XxxLink href>any</XxxLink>
|
|
76
|
+
```
|
|
33
77
|
|
|
34
|
-
|
|
78
|
+
```jsx
|
|
79
|
+
// spread attributesでhref属性が含まれていてもデフォルト設定ではNGになる
|
|
35
80
|
<XxxAnchor {...args} />
|
|
36
81
|
<XxxLink {...args} any="any" />
|
|
37
82
|
```
|
|
@@ -39,17 +84,25 @@
|
|
|
39
84
|
## ✅ Correct
|
|
40
85
|
|
|
41
86
|
```jsx
|
|
87
|
+
// a要素と思われるコンポーネントにhref属性が設定されているのでOK
|
|
42
88
|
<a href="https://www.google.com/search">any</a>
|
|
43
89
|
<XxxAnchor href={hoge}>any</XxxAnchor>
|
|
44
90
|
<XxxLink href={undefined}>any</XxxLink>
|
|
91
|
+
```
|
|
45
92
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// react-router-domを利用している場合
|
|
93
|
+
```jsx
|
|
94
|
+
// react-routerを利用している場合、かつto属性を設定しているためOK
|
|
50
95
|
<Link to={hoge}>any</Link>
|
|
96
|
+
```
|
|
51
97
|
|
|
52
|
-
|
|
98
|
+
```jsx
|
|
99
|
+
// next/Linkを利用している場合、子のaにhref属性がなくてもOK
|
|
100
|
+
<Link href={hoge}><a>any</a></Link>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```jsx
|
|
104
|
+
// checkType: 'allow-spread-attributes' を指定している場合、
|
|
105
|
+
// 仮にspread attributes内にhrefが含まれていなくてもOKになるため注意
|
|
53
106
|
<XxxAnchor {...args} />
|
|
54
107
|
<XxxLink {...args} any="any" />
|
|
55
108
|
```
|
|
@@ -1,11 +1,73 @@
|
|
|
1
1
|
# smarthr/a11y-clickable-element-has-text
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
ButtonやAnchor,Link コンポーネントなどクリック可能(クリッカブル)な要素にテキストを設定することを促すルールです。
|
|
4
|
+
|
|
5
|
+
## なぜクリッカブルな要素にテキストが必要なのか
|
|
6
|
+
|
|
7
|
+
スクリーンリーダーなどの一部のブラウザで**クリックした対象物が何であるか?という情報が欠落することを防ぐ**目的があります。
|
|
8
|
+
閲覧可能なUI上では十分な情報が存在していても、テキスト読み上げなどでは情報が不足することがあるため、**クリックすることで起きる内容を説明する必要があります。**
|
|
9
|
+
|
|
10
|
+
## 適切なテキストの設定方法について
|
|
11
|
+
|
|
12
|
+
基本的にはchildrenにテキストを設定してください。
|
|
13
|
+
|
|
14
|
+
```jsx
|
|
15
|
+
<button>アクション</button>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
変数を設定した場合もテキストを設定したものとして扱われます
|
|
19
|
+
|
|
20
|
+
```jsx
|
|
21
|
+
<AnyAnchor>{hoge}</AnyAnchor>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 画像、もしくはそれに類する要素の場合
|
|
25
|
+
|
|
26
|
+
画像要素の場合は `alt` 等、代替テキストを設定してください。
|
|
27
|
+
|
|
28
|
+
```jsx
|
|
29
|
+
// 代替テキストを設定した画像を含むためOK
|
|
30
|
+
<AnyLink>
|
|
31
|
+
<XxxImage alt="fuga" />
|
|
32
|
+
</AnyLink>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
svg要素など、画像ではないが画像として扱いたい要素の場合、`role="img"` `aria-label="任意の文字列"` を指定することで**代替テキストが設定された画像**と同等に扱われるようになります。
|
|
36
|
+
|
|
37
|
+
```jsx
|
|
38
|
+
// svgだが代替テキストを設定した画像として扱うためOK
|
|
39
|
+
<AnyButton>
|
|
40
|
+
<svg role="img" aria-label="HOGE">...</svg>
|
|
41
|
+
</AnyButton>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 対象要素がそもそもテキストを内包している場合
|
|
45
|
+
|
|
46
|
+
a, button要素を拡張したコンポーネントがテキストを適切に内包している場合、childrenを設定しない状態で利用すればこのルールはcorrectとして扱います。
|
|
47
|
+
|
|
48
|
+
```jsx
|
|
49
|
+
// childrenが存在しないため、コンポーネントがテキストを内包しているものとして扱うのでOK
|
|
50
|
+
<HogeAnchor />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
またchildrenにわたすコンポーネントがテキストを内包している場合、chilrenに渡すコンポーネントの名称のsuffixが `Text` になっていれば、correctとして扱われます。
|
|
54
|
+
|
|
55
|
+
```jsx
|
|
56
|
+
// children内に XxxText 形式のコンポーネントが渡されているのでOK
|
|
57
|
+
<AnyLink>
|
|
58
|
+
<HogeText />
|
|
59
|
+
</AnyLink>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
childrenにわたすコンポーネントの名称を変更することが難しい場合、lintのoptionとして `componentsWithText` に配列として完全一致するコンポーネント名を設定すれば、そのコンポーネントはテキストを含むものとして扱われます。
|
|
63
|
+
|
|
64
|
+
```jsx
|
|
65
|
+
// componentsWithText: ['Hoge'] を設定している場合
|
|
66
|
+
// Hogeコンポーネントはテキストを含むものとして扱われるのでOK
|
|
67
|
+
<XxxButton>
|
|
68
|
+
<Hoge />
|
|
69
|
+
</XxxButton>
|
|
70
|
+
```
|
|
9
71
|
|
|
10
72
|
## rules
|
|
11
73
|
|
|
@@ -25,24 +87,16 @@
|
|
|
25
87
|
## ❌ Incorrect
|
|
26
88
|
|
|
27
89
|
```jsx
|
|
90
|
+
// テキストとみなされるものがchildrenに存在しないためNG
|
|
28
91
|
<XxxAnchor>
|
|
29
92
|
<Xxx />
|
|
30
93
|
</XxxAnchor>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
```jsx
|
|
34
94
|
<XxxLink>
|
|
35
95
|
<Yyy />
|
|
36
96
|
</XxxLink>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
```jsx
|
|
40
97
|
<XxxButton>
|
|
41
98
|
<Zzz />
|
|
42
99
|
</XxxButton>
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
```jsx
|
|
46
100
|
<XxxAnchor>
|
|
47
101
|
<XxxTextYyyy />
|
|
48
102
|
</XxxAnchor>
|
|
@@ -51,49 +105,40 @@
|
|
|
51
105
|
## ✅ Correct
|
|
52
106
|
|
|
53
107
|
```jsx
|
|
108
|
+
// テキストがchildrenに含まれるためOK
|
|
54
109
|
<XxxAnchor>
|
|
55
110
|
Hoge
|
|
56
111
|
</XxxAnchor>
|
|
57
112
|
```
|
|
58
113
|
```jsx
|
|
114
|
+
// テキスト以外が同時に含まれている場合もOK
|
|
59
115
|
<XxxLink>
|
|
60
116
|
<YyyIcon />
|
|
61
117
|
Fuga
|
|
62
118
|
</XxxLink>
|
|
63
119
|
```
|
|
64
120
|
```jsx
|
|
65
|
-
|
|
66
|
-
<YyyIcon visuallyHiddenText="hoge" />
|
|
67
|
-
</XxxAnchor>
|
|
68
|
-
```
|
|
69
|
-
```jsx
|
|
121
|
+
// 画像・Iconの場合、代替テキストが指定されていればテキストを含むものとして扱われるためOK
|
|
70
122
|
<XxxButton>
|
|
71
123
|
<YyyImage alt="fuga" />
|
|
72
124
|
</XxxButton>
|
|
73
125
|
```
|
|
74
126
|
|
|
75
127
|
```jsx
|
|
128
|
+
// childrenが存在しないコンポーネントの場合、テキストを内包するものとして扱われるためOK
|
|
76
129
|
<YyyAnchor />
|
|
77
130
|
```
|
|
78
131
|
|
|
79
132
|
```jsx
|
|
133
|
+
// childrenに含むコンポーネント名が、TextがsuffixになっているためOK
|
|
80
134
|
<XxxAnchor>
|
|
81
135
|
<XxxText />
|
|
82
136
|
</XxxAnchor>
|
|
83
137
|
```
|
|
84
138
|
|
|
85
139
|
```jsx
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'smarthr/a11y-clickable-element-has-text': [
|
|
89
|
-
'error',
|
|
90
|
-
{
|
|
91
|
-
componentsWithText: ['Hoge'],
|
|
92
|
-
},
|
|
93
|
-
]
|
|
94
|
-
},
|
|
95
|
-
*/
|
|
96
|
-
|
|
140
|
+
// componentsWithText: ['Hoge'] を設定している場合
|
|
141
|
+
// Hogeコンポーネントはテキストを含むものとして扱われるのでOK
|
|
97
142
|
<XxxButton>
|
|
98
143
|
<Hoge />
|
|
99
144
|
</XxxButton>
|
|
@@ -1,12 +1,117 @@
|
|
|
1
1
|
# smarthr/a11y-form-control-in-form
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
-
|
|
5
|
-
- 適切にマークアップできるようになり、フォームの範囲などがスクリーンリーダーに正しく伝わる
|
|
6
|
-
- 入力要素にfocusした状態でEnterを押せばフォームをsubmitできる
|
|
7
|
-
- inputのrequired属性、pattern属性を利用した入力チェックをブラウザの機能として実行できる
|
|
8
|
-
- smarthr/a11y-input-in-form-control と組み合わせることでより厳密なフォームのマークアップを行えます
|
|
3
|
+
fieldset, Fieldset, FormControl を利用する場合、form要素で囲むことを促すルールです。
|
|
4
|
+
このルールは[smarthr/a11y-input-in-form-control](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/a11y-input-in-form-control) と組み合わせることでより厳密なチェックを行えます。
|
|
9
5
|
|
|
6
|
+
## なぜform要素で囲むことを推奨するのか
|
|
7
|
+
|
|
8
|
+
form要素で対象コンポーネントを囲むことで `formの範囲が明確になる` 以外に以下のメリットが存在します。
|
|
9
|
+
|
|
10
|
+
### 入力要素にfocusした状態でEnterキーなどでフォームを送信(submit)できる
|
|
11
|
+
|
|
12
|
+
ブラウザの標準機能として入力要素にfocusした状態でEnterキー、またはそれに準ずるキーボード操作などをした場合、formを送信(submit)することができるようになります。
|
|
13
|
+
この機能は**ブラウザの標準機能として存在するため、submitできることを期待する利用者は多い**ため、form要素を利用したマークアップをすることを推奨しています。
|
|
14
|
+
|
|
15
|
+
状況によっては**ユーザーの誤操作を防止するためEnterなどでsubmitさせたくない**場合もありえますが、この挙動は**ブラウザの標準機能のため、無効にする場合は慎重に判断してください。**
|
|
16
|
+
|
|
17
|
+
### input要素のrequired属性、pattern属性を利用した入力チェックが有効になる
|
|
18
|
+
|
|
19
|
+
input要素のrequired属性、pattern属性はform要素で囲んでいない場合でも設定自体は出来ますが、submit時のチェック自体は発火しません。
|
|
20
|
+
これらの属性はform要素で囲まれる事により、はじめて有効になります。
|
|
21
|
+
|
|
22
|
+
ブラウザの機能として入力チェックを行うため、jsなどで同等の機能を実装した場合と比較して非常に高速なチェックが可能です。
|
|
23
|
+
ぜひ導入を検討してください。
|
|
24
|
+
|
|
25
|
+
## FormControl、Fieldsetを内包するコンポーネントの名称について
|
|
26
|
+
|
|
27
|
+
FormControl・Fieldsetを内包するコンポーネントを命名する場合、名称のsuffixにFormControl、Fieldset、もしくはFormControls, Fieldsetsのいずれかを設定してください。
|
|
28
|
+
|
|
29
|
+
```jsx
|
|
30
|
+
// FormControl、Fieldsetが内包されるコンポーネントの場合、名称のsuffixを
|
|
31
|
+
// FormControl、Fieldset、もしくはFormControls, Fieldsetsのいずれかにする
|
|
32
|
+
const SampleFormControls = () => (
|
|
33
|
+
<>
|
|
34
|
+
<StyledFormControl name="field1" />
|
|
35
|
+
<StyledFormControl name="field2" />
|
|
36
|
+
<StyledFormControl name="field3" />
|
|
37
|
+
</>
|
|
38
|
+
)
|
|
39
|
+
const SampleFieldset = (props) => (
|
|
40
|
+
<Fieldset {...props}>
|
|
41
|
+
<Any />
|
|
42
|
+
</>
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
上記ルールは **条件によってはFormControl, Fieldsetが表示されない場合がある** 際にも適用してください。
|
|
47
|
+
|
|
48
|
+
```jsx
|
|
49
|
+
// AnyFieldsetは条件次第で表示されない場合があるが、それとは無関係にコンポーネント名は `XxxFieldset` とすること
|
|
50
|
+
const SampleFieldset = () => (
|
|
51
|
+
<>
|
|
52
|
+
<Hoge />
|
|
53
|
+
{condition && (
|
|
54
|
+
<AnyFieldset />
|
|
55
|
+
)}
|
|
56
|
+
<Fuga name="field3" />
|
|
57
|
+
</>
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
このような名称にすることで、対象のコンポーネントがFormの入力要素関連のコンポーネントであることが明示され、lintによるチェックが正常に動作するようになります。
|
|
62
|
+
名称を決定するルールは以下の通りです。
|
|
63
|
+
|
|
64
|
+
### Fieldsetを含まず、FormControlを単一で含む場合 -> `XxxFormControl`
|
|
65
|
+
|
|
66
|
+
```jsx
|
|
67
|
+
const SampleFormControl = (props) => (
|
|
68
|
+
<FormControl {...props} title="Sample" />
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### FormControlを含まず、Fieldsetを単一で含む場合 -> `XxxFieldset`
|
|
73
|
+
|
|
74
|
+
```jsx
|
|
75
|
+
const SampleFieldset = (props) => (
|
|
76
|
+
<Fieldset {...props} title="Sample" />
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Fieldsetを含まず、FormControlを複数含む可能性がある場合 -> `XxxFormControls`
|
|
81
|
+
|
|
82
|
+
```jsx
|
|
83
|
+
const SampleFormControls = () => (
|
|
84
|
+
<>
|
|
85
|
+
<AnyFormControl name="field1" />
|
|
86
|
+
<AnyFormControl name="field2" />
|
|
87
|
+
<AnyFormControl name="field3" />
|
|
88
|
+
</>
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### FormControlを含まず、Fieldsetを複数含む可能性がある場合 -> `XxxFieldsets`
|
|
93
|
+
|
|
94
|
+
```jsx
|
|
95
|
+
const SampleFieldsets = () => (
|
|
96
|
+
<>
|
|
97
|
+
<AnyFieldset name="field1" />
|
|
98
|
+
<AnyFieldset name="field2" />
|
|
99
|
+
<AnyFieldset name="field3" />
|
|
100
|
+
</>
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### FormControl, Fieldsetが複数混ざって存在する場合 -> `XxxFieldsets`
|
|
105
|
+
|
|
106
|
+
```jsx
|
|
107
|
+
const SampleFieldsets = () => (
|
|
108
|
+
<>
|
|
109
|
+
<AnyFormControl name="field1" />
|
|
110
|
+
<AnyFieldset name="field2" />
|
|
111
|
+
<AnyFormControl name="field3" />
|
|
112
|
+
</>
|
|
113
|
+
)
|
|
114
|
+
```
|
|
10
115
|
|
|
11
116
|
## rules
|
|
12
117
|
|
|
@@ -22,46 +127,84 @@
|
|
|
22
127
|
|
|
23
128
|
```jsx
|
|
24
129
|
// formで囲まれていないためNG
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
130
|
+
const Sample = () => (
|
|
131
|
+
<>
|
|
132
|
+
<FormControl />
|
|
133
|
+
<HogeFieldset />
|
|
134
|
+
<fieldset />
|
|
135
|
+
</>
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```jsx
|
|
140
|
+
// FormControl、Fieldsetを内包するコンポーネントの場合、名称のsuffixが
|
|
141
|
+
// FormControl、Fieldset、もしくはFormControls, Fieldsetsのいずれかである必要があるためNG
|
|
142
|
+
const Sample1 = () => (
|
|
143
|
+
<>
|
|
144
|
+
<StyledFormControl name="field1" />
|
|
145
|
+
<StyledFormControl name="field2" />
|
|
146
|
+
<StyledFormControl name="field3" />
|
|
147
|
+
</>
|
|
148
|
+
)
|
|
149
|
+
const Sample2 = (props) => (
|
|
150
|
+
<Fieldset {...props}>
|
|
151
|
+
<Any />
|
|
152
|
+
</>
|
|
153
|
+
)
|
|
30
154
|
```
|
|
31
155
|
|
|
32
156
|
## ✅ Correct
|
|
33
157
|
|
|
34
158
|
```jsx
|
|
35
|
-
// form
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</Hoge>
|
|
159
|
+
// form要素で囲まれているならOK
|
|
160
|
+
const Sample1 = () => (
|
|
161
|
+
// form要素と推測されるコンポーネントならOK
|
|
162
|
+
<StyledForm>
|
|
163
|
+
<FormControl />
|
|
164
|
+
<HogeFieldset />
|
|
165
|
+
<fieldset />
|
|
166
|
+
</StyledForm>
|
|
167
|
+
)
|
|
168
|
+
const Sample2 = () => (
|
|
169
|
+
// as, forwardedAsでform要素にされているコンポーネントの場合もOK
|
|
170
|
+
<Hoge as="form">
|
|
171
|
+
<FormControl />
|
|
172
|
+
<HogeFieldset />
|
|
173
|
+
<fieldset />
|
|
174
|
+
</Hoge>
|
|
175
|
+
)
|
|
176
|
+
```
|
|
51
177
|
|
|
178
|
+
```jsx
|
|
52
179
|
// Dialogの場合、FormDialog・RemoteTriggerFormDialogで囲めばOK
|
|
53
|
-
const
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
180
|
+
const SampleFormDialog = () => (
|
|
181
|
+
<FormDialog>
|
|
182
|
+
<FugaFormControl />
|
|
183
|
+
</FormDialog>
|
|
184
|
+
)
|
|
185
|
+
const SampleRemoteTriggerFormDialog = () => (
|
|
186
|
+
<RemoteTriggerAnyFormDialog>
|
|
187
|
+
<FugaFormControl />
|
|
188
|
+
</RemoteTriggerAnyFormDialog>
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```jsx
|
|
193
|
+
// FormControl、Fieldsetを内包するコンポーネントの場合、名称のsuffixが
|
|
194
|
+
// FormControl、Fieldset、もしくはFormControls, Fieldsetsのいずれかの場合OK
|
|
195
|
+
const SampleFormControls = () => (
|
|
196
|
+
<>
|
|
197
|
+
<StyledFormControl name="field1" />
|
|
198
|
+
<StyledFormControl name="field2" />
|
|
199
|
+
<StyledFormControl name="field3" />
|
|
200
|
+
</>
|
|
201
|
+
)
|
|
202
|
+
const SampleFieldset = (props) => (
|
|
203
|
+
<Fieldset {...props}>
|
|
204
|
+
<Any />
|
|
205
|
+
</>
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
// コンポーネント名を上記の様に調整することで
|
|
209
|
+
// これらのコンポーネントを利用する別コンポーネントでも正しくチェックが行えます
|
|
67
210
|
```
|