useSelect - NVDA keeps reading out the selected value continuously, even when neither focus is set on the dropdown, nor the dropdown value has changed
downshift version: "^8.1.0"
Tools used: NVDA
Relevant code or config
import React, { forwardRef } from 'react';
import _isEmpty from 'lodash/isEmpty';
import { useSelect } from 'downshift';
import { string, arrayOf, shape, func } from 'prop-types';
import '../Dropdown.scss';
import CheckMarkIcon from '../../../resources/images/checkmark-black.svg';
import { classNames } from '../../../site/helper/utils';
const itemToString = item => {
return item ? item.title : '';
};
const Downshift = forwardRef(
(
{
className,
list,
label,
field,
disabled,
selectedValue,
handleOnChange,
errorMessage,
required,
id,
dropdownButtonClassName,
...rest
},
ref
) => {
const selectedItem = list.find(item => item.value === selectedValue?.value) || null;
const { isOpen, getToggleButtonProps, getLabelProps, getMenuProps, highlightedIndex, getItemProps } = useSelect(
{
items: list,
selectedItem,
onSelectedItemChange: ({ selectedItem }) => handleOnChange(selectedItem),
itemToString,
}
);
const showErrorMsg = !!errorMessage;
const dropdownList = (
<ul
className={classNames( className, !isOpen && 'hidden')}
{...getMenuProps()}
>
{list.map((item, index) => (
<li
key={item.value}
type="button"
className={classNames(
(selectedItem?.value === item.value || highlightedIndex === index) && 'selected'
)}
{...getItemProps({ item, index })}
data-testid={`dropdown-item-${item.value}`}
>
{item.title}
{selectedItem?.value === item.value && (
<img src={CheckMarkIcon} alt="ArrowUpIcon" width={12} height={9} />
)}
</li>
))}
</ul>
);
const dropdownButton = (
<div
tabIndex={0}
type="button"
className={dropdownButtonClassName}
disabled={disabled}
{...getToggleButtonProps({
ref,
})}
aria-required={required}
aria-invalid={!!errorMessage}
data-testid="dropdown-button"
{...rest}
id={id}
>
{!_isEmpty(selectedItem) ? (
<>
{
<label className="dd-header-title focused" {...getLabelProps()}>
{label}
</label>
}
<div className="dd-header-title" data-testid="dropdown-selected-item">
{itemToString(selectedItem)}
</div>
</>
) : (
<label className="dd-header-title placeholder" {...getLabelProps()}>
{label}
</label>
)}
</div>
);
return (
<div id={field} className={className}>
{dropdownButton}
{dropdownList}
</div>
);
}
);
Downshift.propTypes = {
list: arrayOf(
shape({
title: string,
value: string,
})
).isRequired,
selectedValue: shape({ title: string, value: string }),
handleOnChange: func.isRequired,
};
Downshift.defaultProps = {
list: [],
selectedValue: { title: '', value: '' },
};
export default Downshift;
Issue faced:
I have this dropdown used inside a Form that contains other input text boxes. When opening form in edit mode, even though focus is not set on the dropdown, it reads out "ABC has been selected", [ABC being here the value set] I fixed this in Voiceover by programmatically setting focus to first field in the form which happens be an input box, so that way Voiceover is now reading out the 1st focused input and isn't reading dropdown status anymore.
But on NVDA, even after this change, it is now first reading out the programmatically focused input, and again the dropdown value, even though no focus is set on dropdown.
Along with this, when i submit the form, by clicking submit button, it keeps on shouting "ABC has been selected", continuously 2-3 times, until the form is closed, even though value of dropdown hasn't changed OR the focus hasn't been set on dropdown.
Hey @Praneetha-CK any change you discovered the issue that causes the NVDA double reading? Let me know what we can improve, thanks!
@silviuaavram The getA11yStatusMessage in version 8, was causing NVDA to read out repeatedly when parent component was re-rendering even the dropdown selected value wasn't changing.
Plus even on voice over, as soon as I opened my form that contained the dropdown, even if focus was set on some other field, the dropdown value was called out by screen reader.
So I saw in the migration guide to v9 - https://github.com/downshift-js/downshift/blob/master/src/hooks/MIGRATION_V9.md#geta11ystatusmessage that for getA11yStatusMessage
there is no default function provided, so you will not get any aria-live message anymore if you don't provide the prop directly to the hooks.
So I upgraded to v9 to fix my issue!