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

feat(rome_js_analyze): useIframeTitle #4067

Merged
merged 7 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/rome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ define_dategories! {
"lint/nursery/useExponentiationOperator": "https://docs.rome.tools/lint/rules/useExponentiationOperator",
"lint/nursery/useIsNan": "https://docs.rome.tools/lint/rules/useIsNan",
"lint/nursery/useMediaCaption": "https://docs.rome.tools/lint/rules/useMediaCaption",
"lint/nursery/useIframeTitle": "https://docs.rome.tools/lint/rules/useIframeTitle",
"lint/nursery/useNumericLiterals": "https://docs.rome.tools/lint/rules/useNumericLiterals",
"lint/nursery/useValidForDirection": "https://docs.rome.tools/lint/rules/useValidForDirection",
"lint/nursery/useHookAtTopLevel": "https://docs.rome.tools/lint/rules/useHookAtTopLevel",
Expand Down
3 changes: 2 additions & 1 deletion crates/rome_js_analyze/src/semantic_analyzers/nursery.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use rome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic};
use rome_console::markup;
use rome_js_syntax::{jsx_ext::AnyJsxElement, AnyJsExpression, AnyJsxAttributeValue};
use rome_rowan::{AstNode, AstNodeList};

declare_rule! {
/// Enforces the usage of the attribute `title` for the element `iframe`
///
/// ## Examples
///
/// ### Invalid
///
/// ```jsx,expect_diagnostic
/// <iframe />
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe></iframe>
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title="" />
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title={""} />
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title={<span className={"token string"}></span>}></iframe>
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title={undefined} />
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title={false} />
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title={true} />
/// ```
///
/// ```jsx,expect_diagnostic
/// <iframe title={42} />
/// ```
///
///
/// ### Valid
///
/// ```jsx
/// <>
/// <iframe title="This is a unique title" />
/// <iframe title={uniqueTitle} />
/// <iframe {...props} />
/// </>
/// ```
///
/// ## Accessibility guidelines
///
/// [WCAG 2.4.1](https://www.w3.org/WAI/WCAG21/Understanding/bypass-blocks)
/// [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value)
///
pub(crate) UseIframeTitle {
version: "12.0.0",
name: "useIframeTitle",
recommended: true,
}
}

impl Rule for UseIframeTitle {
type Query = Ast<AnyJsxElement>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();

if node.name_value_token()?.text_trimmed() != "iframe" {
return None;
}

if node.attributes().is_empty() {
ematipico marked this conversation as resolved.
Show resolved Hide resolved
return Some(());
}

let has_jsx_spread_attribute = node
.attributes()
.iter()
.any(|x| x.as_jsx_spread_attribute().is_some());

if has_jsx_spread_attribute {
return None;
}

let Some(title_attribute) = node.find_attribute_by_name("title") else {
return Some(())
};

let attribute_value = title_attribute.initializer()?.value().ok()?;

match attribute_value {
AnyJsxAttributeValue::JsxString(str) => {
let text = str.inner_string_text().ok()?;
let is_valid_string = !text.is_empty() && text != r#"``"#;
if is_valid_string {
return None;
}
Some(())
}
AnyJsxAttributeValue::JsxExpressionAttributeValue(expr_attribute_value) => {
let expr = expr_attribute_value.expression().ok()?;
if let AnyJsExpression::JsIdentifierExpression(identifier) = expr {
let text = identifier.name().ok()?.value_token().ok()?;
let is_undefined_or_null =
text.text_trimmed() == "undefined" || text.text_trimmed() == "null";
if is_undefined_or_null {
return Some(());
} else {
// we assueme the identifier is a string type
return None;
}
}
Some(())
}
AnyJsxAttributeValue::AnyJsxTag(_) => Some(()),
}
}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
node.syntax().text_trimmed_range(),
markup! {
"Provide a "<Emphasis>"title"</Emphasis>" attribute when using "<Emphasis>"iframe"</Emphasis>" elements."
}
)
.note(markup! {
"Screen readers rely on the title set on an iframe to describe the content being displayed."
}),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<>
{/* invalid */}
<iframe />
<iframe></iframe>
<iframe title="" />
<iframe title={""} />
<iframe title={``} />
<iframe title={<span className={"token string"}></span>}></iframe>
<iframe title={undefined} />
<iframe title={false} />
<iframe title={true} />
<iframe title={42} />
</>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
expression: invalid.jsx
---
# Input
```js
<>
{/* invalid */}
<iframe />
<iframe></iframe>
<iframe title="" />
<iframe title={""} />
<iframe title={``} />
<iframe title={<span className={"token string"}></span>}></iframe>
<iframe title={undefined} />
<iframe title={false} />
<iframe title={true} />
<iframe title={42} />
</>;

```

