mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
fix(autocomplete): ensure focused item matches selected item after filter, selection (#5290)
* fix(autocomplete): ensure focused item matches selected item after filter, selection * chore: apply type and default value * chore: add perpose coment in updated code * test: add focuskey management testcode * docs: add changeset * docs: update changeset * chore: remove comment
This commit is contained in:
parent
d09e602b59
commit
360b2e77fd
5
.changeset/clean-seahorses-cheat.md
Normal file
5
.changeset/clean-seahorses-cheat.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@heroui/autocomplete": patch
|
||||
---
|
||||
|
||||
ensure focused item matches selected item after filter, selection (#5277)
|
||||
@ -940,3 +940,129 @@ describe("Autocomplete with React Hook Form", () => {
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("focusedKey management with selected key", () => {
|
||||
let user: UserEvent;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
it("should set focusedKey to the first non-disabled item when selectedKey is null", async () => {
|
||||
const wrapper = render(
|
||||
<Autocomplete
|
||||
aria-label="Favorite Animal"
|
||||
data-testid="autocomplete"
|
||||
disabledKeys={["penguin"]}
|
||||
label="Favorite Animal"
|
||||
>
|
||||
<AutocompleteItem key="penguin" isDisabled>
|
||||
Penguin
|
||||
</AutocompleteItem>
|
||||
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
|
||||
<AutocompleteItem key="shark">Shark</AutocompleteItem>
|
||||
</Autocomplete>,
|
||||
);
|
||||
|
||||
const autocomplete = wrapper.getByTestId("autocomplete");
|
||||
|
||||
// open the select listbox
|
||||
await user.click(autocomplete);
|
||||
|
||||
const options = wrapper.getAllByRole("option");
|
||||
|
||||
// first non-disabled item is zebra
|
||||
const optionItem = options[1];
|
||||
|
||||
expect(optionItem).toHaveAttribute("data-focus", "true");
|
||||
});
|
||||
|
||||
it("should set focusedKey to the item's key when an item is selected", async () => {
|
||||
const wrapper = render(
|
||||
<Autocomplete aria-label="Favorite Animal" data-testid="autocomplete" label="Favorite Animal">
|
||||
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
|
||||
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
|
||||
<AutocompleteItem key="shark">Shark</AutocompleteItem>
|
||||
</Autocomplete>,
|
||||
);
|
||||
|
||||
const autocomplete = wrapper.getByTestId("autocomplete");
|
||||
|
||||
// open the select listbox
|
||||
await user.click(autocomplete);
|
||||
|
||||
// select the target item using keyboard
|
||||
await user.keyboard("penguin");
|
||||
await user.keyboard("{Enter}");
|
||||
await user.click(autocomplete);
|
||||
|
||||
const options = wrapper.getAllByRole("option");
|
||||
const optionItem = options[0];
|
||||
|
||||
expect(optionItem).toHaveAttribute("data-focus", "true");
|
||||
});
|
||||
|
||||
it("should set focusedKey to the item's key when selectedKey prop is passed", async () => {
|
||||
const wrapper = render(
|
||||
<Autocomplete
|
||||
aria-label="Favorite Animal"
|
||||
data-testid="autocomplete"
|
||||
label="Favorite Animal"
|
||||
selectedKey="penguin"
|
||||
>
|
||||
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
|
||||
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
|
||||
<AutocompleteItem key="shark">Shark</AutocompleteItem>
|
||||
</Autocomplete>,
|
||||
);
|
||||
|
||||
const autocomplete = wrapper.getByTestId("autocomplete");
|
||||
|
||||
// open the select listbox
|
||||
await user.click(autocomplete);
|
||||
|
||||
const options = wrapper.getAllByRole("option");
|
||||
const optionItem = options[0];
|
||||
|
||||
expect(optionItem).toHaveAttribute("data-focus", "true");
|
||||
});
|
||||
|
||||
it("should set focusedKey to the default item's key when using react-hook-form defaultValues", async () => {
|
||||
const {result} = renderHook(() =>
|
||||
useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: "zebra",
|
||||
withoutDefaultValue: "",
|
||||
requiredField: "",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const {register} = result.current;
|
||||
|
||||
const wrapper = render(
|
||||
<form>
|
||||
<Autocomplete
|
||||
{...register("withDefaultValue")}
|
||||
aria-label="Favorite Animal"
|
||||
data-testid="autocomplete"
|
||||
label="Favorite Animal"
|
||||
>
|
||||
<AutocompleteItem key="penguin">Penguin</AutocompleteItem>
|
||||
<AutocompleteItem key="zebra">Zebra</AutocompleteItem>
|
||||
<AutocompleteItem key="shark">Shark</AutocompleteItem>
|
||||
</Autocomplete>
|
||||
</form>,
|
||||
);
|
||||
|
||||
const autocomplete = wrapper.getByTestId("autocomplete");
|
||||
|
||||
// open the select listbox
|
||||
await user.click(autocomplete);
|
||||
|
||||
const options = wrapper.getAllByRole("option");
|
||||
const optionItem = options[1];
|
||||
|
||||
expect(optionItem).toHaveAttribute("data-focus", "true");
|
||||
});
|
||||
});
|
||||
|
||||
@ -339,15 +339,27 @@ export function useAutocomplete<T extends object>(originalProps: UseAutocomplete
|
||||
}
|
||||
}, [inputRef.current]);
|
||||
|
||||
// focus first non-disabled item
|
||||
// Ensure the focused item in the dropdown correctly reflects the
|
||||
// selected key when the component mounts or relevant state changes.
|
||||
useEffect(() => {
|
||||
let key = state.collection.getFirstKey();
|
||||
let keyToFocus: React.Key | null;
|
||||
|
||||
while (key && state.disabledKeys.has(key)) {
|
||||
key = state.collection.getKeyAfter(key);
|
||||
if (
|
||||
state.selectedKey !== null &&
|
||||
state.collection.getItem(state.selectedKey) &&
|
||||
!state.disabledKeys.has(state.selectedKey)
|
||||
) {
|
||||
keyToFocus = state.selectedKey;
|
||||
} else {
|
||||
let firstAvailableKey = state.collection.getFirstKey();
|
||||
|
||||
while (firstAvailableKey && state.disabledKeys.has(firstAvailableKey)) {
|
||||
firstAvailableKey = state.collection.getKeyAfter(firstAvailableKey);
|
||||
}
|
||||
keyToFocus = firstAvailableKey;
|
||||
}
|
||||
state.selectionManager.setFocusedKey(key);
|
||||
}, [state.collection, state.disabledKeys]);
|
||||
state.selectionManager.setFocusedKey(keyToFocus);
|
||||
}, [state.collection, state.disabledKeys, state.selectedKey]);
|
||||
|
||||
// scroll the listbox to the selected item
|
||||
useEffect(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user