eslint-plugin-smarthr 1.11.0 → 2.1.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +0 -1
  3. package/libs/format_styled_components.js +1 -1
  4. package/package.json +4 -4
  5. package/rules/a11y-anchor-has-href-attribute/index.js +44 -80
  6. package/rules/a11y-clickable-element-has-text/index.js +9 -76
  7. package/rules/a11y-image-has-alt-attribute/index.js +14 -51
  8. package/rules/a11y-input-has-name-attribute/index.js +34 -70
  9. package/rules/a11y-prohibit-input-maxlength-attribute/index.js +5 -11
  10. package/rules/a11y-prohibit-input-placeholder/index.js +31 -52
  11. package/rules/a11y-prohibit-useless-sectioning-fragment/index.js +9 -44
  12. package/rules/a11y-trigger-has-button/index.js +18 -36
  13. package/rules/best-practice-for-async-current-target/index.js +14 -21
  14. package/rules/best-practice-for-button-element/index.js +10 -23
  15. package/rules/best-practice-for-data-test-attribute/index.js +9 -12
  16. package/rules/best-practice-for-date/index.js +16 -29
  17. package/rules/best-practice-for-nested-attributes-array-index/index.js +7 -15
  18. package/rules/best-practice-for-remote-trigger-dialog/index.js +10 -23
  19. package/rules/best-practice-for-tailwind-prohibit-root-margin/index.js +5 -93
  20. package/rules/design-system-guideline-prohibit-double-icons/index.js +6 -31
  21. package/rules/prohibit-export-array-type/index.js +6 -9
  22. package/rules/require-i18n-text/README.md +123 -0
  23. package/rules/require-i18n-text/index.js +94 -0
  24. package/test/a11y-clickable-element-has-text.js +0 -4
  25. package/test/a11y-image-has-alt-attribute.js +0 -1
  26. package/test/a11y-prohibit-useless-sectioning-fragment.js +7 -7
  27. package/test/a11y-trigger-has-button.js +8 -7
  28. package/test/best-practice-for-button-element.js +0 -3
  29. package/test/best-practice-for-data-test-attribute.js +9 -8
  30. package/test/best-practice-for-remote-trigger-dialog.js +3 -9
  31. package/test/best-practice-for-tailwind-prohibit-root-margin.js +16 -7
  32. package/test/design-system-guideline-prohibit-double-icons.js +1 -3
  33. package/test/require-i18n-text.js +170 -0
  34. package/rules/a11y-replace-unreadable-symbol/README.md +0 -38
  35. package/rules/a11y-replace-unreadable-symbol/index.js +0 -31
  36. package/test/a11y-replace-unreadable-symbol.js +0 -35
@@ -1,7 +1,5 @@
1
1
  const SCHEMA = []
2
2
 
3
- const REGEX_PATTERN = /(Button|Link)$/
4
-
5
3
  /**
6
4
  * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
7
5
  */
@@ -12,35 +10,12 @@ module.exports = {
12
10
  },
