eslint-plugin-smarthr 1.9.0 → 1.10.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,13 @@
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
+ ## [1.10.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.9.0...eslint-plugin-smarthr-v1.10.0) (2025-10-01)
6
+
7
+
8
+ ### Features
9
+
10
+ * セル内のCheckboxおよびRadioButtonを禁止するルールを追加 ([#792](https://github.com/kufu/tamatebako/issues/792)) ([17ab980](https://github.com/kufu/tamatebako/commit/17ab98018c81a81376ac213e3309e60080ed9326))
11
+
5
12
  ## [1.9.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.8.1...eslint-plugin-smarthr-v1.9.0) (2025-09-08)
6
13
 
7
14
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
7
7
  "main": "index.js",
8
8
  "engines": {
9
- "node": ">=22.17.1"
9
+ "node": ">=22.18.0"
10
10
  },
11
11
  "scripts": {
12
12
  "test": "jest"
@@ -37,5 +37,5 @@
37
37
  "eslintplugin",
38
38
  "smarthr"
39
39
  ],
40
- "gitHead": "de243698bc64eeb8c3ac0592d96d129eabe1847a"
40
+ "gitHead": "343b735f1202c536e8bf9c897fdb4d5289455b8e"
41
41
  }
@@ -0,0 +1,40 @@
1
+ # smarthr/a11y-prohibit-checkbox-or-radio-in-table-cell
2
+
3
+ - テーブルセル(Th, Td)内に Checkbox, RadioButton を配置することを禁止するルールです
4
+ - SmartHR UI には、デフォルトでアクセシブルネームを設定する TdCheckbox, ThCheckbox, TdRadioButton といったより適切なコンポーネントが用意されています
5
+
6
+ ## rules
7
+
8
+ ```js
9
+ {
10
+ rules: {
11
+ 'smarthr/a11y-prohibit-checkbox-or-radio-in-table-cell': [
12
+ 'error', // 'warn', 'off'
13
+ ]
14
+ },
15
+ }
16
+ ```
17
+
18
+ ## ❌ Incorrect
19
+
20
+ ```jsx
21
+ <Td>
22
+ <Checkbox name="foo" />
23
+ </Td>
24
+
25
+ <Th>
26
+ <Checkbox name="bar" />
27
+ </Th>
28
+
29
+ <Td>
30
+ <RadioButton name="baz" />
31
+ </Td>
32
+ ```
33
+
34
+ ## ✅ Correct
35
+
36
+ ```jsx
37
+ <TdCheckbox name="foo" />
38
+ <ThCheckbox name="bar" />
39
+ <TdRadioButton name="baz" />
40
+ ```
@@ -0,0 +1,71 @@
1
+ const findClosestThFromAncestor = (node) => {
2
+ if (node.type === 'JSXElement' && node.openingElement.name.name === 'Th') {
3
+ return node
4
+ }
5
+ if (node.parent) {
6
+ return findClosestThFromAncestor(node.parent)
7
+ }
8
+ }
9
+
10
+ /**
11
+ * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
12
+ */
13
+ module.exports = {
14
+ meta: {
15
+ type: 'problem',
16
+ fixable: 'code',
17
+ schema: [],
18
+ messages: {
19
+ default: '{{cell}} の子孫に {{component}} を置くことはできません。代わりに {{preferred}} を使用してください。',
20
+ },
21
+ },
22
+ create(context) {
23
+ const sourceCode = context.sourceCode
24
+
25
+ return {
26
+ 'JSXElement[openingElement.name.name=/Td$/] JSXElement[openingElement.name.name=/Check(b|B)ox$/]': (node) => {
27
+ context.report({
28
+ node,
29
+ messageId: 'default',
30
+ data: {
31
+ cell: 'Td',
32
+ component: 'Checkbox',
33
+ preferred: 'TdCheckbox',
34
+ },
35
+ })
36
+ },
37
+ 'JSXElement[openingElement.name.name=/Td$/] JSXElement[openingElement.name.name=/RadioButton$/]': (node) => {
38
+ context.report({
39
+ node,
40
+ messageId: 'default',
41
+ data: {
42
+ cell: 'Td',
43
+ component: 'RadioButton',
44
+ preferred: 'TdRadioButton',
45
+ },
46
+ })
47
+ },
48
+ 'JSXElement[openingElement.name.name=/Th$/] JSXElement[openingElement.name.name=/Check(b|B)ox$/]': (node) => {
49
+ context.report({
50
+ node,
51
+ messageId: 'default',
52
+ data: {
53
+ cell: 'Th',
54
+ component: 'Checkbox',
55
+ preferred: 'ThCheckbox',
56
+ },
57
+ *fix(fixer) {
58
+ const th = findClosestThFromAncestor(node)
59
+ if (th) {
60
+ const thCheckbox = sourceCode.getText(node).replace(/<Check(b|B)ox/, '<ThCheckbox')
61
+ yield fixer.insertTextAfter(th, thCheckbox)
62
+ yield fixer.remove(th)
63
+ }
64
+ },
65
+ })
66
+ },
67
+ }
68
+ },
69
+ }
70
+
71
+ module.exports.schema = []
@@ -0,0 +1,73 @@
1
+ const rule = require('../rules/a11y-prohibit-checkbox-or-radio-in-table-cell')
2
+
3
+ const RuleTester = require('eslint').RuleTester
4
+
5
+ const ruleTester = new RuleTester({
6
+ languageOptions: {
7
+ parserOptions: {
8
+ ecmaFeatures: {
9
+ jsx: true,
10
+ },
11
+ },
12
+ },
13
+ })
14
+
15
+ ruleTester.run('a11y-prohibit-checkbox-or-radio-in-table-cell', rule, {
16
+ valid: ['<TdCheckbox />', '<ThCheckbox />', '<TdRadioButton />', '<Td>hello</Td>', '<Th>hello</Th>'],
17
+ invalid: [
18
+ {
19
+ code: `<Td><Checkbox /></Td>`,
20
+ errors: [{ message: 'Td の子孫に Checkbox を置くことはできません。代わりに TdCheckbox を使用してください。' }],
21
+ },
22
+ {
23
+ code: `<Th><Checkbox /></Th>`,
24
+ output: `<ThCheckbox />`,
25
+ errors: [{ message: 'Th の子孫に Checkbox を置くことはできません。代わりに ThCheckbox を使用してください。' }],
26
+ },
27
+ {
28
+ code: `<Th><Checkbox id="my-checkbox" name="agree" error /></Th>`,
29
+ output: `<ThCheckbox id="my-checkbox" name="agree" error />`,
30
+ errors: [{ message: 'Th の子孫に Checkbox を置くことはできません。代わりに ThCheckbox を使用してください。' }],
31
+ },
32
+ {
33
+ code: `<Td><RadioButton /></Td>`,
34
+ errors: [{ message: 'Td の子孫に RadioButton を置くことはできません。代わりに TdRadioButton を使用してください。' }],
35
+ },
36
+
37
+ {
38
+ code: `<Td><div><div><Checkbox /></div></div></Td>`,
39
+ errors: [{ message: 'Td の子孫に Checkbox を置くことはできません。代わりに TdCheckbox を使用してください。' }],
40
+ },
41
+ {
42
+ code: `<Td><><><Checkbox /></></></Td>`,
43
+ errors: [{ message: 'Td の子孫に Checkbox を置くことはできません。代わりに TdCheckbox を使用してください。' }],
44
+ },
45
+
46
+ {
47
+ code: `<CustomTd><CustomCheckbox /></CustomTd>`,
48
+ errors: [{ message: 'Td の子孫に Checkbox を置くことはできません。代わりに TdCheckbox を使用してください。' }],
49
+ },
50
+ {
51
+ code: `<CustomTh><CustomCheckbox /></CustomTh>`,
52
+ output: null,
53
+ errors: [{ message: 'Th の子孫に Checkbox を置くことはできません。代わりに ThCheckbox を使用してください。' }],
54
+ },
55
+ {
56
+ code: `<CustomTd><CustomRadioButton /></CustomTd>`,
57
+ errors: [{ message: 'Td の子孫に RadioButton を置くことはできません。代わりに TdRadioButton を使用してください。' }],
58
+ },
59
+ {
60
+ name: "https://smarthr.atlassian.net/browse/A11Y2-23",
61
+ code: `
62
+ <CheckTd onClick={() => toggleChecked(crewEvaluation.id)}>
63
+ <CheckBox
64
+ name="checkEvaluation"
65
+ checked={checked}
66
+ onChange={() => toggleChecked(crewEvaluation.id)}
67
+ />
68
+ </CheckTd>
69
+ `,
70
+ errors: [{ message: 'Td の子孫に Checkbox を置くことはできません。代わりに TdCheckbox を使用してください。' }],
71
+ }
72
+ ],
73
+ })