# Diagnostics
```
invalid.jsx:3:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

1 │ <>
2 │ {/* invalid */}
> 3 │ <iframe />
│ ^^^^^^^^^^
4 │ <iframe></iframe>
5 │ <iframe title="" />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:4:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

2 │ {/* invalid */}
3 │ <iframe />
> 4 │ <iframe></iframe>
│ ^^^^^^^^
5 │ <iframe title="" />
6 │ <iframe title={""} />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:5:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

3 │ <iframe />
4 │ <iframe></iframe>
> 5 │ <iframe title="" />
│ ^^^^^^^^^^^^^^^^^^^
6 │ <iframe title={""} />
7 │ <iframe title={``} />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:6:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

4 │ <iframe></iframe>
5 │ <iframe title="" />
> 6 │ <iframe title={""} />
│ ^^^^^^^^^^^^^^^^^^^^^
7 │ <iframe title={``} />
8 │ <iframe title={<span className={"token string"}></span>}></iframe>

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:7:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

5 │ <iframe title="" />
6 │ <iframe title={""} />
> 7 │ <iframe title={``} />
│ ^^^^^^^^^^^^^^^^^^^^^
8 │ <iframe title={<span className={"token string"}></span>}></iframe>
9 │ <iframe title={undefined} />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:8:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

6 │ <iframe title={""} />
7 │ <iframe title={``} />
> 8 │ <iframe title={<span className={"token string"}></span>}></iframe>
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9 │ <iframe title={undefined} />
10 │ <iframe title={false} />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:9:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

7 │ <iframe title={``} />
8 │ <iframe title={<span className={"token string"}></span>}></iframe>
> 9 │ <iframe title={undefined} />
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10 │ <iframe title={false} />
11 │ <iframe title={true} />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:10:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

8 │ <iframe title={<span className={"token string"}></span>}></iframe>
9 │ <iframe title={undefined} />
> 10 │ <iframe title={false} />
│ ^^^^^^^^^^^^^^^^^^^^^^^^
11 │ <iframe title={true} />
12 │ <iframe title={42} />

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:11:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

9 │ <iframe title={undefined} />
10 │ <iframe title={false} />
> 11 │ <iframe title={true} />
│ ^^^^^^^^^^^^^^^^^^^^^^^
12 │ <iframe title={42} />
13 │ </>;

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```

```
invalid.jsx:12:2 lint/nursery/useIframeTitle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Provide a title attribute when using iframe elements.

10 │ <iframe title={false} />
11 │ <iframe title={true} />
> 12 │ <iframe title={42} />
│ ^^^^^^^^^^^^^^^^^^^^^
13 │ </>;
14 │

i Screen readers rely on the title set on an iframe to describe the content being displayed.


```


Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<>
{/* valid */}
<iframe title="This is a unique title" />
<iframe title={uniqueTitle} />
{/* this case might contain `title` attribute */}
<iframe {...{ title: "title" }} />
<iframe {...props} />
</>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
expression: valid.jsx
---
# Input
```js
<>
{/* valid */}
<iframe title="This is a unique title" />
<iframe title={uniqueTitle} />
{/* this case might contain `title` attribute */}
<iframe {...{ title: "title" }} />
<iframe {...props} />
</>;

```


Loading