@markuplint/ml-spec 3.0.0-canary.5 → 3.0.0-dev.177

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +1 -1
  2. package/lib/constant/aria-version.d.ts +2 -0
  3. package/lib/constant/aria-version.js +5 -0
  4. package/lib/dom-traverse/accname-computation.js +3 -1
  5. package/lib/dom-traverse/get-attr-specs.d.ts +1 -1
  6. package/lib/dom-traverse/get-attr-specs.js +3 -1
  7. package/lib/dom-traverse/get-computed-aria-props.d.ts +6 -6
  8. package/lib/dom-traverse/get-computed-aria-props.js +4 -2
  9. package/lib/dom-traverse/get-computed-role.d.ts +6 -1
  10. package/lib/dom-traverse/get-computed-role.js +41 -10
  11. package/lib/dom-traverse/get-content-model.d.ts +28 -2
  12. package/lib/dom-traverse/get-content-model.js +4 -2
  13. package/lib/dom-traverse/get-explicit-role.js +9 -4
  14. package/lib/dom-traverse/get-implicit-role.d.ts +6 -2
  15. package/lib/dom-traverse/get-implicit-role.js +6 -2
  16. package/lib/dom-traverse/get-non-presentational-ancestor.d.ts +10 -4
  17. package/lib/dom-traverse/get-non-presentational-ancestor.js +10 -2
  18. package/lib/dom-traverse/get-permitted-roles.d.ts +7 -3
  19. package/lib/dom-traverse/get-permitted-roles.js +3 -1
  20. package/lib/dom-traverse/get-spec.d.ts +4 -1
  21. package/lib/dom-traverse/get-spec.js +3 -1
  22. package/lib/dom-traverse/has-required-owned-elements.d.ts +8 -2
  23. package/lib/dom-traverse/has-required-owned-elements.js +14 -8
  24. package/lib/dom-traverse/is-exposed.d.ts +1 -1
  25. package/lib/dom-traverse/is-exposed.js +43 -7
  26. package/lib/dom-traverse/matches-context-role.d.ts +7 -0
  27. package/lib/dom-traverse/matches-context-role.js +27 -0
  28. package/lib/dom-traverse/may-be-focusable.d.ts +1 -1
  29. package/lib/dom-traverse/may-be-focusable.js +3 -1
  30. package/lib/index.d.ts +4 -0
  31. package/lib/index.js +4 -0
  32. package/lib/specs/aria-specs.d.ts +7 -4
  33. package/lib/specs/content-model-category-to-tag-names.d.ts +4 -1
  34. package/lib/specs/get-aria.d.ts +8 -1
  35. package/lib/specs/get-aria.js +8 -2
  36. package/lib/specs/get-attr-specs.d.ts +6 -2
  37. package/lib/specs/get-attr-specs.js +2 -14
  38. package/lib/specs/get-implicit-role.d.ts +7 -1
  39. package/lib/specs/get-permitted-roles.d.ts +9 -3
  40. package/lib/specs/get-permitted-roles.js +19 -15
  41. package/lib/specs/get-role-spec.d.ts +10 -3
  42. package/lib/specs/get-role-spec.js +2 -2
  43. package/lib/specs/get-selectors-by-content-model-category.d.ts +1 -1
  44. package/lib/specs/get-selectors-by-content-model-category.js +1 -1
  45. package/lib/specs/get-spec-by-tag-name.d.ts +5 -1
  46. package/lib/specs/get-spec-by-tag-name.js +3 -2
  47. package/lib/specs/is-nothing-content-model.d.ts +5 -0
  48. package/lib/specs/is-nothing-content-model.js +14 -0
  49. package/lib/specs/is-palpable-elements.d.ts +8 -4
  50. package/lib/specs/is-palpable-elements.js +6 -4
  51. package/lib/specs/is-void-element.js +3 -1
  52. package/lib/specs/resolve-version.d.ts +5 -1
  53. package/lib/specs/resolve-version.js +1 -1
  54. package/lib/specs/schema-to-spec.d.ts +4 -4
  55. package/lib/specs/schema-to-spec.js +29 -18
  56. package/lib/types/aria.d.ts +149 -114
  57. package/lib/types/attributes.d.ts +1490 -111
  58. package/lib/types/index.d.ts +178 -149
  59. package/lib/types/permitted-structures.d.ts +74 -31
  60. package/lib/utils/merge-array.d.ts +9 -4
  61. package/lib/utils/resolve-namespace.d.ts +4 -4
  62. package/lib/utils/resolve-namespace.js +3 -2
  63. package/lib/utils/validateAriaVersion.d.ts +2 -0
  64. package/lib/utils/validateAriaVersion.js +8 -0
  65. package/package.json +13 -8
  66. package/{src/dom-traverse/accname-computation.spec.ts → test/dom-traverse/accname-computation.spec.js} +4 -4
  67. package/test/dom-traverse/get-computed-aria-props.spec.js +758 -0
  68. package/test/dom-traverse/get-computed-role.spec.js +294 -0
  69. package/test/dom-traverse/get-implicit-role.spec.js +40 -0
  70. package/{src/dom-traverse/has-required-owned-elements.spec.ts → test/dom-traverse/has-required-owned-elements.spec.js} +7 -9
  71. package/{src/dom-traverse/is-exposed.spec.ts → test/dom-traverse/is-exposed.spec.js} +29 -9
  72. package/test/dom-traverse/matches-context-role.spec.js +60 -0
  73. package/{src/specs/get-attr-specs.spec.ts → test/specs/get-attr-specs.spec.js} +2 -8
  74. package/{src/specs/get-implicit-role.spec.ts → test/specs/get-implicit-role.spec.js} +9 -11
  75. package/{src/specs/get-permitted-roles.spec.ts → test/specs/get-permitted-roles.spec.js} +8 -10
  76. package/{src/specs/get-role-spec.spec.ts → test/specs/get-role-spec.spec.js} +4 -4
  77. package/{src/specs/get-spec-by-tag-name.spec.ts → test/specs/get-spec-by-tag-name.spec.js} +2 -2
  78. package/test/specs/resolve-version.spec.js +34 -0
  79. package/test/specs/schema-to-spec.spec.js +61 -0
  80. package/{src/utils/resolve-namespace.spec.ts → test/utils/resolve-namespace.spec.js} +1 -1
  81. package/lib/types/permitted-structres.d.ts +0 -58
  82. package/lib/types/permitted-structres.js +0 -8
  83. package/schemas/aria.schema.json +0 -258
  84. package/schemas/attributes.schema.json +0 -204
  85. package/schemas/content-models.schema.json +0 -199
  86. package/schemas/element.schema.json +0 -11
  87. package/schemas/global-attributes.schema.json +0 -761
  88. package/src/dom-traverse/accname-computation.ts +0 -5
  89. package/src/dom-traverse/get-attr-specs.ts +0 -8
  90. package/src/dom-traverse/get-computed-aria-props.spec.ts +0 -368
  91. package/src/dom-traverse/get-computed-aria-props.ts +0 -130
  92. package/src/dom-traverse/get-computed-role.spec.ts +0 -134
  93. package/src/dom-traverse/get-computed-role.ts +0 -181
  94. package/src/dom-traverse/get-content-model.ts +0 -32
  95. package/src/dom-traverse/get-explicit-role.ts +0 -112
  96. package/src/dom-traverse/get-implicit-role.ts +0 -36
  97. package/src/dom-traverse/get-non-presentational-ancestor.ts +0 -20
  98. package/src/dom-traverse/get-permitted-roles.ts +0 -7
  99. package/src/dom-traverse/get-spec.ts +0 -7
  100. package/src/dom-traverse/has-required-owned-elements.ts +0 -85
  101. package/src/dom-traverse/is-exposed.ts +0 -196
  102. package/src/dom-traverse/may-be-focusable.ts +0 -21
  103. package/src/index.ts +0 -23
  104. package/src/specs/aria-specs.ts +0 -6
  105. package/src/specs/content-model-category-to-tag-names.ts +0 -15
  106. package/src/specs/get-aria.ts +0 -85
  107. package/src/specs/get-attr-specs.ts +0 -116
  108. package/src/specs/get-implicit-role.ts +0 -17
  109. package/src/specs/get-permitted-roles.ts +0 -87
  110. package/src/specs/get-role-spec.ts +0 -72
  111. package/src/specs/get-selectors-by-content-model-category.ts +0 -10
  112. package/src/specs/get-spec-by-tag-name.ts +0 -20
  113. package/src/specs/is-palpable-elements.ts +0 -55
  114. package/src/specs/is-presentational.ts +0 -6
  115. package/src/specs/is-void-element.ts +0 -22
  116. package/src/specs/resolve-version.ts +0 -20
  117. package/src/specs/schema-to-spec.spec.ts +0 -39
  118. package/src/specs/schema-to-spec.ts +0 -103
  119. package/src/types/aria.ts +0 -153
  120. package/src/types/attributes.ts +0 -1473
  121. package/src/types/index.ts +0 -255
  122. package/src/types/permitted-structres.ts +0 -100
  123. package/src/types/permitted-structures.ts +0 -96
  124. package/src/utils/get-ns.ts +0 -18
  125. package/src/utils/merge-array.ts +0 -35
  126. package/src/utils/resolve-namespace.ts +0 -40
  127. package/tsconfig.test.json +0 -3
  128. package/tsconfig.tsbuildinfo +0 -1
