NumberFilter: a11y speak a description of the filter once all inputs are populated.

This commit is contained in:
Jeff Stieler 2018-12-17 18:59:01 -07:00
parent cbc5bc36fd
commit 7663cb2881
2 changed files with 58 additions and 11 deletions

View File

@ -3,7 +3,7 @@
* External dependencies * External dependencies
*/ */
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import { SelectControl, TextControl } from '@wordpress/components'; import { withSpokenMessages, SelectControl, TextControl } from '@wordpress/components';
import { get, find, partial } from 'lodash'; import { get, find, partial } from 'lodash';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import classnames from 'classnames'; import classnames from 'classnames';
@ -14,18 +14,33 @@ import { sprintf, __, _x } from '@wordpress/i18n';
*/ */
import TextControlWithAffixes from '../../text-control-with-affixes'; import TextControlWithAffixes from '../../text-control-with-affixes';
import { formatCurrency } from '@woocommerce/currency'; import { formatCurrency } from '@woocommerce/currency';
import { ariaHideStrings } from './utils'; import { ariaHideStrings, textContent } from './utils';
class NumberFilter extends Component { class NumberFilter extends Component {
componentDidUpdate() {
const { config, debouncedSpeak, filter } = this.props;
const [ rangeStart, rangeEnd ] = ( filter.value || '' ).split( ',' );
if ( 'between' === filter.rule && ( ! rangeStart || ! rangeEnd ) ) {
return;
}
if ( ! rangeStart ) {
return;
}
debouncedSpeak( this.getScreenReaderText( filter, config ) );
}
getBetweenString() { getBetweenString() {
return _x( return _x(
'{{rangeStart /}}{{span}}and{{/span}}{{rangeEnd /}}', '{{rangeStart /}}{{span}} and {{/span}}{{rangeEnd /}}',
'Numerical range inputs arranged on a single line', 'Numerical range inputs arranged on a single line',
'wc-admin' 'wc-admin'
); );
} }
getLegend( filter, config ) { getScreenReaderText( filter, config ) {
const inputType = get( config, [ 'input', 'type' ], 'number' ); const inputType = get( config, [ 'input', 'type' ], 'number' );
const rule = find( config.rules, { value: filter.rule } ) || {}; const rule = find( config.rules, { value: filter.rule } ) || {};
let [ rangeStart, rangeEnd ] = ( filter.value || '' ).split( ',' ); let [ rangeStart, rangeEnd ] = ( filter.value || '' ).split( ',' );
@ -35,7 +50,7 @@ class NumberFilter extends Component {
! rangeStart || ! rangeStart ||
( 'between' === rule.value && ! rangeEnd ) ( 'between' === rule.value && ! rangeEnd )
) { ) {
return get( config, [ 'labels', 'add' ] ); return '';
} }
if ( 'currency' === inputType ) { if ( 'currency' === inputType ) {
@ -56,13 +71,13 @@ class NumberFilter extends Component {
} ); } );
} }
return interpolateComponents( { return textContent( interpolateComponents( {
mixedString: config.labels.title, mixedString: config.labels.title,
components: { components: {
filter: <span>{ filterStr }</span>, filter: <Fragment>{ filterStr }</Fragment>,
rule: <span>{ rule.label }</span>, rule: <Fragment>{ rule.label }</Fragment>,
}, },
} ); } ) );
} }
getFormControl( { type, value, label, onChange } ) { getFormControl( { type, value, label, onChange } ) {
@ -229,4 +244,4 @@ class NumberFilter extends Component {
} }
} }
export default NumberFilter; export default withSpokenMessages( NumberFilter );

View File

@ -1,7 +1,7 @@
/** /**
* External dependencies * External dependencies
*/ */
import { isArray, isString } from 'lodash'; import { isArray, isNumber, isString } from 'lodash';
/** /**
* Hide all bare strings from the accessibility tree. * Hide all bare strings from the accessibility tree.
@ -22,3 +22,35 @@ export function ariaHideStrings( components ) {
: component : component
) ); ) );
} }
/**
* DOM Node.textContent for React components
* See: https://github.com/rwu823/react-addons-text-content/blob/master/src/index.js
*
* @param {Array<string|ReactNode>} components array of components
*
* @returns {string} concatenated text content of all nodes
*/
export function textContent( components ) {
let text = '';
const toText = ( component ) => {
if ( isString( component ) || isNumber( component ) ) {
text += component;
} else if ( isArray( component ) ) {
component.forEach( toText );
} else if ( component && component.props ) {
const { children } = component.props;
if ( isArray( children ) ) {
children.forEach( toText );
} else {
toText( children );
}
}
};
toText( components );
return text;
}