Skip to content

Commit ff98d60

Browse files
authored
Pressing enter selects all filtered/remaining contents in selector (#2710)
* now on pressing enter will automatically select remaining/filtered contents * added test for new feat added of geting all filtered items selected on pressing entered
1 parent 242d992 commit ff98d60

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

frontend/taipy-gui/src/components/Taipy/Selector.spec.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,41 @@ describe("Selector Component", () => {
176176
await userEvent.clear(search);
177177
expect(queryAllByText(/Item /)).toHaveLength(4);
178178
});
179-
});
179+
it("selects all filtered items on Enter in multi-select mode", async () => {
180+
const user = userEvent.setup();
181+
const dispatch = jest.fn();
182+
const state = INITIAL_STATE;
183+
const { getByPlaceholderText, queryAllByText } = render(
184+
<TaipyContext.Provider value={{ state, dispatch }}>
185+
<Selector lov={lov} filter={true} multiple={true} updateVarName="varname" />
186+
</TaipyContext.Provider>
187+
);
188+
const search = getByPlaceholderText("Search field");
189+
// Filter to items containing 'Item 2' or 'Item 3'
190+
await user.type(search, "2");
191+
expect(queryAllByText(/Item /)).toHaveLength(1);
192+
// Press Enter
193+
await user.keyboard("{Enter}");
194+
// Should dispatch with filtered id
195+
expect(dispatch).toHaveBeenLastCalledWith({
196+
name: "varname",
197+
payload: { value: ["id2"] },
198+
propagate: true,
199+
type: "SEND_UPDATE_ACTION",
200+
});
201+
// Now clear and filter for 'Item'
202+
await user.clear(search);
203+
await user.type(search, "Item");
204+
expect(queryAllByText(/Item /)).toHaveLength(4);
205+
await user.keyboard("{Enter}");
206+
expect(dispatch).toHaveBeenLastCalledWith({
207+
name: "varname",
208+
payload: { value: ["id1", "id2", "id3", "id4"] },
209+
propagate: true,
210+
type: "SEND_UPDATE_ACTION",
211+
});
212+
});
213+
});
180214
describe("Selector Component with dropdown", () => {
181215
//dropdown
182216
it("displays as an empty control with arrow", async () => {

frontend/taipy-gui/src/components/Taipy/Selector.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,31 @@ const Selector = (props: SelectorProps) => {
549549

550550
const handleInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => setSearchValue(e.target.value), []);
551551

552+
// Handle Enter key in filter input: select all filtered options in multi-select mode
553+
const handleFilterInputKeyDown = useCallback(
554+
(e: React.KeyboardEvent<HTMLInputElement>) => {
555+
if (e.key === "Enter" && multiple && filter) {
556+
// Find filtered items
557+
const filtered = lovList.filter((elt) => showItem(elt, searchValue));
558+
const filteredIds = filtered.map((elt) => elt.id);
559+
setSelectedValue(filteredIds);
560+
dispatch(
561+
createSendUpdateAction(
562+
updateVarName,
563+
filteredIds,
564+
module,
565+
props.onChange,
566+
propagate,
567+
valueById ? undefined : lovVarName
568+
)
569+
);
570+
e.preventDefault();
571+
e.stopPropagation();
572+
}
573+
},
574+
[multiple, filter, lovList, searchValue, setSelectedValue, dispatch, updateVarName, module, props.onChange, propagate, valueById, lovVarName]
575+
);
576+
552577
const dropdownValue = ((dropdown || isRadio) &&
553578
(multiple ? selectedValue : selectedValue.length ? selectedValue[0] : "")) as string[];
554579

@@ -732,6 +757,7 @@ const Selector = (props: SelectorProps) => {
732757
placeholder="Search field"
733758
value={searchValue}
734759
onChange={handleInput}
760+
onKeyDown={handleFilterInputKeyDown}
735761
disabled={!active}
736762
startAdornment={
737763
multiple && showSelectAll ? (

0 commit comments

Comments
 (0)