Skip to content

Commit

Permalink
fix: noAriaHiddenonFocusable when using a JSX expression (#3708)
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonyshew authored Aug 26, 2024
1 parent f64769b commit b26cc29
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 47 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

- Migrating from Prettier or ESLint no longer overwrite the `overrides` field from the configuration ([#3544](https://github.com/biomejs/biome/issues/3544)). Contributed by @Conaclos

- Fix JSX expressions for `noAriaHiddenOnFocusable` ([#3708](https://github.com/biomejs/biome/pull/3708)) . Contributed by @anthonyshew

### Configuration

- Add support for loading configuration from `.editorconfig` files ([#1724](https://github.com/biomejs/biome/issues/1724)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use biome_analyze::{
RuleSource,
};
use biome_console::markup;
use biome_js_syntax::jsx_ext::AnyJsxElement;
use biome_js_syntax::{jsx_ext::AnyJsxElement, AnyJsxAttributeValue, AnyNumberLikeExpression};
use biome_rowan::{AstNode, BatchMutationExt};

declare_lint_rule! {
Expand Down Expand Up @@ -33,6 +33,10 @@ declare_lint_rule! {
/// ```
///
/// ```jsx
/// <button aria-hidden="true" tabIndex={-1} />
/// ```
///
/// ```jsx
/// <div aria-hidden="true"><a href="#"></a></div>
/// ```
///
Expand Down Expand Up @@ -76,12 +80,34 @@ impl Rule for NoAriaHiddenOnFocusable {
}

if let Some(tabindex_attr) = node.find_attribute_by_name("tabIndex") {
if let Some(tabindex_static) = tabindex_attr.as_static_value() {
let tabindex_text = tabindex_static.text();
let tabindex_val = tabindex_text.trim().parse::<i32>();
let tabindex_val = tabindex_attr.initializer()?.value().ok()?;

if let Ok(num) = tabindex_val {
return (num >= 0).then_some(());
match tabindex_val {
AnyJsxAttributeValue::AnyJsxTag(jsx_tag) => {
let value = jsx_tag.text().parse::<i32>();
if let Ok(num) = value {
return (num >= 0).then_some(());
}
}
AnyJsxAttributeValue::JsxString(jsx_string) => {
let value = jsx_string
.inner_string_text()
.ok()?
.to_string()
.parse::<i32>();
if let Ok(num) = value {
return (num >= 0).then_some(());
}
}
AnyJsxAttributeValue::JsxExpressionAttributeValue(value) => {
let expression = value.expression().ok()?;
let expression_value =
AnyNumberLikeExpression::cast(expression.into_syntax())?
.value()?
.parse::<i32>();
if let Ok(num) = expression_value {
return (num >= 0).then_some(());
}
}
}
}
Expand All @@ -90,7 +116,6 @@ impl Rule for NoAriaHiddenOnFocusable {
return Some(());
}
}

None
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ use biome_analyze::{
use biome_aria::AriaRoles;
use biome_console::markup;
use biome_js_syntax::{
jsx_ext::AnyJsxElement, AnyJsxAttributeValue, JsNumberLiteralExpression,
JsStringLiteralExpression, JsUnaryExpression, TextRange,
jsx_ext::AnyJsxElement, AnyJsxAttributeValue, AnyNumberLikeExpression, TextRange,
};
use biome_rowan::{declare_node_union, AstNode, BatchMutationExt};
use biome_rowan::{AstNode, BatchMutationExt};

declare_lint_rule! {
/// Enforce that `tabIndex` is not assigned to non-interactive HTML elements.
Expand Down Expand Up @@ -58,40 +57,6 @@ declare_lint_rule! {
}
}

declare_node_union! {
/// Subset of expressions supported by this rule.
///
/// ## Examples
///
/// - `JsStringLiteralExpression` &mdash; `"5"`
/// - `JsNumberLiteralExpression` &mdash; `5`
/// - `JsUnaryExpression` &mdash; `+5` | `-5`
///
pub AnyNumberLikeExpression = JsStringLiteralExpression | JsNumberLiteralExpression | JsUnaryExpression
}

impl AnyNumberLikeExpression {
/// Returns the value of a number-like expression; it returns the expression
/// text for literal expressions. However, for unary expressions, it only
/// returns the value for signed numeric expressions.
pub(crate) fn value(&self) -> Option<String> {
match self {
AnyNumberLikeExpression::JsStringLiteralExpression(string_literal) => {
return Some(string_literal.inner_string_text().ok()?.to_string());
}
AnyNumberLikeExpression::JsNumberLiteralExpression(number_literal) => {
return Some(number_literal.value_token().ok()?.to_string());
}
AnyNumberLikeExpression::JsUnaryExpression(unary_expression) => {
if unary_expression.is_signed_numeric_literal().ok()? {
return Some(unary_expression.text());
}
}
}
None
}
}

pub struct RuleState {
attribute_range: TextRange,
element_name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<img aria-hidden="true" />
<a aria-hidden="false" href="#" />
<button aria-hidden="true" tabIndex="-1" />
<button aria-hidden="true" tabIndex={-1} />
<a href="/" />
<div aria-hidden="true"><a href="#"></a></div>
<button />
</>
</>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
assertion_line: 86
expression: valid.jsx
---
# Input
Expand All @@ -9,10 +10,10 @@ expression: valid.jsx
<img aria-hidden="true" />
<a aria-hidden="false" href="#" />
<button aria-hidden="true" tabIndex="-1" />
<button aria-hidden="true" tabIndex={-1} />
<a href="/" />
<div aria-hidden="true"><a href="#"></a></div>
<button />
</>
```


```
34 changes: 34 additions & 0 deletions crates/biome_js_syntax/src/expr_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2241,3 +2241,37 @@ mod test {
assert!(template.is_test_each_pattern_callee());
}
}

declare_node_union! {
/// Subset of expressions supported by this rule.
///
/// ## Examples
///
/// - `JsStringLiteralExpression` &mdash; `"5"`
/// - `JsNumberLiteralExpression` &mdash; `5`
/// - `JsUnaryExpression` &mdash; `+5` | `-5`
///
pub AnyNumberLikeExpression = JsStringLiteralExpression | JsNumberLiteralExpression | JsUnaryExpression
}

impl AnyNumberLikeExpression {
/// Returns the value of a number-like expression; it returns the expression
/// text for literal expressions. However, for unary expressions, it only
/// returns the value for signed numeric expressions.
pub fn value(&self) -> Option<String> {
match self {
AnyNumberLikeExpression::JsStringLiteralExpression(string_literal) => {
return Some(string_literal.inner_string_text().ok()?.to_string());
}
AnyNumberLikeExpression::JsNumberLiteralExpression(number_literal) => {
return Some(number_literal.value_token().ok()?.to_string());
}
AnyNumberLikeExpression::JsUnaryExpression(unary_expression) => {
if unary_expression.is_signed_numeric_literal().ok()? {
return Some(unary_expression.text());
}
}
}
None
}
}

0 comments on commit b26cc29

Please sign in to comment.