downshift-js / downshift

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.
http://downshift-js.com/
MIT License
12.08k stars 928 forks source link

Keyboard navigation from toggle with menu closed highlights disabled items #1466

Closed Rendez closed 1 year ago

Rendez commented 1 year ago

Relevant code or config

useSelect(
// [...]
stateReducer({highlightedIndex}, {changes, type, altKey}) {
                let highlightableIndex = changes.highlightedIndex!;
                switch (type) {
                    case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowDown:
                    case useSelect.stateChangeTypes.ToggleButtonKeyDownHome: {
                        /*
                         * Fixes highlighting disabled items:
                         *  1. it's possible to open/close the menu without highlighting with the `alt` key.
                         *  2. if state.selectedItem == null, then selectedIndex is set to -1
                         */
                        if (type === useSelect.stateChangeTypes.ToggleButtonKeyDownHome || !altKey) {
                            // when selectedItem == null and !isOpen, Downshift.js doesn't check if the item is disabled like it does normally.
                            while (items[highlightableIndex].disabled) {
                                if (++highlightableIndex >= items.length) {
                                    highlightableIndex = -1;
                                    break;
                                }
                            }
                        }
                        return {
                            ...changes,
                            highlightedIndex: highlightableIndex
                        };
                    }
                    case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowUp:
                    case useSelect.stateChangeTypes.ToggleButtonKeyDownEnd: {
                        // when selectedItem == null and !isOpen, Downshift.js doesn't check if the item is disabled like it does normally.
                        while (items[highlightableIndex].disabled) {
                            if (--highlightableIndex < 0) {
                                break;
                            }
                        }

                        return {
                            ...changes,
                            highlightedIndex: highlightableIndex
                        };
                    }
                    default:
                        return changes;
                }
            },
// [...]
)

Same goes for useCombobox() with InputKeyDownArrowDown/InputKeyDownArrowUp.

What you did:

Produce a ToggleButtonKeyDownHome event with keyboard navigation while the menu is closed and obviously while focused on the toggle button. If the first item or item-to-be-highlighted is disabled, it doesn't skip that item but highlights it. This behavior doesn't happen when the menu is already opened.

What happened:

Disabled items get highlighted.

Reproduction repository:

Problem description:

Suggested solution:

silviuaavram commented 1 year ago

We know about the problem. It's because the current API provides disabled as a prop to getItemProps which is called with the menu open. WIP to replace this with a hook prop isItemDisabled.

Will close this as we are tracking it here https://github.com/downshift-js/downshift/issues/1176. I am working to make this possible in v8, which I plan to release in about a month. Right now I am piling up breaking changes for it.

Let me know if I can assist further.

Rendez commented 1 year ago

Makes total sense, also to remove the disabled HTML dom attribute, thanks!