Skip to content

Commit

Permalink
Try to fix lost selection message with custom getA11ySelectionMessage fn
Browse files Browse the repository at this point in the history
Unfortunately it does not work because of the debouncing done inside Downshift. [Here's my explanation](downshift-js/downshift#1244):

> For what it's worth, I have just come across an issue with the existing a11y-message implementation and specifically [how updateA11yStatus is debounced](https://github.com/downshift-js/downshift/blob/master/src/hooks/utils.js#L85). In my application, I have one dropdown on a page that modifies the available options for another dropdown further down the page. (This isn't ideal from an accessibility standpoint, but [the WCAG docs](https://www.w3.org/WAI/WCAG21/Understanding/on-input) do say we can do that if we warn users that it's going to happen, and it will take a while before we can design out all the instances of it across multiple applications.) I wanted to write a custom `getA11ySelectionMessage` function that would determine when it's one of those incidentally changed dropdowns rather than the one the user is interacting with so that only the desired `${itemToString(selectedItem)} has been selected.` gets printed to the a11y-message div. However, due to the debouncing, none of the other `getA11ySelectionMessage` functions even get called except for the last one, which is the one I don't want to announce. If Downshift called those `getA11yMessage` functions instead of passing them to `updateA11yStatus` and then ignored them if they returned, say, `undefined`, this plan would work. But right now the agency from each individual dropdown is taken from it to determine whether its status message should be announced. It's getting lost in the debounce.
  • Loading branch information
pwolfert committed Jul 28, 2023
1 parent 4d78e6b commit ce3aa36
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
11 changes: 10 additions & 1 deletion packages/design-system/src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useSelect, UseSelectProps, UseSelectStateChangeOptions } from 'downshif
import { isOptGroupArray, parseChildren, validateProps } from './utils';
import { uniqueId } from 'lodash';
import useHighlightStatusMessageFn from './useHighlightStatusMessageFn';
import useIgnoreRefillSelectionMessage from './useIgnoreRefillSelectionMessageFn';

export type DropdownSize = 'small' | 'medium';
export type DropdownValue = number | string;
Expand Down Expand Up @@ -87,6 +88,11 @@ export interface BaseDropdownProps extends Omit<FormFieldProps, 'id'> {
* aria-live during certain interactions. [Read more on downshift docs.](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#geta11ystatusmessage)
*/
getA11yStatusMessage?: UseSelectProps<any>['getA11yStatusMessage'];
/**
* Customize the default status messages announced to screen reader users via
* aria-live when a selection is made. [Read more on downshift docs.](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#geta11yselectionmessage)
*/
getA11ySelectionMessage?: UseSelectProps<any>['getA11ySelectionMessage'];
}

type OptionsOrChildren =
Expand Down Expand Up @@ -133,6 +139,8 @@ export const Dropdown: React.FC<DropdownProps> = (props: DropdownProps) => {
defaultValue,
value,
inputRef,
getA11yStatusMessage,
getA11ySelectionMessage,
...extraProps
} = props;

Expand Down Expand Up @@ -191,7 +199,8 @@ export const Dropdown: React.FC<DropdownProps> = (props: DropdownProps) => {
menuId,
items,
itemToString,
getA11yStatusMessage: useHighlightStatusMessageFn(),
getA11yStatusMessage: useHighlightStatusMessageFn(getA11yStatusMessage),
getA11ySelectionMessage: useIgnoreRefillSelectionMessage(getA11ySelectionMessage),
onSelectedItemChange:
onChange &&
((changes: UseSelectStateChangeOptions<any>) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { A11yStatusMessageOptions, UseComboboxProps } from 'downshift';

type GetA11ySelectionMessageFn = UseComboboxProps<any>['getA11ySelectionMessage'];

// Downshift doesn't expose this one, but this is the logic from their source
const defaultGetA11ySelectionMessage: GetA11ySelectionMessageFn = ({
selectedItem,
itemToString,
}) => (selectedItem ? `${itemToString(selectedItem)} has been selected.` : '');

/**
* As our default selection message function, we want to ignore cases where the
* options for the dropdown are changing. I'm calling this "refilling" the
* "results" (what Downshift calls the options). If the application has just
* refilled the results, we probably don't want to announce the selection to
* screen readers, because it probably didn't result from the end user
* interacting with this dropdown directly. In fact, the reason I'm adding this
* code is that this message can overwrite the message from another dropdown on
* the page that we actually do want to hear. Hopefully in Downshift v9, this
* overwriting of the shared `#a11y-status-message` div [will no longer be a
* problem](https://github.com/downshift-js/downshift/issues/1244#issuecomment-1651092460).
*/
export default function useIgnoreRefillSelectionMessage(
originalGetA11ySelectionMessage: GetA11ySelectionMessageFn = defaultGetA11ySelectionMessage
): GetA11ySelectionMessageFn {
return (args: A11yStatusMessageOptions<any>) => {
if (args.previousResultCount === args.resultCount) {
return originalGetA11ySelectionMessage(args);
}
};
}

0 comments on commit ce3aa36

Please sign in to comment.