Skip to content

Commit

Permalink
feat(lint): new UseValidAriaRoles
Browse files Browse the repository at this point in the history
  • Loading branch information
vasucp1207 committed Oct 19, 2023
1 parent aa3d3a5 commit 61dbed6
Show file tree
Hide file tree
Showing 15 changed files with 419 additions and 4 deletions.
1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ define_categories! {
"lint/nursery/useGroupedTypeImport": "https://biomejs.dev/linter/rules/use-grouped-type-import",
"lint/nursery/useImportRestrictions": "https://biomejs.dev/linter/rules/use-import-restrictions",
"lint/nursery/useShorthandAssign": "https://biomejs.dev/lint/rules/use-shorthand-assign",
"lint/nursery/useValidAriaRole": "https://biomejs.dev/lint/rules/use-valid-aria-role",
"lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread",
"lint/performance/noDelete": "https://biomejs.dev/linter/rules/no-delete",
"lint/security/noDangerouslySetInnerHtml": "https://biomejs.dev/linter/rules/no-dangerously-set-inner-html",
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/aria_analyzers/nursery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use biome_analyze::declare_group;

pub(crate) mod no_interactive_element_to_noninteractive_role;
pub(crate) mod use_aria_activedescendant_with_tabindex;
pub(crate) mod use_valid_aria_role;

declare_group! {
pub (crate) Nursery {
name : "nursery" ,
rules : [
self :: no_interactive_element_to_noninteractive_role :: NoInteractiveElementToNoninteractiveRole ,
self :: use_aria_activedescendant_with_tabindex :: UseAriaActivedescendantWithTabindex ,
self :: use_valid_aria_role :: UseValidAriaRole ,
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use crate::aria_services::Aria;
use biome_analyze::{context::RuleContext, declare_rule, Rule, RuleDiagnostic};
use biome_console::markup;
use biome_js_syntax::jsx_ext::AnyJsxElement;
use biome_rowan::AstNode;
use serde::{Deserialize, Serialize};

declare_rule! {
/// Elements with ARIA roles must use a valid, non-abstract ARIA role.
///
/// Source: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-role.md
///
///
/// ## Examples
///
/// ### Invalid
///
/// ```js,expect_diagnostic
/// <div role="datepicker"></div>
/// ```
///
/// ```js,expect_diagnostic
/// <div role="range"></div>
/// ```
///
/// ```js,expect_diagnostic
/// <div role=""></div>
/// ```
///
/// ```js,expect_diagnostic
/// <Foo role="foo"></Foo>
/// ```
///
/// ### Valid
///
/// ```js
/// <>
/// <div role="button"></div>
/// <div role={role}></div>
/// <div></div>
/// </>
/// ```
///
/// ## Accessibility guidelines
/// - [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value)
///
/// ## Resources
/// - [Chrome Audit Rules, AX_ARIA_01](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_aria_01)
/// - [DPUB-ARIA roles](https://www.w3.org/TR/dpub-aria-1.0/)
/// - [MDN: Using ARIA: Roles, states, and properties](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques)
///
pub(crate) UseValidAriaRole {
version: "next",
name: "useValidAriaRole",
recommended: false,
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UseValidAriaRoleOptions {
allowed_invalid_roles: Vec<String>,
ignore_non_dom: bool,
}

impl Default for UseValidAriaRoleOptions {
fn default() -> Self {
Self {
allowed_invalid_roles: Vec::new(),
ignore_non_dom: false,
}
}
}

impl Rule for UseValidAriaRole {
type Query = Aria<AnyJsxElement>;
type State = ();
type Signals = Option<Self::State>;
type Options = UseValidAriaRoleOptions;

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

let ignore_non_dom = options.ignore_non_dom;
let allowed_invalid_roles = &options.allowed_invalid_roles;

if ignore_non_dom {
if node.is_custom_component() {
return None;
}
}

let role_attribute = node.find_attribute_by_name("role")?;
if role_attribute.is_value_null_or_undefined() {
return None;
}

if node.is_custom_component() {
return Some(());
}

let role_attribute_static_value = role_attribute.as_static_value()?;
let role_attribute_value = role_attribute_static_value.text();
if role_attribute_static_value.is_null_or_undefined() {
return None;
}

let role_data = aria_roles.get_role(role_attribute_value);

let is_valid = allowed_invalid_roles.contains(&role_attribute_value.to_string());

if is_valid || role_data.is_some() {
return None;
}

Some(())
}

fn diagnostic(ctx: &RuleContext<Self>, _: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();

Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role."
},
)
.note(markup! {
"Elements with ARIA roles must use a valid, non-abstract ARIA role."
})
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<>
<div role="range"></div>
<div role="datepicker"></div>
<div role=""></div>
<Foo role={foo}></Foo>
</>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid.jsx
---
# Input
```js
<>
<div role="range"></div>
<div role="datepicker"></div>
<div role=""></div>
<Foo role={foo}></Foo>
</>
```

# Diagnostics
```
invalid.jsx:2:3 lint/nursery/useValidAriaRole ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role.
1 │ <>
> 2 │ <div role="range"></div>
│ ^^^^^^^^^^^^^^^^^^
3 │ <div role="datepicker"></div>
4 │ <div role=""></div>
i Elements with ARIA roles must use a valid, non-abstract ARIA role.
```

```
invalid.jsx:3:3 lint/nursery/useValidAriaRole ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role.
1 │ <>
2 │ <div role="range"></div>
> 3 │ <div role="datepicker"></div>
│ ^^^^^^^^^^^^^^^^^^^^^^^
4 │ <div role=""></div>
5 │ <Foo role={foo}></Foo>
i Elements with ARIA roles must use a valid, non-abstract ARIA role.
```

```
invalid.jsx:4:3 lint/nursery/useValidAriaRole ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role.
2 │ <div role="range"></div>
3 │ <div role="datepicker"></div>
> 4 │ <div role=""></div>
│ ^^^^^^^^^^^^^
5 │ <Foo role={foo}></Foo>
6 │ </>
i Elements with ARIA roles must use a valid, non-abstract ARIA role.
```

```
invalid.jsx:5:3 lint/nursery/useValidAriaRole ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role.
3 │ <div role="datepicker"></div>
4 │ <div role=""></div>
> 5 │ <Foo role={foo}></Foo>
│ ^^^^^^^^^^^^^^^^
6 │ </>
i Elements with ARIA roles must use a valid, non-abstract ARIA role.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<>
<div role="button"></div>
<div role={role}></div>
<div role></div>
<div></div>
</>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: valid.jsx
---
# Input
```js
<>
<div role="button"></div>
<div role={role}></div>
<div role></div>
<div></div>
</>
```


23 changes: 20 additions & 3 deletions crates/biome_service/src/configuration/linter/rules.rs

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

24 changes: 24 additions & 0 deletions crates/biome_service/src/configuration/parse/json/rules.rs

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

Loading

0 comments on commit 61dbed6

Please sign in to comment.