diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 97138c553d5ea..41d5162aafe24 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -254,6 +254,7 @@ mod jsx_a11y { pub mod no_autofocus; pub mod no_distracting_elements; pub mod prefer_tag_over_role; + pub mod role_support_aria_props; pub mod scope; pub mod tab_index_no_positive; } @@ -507,6 +508,7 @@ oxc_macros::declare_all_lint_rules! { jsx_a11y::tab_index_no_positive, jsx_a11y::aria_role, jsx_a11y::no_distracting_elements, + jsx_a11y::role_support_aria_props, oxc::approx_constant, oxc::const_comparisons, oxc::double_comparisons, diff --git a/crates/oxc_linter/src/rules/jsx_a11y/role_support_aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/role_support_aria_props.rs new file mode 100644 index 0000000000000..e5e11c28bc1d7 --- /dev/null +++ b/crates/oxc_linter/src/rules/jsx_a11y/role_support_aria_props.rs @@ -0,0 +1,1600 @@ +use oxc_ast::{ + ast::{JSXAttributeItem, JSXOpeningElement}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::{self, Error}, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; +use phf::phf_set; + +use crate::{ + context::LintContext, + globals::{VALID_ARIA_PROPS, VALID_ARIA_ROLES}, + rule::Rule, + utils::{ + get_attribute_name, get_element_type, get_string_literal_prop_value, has_jsx_prop_lowercase, + }, + AstNode, +}; + +declare_oxc_lint!( + /// ### What it does + /// + /// Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`. Many ARIA attributes (states and properties) can only be used on elements with particular roles. Some elements have implicit roles, such as ``, which will resolve to `role="link"`. + /// + /// ### Example + /// ```jsx + /// // Good + /// + /// + /// // Bad + /// + /// ``` + /// + RoleSupportAriaProps, + correctness +); + +#[derive(Debug, Default, Clone)] +pub struct RoleSupportAriaProps; + +#[derive(Debug, Error, Diagnostic)] +enum RoleSupportAriaPropsDiagnostic { + #[error("eslint-plugin-jsx-a11y(role-support-aria-props): The attribute {1} is not supported by the role {2}.")] + #[diagnostic(severity(warning), help("Try to remove invalid attribute {1}."))] + Default(#[label] Span, String, String), + + #[error("eslint-plugin-jsx-a11y(role-support-aria-props): The attribute {1} is not supported by the role {2}. This role is implicit on the element {3}.")] + #[diagnostic(severity(warning), help("Try to remove invalid attribute {1}."))] + IsImplicit(#[label] Span, String, String, String), +} + +impl Rule for RoleSupportAriaProps { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { + if let Some(el_type) = get_element_type(ctx, jsx_el) { + let role = has_jsx_prop_lowercase(jsx_el, "role"); + let role_value = role.map_or_else( + || get_implicit_role(jsx_el, el_type.as_str()), + |i| get_string_literal_prop_value(i), + ); + let is_implicit = role_value.is_some() && role.is_none(); + if let Some(role_value) = role_value { + if !VALID_ARIA_ROLES.contains(role_value) { + return; + } + let invalid_props = get_invalid_aria_props_for_role(role_value); + for attr in &jsx_el.attributes { + if let JSXAttributeItem::Attribute(attr) = attr { + let name = get_attribute_name(&attr.name).to_lowercase(); + if invalid_props.contains(&&name.as_str()) { + ctx.diagnostic(if is_implicit { + RoleSupportAriaPropsDiagnostic::IsImplicit( + attr.span, + name, + role_value.to_string(), + el_type.clone(), + ) + } else { + RoleSupportAriaPropsDiagnostic::Default( + attr.span, + name, + role_value.to_string(), + ) + }); + } + } + } + } + } + } + } +} + +/// ref: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/util/getImplicitRole.js +fn get_implicit_role<'a>( + node: &'a JSXOpeningElement<'a>, + element_type: &str, +) -> Option<&'static str> { + let implicit_role = match element_type { + "a" | "area" | "link" => match has_jsx_prop_lowercase(node, "href") { + Some(_) => "link", + None => "", + }, + "article" => "article", + "aside" => "complementary", + "body" => "document", + "button" => "button", + "datalist" | "select" => "listbox", + "details" => "group", + "dialog" => "dialog", + "form" => "form", + "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => "heading", + "hr" => "separator", + "img" => has_jsx_prop_lowercase(node, "alt").map_or("img", |i| { + get_string_literal_prop_value(i) + .map_or("img", |v| if v.is_empty() { "" } else { "img" }) + }), + "input" => has_jsx_prop_lowercase(node, "type").map_or("textbox", |input_type| { + match get_string_literal_prop_value(input_type) { + Some("button" | "image" | "reset" | "submit") => "button", + Some("checkbox") => "checkbox", + Some("radio") => "radio", + Some("range") => "slider", + _ => "textbox", + } + }), + "li" => "listitem", + "menu" => has_jsx_prop_lowercase(node, "type").map_or("", |v| { + get_string_literal_prop_value(v) + .map_or("", |v| if v == "toolbar" { "toolbar" } else { "" }) + }), + "menuitem" => { + has_jsx_prop_lowercase(node, "type").map_or( + "", + |v| match get_string_literal_prop_value(v) { + Some("checkbox") => "menuitemcheckbox", + Some("command") => "menuitem", + Some("radio") => "menuitemradio", + _ => "", + }, + ) + } + "meter" | "progress" => "progressbar", + "nav" => "navigation", + "ol" | "ul" => "list", + "option" => "option", + "output" => "status", + "section" => "region", + "tbody" | "tfoot" | "thead" => "rowgroup", + "textarea" => "textbox", + _ => "", + }; + + if VALID_ARIA_ROLES.contains(implicit_role) { + Some(implicit_role) + } else { + None + } +} + +fn get_invalid_aria_props_for_role(role_value: &str) -> Vec<&&str> { + // ref: https://github.com/A11yance/aria-query/blob/fff6f07c714e8048f4fe084cec74f24248e5673d/scripts/roles.json + let valid_props_for_value: phf::Set<&'static str> = match role_value { + "alert" | "banner" | "blockquote" | "command" | "complementary" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription", + }, + "alertdialog" | "dialog" | "window" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-modal", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "application" | "graphics-object" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "article" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize" + }, + "button" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-pressed", + "aria-relevant", + "aria-roledescription" + }, + "caption" | "code" | "deletion" | "emphasis" | "generic" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "cell" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-colindex", + "aria-colspan", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription", + "aria-rowindex", + "aria-rowspan" + }, + "checkbox" | "switch" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-checked", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription" + }, + "columnheader" | "rowheader" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-colindex", + "aria-colspan", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + "aria-rowindex", + "aria-rowspan", + "aria-selected", + "aria-sort" + }, + "combobox" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-autocomplete", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + }, + "composite" | "group" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "contentinfo" | "definition" | "directory" | "document" | "feed" | "figure" | "form" + | "img" | "landmark" | "list" | "log" | "main" | "marquee" | "math" | "navigation" + | "note" | "region" | "roletype" | "rowgroup" | "search" | "section" | "sectionhead" + | "status" | "structure" | "tabpanel" | "term" | "time" | "timer" | "tooltip" + | "widget" => { + phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + } + } + "doc-abstract" + | "doc-acknowledgments" + | "doc-afterword" + | "doc-appendix" + | "doc-backlink" + | "doc-bibliography" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "doc-biblioentry" | "doc-endnote" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize" + }, + "doc-biblioref" | "doc-chapter" | "doc-colophon" | "doc-conclusion" | "doc-cover" + | "doc-credit" | "doc-credits" | "doc-dedication" | "doc-endnotes" | "doc-epigraph" + | "doc-epilogue" | "doc-errata" | "doc-example" | "doc-footnote" | "doc-foreword" + | "doc-glossary" | "doc-glossref" | "doc-index" | "doc-introduction" | "doc-noteref" + | "doc-notice" | "doc-pagelist" | "doc-part" | "doc-preface" | "doc-prologue" + | "doc-qna" | "doc-subtitle" | "doc-tip" | "doc-toc" | "graphics-document" + | "graphics-symbol" => { + phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + } + } + "doc-pagebreak" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-orientation", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "doc-pullquote" | "none" => phf_set! {}, + "grid" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-colcount", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-multiselectable", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-roledescription", + "aria-rowcount" + }, + "gridcell" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-colindex", + "aria-colspan", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + "aria-rowindex", + "aria-rowspan", + "aria-selected" + }, + "heading" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription", + }, + "input" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "insertion" | "paragraph" | "presentation" | "strong" | "subscript" | "superscript" => { + phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + } + } + "link" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "listbox" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-multiselectable", + "aria-orientation", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + }, + "listitem" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize" + }, + "mark" => phf_set! { + "aria-atomic", + "aria-braillelabel", + "aria-brailleroledescription", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-description", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription" + }, + "menu" | "menubar" | "select" | "toolbar" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-orientation", + "aria-owns", + "aria-relevant", + "aria-roledescription", + }, + "menuitem" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize" + }, + "menuitemcheckbox" | "menuitemradio" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-checked", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + "aria-setsize" + }, + "meter" | "progressbar" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + "aria-valuetext", + }, + "option" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-checked", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize", + "aria-selected", + }, + "radio" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-checked", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize" + }, + "radiogroup" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-orientation", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription" + }, + "range" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow" + }, + "row" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-colindex", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-rowindex", + "aria-selected", + "aria-setsize" + }, + "scrollbar" | "separator" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-orientation", + "aria-owns", + "aria-relevant", + "aria-roledescription", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + "aria-valuetext", + }, + "searchbox" | "textbox" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-autocomplete", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-multiline", + "aria-owns", + "aria-placeholder", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription" + }, + "slider" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-orientation", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-roledescription", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + "aria-valuetext", + }, + "spinbutton" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + "aria-valuetext", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + }, + "tab" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-setsize", + "aria-selected", + }, + "table" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-colcount", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-owns", + "aria-relevant", + "aria-roledescription", + "aria-rowcount" + }, + "tablist" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-multiselectable", + "aria-orientation", + "aria-owns", + "aria-relevant", + "aria-roledescription", + }, + "tree" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-multiselectable", + "aria-orientation", + "aria-owns", + "aria-relevant", + "aria-required", + "aria-roledescription", + }, + "treegrid" => phf_set! { + "aria-activedescendant", + "aria-atomic", + "aria-busy", + "aria-colcount", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-flowto", + "aria-grabbed", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-live", + "aria-multiselectable", + "aria-orientation", + "aria-owns", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + "aria-rowcount" + }, + "treeitem" => phf_set! { + "aria-atomic", + "aria-busy", + "aria-checked", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-owns", + "aria-posinset", + "aria-relevant", + "aria-roledescription", + "aria-selected", + "aria-setsize" + }, + _ => unreachable!("role value is not valid"), + }; + + VALID_ARIA_PROPS.iter().filter(|i| !valid_props_for_value.contains(i)).collect::>() +} + +#[test] +fn test() { + use crate::tester::Tester; + + fn settings() -> serde_json::Value { + serde_json::json!({ + "jsx-a11y": { + "components": { + "Link": "a" + } + }, + }) + } + + let pass = vec![ + (r"", None, None), + (r"
", None, None), + (r#"
"#, None, None), + (r"
", None, None), + (r#"
"#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#"foobar"#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + // TODO: This should pass, but it doesn't. Because the current code does not determine if the attribute value is null, undefined, etc. + //(r#"

"#, None, None), + //(r#"

"#, None, None), + (r"