Skip to content

Commit 6931f5e

Browse files
authored
Merge pull request #2 from devcollaborative/facets
Facet updates - Reviewed each facet and made some accessibility improvements - Removed some non-a11y features - Rewrote JS to remove jQuery dependency - Disabled facets with accessibility issues - Added Submit facet - Enable ghosts for checkbox facet
2 parents 984864a + 2e25149 commit 6931f5e

File tree

2 files changed

+152
-163
lines changed

2 files changed

+152
-163
lines changed

a11y-add-on-for-facetwp.php

Lines changed: 130 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,36 @@
1616

1717
define( 'A11Y_ADDON_VERSION', '1.1.0' );
1818

19+
/**
20+
* Load custom facet types
21+
*/
22+
function load_facets( $facet_types ) {
23+
// Load custom facets
24+
include( dirname( __FILE__ ) . '/facets/Submit.php' );
25+
$facet_types['submit'] = new FacetWP_Facet_Submit();
26+
27+
// Remove facets that aren't yet accessible
28+
$disabled_facets = [
29+
'autocomplete', // Autocomplete dropdown is not focusable.
30+
'slider', // Using min + max setting is not accessible. Needs further review.
31+
'date_range', // Uses a date picker library that isn't accessible.
32+
'hierarchy', // Not keyboard or screen reader accessible.
33+
'rating', // Not keyboard or screen reader accessible.
34+
'fselect', // Not keyboard or screen reader accessible.
35+
'number_range', // Multiple inputs are not accessible.
36+
'proximity', // Missing labels, among other things.
37+
];
38+
39+
foreach( $disabled_facets as $facet ) {
40+
if( isset( $facet_types[ $facet ] ) ) {
41+
unset( $facet_types[ $facet ] );
42+
}
43+
}
44+
45+
return $facet_types;
46+
}
47+
add_filter( 'facetwp_facet_types', 'load_facets' );
48+
1949
/**
2050
* Run plugin update process on activation.
2151
*/
@@ -39,45 +69,33 @@ function a11y_addon_update_check() {
3969
}
4070
add_action( 'plugins_loaded', 'a11y_addon_update_check' );
4171

