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.
|
|
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.
|
|
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": "
|
|
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
|
+
})
|