Skip to content
This repository was archived by the owner on Aug 31, 2023. It is now read-only.

feat(rome_js_analyzer): add useAltText lint rule for JSX expression #3371

Closed
wants to merge 3 commits into from

Conversation

b4s36t4
Copy link
Contributor

@b4s36t4 b4s36t4 commented Oct 8, 2022

Summary

Closes #3298 following the document given for valid and in-valid conditions.

#3298

Test Plan

A lint rule to check for alt attribute in JSX for tags like input type='image', img, area.

@b4s36t4 b4s36t4 requested a review from a team October 8, 2022 18:53
@netlify
Copy link

netlify bot commented Oct 8, 2022

Deploy Preview for docs-rometools failed.

Name Link
🔨 Latest commit 2014015
🔍 Latest deploy log https://app.netlify.com/sites/docs-rometools/deploys/634daa06b70bb9000befc44f

Copy link
Contributor

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution! I left some suggestion and correction to the cases covered

}

declare_node_union! {
pub (crate) UseAltTextQuery = JsxSelfClosingElement | JsCallExpression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should limit ourselves only to JsxSelfClosingElement nodes. Which means that union is not needed anymore, and the Query will become:

type Query = Ast<JsxSelfClosingElement>;

None
}

fn is_valid_tag(name: &JsxAnyElementName) -> Option<bool> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document this function? "Valid tag" is not clear, unless we look at the implementation of the function.

}
}

fn is_valid_input(element: &JsxSelfClosingElement) -> Option<bool> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document this function? "Valid input" needs to have a context; we have to tell developers what a valid input means in this case.

Also, this function could be called input_has_type_image