42-
43-
/**
44-
* Enqueue jQuery because FacetWP just assumes it's enqueued.
45-
*/
46-
function a11y_addon_facet_assets() {
47-
wp_enqueue_script('jquery');
48-
}
49-
add_action( 'wp_enqueue_scripts', 'a11y_addon_facet_assets' );
50-
51-
5272
/**
5373
* Adjusts markup for specific facets so they use real input elements
5474
* Adds matching label for and ids to all facets
5575
*
5676
* @param string $output HTML
5777
* @param array $params FacetWP field parameters
58-
*
78+
*
5979
* @return string Updated HTML output for a facet
60-
*
80+
*
6181
* @todo consider whether a combination of totally custom output and str_replace make sense or whether doing something with the WP HTML API might make more sense in the long term
62-
*
82+
*
6383
* most of this courtesy of Mark Root-Wiley
6484
* @link https://github.com/mrwweb/accessibility-addon-for-facetwp
6585
*/
6686
function a11y_addon_transform_facet_markup( $output, $params ) {
67-
6887
$facet_type = $params['facet']['type'];
6988

70-
switch ($facet_type) {
71-
89+
switch ( $facet_type ) {
7290
case 'checkboxes':
73-
// Note: The trick to this working was moving the facetwp-checkbox class and data-value attribute to the `input`. Clicking the label works because the input element still emits a click event when the label is clicked. Leaving that class and attribute on the wrapping list item resulted in two events being fired when the label was clicked.
74-
$output = sprintf('<fieldset><legend>%1$s</legend>',
75-
$params['facet']['label'] );
91+
// Note: The trick to this working was moving the facetwp-checkbox class and data-value attribute to the `input`.
92+
// Clicking the label works because the input element still emits a click event when the label is clicked.
93+
// Leaving that class and attribute on the wrapping list item resulted in two events being fired when the label was clicked.
94+
$output = '';
7695
foreach( $params['values'] as $value ) {
77-
if( $value['counter'] > 0 || ! $params['facet']['preserve_ghosts'] === 'no' ) {
78-
$output .= sprintf(
79-
'<div>
80-
<input type="checkbox" id="%3$s"%1$s value="%2$s" class="facetwp-checkbox%1$s" data-value="%2$s">
96+
$checkbox = sprintf(
97+
'<div class="facetwp-checkbox-wrapper %6$s">
98+
<input type="checkbox" id="%3$s"%1$s value="%2$s" class="facetwp-checkbox%1$s" %6$s data-value="%2$s">
8199
<label for="%3$s">
82100
<span class="facetwp-display-value">%4$s</span>
83101
<span class="facetwp-counter">(%5$d)</span>
@@ -87,75 +105,59 @@ function a11y_addon_transform_facet_markup( $output, $params ) {
87105
esc_attr( $value['facet_value'] ),
88106
'checkbox-' . esc_attr( $value['term_id'] ),
89107
esc_html( $value['facet_display_value'] ),
90-
$value['counter']
108+
$value['counter'],
109+
$value['counter'] == 0 ? 'disabled' : ''
91110
);
92-
}
111+
112+
$output .= $checkbox;
93113
}
94-
$output .= '</fieldset>';
95114
break;
96115

97116
case 'search':
98-
// use search landmark and insert a real button
99-
$output = '<search>' . $output . '</search>';
100117
// remove the fake button
101118
$output = str_replace( '<i class="facetwp-icon"></i>', '', $output );
119+
102120
// add label to search input
103121
$id = $params['facet']['name'];
104-
$output = str_replace(
105-
'<input', '<div class="trec-facetwp-search-wrapper"><input id="' . esc_attr( $id ) . '"', $output );
106-
122+
$output = str_replace( '<input', '<div class="trec-facetwp-search-wrapper"><input id="' . esc_attr( $id ) . '"', $output );
123+
107124
// placeholders are bad for UX
108125
$output = str_replace( 'placeholder="Enter keywords"', '', $output );
109126
break;
110127

111128
case 'dropdown':
112-
113129
$output = str_replace( 'facetwp-dropdown', 'facetwp-dropdown a11y-addon-filter', $output );
114130

115-
$id_string = 'id="'.$params['facet']['name'].'" class=';
131+
$id_string = 'id="' . $params['facet']['name'] . '" class=';
116132

117133
$output = str_replace('class=', $id_string, $output);
118-
119-
break;
120-
121-
case 'radio':
122-
$output = sprintf('<fieldset><legend>%1$s</legend>',
123-
$params['facet']['label'] );
124-
foreach( $params['values'] as $value ) {
125-
$output .= sprintf(
126-
'<div><input type="radio" id="%1$s" name="%3$s" value="%2$s">
127-
<label for="%1$s">%2$s</label></div>',
128-
esc_attr( 'radio-'.$value['facet_value'] ),
129-
esc_html( $value['facet_display_value'] ),
130-
esc_attr( $params['facet']['name'] )
131-
);
132-
}
133-
134-
$output .= '</fieldset>';
135134
break;
136135

137136
case 'reset':
138-
139-
$output = str_replace('button','button type="reset"',$output);
140-
141-
break;
142-
143-
case 'pager':
144-
145-
$output = sprintf('
146-
<nav class="navigation pagination" aria-label="Pagination"><h2 class="screen-reader-text">Pagination</h2>%1$s</nav>',
147-
$output);
148-
137+
$output = str_replace('<button', '<button type="reset"', $output);
149138
break;
150139

151140
default:
152141

153142
$id_string = 'id="'.$params['facet']['name'].'" class=';
154143

155144
$output = str_replace('class=', $id_string, $output);
145+
break;
146+
147+
case 'pager':
148+
// Use nav element for pager
149+
$output = str_replace( '<div', '<nav aria-label="' . esc_html__( 'Pagination', 'aawp' ) . '"', $output );
150+
$output = str_replace( '</div>', '</nav>', $output );
156151

152+
// Add role="presentation" to dots & active item so screen readers don't read them as links
153+
$output = str_replace( 'facetwp-page dots"', 'facetwp-page dots" role="presentation"', $output );
154+
$output = str_replace( 'facetwp-page active"', 'facetwp-page active" role="presentation"', $output );
155+
156+
// Change page links to buttons
157+
$output = str_replace( 'a ', 'button ', $output );
158+
break;
157159
}
158-
160+
159161
return $output;
160162
}
161163

@@ -166,84 +168,94 @@ function a11y_addon_transform_facet_markup( $output, $params ) {
166168
* Programatically add labels above filters
167169
* @link https://facetwp.com/add-labels-above-each-facet/
168170
*/
169-
170171
function a11y_addon_add_facet_labels() {
171172
?>
172173
<script>
173-
(function($) {
174-
175-
function remove_underscores( name ) {
176-
return name.replace(/_/g, ' ');
177-
}
178-
179-
// Make enter & space work for links
180-
$(document).on('keydown', '.facetwp-page, .facetwp-toggle, .facetwp-selection-value', function(e) {
181-
var keyCode = e.originalEvent.keyCode;
182-
if ( 32 == keyCode || 13 == keyCode ) {
183-
e.preventDefault();
184-
$(this).click();
185-
}
186-
});
187-
188-
$(document).on('keydown', '.facetwp-checkbox, .facetwp-radio', function(e) {
189-
var keyCode = e.originalEvent.keyCode;
190-
if ( 32 == keyCode || 13 == keyCode ) {
191-
var is_checked = $(this).hasClass('checked');
192-
193-
if (!is_checked) {
194-
$(this).attr('aria-checked', 'true');
195-
} else {
196-
$(this).attr('aria-checked', 'false');
197-
}
198-
199-
e.preventDefault();
200-
$(this).click();
201-
}
202-
});
203-
204-
$(document).on('facetwp-loaded', function() {
205-
206-
// pager
207-
$('.facetwp-pager').attr('role', 'navigation');
208-
174+
(function() {
175+
document.addEventListener('facetwp-loaded', function() {
209176
// Add labels
210-
$('.facetwp-facet').each(function() {
211-
var $facet = $(this);
212-
var facet_name = $facet.attr('data-name');
213-
var facet_type = $facet.attr('data-type');
214-
215-
if ( facet_name && facet_type ) {
216-
// Don't label some facets
217-
if ( facet_type.match(/checkboxes|radio|pager|reset|results_count/g) ) {
177+
var facets = document.querySelectorAll('.facetwp-facet');
178+
179+
facets.forEach(function(facet) {
180+
var facet_name = facet.getAttribute('data-name');
181+
var facet_type = facet.getAttribute('data-type');
182+
183+
if (facet_name && facet_type) {
184+
// Exclude some facets from getting labels
185+
if (facet_name.match(/pagination/g) ||
186+
facet_name.match(/reset/g) ||
187+
facet_name.match(/submit/g) ||
188+
facet_name.match(/results_count/g)) {
218189
return;
219190
}
220191

221192
var facet_label = FWP.settings.labels[facet_name];
222193

223-
if ($facet.closest('.facet-wrap').length < 1 && $facet.closest('.facetwp-flyout').length < 1) {
224-
225-
//wrapper div
226-
$facet.wrap(`<div class="facet-wrap facet-wrap-${facet_name}"></div>`);
227-
228-
//label
229-
$facet.before('<label class="facet-label" for="'+facet_name.replace(/\s/g, '')+'">' + facet_label + '</label>');
230-
194+
if (!facet.closest('.facet-wrap') && !facet.closest('.facetwp-flyout')) {
195+
if (facet_type.match(/checkboxes/g) || facet_type.match(/radio/g)) {
196+
// Checkboxes & radio buttons need a fieldset/legend created here using role group & arialabelledby
197+
var wrapDiv = document.createElement('div');
198+
wrapDiv.className = 'facet-wrap facet-wrap-' + facet_name;
199+
wrapDiv.setAttribute('role', 'group');
200+
wrapDiv.setAttribute('aria-labelledby', facet_name + '_label');
201+
202+
var labelDiv = document.createElement('div');
203+
labelDiv.id = facet_name + '_label';
204+
labelDiv.className = 'facet-label';
205+
labelDiv.textContent = facet_label;
206+
207+
facet.parentNode.insertBefore(wrapDiv, facet);
208+
wrapDiv.appendChild(labelDiv);
209+
wrapDiv.appendChild(facet);
210+
} else {
211+
var wrapDiv = document.createElement('div');
212+
wrapDiv.className = 'facet-wrap facet-wrap-' + facet_name;
213+
214+
var label = document.createElement('label');
215+
label.className = 'facet-label';
216+
label.setAttribute('for', facet_name.replace(/\s/g, '')); // remove spaces for id
217+
label.textContent = facet_label;
218+
219+
facet.parentNode.insertBefore(wrapDiv, facet);
220+
wrapDiv.appendChild(label);
221+
wrapDiv.appendChild(facet);
222+
}
231223
}
232224
}
233225
});
234226
});
235-
})(jQuery);
227+
})();
236228
</script>
237229
<?php
238230
}
239231

240-
add_action( 'wp_head', 'a11y_addon_add_facet_labels', 100 );
232+
add_action( 'facetwp_scripts', 'a11y_addon_add_facet_labels', 100 );
233+
234+
/**
235+
* Submit form when enter key is pressed
236+
* @link https://facetwp.com/help-center/add-on-features-and-extras/submit-button#submit-on-enter
237+
*/
238+
function a11y_addon_add_facet_submit_form() {
239+
?>
240+
<script>
241+
(function() {
242+
document.addEventListener('facetwp-loaded', function() {
243+
document.addEventListener('keyup', function(event) {
244+
if (event.keyCode === 13) {
245+
FWP.refresh()
246+
}
247+
});
248+
});
249+
})();
250+
</script>
251+
<?php
252+
}
253+
add_action( 'facetwp_scripts', 'a11y_addon_add_facet_submit_form', 100 );
241254

242255
/**
243256
* Hide counts in all dropdowns
244257
* @link https://facetwp.com/help-center/facets/facet-types/dropdown/
245258
*/
246-
247259
function a11y_addon_hide_dropdown_counts( $return, $params ) {
248260
return false;
249261
}
@@ -267,48 +279,3 @@ function a11y_addon_disable_auto_refresh() {
267279
<?php
268280
}
269281
add_action( 'facetwp_scripts', 'a11y_addon_disable_auto_refresh', 100 );
270-
271-
// Customize icon for prev/next pagination links.
272-
function a11y_addon_facetwp_facet_pager_link($html, $params) {
273-
if ( 'next' == $params['extra_class'] ) {
274-
$icon = 'Next <svg class="icon" aria-hidden="true"><use xlink:href="#caret-right"/></svg></svg>';
275-
$html = str_replace( 'Next', $icon, $html );
276-
}
277-
278-
if ( 'prev' == $params['extra_class'] ) {
279-
$icon = '<svg class="icon" aria-hidden="true"><use xlink:href="#caret-left"/></svg></svg> Prev';
280-
$html = str_replace( 'Prev', $icon, $html );
281-
}
282-
283-
return $html;
284-
}
285-
add_action( 'facetwp_facet_pager_link', 'a11y_addon_facetwp_facet_pager_link', 100, 2 );
286-
287-
/**
288-
* Scroll back to top of results when pager is clicked
289-
* @link https://facetwp.com/how-to-scroll-the-page-on-facet-interaction/#scroll-when-a-pager-facet-is-used
290-
**/
291-
292-
function a11y_addon_scroll_on_pager_interaction() {
293-
?>
294-
<script>
295-
(function($) {
296-
$(document).on('facetwp-refresh', function() {
297-
if ( FWP.soft_refresh == true ) {
298-
FWP.enable_scroll = true;
299-
} else {
300-
FWP.enable_scroll = false;
301-
}
302-
});
303-
$(document).on('facetwp-loaded', function() {
304-
if (FWP.enable_scroll == true) {
305-
$('html, body').animate({
306-
scrollTop: $('.facetwp-template').offset().top
307-
}, 500);
308-
}
309-
});
310-
})(jQuery);
311-
</script>
312-
<?php
313-
}
314-
add_action( 'facetwp_scripts', 'a11y_addon_scroll_on_pager_interaction' );

0 commit comments

Comments
 (0)