@markuplint/ml-spec 3.0.0-dev.24 → 3.0.0-dev.38

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 (58) hide show
  1. package/lib/dom-traverse/is-exposed.js +8 -0
  2. package/package.json +4 -4
  3. package/schemas/aria.schema.json +0 -258
  4. package/schemas/attributes.schema.json +0 -204
  5. package/schemas/content-models.schema.json +0 -199
  6. package/schemas/element.schema.json +0 -11
  7. package/schemas/global-attributes.schema.json +0 -761
  8. package/src/dom-traverse/accname-computation.spec.ts +0 -69
  9. package/src/dom-traverse/accname-computation.ts +0 -5
  10. package/src/dom-traverse/get-attr-specs.ts +0 -8
  11. package/src/dom-traverse/get-computed-aria-props.spec.ts +0 -368
  12. package/src/dom-traverse/get-computed-aria-props.ts +0 -130
  13. package/src/dom-traverse/get-computed-role.spec.ts +0 -134
  14. package/src/dom-traverse/get-computed-role.ts +0 -181
  15. package/src/dom-traverse/get-content-model.ts +0 -32
  16. package/src/dom-traverse/get-explicit-role.ts +0 -112
  17. package/src/dom-traverse/get-implicit-role.ts +0 -36
  18. package/src/dom-traverse/get-non-presentational-ancestor.ts +0 -20
  19. package/src/dom-traverse/get-permitted-roles.ts +0 -7
  20. package/src/dom-traverse/get-spec.ts +0 -7
  21. package/src/dom-traverse/has-required-owned-elements.spec.ts +0 -36
  22. package/src/dom-traverse/has-required-owned-elements.ts +0 -85
  23. package/src/dom-traverse/is-exposed.spec.ts +0 -50
  24. package/src/dom-traverse/is-exposed.ts +0 -196
  25. package/src/dom-traverse/may-be-focusable.ts +0 -21
  26. package/src/index.ts +0 -24
  27. package/src/specs/aria-specs.ts +0 -6
  28. package/src/specs/content-model-category-to-tag-names.ts +0 -15
  29. package/src/specs/get-aria.ts +0 -85
  30. package/src/specs/get-attr-specs.spec.ts +0 -47
  31. package/src/specs/get-attr-specs.ts +0 -116
  32. package/src/specs/get-implicit-role.spec.ts +0 -81
  33. package/src/specs/get-implicit-role.ts +0 -17
  34. package/src/specs/get-permitted-roles.spec.ts +0 -420
  35. package/src/specs/get-permitted-roles.ts +0 -87
  36. package/src/specs/get-role-spec.spec.ts +0 -67
  37. package/src/specs/get-role-spec.ts +0 -72
  38. package/src/specs/get-selectors-by-content-model-category.ts +0 -10
  39. package/src/specs/get-spec-by-tag-name.spec.ts +0 -68
  40. package/src/specs/get-spec-by-tag-name.ts +0 -20
  41. package/src/specs/is-nothing-content-model.ts +0 -9
  42. package/src/specs/is-palpable-elements.ts +0 -55
  43. package/src/specs/is-presentational.ts +0 -6
  44. package/src/specs/is-void-element.ts +0 -22
  45. package/src/specs/resolve-version.ts +0 -20
  46. package/src/specs/schema-to-spec.spec.ts +0 -39
  47. package/src/specs/schema-to-spec.ts +0 -103
  48. package/src/types/aria.ts +0 -153
  49. package/src/types/attributes.ts +0 -1524
  50. package/src/types/index.ts +0 -255
  51. package/src/types/permitted-structres.ts +0 -100
  52. package/src/types/permitted-structures.ts +0 -96
  53. package/src/utils/get-ns.ts +0 -18
  54. package/src/utils/merge-array.ts +0 -35
  55. package/src/utils/resolve-namespace.spec.ts +0 -37
  56. package/src/utils/resolve-namespace.ts +0 -40
  57. package/tsconfig.test.json +0 -3
  58. package/tsconfig.tsbuildinfo +0 -1
