source: frontend/node_modules/eslint-plugin-jsx-a11y/__mocks__/genInteractives.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.6 KB
Line 
1/**
2 * @flow
3 */
4
5import { dom, roles } from 'aria-query';
6import includes from 'array-includes';
7import fromEntries from 'object.fromentries';
8
9import JSXAttributeMock from './JSXAttributeMock';
10import JSXElementMock from './JSXElementMock';
11
12import type { JSXAttributeMockType } from './JSXAttributeMock';
13import type { JSXElementMockType } from './JSXElementMock';
14
15const domElements = dom.keys();
16const roleNames = roles.keys();
17
18const interactiveElementsMap = {
19 a: [{ prop: 'href', value: '#' }],
20 area: [{ prop: 'href', value: '#' }],
21 audio: [],
22 button: [],
23 canvas: [],
24 datalist: [],
25 embed: [],
26 input: [],
27 'input[type="button"]': [{ prop: 'type', value: 'button' }],
28 'input[type="checkbox"]': [{ prop: 'type', value: 'checkbox' }],
29 'input[type="color"]': [{ prop: 'type', value: 'color' }],
30 'input[type="date"]': [{ prop: 'type', value: 'date' }],
31 'input[type="datetime"]': [{ prop: 'type', value: 'datetime' }],
32 'input[type="email"]': [{ prop: 'type', value: 'email' }],
33 'input[type="file"]': [{ prop: 'type', value: 'file' }],
34 'input[type="image"]': [{ prop: 'type', value: 'image' }],
35 'input[type="month"]': [{ prop: 'type', value: 'month' }],
36 'input[type="number"]': [{ prop: 'type', value: 'number' }],
37 'input[type="password"]': [{ prop: 'type', value: 'password' }],
38 'input[type="radio"]': [{ prop: 'type', value: 'radio' }],
39 'input[type="range"]': [{ prop: 'type', value: 'range' }],
40 'input[type="reset"]': [{ prop: 'type', value: 'reset' }],
41 'input[type="search"]': [{ prop: 'type', value: 'search' }],
42 'input[type="submit"]': [{ prop: 'type', value: 'submit' }],
43 'input[type="tel"]': [{ prop: 'type', value: 'tel' }],
44 'input[type="text"]': [{ prop: 'type', value: 'text' }],
45 'input[type="time"]': [{ prop: 'type', value: 'time' }],
46 'input[type="url"]': [{ prop: 'type', value: 'url' }],
47 'input[type="week"]': [{ prop: 'type', value: 'week' }],
48 menuitem: [],
49 option: [],
50 select: [],
51 summary: [],
52 // Whereas ARIA makes a distinction between cell and gridcell, the AXObject
53 // treats them both as CellRole and since gridcell is interactive, we consider
54 // cell interactive as well.
55 td: [],
56 th: [],
57 tr: [],
58 textarea: [],
59 video: [],
60};
61
62const nonInteractiveElementsMap: {[string]: Array<{[string]: string}>} = {
63 abbr: [],
64 address: [],
65 article: [],
66 aside: [],
67 blockquote: [],
68 br: [],
69 caption: [],
70 code: [],
71 dd: [],
72 del: [],
73 details: [],
74 dfn: [],
75 dialog: [],
76 dir: [],
77 dl: [],
78 dt: [],
79 em: [],
80 fieldset: [],
81 figcaption: [],
82 figure: [],
83 footer: [],
84 form: [],
85 h1: [],
86 h2: [],
87 h3: [],
88 h4: [],
89 h5: [],
90 h6: [],
91 hr: [],
92 html: [],
93 iframe: [],
94 img: [],
95 ins: [],
96 label: [],
97 legend: [],
98 li: [],
99 main: [],
100 mark: [],
101 marquee: [],
102 menu: [],
103 meter: [],
104 nav: [],
105 ol: [],
106 optgroup: [],
107 output: [],
108 p: [],
109 pre: [],
110 progress: [],
111 ruby: [],
112 'section[aria-label]': [{ prop: 'aria-label' }],
113 'section[aria-labelledby]': [{ prop: 'aria-labelledby' }],
114 strong: [],
115 sub: [],
116 sup: [],
117 table: [],
118 tbody: [],
119 tfoot: [],
120 thead: [],
121 time: [],
122 ul: [],
123};
124
125const indeterminantInteractiveElementsMap: { [key: string]: Array<any> } = fromEntries(domElements.map((name) => [name, []]));
126
127Object.keys(interactiveElementsMap)
128 .concat(Object.keys(nonInteractiveElementsMap))
129 .forEach((name) => delete indeterminantInteractiveElementsMap[name]);
130
131const abstractRoles = roleNames.filter((role) => roles.get(role).abstract);
132
133const nonAbstractRoles = roleNames.filter((role) => !roles.get(role).abstract);
134
135const interactiveRoles = []
136 .concat(
137 roleNames,
138 // 'toolbar' does not descend from widget, but it does support
139 // aria-activedescendant, thus in practice we treat it as a widget.
140 'toolbar',
141 )
142 .filter((role) => (
143 !roles.get(role).abstract
144 && roles.get(role).superClass.some((klasses) => includes(klasses, 'widget'))
145 ));
146
147const nonInteractiveRoles = roleNames
148 .filter((role) => (
149 !roles.get(role).abstract
150 && !roles.get(role).superClass.some((klasses) => includes(klasses, 'widget'))
151
152 // 'toolbar' does not descend from widget, but it does support
153 // aria-activedescendant, thus in practice we treat it as a widget.
154 && !includes(['toolbar'], role)
155 ));
156
157export function genElementSymbol(openingElement: Object): string {
158 return (
159 openingElement.name.name + (openingElement.attributes.length > 0
160 ? `${openingElement.attributes.map((attr) => `[${attr.name.name}="${attr.value.value}"]`).join('')}`
161 : ''
162 )
163 );
164}
165
166export function genInteractiveElements(): Array<JSXElementMockType> {
167 return Object.keys(interactiveElementsMap).map((elementSymbol: string): JSXElementMockType => {
168 const bracketIndex = elementSymbol.indexOf('[');
169 let name = elementSymbol;
170 if (bracketIndex > -1) {
171 name = elementSymbol.slice(0, bracketIndex);
172 }
173 const attributes = interactiveElementsMap[elementSymbol].map(({ prop, value }) => JSXAttributeMock(prop, value));
174 return JSXElementMock(name, attributes);
175 });
176}
177
178export function genInteractiveRoleElements(): Array<JSXElementMockType> {
179 return interactiveRoles.concat('button article', 'fakerole button article').map((value): JSXElementMockType => JSXElementMock(
180 'div',
181 [JSXAttributeMock('role', value)],
182 ));
183}
184
185export function genNonInteractiveElements(): Array<JSXElementMockType> {
186 return Object.keys(nonInteractiveElementsMap).map((elementSymbol): JSXElementMockType => {
187 const bracketIndex = elementSymbol.indexOf('[');
188 let name = elementSymbol;
189 if (bracketIndex > -1) {
190 name = elementSymbol.slice(0, bracketIndex);
191 }
192 const attributes = nonInteractiveElementsMap[elementSymbol].map(({ prop, value }) => JSXAttributeMock(prop, value));
193 return JSXElementMock(name, attributes);
194 });
195}
196
197export function genNonInteractiveRoleElements(): Array<JSXElementMockType> {
198 return [
199 ...nonInteractiveRoles,
200 'article button',
201 'fakerole article button',
202 ].map((value) => JSXElementMock('div', [JSXAttributeMock('role', value)]));
203}
204
205export function genAbstractRoleElements(): Array<JSXElementMockType> {
206 return abstractRoles.map((value) => JSXElementMock('div', [JSXAttributeMock('role', value)]));
207}
208
209export function genNonAbstractRoleElements(): Array<JSXElementMockType> {
210 return nonAbstractRoles.map((value) => JSXElementMock('div', [JSXAttributeMock('role', value)]));
211}
212
213export function genIndeterminantInteractiveElements(): Array<JSXElementMockType> {
214 return Object.keys(indeterminantInteractiveElementsMap).map((name) => {
215 const attributes = indeterminantInteractiveElementsMap[name].map(({ prop, value }): JSXAttributeMockType => JSXAttributeMock(prop, value));
216 return JSXElementMock(name, attributes);
217 });
218}
Note: See TracBrowser for help on using the repository browser.