eslint-plugin-smarthr 0.4.1 → 0.5.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 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
+ ## [0.5.0](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.2...v0.5.0) (2024-03-11)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * spread attributesが指定されている場合、ruleをcorrectにする smartr オプションを allow-spread-attributes オプションにリネームする (#119)
11
+
12
+ ### Bug Fixes
13
+
14
+ * 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))
15
+
16
+ ### [0.4.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.1...v0.4.2) (2024-03-03)
17
+
18
+
19
+ ### Features
20
+
21
+ * 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))
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * 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))
27
+
5
28
  ### [0.4.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.0...v0.4.1) (2024-02-21)
6
29
 
7
30
 
@@ -321,7 +344,7 @@ All notable changes to this project will be documented in this file. See [standa
321
344
  ### [0.2.14](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.13...v0.2.14) (2022-12-13)
322
345
 
323
346
  ### Features
324
- * trim-propsルールを追加 ([#44](https://github.com/kufu/eslint-plugin-smarthr/pull/44))
347
+ * trim-propsルールを追加 ([#44](https://github.com/kufu/eslint-plugin-smarthr/pull/44))
325
348
 
326
349
  ### [0.2.13](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.12...v0.2.13) (2022-12-07)
327
350
 
@@ -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
- if (!node.init) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
@@ -6,7 +6,7 @@
6
6
  - URL遷移を行う場合、hrefが設定されていないとキーボード操作やコンテキストメニューからの遷移ができなくなります
7
7
  - これらの操作は href属性を参照します
8
8
  - 無効化されたリンクであることを表したい場合 `href={undefined}` を設定してください
9
- - checkTypeオプションに 'smart' を指定することで spread attributeが設定されている場合はcorrectに出来ます
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' || 'smart' */
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: 'smart'
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 !== 'smart' || !node.attributes.some(findSpreadAttr))
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', 'smart'], default: 'always' },
90
+ checkType: { type: 'string', enum: ['always', 'allow-spread-attributes'], default: 'always' },
91
91
  },
92
92
  additionalProperties: false,
93
93
  }
@@ -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
- <YyyAnchoor />
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 HIT_TYPES_RECURSICVE_SEARCH = ['JSXText', 'JSXExpressionContainer']
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 (HIT_TYPES_RECURSICVE_SEARCH.includes(c.type)) {
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
- !HIT_TEXT_ATTRS.includes(a.name.name)
100
+ HIT_TEXT_ATTR !== a.name.name
101
101
  ) {
102
102
  return prev
103
103
  }
@@ -1,10 +1,10 @@
1
- # smarthr/a11y-delegate-element-has-role-presantation
1
+ # smarthr/a11y-delegate-element-has-role-presentation
2
2
 
3
- - 'role="presantation"'を適切に設定することを促すルールです
4
- - インタラクティブな要素に対して'role="presantation"'が設定されている場合、エラーになります
3
+ - 'role="presentation"'を適切に設定することを促すルールです
4
+ - インタラクティブな要素に対して'role="presentation"'が設定されている場合、エラーになります
5
5
  - インタラクティブな要素とは form, inputなどの入力要素、button, a などのクリッカブルな要素を指します
6
- - インタラクティブな要素から発生するイベントを親要素でキャッチする場合、親要素に 'role="presantation"' を設定することを促します
7
- - インタラクティブではない要素でイベントをキャッチしており、かつ'role="presantation"'を設定しているにも関わらず、子要素にインタラクティブな要素がない場合はエラーになります
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-presantation': [
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="presantation" は設定できない
27
- <Button role="presantation">text.</Button>
28
- <input type="text" role="presantation" />
26
+ // インタラクティブな要素に対して role="presentation" は設定できない
27
+ <Button role="presentation">text.</Button>
28
+ <input type="text" role="presentation" />
29
29
 
30
30
  // インタラクティブな要素で発生するイベントを非インタラクティブな要素でキャッチする場合
31
- // role="presantation" を設定する必要がある
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="presantation" を設定する
47
+ // role="presentation" を設定する
48
48
  <div onClick={hoge} role="presentation">
49
49
  <Button>text.</Button>
50
50
  </div>
@@ -1,7 +1,7 @@
1
1
  # smarthr/a11y-image-has-alt-attribute
2
2
 
3
3
  - 画像やアイコンにalt属性を設定することを強制するルールです
4
- - checkTypeオプションに 'smart' を指定することで spread attributeが設定されている場合はcorrectに出来ます。
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' || 'smart' */
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: 'smart'
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', 'smart'], default: '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 !== 'smart' ||
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オプションに 'smart' を指定することで spread attributeが設定されている場合はcorrectに出来ます。
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' || 'smart' */
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: 'smart'
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', 'smart'], default: '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 !== 'smart' ||
63
+ checkType !== 'allow-spread-attributes' ||
64
64
  !node.attributes.some(findSpreadAttr)
65
65
  ) {
66
66
  const isRadio =
@@ -6,20 +6,27 @@ const EXPECTED_NAMES = {
6
6
  }
7
7
  const UNEXPECTED_NAMES = EXPECTED_NAMES
8
8
 
9
- const NUMBERED_TEXT_REGEX = /^[\s\n]*(([1-9])([^1-9]{2})[^\s\n]*)/
9
+ const NUMBERED_TEXT_REGEX = /^[\s\n]*(([0-9])([^0-9]{2})[^\s\n]*)/
10
10
  const ORDERED_LIST_REGEX = /(Ordered(.*)List|^ol)$/
11
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'
12
16
 
13
17
  const searchOrderedList = (node) => {
14
18
  if (node.type === 'JSXElement' && node.openingElement.name?.name) {
15
19
  const name = node.openingElement.name.name
16
20
 
17
- if (name.match(ORDERED_LIST_REGEX)) {
18
- return node.openingElement
19
- } else if (name.match(SELECT_REGEX)) {
21
+ if (name.match(SELECT_REGEX)) {
20
22
  // HINT: select要素の場合、optionのラベルに連番がついている場合がありえるのでignoreする
21
23
  // 通常と処理を分けるためnullではなく0を返す
22
24
  return 0
25
+ } else if (
26
+ name.match(ORDERED_LIST_REGEX) ||
27
+ node.openingElement.attributes.find(findAsOlAttr)
28
+ ) {
29
+ return node.openingElement
23
30
  }
24
31
  }
25
32
 
@@ -121,7 +128,7 @@ module.exports = {
121
128
  return {
122
129
  ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
123
130
  JSXAttribute: (node) => {
124
- if (node.value?.value) {
131
+ if (node.value?.value && !IGNORE_ATTRIBUTE_REGEX.test(node.name?.name)) {
125
132
  checker(node, node.value.value.match(NUMBERED_TEXT_REGEX))
126
133
  }
127
134
  },
@@ -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
@@ -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: 'smart' }] },
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'"` } ] },
@@ -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 }]
@@ -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: 'smart' }] },
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: 'smart' }] },
50
- { code: '<Input {...args1} {...args2} />', options: [{ checkType: 'smart' }] },
51
- { code: '<Input {...args} hoge="fuga" />', options: [{ checkType: 'smart' }] },
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'"` } ] },
@@ -21,7 +21,10 @@ ruleTester.run('a11y-numbered-text-within-ol', rule, {
21
21
  { code: `<><p>2: hoge</p><p>1: hoge</p></>` },
22
22
  { code: `<ol><li>abc</li><li>def</li></ol>` },
23
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>` },
24
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" /></>` },
25
28
  ],
26
29
  invalid: [
27
30
  { code: `<><p>1: hoge</p><p>2: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
@@ -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
+ })