-
-
Notifications
You must be signed in to change notification settings - Fork 393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(lint/noStaticElementInteractions): add rule #2981
base: main
Are you sure you want to change the base?
feat(lint/noStaticElementInteractions): add rule #2981
Conversation
CodSpeed Performance ReportMerging #2981 will improve performances by 6.22%Comparing Summary
Benchmarks breakdown
|
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
As this comment states, the following two methods were created to bridge the gap between eslint-plugin-jsx-a11y and Biome
However, I thought it was necessary to optimize the Biome definition based on the official documentation, rather than discussing the two options of which way to go. Is this an issue that should be addressed in this PR? |
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
Let's address that in another PR. |
m.insert("clipboard", vec!["onCopy", "onCut", "onPaste"]); | ||
m.insert("composition", vec!["onCompositionEnd", "onCompositionStart", "onCompositionUpdate"]); | ||
m.insert("keyboard", vec!["onKeyDown", "onKeyPress", "onKeyUp"]); | ||
m.insert("focus", vec!["onFocus", "onBlur"]); | ||
m.insert("form", vec!["onChange", "onInput", "onSubmit"]); | ||
m.insert("mouse", vec![ | ||
"onClick", "onContextMenu", "onDblClick", "onDoubleClick", "onDrag", "onDragEnd", | ||
"onDragEnter", "onDragExit", "onDragLeave", "onDragOver", "onDragStart", "onDrop", | ||
"onMouseDown", "onMouseEnter", "onMouseLeave", "onMouseMove", "onMouseOut", | ||
"onMouseOver", "onMouseUp" | ||
]); | ||
m.insert("selection", vec!["onSelect"]); | ||
m.insert("touch", vec!["onTouchCancel", "onTouchEnd", "onTouchMove", "onTouchStart"]); | ||
m.insert("ui", vec!["onScroll"]); | ||
m.insert("wheel", vec!["onWheel"]); | ||
m.insert("media", vec![ | ||
"onAbort", "onCanPlay", "onCanPlayThrough", "onDurationChange", "onEmptied", | ||
"onEncrypted", "onEnded", "onError", "onLoadedData", "onLoadedMetadata", "onLoadStart", | ||
"onPause", "onPlay", "onPlaying", "onProgress", "onRateChange", "onSeeked", "onSeeking", | ||
"onStalled", "onSuspend", "onTimeUpdate", "onVolumeChange", "onWaiting" | ||
]); | ||
m.insert("image", vec!["onLoad", "onError"]); | ||
m.insert("animation", vec!["onAnimationStart", "onAnimationEnd", "onAnimationIteration"]); | ||
m.insert("transition", vec!["onTransitionEnd"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need elements (and handlers) other than in CATEGORIES_TO_CHECK
?
We only use focus
, keyboard
and mouse
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@unvalley
In ESlint, interactions are managed in independent files named eventHandlersByType
.
ref: https://github.com/jsx-eslint/jsx-ast-utils/blob/main/src/eventHandlers.js
Among them, ESlint uses focus
, keyboard
and mouse
by default.
ref: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/974275353598e9407c76bd4a50c331a953755cee/src/rules/no-static-element-interactions.js#L33-L37
In other words, the EVENT_TO_HANDLERS created in this PR was originally intended to be used for other rules, etc., but since it is currently unclear where to place them, they are implemented this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. So, I guess it means we don't have any other rules that need this CATEGORIES_TO_CHECK
yet.
Alright, we can locate the hashmap here, but I suggest commenting out elements that aren't used here. It may be a waste of overhead. Insertion for focus
, keyboard
, and mouse
is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@unvalley
I see, I understand!
I'll fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to barge in during this review. I think this code would benefit from a refactor. I don't think it's a good custom to try to replicate JS code in Rust. For example, this code, while it's created once via lazy_static
, still creates a bunch of vectors that are created at runtime (heap).
Instead, why not create simple arrays?
Also, I think we should explain what this code is. It's copied from another source, which is completely fine, but there's no comment that explains what are the keys and what are the values, how they are used and what they are meant to be.
For example composition
-> [...]
. What's composition
? Where is it coming from? Is it an HTML element? Is it a role? Remember that in Biome, we want to aim for good standards, DX-wise and coding-wise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@unvalley
Sorry, I didn't address to additional comments, please leave them as they are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ematipico @unvalley
Thank you for pointing this out.
As you said, it was causing overhead, so we defined it as a fixed-length array.
Also modified the comment to describe what is stored in this array and added the URL to the MDN reference describing each event handler
fixed in this commit b330131
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
@unvalley |
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
…added explanatory comments for better clarity and performance.
…attributes function
8b225b2
to
6394998
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Just needs to remove the commented code, then we can merge it :)
.text() | ||
.split(' ') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use this instead: https://doc.rust-lang.org/std/primitive.str.html#method.split_whitespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ematipico
Thanks your advice, I have incorporated the changes you suggested!
2dcbc5e
{/* <summary> is inherently an interactive element, but in eslint-plugin-jsx-a11y, | ||
it was made non-interactive due to the influence of an external library. */} | ||
{/* ref: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/0be7ea95f560c6afc6817d381054d914ebd0b2ca/src/util/isInteractiveElement.js#L86-L89 */} | ||
{/* <summary onClick={() => {}} /> */} | ||
|
||
{/* This element is rejected by HTML Standard */} | ||
{/* ref: https://lists.w3.org/Archives/Public/public-whatwg-archive/2012Aug/0298.html */} | ||
{/* <content onClick={() => {}} /> */} | ||
|
||
{/* This element is rejected by HTML Standard */} | ||
{/* ref: https://html.spec.whatwg.org/multipage/obsolete.html */} | ||
{/* <acronym onClick={() => {}} /> */} | ||
{/* <applet onClick={() => {}} /> */} | ||
{/* <frame onClick={() => { }} /> */} | ||
{/* <frameset onClick={() => { }} /> */} | ||
{/* <center onClick={() => {}} /> */} | ||
{/* <font onClick={() => {}} /> */} | ||
{/* <big onClick={() => {}} /> */} | ||
{/* <blink onClick={() => {}} /> */} | ||
{/* <rtc onClick={() => {}} /> */} | ||
{/* <xmp onClick={() => {}} /> */} | ||
{/* <strike onClick={() => {}} /> */} | ||
{/* <param onClick={() => {}} /> */} | ||
{/* <keygen onClick={() => {}} /> */} | ||
{/* <noembed onClick={() => {}} /> */} | ||
{/* <spacer onClick={() => {}} /> */} | ||
{/* <tt onClick={() => {}} /> */} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these commented on purpose? If so, we should add a comment saying why, if not, we should remove them or uncomment them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ematipico
I removed those.
7851727
// ( | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent | ||
// "clipboard", | ||
// &[ | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event | ||
// "onCopy", | ||
// //ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/cut_event | ||
// "onCut", | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event | ||
// "onPaste", | ||
// ], | ||
// ), | ||
// ( | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent | ||
// "composition", | ||
// &[ | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionstart_event | ||
// "onCompositionStart", | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionend_event | ||
// "onCompositionEnd", | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionupdate_event | ||
// "onCompositionUpdate", | ||
// ], | ||
// ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ematipico
I addressed this commit!
7851727
// ( | ||
// "form", | ||
// &[ | ||
// /// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event | ||
// "onChange", | ||
// // ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event | ||
// "onInput", | ||
// // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event | ||
// "onSubmit", | ||
// ], | ||
// ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ematipico
I addressed this commit!
7851727
@ematipico @unvalley |
crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs
Outdated
Show resolved
Hide resolved
const EVENT_TO_HANDLERS: &[(&str, &[&str])] = &[ | ||
( | ||
// ref https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent | ||
"keyboard", | ||
&[ | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event | ||
"onKeyDown", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/keyup_event | ||
"onKeyUp", | ||
//ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event | ||
"onKeyPress", | ||
], | ||
), | ||
( | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent | ||
"focus", | ||
&[ | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event | ||
"onFocus", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event | ||
"onBlur", | ||
], | ||
), | ||
( | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent | ||
"mouse", | ||
&[ | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event | ||
"onClick", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event | ||
"onContextMenu", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event | ||
"onDblClick", | ||
"onDoubleClick", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drag_event | ||
"onDrag", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragend_event | ||
"onDragEnd", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event | ||
"onDragEnter", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragleave_event | ||
"onDragLeave", | ||
"onDragExit", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event | ||
"onDragOver", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragstart_event | ||
"onDragStart", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event | ||
"onDrop", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event | ||
"onMouseDown", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event | ||
"onMouseEnter", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event | ||
"onMouseLeave", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event | ||
"onMouseMove", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event | ||
"onMouseOut", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseover_event | ||
"onMouseOver", | ||
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event | ||
"onMouseUp", | ||
], | ||
), | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nits: i think we don't need the ref
comments, because they are obvious and redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, helps are always welcome 😄 |
@ryo-ebata could you apply codegen to pass the CI? |
crates/biome_aria/src/roles.rs
Outdated
let role_name = if let Some(role) = attributes.get("role") { | ||
if let Some(r) = role.first() { | ||
self.get_role(r) | ||
} else { | ||
None | ||
} | ||
} else { | ||
self.get_implicit_role(element_name, attributes) | ||
}; | ||
|
||
if let Some(role) = role_name { | ||
match role.type_name() { | ||
"biome_aria::roles::PresentationRole" | "biome_aria::roles::GenericRole" => { | ||
return false | ||
} | ||
_ => return true, | ||
} | ||
} | ||
|
||
false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be more simple (if this works)
let role_name = if let Some(role) = attributes.get("role") { | |
if let Some(r) = role.first() { | |
self.get_role(r) | |
} else { | |
None | |
} | |
} else { | |
self.get_implicit_role(element_name, attributes) | |
}; | |
if let Some(role) = role_name { | |
match role.type_name() { | |
"biome_aria::roles::PresentationRole" | "biome_aria::roles::GenericRole" => { | |
return false | |
} | |
_ => return true, | |
} | |
} | |
false | |
let role_name = attributes | |
.get("role") | |
.and_then(|role| role.first()) | |
.map(|r| self.get_role(r)) | |
.or_else(|| self.get_implicit_role(element_name, attributes)); | |
match role_name.map(|role| role.type_name()) { | |
Some("biome_aria::roles::PresentationRole" | "biome_aria::roles::GenericRole") => false, | |
Some(_) => true, | |
None => false, | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some suggestions but they are nits or for refactor. Thank you!
@unvalley Also, I have an existing nursery rule that fails the snapshot test with my changes, so I'm going to address that with another PR. |
Summary
Enforce that non-interactive, visible elements (such as
<div>
) that have click handlers use the role attribute.Implements #527
Test Plan
Added snaps for valid/invalid cases.
Basically, it conforms to the test cases of eslint-plugin-jsx-a11y/no-static-element-interactions, and test cases that cannot be handled by the resources within Biome are handled individually.
ref:
https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/__tests__/src/rules/no-static-element-interactions-test.js