eslint-plugin-smarthr 0.2.9 → 0.2.12

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,32 @@
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.2.12](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.11...v0.2.12) (2022-12-07)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * ファイルが複数の.を保つ場合(例 xxx.test.tsx)正常に動作しないバグを修正する ([#42](https://github.com/kufu/eslint-plugin-smarthr/issues/42)) ([23eb5b5](https://github.com/kufu/eslint-plugin-smarthr/commit/23eb5b51039085d3239adca42c65395b81869f9d))
11
+
12
+ ### [0.2.11](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.10...v0.2.11) (2022-12-07)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * redundant-nameの省略文字列がファイルパスによって正常に生成できないバグを修正 ([49eb1d9](https://github.com/kufu/eslint-plugin-smarthr/commit/49eb1d9ad1e9c153b7cb150190a81e5fae6bedf6))
18
+
19
+ ### [0.2.10](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.9...v0.2.10) (2022-11-22)
20
+
21
+ ### Features
22
+
23
+ * a11y-prohibit-input-placeholder を最新のComboBoxに対応させる ([#39](https://github.com/kufu/eslint-plugin-smarthr/issues/39)) ([682281c](https://github.com/kufu/eslint-plugin-smarthr/pull/39/commits/682281cd0f6ed73b4ec1295f34680bd9576ba831))
24
+ * placeholder禁止対象にdate pickerが含まれていなかったため対応 ([#39](https://github.com/kufu/eslint-plugin-smarthr/issues/39)) ([abf89f0](https://github.com/kufu/eslint-plugin-smarthr/pull/39/commits/abf89f0fe5a88b4d03fdbd0e1ed344bae1c6397a))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * a11y-image-has-alt-attribute が svg > image を誤検知してしまうバグを修正する ([#40](https://github.com/kufu/eslint-plugin-smarthr/issues/40)) ([1f21879](https://github.com/kufu/eslint-plugin-smarthr/commit/1f21879a0309bfec15cfa186db4f6203cd80cc14))
30
+
5
31
  ### [0.2.9](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.8...v0.2.9) (2022-10-19)
6
32
 
7
33
 
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  - [a11y-clickable-element-has-text](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-clickable-element-has-text)
4
4
  - [a11y-image-has-alt-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-image-has-alt-attribute)
5
+ - [a11y-prohibit-input-placeholder](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-prohibit-input-placeholder)
5
6
  - [a11y-trigger-has-button](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-trigger-has-button)
6
7
  - [best-practice-for-date](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/best-practice-for-date)
7
8
  - [format-import-path](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/format-import-path)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "0.2.9",
3
+ "version": "0.2.12",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
@@ -7,6 +7,21 @@ const EXPECTED_NAMES = {
7
7
  '^(img|svg)$': '(Img|Image|Icon)$',
8
8
  }
9
9
 
10
+ const isWithinSvgJsxElement = (node) => {
11
+ if (
12
+ node.type === 'JSXElement' &&
13
+ node.openingElement.name?.name === 'svg'
14
+ ) {
15
+ return true
16
+ }
17
+
18
+ if (!node.parent) {
19
+ return false
20
+ }
21
+
22
+ return isWithinSvgJsxElement(node.parent)
23
+ }
24
+
10
25
  module.exports = {
11
26
  meta: {
12
27
  type: 'problem',
@@ -20,13 +35,16 @@ module.exports = {
20
35
  return {
21
36
  ...generateTagFormatter({ context, EXPECTED_NAMES }),
22
37
  JSXOpeningElement: (node) => {
23
- if ((node.name.name || '').match(/(img|image)$/i)) { // HINT: Iconは別途テキストが存在する場合が多いためチェックの対象外とする
38
+ const matcher = (node.name.name || '').match(/(img|image)$/i) // HINT: Iconは別途テキストが存在する場合が多いためチェックの対象外とする
39
+ if (matcher) {
24
40
  const alt = node.attributes.find((a) => a.name?.name === 'alt')
25
41
 
26
42
  let message = ''
27
43
 
28
44
  if (!alt) {
29
- message = '画像にはalt属性を指定してください。SVG component の場合、altを属性として受け取れるようにした上で `<svg role="img" aria-label={alt}>` のように指定してください。画像ではない場合、img or image を末尾に持たない名称に変更してください。'
45
+ if (matcher.input !== 'image' || !isWithinSvgJsxElement(node.parent)) {
46
+ message = '画像にはalt属性を指定してください。SVG component の場合、altを属性として受け取れるようにした上で `<svg role="img" aria-label={alt}>` のように指定してください。画像ではない場合、img or image を末尾に持たない名称に変更してください。'
47
+ }
30
48
  } else if (alt.value.value === '') {
31
49
  message = '画像の情報をテキストにした代替テキスト(`alt`)を設定してください。装飾目的の画像など、alt属性に指定すべき文字がない場合は背景画像にすることを検討してください。'
32
50
  }
@@ -6,6 +6,7 @@ const EXPECTED_NAMES = {
6
6
  '(t|T)extarea$': 'Textarea$',
7
7
  'FieldSet$': 'FieldSet$',
8
8
  'ComboBox$': 'ComboBox$',
9
+ 'DatePicker$': 'DatePicker$',
9
10
  }
10
11
 
11
12
  module.exports = {
@@ -27,7 +28,7 @@ module.exports = {
27
28
  return
28
29
  }
29
30
 
30
- if (!name.match(/((i|I)nput|(t|T)extarea|FieldSet|ComboBox)$/)) {
31
+ if (!name.match(/((i|I)nput|(t|T)extarea|FieldSet|ComboBox|DatePicker)$/)) {
31
32
  return
32
33
  }
33
34
 
@@ -46,6 +47,41 @@ module.exports = {
46
47
  },
47
48
  })
48
49
  }
50
+ } else if (name.match(/ComboBox$/)) {
51
+ let defaultItem
52
+ let dropdownHelpMessage
53
+
54
+ node.attributes.forEach((a) => {
55
+ switch(a.name?.name) {
56
+ case 'defaultItem':
57
+ defaultItem = a
58
+ break
59
+ case 'dropdownHelpMessage':
60
+ dropdownHelpMessage = a
61
+ break
62
+ }
63
+ })
64
+
65
+ if (defaultItem) {
66
+ context.report({
67
+ node: placeholder,
68
+ messageId: 'a11y-prohibit-input-placeholder',
69
+ data: {
70
+ message: `${name} にはdefaultItemが設定されているため、placeholder属性を閲覧出来ません。削除してください。`,
71
+ },
72
+ })
73
+ } else if (!dropdownHelpMessage) {
74
+ context.report({
75
+ node: placeholder,
76
+ messageId: 'a11y-prohibit-input-placeholder',
77
+ data: {
78
+ message: `${name} にはplaceholder属性は設定せず、以下のいずれか、もしくは組み合わせての対応を検討してください。
79
+ - 選択肢をどんな値で絞り込めるかの説明をしたい場合は dropdownHelpMessage 属性に変更してください。
80
+ - 空の値の説明のためにplaceholderを利用している場合は defaultItem 属性に変更してください。
81
+ - 上記以外の説明を行いたい場合、ヒント用要素を設置してください。(例: '<div><${name} /><Hint>ヒント</Hint></div>')`,
82
+ },
83
+ })
84
+ }
49
85
  } else {
50
86
  context.report({
51
87
  node: placeholder,
@@ -86,7 +86,8 @@ const fetchTerminalImportName = (filename) => {
86
86
  const generateRedundantKeywords = ({ args, key, terminalImportName }) => {
87
87
  const option = args.option[key] || {}
88
88
  const ignoreKeywords = option.ignoreKeywords || DEFAULT_CONFIG[key].IGNORE_KEYWORDS
89
- const terminalImportKeyword = terminalImportName ? terminalImportName.toLowerCase() : ''
89
+ const terminalImportKeyword = terminalImportName ? terminalImportName.toLowerCase() : ''
90
+
90
91
  return args.keywords.reduce((prev, keyword) => {
91
92
  if (keyword === terminalImportKeyword || ignoreKeywords.includes(keyword)) {
92
93
  return prev
@@ -100,7 +101,7 @@ const generateRedundantKeywords = ({ args, key, terminalImportName }) => {
100
101
  }, [])
101
102
  }
102
103
  const handleReportBetterName = ({
103
- key,
104
+ key,
104
105
  context,
105
106
  option,
106
107
  filename,
@@ -170,13 +171,13 @@ const handleReportBetterName = ({
170
171
  Object.entries(option.betterNames).forEach(([regex, calc]) => {
171
172
  if (calc && filename.match(new RegExp(regex))) {
172
173
  switch(calc.operator) {
173
- case '=':
174
+ case '=':
174
175
  candidates = calc.names
175
176
  break
176
- case '-':
177
+ case '-':
177
178
  candidates = candidates.filter((c) => !calc.names.includes(c))
178
179
  break
179
- case '+':
180
+ case '+':
180
181
  candidates = uniq([...candidates, ...calc.names])
181
182
  break
182
183
  }
@@ -412,12 +413,15 @@ module.exports = {
412
413
  let rules = {}
413
414
 
414
415
  const option = context.options[0]
415
- const filename = context.getFilename().match(/^(.+?)\..+?$/)[1]
416
+ let filename = context.getFilename()
416
417
  const keywords = uniq((() => {
417
418
  const keywordMatcher = filename.match(new RegExp(`${rootPath}/(.+?)$`))
418
419
 
419
420
  if (keywordMatcher) {
420
421
  const keywords = keywordMatcher[1].split('/')
422
+ keywords[keywords.length - 1] = keywords[keywords.length - 1].split('.')[0]
423
+
424
+ filename = keywords.join('/')
421
425
 
422
426
  if (keywords[keywords.length - 1] === 'index') {
423
427
  keywords.pop()
@@ -435,7 +439,7 @@ module.exports = {
435
439
  return []
436
440
  })())
437
441
 
438
- const args = {
442
+ const args = {
439
443
  context,
440
444
  option,
441
445
  filename,
@@ -30,6 +30,7 @@ ruleTester.run('a11y-image-has-alt-attribute', rule, {
30
30
  { code: '<HogeImg alt="hoge" />' },
31
31
  { code: '<HogeImage alt="hoge" />' },
32
32
  { code: '<HogeIcon />' },
33
+ { code: '<svg><image /></svg>' },
33
34
  ],
34
35
  invalid: [
35
36
  { code: `import hoge from 'styled-components'`, errors: [ { message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`" } ] },
@@ -40,5 +41,6 @@ ruleTester.run('a11y-image-has-alt-attribute', rule, {
40
41
  { code: 'const Hoge = styled(Image)``', errors: [ { message: `Hogeを正規表現 "/Image$/" がmatchする名称に変更してください` } ] },
41
42
  { code: '<img />', errors: [ { message: '画像にはalt属性を指定してください。SVG component の場合、altを属性として受け取れるようにした上で `<svg role="img" aria-label={alt}>` のように指定してください。画像ではない場合、img or image を末尾に持たない名称に変更してください。' } ] },
42
43
  { code: '<HogeImage alt="" />', errors: [ { message: '画像の情報をテキストにした代替テキスト(`alt`)を設定してください。装飾目的の画像など、alt属性に指定すべき文字がない場合は背景画像にすることを検討してください。' } ] },
44
+ { code: '<hoge><image /></hoge>', errors: [ { message: '画像にはalt属性を指定してください。SVG component の場合、altを属性として受け取れるようにした上で `<svg role="img" aria-label={alt}>` のように指定してください。画像ではない場合、img or image を末尾に持たない名称に変更してください。' } ] },
43
45
  ]
44
46
  })
@@ -35,8 +35,12 @@ ruleTester.run('a11y-prohibit-input-placeholder', rule, {
35
35
  { code: `<FugaFieldSet />` },
36
36
  { code: `<CustomComboBox />` },
37
37
  { code: `<SearchInput />` },
38
+ { code: `<DatePicker />` },
38
39
  { code: `<CustomSearchInput tooltipMessage="hoge" />` },
39
40
  { code: `<CustomSearchInput tooltipMessage="hoge" placeholder="fuga" />` },
41
+ { code: `<ComboBox defaultItem={items[0]} />` },
42
+ { code: `<ComboBox defaultItem={items[0]} dropdownHelpMessage="fuga" />` },
43
+ { code: `<ComboBox placeholder="hoge" dropdownHelpMessage="fuga" />` },
40
44
  ],
41
45
  invalid: [
42
46
  { code: `import hoge from 'styled-components'`, errors: [ { message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`" } ] },
@@ -58,7 +62,12 @@ ruleTester.run('a11y-prohibit-input-placeholder', rule, {
58
62
  { code: `<StyledInput placeholder={any} />`, errors: [ { message: `StyledInput にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><StyledInput /><Hint>ヒント</Hint></div>')` } ] },
59
63
  { code: `<HogeTextarea placeholder="any" />`, errors: [ { message: `HogeTextarea にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeTextarea /><Hint>ヒント</Hint></div>')` } ] },
60
64
  { code: `<HogeFieldSet placeholder="any" />`, errors: [ { message: `HogeFieldSet にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeFieldSet /><Hint>ヒント</Hint></div>')` } ] },
61
- { code: `<HogeComboBox placeholder="any" />`, errors: [ { message: `HogeComboBox にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeComboBox /><Hint>ヒント</Hint></div>')` } ] },
65
+ { code: `<HogeDatePicker placeholder="any" />`, errors: [ { message: `HogeDatePicker にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeDatePicker /><Hint>ヒント</Hint></div>')` } ] },
66
+ { code: `<HogeComboBox placeholder="any" />`, errors: [ { message: `HogeComboBox にはplaceholder属性は設定せず、以下のいずれか、もしくは組み合わせての対応を検討してください。
67
+ - 選択肢をどんな値で絞り込めるかの説明をしたい場合は dropdownHelpMessage 属性に変更してください。
68
+ - 空の値の説明のためにplaceholderを利用している場合は defaultItem 属性に変更してください。
69
+ - 上記以外の説明を行いたい場合、ヒント用要素を設置してください。(例: '<div><HogeComboBox /><Hint>ヒント</Hint></div>')` } ] },
62
70
  { code: `<SearchInput placeholder="any" />`, errors: [ { message: `SearchInput にはplaceholder属性を単独で利用せず、tooltipMessageオプションのみ、もしくはplaceholderとtooltipMessageの併用を検討してください。 (例: '<SearchInput tooltipMessage="ヒント" />', '<SearchInput tooltipMessage={hint} placeholder={hint} />')` } ] },
71
+ { code: `<ComboBox defaultItem={items[0]} placeholder="any" />`, errors: [ { message: `ComboBox にはdefaultItemが設定されているため、placeholder属性を閲覧出来ません。削除してください。` } ] },
63
72
  ]
64
73
  })