@@ -1,69 +0,0 @@
1
- import { createJSDOMElement as c } from '@markuplint/test-tools';
2
-
3
- import { getAccname } from './accname-computation';
4
-
5
- test('Get accessible name', () => {
6
- expect(getAccname(c('<button>label</button>'))).toBe('label');
7
- expect(getAccname(c('<div>text</div>'))).toBe('');
8
- expect(getAccname(c('<div aria-label="label">text</div>'))).toBe('label');
9
- expect(getAccname(c('<span aria-label="label">text</span>'))).toBe('label');
10
- expect(getAccname(c('<img alt="alternative-text" />'))).toBe('alternative-text');
11
- expect(getAccname(c('<img title="title" />'))).toBe('title');
12
- expect(getAccname(c('<div><label for="a">label</label><input id="a" /></div>').children[1])).toBe('label');
13
- });
14
-
15
- test('Invisible element', () => {
16
- expect(getAccname(c('<button>label</button>'))).toBe('label');
17
- expect(getAccname(c('<button aria-disabled="true">label</button>'))).toBe('label');
18
- expect(getAccname(c('<button aria-hidden="true">label</button>'))).toBe('');
19
- expect(getAccname(c('<button hidden>label</button>'))).toBe('');
20
- expect(getAccname(c('<button style="display: none">label</button>'))).toBe('');
21
- });
22
-
23
- /**
24
- * @see https://www.w3.org/TR/accname-1.1/#ex-1-example-1-element1-id-el1-aria-labelledby-el3-element2-id-el2-aria-labelledby-el1-element3-id-el3-hello-element3
25
- */
26
- test('accname-1.1 Example 1', () => {
27
- const complex = c(`<div>
28
- <input id="el1" aria-labelledby="el3" />
29
- <input id="el2" aria-labelledby="el1" />
30
- <h1 id="el3"> hello </h1>
31
- </div>`);
32
- expect(getAccname(complex.children[0])).toBe('hello');
33
- expect(getAccname(complex.children[1])).toBe('');
34
- expect(getAccname(complex.children[2])).toBe('hello');
35
- });
36
-
37
- /**
38
- * @see https://www.w3.org/TR/accname-1.1/#ex-2-example-2-h1-files-h1-ul-li-a-id-file_row1-href-files-documentation-pdf-documentation-pdf-a-span-role-button-tabindex-0-id-del_row1-aria-label-delete-aria-labelledby-del_row1-file_row1-span-li-li-a-id-file_row2-href-files-holidayletter-pdf-holidayletter-pdf-a-span-role-button-tabindex-0-id-del_row2-aria-label-delete-aria-labelledby-del_row2-file_row2-span-li-ul
39
- */
40
- test('accname-1.1 Example 2', () => {
41
- const complex = c(`<ul>
42
- <li>
43
- <a id="file_row1" href="./files/Documentation.pdf">Documentation.pdf</a>
44
- <span role="button" tabindex="0" id="del_row1" aria-label="Delete" aria-labelledby="del_row1 file_row1"></span>
45
- </li>
46
- <li>
47
- <a id="file_row2" href="./files/HolidayLetter.pdf">HolidayLetter.pdf</a>
48
- <span role="button" tabindex="0" id="del_row2" aria-label="Delete" aria-labelledby="del_row2 file_row2"></span>
49
- </li>
50
- </ul>`);
51
- const spans = complex.querySelectorAll('span');
52
- expect(getAccname(spans[0])).toBe('Delete Documentation.pdf');
53
- expect(getAccname(spans[1])).toBe('Delete HolidayLetter.pdf');
54
- });
55
-
56
- /**
57
- * @see https://www.w3.org/TR/accname-1.1/#ex-3-example-3-div-role-checkbox-aria-checked-false-flash-the-screen-span-role-textbox-aria-multiline-false-5-span-times-div
58
- */
59
- test('accname-1.1 Example 3', () => {
60
- const complex = c(
61
- '<div role="checkbox" aria-checked="false">Flash the screen <span role="textbox" aria-multiline="false"> 5 </span> times</div>',
62
- );
63
- expect(getAccname(complex)).toBe('Flash the screen 5 times');
64
- });
65
-
66
- test('with comment', () => {
67
- expect(getAccname(c('<button>label</button>'))).toBe('label');
68
- expect(getAccname(c('<button>label<!-- comment --></button>'))).toBe('label');
69
- });
@@ -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
- }