@@ -1,5 +0,0 @@
1
- import { computeAccessibleName } from 'dom-accessibility-api';
2
-
3
- export function getAccname(el: Element) {
4
- return computeAccessibleName(el);
5
- }
@@ -1,8 +0,0 @@
1
- import type { MLMLSpec } from '../types';
2
- import type { NamespaceURI } from '@markuplint/ml-ast';
3
-
4
- import { getAttrSpecs as _getAttrSpecs } from '../specs/get-attr-specs';
5
-
6
- export function getAttrSpecs(el: Element, schema: MLMLSpec) {
7
- return _getAttrSpecs(el.localName, el.namespaceURI as NamespaceURI, schema);
8
- }
@@ -1,368 +0,0 @@
1
- import type { ARIAVersion } from '../types';
2
-
3
- import specs from '@markuplint/html-spec';
4
- import { createSelector } from '@markuplint/selector';
5
- import { createJSDOMElement } from '@markuplint/test-tools';
6
-
7
- import { getComputedAriaProps } from './get-computed-aria-props';
8
-
9
- function _(html: string, selector?: string) {
10
- return createJSDOMElement(html, selector, function (selector) {
11
- // JSDOM supports no level 4 selectors yet.
12
- return !!createSelector(selector, specs).match(this);
13
- });
14
- }
15
-
16
- function c(html: string, version: ARIAVersion, selector?: string) {
17
- return getComputedAriaProps(specs, _(html, selector), version);
18
- }
19
-
20
- describe('getComputedAriaProps', () => {
21
- test('the link role', () => {
22
- expect(c('<a href></a>', '1.2')).toStrictEqual({
23
- 'aria-atomic': {
24
- name: 'aria-atomic',
25
- value: 'false',
26
- deprecated: false,
27
- required: false,
28
- from: 'default',
29
- },
30
- 'aria-busy': {
31
- name: 'aria-busy',
32
- value: 'false',
33
- deprecated: false,
34
- required: false,
35
- from: 'default',
36
- },
37
- 'aria-controls': {
38
- name: 'aria-controls',
39
- value: undefined,
40
- deprecated: false,
41
- required: false,
42
- from: 'default',
43
- },
44
- 'aria-current': {
45
- name: 'aria-current',
46
- value: 'false',
47
- deprecated: false,
48
- required: false,
49
- from: 'default',
50
- },
51
- 'aria-describedby': {
52
- name: 'aria-describedby',
53
- value: undefined,
54
- deprecated: false,
55
- required: false,
56
- from: 'default',
57
- },
58
- 'aria-details': {
59
- name: 'aria-details',
60
- value: undefined,
61
- deprecated: false,
62
- required: false,
63
- from: 'default',
64
- },
65
- 'aria-disabled': {
66
- name: 'aria-disabled',
67
- value: 'false',
68
- deprecated: false,
69
- required: false,
70
- from: 'default',
71
- },
72
- 'aria-dropeffect': {
73
- name: 'aria-dropeffect',
74
- value: 'none',
75
- deprecated: true,
76
- required: false,
77
- from: 'default',
78
- },
79
- 'aria-errormessage': {
80
- name: 'aria-errormessage',
81
- value: undefined,
82
- deprecated: false,
83
- required: false,
84
- from: 'default',
85
- },
86
- 'aria-expanded': {
87
- name: 'aria-expanded',
88
- value: 'undefined',
89
- deprecated: false,
90
- required: false,
91
- from: 'default',
92
- },
93
- 'aria-flowto': {
94
- name: 'aria-flowto',
95
- value: undefined,
96
- deprecated: false,
97
- required: false,
98
- from: 'default',
99
- },
100
- 'aria-grabbed': {
101
- name: 'aria-grabbed',
102
- value: 'undefined',
103
- deprecated: true,
104
- required: false,
105
- from: 'default',
106
- },
107
- 'aria-haspopup': {
108
- name: 'aria-haspopup',
109
- value: 'false',
110
- deprecated: false,
111
- required: false,
112
- from: 'default',
113
- },
114
- 'aria-hidden': {
115
- name: 'aria-hidden',
116
- value: 'undefined',
117
- deprecated: false,
118
- required: false,
119
- from: 'default',
120
- },
121
- 'aria-invalid': {
122
- name: 'aria-invalid',
123
- value: 'false',
124
- deprecated: false,
125
- required: false,
126
- from: 'default',
127
- },
128
- 'aria-keyshortcuts': {
129
- name: 'aria-keyshortcuts',
130
- value: undefined,
131
- deprecated: false,
132
- required: false,
133
- from: 'default',
134
- },
135
- 'aria-label': {
136
- name: 'aria-label',
137
- value: undefined,
138
- deprecated: false,
139
- required: false,
140
- from: 'default',
141
- },
142
- 'aria-labelledby': {
143
- name: 'aria-labelledby',
144
- value: undefined,
145
- deprecated: false,
146
- required: false,
147
- from: 'default',
148
- },
149
- 'aria-live': {
150
- name: 'aria-live',
151
- value: 'off',
152
- deprecated: false,
153
- required: false,
154
- from: 'default',
155
- },
156
- 'aria-owns': {
157
- name: 'aria-owns',
158
- value: undefined,
159
- deprecated: false,
160
- required: false,
161
- from: 'default',
162
- },
163
- 'aria-relevant': {
164
- name: 'aria-relevant',
165
- value: 'additions text',
166
- deprecated: false,
167
- required: false,
168
- from: 'default',
169
- },
170
- 'aria-roledescription': {
171
- name: 'aria-roledescription',
172
- value: undefined,
173
- deprecated: false,
174
- required: false,
175
- from: 'default',
176
- },
177
- });
178
- });
179
-
180
- test('the heading role', () => {
181
- expect(c('<h1></h1>', '1.2')).toStrictEqual({
182
- 'aria-atomic': {
183
- name: 'aria-atomic',
184
- value: 'false',
185
- deprecated: false,
186
- required: false,
187
- from: 'default',
188
- },
189
- 'aria-busy': {
190
- name: 'aria-busy',
191
- value: 'false',
192
- deprecated: false,
193
- required: false,
194
- from: 'default',
195
- },
196
- 'aria-controls': {
197
- name: 'aria-controls',
198
- value: undefined,
199
- deprecated: false,
200
- required: false,
201
- from: 'default',
202
- },
203
- 'aria-current': {
204
- name: 'aria-current',
205
- value: 'false',
206
- deprecated: false,
207
- required: false,
208
- from: 'default',
209
- },
210
- 'aria-describedby': {
211
- name: 'aria-describedby',
212
- value: undefined,
213
- deprecated: false,
214
- required: false,
215
- from: 'default',
216
- },
217
- 'aria-details': {
218
- name: 'aria-details',
219
- value: undefined,
220
- deprecated: false,
221
- required: false,
222
- from: 'default',
223
- },
224
- 'aria-disabled': {
225
- name: 'aria-disabled',
226
- value: 'false',
227
- deprecated: false,
228
- required: false,
229
- from: 'default',
230
- },
231
- 'aria-dropeffect': {
232
- name: 'aria-dropeffect',
233
- value: 'none',
234
- deprecated: true,
235
- required: false,
236
- from: 'default',
237
- },
238
- 'aria-errormessage': {
239
- name: 'aria-errormessage',
240
- value: undefined,
241
- deprecated: false,
242
- required: false,
243
- from: 'default',
244
- },
245
- 'aria-flowto': {
246
- name: 'aria-flowto',
247
- value: undefined,
248
- deprecated: false,
249
- required: false,
250
- from: 'default',
251
- },
252
- 'aria-grabbed': {
253
- name: 'aria-grabbed',
254
- value: 'undefined',
255
- deprecated: true,
256
- required: false,
257
- from: 'default',
258
- },
259
- 'aria-haspopup': {
260
- name: 'aria-haspopup',
261
- value: 'false',
262
- deprecated: false,
263
- required: false,
264
- from: 'default',
265
- },
266
- 'aria-hidden': {
267
- name: 'aria-hidden',
268
- value: 'undefined',
269
- deprecated: false,
270
- required: false,
271
- from: 'default',
272
- },
273
- 'aria-invalid': {
274
- name: 'aria-invalid',
275
- value: 'false',
276
- deprecated: false,
277
- required: false,
278
- from: 'default',
279
- },
280
- 'aria-keyshortcuts': {
281
- name: 'aria-keyshortcuts',
282
- value: undefined,
283
- deprecated: false,
284
- required: false,
285
- from: 'default',
286
- },
287
- 'aria-label': {
288
- name: 'aria-label',
289
- value: undefined,
290
- deprecated: false,
291
- required: false,
292
- from: 'default',
293
- },
294
- 'aria-labelledby': {
295
- name: 'aria-labelledby',
296
- value: undefined,
297
- deprecated: false,
298
- required: false,
299
- from: 'default',
300
- },
301
- 'aria-level': {
302
- name: 'aria-level',
303
- value: '1',
304
- deprecated: false,
305
- required: true,
306
- from: 'default',
307
- },
308
- 'aria-live': {
309
- name: 'aria-live',
310
- value: 'off',
311
- deprecated: false,
312
- required: false,
313
- from: 'default',
314
- },
315
- 'aria-owns': {
316
- name: 'aria-owns',
317
- value: undefined,
318
- deprecated: false,
319
- required: false,
320
- from: 'default',
321
- },
322
- 'aria-relevant': {
323
- name: 'aria-relevant',
324
- value: 'additions text',
325
- deprecated: false,
326
- required: false,
327
- from: 'default',
328
- },
329
- 'aria-roledescription': {
330
- name: 'aria-roledescription',
331
- value: undefined,
332
- deprecated: false,
333
- required: false,
334
- from: 'default',
335
- },
336
- });
337
- });
338
-
339
- test('from html-attr', () => {
340
- expect(c('<input type="checkbox" checked>', '1.2')['aria-checked']).toStrictEqual({
341
- name: 'aria-checked',
342
- value: 'true',
343
- deprecated: false,
344
- required: true,
345
- from: 'html-attr',
346
- });
347
- });
348
-
349
- test('overwrite aria-attr', () => {
350
- expect(c('<input type="checkbox" checked aria-checked="false">', '1.2')['aria-checked']).toStrictEqual({
351
- name: 'aria-checked',
352
- value: 'false',
353
- deprecated: false,
354
- required: true,
355
- from: 'aria-attr',
356
- });
357
- });
358
-
359
- test('overwrite aria-attr', () => {
360
- expect(c('<input type="checkbox" checked aria-checked="mixed">', '1.2')['aria-checked']).toStrictEqual({
361
- name: 'aria-checked',
362
- value: 'mixed',
363
- deprecated: false,
364
- required: true,
365
- from: 'aria-attr',
366
- });
367
- });
368
- });
@@ -1,130 +0,0 @@
1
- import type { ARIAAttributeValue, ARIAProperty, ARIAVersion, MLMLSpec } from '../types';
2
-
3
- import { ariaSpecs as _ariaSpecs } from '../specs/aria-specs';
4
-
5
- import { getComputedRole } from './get-computed-role';
6
-
7
- type ARIAProps = Record<string, ARIAProp>;
8
-
9
- type ARIAProp = {
10
- name: string;
11
- value: string | undefined;
12
- required: boolean;
13
- deprecated: boolean;
14
- from: ARIAPropReferenceType;
15
- };
16
-
17
- type ARIAPropReferenceType = 'default' | 'html-attr' | 'aria-attr';
18
-
19
- export function getComputedAriaProps(specs: Readonly<MLMLSpec>, el: Element, version: ARIAVersion): ARIAProps {
20
- const ariaSpecs = _ariaSpecs(specs, version);
21
-
22
- const { role } = getComputedRole(specs, el, version);
23
-
24
- if (!role) {
25
- return {};
26
- }
27
-
28
- const props: ARIAProps = {};
29
-
30
- role.ownedProperties.forEach(ownedProp => {
31
- const spec = ariaSpecs.props.find(propSpec => propSpec.name === ownedProp.name);
32
- if (!spec) {
33
- return;
34
- }
35
-
36
- if (el.hasAttribute(spec.name)) {
37
- const attrValue = el.getAttribute(spec.name);
38
- if (attrValue && isValidAriaValue(spec, role.name, attrValue, spec.enum)) {
39
- props[ownedProp.name] = {
40
- name: ownedProp.name,
41
- value: attrValue,
42
- deprecated: !!spec.deprecated,
43
- required: !!ownedProp.required,
44
- from: 'aria-attr',
45
- };
46
- return;
47
- }
48
- }
49
-
50
- const equivalentHtmlAttr = spec.equivalentHtmlAttrs?.[0];
51
- if (equivalentHtmlAttr && el.hasAttribute(equivalentHtmlAttr.htmlAttrName)) {
52
- const value =
53
- equivalentHtmlAttr.value === null
54
- ? el.getAttribute(equivalentHtmlAttr.htmlAttrName)
55
- : equivalentHtmlAttr.value;
56
- if (value != null) {
57
- props[ownedProp.name] = {
58
- name: ownedProp.name,
59
- value,
60
- deprecated: !!spec.deprecated,
61
- required: !!ownedProp.required,
62
- from: 'html-attr',
63
- };
64
- return;
65
- }
66
- }
67
-
68
- let defaultValue = spec.defaultValue;
69
-
70
- /**
71
- * @see https://www.w3.org/TR/html-aria/#el-h1-h6
72
- */
73
- if (ownedProp.name === 'aria-level' && /^H[1-6]$/.test(el.nodeName)) {
74
- defaultValue = el.nodeName.replace('H', '');
75
- }
76
-
77
- props[ownedProp.name] = {
78
- name: ownedProp.name,
79
- value: defaultValue,
80
- deprecated: !!spec.deprecated,
81
- required: !!ownedProp.required,
82
- from: 'default',
83
- };
84
- });
85
-
86
- return props;
87
- }
88
-
89
- function isValidAriaValue(spec: ARIAProperty, role: string, value: string | undefined, enumList: string[]) {
90
- let type: ARIAAttributeValue = spec.value;
91
- if (spec.conditionalValue) {
92
- for (const cond of spec.conditionalValue) {
93
- if (cond.role.includes(role)) {
94
- type = cond.value;
95
- }
96
- }
97
- }
98
-
99
- value = (value ?? '').trim();
100
- switch (type) {
101
- case 'string': {
102
- return true;
103
- }
104
- case 'ID reference':
105
- case 'ID reference list':
106
- case 'URI': {
107
- return !!value;
108
- }
109
- case 'integer':
110
- case 'number': {
111
- return !isNaN(parseFloat(value));
112
- }
113
- case 'token':
114
- case 'token list': {
115
- if (!enumList.length) {
116
- throw new Error('Need an enum list in token and token list types');
117
- }
118
- return enumList.includes(value.toLowerCase());
119
- }
120
- case 'tristate': {
121
- return ['true', 'false', 'mixed'].includes(value.toLowerCase());
122
- }
123
- case 'true/false': {
124
- return ['true', 'false'].includes(value.toLowerCase());
125
- }
126
- case 'true/false/undefined': {
127
- return ['true', 'false', 'undefined'].includes(value.toLowerCase());
128
- }
129
- }
130
- }
@@ -1,134 +0,0 @@
1
- import type { ARIAVersion } from '../types';
2
-
3
- import specs from '@markuplint/html-spec';
4
- import { createSelector } from '@markuplint/selector';
5
- import { createJSDOMElement } from '@markuplint/test-tools';
6
-
7
- import { getComputedRole } from './get-computed-role';
8
-
9
- function _(html: string, selector?: string) {
10
- return createJSDOMElement(html, selector, function (selector) {
11
- // JSDOM supports no level 4 selectors yet.
12
- return !!createSelector(selector, specs).match(this);
13
- });
14
- }
15
-
16
- function c(html: string, version: ARIAVersion, selector?: string) {
17
- return getComputedRole(specs, _(html, selector), version);
18
- }
19
-
20
- function tree(html: string, version: ARIAVersion) {
21
- const el = _(html);
22
- const tree: [string, string | null][] = [];
23
- let current: Element | null = el;
24
- while (current) {
25
- tree.push([current.localName, getComputedRole(specs, current, version).role?.name || null]);
26
- current = current.children.item(0);
27
- }
28
- return tree;
29
- }
30
-
31
- describe('getComputedRole', () => {
32
- test('the a element', () => {
33
- expect(c('<a></a>', '1.2').role?.name).toBe(undefined);
34
- expect(c('<a href></a>', '1.2').role?.name).toBe('link');
35
- expect(c('<a role="button"></a>', '1.2').role?.name).toBe('button');
36
- expect(c('<a role="button" href></a>', '1.2').role?.name).toBe('button');
37
- expect(c('<a role="foo" href></a>', '1.2').role?.name).toBe('link');
38
- });
39
-
40
- test('the heading role', () => {
41
- expect(c('<h1></h1>', '1.2').role?.name).toBe('heading');
42
- expect(c('<h1></h1>', '1.2').role?.isImplicit).toBe(true);
43
- expect(c('<div role="heading"></div>', '1.2').role?.name).toBe('heading');
44
- expect(c('<div role="heading"></div>', '1.2').role?.isImplicit).toBe(false);
45
- });
46
-
47
- test('multiple', () => {
48
- expect(c('<div role="presentation heading"></div>', '1.2').role?.name).toBe('presentation');
49
- expect(c('<div role="roletype heading"></div>', '1.2').role?.name).toBe('heading');
50
- expect(c('<img alt="alt" role="banner button"/>', '1.2').role?.name).toBe('button');
51
- expect(c('<img alt="alt" role="foo button"/>', '1.2').role?.name).toBe('button');
52
- expect(c('<img alt="alt" role="graphics-symbol button"/>', '1.2').role?.name).toBe('button');
53
- });
54
-
55
- test('svg', () => {
56
- expect(c('<svg><rect role="graphics-symbol img"></rect></svg>', '1.2', 'rect').role?.name).toBe(
57
- 'graphics-symbol',
58
- );
59
- expect(c('<svg><rect role="roletype img"></rect></svg>', '1.2', 'rect').role?.name).toBe('img');
60
- expect(c('<svg><rect role="roletype"></rect></svg>', '1.2', 'rect').role?.name).toBe('graphics-symbol');
61
- });
62
-
63
- test('landmark', () => {
64
- expect(c('<div role="region"></div>', '1.2').role?.name).toBe('generic');
65
- expect(c('<div role="region" aria-label="foo"></div>', '1.2').role?.name).toBe('region');
66
- expect(c('<div role="form"></div>', '1.2').role?.name).toBe('generic');
67
- expect(c('<div role="form" aria-label="foo"></div>', '1.2').role?.name).toBe('form');
68
- expect(c('<div role="navigation"></div>', '1.2').role?.name).toBe('navigation');
69
- expect(c('<div role="navigation" aria-label="foo"></div>', '1.2').role?.name).toBe('navigation');
70
- });
71
-
72
- test('Presentational Roles Conflict Resolution (1) Interactive Elements', () => {
73
- expect(c('<a href="path/to"></a>', '1.2').role?.name).toBe('link');
74
- expect(c('<a role="none" href="path/to"></a>', '1.2').role?.name).toBe('link'); // No permitted role
75
- expect(c('<a></a>', '1.2').role?.name).toBe(undefined);
76
- expect(c('<a role="none"></a>', '1.2').role?.name).toBe('none');
77
- expect(c('<a role="none" href="path/to" disabled></a>', '1.2').role?.name).toBe('link'); // No permitted role
78
- expect(c('<button></button>', '1.2').role?.name).toBe('button');
79
- expect(c('<button disabled></button>', '1.2').role?.name).toBe('button');
80
- expect(c('<button role="none"></button>', '1.2').role?.name).toBe('button'); // No permitted role
81
- expect(c('<button role="none" disabled></button>', '1.2').role?.name).toBe('button'); // No permitted role
82
- expect(c('<div></div>', '1.2').role?.name).toBe('generic');
83
- expect(c('<div role="none"></div>', '1.2').role?.name).toBe('none');
84
- expect(c('<div tabindex="0"></div>', '1.2').role?.name).toBe('generic');
85
- expect(c('<div tabindex="0" role="none"></div>', '1.2').role?.name).toBe('generic');
86
- expect(c('<div><span></span></div>', '1.2', 'span').role?.name).toBe(undefined);
87
- expect(c('<div><span role="none"></span></div>', '1.2', 'span').role?.name).toBe('none');
88
- expect(c('<div><span tabindex="0"></span></div>', '1.2', 'span').role?.name).toBe(undefined);
89
- expect(c('<div><span tabindex="0" role="none"></span></div>', '1.2', 'span').role?.name).toBe(undefined);
90
- expect(c('<div hidden><span></span></div>', '1.2', 'span').role?.name).toBe(undefined);
91
- expect(c('<div hidden><span role="none"></span></div>', '1.2', 'span').role?.name).toBe('none');
92
- expect(c('<div hidden><span tabindex="0"></span></div>', '1.2', 'span').role?.name).toBe(undefined);
93
- expect(c('<div hidden><span tabindex="0" role="none"></span></div>', '1.2', 'span').role?.name).toBe('none');
94
- });
95
-
96
- test('Presentational Roles Conflict Resolution (2-1) Required Owned Elements', () => {
97
- expect(c('<table><tr><td>foo</td></tr></table>', '1.2', 'td').role?.name).toBe('cell');
98
- expect(c('<table><tr><td role="none">foo</td></tr></table>', '1.2', 'td').role?.name).toBe('cell');
99
- expect(c('<table><tbody role="none"><tr><td>foo</td></tr></tbody></table>', '1.2', 'tbody').role?.name).toBe(
100
- 'rowgroup',
101
- );
102
- expect(c('<ul><li></li></ul>', '1.2', 'li').role?.name).toBe('listitem');
103
- expect(c('<ul><li role="presentation"></li></ul>', '1.2', 'li').role?.name).toBe('listitem');
104
- });
105
-
106
- test('Presentational Roles Conflict Resolution (2-2) the accessibility tree to be malformed', () => {
107
- expect(c('<table><tr><td>foo</td></tr></table>', '1.2', 'td').role?.name).toBe('cell');
108
- expect(c('<table role="none"><tr><td>foo</td></tr></table>', '1.2', 'td').role?.name).toBe(undefined);
109
- expect(tree('<table><tr><td>foo</td></tr></table>', '1.2')).toStrictEqual([
110
- ['table', 'table'],
111
- ['tbody', 'rowgroup'],
112
- ['tr', 'row'],
113
- ['td', 'cell'],
114
- ]);
115
- expect(tree('<table role="none"><tr><td>foo</td></tr></table>', '1.2')).toStrictEqual([
116
- ['table', 'none'],
117
- ['tbody', null],
118
- ['tr', null],
119
- ['td', null],
120
- ]);
121
- });
122
-
123
- test('Presentational Roles Conflict Resolution (3) Global Props', () => {
124
- /**
125
- * @see https://w3c.github.io/aria/#example-41
126
- */
127
- expect(c('<h1 role="presentation" aria-describedby="comment-1"> Sample Content </h1>', '1.2').role?.name).toBe(
128
- 'heading',
129
- );
130
- expect(c('<h1 role="presentation" aria-level="2"> Sample Content </h1>', '1.2').role?.name).toBe(
131
- 'presentation',
132
- );
133
- });
134
- });