From a72749e6ac25552749aeca8b4574aacd644c39ad Mon Sep 17 00:00:00 2001 From: Matt Charlton Date: Fri, 25 Jun 2021 15:19:32 +1000 Subject: [PATCH 01/24] fix VO,NVDA focus announcements --- docs/examples/BasicMulti.tsx | 1 + docs/examples/BasicSingle.tsx | 6 +- docs/pages/home/index.tsx | 147 ------------------ packages/react-select/src/Select.tsx | 22 ++- .../react-select/src/accessibility/index.ts | 12 +- .../src/components/LiveRegion.tsx | 53 ++++++- .../src/components/containers.tsx | 1 + 7 files changed, 83 insertions(+), 159 deletions(-) diff --git a/docs/examples/BasicMulti.tsx b/docs/examples/BasicMulti.tsx index 9cc30d6d43..0d6a8b126d 100644 --- a/docs/examples/BasicMulti.tsx +++ b/docs/examples/BasicMulti.tsx @@ -11,5 +11,6 @@ export default () => ( options={colourOptions} className="basic-multi-select" classNamePrefix="select" + aria-live="assertive" /> ); diff --git a/docs/examples/BasicSingle.tsx b/docs/examples/BasicSingle.tsx index 37aa1d8b67..261f7510fa 100644 --- a/docs/examples/BasicSingle.tsx +++ b/docs/examples/BasicSingle.tsx @@ -1,6 +1,6 @@ import React, { Component, Fragment } from 'react'; -import Select from 'react-select'; +import Select from '../../packages/react-select/src/'; import { colourOptions } from '../data'; import { Note } from '../styled-components'; @@ -42,7 +42,7 @@ export default class SingleSelect extends Component<{}, State> { - ) - ~~~ - - ${( - - - - )} - - ## Animated Components - - React-Select comes with a makeAnimated function that create animated wrappers around components passed in as arguments. - If no arguments are passed, built-in components are wrapped instead. - - ~~~jsx - import makeAnimated from 'react-select/animated'; - ~~~ - - Remove the values below to see them in action. - - ${( - - - - )} - - ## Custom Styles - - Style individual components with custom css using the \`styles\` prop. - - ${( - - - - )} - - ${( - - - - )} - - You can see a full explanation of how to do this on the [styles](/styles) page. - - # Async - Use the Async component to load options from a remote source as the user types. - - ~~~jsx - import AsyncSelect from 'react-select/async'; - ~~~ - - ${( - - - - )} - - ${( - - - - )} - - You can see a full explanation of how to do this on the [async](/async) page. - - # Creatable - The Creatable component enables users to create new options along with choosing existing options. - - ~~~jsx - import Creatable from 'react-select/creatable'; - ~~~ - - ${( - - - - )} - - You can see a full explanation of how to do this on the [creatable](/creatable) page. - - # Fixed Options - - ${( - - - - )} `; } diff --git a/packages/react-select/src/Select.tsx b/packages/react-select/src/Select.tsx index 30e63fb994..a6e914f6b5 100644 --- a/packages/react-select/src/Select.tsx +++ b/packages/react-select/src/Select.tsx @@ -628,7 +628,19 @@ export default class Select< this.instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId); this.state.selectValue = cleanValue(props.value); + + // If `value` or `defaultValue` props are not empty then announce them + // when the Select is focussed + if (this.state.selectValue.length) { + this.state.ariaSelection = { + value: this.state.selectValue as OnChangeValue, + action: 'select-option', + option: this.state.selectValue, + name: this.props.name, + }; + } } + static getDerivedStateFromProps( props: Props>, state: State> @@ -1027,7 +1039,9 @@ export default class Select< const custom = this.props.styles[key]; return custom ? custom(base, props as any) : base; }; - getElementId = (element: 'group' | 'input' | 'listbox' | 'option') => { + getElementId = ( + element: 'group' | 'input' | 'listbox' | 'option' | 'placeholder' + ) => { return `${this.instancePrefix}-${element}`; }; @@ -1524,6 +1538,9 @@ export default class Select< ...(!isSearchable && { 'aria-readonly': true, }), + ...(!this.hasValue() && { + 'aria-describedby': this.getElementId('placeholder'), + }), }; if (!isSearchable) { @@ -1535,9 +1552,9 @@ export default class Select< onBlur={this.onInputBlur} onChange={noop} onFocus={this.onInputFocus} + inputMode="none" disabled={isDisabled} tabIndex={tabIndex} - inputMode="none" form={form} value="" {...ariaAttributes} @@ -1593,6 +1610,7 @@ export default class Select< key="placeholder" isDisabled={isDisabled} isFocused={isFocused} + innerProps={{ id: this.getElementId('placeholder') }} > {placeholder} diff --git a/packages/react-select/src/accessibility/index.ts b/packages/react-select/src/accessibility/index.ts index 1803266435..9757875331 100644 --- a/packages/react-select/src/accessibility/index.ts +++ b/packages/react-select/src/accessibility/index.ts @@ -13,10 +13,13 @@ export type GuidanceContext = 'menu' | 'input' | 'value'; export type AriaLive = 'polite' | 'off' | 'assertive'; -export type AriaSelection