-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom select control: don't announce external value changes #22815
Merged
Merged
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e8909cf
Don't announce external value change
tellthemachines d8b5ed7
Correct aria attributes
tellthemachines 6c6297e
Improve value equality checking.
tellthemachines cd87011
Selected item doesn't always exist
tellthemachines e1c1869
Check if value exists and add default
tellthemachines 1ae478b
Fix initial selection
tellthemachines 914d485
Memoize getSelectOptions instead
tellthemachines 5e980b0
Switch to useMemo
tellthemachines File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -22,6 +22,12 @@ const stateReducer = ( | |||||
{ selectedItem }, | ||||||
{ type, changes, props: { items } } | ||||||
) => { | ||||||
const selectedItemIndex = items.findIndex( | ||||||
( item ) => item.key === selectedItem?.key | ||||||
); | ||||||
if ( changes.highlightedIndex < 0 ) { | ||||||
changes.highlightedIndex = selectedItemIndex; | ||||||
} | ||||||
switch ( type ) { | ||||||
case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowDown: | ||||||
// If we already have a selected item, try to select the next one, | ||||||
|
@@ -31,7 +37,7 @@ const stateReducer = ( | |||||
items[ | ||||||
selectedItem | ||||||
? Math.min( | ||||||
items.indexOf( selectedItem ) + 1, | ||||||
selectedItemIndex + 1, | ||||||
items.length - 1 | ||||||
) | ||||||
: 0 | ||||||
|
@@ -44,7 +50,7 @@ const stateReducer = ( | |||||
selectedItem: | ||||||
items[ | ||||||
selectedItem | ||||||
? Math.max( items.indexOf( selectedItem ) - 1, 0 ) | ||||||
? Math.max( selectedItemIndex - 1, 0 ) | ||||||
: items.length - 1 | ||||||
], | ||||||
}; | ||||||
|
@@ -58,8 +64,9 @@ export default function CustomSelectControl( { | |||||
label, | ||||||
options: items, | ||||||
onChange: onSelectedItemChange, | ||||||
value: _selectedItem, | ||||||
value, | ||||||
} ) { | ||||||
const valueIndex = items.findIndex( ( item ) => item.key === value?.key ); | ||||||
const { | ||||||
getLabelProps, | ||||||
getToggleButtonProps, | ||||||
|
@@ -69,14 +76,12 @@ export default function CustomSelectControl( { | |||||
highlightedIndex, | ||||||
selectedItem, | ||||||
} = useSelect( { | ||||||
initialSelectedItem: items[ 0 ], | ||||||
initialSelectedItem: items[ valueIndex >= 0 ? valueIndex : 0 ], | ||||||
items, | ||||||
itemToString, | ||||||
onSelectedItemChange, | ||||||
selectedItem: _selectedItem, | ||||||
stateReducer, | ||||||
} ); | ||||||
|
||||||
const menuProps = getMenuProps( { | ||||||
className: 'components-custom-select-control__menu', | ||||||
'aria-hidden': ! isOpen, | ||||||
|
@@ -85,13 +90,12 @@ export default function CustomSelectControl( { | |||||
// fully ARIA compliant. | ||||||
if ( | ||||||
menuProps[ 'aria-activedescendant' ] && | ||||||
menuProps[ 'aria-activedescendant' ].slice( | ||||||
0, | ||||||
'downshift-null'.length | ||||||
) === 'downshift-null' | ||||||
menuProps[ 'aria-hidden' ] === true | ||||||
) { | ||||||
delete menuProps[ 'aria-activedescendant' ]; | ||||||
} | ||||||
const selectedItemValue = value ? value : selectedItem; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
return ( | ||||||
<div | ||||||
className={ classnames( | ||||||
|
@@ -122,7 +126,7 @@ export default function CustomSelectControl( { | |||||
isSmall: true, | ||||||
} ) } | ||||||
> | ||||||
{ itemToString( selectedItem ) } | ||||||
{ itemToString( selectedItemValue ) } | ||||||
<Icon | ||||||
icon={ chevronDown } | ||||||
className="components-custom-select-control__button-icon" | ||||||
|
@@ -131,7 +135,7 @@ export default function CustomSelectControl( { | |||||
<ul { ...menuProps }> | ||||||
{ isOpen && | ||||||
items.map( ( item, index ) => ( | ||||||
// eslint-disable-next-line react/jsx-key | ||||||
// eslint-disable-next-line react/jsx-key, jsx-a11y/role-supports-aria-props | ||||||
<li | ||||||
{ ...getItemProps( { | ||||||
item, | ||||||
|
@@ -146,9 +150,10 @@ export default function CustomSelectControl( { | |||||
} | ||||||
), | ||||||
style: item.style, | ||||||
'aria-selected': index === valueIndex, | ||||||
} ) } | ||||||
> | ||||||
{ item === selectedItem && ( | ||||||
{ item.key === selectedItemValue.key && ( | ||||||
<Icon | ||||||
icon={ check } | ||||||
className="components-custom-select-control__item-icon" | ||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Downshift needs to be aware when the item really does change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid that's the one thing we can't do, because it causes Downshift to announce changes to the selection whenever the component re-renders. Apart from the minor issue of the highlight not always matching the selected item (which we may be able to fix by leveraging
:focus
styles), is there anything else that won't work with these changes? Otherwise I think we'll have to use a different library, or implement our own solution.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the component rerenders and
value
is referentially equal, then it shouldn't be announcing anything, right? I think the issue is that the value is being recreated on render.Have you tried memoizing
getSelectOptions
in the font size picker?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I haven't. But that still won't solve the problem of changes being announced whenever the value changes outside of the component, e.g. if I select a random value in the font size number input, it announces "custom has been selected".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would only happen once when the value is switched to custom. Is that still worth avoiding?
Not passing Downshift the value means that when it changes elsewhere, Downshift won't know about it. If I understand how it works correctly, this means that all the prop getters will return props based on an outdated selected item. E.g., entering a custom value would change the rendered label because you are getting the value manually for that. Still, all the prop getters won't know about it internally so that they can return the relevant props.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's why the highlighting isn't working correctly when the value is updated elsewhere. With all the other props, I've managed to work around.
I'll try memoizing and if it solves the main issue, it's probably good enough for now. But we should revisit this later, as having that unexpected announcement when using the number input isn't really great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can add a prop to "silence" the announcement in those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have anything specific in mind? There doesn't seem to be a way of silencing the announcement in Downshift, and I can only think of extremely dodgy solutions to do it on our side 😅 (like removing Downshift's
aria-live
container or trying to override it with an emptyspeak
- not sure that'd even work btw)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: memoizing
getSelectOptions
works for the announcement when switching editor modes, so that's the main issue solved ✅There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my line of thinking as well, haha.
Awesome!