1
+ /**
2
+ * @flow
3
+ */
4
+
1
5
import {
2
6
dom ,
3
7
roles ,
@@ -6,63 +10,153 @@ import JSXAttributeMock from './JSXAttributeMock';
6
10
import JSXElementMock from './JSXElementMock' ;
7
11
8
12
const domElements = [ ...dom . keys ( ) ] ;
9
-
10
- const pureInteractiveElements = domElements
11
- . filter ( name => dom . get ( name ) . interactive === true )
12
- . reduce ( ( interactiveElements , name ) => {
13
- interactiveElements [ name ] = [ ] ;
14
- return interactiveElements ;
15
- } , { } ) ;
13
+ const roleNames = [ ...roles . keys ( ) ] ;
16
14
17
15
const interactiveElementsMap = {
18
- ...pureInteractiveElements ,
19
- a : [
20
- { prop : 'href' , value : '#' }
21
- ] ,
22
- area : [
23
- { prop : 'href' , value : '#' }
24
- ] ,
25
- input : [
26
- { prop : 'type' , value : 'text' }
27
- ] ,
16
+ a : [ { prop : 'href' , value : '#' } ] ,
17
+ area : [ { prop : 'href' , value : '#' } ] ,
18
+ button : [ ] ,
19
+ input : [ ] ,
20
+ 'input[type=\"button\"]' : [ { prop : 'type' , value : 'button' } ] ,
21
+ 'input[type=\"checkbox\"]' : [ { prop : 'type' , value : 'checkbox' } ] ,
22
+ 'input[type=\"color\"]' : [ { prop : 'type' , value : 'color' } ] ,
23
+ 'input[type=\"date\"]' : [ { prop : 'type' , value : 'date' } ] ,
24
+ 'input[type=\"datetime\"]' : [ { prop : 'type' , value : 'datetime' } ] ,
25
+ 'input[type=\"datetime\"]' : [ { prop : 'type' , value : 'datetime' } ] ,
26
+ 'input[type=\"email\"]' : [ { prop : 'type' , value : 'email' } ] ,
27
+ 'input[type=\"file\"]' : [ { prop : 'type' , value : 'file' } ] ,
28
+ 'input[type=\"image\"]' : [ { prop : 'type' , value : 'image' } ] ,
29
+ 'input[type=\"month\"]' : [ { prop : 'type' , value : 'month' } ] ,
30
+ 'input[type=\"number\"]' : [ { prop : 'type' , value : 'number' } ] ,
31
+ 'input[type=\"password\"]' : [ { prop : 'type' , value : 'password' } ] ,
32
+ 'input[type=\"radio\"]' : [ { prop : 'type' , value : 'radio' } ] ,
33
+ 'input[type=\"range\"]' : [ { prop : 'type' , value : 'range' } ] ,
34
+ 'input[type=\"reset\"]' : [ { prop : 'type' , value : 'reset' } ] ,
35
+ 'input[type=\"search\"]' : [ { prop : 'type' , value : 'search' } ] ,
36
+ 'input[type=\"submit\"]' : [ { prop : 'type' , value : 'submit' } ] ,
37
+ 'input[type=\"tel\"]' : [ { prop : 'type' , value : 'tel' } ] ,
38
+ 'input[type=\"text\"]' : [ { prop : 'type' , value : 'text' } ] ,
39
+ 'input[type=\"time\"]' : [ { prop : 'type' , value : 'time' } ] ,
40
+ 'input[type=\"url\"]' : [ { prop : 'type' , value : 'url' } ] ,
41
+ 'input[type=\"week\"]' : [ { prop : 'type' , value : 'week' } ] ,
42
+ menuitem :[ ] ,
43
+ option : [ ] ,
44
+ select : [ ] ,
45
+ 'table[role="grid"]' : [ { prop : 'role' , value : 'grid' } ] ,
46
+ 'td[role="gridcell"]' : [ { prop : 'role' , value : 'gridcell' } ] ,
47
+ tr : [ ] ,
48
+ textarea : [ ] ,
28
49
} ;
29
50
30
- const pureNonInteractiveElementsMap = domElements
31
- . filter ( name => ! dom . get ( name ) . interactive )
32
- . reduce ( ( nonInteractiveElements , name ) => {
33
- nonInteractiveElements [ name ] = [ ] ;
34
- return nonInteractiveElements ;
35
- } , { } ) ;
36
-
37
51
const nonInteractiveElementsMap = {
38
- ...pureNonInteractiveElementsMap ,
39
- input : [
40
- { prop : 'type' , value : 'hidden' }
41
- ] ,
52
+ area : [ ] ,
53
+ article : [ ] ,
54
+ dd : [ ] ,
55
+ dfn : [ ] ,
56
+ dt : [ ] ,
57
+ fieldset : [ ] ,
58
+ figure : [ ] ,
59
+ form : [ ] ,
60
+ frame : [ ] ,
61
+ h1 : [ ] ,
62
+ h2 : [ ] ,
63
+ h3 : [ ] ,
64
+ h4 : [ ] ,
65
+ h5 : [ ] ,
66
+ h6 : [ ] ,
67
+ hr : [ ] ,
68
+ img : [ ] ,
69
+ 'input[type=\"hidden\"]' : [ { prop : 'type' , value : 'hidden' } ] ,
70
+ li : [ ] ,
71
+ main : [ ] ,
72
+ nav : [ ] ,
73
+ ol : [ ] ,
74
+ table : [ ] ,
75
+ td : [ ] ,
76
+ tbody : [ ] ,
77
+ tfoot : [ ] ,
78
+ thead : [ ] ,
79
+ ul : [ ] ,
42
80
} ;
43
81
44
- const roleNames = [ ...roles . keys ( ) ] ;
82
+ const indeterminantInteractiveElementsMap = domElements
83
+ . reduce (
84
+ (
85
+ accumulator : { [ key : string ] : Array < any > } ,
86
+ name : string
87
+ ) : { [ key : string ] : Array < any > } => {
88
+ accumulator[ name ] = [ ] ;
89
+ return accumulator ;
90
+ } ,
91
+ { } ,
92
+ ) ;
93
+
94
+ Object . keys ( interactiveElementsMap )
95
+ . concat ( Object . keys ( nonInteractiveElementsMap ) )
96
+ . forEach (
97
+ ( name : string ) => delete indeterminantInteractiveElementsMap [ name ]
98
+ ) ;
99
+
100
+ const abstractRoles = roleNames
101
+ . filter ( role => roles . get ( role ) . abstract ) ;
45
102
46
- const interactiveRoles = roleNames . filter (
47
- role => roles . get ( role ) . interactive === true
48
- ) ;
103
+ const nonAbstractRoles = roleNames
104
+ . filter ( role => ! roles . get ( role ) . abstract ) ;
49
105
50
- const nonInteractiveRoles = roleNames . filter (
51
- role => roles . get ( role ) . interactive === false
52
- ) ;
106
+ const interactiveRoles = [ ]
107
+ . concat (
108
+ roleNames ,
109
+ // 'toolbar' does not descend from widget, but it does support
110
+ // aria-activedescendant, thus in practice we treat it as a widget.
111
+ 'toolbar' ,
112
+ )
113
+ . filter ( role => ! roles . get ( role ) . abstract )
114
+ . filter ( role => roles . get ( role ) . superClass . some (
115
+ klasses => klasses . includes ( 'widget' ) ) ,
116
+ ) ;
117
+
118
+ const nonInteractiveRoles = roleNames
119
+ . filter ( role => ! roles . get ( role ) . abstract )
120
+ . filter ( role => ! roles . get ( role ) . superClass . some (
121
+ klasses => klasses . includes ( 'widget' ) ) ,
122
+ )
123
+ // 'toolbar' does not descend from widget, but it does support
124
+ // aria-activedescendant, thus in practice we treat it as a widget.
125
+ . filter ( role => ! [ 'toolbar' ] . includes ( role ) ) ;
126
+
127
+ export function genElementSymbol (
128
+ openingElement : Object ,
129
+ ) {
130
+ return openingElement . name . name + (
131
+ ( openingElement . attributes . length > 0 )
132
+ ? `${ openingElement . attributes . map (
133
+ attr => `[${ attr . name . name } =\"${ attr . value . value } \"]` ) . join ( '' )
134
+ } `
135
+ : ''
136
+ ) ;
137
+ } ;
53
138
54
139
export function genInteractiveElements ( ) {
55
140
return Object . keys ( interactiveElementsMap )
56
- . map ( name => {
57
- const attributes = interactiveElementsMap [ name ] . map (
141
+ . map ( elementSymbol => {
142
+ const bracketIndex = elementSymbol . indexOf ( '[' ) ;
143
+ let name = elementSymbol ;
144
+ if ( bracketIndex > - 1 ) {
145
+ name = elementSymbol . slice ( 0 , bracketIndex ) ;
146
+ }
147
+ const attributes = interactiveElementsMap [ elementSymbol ] . map (
58
148
( { prop, value} ) => JSXAttributeMock ( prop , value )
59
149
) ;
60
150
return JSXElementMock ( name , attributes ) ;
61
151
} ) ;
62
152
}
63
153
64
154
export function genInteractiveRoleElements ( ) {
65
- return interactiveRoles . map (
155
+ return [
156
+ ...interactiveRoles ,
157
+ 'button article' ,
158
+ 'fakerole button article' ,
159
+ ] . map (
66
160
value => JSXElementMock ( 'div' , [
67
161
JSXAttributeMock ( 'role' , value )
68
162
] )
@@ -71,18 +165,53 @@ export function genInteractiveRoleElements () {
71
165
72
166
export function genNonInteractiveElements ( ) {
73
167
return Object . keys ( nonInteractiveElementsMap )
74
- . map ( name => {
75
- const attributes = nonInteractiveElementsMap [ name ] . map (
168
+ . map ( elementSymbol => {
169
+ const bracketIndex = elementSymbol . indexOf ( '[' ) ;
170
+ let name = elementSymbol ;
171
+ if ( bracketIndex > - 1 ) {
172
+ name = elementSymbol . slice ( 0 , bracketIndex ) ;
173
+ }
174
+ const attributes = nonInteractiveElementsMap [ elementSymbol ] . map (
76
175
( { prop, value} ) => JSXAttributeMock ( prop , value )
77
176
) ;
78
177
return JSXElementMock ( name , attributes ) ;
79
178
} ) ;
80
179
}
81
180
82
181
export function genNonInteractiveRoleElements ( ) {
83
- return nonInteractiveRoles . map (
182
+ return [
183
+ ...nonInteractiveRoles ,
184
+ 'article button' ,
185
+ 'fakerole article button' ,
186
+ ] . map (
84
187
value => JSXElementMock ( 'div' , [
85
188
JSXAttributeMock ( 'role' , value )
86
189
] )
87
190
) ;
88
191
}
192
+
193
+ export function genAbstractRoleElements ( ) {
194
+ return abstractRoles . map (
195
+ value => JSXElementMock ( 'div' , [
196
+ JSXAttributeMock ( 'role' , value )
197
+ ] )
198
+ ) ;
199
+ } ;
200
+
201
+ export function genNonAbstractRoleElements ( ) {
202
+ return nonAbstractRoles . map (
203
+ value => JSXElementMock ( 'div' , [
204
+ JSXAttributeMock ( 'role' , value )
205
+ ] )
206
+ ) ;
207
+ } ;
208
+
209
+ export function genIndeterminantInteractiveElements ( ) {
210
+ return Object . keys ( indeterminantInteractiveElementsMap )
211
+ . map ( name => {
212
+ const attributes = indeterminantInteractiveElementsMap [ name ] . map (
213
+ ( { prop, value} ) => JSXAttributeMock ( prop , value )
214
+ ) ;
215
+ return JSXElementMock ( name , attributes ) ;
216
+ } ) ;
217
+ }
0 commit comments