-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
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
[Select]Bug when the first child is a ListSubheader #27299
[Select]Bug when the first child is a ListSubheader #27299
Conversation
…tion as selected if the first child is a ListSubheader
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.
Could you please add some test to check the various branches of the code you introduced? It's not clear what we need all this added code for.
A demo would also be nice for this feature. Especially for manual testing.
I will try to explain the code I've made |
return true; | ||
}; | ||
|
||
if (child.props.value === undefined) { |
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.
This returns a different object when the option is a ListSubheader. The role option is because the role listbox of the Menu needs that the children have the role option, but when the option is not a selectable option the aria-selected should be omitted.
@@ -385,7 +399,7 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { | |||
} | |||
}, | |||
role: 'option', | |||
selected, | |||
selected: index === 1 && arr[0].props.value === undefined ? hasOptionSelected() : 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.
Here when the first child is a ListSubheader it sets the selected true to the second option, to gains the focus when the list of options is open, with the tabindex = 0, to the arrow down works.
@@ -369,6 +369,20 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { | |||
foundMatch = true; | |||
} | |||
|
|||
const hasOptionSelected = () => { |
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.
And here, when you select an option don't need more to set the second option as selected true to gain the focus.
codesandbox example |
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.
Hi @DouglasPds. I've taken a look at your implementation and found one issue - the problem remains if the second element is also a header or a disabled MenuItem. In such cases, instead of selecting the second element, we'd have to look for the first selectable one.
@michaldudak you're right, this creates another problem, I will try this approach. |
…to the new mui-material folder
@DouglasPds you may be interested to know that we've recently created an unstyled select (https://mui.com/components/selects/#unstyled) that does not suffer from this issue. If you're still having this problem in your application, you may consider evaluating the SelectUnstyled instead. |
@michaldudak oh nice, this issue doesn't occur. I will try them. And I find a solution to use with the Select, which I will share here for you to see and evaluate. |
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.
Sorry for the delay. This solution does work better, but I think the code may be improved a bit.
if (value) { | ||
return 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.
This could be placed before arr.find(...)
as it doesn't use its result.
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.
This looks good! Thanks for your work!
@@ -405,7 +425,10 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { | |||
} | |||
}, | |||
role: 'option', | |||
selected, | |||
selected: | |||
arr[0].props.value === undefined || arr[0].props.disabled === true |
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.
This assumes that the first child is always a valid React component.
This leads to crash if the first child is of different type. Here is a first report
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.
Yes, I tried here and when the first child is a number, string, NaN, it crashes. The first solution that works was to put optional chaining to check the values after the props. Here where the value is passed to selected and inside the function isFirstSelectableElement.
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.
arr[0].props?.value === undefined || arr[0].props?.disabled === true
You are suggesting this, right? As long as this doesn't break the logic of the fix this PR originally contained, I am fine with that. Will you be willing to ensure that and open a PR with the change?
return selected; | ||
} | ||
const firstSelectableElement = arr.find( | ||
(item) => item.props.value !== undefined && item.props.disabled !== true, |
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.
@DouglasPds Let's ensure that item
is a valid React component here as well to prevent potential crash.
@michaldudak How in the world could you approve this PR because it's not resolving the root cause of keyboard interactivity issue but invalidly selects an item that shouldn't be selected in the first place (just to mitigate the keyboard bug)? This code should be reverted and an actual solution should be made. The problem seems to be that the code puts the focus on the wrong item. That code should be changed to resolve the bug and not as it was done here. |
Checking if the child is a ListSubheader to return a different object in React.cloneElement.
And setting the second child as selected when the first child is a ListSubheader, to can use the arrow to navigate the options.
And when an option is selected has no more need to set the second option as selected.
#26790