@@ -10,21 +10,27 @@ const Catalog = ({widgets = [], isLoading = true}) => {
10
10
const [ state , setState ] = useState ( {
11
11
searchText : '' ,
12
12
showingFilters : false ,
13
+ showingAccessibility : false ,
13
14
activeFilters : [ ] ,
14
- showMobileFilters : false
15
+ showMobileFilters : false ,
16
+ showMobileAccessibilityFilters : false
15
17
} )
16
18
const totalWidgets = widgets . length
17
19
18
20
// collect all unique features and supported data
19
21
const filters = useMemo ( ( ) => {
20
- const filters = new Set ( )
22
+ const features = new Set ( )
23
+ const accessibility = new Set ( )
21
24
widgets . forEach ( w => {
22
- w . meta_data . features . forEach ( f => { filters . add ( f ) } )
23
- w . meta_data . supported_data . forEach ( f => { filters . add ( f ) } )
24
- if ( w . meta_data . hasOwnProperty ( 'accessibility_keyboard' ) ) filters . add ( 'Keyboard Accessible' )
25
- if ( w . meta_data . hasOwnProperty ( 'accessibility_reader' ) ) filters . add ( 'Screen Reader Accessible' )
25
+ w . meta_data . features . forEach ( f => { features . add ( f ) } )
26
+ w . meta_data . supported_data . forEach ( f => { features . add ( f ) } )
27
+ if ( w . meta_data . hasOwnProperty ( 'accessibility_keyboard' ) ) accessibility . add ( 'Keyboard Accessible' )
28
+ if ( w . meta_data . hasOwnProperty ( 'accessibility_reader' ) ) accessibility . add ( 'Screen Reader Accessible' )
26
29
} )
27
- return Array . from ( filters )
30
+ return {
31
+ features : Array . from ( features ) ,
32
+ accessibility : Array . from ( accessibility )
33
+ }
28
34
} ,
29
35
[ widgets ]
30
36
)
@@ -72,6 +78,15 @@ const Catalog = ({widgets = [], isLoading = true}) => {
72
78
setState ( { ...state , activeFilters : newFilters , showMobileFilters : false } )
73
79
}
74
80
81
+ const accessibilityLinkClickHandler = ( ) => {
82
+ if ( state . showingAccessibility ) {
83
+ setState ( { ...state , showingAccessibility : ! state . showingAccessibility , activeFilters : [ ] } )
84
+ }
85
+ else {
86
+ setState ( { ...state , showingAccessibility : ! state . showingAccessibility } )
87
+ }
88
+ }
89
+
75
90
const filterLinkClickHandler = ( ) => {
76
91
if ( state . showingFilters ) {
77
92
setState ( { ...state , showingFilters : ! state . showingFilters , activeFilters : [ ] } )
@@ -92,7 +107,7 @@ const Catalog = ({widgets = [], isLoading = true}) => {
92
107
93
108
let mobileFilterRender = null
94
109
if ( state . showMobileFilters ) {
95
- const mobileFilterOptionsRender = filters . map ( filter => (
110
+ const mobileFilterOptionsRender = filters . features . map ( filter => (
96
111
< label key = { filter } >
97
112
< input type = 'checkbox'
98
113
className = 'filter-button'
@@ -112,14 +127,51 @@ const Catalog = ({widgets = [], isLoading = true}) => {
112
127
{ mobileFilterOptionsRender }
113
128
</ div >
114
129
)
130
+ } else if ( state . showMobileAccessibilityFilters ) {
131
+ const mobileFilterOptionsRender = filters . accessibility . map ( filter => (
132
+ < label key = { filter } >
133
+ < input type = 'checkbox'
134
+ className = 'filter-button'
135
+ checked = { state . activeFilters . includes ( filter ) }
136
+ readOnly = { true }
137
+ onClick = { ( ) => toggleFilter ( filter ) }
138
+ />
139
+ { filter }
140
+ </ label >
141
+ ) )
142
+
143
+ mobileFilterRender = (
144
+ < div
145
+ id = 'filter-dropdown'
146
+ className = 'mobile-only accessibility'
147
+ aria-hidden = { ! isMobileDevice ( ) } >
148
+ { mobileFilterOptionsRender }
149
+ </ div >
150
+ )
115
151
}
116
152
117
- const filterOptionsRender = filters . map ( ( filter , index ) => {
153
+ const filterOptionsRender = filters . features . map ( ( filter , index ) => {
118
154
const isEnabled = state . activeFilters . includes ( filter )
119
155
const filterOptionClickHandler = ( ) => toggleFilter ( filter )
120
156
return < button key = { index }
121
157
className = { 'feature-button' + ( isEnabled ? ' selected' : '' ) }
158
+ aria-label = { `Filter by ${ filter } . ${ isEnabled ? 'Selected.' : '' } ` }
122
159
aria-hidden = { ! state . showingFilters }
160
+ disabled = { ! state . showingFilters }
161
+ onClick = { filterOptionClickHandler } >
162
+ { filter }
163
+ </ button >
164
+ }
165
+ )
166
+
167
+ const accessibilityOptionsRender = filters . accessibility . map ( ( filter , index ) => {
168
+ const isEnabled = state . activeFilters . includes ( filter )
169
+ const filterOptionClickHandler = ( ) => toggleFilter ( filter )
170
+ return < button key = { index }
171
+ className = { 'feature-button' + ( isEnabled ? ' selected' : '' ) }
172
+ aria-label = { `Filter by ${ filter } . ${ isEnabled ? 'Selected.' : '' } ` }
173
+ aria-hidden = { ! state . showingAccessibility }
174
+ disabled = { ! state . showingAccessibility }
123
175
onClick = { filterOptionClickHandler } >
124
176
{ filter == 'Keyboard Accessible' ? < KeyboardIcon color = '#000' /> : '' }
125
177
{ filter == 'Screen Reader Accessible' ? < ScreenReaderIcon color = '#000' /> : '' }
@@ -203,40 +255,59 @@ const Catalog = ({widgets = [], isLoading = true}) => {
203
255
204
256
< div className = 'top' >
205
257
< h1 > Widget Catalog</ h1 >
206
- < button
207
- className = 'filter-toggle cancel_button desktop-only'
208
- onClick = { filterLinkClickHandler } >
209
- { state . showingFilters ? 'Clear Filters' : 'Filter by feature' }
210
- </ button >
211
- < div className = { 'search' + ( state . searchText === '' ? '' : ' not-empty' ) } >
212
- < input value = { state . searchText } onChange = { ( e ) => { setState ( { ...state , searchText : e . target . value } ) } } type = 'text' />
213
- < div className = 'search-icon' >
214
- < svg viewBox = '0 0 250.313 250.313' >
215
- < path d = 'm244.19 214.6l-54.379-54.378c-0.289-0.289-0.628-0.491-0.93-0.76 10.7-16.231 16.945-35.66 16.945-56.554 0-56.837-46.075-102.91-102.91-102.91s-102.91 46.075-102.91 102.91c0 56.835 46.074 102.91 102.91 102.91 20.895 0 40.323-6.245 56.554-16.945 0.269 0.301 0.47 0.64 0.759 0.929l54.38 54.38c8.169 8.168 21.413 8.168 29.583 0 8.168-8.169 8.168-21.413 0-29.582zm-141.28-44.458c-37.134 0-67.236-30.102-67.236-67.235 0-37.134 30.103-67.236 67.236-67.236 37.132 0 67.235 30.103 67.235 67.236s-30.103 67.235-67.235 67.235z'
216
- clipRule = 'evenodd'
217
- fillRule = 'evenodd' />
218
- </ svg >
258
+ < aside >
259
+ < span className = 'label' > Filter by:</ span >
260
+ < button
261
+ className = { `filter-toggle desktop-only ${ state . showingFilters ? 'close-mode' : '' } ` }
262
+ aria-label = { state . showingFilters ? 'Feature filters drawer open' : 'Filter catalog by features' }
263
+ onClick = { filterLinkClickHandler } >
264
+ Feature</ button >
265
+ < button
266
+ className = { `filter-toggle desktop-only ${ state . showingAccessibility ? 'close-mode' : '' } ` }
267
+ aria-label = { state . showingAccessibility ? 'Accessibility filters drawer open' : 'Filter catalog by accessibility' }
268
+ onClick = { accessibilityLinkClickHandler } >
269
+ Accessibility</ button >
270
+ < div className = { 'search' + ( state . searchText === '' ? '' : ' not-empty' ) } >
271
+ < input value = { state . searchText } onChange = { ( e ) => { setState ( { ...state , searchText : e . target . value } ) } } type = 'text' />
272
+ < div className = 'search-icon' >
273
+ < svg viewBox = '0 0 250.313 250.313' >
274
+ < path d = 'm244.19 214.6l-54.379-54.378c-0.289-0.289-0.628-0.491-0.93-0.76 10.7-16.231 16.945-35.66 16.945-56.554 0-56.837-46.075-102.91-102.91-102.91s-102.91 46.075-102.91 102.91c0 56.835 46.074 102.91 102.91 102.91 20.895 0 40.323-6.245 56.554-16.945 0.269 0.301 0.47 0.64 0.759 0.929l54.38 54.38c8.169 8.168 21.413 8.168 29.583 0 8.168-8.169 8.168-21.413 0-29.582zm-141.28-44.458c-37.134 0-67.236-30.102-67.236-67.235 0-37.134 30.103-67.236 67.236-67.236 37.132 0 67.235 30.103 67.235 67.236s-30.103 67.235-67.235 67.235z'
275
+ clipRule = 'evenodd'
276
+ fillRule = 'evenodd' />
277
+ </ svg >
278
+ </ div >
279
+ { searchCloseRender }
219
280
</ div >
220
- { searchCloseRender }
221
- </ div >
281
+ </ aside >
282
+
222
283
</ div >
223
284
224
- < div aria-hidden = { ! isMobileDevice ( ) } id = 'active-filters' className = ' mobile-only'>
225
- < button id = 'add-filter'
226
- onClick = { ( ) => { setState ( { ...state , showMobileFilters : ! state . showMobileFilters } ) } } >
285
+ < div aria-hidden = { ! isMobileDevice ( ) } className = 'mobile-filter-select mobile-only'>
286
+ < button className = 'add-filter'
287
+ onClick = { ( ) => { setState ( { ...state , showMobileFilters : ! state . showMobileFilters , showMobileAccessibilityFilters : false } ) } } >
227
288
{ state . activeFilters . length ? 'Filters' : 'Filter by Feature' }
228
289
</ button >
229
- < div >
290
+ < button className = 'add-filter'
291
+ onClick = { ( ) => { setState ( { ...state , showMobileAccessibilityFilters : ! state . showMobileAccessibilityFilters , showMobileFilters : false } ) } } >
292
+ { state . activeFilters . length ? 'Accessibility' : 'Filter by Accessibility' }
293
+ </ button >
294
+ < div className = 'active-filters' >
230
295
{ state . activeFilters . join ( ', ' ) }
231
296
</ div >
232
297
</ div >
233
298
{ mobileFilterRender }
234
299
< div id = 'filters-container'
235
- className = { `ready ${ state . showingFilters ? 'open' : 'closed' } ` } >
236
- < div className = 'filter-labels-container' >
300
+ className = { `ready ${ state . showingFilters ? 'open' : 'closed' } ` } aria-hidden = { ! state . showingFilters } >
301
+ < div className = 'filter-labels-container' disabled = { ! state . showingFilters } >
237
302
{ filterOptionsRender }
238
303
</ div >
239
304
</ div >
305
+ < div id = 'filters-container'
306
+ className = { `ready ${ state . showingAccessibility ? 'open' : 'closed' } ` } aria-hidden = { ! state . showingAccessibility } >
307
+ < div className = 'filter-labels-container accessibility' disabled = { ! state . showingAccessibility } >
308
+ { accessibilityOptionsRender }
309
+ </ div >
310
+ </ div >
240
311
241
312
{ featuredWidgetsRender }
242
313
0 commit comments