diff --git a/crates/rome_aria/src/lib.rs b/crates/rome_aria/src/lib.rs index 3597ff48e524..3368ec63fc7d 100644 --- a/crates/rome_aria/src/lib.rs +++ b/crates/rome_aria/src/lib.rs @@ -7,7 +7,7 @@ pub mod roles; pub use properties::AriaProperties; pub(crate) use roles::AriaRoleDefinition; pub use roles::AriaRoles; -use rome_aria_metadata::{AriaPropertiesEnum, AriaPropertyTypeEnum}; +pub use rome_aria_metadata::{AriaPropertiesEnum, AriaPropertyTypeEnum}; /// It checks if an ARIA property is valid /// diff --git a/crates/rome_aria/src/macros.rs b/crates/rome_aria/src/macros.rs index ae69d9d25f64..0308a853efc0 100644 --- a/crates/rome_aria/src/macros.rs +++ b/crates/rome_aria/src/macros.rs @@ -14,11 +14,11 @@ macro_rules! define_role { } impl $crate::AriaRoleDefinition for $id { - fn properties<'a>(&self) -> std::slice::Iter<'a, (&str, bool)> { + fn properties(&self) -> std::slice::Iter<(&str, bool)> { $id::PROPS.iter() } - fn roles<'a>(&self) -> std::slice::Iter<'a, &str> { + fn roles(&self) -> std::slice::Iter<&str> { $id::ROLES.iter() } } @@ -40,11 +40,11 @@ macro_rules! define_property { } impl AriaPropertyDefinition for $id { - fn values<'a>(&self) -> std::slice::Iter<'a, &str> { + fn values(&self) -> std::slice::Iter<&'static str> { $id::VALUES.iter() } - fn property_type<'a>(&self) -> $crate::AriaPropertyTypeEnum { + fn property_type(&self) -> $crate::AriaPropertyTypeEnum { // SAFETY: PROPERTY_TYPE is internal and should not contain extraneous properties $crate::AriaPropertyTypeEnum::from_str($id::PROPERTY_TYPE).unwrap() } diff --git a/crates/rome_aria/src/properties.rs b/crates/rome_aria/src/properties.rs index 54db7ddd743c..6b312ff76ed0 100644 --- a/crates/rome_aria/src/properties.rs +++ b/crates/rome_aria/src/properties.rs @@ -97,7 +97,7 @@ define_property! { define_property! { AriaDropeffect { - PROPERTY_TYPE: "id", + PROPERTY_TYPE: "tokenlist", VALUES: ["copy", "execute", "link", "move", "none", "popup"], } } @@ -342,7 +342,7 @@ define_property! { pub struct AriaProperties; impl AriaProperties { - pub fn get_property(&self, property_name: &str) -> Option<&dyn AriaPropertyDefinition> { + pub fn get_property<'a>(&self, property_name: &str) -> Option<&'a dyn AriaPropertyDefinition> { Some(match property_name { "aria-activedescendant" => &AriaActivedescendant as &dyn AriaPropertyDefinition, "aria-autocomplete" => &AriaAutocomplete as &dyn AriaPropertyDefinition, @@ -398,7 +398,7 @@ impl AriaProperties { pub trait AriaPropertyDefinition: Debug { /// Returns the allowed values by this property - fn values<'a>(&self) -> Iter<'a, &str>; + fn values(&self) -> Iter<&str>; /// Returns the property type fn property_type(&self) -> AriaPropertyTypeEnum; diff --git a/crates/rome_aria/src/roles.rs b/crates/rome_aria/src/roles.rs index 04df57f4098e..60bca8608114 100644 --- a/crates/rome_aria/src/roles.rs +++ b/crates/rome_aria/src/roles.rs @@ -18,10 +18,10 @@ pub trait AriaRoleDefinition: Debug { /// let properties = checkbox_role.properties(); /// assert_eq!(properties.len(), 2); /// ``` - fn properties<'a>(&self) -> Iter<'a, (&str, bool)>; + fn properties(&self) -> Iter<(&str, bool)>; /// It returns an iterator over the possible roles of this definition - fn roles<'a>(&self) -> Iter<'a, &str>; + fn roles(&self) -> Iter<&str>; /// Given a [aria property](ARIA_PROPERTIES) as input, it checks if it's required /// for the current role. @@ -45,7 +45,7 @@ pub trait AriaRoleDefinition: Debug { if is_aria_property_valid(property_to_check) { let property_to_check = AriaPropertiesEnum::from_str(property_to_check); if let Ok(property_to_check) = property_to_check { - for (property, required) in self.properties().as_ref() { + for (property, required) in self.properties() { let property = AriaPropertiesEnum::from_str(property).unwrap(); if property == property_to_check { return *required; diff --git a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs index a9856fb0257e..7ad39e224e1c 100644 --- a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs +++ b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs @@ -1,12 +1,14 @@ use crate::aria_services::Aria; use rome_analyze::context::RuleContext; use rome_analyze::{declare_rule, Rule, RuleDiagnostic}; +use rome_aria::AriaPropertyTypeEnum; use rome_console::markup; use rome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, AnyJsxAttributeValue, JsSyntaxToken, JsxAttribute, TextRange, }; use rome_rowan::{AstNode, AstNodeList}; +use std::slice::Iter; declare_rule! { /// Enforce that ARIA state and property values are valid. @@ -24,6 +26,14 @@ declare_rule! { /// some text /// ``` /// + /// ```jsx, expect_diagnostic + /// some text + /// ``` + /// + /// ```jsx, expect_diagnostic + /// some text + /// ``` + /// /// ### Valid /// /// ```jsx @@ -48,8 +58,9 @@ declare_rule! { pub(crate) struct UseAriaProptypesState { attribute_value_range: TextRange, - allowed_values: Vec, + allowed_values: Iter<'static, &'static str>, attribute_name: JsSyntaxToken, + property_type: AriaPropertyTypeEnum, } impl Rule for UseAriaPropTypes { @@ -76,15 +87,21 @@ impl Rule for UseAriaPropTypes { // Early error, the template literal is empty return Some(UseAriaProptypesState { attribute_value_range, - allowed_values: aria_property - .values() - .map(|value| value.to_string()) - .collect::>(), + allowed_values: aria_property.values(), attribute_name, + property_type: aria_property.property_type(), }); - } else { - None } + template + .elements() + .iter() + .next() + .and_then(|chunk| { + chunk + .as_js_template_chunk_element() + .and_then(|t| t.template_chunk_token().ok()) + }) + .map(|t| t.token_text_trimmed()) } AnyJsExpression::AnyJsLiteralExpression( AnyJsLiteralExpression::JsStringLiteralExpression(string), @@ -98,11 +115,9 @@ impl Rule for UseAriaPropTypes { if !aria_property.contains_correct_value(attribute_text.text()) { return Some(UseAriaProptypesState { attribute_value_range, - allowed_values: aria_property - .values() - .map(|value| value.to_string()) - .collect::>(), + allowed_values: aria_property.values(), attribute_name, + property_type: aria_property.property_type(), }); } } @@ -112,19 +127,73 @@ impl Rule for UseAriaPropTypes { fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { let attribute_name = state.attribute_name.text_trimmed(); - Some( - RuleDiagnostic::new( - rule_category!(), - state.attribute_value_range, - markup! { + let diagnostic = RuleDiagnostic::new( + rule_category!(), + state.attribute_value_range, + markup! { "The value of the ARIA attribute "{attribute_name}" is not correct." }, - ).footer_list( - markup!{ - "The supported values for the "{attribute_name}" attribute are:" + ); + + let diagnostic = match state.property_type { + AriaPropertyTypeEnum::Boolean => { + diagnostic.footer_list( + markup!{ + "The only supported values for the "{attribute_name}" is one of the following:" + }, + &["true", "false"] + ) + } + AriaPropertyTypeEnum::Integer => { + diagnostic.note( + markup!{ + "The only value supported is a number without fractional components." + } + ) + } + AriaPropertyTypeEnum::Id | + AriaPropertyTypeEnum::Idlist | + AriaPropertyTypeEnum::String => { + diagnostic.note( + markup!{ + "The only supported value is text." + } + ) + } + + AriaPropertyTypeEnum::Number => { + diagnostic.note( + markup!{ + "The only supported value is number." + } + ) + } + AriaPropertyTypeEnum::Token => { + diagnostic.footer_list( + markup!{ + "The only supported value for the "{attribute_name}" is one of the following:" }, - &state.allowed_values - ) - ) + state.allowed_values.as_slice() + ) + } + AriaPropertyTypeEnum::Tokenlist => { + diagnostic.footer_list( + markup!{ + "The values supported for "{attribute_name}" are one or more of the following:" + }, + state.allowed_values.as_slice() + ) + } + AriaPropertyTypeEnum::Tristate => { + diagnostic.footer_list( + markup!{ + "The only supported value for the "{attribute_name}" one of the following:" + }, + &["true", "false", "mixed"] + ) + } + }; + + Some(diagnostic) } } diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index 067938126813..9556b73fbd3d 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -226,17 +226,13 @@ mod tests { String::from_utf8(buffer).unwrap() } - const SOURCE: &str = r#"something.forEach((Element, index) => { - return
foo
-
; -})"#; + const SOURCE: &str = r#""#; let parsed = parse(SOURCE, FileId::zero(), SourceType::jsx()); let mut error_ranges: Vec = Vec::new(); let options = AnalyzerOptions::default(); - let rule_filter = RuleFilter::Rule("suspicious", "noArrayIndexKey"); + let rule_filter = RuleFilter::Rule("nursery", "useAriaPropTypes"); analyze( FileId::zero(), &parsed.tree(), diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx index 4c19a20bc193..05542f30023a 100644 --- a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx +++ b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx @@ -1,8 +1,15 @@ var a = ; var a = ; var a = ; +var a = ; +var a = ; var a = ; var a = ; var a = ; var a = ; +var a = ; var a = ; +var a = ; +var a = ; +var a = ; +var a = ; diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap index 931c89447228..8a9958050565 100644 --- a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap @@ -7,11 +7,18 @@ expression: invalid.jsx var a = ; var a = ; var a = ; +var a = ; +var a = ; var a = ; var a = ; var a = ; var a = ; +var a = ; var a = ; +var a = ; +var a = ; +var a = ; +var a = ; ``` @@ -26,6 +33,12 @@ invalid.jsx:1:31 lint/nursery/useAriaPropTypes ━━━━━━━━━━━ 2 │ var a = ; 3 │ var a = ; + i The only supported value for the aria-checked one of the following: + + - true + - false + - mixed + ``` @@ -38,9 +51,9 @@ invalid.jsx:2:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━ > 2 │ var a = ; │ ^^^^^^^^^^^^^^^^^^^^^^^^ 3 │ var a = ; - 4 │ var a = ; + 4 │ var a = ; - i The supported values for the aria-autocomplete attribute are: + i The only supported value for the aria-autocomplete is one of the following: - inline - list @@ -59,10 +72,10 @@ invalid.jsx:3:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━ 2 │ var a = ; > 3 │ var a = ; │ ^^^^^^^^^^^^^^^^^^ - 4 │ var a = ; - 5 │ var a = ; + 4 │ var a = ; + 5 │ var a = ; - i The supported values for the aria-invalid attribute are: + i The only supported value for the aria-invalid is one of the following: - grammar - false @@ -75,14 +88,21 @@ invalid.jsx:3:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━ ``` invalid.jsx:4:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! The value of the ARIA attribute aria-errormessage is not correct. + ! The value of the ARIA attribute aria-invalid is not correct. 2 │ var a = ; 3 │ var a = ; - > 4 │ var a = ; + > 4 │ var a = ; │ ^^^^^^^^^^^^^^^^^^^^ - 5 │ var a = ; - 6 │ var a = ; + 5 │ var a = ; + 6 │ var a = ; + + i The only supported value for the aria-invalid is one of the following: + + - grammar + - false + - spelling + - true ``` @@ -90,16 +110,55 @@ invalid.jsx:4:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━ ``` invalid.jsx:5:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! The value of the ARIA attribute aria-relevant is not correct. + ! The value of the ARIA attribute aria-invalid is not correct. 3 │ var a = ; - 4 │ var a = ; - > 5 │ var a = ; + 4 │ var a = ; + > 5 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 6 │ var a = ; + 7 │ var a = ; + + i The only supported value for the aria-invalid is one of the following: + + - grammar + - false + - spelling + - true + + +``` + +``` +invalid.jsx:6:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-errormessage is not correct. + + 4 │ var a = ; + 5 │ var a = ; + > 6 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 7 │ var a = ; + 8 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:7:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-relevant is not correct. + + 5 │ var a = ; + 6 │ var a = ; + > 7 │ var a = ; │ ^^^^^^^^^^^^^^^^^^^^^ - 6 │ var a = ; - 7 │ var a = ; + 8 │ var a = ; + 9 │ var a = ; - i The supported values for the aria-relevant attribute are: + i The values supported for aria-relevant are one or more of the following: - additions - all @@ -110,45 +169,147 @@ invalid.jsx:5:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━ ``` ``` -invalid.jsx:6:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:8:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! The value of the ARIA attribute aria-labelledby is not correct. - 4 │ var a = ; - 5 │ var a = ; - > 6 │ var a = ; - │ ^^^^^^^^^^^^^^^^^^ - 7 │ var a = ; - 8 │ var a = ; + 6 │ var a = ; + 7 │ var a = ; + > 8 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^ + 9 │ var a = ; + 10 │ var a = ; + + i The only supported value is text. ``` ``` -invalid.jsx:7:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:9:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! The value of the ARIA attribute aria-labelledby is not correct. - 5 │ var a = ; - 6 │ var a = ; - > 7 │ var a = ; - │ ^^^^^^^^^^^^^^^^^^^^ - 8 │ var a = ; - 9 │ + 7 │ var a = ; + 8 │ var a = ; + > 9 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 10 │ var a = ; + 11 │ var a = ; + + i The only supported value is text. ``` ``` -invalid.jsx:8:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:10:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-labelledby is not correct. + + 8 │ var a = ; + 9 │ var a = ; + > 10 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 11 │ var a = ; + 12 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:11:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! The value of the ARIA attribute aria-details is not correct. - 6 │ var a = ; - 7 │ var a = ; - > 8 │ var a = ; - │ ^^^^^^^^^^^^^^^ - 9 │ + 9 │ var a = ; + 10 │ var a = ; + > 11 │ var a = ; + │ ^^^^^^^^^^^^^^^ + 12 │ var a = ; + 13 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:12:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-setsize is not correct. + + 10 │ var a = ; + 11 │ var a = ; + > 12 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^ + 13 │ var a = ; + 14 │ var a = ; + + i The only value supported is a number without fractional components. + + +``` + +``` +invalid.jsx:13:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-valuemax is not correct. + + 11 │ var a = ; + 12 │ var a = ; + > 13 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^ + 14 │ var a = ; + 15 │ var a = ; + + i The only supported value is number. + + +``` + +``` +invalid.jsx:14:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-dropeffect is not correct. + + 12 │ var a = ; + 13 │ var a = ; + > 14 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^^ + 15 │ var a = ; + 16 │ + + i The values supported for aria-dropeffect are one or more of the following: + + - copy + - execute + - link + - move + - none + - popup + + +``` + +``` +invalid.jsx:15:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-orientation is not correct. + + 13 │ var a = ; + 14 │ var a = ; + > 15 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^^^ + 16 │ + + i The only supported value for the aria-orientation is one of the following: + + - vertical + - undefined + - horizontal ``` diff --git a/website/src/pages/lint/rules/useAriaPropTypes.md b/website/src/pages/lint/rules/useAriaPropTypes.md index 58fbd66da1e8..9971ebfc5857 100644 --- a/website/src/pages/lint/rules/useAriaPropTypes.md +++ b/website/src/pages/lint/rules/useAriaPropTypes.md @@ -23,6 +23,12 @@ Enforce that ARIA state and property values are valid. ^^^^^^^^^^^^^^^^^^^ 2 │ + The only supported value for the aria-checked one of the following: + + - true + - false + - mixed + ```jsx @@ -37,6 +43,44 @@ Enforce that ARIA state and property values are valid. ^^^^^^^^^^^^^^^^^^ 2 │ + The only supported value is text. + + + +```jsx +some text +``` + +
nursery/useAriaPropTypes.js:1:7 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   The value of the ARIA attribute aria-valuemax is not correct.
+  
+  > 1 │ <span aria-valuemax="hey">some text</span>
+         ^^^^^^^^^^^^^^^^^^^
+    2 │ 
+  
+   The only supported value is number.
+  
+
+ +```jsx +some text +``` + +
nursery/useAriaPropTypes.js:1:7 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   The value of the ARIA attribute aria-orientation is not correct.
+  
+  > 1 │ <span aria-orientation="hey">some text</span>
+         ^^^^^^^^^^^^^^^^^^^^^^
+    2 │ 
+  
+   The only supported value for the aria-orientation is one of the following:
+  
+  - vertical
+  - undefined
+  - horizontal
+  
 
### Valid