13
11
  create(context) {
14
12
  return {
15
- JSXOpeningElement: (node) => {
16
- const nodeName = node.name.name
17
-
18
- if (REGEX_PATTERN.test(nodeName)) {
19
- let prefix = null
20
- let suffix = null
21
-
22
- for (const attr of node.attributes) {
23
- switch (attr.name.name) {
24
- case 'prefix':
25
- prefix = attr
26
- break
27
- case 'suffix':
28
- suffix = attr
29
- break
30
- }
31
-
32
- if(prefix && suffix) {
33
- context.report({
34
- node,
35
- message: `${nodeName} には prefix と suffix は同時に設定できません。
36
- - prefix または suffix のみを設定してください。
37
- - どちらにもアイコンをつけられそうな場合は、アイコン付き(右)(サフィックス)を優先し、アイコン付き(左)(プレフィックス)には指定しないでください。
38
- - 両方設定したい場合は、'eslint-disable-next-line' 等を利用して、このルールを無効化してください。`,
39
- })
40
- break
41
- }
42
- }
43
- }
13
+ 'JSXOpeningElement[name.name=/(Button|Link)$/]:has(JSXAttribute[name.name="prefix"]):has(JSXAttribute[name.name="suffix"])': (node) => {
14
+ context.report({
15
+ node,
16
+ message: `${node.name.name} には prefix と suffix は同時に設定できません。
17
+ - どちらにもアイコンをつけられそうな場合は、prefixを優先してください。`,
18
+ })
44
19
  }
45
20
  }
46
21
  },
@@ -7,18 +7,15 @@ module.exports = {
7
7
  schema: [],
8
8
  },
9
9
  create(context) {
10
- const checker = (node) => {
11
- if (node.declaration?.typeAnnotation?.type === 'TSArrayType') {
10
+ return {
11
+ ':matches(TSTypeReference,ExportNamedDeclaration):matches([declaration.typeAnnotation.type="TSArrayType"],[declaration.typeAnnotation.typeName.name="Array"])': (node) => {
12
12
  context.report({
13
13
  node,
14
- message: '利用する際、配列かどうかわかりにくいため、配列ではない状態でexportしてください',
14
+ message: `型をexportする際、配列ではなくアイテムの型をexportしてください。
15
+ - 型を配列でexportすると、その型が配列かどうかを判定するための情報は名称のみになります
16
+ - 名称から配列かどうかを判定しにくい場合があるため、利用するファイル内で配列として型を設定してください`,
15
17
  })
16
- }
17
- }
18
-
19
- return {
20
- ExportDefaultDeclaration: checker,
21
- ExportNamedDeclaration: checker,
18
+ },
22
19
  }
23
20
  },
24
21
  }
@@ -0,0 +1,123 @@
1
+ # smarthr/require-i18n-text
2
+
3
+ - JSX/TSXファイル内で文字列リテラルが直接指定されていないかをチェックするルールです
4
+ - 多言語化対応での翻訳対応漏れを防ぐために使用します
5
+ - HTML要素の属性、カスタムコンポーネントの属性、子要素の文字列リテラルを検査対象とします
6
+ - 数値リテラル、真偽値、空文字列は検査対象外です
7
+
8
+ ## rules
9
+
10
+ ```js
11
+ {
12
+ rules: {
13
+ 'smarthr/require-i18n-text': [
14
+ 'error', // 'warn', 'off'
15
+ {
16
+ elements: {
17
+ // HTML要素
18
+ 'img': ['alt', 'title'],
19
+ 'input': ['placeholder', 'title'],
20
+ 'button': ['title', 'aria-label'],
21
+
22
+ // カスタムコンポーネント
23
+ 'Button': ['label', 'errorMessage'],
24
+ 'Input': ['placeholder', 'helperText'],
25
+ 'Dialog': ['title', 'description'],
26
+
27
+ // すべての要素に適用
28
+ '*': ['data-tooltip']
29
+ }
30
+ }
31
+ ]
32
+ },
33
+ }
34
+ ```
35
+
36
+ ## options
37
+
38
+ ### elements
39
+
40
+ HTML要素とカスタムコンポーネントごとに検査対象とする属性名を指定します。
41
+ オブジェクトのキーに要素名/コンポーネント名、値に属性名の配列を指定します。
42
+
43
+ デフォルト: `{ '*': ['alt', 'aria-label', 'term', 'title'] }`
44
+
45
+ オプションを指定しない場合、デフォルトでi18n候補属性がすべての要素で検査されます。
46
+
47
+ #### ワイルドカード `'*'` の使用
48
+
49
+ `'*'` をキーとして使用すると、すべての要素(HTML要素とカスタムコンポーネント両方)に対して属性をチェックできます。
50
+ 個別の要素設定がある場合は、そちらが優先されます。
51
+
52
+ ```js
53
+ {
54
+ elements: {
55
+ // すべての要素で title をチェック
56
+ '*': ['title'],
57
+ // img要素は alt も追加でチェック
58
+ 'img': ['alt', 'title'],
59
+ // Icon は除外(空配列で上書き)
60
+ 'Icon': []
61
+ }
62
+ }
63
+ ```
64
+
65
+ #### デフォルト設定の上書き
66
+
67
+ ワイルドカード `'*'` を明示的に設定すると、デフォルト設定を上書きできます。
68
+
69
+ ```js
70
+ {
71
+ elements: {
72
+ // デフォルトを上書き
73
+ '*': ['data-tooltip'],
74
+ // 個別設定も可能
75
+ 'Button': ['label']
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## ❌ Incorrect
81
+
82
+ ```jsx
83
+ // HTML要素の属性に文字列リテラルを直接指定
84
+ <img alt="Profile picture" />
85
+ <input placeholder="Enter your name" />
86
+ <button title="Close dialog" />
87
+
88
+ // カスタムコンポーネントの属性に文字列リテラルを直接指定
89
+ <Button label="Submit" />
90
+ <Input helperText="Required field" />
91
+
92
+ // 子要素に文字列リテラルを直接指定
93
+ <div>Hello World</div>
94
+ <Button>Submit</Button>
95
+ <p>Welcome to our application</p>
96
+ ```
97
+
98
+ ## ✅ Correct
99
+
100
+ ```jsx
101
+ // 翻訳関数を使用
102
+ <img alt={t('profile_picture')} />
103
+ <input placeholder={t('enter_your_name')} />
104
+ <button title={t('close_dialog')} />
105
+
106
+ // カスタムコンポーネントでも翻訳関数を使用
107
+ <Button label={t('submit')} />
108
+ <Input helperText={t('required_field')} />
109
+
110
+ // 子要素でも翻訳関数を使用
111
+ <div>{t('hello_world')}</div>
112
+ <Button>{t('submit')}</Button>
113
+ <p>{t('welcome_message')}</p>
114
+
115
+ // 数値リテラル、真偽値、空文字列は検査対象外
116
+ <div>{123}</div>
117
+ <Button disabled={true} />
118
+ <input value="" />
119
+
120
+ // 検査対象外の属性(設定していない属性)
121
+ <input name="username" />
122
+ <div id="main-content" />
123
+ ```
@@ -0,0 +1,94 @@
1
+ // デフォルトのワイルドカード設定
2
+ const DEFAULT_WILDCARD_ATTRIBUTES = [
3
+ 'alt',
4
+ 'aria-label',
5
+ // smarthr-ui DefinitionListItem
6
+ 'term',
7
+ 'title',
8
+ ]
9
+
10
+ const SCHEMA = [
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ elements: {
15
+ type: 'object',
16
+ patternProperties: {
17
+ '.+': {
18
+ type: 'array',
19
+ items: {
20
+ type: 'string',
21
+ },
22
+ },
23
+ },
24
+ default: {},
25
+ },
26
+ },
27
+ additionalProperties: false,
28
+ },
29
+ ]
30
+
31
+ // 文字列リテラルを持つ属性を選択するセレクタの条件部分
32
+ const STRING_LITERAL_CONDITION =
33
+ ':matches([value.type="Literal"][value.value=/\\S/], [value.type="JSXExpressionContainer"][value.expression.type="Literal"][value.expression.value=/\\S/])'
34
+
35
+ const generateAttributeSelector = (attributes) =>
36
+ `JSXAttribute[name.name=/^(${attributes.join('|')})$/]${STRING_LITERAL_CONDITION}`
37
+
38
+ /**
39
+ * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
40
+ */
41
+ module.exports = {
42
+ meta: {
43
+ type: 'suggestion',
44
+ schema: SCHEMA,
45
+ },
46
+ create(context) {
47
+ const options = context.options[0] || {}
48
+ const elementsObj = options.elements || {}
49
+
50
+ // ユーザーが'*'を設定していない場合のみデフォルトを適用
51
+ const wildcardAttributes = elementsObj['*'] || DEFAULT_WILDCARD_ATTRIBUTES
52
+ const specificElements = Object.keys(elementsObj).filter((k) => k !== '*')
53
+ const handlers = {}
54
+
55
+ const reportAttributeError = (node) => {
56
+ context.report({
57
+ node,
58
+ message: `${node.parent.name.name}の${node.name.name}属性に文字列リテラルが指定されています。多言語化対応のため、翻訳関数を使用してください`,
59
+ })
60
+ }
61
+
62
+ // 個別要素の設定
63
+ for (const elementName of specificElements) {
64
+ const attributes = elementsObj[elementName]
65
+ if (attributes.length === 0) continue
66
+
67
+ handlers[`JSXOpeningElement[name.name="${elementName}"] > ${generateAttributeSelector(attributes)}`] = reportAttributeError
68
+ }
69
+
70
+ // ワイルドカード設定
71
+ if (wildcardAttributes && wildcardAttributes.length > 0) {
72
+ const attributeSelector = generateAttributeSelector(wildcardAttributes)
73
+ if (specificElements.length > 0) {
74
+ // 個別設定要素を除外
75
+ handlers[`JSXOpeningElement:not([name.name=/^(${specificElements.join('|')})$/]) > ${attributeSelector}`] =
76
+ reportAttributeError
77
+ } else {
78
+ handlers[attributeSelector] = reportAttributeError
79
+ }
80
+ }
81
+
82
+ // 子要素の文字列リテラルチェック(空白のみのテキストは除外)
83
+ handlers['JSXText[value=/\\S/]'] = (node) => {
84
+ context.report({
85
+ node,
86
+ message: '子要素に文字列リテラルが指定されています。多言語化対応のため、翻訳関数を使用してください',
87
+ })
88
+ }
89
+
90
+ return handlers
91
+ },
92
+ }
93
+
94
+ module.exports.schema = SCHEMA
@@ -142,10 +142,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
142
142
  code: `<button><SmartHRLogoSuffix /></button>`,
143
143
  errors: [{ message: defaultErrorMessage }]
144
144
  },
145
- {
146
- code: `<a><div role="article" aria-label="hoge" /></a>`,
147
- errors: [{ message: defaultErrorMessage }]
148
- },
149
145
  {
150
146
  code: `<a><TextWithHoge /></a>`,
151
147
  errors: [{ message: defaultErrorMessage }]
@@ -33,7 +33,6 @@ ruleTester.run('a11y-image-has-alt-attribute', rule, {
33
33
  invalid: [
34
34
  { code: '<img />', errors: [ { message: messageNotExistAlt } ] },
35
35
  { code: '<HogeImage alt="" />', errors: [ { message: messageNullAlt } ] },
36
- { code: '<hoge><image /></hoge>', errors: [ { message: messageNotExistAlt } ] },
37
36
  { code: '<AnyImg {...hoge} />', errors: [ { message: messageNotExistAlt } ] },
38
37
  { code: '<AnyImg {...hoge} />', options: [{ checkType: 'always' }], errors: [ { message: messageNotExistAlt } ] },
39
38
  ]
@@ -10,7 +10,7 @@ const ruleTester = new RuleTester({
10
10
  },
11
11
  },
12
12
  })
13
- const error = (tag) => `無意味なSectioningFragmentが記述されています。子要素である${tag}で問題なくセクションは設定されているため、このSectioningFragmentは削除してください`
13
+ const ERROR = `無意味なSectioningFragmentが記述されています。子要素で問題なくセクションは設定されているため、このSectioningFragmentは削除してください`
14
14
 
15
15
  ruleTester.run('a11y-prohibit-useless-sectioning-fragment', rule, {
16
16
  valid: [
@@ -21,11 +21,11 @@ ruleTester.run('a11y-prohibit-useless-sectioning-fragment', rule, {
21
21
  { code: `<AnyArticle>hoge</AnyArticle>` },
22
22
  ],
23
23
  invalid: [
24
- { code: `<SectioningFragment><AnySection /></SectioningFragment>`, errors: [ { message: error('<AnySection>') } ] },
25
- { code: `<SectioningFragment><AnyAside>hoge</AnyAside></SectioningFragment>`, errors: [ { message: error('<AnyAside>') } ] },
26
- { code: `<SectioningFragment><HogeStack as="aside">hoge</HogeStack></SectioningFragment>`, errors: [ { message: error('<HogeStack as="aside">') } ] },
27
- { code: `<SectioningFragment><HogeReel forwardedAs="nav">hoge</HogeReel></SectioningFragment>`, errors: [ { message: error('<HogeReel forwardedAs="nav">') } ] },
28
- { code: `<SectioningFragment><FugaBase as="article">hoge</FugaBase></SectioningFragment>`, errors: [ { message: error('<FugaBase as="article">') } ] },
29
- { code: `<SectioningFragment><FugaBaseColumn as="article">hoge</FugaBaseColumn></SectioningFragment>`, errors: [ { message: error('<FugaBaseColumn as="article">') } ] },
24
+ { code: `<SectioningFragment><AnySection /></SectioningFragment>`, errors: [ { message: ERROR } ] },
25
+ { code: `<SectioningFragment><AnyAside>hoge</AnyAside></SectioningFragment>`, errors: [ { message: ERROR } ] },
26
+ { code: `<SectioningFragment><HogeStack as="aside">hoge</HogeStack></SectioningFragment>`, errors: [ { message: ERROR } ] },
27
+ { code: `<SectioningFragment><HogeReel forwardedAs="nav">hoge</HogeReel></SectioningFragment>`, errors: [ { message: ERROR } ] },
28
+ { code: `<SectioningFragment><FugaBase as="article">hoge</FugaBase></SectioningFragment>`, errors: [ { message: ERROR } ] },
29
+ { code: `<SectioningFragment><FugaBaseColumn as="article">hoge</FugaBaseColumn></SectioningFragment>`, errors: [ { message: ERROR } ] },
30
30
  ]
31
31
  })
@@ -13,15 +13,16 @@ const ruleTester = new RuleTester({
13
13
  ruleTester.run('a11y-trigger-has-button', rule, {
14
14
  valid: [
15
15
  { code: '<DropdownTrigger><button>hoge</button></DropdownTrigger>' },
16
- { code: '<DialogTrigger><button>{hoge}</button></DialogTrigger>' },
16
+ { code: '<DialogTrigger><AnyButton>{hoge}</AnyButton></DialogTrigger>' },
17
17
  { code: '<DropdownTrigger>{hoge}</DropdownTrigger>' },
18
+ { code: '<AnyDropdownTrigger/>' },
18
19
  ],
19
20
  invalid: [
20
- { code: '<DropdownTrigger>ほげ</DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
21
- { code: '<DialogTrigger><span><Button>ほげ</Button></span></DialogTrigger>', errors: [ { message: 'DialogTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
22
- { code: '<DropdownTrigger><AnchorButton>ほげ</AnchorButton></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
23
- { code: '<DropdownTrigger><ButtonAnchor>ほげ</ButtonAnchor></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
24
- { code: '<DialogTrigger><button>{hoge}</button>{hoge}</DialogTrigger>', errors: [ { message: 'DialogTrigger の直下には複数のコンポーネントを設置することは出来ません。buttonコンポーネントが一つだけ設置されている状態にしてください' } ] },
25
- { code: '<DropdownTrigger>{hoge}<span>text</span></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下には複数のコンポーネントを設置することは出来ません。buttonコンポーネントが一つだけ設置されている状態にしてください' } ] },
21
+ { code: '<DropdownTrigger>ほげ</DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbutton要素のみ設置してください(AnchorButtonはa要素のため設置できません)' } ] },
22
+ { code: '<DialogTrigger><span><Button>ほげ</Button></span></DialogTrigger>', errors: [ { message: 'DialogTrigger の直下にはbutton要素のみ設置してください(AnchorButtonはa要素のため設置できません)' } ] },
23
+ { code: '<DropdownTrigger><AnchorButton>ほげ</AnchorButton></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbutton要素のみ設置してください(AnchorButtonはa要素のため設置できません)' } ] },
24
+ { code: '<DropdownTrigger><ButtonAnchor>ほげ</ButtonAnchor></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbutton要素のみ設置してください(AnchorButtonはa要素のため設置できません)' } ] },
25
+ { code: '<DialogTrigger><button>{hoge}</button>{hoge}</DialogTrigger>', errors: [ { message: 'DialogTrigger の直下には複数のコンポーネントを設置することは出来ません。button要素が一つだけ設置されている状態にしてください' } ] },
26
+ { code: '<DropdownTrigger>{hoge}<span>text</span></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下には複数のコンポーネントを設置することは出来ません。button要素が一つだけ設置されている状態にしてください' } ] },
26
27
  ]
27
28
  })
@@ -19,8 +19,6 @@ const ERRORMESSAGE_PROHIBIT_STYLED = `"styled.button" の直接利用をやめ
19
19
 
20
20
  ruleTester.run('best-practice-for-button-element', rule, {
21
21
  valid: [
22
- { code: `import styled from 'styled-components'` },
23
- { code: `import styled, { css } from 'styled-components'` },
24
22
  { code: `<Button />` },
25
23
  { code: `<Button>ほげ</Button>` },
26
24
  { code: `<AnyButton>ほげ</AnyButton>` },
@@ -29,7 +27,6 @@ ruleTester.run('best-practice-for-button-element', rule, {
29
27
  { code: 'const HogeButton = styled(HogeButton)``' },
30
28
  ],
31
29
  invalid: [
32
- { code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
33
30
  { code: `<button>ほげ</button>`, errors: [ { message: ERRORMESSAGE_REQUIRED_TYPE_ATTR } ] },
34
31
  { code: 'const HogeButton = styled.button``', errors: [ { message: ERRORMESSAGE_PROHIBIT_STYLED } ] },
35
32
  ]
@@ -10,12 +10,13 @@ const ruleTester = new RuleTester({
10
10
  },
11
11
  },
12
12
  })
13
- const generateErrorMessage = (attr) => `テストのために要素を指定するために、${attr} 属性を利用するのではなく、他の方法で要素を指定することを検討してください。
14
- - 方法1: click_link, click_button等を利用したりすることで、利用しているテスト環境に準じた方法で要素を指定することを検討してください。
13
+ const ERROR_MESSAGE = `テストしたい要素を指定するためにテスト用の属性は利用せず、他の方法を検討してください
14
+ - 方法1: click_link, click_button等を利用することで、テスト環境に準じた方法で要素を指定することを検討してください
15
15
  - 参考(Testing Library): https://testing-library.com/docs/queries/about
16
16
  - 参考(Capybara): https://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Finders
17
- - 方法2: テスト環境のメソッド等で要素が指定できない場合はrole属性、name属性、id属性等を利用した方法で要素を指定することを検討してください。
18
- - 方法3: 上記の方法でも要素が指定できない場合は、'eslint-disable-next-line' 等を利用して、このルールを無効化してください。`
17
+ - 方法2: テスト環境のメソッド等で要素が指定できない場合はrolename、aria系などユーザーが認識できる属性を利用した方法で要素を指定することを検討してください
18
+ - 画像の場合、alt属性が利用できます
19
+ - id, class属性は基本的にユーザーが認識出来ないため利用しないでください`
19
20
 
20
21
 
21
22
  ruleTester.run('best-practice-for-data-test-attribute', rule, {
@@ -25,9 +26,9 @@ ruleTester.run('best-practice-for-data-test-attribute', rule, {
25
26
  { code: '<Any data-any="fuga">ほげ</Any>'},
26
27
  ],
27
28
  invalid: [
28
- { code: '<Any data-spec="hijklmn">ほげ</Any>', errors: [{message: generateErrorMessage("data-spec")}] },
29
- { code: '<Any data-spec>ほげ</Any>', errors: [{message: generateErrorMessage("data-spec")}] },
30
- { code: '<Any data-testid="abcdefg">ほげ</Any>', errors: [{message: generateErrorMessage("data-testid")}] },
31
- { code: '<Any data-testid>ほげ</Any>', errors: [{message: generateErrorMessage("data-testid")}] },
29
+ { code: '<Any data-spec="hijklmn">ほげ</Any>', errors: [{message: ERROR_MESSAGE}] },
30
+ { code: '<Any data-spec>ほげ</Any>', errors: [{message: ERROR_MESSAGE}] },
31
+ { code: '<Any data-testid="abcdefg">ほげ</Any>', errors: [{message: ERROR_MESSAGE}] },
32
+ { code: '<Any data-testid>ほげ</Any>', errors: [{message: ERROR_MESSAGE}] },
32
33
  ]
33
34
  })
@@ -19,16 +19,10 @@ ruleTester.run('best-practice-for-remote-trigger-dialog', rule, {
19
19
  ],
20
20
  invalid: [
21
21
  { code: '<RemoteDialogTrigger targetId={hoge}>open.</RemoteDialogTrigger>', errors: [ { message: `RemoteDialogTriggerのtargetId属性には直接文字列を指定してください。
22
- - 変数などは利用できません(これは関連するTriggerとDialogを検索しやすくするためです)
23
- - RemoteTriggerActionDialogはループやDropdown内にTriggerが存在する場合に利用してください
24
- - ループやDropdown以外にTriggerが設定されている場合、TriggerAndActionDialogを利用してください` } ] },
22
+ - 変数などは利用できません(これは関連するTriggerとDialogを検索しやすくするためです)` } ] },
25
23
  { code: '<StyledRemoteDialogTrigger targetId={"fuga"}>open.</StyledRemoteDialogTrigger>', errors: [ { message: `StyledRemoteDialogTriggerのtargetId属性には直接文字列を指定してください。
26
- - 変数などは利用できません(これは関連するTriggerとDialogを検索しやすくするためです)
27
- - RemoteTriggerActionDialogはループやDropdown内にTriggerが存在する場合に利用してください
28
- - ループやDropdown以外にTriggerが設定されている場合、TriggerAndActionDialogを利用してください` } ] },
24
+ - 変数などは利用できません(これは関連するTriggerとDialogを検索しやすくするためです)` } ] },
29
25
  { code: '<StyldRemoteTriggerActionDialog {...args} id={"fuga"}>content.</StyldRemoteTriggerActionDialog>', errors: [ { message: `StyldRemoteTriggerActionDialogのid属性には直接文字列を指定してください。
30
- - 変数などは利用できません(これは関連するTriggerとDialogを検索しやすくするためです)
31
- - RemoteTriggerActionDialogはループやDropdown内にTriggerが存在する場合に利用してください
32
- - ループやDropdown以外にTriggerが設定されている場合、TriggerAndActionDialogを利用してください` } ] },
26
+ - 変数などは利用できません(これは関連するTriggerとDialogを検索しやすくするためです)` } ] },
33
27
  ]
34
28
  })
@@ -25,15 +25,24 @@ ruleTester.run('best-practice-for-tailwind-prohibit-root-margin', rule, {
25
25
  `,
26
26
  },
27
27
  // コンポーネントのルート以外の要素での余白使用
28
+ {
29
+ code: `
30
+ const Card = () => (
31
+ <div className="shr-bg-white">
32
+ <h2 className="shr-mt-4">Title</h2>
33
+ <p className="shr-mb-2 shr-pt-2">Content</p>
34
+ </div>
35
+ )
36
+ `,
37
+ },
38
+ // returnしない場合
28
39
  {
29
40
  code: `
30
41
  const Card = () => {
31
- return (
32
- <div className="shr-bg-white">
33
- <h2 className="shr-mt-4">Title</h2>
34
- <p className="shr-mb-2 shr-pt-2">Content</p>
35
- </div>
36
- )
42
+ <div className="shr-bg-white">
43
+ <h2 className="shr-mt-4">Title</h2>
44
+ <p className="shr-mb-2 shr-pt-2">Content</p>
45
+ </div>
37
46
  }
38
47
  `,
39
48
  },
@@ -91,7 +100,7 @@ ruleTester.run('best-practice-for-tailwind-prohibit-root-margin', rule, {
91
100
  {
92
101
  code: `
93
102
  const Box = () => (
94
- <div className="shr-bg-gray-100 shr-ml-2">
103
+ <div className="shr-bg-gray-100 shr-ml-2 hoge">
95
104
  <p>Content</p>
96
105
  </div>
97
106
  )
@@ -11,9 +11,7 @@ const ruleTester = new RuleTester({
11
11
  },
12
12
  })
13
13
  const generateErrorText = (name) => `${name} には prefix と suffix は同時に設定できません。
14
- - prefix または suffix のみを設定してください。
15
- - どちらにもアイコンをつけられそうな場合は、アイコン付き(右)(サフィックス)を優先し、アイコン付き(左)(プレフィックス)には指定しないでください。
16
- - 両方設定したい場合は、'eslint-disable-next-line' 等を利用して、このルールを無効化してください。`
14
+ - どちらにもアイコンをつけられそうな場合は、prefixを優先してください。`
17
15
 
18
16
  ruleTester.run('design-system-guideline-prohibit-double-icons', rule, {
19
17
  valid: [