Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eslint-plugin-smarthr

Package Overview
Dependencies
Maintainers
22
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-smarthr - npm Package Compare versions

Comparing version 0.3.18 to 0.3.19

7

CHANGELOG.md

@@ -5,2 +5,9 @@ # Changelog

### [0.3.19](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.18...v0.3.19) (2024-01-01)
### Bug Fixes
* a11y-delegate-element-has-role-presentation のインタラクティブな要素の判定を調整する ([#96](https://github.com/kufu/eslint-plugin-smarthr/issues/96)) ([63c9bda](https://github.com/kufu/eslint-plugin-smarthr/commit/63c9bda3b5d52baa3c1c2fd183ebee06d3a429d2))
### [0.3.18](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.17...v0.3.18) (2023-12-30)

@@ -7,0 +14,0 @@

2

package.json
{
"name": "eslint-plugin-smarthr",
"version": "0.3.18",
"version": "0.3.19",
"author": "SmartHR",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -41,3 +41,6 @@ const { generateTagFormatter } = require('../../libs/format_styled_components');

const INTERACTIVE_ON_REGEX = /^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/
const MEANED_ROLE_REGEX = /^(combobox|group|slider|toolbar)$/
const INTERACTIVE_NODE_TYPE = ['JSXElement', 'JSXExpressionContainer', 'ConditionalExpression']
const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex, onAttrs) => {

@@ -51,3 +54,5 @@ const onAttrsText = onAttrs.join(', ')

- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください`
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください
- 'role="presentation"' を設定した要素はマークアップとしての意味がなくなるため、div・span などマークアップとしての意味を持たない要素に設定してください
- 'role="presentation"' を設定する適切な要素が存在しない場合、div、またはspanでイベントが発生する要素を囲んだ上でrole属性を設定してください`
}

@@ -82,3 +87,28 @@ const messageRolePresentationNotHasInteractive = (nodeName, interactiveComponentRegex, onAttrs) => `${nodeName}に 'role="presentation"' が設定されているにも関わらず、子要素にinput、buttonやaなどのインタラクティブな要素が見つからないため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。

const interactiveComponentRegex = new RegExp(`(${INTERACTIVE_COMPONENT_NAMES.join('|')}${options?.additionalInteractiveComponentRegex ? `|${options.additionalInteractiveComponentRegex.join('|')}` : ''})`)
const isHasInteractive = (c) => {
switch (c.type) {
case 'JSXElement': {
if ((c.openingElement.name.name || '').match(interactiveComponentRegex)) {
return true
}
if (c.children.length > 0) {
return !!c.children.find(isHasInteractive)
}
}
case 'JSXExpressionContainer':
case 'ConditionalExpression': {
let expression = c
if (c.expression) {
expression = c.expression
}
return !![expression.right, expression.consequent, expression.alternate].find((ec) => INTERACTIVE_NODE_TYPE.includes(ec?.type) && isHasInteractive(ec))
}
}
return false
}
return {

@@ -90,2 +120,3 @@ ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),

let onAttrs = []
let isMeanedRole = false
let isRolePresentation = false

@@ -98,4 +129,10 @@

onAttrs.push(aName)
} else if (aName === 'role' && a.value?.value === 'presentation') {
isRolePresentation = true
} else if (aName === 'role') {
const v = a.value?.value || ''
if (v === 'presentation') {
isMeanedRole = isRolePresentation = true
} else if (v.match(MEANED_ROLE_REGEX)) {
isMeanedRole = true
}
}

@@ -107,27 +144,16 @@ })

if (!isRolePresentation) {
context.report({
node,
message: messageNonInteractiveEventHandler(nodeName, interactiveComponentRegex, onAttrs),
});
} else {
const isHasInteractive = (c) => {
if (c.type === 'JSXElement') {
if ((c.openingElement.name.name || '').match(interactiveComponentRegex)) {
return true
}
if (c.children.length > 0) {
return c.children.find(isHasInteractive)
}
}
return false
}
if (!node.parent.children.find(isHasInteractive)) {
// HINT: role="presentation"以外で意味があるroleが設定されている場合はエラーにしない
// 基本的にsmarthr-uiでroleの設定などは巻き取る && そもそもroleを設定するよりタグを適切にマークアップすることが優先されるため
// エラーなどには表示しない
if (!isMeanedRole) {
context.report({
node,
message: messageRolePresentationNotHasInteractive(nodeName, interactiveComponentRegex, onAttrs)
})
message: messageNonInteractiveEventHandler(nodeName, interactiveComponentRegex, onAttrs),
});
}
} else if (!node.parent.children.find(isHasInteractive)) {
context.report({
node,
message: messageRolePresentationNotHasInteractive(nodeName, interactiveComponentRegex, onAttrs)
})
}

@@ -134,0 +160,0 @@ }

@@ -15,3 +15,3 @@ const rule = require('../rules/a11y-delegate-element-has-role-presentation');

const defaultInteractiveRegex = '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/'
const defaultInteractiveRegex = '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|Switch$|SegmentedControl$|RightFixedNote$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|FormDialog$|Pagination$|SideNav$|AccordionPanel$)/'
const messageNonInteractiveEventHandler = (nodeName, onAttrs, interactiveComponentRegex = defaultInteractiveRegex) => {

@@ -25,3 +25,5 @@ const onAttrsText = onAttrs.join(', ')

- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください`
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください
- 'role="presentation"' を設定した要素はマークアップとしての意味がなくなるため、div・span などマークアップとしての意味を持たない要素に設定してください
- 'role="presentation"' を設定する適切な要素が存在しない場合、div、またはspanでイベントが発生する要素を囲んだ上でrole属性を設定してください`
}

@@ -49,2 +51,8 @@ const messageRolePresentationNotHasInteractive = (nodeName, onAttrs, interactiveComponentRegex = defaultInteractiveRegex) => `${nodeName}に 'role="presentation"' が設定されているにも関わらず、子要素にinput、buttonやaなどのインタラクティブな要素が見つからないため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。

{ code: '<Wrapper onClick={any} role="presentation"><any><Link /></any></Wrapper>' },
{ code: '<Wrapper onClick={any} role="presentation">{any && <AnyButton />}</Wrapper>' },
{ code: '<Wrapper onClick={any} role="presentation">{any || <AnyButton />}</Wrapper>' },
{ code: '<Wrapper onClick={any} role="presentation">{any1 && (any2 || any3) && <AnyButton />}</Wrapper>' },
{ code: '<Wrapper onClick={any} role="presentation">{any ? <AnyButton /> : null}</Wrapper>' },
{ code: '<Wrapper onClick={any} role="presentation">{any1 ? (any2 ? <HogeLink /> : null) : null}</Wrapper>' },
{ code: '<Wrapper onClick={any} role="presentation">{any ? null : (hoge ? <AnyLink /> : null)}</Wrapper>' },
],

@@ -59,5 +67,6 @@ invalid: [

{ code: '<Wrapper onClick={any}><Link /></Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick']) } ] },
{ code: '<Wrapper onSubmit={any}><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }], errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onSubmit'], '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$|^Hoge$)/') } ] },
{ code: '<Wrapper onSubmit={any}><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }], errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onSubmit'], '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|Switch$|SegmentedControl$|RightFixedNote$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|FormDialog$|Pagination$|SideNav$|AccordionPanel$|^Hoge$)/') } ] },
{ code: '<Wrapper onClick={any} onChange={anyany}><any><Link /></any></Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick', 'onChange']) } ] },
{ code: '<Wrapper onClick={any}>{any ? null : (hoge ? <AnyLink /> : null)}</Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick']) } ] },
],
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc