eslint-plugin-smarthr 0.4.0 → 0.4.2
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 +20 -0
- package/README.md +1 -0
- package/libs/format_styled_components.js +40 -34
- package/package.json +1 -1
- package/rules/a11y-anchor-has-href-attribute/README.md +3 -1
- package/rules/a11y-clickable-element-has-text/README.md +10 -5
- package/rules/a11y-clickable-element-has-text/index.js +4 -4
- package/rules/a11y-input-in-form-control/index.js +7 -0
- package/rules/a11y-numbered-text-within-ol/README.md +82 -0
- package/rules/a11y-numbered-text-within-ol/index.js +141 -0
- package/rules/best-practice-for-button-element/README.md +35 -0
- package/rules/best-practice-for-button-element/index.js +43 -0
- package/test/a11y-clickable-element-has-text.js +0 -14
- package/test/a11y-input-in-form-control.js +3 -0
- package/test/a11y-numbered-text-within-ol.js +114 -0
- package/test/best-practice-for-button-element.js +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
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.4.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.1...v0.4.2) (2024-03-03)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* best-practice-for-button-elementを追加 ([#115](https://github.com/kufu/eslint-plugin-smarthr/issues/115)) ([c19ab10](https://github.com/kufu/eslint-plugin-smarthr/commit/c19ab1062d00aa000cfcf9b86535af21b47a0ead))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* a11y-numbered-text-within-ol でwidthの値が誤検知されてしまう場合に対応する ([#117](https://github.com/kufu/eslint-plugin-smarthr/issues/117)) ([3741d54](https://github.com/kufu/eslint-plugin-smarthr/commit/3741d5412f62ac04286e0626e37d2ca3c57c4f60))
|
|
16
|
+
|
|
17
|
+
### [0.4.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.0...v0.4.1) (2024-02-21)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* a11y-numbered-text-within-olを追加する ([#105](https://github.com/kufu/eslint-plugin-smarthr/issues/105)) ([167b92f](https://github.com/kufu/eslint-plugin-smarthr/commit/167b92f0f29db8ee9a446d25e09e04a7b11ce340))
|
|
23
|
+
* ComboBoxなどのinputAttributesでtitle属性が指定された場合、擬似的にラベルが付いていると判定するように修正 ([#113](https://github.com/kufu/eslint-plugin-smarthr/issues/113)) ([5f3b594](https://github.com/kufu/eslint-plugin-smarthr/commit/5f3b5943ec64a18d094a9c66627ad1db2bbabe08))
|
|
24
|
+
|
|
5
25
|
## [0.4.0](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.27...v0.4.0) (2024-02-05)
|
|
6
26
|
|
|
7
27
|
|
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- [a11y-image-has-alt-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-image-has-alt-attribute)
|
|
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
|
+
- [a11y-numbered-text-within-ol](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-numbered-text-within-ol)
|
|
10
11
|
- [a11y-prohibit-input-placeholder](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-input-placeholder)
|
|
11
12
|
- [a11y-prohibit-useless-sectioning-fragment](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-useless-sectioning-fragment)
|
|
12
13
|
- [a11y-trigger-has-button](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-trigger-has-button)
|
|
@@ -18,6 +18,44 @@ const checkImportStyledComponents = (node, context) => {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const getStyledComponentBaseName = (node) => {
|
|
22
|
+
let base = null
|
|
23
|
+
|
|
24
|
+
if (!node.init) {
|
|
25
|
+
return base
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const tag = node.init.tag || node.init
|
|
29
|
+
|
|
30
|
+
if (tag.object?.name === STYLED_COMPONENTS_METHOD) {
|
|
31
|
+
base = tag.property.name
|
|
32
|
+
} else if (tag.callee) {
|
|
33
|
+
const callee = tag.callee
|
|
34
|
+
|
|
35
|
+
switch (STYLED_COMPONENTS_METHOD) {
|
|
36
|
+
case callee.name: {
|
|
37
|
+
const arg = tag.arguments[0]
|
|
38
|
+
base = arg.name || arg.value
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
case callee.callee?.name: {
|
|
42
|
+
const arg = callee.arguments[0]
|
|
43
|
+
base = arg.name || arg.value
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
case callee.object?.name:
|
|
47
|
+
base = callee.property.name
|
|
48
|
+
break
|
|
49
|
+
case callee.object?.callee?.name:
|
|
50
|
+
const arg = callee.object.arguments[0]
|
|
51
|
+
base = arg.name || arg.value
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return base
|
|
57
|
+
}
|
|
58
|
+
|
|
21
59
|
const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) => {
|
|
22
60
|
const entriesesTagNames = Object.entries(EXPECTED_NAMES).map(([b, e]) => [ new RegExp(b), new RegExp(e) ])
|
|
23
61
|
const entriesesUnTagNames = UNEXPECTED_NAMES ? Object.entries(UNEXPECTED_NAMES).map(([b, e]) => {
|
|
@@ -52,39 +90,7 @@ const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) =>
|
|
|
52
90
|
}
|
|
53
91
|
},
|
|
54
92
|
VariableDeclarator: (node) => {
|
|
55
|
-
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const tag = node.init.tag || node.init
|
|
60
|
-
|
|
61
|
-
let base = null
|
|
62
|
-
|
|
63
|
-
if (tag.object?.name === STYLED_COMPONENTS_METHOD) {
|
|
64
|
-
base = tag.property.name
|
|
65
|
-
} else if (tag.callee) {
|
|
66
|
-
const callee = tag.callee
|
|
67
|
-
|
|
68
|
-
switch (STYLED_COMPONENTS_METHOD) {
|
|
69
|
-
case callee.name: {
|
|
70
|
-
const arg = tag.arguments[0]
|
|
71
|
-
base = arg.name || arg.value
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
|
-
case callee.callee?.name: {
|
|
75
|
-
const arg = callee.arguments[0]
|
|
76
|
-
base = arg.name || arg.value
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
case callee.object?.name:
|
|
80
|
-
base = callee.property.name
|
|
81
|
-
break
|
|
82
|
-
case callee.object?.callee?.name:
|
|
83
|
-
const arg = callee.object.arguments[0]
|
|
84
|
-
base = arg.name || arg.value
|
|
85
|
-
break
|
|
86
|
-
}
|
|
87
|
-
}
|
|
93
|
+
const base = getStyledComponentBaseName(node)
|
|
88
94
|
|
|
89
95
|
if (base) {
|
|
90
96
|
const extended = node.id.name
|
|
@@ -117,4 +123,4 @@ const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) =>
|
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
module.exports = { generateTagFormatter }
|
|
126
|
+
module.exports = { generateTagFormatter, checkImportStyledComponents, getStyledComponentBaseName }
|
package/package.json
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
- URL遷移を行う場合、hrefが設定されていないとキーボード操作やコンテキストメニューからの遷移ができなくなります
|
|
7
7
|
- これらの操作は href属性を参照します
|
|
8
8
|
- 無効化されたリンクであることを表したい場合 `href={undefined}` を設定してください
|
|
9
|
-
- checkTypeオプションに 'smart' を指定することで spread attributeが設定されている場合はcorrect
|
|
9
|
+
- checkTypeオプションに 'smart' を指定することで spread attributeが設定されている場合はcorrectに出来ます
|
|
10
|
+
- react-router-dom packageを利用している場合、a要素にto属性が指定されている場合、href属性が指定されているものとして許容します
|
|
11
|
+
- next/link コンポーネント直下のa要素にhref属性が指定されていないことを許容します
|
|
10
12
|
|
|
11
13
|
## rules
|
|
12
14
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# smarthr/a11y-clickable-element-has-text
|
|
2
2
|
|
|
3
|
-
- ButtonやAnchor,Link
|
|
3
|
+
- ButtonやAnchor,Link コンポーネントにテキスト要素が設定されていない場合、スクリーンリーダーで押したものが何だったのかわからない等の問題が発生する可能性を防ぐルールです
|
|
4
|
+
- a要素やbutton要素の中身にtextがあることを担保するルール
|
|
5
|
+
- 画像要素の場合は `visuallyHiddenText`や `alt`等代替テキストを設定する
|
|
6
|
+
- SVGの場合はrole="img" と aria-labelを設定する
|
|
7
|
+
- linkとかanchorのchildrenを含まない要素はチェックしない
|
|
8
|
+
- 例) `<YyyAnchor />`
|
|
4
9
|
|
|
5
10
|
## rules
|
|
6
11
|
|
|
@@ -38,7 +43,7 @@
|
|
|
38
43
|
```
|
|
39
44
|
|
|
40
45
|
```jsx
|
|
41
|
-
<XxxAnchor
|
|
46
|
+
<XxxAnchor>
|
|
42
47
|
<XxxTextYyyy />
|
|
43
48
|
</XxxAnchor>
|
|
44
49
|
```
|
|
@@ -57,7 +62,7 @@
|
|
|
57
62
|
</XxxLink>
|
|
58
63
|
```
|
|
59
64
|
```jsx
|
|
60
|
-
<XxxAnchor
|
|
65
|
+
<XxxAnchor>
|
|
61
66
|
<YyyIcon visuallyHiddenText="hoge" />
|
|
62
67
|
</XxxAnchor>
|
|
63
68
|
```
|
|
@@ -68,11 +73,11 @@
|
|
|
68
73
|
```
|
|
69
74
|
|
|
70
75
|
```jsx
|
|
71
|
-
<
|
|
76
|
+
<YyyAnchor />
|
|
72
77
|
```
|
|
73
78
|
|
|
74
79
|
```jsx
|
|
75
|
-
<XxxAnchor
|
|
80
|
+
<XxxAnchor>
|
|
76
81
|
<XxxText />
|
|
77
82
|
</XxxAnchor>
|
|
78
83
|
```
|
|
@@ -30,9 +30,9 @@ const REGEX_NLSP = /^\s*\n+\s*$/
|
|
|
30
30
|
const REGEX_CLICKABLE_ELEMENT = /^(a|(.*?)Anchor(Button)?|(.*?)Link|(b|B)utton)$/
|
|
31
31
|
const REGEX_SMARTHR_LOGO = /SmartHRLogo$/
|
|
32
32
|
const REGEX_TEXT_COMPONENT = /(Text|Message)$/
|
|
33
|
+
const REGEX_JSX_TYPE = /^(JSXText|JSXExpressionContainer)$/
|
|
33
34
|
|
|
34
|
-
const
|
|
35
|
-
const HIT_TEXT_ATTRS = ['visuallyHiddenText', 'alt']
|
|
35
|
+
const HIT_TEXT_ATTR = 'alt'
|
|
36
36
|
|
|
37
37
|
const filterFalsyJSXText = (cs) => cs.filter(checkFalsyJSXText)
|
|
38
38
|
const checkFalsyJSXText = (c) => (
|
|
@@ -68,7 +68,7 @@ module.exports = {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const recursiveSearch = (c) => {
|
|
71
|
-
if (
|
|
71
|
+
if (REGEX_JSX_TYPE.test(c.type)) {
|
|
72
72
|
return true
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -97,7 +97,7 @@ module.exports = {
|
|
|
97
97
|
|
|
98
98
|
if (
|
|
99
99
|
prev ||
|
|
100
|
-
|
|
100
|
+
HIT_TEXT_ATTR !== a.name.name
|
|
101
101
|
) {
|
|
102
102
|
return prev
|
|
103
103
|
}
|
|
@@ -63,6 +63,7 @@ const IGNORE_INPUT_CHECK_PARENT_TYPE = /^(Program|ExportNamedDeclaration)$/
|
|
|
63
63
|
|
|
64
64
|
const findRoleGroup = (a) => a.name?.name === 'role' && a.value.value === 'group'
|
|
65
65
|
const findAsSectioning = (a) => a.name?.name.match(AS_REGEX) && a.value.value.match(BARE_SECTIONING_TAG_REGEX)
|
|
66
|
+
const findTitle = (i) => i.key.name === 'title'
|
|
66
67
|
|
|
67
68
|
const SCHEMA = [
|
|
68
69
|
{
|
|
@@ -121,6 +122,12 @@ module.exports = {
|
|
|
121
122
|
case 'title':
|
|
122
123
|
isPseudoLabel = true
|
|
123
124
|
break
|
|
125
|
+
case 'inputAttributes': {
|
|
126
|
+
if (!isPseudoLabel && i.value.expression.type === 'ObjectExpression' && i.value.expression.properties.some(findTitle)) {
|
|
127
|
+
isPseudoLabel = true
|
|
128
|
+
}
|
|
129
|
+
break
|
|
130
|
+
}
|
|
124
131
|
case 'type':
|
|
125
132
|
switch (i.value.value) {
|
|
126
133
|
case 'radio':
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# smarthr/a11y-numbered-text-within-ol
|
|
2
|
+
|
|
3
|
+
- "1. hoge", "2. fuga" ... のように連番のテキストをもつコンポーネントはol要素でマークアップすることを促すルールです
|
|
4
|
+
- ol要素でマークアップすることで連番テキストをもつ要素同士の関係、順番に意味があることを適切に示すことが出来ます
|
|
5
|
+
|
|
6
|
+
## rules
|
|
7
|
+
|
|
8
|
+
```js
|
|
9
|
+
{
|
|
10
|
+
rules: {
|
|
11
|
+
'smarthr/a11y-numbered-text-within-ol': 'error', // 'warn', 'off',
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## ❌ Incorrect
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
// ol要素で囲まれていないためNG
|
|
20
|
+
<Any>1. hoge</Any>
|
|
21
|
+
<Any>2. fuga</Any>
|
|
22
|
+
|
|
23
|
+
// 属性でも同様にチェックする
|
|
24
|
+
<Any title="1. hoge" />
|
|
25
|
+
<Any title="2. fuga" />
|
|
26
|
+
|
|
27
|
+
// ol要素内で連番を設定しているとNG
|
|
28
|
+
<OrderedList>
|
|
29
|
+
<li>1. hoge</li>
|
|
30
|
+
<li>2. fuga</li>
|
|
31
|
+
</OrderedList>
|
|
32
|
+
|
|
33
|
+
// 同一のol要素で囲まれていないためNG
|
|
34
|
+
<OrderedList>
|
|
35
|
+
<li>hoge</li>
|
|
36
|
+
</OrderedList>
|
|
37
|
+
<OrderedList>
|
|
38
|
+
<li>fuga</li>
|
|
39
|
+
</OrderedList>>
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## ✅ Correct
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
<ol>
|
|
47
|
+
<li>hoge</li>
|
|
48
|
+
<li>fuga</li>
|
|
49
|
+
</ol>
|
|
50
|
+
|
|
51
|
+
<OrderedList>
|
|
52
|
+
<Any title="hoge" />
|
|
53
|
+
<Any title="fuga" />
|
|
54
|
+
</OrderedList>
|
|
55
|
+
|
|
56
|
+
// デフォルトの連番からフォーマット、スタイルを変更したい場合
|
|
57
|
+
// counter-reset + counter-increment で表現する
|
|
58
|
+
// 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)
|
|
59
|
+
<OrderedList>
|
|
60
|
+
<li>
|
|
61
|
+
<NumberedHeading>hoge</NumberedHeading>
|
|
62
|
+
<Any />
|
|
63
|
+
</li>
|
|
64
|
+
<li>
|
|
65
|
+
<NumberedHeading>fuga</NumberedHeading>
|
|
66
|
+
<Any />
|
|
67
|
+
</li>
|
|
68
|
+
</OrderedList>
|
|
69
|
+
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
const OrderedList = styled.ol`
|
|
73
|
+
list-style: none; // デフォルトのstyleを消す
|
|
74
|
+
counter-reset: hoge; // カウンターの名称。わかりやすいものなら何でもOK
|
|
75
|
+
`
|
|
76
|
+
const NumberedHeading = styled(Heading)`
|
|
77
|
+
&::before {
|
|
78
|
+
counter-increment: hoge; // hogeカウンターを+1する
|
|
79
|
+
content: "No " counter(hoge) ": "; // 表示される連番のフォーマット
|
|
80
|
+
}
|
|
81
|
+
`
|
|
82
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const { generateTagFormatter } = require('../../libs/format_styled_components')
|
|
2
|
+
|
|
3
|
+
const EXPECTED_NAMES = {
|
|
4
|
+
'(Ordered(.*)List|^ol)$': '(Ordered(.*)List)$',
|
|
5
|
+
'(S|s)elect$': '(Select)$',
|
|
6
|
+
}
|
|
7
|
+
const UNEXPECTED_NAMES = EXPECTED_NAMES
|
|
8
|
+
|
|
9
|
+
const NUMBERED_TEXT_REGEX = /^[\s\n]*(([0-9])([^0-9]{2})[^\s\n]*)/
|
|
10
|
+
const ORDERED_LIST_REGEX = /(Ordered(.*)List|^ol)$/
|
|
11
|
+
const SELECT_REGEX = /(S|s)elect$/
|
|
12
|
+
const IGNORE_ATTRIBUTE_REGEX = /((w|W)idth|(h|H)eight)$/
|
|
13
|
+
const AS_ATTRIBUTE_REGEX = /^(as|forwardedAs)$/
|
|
14
|
+
|
|
15
|
+
const findAsOlAttr = (a) => a.type === 'JSXAttribute' && AS_ATTRIBUTE_REGEX.test(a.name?.name) && a.value?.value === 'ol'
|
|
16
|
+
|
|
17
|
+
const searchOrderedList = (node) => {
|
|
18
|
+
if (node.type === 'JSXElement' && node.openingElement.name?.name) {
|
|
19
|
+
const name = node.openingElement.name.name
|
|
20
|
+
|
|
21
|
+
if (name.match(SELECT_REGEX)) {
|
|
22
|
+
// HINT: select要素の場合、optionのラベルに連番がついている場合がありえるのでignoreする
|
|
23
|
+
// 通常と処理を分けるためnullではなく0を返す
|
|
24
|
+
return 0
|
|
25
|
+
} else if (
|
|
26
|
+
name.match(ORDERED_LIST_REGEX) ||
|
|
27
|
+
node.openingElement.attributes.find(findAsOlAttr)
|
|
28
|
+
) {
|
|
29
|
+
return node.openingElement
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (node.type === 'Program') {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return searchOrderedList(node.parent)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const checkNumberedTextInOl = (result, node, context) => {
|
|
41
|
+
if (result) {
|
|
42
|
+
context.report({
|
|
43
|
+
node,
|
|
44
|
+
message: `${result.name.name} 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
45
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
46
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)`,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const renderTag = (node) => `\`${node.name.name}="${node.value.value}"\``
|
|
52
|
+
const renderNode = (node, matcher) => node.type === 'JSXText' ? `\`${matcher[1]}\`` : renderTag(node)
|
|
53
|
+
|
|
54
|
+
const SCHEMA = []
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
meta: {
|
|
58
|
+
type: 'problem',
|
|
59
|
+
schema: SCHEMA,
|
|
60
|
+
},
|
|
61
|
+
create(context) {
|
|
62
|
+
let firstNumber = 0
|
|
63
|
+
let firstNumberedNode = null
|
|
64
|
+
let firstNumberedMatcher = null
|
|
65
|
+
|
|
66
|
+
function checker(node, matcher) {
|
|
67
|
+
if (matcher) {
|
|
68
|
+
const result = searchOrderedList(node)
|
|
69
|
+
|
|
70
|
+
if (result !== 0) {
|
|
71
|
+
checkNumberedTextInOl(result, node, context)
|
|
72
|
+
|
|
73
|
+
const nowNumber = matcher[2] * 1
|
|
74
|
+
|
|
75
|
+
if (firstNumberedNode && nowNumber !== firstNumber) {
|
|
76
|
+
if (nowNumber === firstNumber + 1) {
|
|
77
|
+
const resultFirst = searchOrderedList(firstNumberedNode)
|
|
78
|
+
|
|
79
|
+
if (!resultFirst) {
|
|
80
|
+
if (!result) {
|
|
81
|
+
context.report({
|
|
82
|
+
node: firstNumberedNode,
|
|
83
|
+
message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
84
|
+
- ${renderNode(firstNumberedNode, firstNumberedMatcher)} と ${renderNode(node, matcher)} が同じol要素内に存在するように修正してください
|
|
85
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります`,
|
|
86
|
+
})
|
|
87
|
+
} else {
|
|
88
|
+
context.report({
|
|
89
|
+
node: firstNumberedNode,
|
|
90
|
+
message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
91
|
+
- ${renderNode(firstNumberedNode, firstNumberedMatcher)} が ${renderNode(node, matcher)} を囲んでいるol要素内(<${result.name.name}>)に存在するように修正してください
|
|
92
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol要素内(<${result.name.name}>)に存在する必要があります`,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
if (!result) {
|
|
97
|
+
context.report({
|
|
98
|
+
node,
|
|
99
|
+
message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
100
|
+
- ${renderNode(node, matcher)} が ${renderNode(firstNumberedNode, firstNumberedMatcher)} を囲んでいるol要素内(<${resultFirst.name.name}>)に存在するように修正してください
|
|
101
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol要素内(<${resultFirst.name.name}>)に存在する必要があります`,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
firstNumberedNode = null
|
|
105
|
+
} else if (resultFirst !== result) {
|
|
106
|
+
context.report({
|
|
107
|
+
node,
|
|
108
|
+
message: `連番を含むテキストが同一のol要素でマークアップされていません。同一のol要素でマークアップすることでリスト内の要素関連性を正しく表せるためマークアップの修正を行ってください。
|
|
109
|
+
- ${renderNode(firstNumberedNode, firstNumberedMatcher)} と ${renderNode(node, matcher)} が同じol要素内に存在するように修正してください
|
|
110
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります`,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
firstNumber = nowNumber
|
|
117
|
+
firstNumberedNode = node
|
|
118
|
+
firstNumberedMatcher = matcher
|
|
119
|
+
} else if (!firstNumberedNode || nowNumber === firstNumber) {
|
|
120
|
+
firstNumber = nowNumber
|
|
121
|
+
firstNumberedNode = node
|
|
122
|
+
firstNumberedMatcher = matcher
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
|
|
130
|
+
JSXAttribute: (node) => {
|
|
131
|
+
if (node.value?.value && !IGNORE_ATTRIBUTE_REGEX.test(node.name?.name)) {
|
|
132
|
+
checker(node, node.value.value.match(NUMBERED_TEXT_REGEX))
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
JSXText: (node) => {
|
|
136
|
+
checker(node, node.value.match(NUMBERED_TEXT_REGEX))
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
module.exports.schema = SCHEMA
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# smarthr/best-practice-for-button-element
|
|
2
|
+
|
|
3
|
+
- button要素の利用を禁止し、smarthr/Button、もしくはsmarthr/UnstyledButtonの利用を促すルールです
|
|
4
|
+
- button要素のtype属性のデフォルトは 'submit' であり、これはform要素にbuttonを設置すると、clickでformをsubmitする状態になります
|
|
5
|
+
- 上記挙動は開発者が意図しづらいため、smarthr/Button, smarthr/UnstyledButtonはtype属性のデフォルトを 'button' に変更しています
|
|
6
|
+
|
|
7
|
+
## rules
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
{
|
|
11
|
+
rules: {
|
|
12
|
+
'smarthr/best-practice-for-button-element': 'error', // 'warn', 'off'
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## ❌ Incorrect
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
<button>click</button>
|
|
21
|
+
|
|
22
|
+
const AnyButton = styled.button``
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## ✅ Correct
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
<button type="button">click</button>
|
|
30
|
+
<button type="submit">click</button>
|
|
31
|
+
<Button>click</Button>
|
|
32
|
+
<AnyButton>click</AnyButton>
|
|
33
|
+
|
|
34
|
+
const AnyButton = styled(Button)``
|
|
35
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { checkImportStyledComponents, getStyledComponentBaseName } = require('../../libs/format_styled_components')
|
|
2
|
+
|
|
3
|
+
const ERRORMESSAGE_SUFFIX = `
|
|
4
|
+
- button要素のtype属性のデフォルトは "submit" のため、button要素がformでラップされていると意図しないsubmitを引き起こす可能性があります
|
|
5
|
+
- smarthr-ui/Button, smarthr-ui/UnstyledButtonのtype属性のデフォルトは "button" になっているため、buttonから置き換えることをおすすめします`
|
|
6
|
+
const ERRORMESSAGE_REQUIRED_TYPE_ATTR = `button要素を利用する場合、type属性に "button" もしくは "submit" を指定してください${ERRORMESSAGE_SUFFIX}`
|
|
7
|
+
const ERRORMESSAGE_PROHIBIT_STYLED = `"styled.button" の直接利用をやめ、smarthr-ui/Button、もしくはsmarthr-ui/UnstyledButtonを利用してください${ERRORMESSAGE_SUFFIX}`
|
|
8
|
+
|
|
9
|
+
const findTypeAttr = (a) => a.type === 'JSXAttribute' && a.name.name === 'type'
|
|
10
|
+
|
|
11
|
+
const SCHEMA = []
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
meta: {
|
|
15
|
+
type: 'problem',
|
|
16
|
+
fixable: 'code',
|
|
17
|
+
schema: SCHEMA,
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
ImportDeclaration: (node) => {
|
|
22
|
+
checkImportStyledComponents(node, context)
|
|
23
|
+
},
|
|
24
|
+
JSXOpeningElement: (node) => {
|
|
25
|
+
if (node.name.name === 'button' && !node.attributes.find(findTypeAttr)) {
|
|
26
|
+
context.report({
|
|
27
|
+
node,
|
|
28
|
+
message: ERRORMESSAGE_REQUIRED_TYPE_ATTR,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
VariableDeclarator: (node) => {
|
|
33
|
+
if (getStyledComponentBaseName(node) === 'button') {
|
|
34
|
+
context.report({
|
|
35
|
+
node,
|
|
36
|
+
message: ERRORMESSAGE_PROHIBIT_STYLED,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
module.exports.schema = SCHEMA
|
|
@@ -92,12 +92,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
|
|
|
92
92
|
{
|
|
93
93
|
code: `<a><img src="hoge.jpg" alt="ほげ" /></a>`,
|
|
94
94
|
},
|
|
95
|
-
{
|
|
96
|
-
code: `<a><Icon visuallyHiddenText="ほげ" /></a>`,
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
code: `<a><AnyComponent><Icon visuallyHiddenText="ほげ" /></AnyComponent></a>`,
|
|
100
|
-
},
|
|
101
95
|
{
|
|
102
96
|
code: `<a>{any}</a>`,
|
|
103
97
|
},
|
|
@@ -194,10 +188,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
|
|
|
194
188
|
code: `<a><img src="hoge.jpg" alt="" /></a>`,
|
|
195
189
|
errors: [{ message: defaultErrorMessage }]
|
|
196
190
|
},
|
|
197
|
-
{
|
|
198
|
-
code: `<a><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></a>`,
|
|
199
|
-
errors: [{ message: defaultErrorMessage }]
|
|
200
|
-
},
|
|
201
191
|
{
|
|
202
192
|
code: `<button><img src="hoge.jpg" /></button>`,
|
|
203
193
|
errors: [{ message: defaultErrorMessage }]
|
|
@@ -214,10 +204,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
|
|
|
214
204
|
code: `<button><img src="hoge.jpg" alt="" /></button>`,
|
|
215
205
|
errors: [{ message: defaultErrorMessage }]
|
|
216
206
|
},
|
|
217
|
-
{
|
|
218
|
-
code: `<button><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></button>`,
|
|
219
|
-
errors: [{ message: defaultErrorMessage }]
|
|
220
|
-
},
|
|
221
207
|
{
|
|
222
208
|
code: `<button><SmartHRLogoSuffix /></button>`,
|
|
223
209
|
errors: [{ message: defaultErrorMessage }]
|
|
@@ -129,6 +129,8 @@ ruleTester.run('a11y-input-in-form-control', rule, {
|
|
|
129
129
|
{ code: '<HogeFieldset><HogeCheckBox /><HogeInput title="any" /></HogeFieldset>' },
|
|
130
130
|
{ code: '<FugaSection><HogeInput title="any" /></FugaSection>' },
|
|
131
131
|
{ code: '<HogeTextarea title="any" />' },
|
|
132
|
+
{ code: '<HogeComboBox inputAttributes={{ title: "any" }} />' },
|
|
133
|
+
{ code: '<HogeComboBox inputAttributes={{ title }} />' },
|
|
132
134
|
{ code: '<Fieldset><HogeRadioButtons /></Fieldset>' },
|
|
133
135
|
{ code: '<Fieldset><HogeRadioButtonPanels /></Fieldset>' },
|
|
134
136
|
{ code: '<Fieldset><HogeCheckBoxs /></Fieldset>' },
|
|
@@ -154,6 +156,7 @@ ruleTester.run('a11y-input-in-form-control', rule, {
|
|
|
154
156
|
{ code: '<HogeSelect />', errors: [ { message: noLabeledSelect('HogeSelect') } ] },
|
|
155
157
|
{ code: '<HogeInputFile />', errors: [ { message: noLabeledInput('HogeInputFile') } ] },
|
|
156
158
|
{ code: '<HogeComboBox />', errors: [ { message: noLabeledInput('HogeComboBox') } ] },
|
|
159
|
+
{ code: '<HogeComboBox inputAttributes={{ any }} />', errors: [ { message: noLabeledInput('HogeComboBox') } ] },
|
|
157
160
|
{ code: '<HogeDatePicker />', errors: [ { message: noLabeledInput('HogeDatePicker') } ] },
|
|
158
161
|
{ code: '<HogeFormControl><Input type="checkbox" /><Input type="checkbox" /></HogeFormControl>', errors: [ { message: invalidPureCheckboxInFormControl('Input') } ] },
|
|
159
162
|
{ code: '<HogeFormControl><HogeCheckBox /><Input /></HogeFormControl>', errors: [ { message: invalidMultiInputsInFormControl() } ] },
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const rule = require('../rules/a11y-numbered-text-within-ol')
|
|
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
|
+
ruleTester.run('a11y-numbered-text-within-ol', rule, {
|
|
16
|
+
valid: [
|
|
17
|
+
{ code: `const HogeOrderedFugaList = styled.ol` },
|
|
18
|
+
{ code: `const HogeOrderedFugaList = styled(HogeOrderedList)` },
|
|
19
|
+
{ code: `<p>1: hoge</p>` },
|
|
20
|
+
{ code: `<p title="2. hoge" />` },
|
|
21
|
+
{ code: `<><p>2: hoge</p><p>1: hoge</p></>` },
|
|
22
|
+
{ code: `<ol><li>abc</li><li>def</li></ol>` },
|
|
23
|
+
{ code: `<OrderedList><li>abc</li><li>def</li></OrderedList>` },
|
|
24
|
+
{ code: `<Hoge as="ol"><li>abc</li><li>def</li></Hoge>` },
|
|
25
|
+
{ code: `<Hoge forwardedAs="ol"><li>abc</li><li>def</li></Hoge>` },
|
|
26
|
+
{ code: `<Select><optgroup><option value="hoge">1. hoge</option><option value="fuga">2. fuga</option></optgroup></Select>` },
|
|
27
|
+
{ code: `<><Hoge width="100%" /><Hoge height="200%" /><Hoge maxWidth="30px" /></>` },
|
|
28
|
+
],
|
|
29
|
+
invalid: [
|
|
30
|
+
{ code: `<><p>1: hoge</p><p>2: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
31
|
+
- \`1: hoge\` と \`2: hoge\` が同じol要素内に存在するように修正してください
|
|
32
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
33
|
+
{ code: `
|
|
34
|
+
<>
|
|
35
|
+
<div>
|
|
36
|
+
1. abc
|
|
37
|
+
</div>
|
|
38
|
+
<p>
|
|
39
|
+
2. def
|
|
40
|
+
</p>
|
|
41
|
+
</>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
42
|
+
- \`1. abc\` と \`2. def\` が同じol要素内に存在するように修正してください
|
|
43
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
44
|
+
{ code: `<><p>2: hoge</p><p>1: hoge</p><p>2: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
45
|
+
- \`1: hoge\` と \`2: hoge\` が同じol要素内に存在するように修正してください
|
|
46
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
47
|
+
{ code: `<><p>1: hoge</p><ol><li>2: hoge</li></ol></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
48
|
+
- \`1: hoge\` が \`2: hoge\` を囲んでいるol要素内(<ol>)に存在するように修正してください
|
|
49
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol要素内(<ol>)に存在する必要があります` }, { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
50
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
51
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` } ] },
|
|
52
|
+
{ code: `<><p>1: hoge</p><p title="2. hoge" /></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
53
|
+
- \`1: hoge\` と \`title="2. hoge"\` が同じol要素内に存在するように修正してください
|
|
54
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
55
|
+
{ code: `<><ol><li>1: hoge</li></ol><p>2: hoge</p></>`, errors: [ { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
56
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
57
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` }, { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
58
|
+
- \`2: hoge\` が \`1: hoge\` を囲んでいるol要素内(<ol>)に存在するように修正してください
|
|
59
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol要素内(<ol>)に存在する必要があります` } ] },
|
|
60
|
+
{ code: `<><ol><li>1: hoge</li></ol><ol><li>2: hoge</li></ol></>`, errors: [ { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
61
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
62
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` }, { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
63
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
64
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` }, { message: `連番を含むテキストが同一のol要素でマークアップされていません。同一のol要素でマークアップすることでリスト内の要素関連性を正しく表せるためマークアップの修正を行ってください。
|
|
65
|
+
- \`1: hoge\` と \`2: hoge\` が同じol要素内に存在するように修正してください
|
|
66
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
67
|
+
{ code: `<><Hoge any="1: hoge" /><Hoge any="2: hoge" /></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
68
|
+
- \`any="1: hoge"\` と \`any="2: hoge"\` が同じol要素内に存在するように修正してください
|
|
69
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
70
|
+
{ code: `<><Hoge any="2: hoge" /><Hoge any="1: hoge" /><Hoge any="2: hoge" /></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
71
|
+
- \`any="1: hoge"\` と \`any="2: hoge"\` が同じol要素内に存在するように修正してください
|
|
72
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
73
|
+
{ code: `<><Hoge any="1: hoge" /><ol><li><Hoge any="2: hoge" /></li></ol></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
74
|
+
- \`any="1: hoge"\` が \`any="2: hoge"\` を囲んでいるol要素内(<ol>)に存在するように修正してください
|
|
75
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol要素内(<ol>)に存在する必要があります` }, { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
76
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
77
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` } ] },
|
|
78
|
+
{ code: `<><ol><li><Hoge any="1: hoge" /></li></ol><Hoge any="2: hoge" /></>`, errors: [ { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
79
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
80
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` }, { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
81
|
+
- \`any="2: hoge"\` が \`any="1: hoge"\` を囲んでいるol要素内(<ol>)に存在するように修正してください
|
|
82
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol要素内(<ol>)に存在する必要があります` } ] },
|
|
83
|
+
{ code: `<><ol><li><Hoge any="1: hoge" /></li></ol><ol><li><Hoge any="2: hoge" /></li></ol></>`, errors: [ { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
84
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
85
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` }, { message: `ol 内で連番がテキストとして記述されています。連番はol要素で表現できるため、削除してください。
|
|
86
|
+
- ol要素のデフォルトで表示される連番のフォーマット、スタイルから変更したい場合、counter-reset と counter-increment を利用してください
|
|
87
|
+
- 参考: [MDN CSS カウンターの使用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)` }, { message: `連番を含むテキストが同一のol要素でマークアップされていません。同一のol要素でマークアップすることでリスト内の要素関連性を正しく表せるためマークアップの修正を行ってください。
|
|
88
|
+
- \`any="1: hoge"\` と \`any="2: hoge"\` が同じol要素内に存在するように修正してください
|
|
89
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
90
|
+
{ code: `<><Hoge any="1: hoge" /><Hoge any="2: hoge" /><Hoge any="3: hoge" /><Hoge any="4: hoge" /></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
91
|
+
- \`any="1: hoge"\` と \`any="2: hoge"\` が同じol要素内に存在するように修正してください
|
|
92
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` }, { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
93
|
+
- \`any="2: hoge"\` と \`any="3: hoge"\` が同じol要素内に存在するように修正してください
|
|
94
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` }, { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
95
|
+
- \`any="3: hoge"\` と \`any="4: hoge"\` が同じol要素内に存在するように修正してください
|
|
96
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
97
|
+
{ code: `<><Hoge any="1: hoge" /><Hoge any="3: hoge" /><Hoge any="4: hoge" /><Hoge any="6: hoge" /></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
98
|
+
- \`any="3: hoge"\` と \`any="4: hoge"\` が同じol要素内に存在するように修正してください
|
|
99
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
100
|
+
{ code: `<><p>1: hoge</p><p>2: hoge</p><p>3: hoge</p><p>4: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
101
|
+
- \`1: hoge\` と \`2: hoge\` が同じol要素内に存在するように修正してください
|
|
102
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` }, { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
103
|
+
- \`2: hoge\` と \`3: hoge\` が同じol要素内に存在するように修正してください
|
|
104
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` }, { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
105
|
+
- \`3: hoge\` と \`4: hoge\` が同じol要素内に存在するように修正してください
|
|
106
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
107
|
+
{ code: `<><p>1: hoge</p><p>3: hoge</p><p>4: hoge</p><p>6: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
108
|
+
- \`3: hoge\` と \`4: hoge\` が同じol要素内に存在するように修正してください
|
|
109
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
110
|
+
{ code: `<><p>3: hoge</p><select><option value="hoge">1: hoge</option></select><p>4: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
111
|
+
- \`3: hoge\` と \`4: hoge\` が同じol要素内に存在するように修正してください
|
|
112
|
+
- 上記以外にも関連する連番をふくむ要素が存在する場合、それらも同じol内に存在する必要があります` } ] },
|
|
113
|
+
]
|
|
114
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const rule = require('../rules/best-practice-for-button-element')
|
|
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 ERRORMESSAGE_REQUIRED_TYPE_ATTR = `button要素を利用する場合、type属性に "button" もしくは "submit" を指定してください
|
|
16
|
+
- button要素のtype属性のデフォルトは "submit" のため、button要素がformでラップされていると意図しないsubmitを引き起こす可能性があります
|
|
17
|
+
- smarthr-ui/Button, smarthr-ui/UnstyledButtonのtype属性のデフォルトは "button" になっているため、buttonから置き換えることをおすすめします`
|
|
18
|
+
const ERRORMESSAGE_PROHIBIT_STYLED = `"styled.button" の直接利用をやめ、smarthr-ui/Button、もしくはsmarthr-ui/UnstyledButtonを利用してください
|
|
19
|
+
- button要素のtype属性のデフォルトは "submit" のため、button要素がformでラップされていると意図しないsubmitを引き起こす可能性があります
|
|
20
|
+
- smarthr-ui/Button, smarthr-ui/UnstyledButtonのtype属性のデフォルトは "button" になっているため、buttonから置き換えることをおすすめします`
|
|
21
|
+
|
|
22
|
+
ruleTester.run('best-practice-for-button-element', rule, {
|
|
23
|
+
valid: [
|
|
24
|
+
{ code: `import styled from 'styled-components'` },
|
|
25
|
+
{ code: `import styled, { css } from 'styled-components'` },
|
|
26
|
+
{ code: `<Button />` },
|
|
27
|
+
{ code: `<Button>ほげ</Button>` },
|
|
28
|
+
{ code: `<AnyButton>ほげ</AnyButton>` },
|
|
29
|
+
{ code: `<button type="button">any</button>` },
|
|
30
|
+
{ code: `<button type="submit">any</button>` },
|
|
31
|
+
{ code: 'const HogeButton = styled(HogeButton)``' },
|
|
32
|
+
],
|
|
33
|
+
invalid: [
|
|
34
|
+
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
|
|
35
|
+
{ code: `<button>ほげ</button>`, errors: [ { message: ERRORMESSAGE_REQUIRED_TYPE_ATTR } ] },
|
|
36
|
+
{ code: 'const HogeButton = styled.button``', errors: [ { message: ERRORMESSAGE_PROHIBIT_STYLED } ] },
|
|
37
|
+
]
|
|
38
|
+
})
|