eslint-plugin-smarthr
Advanced tools
Comparing version 0.4.1 to 0.4.2
@@ -5,2 +5,14 @@ # Changelog | ||
### [0.4.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.1...v0.4.2) (2024-03-03) | ||
### Features | ||
* best-practice-for-button-elementを追加 ([#115](https://github.com/kufu/eslint-plugin-smarthr/issues/115)) ([c19ab10](https://github.com/kufu/eslint-plugin-smarthr/commit/c19ab1062d00aa000cfcf9b86535af21b47a0ead)) | ||
### Bug Fixes | ||
* a11y-numbered-text-within-ol でwidthの値が誤検知されてしまう場合に対応する ([#117](https://github.com/kufu/eslint-plugin-smarthr/issues/117)) ([3741d54](https://github.com/kufu/eslint-plugin-smarthr/commit/3741d5412f62ac04286e0626e37d2ca3c57c4f60)) | ||
### [0.4.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.0...v0.4.1) (2024-02-21) | ||
@@ -7,0 +19,0 @@ |
@@ -21,2 +21,40 @@ const STYLED_COMPONENTS_METHOD = 'styled' | ||
const getStyledComponentBaseName = (node) => { | ||
let base = null | ||
if (!node.init) { | ||
return base | ||
} | ||
const tag = node.init.tag || node.init | ||
if (tag.object?.name === STYLED_COMPONENTS_METHOD) { | ||
base = tag.property.name | ||
} else if (tag.callee) { | ||
const callee = tag.callee | ||
switch (STYLED_COMPONENTS_METHOD) { | ||
case callee.name: { | ||
const arg = tag.arguments[0] | ||
base = arg.name || arg.value | ||
break | ||
} | ||
case callee.callee?.name: { | ||
const arg = callee.arguments[0] | ||
base = arg.name || arg.value | ||
break | ||
} | ||
case callee.object?.name: | ||
base = callee.property.name | ||
break | ||
case callee.object?.callee?.name: | ||
const arg = callee.object.arguments[0] | ||
base = arg.name || arg.value | ||
break | ||
} | ||
} | ||
return base | ||
} | ||
const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) => { | ||
@@ -56,36 +94,4 @@ const entriesesTagNames = Object.entries(EXPECTED_NAMES).map(([b, e]) => [ new RegExp(b), new RegExp(e) ]) | ||
VariableDeclarator: (node) => { | ||
if (!node.init) { | ||
return | ||
} | ||
const base = getStyledComponentBaseName(node) | ||
const tag = node.init.tag || node.init | ||
let base = null | ||
if (tag.object?.name === STYLED_COMPONENTS_METHOD) { | ||
base = tag.property.name | ||
} else if (tag.callee) { | ||
const callee = tag.callee | ||
switch (STYLED_COMPONENTS_METHOD) { | ||
case callee.name: { | ||
const arg = tag.arguments[0] | ||
base = arg.name || arg.value | ||
break | ||
} | ||
case callee.callee?.name: { | ||
const arg = callee.arguments[0] | ||
base = arg.name || arg.value | ||
break | ||
} | ||
case callee.object?.name: | ||
base = callee.property.name | ||
break | ||
case callee.object?.callee?.name: | ||
const arg = callee.object.arguments[0] | ||
base = arg.name || arg.value | ||
break | ||
} | ||
} | ||
if (base) { | ||
@@ -122,2 +128,2 @@ const extended = node.id.name | ||
module.exports = { generateTagFormatter } | ||
module.exports = { generateTagFormatter, checkImportStyledComponents, getStyledComponentBaseName } |
{ | ||
"name": "eslint-plugin-smarthr", | ||
"version": "0.4.1", | ||
"version": "0.4.2", | ||
"author": "SmartHR", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -33,5 +33,5 @@ const { generateTagFormatter } = require('../../libs/format_styled_components') | ||
const REGEX_TEXT_COMPONENT = /(Text|Message)$/ | ||
const REGEX_JSX_TYPE = /^(JSXText|JSXExpressionContainer)$/ | ||
const HIT_TYPES_RECURSICVE_SEARCH = ['JSXText', 'JSXExpressionContainer'] | ||
const HIT_TEXT_ATTRS = ['visuallyHiddenText', 'alt'] | ||
const HIT_TEXT_ATTR = 'alt' | ||
@@ -72,3 +72,3 @@ const filterFalsyJSXText = (cs) => cs.filter(checkFalsyJSXText) | ||
const recursiveSearch = (c) => { | ||
if (HIT_TYPES_RECURSICVE_SEARCH.includes(c.type)) { | ||
if (REGEX_JSX_TYPE.test(c.type)) { | ||
return true | ||
@@ -102,3 +102,3 @@ } | ||
prev || | ||
!HIT_TEXT_ATTRS.includes(a.name.name) | ||
HIT_TEXT_ATTR !== a.name.name | ||
) { | ||
@@ -105,0 +105,0 @@ return prev |
# smarthr/a11y-clickable-element-has-text | ||
- ButtonやAnchor,Link コンポーネントにテキスト要素が設定されていない場合、アクセシビリティの問題が発生する可能性を防ぐルールです | ||
- ButtonやAnchor,Link コンポーネントにテキスト要素が設定されていない場合、スクリーンリーダーで押したものが何だったのかわからない等の問題が発生する可能性を防ぐルールです | ||
- a要素やbutton要素の中身にtextがあることを担保するルール | ||
- 画像要素の場合は `visuallyHiddenText`や `alt`等代替テキストを設定する | ||
- SVGの場合はrole="img" と aria-labelを設定する | ||
- linkとかanchorのchildrenを含まない要素はチェックしない | ||
- 例) `<YyyAnchor />` | ||
@@ -41,3 +46,3 @@ ## rules | ||
```jsx | ||
<XxxAnchor>> | ||
<XxxAnchor> | ||
<XxxTextYyyy /> | ||
@@ -61,3 +66,3 @@ </XxxAnchor> | ||
```jsx | ||
<XxxAnchor>> | ||
<XxxAnchor> | ||
<YyyIcon visuallyHiddenText="hoge" /> | ||
@@ -73,7 +78,7 @@ </XxxAnchor> | ||
```jsx | ||
<YyyAnchoor /> | ||
<YyyAnchor /> | ||
``` | ||
```jsx | ||
<XxxAnchor>> | ||
<XxxAnchor> | ||
<XxxText /> | ||
@@ -80,0 +85,0 @@ </XxxAnchor> |
@@ -9,6 +9,10 @@ const { generateTagFormatter } = require('../../libs/format_styled_components') | ||
const NUMBERED_TEXT_REGEX = /^[\s\n]*(([1-9])([^1-9]{2})[^\s\n]*)/ | ||
const NUMBERED_TEXT_REGEX = /^[\s\n]*(([0-9])([^0-9]{2})[^\s\n]*)/ | ||
const ORDERED_LIST_REGEX = /(Ordered(.*)List|^ol)$/ | ||
const SELECT_REGEX = /(S|s)elect$/ | ||
const IGNORE_ATTRIBUTE_REGEX = /((w|W)idth|(h|H)eight)$/ | ||
const AS_ATTRIBUTE_REGEX = /^(as|forwardedAs)$/ | ||
const findAsOlAttr = (a) => a.type === 'JSXAttribute' && AS_ATTRIBUTE_REGEX.test(a.name?.name) && a.value?.value === 'ol' | ||
const searchOrderedList = (node) => { | ||
@@ -18,8 +22,11 @@ if (node.type === 'JSXElement' && node.openingElement.name?.name) { | ||
if (name.match(ORDERED_LIST_REGEX)) { | ||
return node.openingElement | ||
} else if (name.match(SELECT_REGEX)) { | ||
if (name.match(SELECT_REGEX)) { | ||
// HINT: select要素の場合、optionのラベルに連番がついている場合がありえるのでignoreする | ||
// 通常と処理を分けるためnullではなく0を返す | ||
return 0 | ||
} else if ( | ||
name.match(ORDERED_LIST_REGEX) || | ||
node.openingElement.attributes.find(findAsOlAttr) | ||
) { | ||
return node.openingElement | ||
} | ||
@@ -126,3 +133,3 @@ } | ||
JSXAttribute: (node) => { | ||
if (node.value?.value) { | ||
if (node.value?.value && !IGNORE_ATTRIBUTE_REGEX.test(node.name?.name)) { | ||
checker(node, node.value.value.match(NUMBERED_TEXT_REGEX)) | ||
@@ -129,0 +136,0 @@ } |
@@ -96,8 +96,2 @@ const rule = require('../rules/a11y-clickable-element-has-text') | ||
{ | ||
code: `<a><Icon visuallyHiddenText="ほげ" /></a>`, | ||
}, | ||
{ | ||
code: `<a><AnyComponent><Icon visuallyHiddenText="ほげ" /></AnyComponent></a>`, | ||
}, | ||
{ | ||
code: `<a>{any}</a>`, | ||
@@ -199,6 +193,2 @@ }, | ||
{ | ||
code: `<a><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></a>`, | ||
errors: [{ message: defaultErrorMessage }] | ||
}, | ||
{ | ||
code: `<button><img src="hoge.jpg" /></button>`, | ||
@@ -220,6 +210,2 @@ errors: [{ message: defaultErrorMessage }] | ||
{ | ||
code: `<button><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></button>`, | ||
errors: [{ message: defaultErrorMessage }] | ||
}, | ||
{ | ||
code: `<button><SmartHRLogoSuffix /></button>`, | ||
@@ -226,0 +212,0 @@ errors: [{ message: defaultErrorMessage }] |
@@ -24,3 +24,6 @@ const rule = require('../rules/a11y-numbered-text-within-ol') | ||
{ code: `<OrderedList><li>abc</li><li>def</li></OrderedList>` }, | ||
{ code: `<Hoge as="ol"><li>abc</li><li>def</li></Hoge>` }, | ||
{ code: `<Hoge forwardedAs="ol"><li>abc</li><li>def</li></Hoge>` }, | ||
{ code: `<Select><optgroup><option value="hoge">1. hoge</option><option value="fuga">2. fuga</option></optgroup></Select>` }, | ||
{ code: `<><Hoge width="100%" /><Hoge height="200%" /><Hoge maxWidth="30px" /></>` }, | ||
], | ||
@@ -27,0 +30,0 @@ invalid: [ |
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
353702
87
5073