Skip to content

Commit

Permalink
Improve screen reader accessibility
Browse files Browse the repository at this point in the history
Ramp task: https://ramp.accessibleweb.com/app/websites/83ca83c6265c/tasks/2787246f7ae2
This allows following WAI-ARIA best practices  for combobox elements -
https://www.w3.org/WAI/ARIA/apg/patterns/combobox/.
Changes in this PR mostly correspond to the patw0929#405

Although most of the best practices to improve accessibility were
implemented, the role="combobox" was not added neither the to
the number input (because the input doesn't show/hide the dropdown),
nor to the country code button.
Looks like OSx VoiceOver announces everything correctly.

Changes:
- Add role="listbox" to the countries list
- Refer the countries list by its Id in the country code button
  via aria-controls and aria-owns
- Add role="option" to each country list option
- Use aria-expanded on the country code button element
- Refer focused option by its Id in the country code button element
  via aria-activedescendant to read focused option while navigating
  by keyboard
- Add aria-selected="true" to the selected list option
- Set aria-autocomplete="none" to the country code button element
  since displayed options do not depend on the number input value
- Allow passing arbitrary props to the country code button element via
  flagDropdownProps prop (for example, "aria-labelledby" property
  can be passed)

Updates dist files (built with node 8.17.0)
  • Loading branch information
VladyslavBondarenko committed Nov 8, 2022
1 parent cb3180a commit 11ade03
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 10 deletions.
2 changes: 1 addition & 1 deletion dist/main.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/main.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions example/example.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion example/main.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions example/main.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/components/CountryList.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class CountryList extends Component {
};
const countryClass = classNames(countryClassObj);
const keyPrefix = isPreferred ? 'pref-' : '';
const isSelected = this.props.selectedCountryCode === country.iso2;

return (
<li
Expand All @@ -75,6 +76,10 @@ class CountryList extends Component {
data-country-code={ country.iso2 }
onMouseOver={ this.props.isMobile ? null : this.handleMouseOver }
onClick={ partial(this.setFlag, country.iso2) }
role="option"
aria-selected={ isSelected }
id={ this.props.getItemId(actualIndex) }
tabIndex="-1"
>
<div
ref={ (selectedFlag) => { this.selectedFlag = selectedFlag; } }
Expand Down Expand Up @@ -125,6 +130,8 @@ class CountryList extends Component {
<ul
ref={ (listElement) => { this.listElement = listElement; } }
className={ className }
id={ this.props.countryListId }
role="listbox"
>
{ preferredOptions }
{ divider }
Expand All @@ -144,6 +151,9 @@ CountryList.propTypes = {
changeHighlightCountry: PropTypes.func,
showDropdown: PropTypes.bool,
isMobile: PropTypes.bool,
selectedCountryCode: PropTypes.string.isRequired,
countryListId: PropTypes.string.isRequired,
getItemId: PropTypes.func.isRequired,
};

export default CountryList;
18 changes: 18 additions & 0 deletions src/components/FlagDropDown.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import CountryList from './CountryList';
import RootModal from './RootModal';

class FlagDropDown extends Component {
getItemId = (id) => `intl-tel-item-${this.props.uniqueId}-${id}`

render() {
const flagClassObj = {
'iti-flag': true,
Expand Down Expand Up @@ -35,6 +37,8 @@ class FlagDropDown extends Component {

let genCountryList = () => '';

const countryListId = `intl-tel-countries-list-${this.props.uniqueId}`;

if (this.props.dropdownContainer) {
if (this.props.showDropdown) {
genCountryList = () =>
Expand All @@ -51,6 +55,9 @@ class FlagDropDown extends Component {
preferredCountries={ this.props.preferredCountries }
highlightedCountry={ this.props.highlightedCountry }
changeHighlightCountry={ this.props.changeHighlightCountry }
selectedCountryCode={ this.props.countryCode }
countryListId={ countryListId }
getItemId={ this.getItemId }
/>
</RootModal>;
}
Expand All @@ -68,6 +75,9 @@ class FlagDropDown extends Component {
preferredCountries={ this.props.preferredCountries }
highlightedCountry={ this.props.highlightedCountry }
changeHighlightCountry={ this.props.changeHighlightCountry }
selectedCountryCode={ this.props.countryCode }
countryListId={ countryListId }
getItemId={ this.getItemId }
/>;
}

Expand All @@ -77,11 +87,17 @@ class FlagDropDown extends Component {
className="flag-container"
>
<div
{ ...this.props.flagDropdownProps }
className="selected-flag"
tabIndex={ this.props.allowDropdown ? '0' : '' }
onClick={ this.props.clickSelectedFlag }
onKeyDown={ this.props.handleSelectedFlagKeydown }
title={ this.props.titleTip }
aria-controls={ countryListId }
aria-owns={ countryListId }
aria-autocomplete="none"
aria-activedescendant={ this.getItemId(this.props.highlightedCountry) }
aria-expanded={ this.props.showDropdown }
>
<div className={ flagClass } />
{ genSelectedDialCode() }
Expand Down Expand Up @@ -112,6 +128,8 @@ FlagDropDown.propTypes = {
changeHighlightCountry: PropTypes.func,
titleTip: PropTypes.string,
refCallback: PropTypes.func.isRequired,
uniqueId: PropTypes.string.isRequired,
flagDropdownProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
};

export default FlagDropDown;
7 changes: 7 additions & 0 deletions src/components/IntlTelInputApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class IntlTelInputApp extends Component {
this.handleInputChange = this.handleInputChange.bind(this);
this.changeHighlightCountry = this.changeHighlightCountry.bind(this);
this.formatNumber = this.formatNumber.bind(this);
// using a unique Id not to interfere with other instances of the tel input
this.uniqueId = Math.random().toString(36).substring(2);
}

componentDidMount() {
Expand Down Expand Up @@ -1151,6 +1153,8 @@ class IntlTelInputApp extends Component {
preferredCountries={ this.preferredCountries }
highlightedCountry={ this.state.highlightedCountry }
titleTip={ titleTip }
flagDropdownProps={ this.props.flagDropdownProps }
uniqueId={ this.uniqueId }
/>
<TelInput
refCallback={ this.setTelRef }
Expand Down Expand Up @@ -1210,6 +1214,7 @@ IntlTelInputApp.propTypes = {
style: StylePropTypes,
useMobileFullscreenDropdown: PropTypes.bool,
telInputProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
flagDropdownProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
format: PropTypes.bool,
};

Expand Down Expand Up @@ -1261,6 +1266,8 @@ IntlTelInputApp.defaultProps = {
autoComplete: 'off',
// pass through arbitrary props to the tel input element
telInputProps: {},
// pass through arbitrary props to the flag dropdown element
flagDropdownProps: {},
// always format the number
format: false,
};
Expand Down
2 changes: 1 addition & 1 deletion src/styles/intlTelInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ $mobilePopupMargin: 30px;
// full height
top: 0;
bottom: 0;
right: 0;
left: 0;
// prevent the highlighted child from overlapping the input border
padding: $borderWidth;
}
Expand Down

0 comments on commit 11ade03

Please sign in to comment.