|
1 | 1 | const { roles } = require('aria-query'); |
2 | 2 |
|
3 | | -// Interactive ARIA roles — the set of concrete roles whose taxonomy in WAI-ARIA |
4 | | -// includes a widget / command / composite / input / range ancestor. Derived from |
5 | | -// aria-query so the list stays current with ARIA spec updates (including |
6 | | -// DPUB-ARIA and Graphics-ARIA additions) without hand maintenance. |
| 3 | +// Interactive ARIA roles — concrete roles whose taxonomy descends from `widget` |
| 4 | +// in aria-query. This is the same derivation jsx-a11y and lit-a11y use (they |
| 5 | +// define the canonical peer-plugin behavior here): |
| 6 | +// https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/util/isInteractiveRole.js |
| 7 | +// https://github.com/open-wc/open-wc/blob/main/packages/eslint-plugin-lit-a11y/lib/utils/isInteractiveElement.js |
7 | 8 | // |
8 | | -// `tooltip` and `toolbar` are added explicitly: |
9 | | -// - `tooltip`: ARIA 1.2 §5.4 lists tooltip among widget roles, but aria-query's |
10 | | -// superClass chain doesn't include `widget` for it. Practitioner convention |
11 | | -// (and jsx-a11y/vuejs-accessibility) treats it as interactive. |
12 | | -// - `toolbar`: does not descend from `widget` in aria-query, but supports |
13 | | -// `aria-activedescendant` and is widget-like in practice. jsx-a11y adds it |
14 | | -// with the same rationale. |
| 9 | +// `toolbar` is added explicitly — it does not descend from `widget` per |
| 10 | +// aria-query's taxonomy, but supports `aria-activedescendant` and is widget- |
| 11 | +// like in practice. jsx-a11y and lit-a11y add it for the same reason. |
| 12 | +// |
| 13 | +// `tooltip` is also added — ARIA 1.2 doesn't cleanly categorize tooltip under |
| 14 | +// the widget taxonomy (aria-query's superClass is `structure/section`), but |
| 15 | +// tooltips with interactive content (close buttons, links) are common and our |
| 16 | +// existing test suite treats them as interactive. |
15 | 17 | module.exports.INTERACTIVE_ROLES = buildInteractiveRoleSet(); |
16 | 18 |
|
17 | 19 | function buildInteractiveRoleSet() { |
18 | | - const result = new Set(['tooltip', 'toolbar']); |
| 20 | + const result = new Set(['toolbar', 'tooltip']); |
19 | 21 | for (const [role, def] of roles) { |
20 | 22 | if (def.abstract) { |
21 | 23 | continue; |
22 | 24 | } |
23 | | - const ancestors = new Set(); |
24 | | - for (const chain of def.superClass || []) { |
25 | | - for (const cls of chain) { |
26 | | - ancestors.add(cls); |
27 | | - } |
28 | | - } |
29 | | - if ( |
30 | | - ancestors.has('widget') || |
31 | | - ancestors.has('command') || |
32 | | - ancestors.has('composite') || |
33 | | - ancestors.has('input') || |
34 | | - ancestors.has('range') |
35 | | - ) { |
| 25 | + const descendsFromWidget = (def.superClass || []).some((chain) => |
| 26 | + chain.includes('widget') |
| 27 | + ); |
| 28 | + if (descendsFromWidget) { |
36 | 29 | result.add(role); |
37 | 30 | } |
38 | 31 | } |
|
0 commit comments