Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

[terra-form-select] - Fixed SR issues #3985

Merged
merged 10 commits into from
Nov 30, 2023
5 changes: 4 additions & 1 deletion packages/terra-form-select/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

## Unreleased

* Fixed
* Fixed screen reader response for `terra-form-select-combobox`.

## 6.51.0 - (November 21, 2023)

* Added
* Added 'aria-invalid' attribute which will be set to true for error input fields and false when resolving errors.
* Added 'aria-invalid' attribute for `terra-form-select-combobox`

## 6.50.0 - (November 13, 2023)

Expand Down
1 change: 1 addition & 0 deletions packages/terra-form-select/src/Combobox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class Combobox extends React.Component {
clearOptionDisplay={clearOptionDisplay}
inputId={inputId}
resetComboboxValue={this.handleResetComboboxValue}
allowClear={allowClear}
>
{this.state.tags}
{children}
Expand Down
1 change: 1 addition & 0 deletions packages/terra-form-select/src/SearchSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class SearchSelect extends React.Component {
clearOptionDisplay={clearOptionDisplay}
inputId={inputId}
resetComboboxValue={this.handleResetComboboxValue}
allowClear={allowClear}
>
{children}
</Frame>
Expand Down
17 changes: 17 additions & 0 deletions packages/terra-form-select/src/combobox/Frame.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ const propTypes = {
* Callback function to reset input value after search
*/
resetComboboxValue: PropTypes.func,
/**
* Whether a clear option is available to clear the selection, will use placeholder text if provided.
*/
allowClear: PropTypes.bool,
};

const defaultProps = {
Expand All @@ -139,6 +143,7 @@ const defaultProps = {
totalOptions: undefined,
value: undefined,
inputId: undefined,
allowClear: false,
};

/* This rule can be removed when eslint-plugin-jsx-a11y is updated to ~> 6.0.0 */
Expand Down Expand Up @@ -263,6 +268,7 @@ class Frame extends React.Component {
* @param {event} event - The onKeyDown event.
*/
handleKeyDown(event) {
const { intl } = this.props;
const { keyCode, target } = event;

if (keyCode === KeyCode.KEY_SPACE && target !== this.input) {
Expand All @@ -285,6 +291,17 @@ class Frame extends React.Component {
searchValue: '',
});
event.stopPropagation();
} else if (keyCode === KeyCode.KEY_ESCAPE && this.props.allowClear) {
this.hasEscPressed = false;
if (this.props.resetComboboxValue) {
this.props.resetComboboxValue();
}
this.setState({
hasSearchChanged: false,
searchValue: '',
});
this.visuallyHiddenComponent.current.innerText = intl.formatMessage({ id: 'Terra.form.select.selectCleared' });
event.stopPropagation();
}
}

Expand Down
34 changes: 16 additions & 18 deletions packages/terra-form-select/src/combobox/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as KeyCode from 'keycode-js';
import AddOption from '../shared/_AddOption';
import ClearOption from '../shared/_ClearOption';
import MenuUtil from '../shared/_MenuUtil';
import SharedUtil from '../shared/_SharedUtil';
import SearchResults from '../shared/_SearchResults';
import styles from '../shared/_Menu.module.scss';
import NoResults from '../shared/_NoResults';
Expand Down Expand Up @@ -104,7 +103,9 @@ class Menu extends React.Component {
constructor(props) {
super(props);

this.state = {};
this.state = {
closedViaKeyEvent: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide link of code line where we are changing the value of this state from its initial value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2023-11-23 at 6 39 32 PM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.setState({ closedViaKeyEvent: true });

};

this.clearScrollTimeout = this.clearScrollTimeout.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
Expand Down Expand Up @@ -177,7 +178,6 @@ class Menu extends React.Component {
}

componentDidUpdate() {
this.updateNoResultsScreenReader();
this.updateCurrentActiveScreenReader();
}

Expand Down Expand Up @@ -239,8 +239,8 @@ class Menu extends React.Component {
results in reading the display text followed by reading the aria-live message which is
the display text + 'selected'
*/
if (!SharedUtil.isSafari()) {
this.props.visuallyHiddenComponent.current.innerText = `${option.props.display} ${selectedTxt}`;
if (option.props.value) {
this.props.visuallyHiddenComponent.current.innerText = `${option.props.value} ${selectedTxt}`;
}
}

Expand Down Expand Up @@ -299,30 +299,23 @@ class Menu extends React.Component {
return this.state.active === this.props.value;
}

updateNoResultsScreenReader() {
updateNoResultsScreenReader(freeTextValue) {
if (this.liveRegionTimeOut) {
clearTimeout(this.liveRegionTimeOut);
}

this.liveRegionTimeOut = setTimeout(() => {
const { hasNoResults } = this.state;

const {
intl,
visuallyHiddenComponent,
searchValue,
} = this.props;

// Race condition can occur between calling timeout and unmounting this component.
if (!visuallyHiddenComponent || !visuallyHiddenComponent.current) {
return;
}

if (hasNoResults) {
visuallyHiddenComponent.current.innerText = intl.formatMessage({ id: 'Terra.form.select.noResults' }, { text: searchValue });
} else {
visuallyHiddenComponent.current.innerText = '';
}
const noMatchingResultText = intl.formatMessage({ id: 'Terra.form.select.noResults' }, { text: searchValue });
visuallyHiddenComponent.current.innerText = `${noMatchingResultText}, ${freeTextValue}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you attach JAWS speech history as well similar to VO screenshots as a evidence

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks

}, 1000);
}

Expand All @@ -334,6 +327,7 @@ class Menu extends React.Component {
} = this.props;

const clearSelectTxt = intl.formatMessage({ id: 'Terra.form.select.clearSelect' });
const { hasNoResults } = this.state;

if (this.menu !== null && this.state.active !== null) {
this.menu.setAttribute('aria-activedescendant', `terra-select-option-${this.state.active}`);
Expand All @@ -346,17 +340,21 @@ class Menu extends React.Component {

// Detects if option is clear option and provides accessible text
if (clearOptionDisplay) {
const active = this.menu.querySelector('[data-select-active]');
const active = this.menu && this.menu.querySelector('[data-select-active]');
if (active && active.hasAttribute('data-terra-select-clear-option')) {
visuallyHiddenComponent.current.innerText = clearSelectTxt;
}
}

// Detects if option is an "Add option" and provides accessible text
const active = this.menu.querySelector('[data-select-active]');
const active = this.menu && this.menu.querySelector('[data-select-active]');
if (active && active.hasAttribute('data-terra-select-add-option')) {
const display = active.querySelector('[data-terra-add-option]') ? active.querySelector('[data-terra-add-option]').innerText : null;
visuallyHiddenComponent.current.innerText = display;
if (hasNoResults && !this.state.closedViaKeyEvent) {
this.updateNoResultsScreenReader(display);
} else {
visuallyHiddenComponent.current.innerText = display;
}
}

const optGroupElement = MenuUtil.getOptGroupElement(this.props.children, this.state.active);
Expand Down
17 changes: 17 additions & 0 deletions packages/terra-form-select/src/search/Frame.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ const propTypes = {
* Callback function to reset input value after search
*/
resetComboboxValue: PropTypes.func,
/**
* Whether a clear option is available to clear the selection, will use placeholder text if provided.
*/
allowClear: PropTypes.bool,
};

const defaultProps = {
Expand All @@ -139,6 +143,7 @@ const defaultProps = {
totalOptions: undefined,
value: undefined,
inputId: undefined,
allowClear: false,
};

/* This rule can be removed when eslint-plugin-jsx-a11y is updated to ~> 6.0.0 */
Expand Down Expand Up @@ -253,6 +258,7 @@ class Frame extends React.Component {
* @param {event} event - The onKeyDown event.
*/
handleKeyDown(event) {
const { intl } = this.props;
const { keyCode, target } = event;

if (keyCode === KeyCode.KEY_SPACE && target !== this.input) {
Expand All @@ -275,6 +281,17 @@ class Frame extends React.Component {
searchValue: '',
});
event.stopPropagation();
} else if (keyCode === KeyCode.KEY_ESCAPE && this.props.allowClear) {
this.hasEscPressed = false;
if (this.props.resetComboboxValue) {
this.props.resetComboboxValue();
}
this.setState({
hasSearchChanged: false,
searchValue: '',
});
this.visuallyHiddenComponent.current.innerText = intl.formatMessage({ id: 'Terra.form.select.selectCleared' });
event.stopPropagation();
}
}

Expand Down
3 changes: 1 addition & 2 deletions packages/terra-form-select/src/search/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as KeyCode from 'keycode-js';
import ClearOption from '../shared/_ClearOption';
import NoResults from '../shared/_NoResults';
import MenuUtil from '../shared/_MenuUtil';
import SharedUtil from '../shared/_SharedUtil';
import styles from '../shared/_Menu.module.scss';

const cx = classNamesBind.bind(styles);
Expand Down Expand Up @@ -214,7 +213,7 @@ class Menu extends React.Component {
results in reading the display text followed by reading the aria-live message which is
the display text + 'selected'
*/
if (!SharedUtil.isSafari()) {
if (option.props.display !== this.props.input.placeholder) {
this.props.visuallyHiddenComponent.current.innerText = `${option.props.display} ${selectedTxt}`;
}
onSelect(option.props.value, option);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ exports[`Frame should render a clear option 1`] = `

exports[`Frame should render a combobox variant 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -382,6 +383,7 @@ exports[`Frame should render a combobox variant 1`] = `

exports[`Frame should render a combobox variant with a placeholder 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -531,6 +533,7 @@ exports[`Frame should render a default variant with a placeholder 1`] = `

exports[`Frame should render a disabled combobox variant 1`] = `
<Frame
allowClear={false}
disabled={true}
intl={
Object {
Expand Down Expand Up @@ -641,6 +644,7 @@ exports[`Frame should render a disabled multiple variant 1`] = `

exports[`Frame should render a disabled search variant 1`] = `
<Frame
allowClear={false}
disabled={true}
intl={
Object {
Expand Down Expand Up @@ -862,6 +866,7 @@ exports[`Frame should render a required default variant 1`] = `

exports[`Frame should render a search variant 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -899,6 +904,7 @@ exports[`Frame should render a search variant 1`] = `

exports[`Frame should render a search variant with a placeholder 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -1012,6 +1018,7 @@ exports[`Frame should render a tag variant with a placeholder 1`] = `

exports[`Frame should render an incomplete combobox variant 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -1122,6 +1129,7 @@ exports[`Frame should render an incomplete multiple variant 1`] = `

exports[`Frame should render an incomplete search variant 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -1196,6 +1204,7 @@ exports[`Frame should render an incomplete tag variant 1`] = `

exports[`Frame should render an invalid combobox variant 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down Expand Up @@ -1306,6 +1315,7 @@ exports[`Frame should render an invalid multiple variant 1`] = `

exports[`Frame should render an invalid search variant 1`] = `
<Frame
allowClear={false}
disabled={false}
intl={
Object {
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "{text} Elemente begrenzen.",
"Terra.form.select.maxSelectionOption": "Maximale Anzahl an ausgewählten {text} Elementen",
"Terra.form.select.noResults": "Keine Übereinstimmungen für \"{text}\"",
"Terra.form.select.selectedText": "Ausgewählt: {text}. ({index} von {totalOptions})",
"Terra.form.select.activeOption": "{text} ({index} von {totalOptions}).",
"Terra.form.select.selectedText": "Ausgewählt: {text}. {index} von {totalOptions}",
"Terra.form.select.activeOption": "{text} {index} von {totalOptions}.",
"Terra.form.select.of": "von",
"Terra.form.select.unselectedText": "{text} nicht ausgewählt",
"Terra.form.select.selected": "Ausgewählt",
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/en-AU.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "Limit {text} items.",
"Terra.form.select.maxSelectionOption": "Maximum number of {text} items selected",
"Terra.form.select.ariaLabel": "Search",
"Terra.form.select.selectedText": "{text} Selected. ({index} of {totalOptions}).",
"Terra.form.select.activeOption": "{text} ({index} of {totalOptions}).",
"Terra.form.select.selectedText": "{text} Selected. {index} of {totalOptions}.",
"Terra.form.select.activeOption": "{text} {index} of {totalOptions}.",
"Terra.form.select.of": "of",
"Terra.form.select.unselectedText": "{text} Unselected.",
"Terra.form.select.selected": "Selected.",
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/en-CA.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "Limit {text} items.",
"Terra.form.select.maxSelectionOption": "Maximum number of {text} items selected",
"Terra.form.select.ariaLabel": "Search",
"Terra.form.select.selectedText": "{text} Selected. ({index} of {totalOptions}).",
"Terra.form.select.activeOption": "{text} ({index} of {totalOptions}).",
"Terra.form.select.selectedText": "{text} Selected. {index} of {totalOptions}.",
"Terra.form.select.activeOption": "{text} {index} of {totalOptions}.",
"Terra.form.select.of": "of",
"Terra.form.select.unselectedText": "{text} Unselected.",
"Terra.form.select.selected": "Selected.",
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "Limit {text} items.",
"Terra.form.select.maxSelectionOption": "Maximum number of {text} items selected",
"Terra.form.select.ariaLabel": "Search",
"Terra.form.select.selectedText": "{text} Selected. ({index} of {totalOptions}).",
"Terra.form.select.activeOption": "{text} ({index} of {totalOptions}).",
"Terra.form.select.selectedText": "{text} Selected. {index} of {totalOptions}.",
"Terra.form.select.activeOption": "{text} {index} of {totalOptions}.",
"Terra.form.select.of": "of",
"Terra.form.select.unselectedText": "{text} Unselected",
"Terra.form.select.selected": "Selected.",
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "Limit {text} items.",
"Terra.form.select.maxSelectionOption": "Maximum number of {text} items selected",
"Terra.form.select.ariaLabel": "Search",
"Terra.form.select.selectedText": "{text} Selected. ({index} of {totalOptions}).",
"Terra.form.select.activeOption": "{text} ({index} of {totalOptions}).",
"Terra.form.select.selectedText": "{text} Selected. {index} of {totalOptions}.",
"Terra.form.select.activeOption": "{text} {index} of {totalOptions}.",
"Terra.form.select.of": "of",
"Terra.form.select.unselectedText": "{text} Unselected.",
"Terra.form.select.selected": "Selected.",
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "Limit {text} items.",
"Terra.form.select.maxSelectionOption": "Maximum number of {text} items selected",
"Terra.form.select.ariaLabel": "Search",
"Terra.form.select.selectedText": "{text} Selected. ({index} of {totalOptions}).",
"Terra.form.select.activeOption": "{text} ({index} of {totalOptions}).",
"Terra.form.select.selectedText": "{text} Selected. {index} of {totalOptions}.",
"Terra.form.select.activeOption": "{text} {index} of {totalOptions}.",
"Terra.form.select.of": "of",
"Terra.form.select.unselectedText": "{text} Unselected.",
"Terra.form.select.selected": "Selected.",
Expand Down
4 changes: 2 additions & 2 deletions packages/terra-form-select/translations/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"Terra.form.select.maxSelectionHelp": "Limite los elementos {text}.",
"Terra.form.select.maxSelectionOption": "Número máximo de elementos {text} seleccionados",
"Terra.form.select.ariaLabel": "Buscar",
"Terra.form.select.selectedText": "Se seleccionó {text}. ({index} de {totalOptions}).",
"Terra.form.select.activeOption": "{text} ({index} de {totalOptions}).",
"Terra.form.select.selectedText": "Se seleccionó {text}. {index} de {totalOptions}.",
"Terra.form.select.activeOption": "{text} {index} de {totalOptions}.",
"Terra.form.select.of": "de",
"Terra.form.select.unselectedText": "{text} sin seleccionar.",
"Terra.form.select.selected": "Seleccionado.",
Expand Down
Loading
Loading