Skip to content

Commit

Permalink
fix: allow for reselecting the last selection of InputSearch if clear…
Browse files Browse the repository at this point in the history
…ed. Trigger InputSearch onSelect callback when selection is cleared. (#682)
  • Loading branch information
wp-aberg authored Dec 20, 2024
1 parent 53a18d0 commit ae793dd
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Ruler = styled("div", {
const Body = styled("div", {
paddingBlock: "1px",
fontFamily: theme.fonts.meta,
placeItems: "left"
placeItems: "left",
});

const PointsList = styled("ul", {
Expand Down Expand Up @@ -177,4 +177,4 @@ const Breakpoints = () => {
);
};

export default Breakpoints;
export default Breakpoints;
25 changes: 9 additions & 16 deletions packages/kit/src/input-search/InputSearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const InputSearchInput = React.forwardRef<
{
label = "Search",
autocomplete = true,
autoComplete = "off",
id,
value,
...rest
Expand All @@ -46,6 +47,13 @@ export const InputSearchInput = React.forwardRef<
if (inputProps.onChange) inputProps.onChange(event);
};

React.useEffect(() => {
// allow for external changes for controlled inputs
if (value !== undefined && value !== null && value !== inputProps.value) {
state.setInputValue(value);
}
}, [value, inputProps.value, state]);

const [tempText, setTempText] = React.useState<string>();
const withKeyboard = React.useRef(false);
React.useEffect(() => {
Expand All @@ -62,26 +70,10 @@ export const InputSearchInput = React.forwardRef<
}
}, [state.selectionManager.focusedKey, setTempText]);

if (value !== undefined && value !== null) {
inputProps.value = value;
}

if (autocomplete && withKeyboard.current) {
inputProps.value = tempText;
}

const [, setRerender] = React.useState(false);
if (!inputProps.value && inputRef.current) {
const el = inputRef.current as HTMLInputElement;
if (el.value) {
// if a controlled input is passed an empty value,
// an extra render is needed to reset the input's internal state
requestAnimationFrame(() => {
setRerender((prev) => !prev);
});
}
}

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
withKeyboard.current = true;
Expand Down Expand Up @@ -111,6 +103,7 @@ export const InputSearchInput = React.forwardRef<
onChange={handleChange}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
autoComplete={autoComplete}
/>
);
}
Expand Down
18 changes: 10 additions & 8 deletions packages/kit/src/input-search/InputSearchRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,16 @@ export const InputSearchRoot = ({

const prevSelectedKey = React.useRef(state.selectedKey);
React.useEffect(() => {
if (
state.selectedItem &&
onSelect &&
prevSelectedKey.current !== state.selectedKey
) {
onSelect(
state.selectedItem.textValue || (state.selectedItem.rendered as string)
);
if (!onSelect) return;
if (prevSelectedKey.current !== state.selectedKey) {
if (state.selectedItem) {
onSelect(
state.selectedItem.textValue ||
(state.selectedItem.rendered as string)
);
} else if (state.selectedItem === null) {
onSelect("");
}
prevSelectedKey.current = state.selectedKey;
}
}, [state.selectedItem, onSelect]);
Expand Down
43 changes: 33 additions & 10 deletions packages/kit/src/input-search/play.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo, useState, useEffect } from "react";
import { screen, userEvent } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
import React, { useMemo, useState, useEffect, use } from "react";
import { screen, userEvent, waitFor } from "@storybook/testing-library";
import { expect, jest } from "@storybook/jest";
import { Box } from "../box";
import { matchSorter } from "match-sorter";
import { InputSearch } from "./";
Expand Down Expand Up @@ -267,6 +267,8 @@ const ControlledTemplate: StoryFn<typeof InputSearch.Root> = (args) => {
openOnFocus
onSelect={(value) => {
setTerm(value);
console.log("onSelect", value);
args.onSelect && args.onSelect(value);
}}
>
<InputSearch.Input
Expand Down Expand Up @@ -294,16 +296,21 @@ const ControlledTemplate: StoryFn<typeof InputSearch.Root> = (args) => {

export const Controlled = {
render: ControlledTemplate,
args: {},

args: {
onSelect: jest.fn(),
},
parameters: {
chromatic: { disableSnapshot: true },
},
};

const InteractionsTemplate: StoryFn<typeof InputSearch.Root> = () => (
const InteractionsTemplate: StoryFn<typeof InputSearch.Root> = (args) => (
<Box css={{ width: "275px", height: "340px" }}>
<InputSearch.Root aria-label="Example-Search" openOnFocus>
<InputSearch.Root
aria-label="Example-Search"
openOnFocus
onSelect={args.onSelect}
>
<InputSearch.Input name="city" id="city" />
<InputSearch.Popover>
<InputSearch.List>
Expand All @@ -320,21 +327,28 @@ const InteractionsTemplate: StoryFn<typeof InputSearch.Root> = () => (

export const Interactions = {
render: InteractionsTemplate,

play: async () => {
args: {
onSelect: jest.fn(),
},
play: async ({ args }) => {
const input = await screen.findByLabelText("Search");
await userEvent.type(input, "app", {
delay: 100,
});
await userEvent.keyboard("[ArrowDown]");
await expect(input).toHaveDisplayValue("Apple");
await userEvent.keyboard("[Enter]");
await expect(args.onSelect).toHaveBeenCalledWith("Apple");
const clearButton = await screen.findByRole("button", { name: "Clear" });
await userEvent.click(clearButton);
await expect(args.onSelect).toHaveBeenCalledWith("");
},
};

export const ControlledKeyboardInteractions = {
render: ControlledTemplate,

play: async () => {
play: async ({ args }) => {
const input = await screen.findByLabelText("Search");
await userEvent.type(input, "test", {
delay: 100,
Expand All @@ -345,6 +359,9 @@ export const ControlledKeyboardInteractions = {
await expect(input).toHaveDisplayValue("Orange");
await userEvent.keyboard("[Backspace]");
await expect(input).toHaveDisplayValue("Orang");
await userEvent.keyboard("[ArrowUp]");
await userEvent.keyboard("[Enter]");
await expect(args.onSelect).toHaveBeenCalledWith("Pineapple");
const clearButton = await screen.findByText("Clear");
await userEvent.click(clearButton);
await expect(input).toHaveDisplayValue("");
Expand All @@ -356,6 +373,12 @@ export const ControlledKeyboardInteractions = {
const externalClearButton = await screen.findByText("External Clear");
await userEvent.click(externalClearButton);
await expect(input).toHaveDisplayValue("");
await userEvent.click(input);
await expect(input).toHaveFocus();
const appleOption = await screen.findByRole("option", { name: "Apple" });
await userEvent.click(appleOption);
await expect(input).toHaveDisplayValue("Apple");
await expect(args.onSelect).toHaveBeenCalledWith("Apple");
//
},
};

0 comments on commit ae793dd

Please sign in to comment.