eslint-plugin-smarthr 0.5.6 → 0.5.7
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 +8 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/rules/a11y-delegate-element-has-role-presentation/index.js +6 -1
- package/rules/a11y-replace-unreadable-symbol/README.md +38 -0
- package/rules/a11y-replace-unreadable-symbol/index.js +28 -0
- package/test/a11y-delegate-element-has-role-presantation.js +2 -0
- package/test/a11y-replace-unreadable-symbol.js +36 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
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.7](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.5.6...v0.5.7) (2024-04-01)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* a11y-delegate-element-has-role-presentationでas, forwardedAsにform, fieldsetが指定されている場合、インタファクティブな要素として扱うように修正 ([#132](https://github.com/kufu/eslint-plugin-smarthr/issues/132)) ([3d629fa](https://github.com/kufu/eslint-plugin-smarthr/commit/3d629fa73e7346c229831a0075478fcbfe582de1))
|
|
11
|
+
* a11y-replace-unreadable-symbol ([#128](https://github.com/kufu/eslint-plugin-smarthr/issues/128)) ([9410ff9](https://github.com/kufu/eslint-plugin-smarthr/commit/9410ff9ad9ed5a0d18945e3567a1fee8c056f822))
|
|
12
|
+
|
|
5
13
|
### [0.5.6](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.5.5...v0.5.6) (2024-03-29)
|
|
6
14
|
|
|
7
15
|
|
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- [a11y-numbered-text-within-ol](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-numbered-text-within-ol)
|
|
12
12
|
- [a11y-prohibit-input-placeholder](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-input-placeholder)
|
|
13
13
|
- [a11y-prohibit-useless-sectioning-fragment](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-useless-sectioning-fragment)
|
|
14
|
+
- [a11y-replace-unreadable-symbol](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-replace-unreadable-symbol)
|
|
14
15
|
- [a11y-trigger-has-button](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-trigger-has-button)
|
|
15
16
|
- [best-practice-for-button-element](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/best-practice-for-button-element)
|
|
16
17
|
- [best-practice-for-date](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/best-practice-for-date)
|
package/package.json
CHANGED
|
@@ -43,6 +43,8 @@ const INTERACTIVE_COMPONENT_NAMES = Object.keys(EXPECTED_NAMES).join('|')
|
|
|
43
43
|
const INTERACTIVE_ON_REGEX = /^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/
|
|
44
44
|
const MEANED_ROLE_REGEX = /^(combobox|group|slider|toolbar)$/
|
|
45
45
|
const INTERACTIVE_NODE_TYPE_REGEX = /^(JSXElement|JSXExpressionContainer|ConditionalExpression)$/
|
|
46
|
+
const AS_REGEX = /^(as|forwardedAs)$/
|
|
47
|
+
const AS_VALUE_REGEX = /^(form|fieldset)$/
|
|
46
48
|
|
|
47
49
|
const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex, onAttrs) => {
|
|
48
50
|
const onAttrsText = onAttrs.join(', ')
|
|
@@ -122,12 +124,15 @@ module.exports = {
|
|
|
122
124
|
let onAttrs = []
|
|
123
125
|
let roleMean = undefined
|
|
124
126
|
let isRolePresentation = false
|
|
127
|
+
let isAsInteractive = false
|
|
125
128
|
|
|
126
129
|
node.attributes.forEach((a) => {
|
|
127
130
|
const aName = a.name?.name || ''
|
|
128
131
|
|
|
129
132
|
if (aName.match(INTERACTIVE_ON_REGEX)) {
|
|
130
133
|
onAttrs.push(aName)
|
|
134
|
+
} else if (AS_REGEX.test(aName) && AS_VALUE_REGEX.test(a.value?.value || '')) {
|
|
135
|
+
isAsInteractive = true
|
|
131
136
|
} else if (aName === 'role') {
|
|
132
137
|
const v = a.value?.value || ''
|
|
133
138
|
|
|
@@ -140,7 +145,7 @@ module.exports = {
|
|
|
140
145
|
}
|
|
141
146
|
})
|
|
142
147
|
|
|
143
|
-
if (nodeName.match(interactiveComponentRegex)) {
|
|
148
|
+
if (isAsInteractive || nodeName.match(interactiveComponentRegex)) {
|
|
144
149
|
if (isRolePresentation) {
|
|
145
150
|
context.report({
|
|
146
151
|
node,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# smarthr/a11y-replace-unreadable-symbol
|
|
2
|
+
|
|
3
|
+
- 一部記号はスクリーンリーダーで読み上げられない、もしくは記号名そのままで読み上げられてしまい、意図が正しく伝えられない場合があります
|
|
4
|
+
- それらの記号を適切に読み上げられるコンポーネントに置き換えることを促すルールです
|
|
5
|
+
|
|
6
|
+
## rules
|
|
7
|
+
|
|
8
|
+
```js
|
|
9
|
+
{
|
|
10
|
+
rules: {
|
|
11
|
+
'smarthr/a11y-replace-unreadable-symbol': 'error', // 'warn', 'off',
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## ❌ Incorrect
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
<>XXXX年YY月ZZ日 〜 XXXX年YY月ZZ日</>
|
|
20
|
+
// スクリーンリーダーは "XXXX年YY月ZZ日XXXX年YY月ZZ日" と読み上げる場合があります
|
|
21
|
+
|
|
22
|
+
<p>選択できる数値の範囲は 0 ~ 9999 です</p>
|
|
23
|
+
// スクリーンリーダーは "選択できる数値の範囲は09999です" と読み上げる場合があります
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## ✅ Correct
|
|
27
|
+
|
|
28
|
+
```jsx
|
|
29
|
+
//
|
|
30
|
+
<>XXXX年YY月ZZ日 <RangeSeparator /> XXXX年YY月ZZ日</>
|
|
31
|
+
// スクリーンリーダーは "XXXX年YY月ZZ日からXXXX年YY月ZZ日" と読み上げます
|
|
32
|
+
|
|
33
|
+
<p>選択できる数値の範囲は 0 <RangeSeparator /> 9999 です</p>
|
|
34
|
+
// スクリーンリーダーは "選択できる数値の範囲は0から9999です" と読み上げます
|
|
35
|
+
|
|
36
|
+
<p>入力できる記号は <RangeSeparator decorators={{ text: '~', visuallyHiddenText: '半角チルダ' }} /> です</p>
|
|
37
|
+
// スクリーンリーダーは "入力できる記号は半角チルダです" と読み上げます
|
|
38
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const TILDE_REGEX = /([~〜])/
|
|
2
|
+
|
|
3
|
+
const SCHEMA = []
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'problem',
|
|
8
|
+
schema: SCHEMA,
|
|
9
|
+
},
|
|
10
|
+
create(context) {
|
|
11
|
+
return {
|
|
12
|
+
JSXText: (node) => {
|
|
13
|
+
const matcher = node.value.match(TILDE_REGEX)
|
|
14
|
+
|
|
15
|
+
if (matcher) {
|
|
16
|
+
context.report({
|
|
17
|
+
node,
|
|
18
|
+
message: `"${matcher[1]}"はスクリーンリーダーが正しく読み上げることができない場合があるため、smarthr-ui/RangeSeparatorに置き換えてください。
|
|
19
|
+
- エラー表示されている行に"${matcher[1]}"が存在しない場合、改行文字を含む関係で行番号がずれている場合があります。数行下の範囲を確認してください
|
|
20
|
+
- smarthr-ui/RangeSeparatorに置き換えることでスクリーンリーダーが "から" と読み上げることができます
|
|
21
|
+
- 前後の文脈などで "から" と読まれることが不適切な場合 \`<RangeSeparator decorators={{ visuallyHiddenText: () => "ANY" }} />\` のようにdecoratorsを指定してください`,
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
module.exports.schema = SCHEMA
|
|
@@ -56,6 +56,8 @@ ruleTester.run('smarthr/a11y-delegate-element-has-role-presentation', rule, {
|
|
|
56
56
|
{ code: '<Wrapper onClick={any} role="presentation">{any1 ? (any2 ? <HogeLink /> : null) : null}</Wrapper>' },
|
|
57
57
|
{ code: '<Wrapper onClick={any} role="presentation">{any ? null : (hoge ? <AnyLink /> : null)}</Wrapper>' },
|
|
58
58
|
{ code: '<Wrapper onClick={any} role="slider">Hoge</Wrapper>' },
|
|
59
|
+
{ code: '<Wrapper onSubmit={any} as="form" />' },
|
|
60
|
+
{ code: '<Wrapper onSubmit={any} forwardedAs="fieldset">any</Wrapper>' },
|
|
59
61
|
],
|
|
60
62
|
invalid: [
|
|
61
63
|
{ code: '<Input role="presentation" />', errors: [ { message: messageInteractiveHasRolePresentation('Input') } ] },
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const rule = require('../rules/a11y-replace-unreadable-symbol')
|
|
2
|
+
const RuleTester = require('eslint').RuleTester
|
|
3
|
+
|
|
4
|
+
const generateErrorText = (symbol, replaced, read) => `"${symbol}"はスクリーンリーダーが正しく読み上げることができない場合があるため、smarthr-ui/${replaced}に置き換えてください。
|
|
5
|
+
- エラー表示されている行に"${symbol}"が存在しない場合、改行文字を含む関係で行番号がずれている場合があります。数行下の範囲を確認してください
|
|
6
|
+
- smarthr-ui/${replaced}に置き換えることでスクリーンリーダーが "${read}" と読み上げることができます
|
|
7
|
+
- 前後の文脈などで "${read}" と読まれることが不適切な場合 \`<RangeSeparator decorators={{ visuallyHiddenText: () => "ANY" }} />\` のようにdecoratorsを指定してください`
|
|
8
|
+
|
|
9
|
+
const ruleTester = new RuleTester({
|
|
10
|
+
parserOptions: {
|
|
11
|
+
ecmaVersion: 2018,
|
|
12
|
+
ecmaFeatures: {
|
|
13
|
+
experimentalObjectRestSpread: true,
|
|
14
|
+
jsx: true,
|
|
15
|
+
},
|
|
16
|
+
sourceType: 'module',
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
ruleTester.run('a11y-replace-unreadable-symbol', rule, {
|
|
21
|
+
valid: [
|
|
22
|
+
{ code: `<>ほげふが</>` },
|
|
23
|
+
{ code: `<RangeSeparator />` },
|
|
24
|
+
{ code: `<p>ほげ<RangeSeparator />ふが</p>` },
|
|
25
|
+
{ code: `<p>
|
|
26
|
+
ほげ
|
|
27
|
+
<RangeSeparator />
|
|
28
|
+
ふが
|
|
29
|
+
</p>` },
|
|
30
|
+
],
|
|
31
|
+
invalid: [
|
|
32
|
+
{ code: `<>~</>`, errors: [ { message: generateErrorText('~', 'RangeSeparator', 'から') } ] },
|
|
33
|
+
{ code: `<>ほげ~ふが</>`, errors: [ { message: generateErrorText('~', 'RangeSeparator', 'から') } ] },
|
|
34
|
+
{ code: `<p>ほげ〜ふが</p>`, errors: [ { message: generateErrorText('〜', 'RangeSeparator', 'から') } ] },
|
|
35
|
+
]
|
|
36
|
+
})
|