eslint-plugin-smarthr
Advanced tools
Comparing version 0.2.8 to 0.2.9
@@ -5,2 +5,16 @@ # Changelog | ||
### [0.2.9](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.8...v0.2.9) (2022-10-19) | ||
### Features | ||
* a11y-prohibit-input-placeholder を SearchInputに対応させる ([#38](https://github.com/kufu/eslint-plugin-smarthr/issues/38)) ([60de76b](https://github.com/kufu/eslint-plugin-smarthr/commit/60de76b58731436fe924a8a99da2242404141381)) | ||
* add require-declaration rule ([#34](https://github.com/kufu/eslint-plugin-smarthr/issues/34)) ([5dc6d44](https://github.com/kufu/eslint-plugin-smarthr/commit/5dc6d444e63f452f933bf6937207cfe23787732f)) | ||
* placeholder非推奨の対象に FieldSet, ComboBox も含める ([#35](https://github.com/kufu/eslint-plugin-smarthr/issues/35)) ([0e8d1d0](https://github.com/kufu/eslint-plugin-smarthr/commit/0e8d1d03377476fbd58adce17455e96533db69fa)) | ||
### Bug Fixes | ||
* a11y-clickable-element-has-textのバグを修正する ([#36](https://github.com/kufu/eslint-plugin-smarthr/issues/36)) ([4efc23d](https://github.com/kufu/eslint-plugin-smarthr/commit/4efc23d33ba6eec2c454b323f561de3f7a678fab)) | ||
### [0.2.8](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.7...v0.2.8) (2022-10-05) | ||
@@ -7,0 +21,0 @@ |
{ | ||
"name": "eslint-plugin-smarthr", | ||
"version": "0.2.8", | ||
"version": "0.2.9", | ||
"author": "SmartHR", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -16,3 +16,4 @@ # eslint-plugin-smarthr | ||
- [require-barrel-import](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/require-barrel-import) | ||
- [require-declaration](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/require-declaration) | ||
- [require-export](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/require-export) | ||
- [require-import](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/require-import) |
const { generateTagFormatter } = require('../../libs/format_styled_components') | ||
const SCHEMA = [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
componentsWithText: { type: 'array', items: { type: 'string' }, default: [] }, | ||
}, | ||
additionalProperties: false, | ||
} | ||
] | ||
const EXPECTED_NAMES = { | ||
@@ -22,5 +32,8 @@ 'SmartHRLogo$': 'SmartHRLogo$', | ||
}, | ||
schema: [], | ||
schema: SCHEMA, | ||
}, | ||
create(context) { | ||
const option = context.options[0] || {} | ||
const componentsWithText = option.componentsWithText || [] | ||
return { | ||
@@ -45,2 +58,10 @@ ...generateTagFormatter({ context, EXPECTED_NAMES }), | ||
if (c.type === 'JSXFragment') { | ||
if (c.children && filterFalsyJSXText(c.children).some(recursiveSearch)) { | ||
return true | ||
} | ||
return false | ||
} | ||
if (c.type === 'JSXElement') { | ||
@@ -51,10 +72,26 @@ // // HINT: SmartHRLogo コンポーネントは内部でaltを持っているため対象外にする | ||
} | ||
if (c.openingElement.attributes.some((a) => { | ||
if (componentsWithText.includes(c.openingElement.name.name)) { | ||
return true | ||
} | ||
// HINT: role & aria-label を同時に設定されている場合は許可 | ||
let existRole = false | ||
let existAriaLabel = false | ||
const result = c.openingElement.attributes.reduce((prev, a) => { | ||
existRole = existRole || (a.name.name === 'role' && a.value.value === 'img') | ||
existAriaLabel = existAriaLabel || a.name.name === 'aria-label' | ||
if (prev) { | ||
return prev | ||
} | ||
if (!['visuallyHiddenText', 'alt'].includes(a.name.name)) { | ||
return false | ||
return prev | ||
} | ||
return (!!a.value.value || a.value.type === 'JSXExpressionContainer') | ||
})) { | ||
return (!!a.value.value || a.value.type === 'JSXExpressionContainer') ? a : prev | ||
}, null) | ||
if (result || (existRole && existAriaLabel)) { | ||
return true | ||
@@ -86,2 +123,2 @@ } | ||
} | ||
module.exports.schema = [] | ||
module.exports.schema = SCHEMA |
@@ -10,3 +10,8 @@ # smarthr/a11y-clickable-element-has-text | ||
rules: { | ||
'smarthr/a11y-clickable-element-has-text': 'error', // 'warn', 'off' | ||
'smarthr/a11y-clickable-element-has-text': [ | ||
'error', // 'warn', 'off' | ||
// { | ||
// componentsWithText: ['AnyComponentName'], | ||
// }, | ||
] | ||
}, | ||
@@ -63,1 +68,18 @@ } | ||
``` | ||
```jsx | ||
/* | ||
rules: { | ||
'smarthr/a11y-clickable-element-has-text': [ | ||
'error', | ||
{ | ||
componentsWithText: ['AnyComponent'], | ||
}, | ||
] | ||
}, | ||
*/ | ||
<XxxButton> | ||
<AnyComponent /> | ||
</XxxButton> | ||
``` |
@@ -5,3 +5,6 @@ const { generateTagFormatter } = require('../../libs/format_styled_components') | ||
'(i|I)nput$': 'Input$', | ||
'SearchInput$': 'SearchInput$', | ||
'(t|T)extarea$': 'Textarea$', | ||
'FieldSet$': 'FieldSet$', | ||
'ComboBox$': 'ComboBox$', | ||
} | ||
@@ -22,9 +25,9 @@ | ||
JSXOpeningElement: (node) => { | ||
if (!node.name.name) { | ||
const name = node.name.name | ||
if (!name) { | ||
return | ||
} | ||
const match = node.name.name.match(/((i|I)nput|(t|T)extarea)$/) | ||
if (!match) { | ||
if (!name.match(/((i|I)nput|(t|T)extarea|FieldSet|ComboBox)$/)) { | ||
return | ||
@@ -36,9 +39,23 @@ } | ||
if (placeholder) { | ||
context.report({ | ||
if (name.match(/SearchInput$/)) { | ||
const tooltipMessage = node.attributes.find((a) => a.name?.name === 'tooltipMessage') | ||
if (!tooltipMessage) { | ||
context.report({ | ||
node: placeholder, | ||
messageId: 'a11y-prohibit-input-placeholder', | ||
data: { | ||
message: `${name} にはplaceholder属性を単独で利用せず、tooltipMessageオプションのみ、もしくはplaceholderとtooltipMessageの併用を検討してください。 (例: '<${name} tooltipMessage="ヒント" />', '<${name} tooltipMessage={hint} placeholder={hint} />')`, | ||
}, | ||
}) | ||
} | ||
} else { | ||
context.report({ | ||
node: placeholder, | ||
messageId: 'a11y-prohibit-input-placeholder', | ||
data: { | ||
message: 'input, textarea要素のplaceholder属性は設定せず、smarthr-ui/Tooltip や別途ヒント用要素の利用を検討してください (例: `<><Input /><Hint>ヒント</Hint></>`, `<Tooltip message="ヒント"><Textarea/></Tooltip>`)', | ||
message: `${name} にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><${name} /><Hint>ヒント</Hint></div>')`, | ||
}, | ||
}) | ||
} | ||
} | ||
@@ -45,0 +62,0 @@ }, |
@@ -99,2 +99,14 @@ const rule = require('../rules/a11y-clickable-element-has-text') | ||
}, | ||
{ | ||
code: `<a><>ほげ</></a>`, | ||
}, | ||
{ | ||
code: `<a><svg role="img" aria-label="hoge" /></a>`, | ||
}, | ||
{ | ||
code: `<a><AnyComponent /></a>`, | ||
options: [{ | ||
componentsWithText: ['AnyComponent'] | ||
}], | ||
}, | ||
], | ||
@@ -155,3 +167,14 @@ invalid: [ | ||
}, | ||
{ | ||
code: `<a><div role="article" aria-label="hoge" /></a>`, | ||
errors: [{ message: defaultErrorMessage }] | ||
}, | ||
{ | ||
code: `<a><AnyComponent /></a>`, | ||
options: [{ | ||
componentsWithText: ['HogeComponent'] | ||
}], | ||
errors: [{ message: defaultErrorMessage }] | ||
}, | ||
] | ||
}) |
@@ -25,6 +25,17 @@ const rule = require('../rules/a11y-prohibit-input-placeholder') | ||
{ code: 'const HogeTextarea = styled(Textarea)``' }, | ||
{ code: 'const hoge = styled.fieldset``' }, | ||
{ code: 'const HogeFieldSet = styled(FieldSet)``' }, | ||
{ code: 'const HogeComboBox = styled(ComboBox)``' }, | ||
{ code: 'const HogeSearchInput = styled(SearchInput)``' }, | ||
{ code: `<input />` }, | ||
{ code: `<textarea />` }, | ||
{ code: `<FieldSet />` }, | ||
{ code: `<ComboBox />` }, | ||
{ code: `<StyledInput />` }, | ||
{ code: `<HogeTextarea />` }, | ||
{ code: `<FugaFieldSet />` }, | ||
{ code: `<CustomComboBox />` }, | ||
{ code: `<SearchInput />` }, | ||
{ code: `<CustomSearchInput tooltipMessage="hoge" />` }, | ||
{ code: `<CustomSearchInput tooltipMessage="hoge" placeholder="fuga" />` }, | ||
], | ||
@@ -37,7 +48,19 @@ invalid: [ | ||
{ code: 'const Hoge = styled(StyledTextarea)``', errors: [ { message: `Hogeを正規表現 "/Textarea$/" がmatchする名称に変更してください` } ] }, | ||
{ code: `<input placeholder />`, errors: [ { message: 'input, textarea要素のplaceholder属性は設定せず、smarthr-ui/Tooltip や別途ヒント用要素の利用を検討してください (例: `<><Input /><Hint>ヒント</Hint></>`, `<Tooltip message="ヒント"><Textarea/></Tooltip>`)' } ] }, | ||
{ code: `<textarea placeholder="hoge" />`, errors: [ { message: 'input, textarea要素のplaceholder属性は設定せず、smarthr-ui/Tooltip や別途ヒント用要素の利用を検討してください (例: `<><Input /><Hint>ヒント</Hint></>`, `<Tooltip message="ヒント"><Textarea/></Tooltip>`)' } ] }, | ||
{ code: `<StyledInput placeholder={any} />`, errors: [ { message: 'input, textarea要素のplaceholder属性は設定せず、smarthr-ui/Tooltip や別途ヒント用要素の利用を検討してください (例: `<><Input /><Hint>ヒント</Hint></>`, `<Tooltip message="ヒント"><Textarea/></Tooltip>`)' } ] }, | ||
{ code: `<HogeTextarea placeholder="any" />`, errors: [ { message: 'input, textarea要素のplaceholder属性は設定せず、smarthr-ui/Tooltip や別途ヒント用要素の利用を検討してください (例: `<><Input /><Hint>ヒント</Hint></>`, `<Tooltip message="ヒント"><Textarea/></Tooltip>`)' } ] }, | ||
{ code: 'const Hoge = styled(FieldSet)``', errors: [ { message: `Hogeを正規表現 "/FieldSet$/" がmatchする名称に変更してください` } ] }, | ||
{ code: 'const Hoge = styled(ComboBox)``', errors: [ { message: `Hogeを正規表現 "/ComboBox$/" がmatchする名称に変更してください` } ] }, | ||
{ | ||
code: 'const Hoge = styled(SearchInput)``', | ||
errors: [ | ||
{ message: `Hogeを正規表現 "/Input$/" がmatchする名称に変更してください` }, | ||
{ message: `Hogeを正規表現 "/SearchInput$/" がmatchする名称に変更してください` }, | ||
], | ||
}, | ||
{ code: `<input placeholder />`, errors: [ { message: `input にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><input /><Hint>ヒント</Hint></div>')` } ] }, | ||
{ code: `<textarea placeholder="hoge" />`, errors: [ { message: `textarea にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><textarea /><Hint>ヒント</Hint></div>')` } ] }, | ||
{ code: `<StyledInput placeholder={any} />`, errors: [ { message: `StyledInput にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><StyledInput /><Hint>ヒント</Hint></div>')` } ] }, | ||
{ code: `<HogeTextarea placeholder="any" />`, errors: [ { message: `HogeTextarea にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeTextarea /><Hint>ヒント</Hint></div>')` } ] }, | ||
{ code: `<HogeFieldSet placeholder="any" />`, errors: [ { message: `HogeFieldSet にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeFieldSet /><Hint>ヒント</Hint></div>')` } ] }, | ||
{ code: `<HogeComboBox placeholder="any" />`, errors: [ { message: `HogeComboBox にはplaceholder属性は設定せず、別途ヒント用要素の利用を検討してください。(例: '<div><HogeComboBox /><Hint>ヒント</Hint></div>')` } ] }, | ||
{ code: `<SearchInput placeholder="any" />`, errors: [ { message: `SearchInput にはplaceholder属性を単独で利用せず、tooltipMessageオプションのみ、もしくはplaceholderとtooltipMessageの併用を検討してください。 (例: '<SearchInput tooltipMessage="ヒント" />', '<SearchInput tooltipMessage={hint} placeholder={hint} />')` } ] }, | ||
] | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
132811
56
2972
19