@@ -64,24 +64,19 @@ ruleTester.run('audit:no-noninteractive-tabindex (gts)', rule, {
6464 // Non-interactive native element + tabindex="-1" — valid via -1 exemption.
6565 '<template><article tabindex="-1"></article></template>' ,
6666
67- // === DIVERGENCE — components whose name lowercases to a native tag ===
68- // jsx-a11y `components` setting maps `<Article>` → `article`, then treats
69- // it like the native tag. Without such a setting jsx-a11y would treat
70- // `<Article>` as an opaque component and skip.
71- // Our rule lowercases `node.tag` before the `dom.has(tag)` check, so
72- // `<Article>` becomes `article` and IS validated against the dom map.
73- // This has two effects:
74- // (a) `<Article tabindex="-1" />` passes via the tabindex=-1 exemption
75- // (valid in jsx-a11y too, so no visible divergence here).
76- // (b) `<Article tabindex={{0}} />` is FLAGGED by our rule (see invalid
77- // section below). jsx-a11y without `components` setting: VALID (opaque
78- // component skip). jsx-a11y with `components: {Article: 'article'}`
79- // setting: INVALID. Our rule: INVALID regardless — false positive
80- // for the no-settings case.
81- // Components whose name does NOT collide with a native tag (e.g.
82- // `<MyButton>` → `mybutton` which is not in the dom map) are correctly
83- // skipped.
67+ // === Upstream parity — PascalCase component skip ===
68+ // Components whose name lowercases to a native tag (e.g. `<Article>` →
69+ // `article`) are correctly skipped by `isComponentInvocation`, which
70+ // classifies the invocation BEFORE the `dom.has(tag)` check. Parity with
71+ // jsx-a11y's no-settings default (opaque component skip).
72+ //
73+ // Previously this was a FALSE POSITIVE (audit B8): `<Article tabindex={{0}} />`
74+ // was flagged because the rule lowercased `node.tag` before the
75+ // `dom.has(tag)` check, matching `article` in the dom map and validating
76+ // the component like the native tag. Adopting `isComponentInvocation`
77+ // fixes the FP.
8478 '<template><Article tabindex="-1" /></template>' ,
79+ '<template><Article tabindex={{0}} /></template>' ,
8580
8681 // === Upstream parity (jsx-a11y recommended valid) ===
8782
@@ -154,23 +149,11 @@ ruleTester.run('audit:no-noninteractive-tabindex (gts)', rule, {
154149 // assertion here; captured as a comment only. Our behavior matches
155150 // jsx-a11y RECOMMENDED, not strict.
156151
157- // === DIVERGENCE — component name collides with a native tag ===
158- // `<Article tabIndex={0} />` — classifications:
159- // jsx-a11y without `components` setting: VALID (opaque component skip).
160- // jsx-a11y with `components: {Article: 'article'}`: INVALID (article
161- // is non-interactive).
162- // Our rule: INVALID unconditionally. We lowercase `node.tag` before the
163- // `dom.has(tag)` check, so `Article` → `article` is found in the dom
164- // map and validated like the native tag. This is a FALSE POSITIVE
165- // relative to jsx-a11y's no-settings default, and accidental parity
166- // with jsx-a11y's components-configured mode.
167- // Components whose lowercased name doesn't collide with a native tag
168- // (e.g. `<MyButton>`) are correctly skipped.
169- {
170- code : '<template><Article tabindex={{0}} /></template>' ,
171- output : null ,
172- errors : [ { messageId : 'noNonInteractiveTabindex' } ] ,
173- } ,
152+ // === Resolved — component name collides with a native tag ===
153+ // Audit B8 previously flagged `<Article tabindex={{0}} />` as a FALSE
154+ // POSITIVE. After adopting `isComponentInvocation`, PascalCase components
155+ // (including those whose lowercased name collides with a native tag) are
156+ // correctly skipped. See the valid section for the parity assertion.
174157 ] ,
175158} ) ;
176159
@@ -189,11 +172,12 @@ hbsRuleTester.run('audit:no-noninteractive-tabindex (hbs)', rule, {
189172 '<article tabindex="-1"></article>' ,
190173 // Dynamic role — parity with jsx-a11y recommended (allowExpressionValues: true).
191174 '<div role={{this.role}} tabindex="0"></div>' ,
192- // Non-tag-colliding component — skipped (not in aria-query dom map).
193- // Parity with jsx-a11y's no-settings default.
194- // (Components whose lowercased name collides with a native tag diverge;
195- // see `<Article tabindex={{0}} / >` in the gts invalid section.)
175+ // Component invocation — skipped via isComponentInvocation. Parity with
176+ // jsx-a11y's no-settings default. Holds for both non-colliding names
177+ // (e.g. `<MyButton>`) and names that lowercase to a native tag
178+ // (e.g. `<Article>` → `article`).
196179 '<MyButton tabindex="0" />' ,
180+ '<Article tabindex="0"></Article>' ,
197181 ] ,
198182 invalid : [
199183 // Parity — neverValid cases in hbs form.
0 commit comments