UseAltTextQuery::JsxSelfClosingElement(element) => {
let name = element.name().ok()?;
if is_valid_tag(&name)? {
let _attributes = element.attributes();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem this is used... so we can remove it?

Comment on lines 77 to 80
let is_spread_available = element
.attributes()
.iter()
.any(|attribute| attribute.as_jsx_spread_attribute().is_some());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage of the spread props is sneaky and we need to be careful to where it's placed inside the component.

Here's two examples:

let first = <img {...props} alt />;
let second = <img alt {...props} />;

The first case is invalid, meaning if should trigger the rule, because alt overrides what's inside props.
The second case is valid, meaning it should not trigger the rule, because props might have the alt property and override the one passed before it. This is a case we don't have enough information to trigger the rule, so has rule of thumb, if we don't know if a rule should be triggered, we don't trigger it.

Check for the spread property might be complex, and I think we should have an API for that. What I would suggest for now, is to avoid checking for spread prop for the time being.

}
return Some(UseAltTextState {
node: UseAltTextNode::from(element.clone()),
missing_alt_prop: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every time UseAltTextState is returned, missing_alt_prop is true, which means that we don't need this property

// invalid

<>
<area {...props} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case is valid. Inside props we might have alt. We don't have enough info so we should not trigger the rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to other tags as well?

Comment on lines 145 to 151
.footer(
Severity::Note,
markup! {
"Meaningful alternative text on elements helps users relying on screen
readers to understand content's purpose within a page."
},
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can footer_note function and pass only the text.


impl Rule for UseAltText {
type Query = Semantic<UseAltTextQuery>;
type State = UseAltTextState;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think State can be a TextRange

type State = TextRange

And when you find a property that is incorrect, you just do

return Some(element.syntaxt().text_range_trimmed())

And the state can be used inside the diagnostic. As you noticed the state is only used to retrieve the range.

@b4s36t4
Copy link
Contributor Author

b4s36t4 commented Oct 17, 2022

@ematipico I have updated the code, please check it.

Note: as part of skipping cases including {...props} spread items existing cases mentioned in the doc will became valid. Please check the snapshots for more context.

@ematipico ematipico changed the title feat: add useAltText lint rule for JSX expression feat(rome_js_analyzer): add useAltText lint rule for JSX expression Oct 18, 2022
// invalid

<>
<area alt={undefined} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we add a case for undefined, I think we should also add a case for alt={null}

Comment on lines +13 to +15
<img {...props} alt />
<img {...props} /> {/* Skipping*/}
<img {...props} alt={undefined} /> {/* Skipping*/}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove these cases? It would be better to tackle spread props in a different PR (it's a feature missing in different rules, not just this one). And for example, the first and third cases are false positives.

return None;
}
let message = markup!(
"Provide "<Emphasis>"alt"</Emphasis>" when using "<Emphasis>"img"</Emphasis>", "<Emphasis>"area"</Emphasis>", "<Emphasis>"input type='image'"</Emphasis>""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Provide "<Emphasis>"alt"</Emphasis>" when using "<Emphasis>"img"</Emphasis>", "<Emphasis>"area"</Emphasis>", "<Emphasis>"input type='image'"</Emphasis>""
"Provide the attribute"<Emphasis>"alt"</Emphasis>" when using "<Emphasis>"img"</Emphasis>", "<Emphasis>"area"</Emphasis>" or "<Emphasis>"input type='image'"</Emphasis>""

You would need to regenerate the snapshots and documentation

).to_owned();

Some(
RuleDiagnostic::new(rule_category!(), state, message).footer(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use footer_note, so you don't need to pass Severity

Comment on lines +123 to +125
/*
This function checks for the attribute `type` for input element where we checking for the input type which is image.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*
This function checks for the attribute `type` for input element where we checking for the input type which is image.
*/
/// This function checks for the attribute `type` for input element where we checking for the input type which is image.

We should use rust documentation comments

Comment on lines +141 to +144
/*
Function to check that the HTML/JSX tag is valid for the lint rule, which check if the tag should be `img`, `input type='image' and `area`.

*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*
Function to check that the HTML/JSX tag is valid for the lint rule, which check if the tag should be `img`, `input type='image' and `area`.
*/
/// Function to check that the HTML/JSX tag is valid for the lint rule, which check if the tag should be `img`, `input type='image' and `area`.

We should use rust documentation comments

Comment on lines +63 to +71
let is_spread_available = element
.attributes()
.iter()
.any(|attribute| attribute.as_jsx_spread_attribute().is_some());

// To-Do - Complex cases might invlove, not triggering rules.
if is_spread_available {
return None;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's best, for now, to remove the check for spread props. It's a feature/functionality that is missing from various rules and the implementation should do more checks. It's OK to remove it, I will work on it later.

@@ -0,0 +1,57 @@
---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file seems incorrect. It should be called useAltText.md. Can you delete it and try to generate it again?

Comment on lines +370 to +376
<<<<<<< HEAD
<h3 data-toc-exclude id="useAltText">
<a href="/docs/lint/rules/useAltText">useAltText (since v0.10.0)</a>
<a class="header-anchor" href="#useAltText"></a>
</h3>
It asserts that alternative text to images or areas, help to rely on to screen readers to understand the purpose and the context of the image.
=======
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some issue with this file too... You should delete it and generate again

Function to check that the HTML/JSX tag is valid for the lint rule, which check if the tag should be `img`, `input type='image' and `area`.

*/
fn is_valid_tag(name: &JsxAnyElementName) -> Option<bool> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"text_trimmed"and the navigation are expensive methods. You can simplify this method, and its call to:

match name.as_jsx_name().map(|x| x.text_trimmed()) {
    Some("input") => {...}
    Some("img" | "area") => {...}
}

return None;
}

let alt_prop = element.find_attribute_by_name("alt").ok()?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may also look at: #3458.
It will improve the code here.

@ematipico ematipico changed the base branch from main to ematipico October 26, 2022 12:32
@ematipico ematipico changed the base branch from ematipico to main October 26, 2022 12:32
@ematipico
Copy link
Contributor

ematipico commented Oct 26, 2022

Closing in favour of #3502

@b4s36t4 I opened a new PR with your commits. We want to have this rule merged before the release.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

useAltText
4 participants