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

Commit

Permalink
[terra-form-select] - Fixed SR issues (#3985)
Browse files Browse the repository at this point in the history
Co-authored-by: MadanKumarGovindaswamy <[email protected]>
Co-authored-by: Supreeth <[email protected]>
  • Loading branch information
3 people authored Nov 30, 2023
1 parent b4468b1 commit bb1c3b7
Show file tree
Hide file tree
Showing 26 changed files with 110 additions and 62 deletions.
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
19 changes: 19 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 Expand Up @@ -646,6 +663,8 @@ class Frame extends React.Component {
required,
totalOptions,
value,
allowClear,
resetComboboxValue,
...customProps
} = this.props;

Expand Down
40 changes: 19 additions & 21 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,
};

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}`;
}, 1000);
}

Expand All @@ -333,7 +326,7 @@ class Menu extends React.Component {
visuallyHiddenComponent,
} = 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 +339,22 @@ 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;
// To match visual label and the text exposed by screen reader
visuallyHiddenComponent.current.innerText = clearOptionDisplay;
}
}

// 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 All @@ -376,7 +374,7 @@ class Menu extends React.Component {
if (element.props.display === '' && element.props.value === '') {
// Used for case where users selects clear option and opens
// dropdown again and navigates to clear option
visuallyHiddenComponent.current.innerText = clearSelectTxt;
visuallyHiddenComponent.current.innerText = clearOptionDisplay;
} else if (this.isActiveSelected()) {
visuallyHiddenComponent.current.innerText = intl.formatMessage({ id: 'Terra.form.select.selectedText' }, { text: displayText, index, totalOptions });
} else {
Expand Down
19 changes: 19 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 Expand Up @@ -634,6 +651,8 @@ class Frame extends React.Component {
required,
totalOptions,
value,
allowClear,
resetComboboxValue,
...customProps
} = this.props;

Expand Down
9 changes: 3 additions & 6 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 Expand Up @@ -303,8 +302,6 @@ class Menu extends React.Component {
visuallyHiddenComponent,
} = this.props;

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

if (this.menu !== null && this.state.active !== null) {
this.menu.setAttribute('aria-activedescendant', `terra-select-option-${this.state.active}`);
}
Expand All @@ -318,7 +315,7 @@ class Menu extends React.Component {
if (clearOptionDisplay) {
const active = this.menu.querySelector('[data-select-active]');
if (active && active.hasAttribute('data-terra-select-clear-option')) {
visuallyHiddenComponent.current.innerText = clearSelectTxt;
visuallyHiddenComponent.current.innerText = clearOptionDisplay;
}
}

Expand All @@ -339,7 +336,7 @@ class Menu extends React.Component {
if (element.props.display === '' && element.props.value === '') {
// Used for case where users selects clear option and opens
// dropdown again and navigates to clear option
visuallyHiddenComponent.current.innerText = clearSelectTxt;
visuallyHiddenComponent.current.innerText = clearOptionDisplay;
} else if (this.isActiveSelected()) {
visuallyHiddenComponent.current.innerText = intl.formatMessage({ id: 'Terra.form.select.selectedText' }, { text: displayText, index, totalOptions });
} else {
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
Loading

0 comments on commit bb1c3b7

Please sign in to comment.