diff --git a/.changeset/blue-shirts-brake.md b/.changeset/blue-shirts-brake.md new file mode 100644 index 0000000..da2bdf2 --- /dev/null +++ b/.changeset/blue-shirts-brake.md @@ -0,0 +1,5 @@ +--- +"@arkejs/ui": patch +--- + +feat: add filterOptions prop to Autocomplete diff --git a/apps/docs/content/docs/components/autocomplete.mdx b/apps/docs/content/docs/components/autocomplete.mdx index 856e08d..ac4602d 100644 --- a/apps/docs/content/docs/components/autocomplete.mdx +++ b/apps/docs/content/docs/components/autocomplete.mdx @@ -86,6 +86,10 @@ The `Autocomplete` component is used to create an input field with suggestions o name: "multiple", type: "boolean", }, + { + name: "filterOptions", + type: "(option: TValue, inputValue: string) => boolean", + }, ]} /> diff --git a/apps/storybook/src/stories/Autocomplete.stories.tsx b/apps/storybook/src/stories/Autocomplete.stories.tsx index 8b1c028..0b00e22 100644 --- a/apps/storybook/src/stories/Autocomplete.stories.tsx +++ b/apps/storybook/src/stories/Autocomplete.stories.tsx @@ -43,6 +43,27 @@ export const Default = (args: Story["args"]) => { ); }; +export const WithFilteredOptions = (args: Story["args"]) => { + const [{ value, values = departments }, updateArgs] = useArgs(); + + const handleChange = (val: unknown) => updateArgs({ value: val }); + + return ( + // @ts-ignore + + option.name.toLowerCase().includes(inputValue) + } + onChange={handleChange} + renderValue={(val) => val?.name} + placeholder="Search..." + /> + ); +}; + export const Nullable = (args: Story["args"]) => { const [{ value, values = departments }, updateArgs] = useArgs(); const [search, setSearch] = useState(""); @@ -101,16 +122,11 @@ export const Clearable = (args: Story["args"]) => { export const Multiple = (args: Story["args"]) => { const [{ value, values = departments }, updateArgs] = useArgs(); - const [search, setSearch] = useState(""); const handleChange = (val: unknown) => { updateArgs({ value: val }); }; - const filteredValues = values.filter((v: { name: string }) => - v.name.toLowerCase().includes(search) - ); - return ( // @ts-ignore { value={value} nullable clearable - values={filteredValues} - onInputChange={(e) => setSearch(e.target.value)} + values={values} + filterOptions={(option, inputValue) => + option.name.toLowerCase().includes(inputValue) + } onChange={handleChange} multiple placeholder="Search..." + renderValue={(val) => val?.name} /> ); }; @@ -183,6 +202,7 @@ export const Icons = (args: Story["args"]) => { startAdornment={} endAdornment={} placeholder="Search..." + renderValue={(val) => val?.name} /> ); }; diff --git a/packages/ui/src/components/Autocomplete/Autocomplete.test.tsx b/packages/ui/src/components/Autocomplete/Autocomplete.test.tsx index 27658e1..498a762 100644 --- a/packages/ui/src/components/Autocomplete/Autocomplete.test.tsx +++ b/packages/ui/src/components/Autocomplete/Autocomplete.test.tsx @@ -75,6 +75,24 @@ describe("Autocomplete", () => { expect(getByText("Test 1")).toBeInTheDocument(); }); + test("should render correct filtered values", async () => { + const { getByTestId, getByText, queryAllByText } = render( + null} + values={mockValues} + renderValue={(val) => val.name} + filterOptions={(option, inputValue) => + option.name.toLowerCase().includes(inputValue.toLowerCase()) + } + /> + ); + await userEvent.type(getByTestId("arke-autocomplete"), "Test 1"); + + expect(getByText("Test 1")).toBeInTheDocument(); + expect(queryAllByText("Test 2")).toHaveLength(0); + expect(queryAllByText("Test 3")).toHaveLength(0); + }); + test("should call onChange when value is selected", async () => { const onChange = jest.fn(); const { getByTestId, getByText } = render( diff --git a/packages/ui/src/components/Autocomplete/Autocomplete.tsx b/packages/ui/src/components/Autocomplete/Autocomplete.tsx index 2a131a1..649f83f 100644 --- a/packages/ui/src/components/Autocomplete/Autocomplete.tsx +++ b/packages/ui/src/components/Autocomplete/Autocomplete.tsx @@ -77,6 +77,7 @@ function Autocomplete({ clearable, className, clearIcon, + filterOptions, }: IAutocompleteProps< TValue, boolean | undefined, @@ -127,6 +128,13 @@ function Autocomplete({ [clearable, nullable, value] ); + const filteredOptions = useMemo(() => { + if (filterOptions) { + return values?.filter((v) => filterOptions(v, inputValue)); + } + return values; + }, [values, filterOptions, inputValue]); + return (
({ className="autocomplete__options" style={getPosition()} > - {values?.length === 0 && ( + {filteredOptions?.length === 0 && (
  • Nothing found
  • )} - {values?.map((val, index) => ( + {filteredOptions?.map((val, index) => ( {({ active, selected }) => (
  • = { renderChips?: boolean; className?: string; clearIcon?: ReactNode; + filterOptions?: (option: TValue, inputValue: string) => boolean; }; export type IAutocompleteProps< diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 957f8e6..7287890 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -46,7 +46,7 @@ importers: version: 2.8.8 turbo: specifier: latest - version: 1.8.8 + version: 1.10.7 apps/docs: dependencies: @@ -16290,65 +16290,65 @@ packages: engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} dev: true - /turbo-darwin-64@1.8.8: - resolution: {integrity: sha512-18cSeIm7aeEvIxGyq7PVoFyEnPpWDM/0CpZvXKHpQ6qMTkfNt517qVqUTAwsIYqNS8xazcKAqkNbvU1V49n65Q==} + /turbo-darwin-64@1.10.7: + resolution: {integrity: sha512-N2MNuhwrl6g7vGuz4y3fFG2aR1oCs0UZ5HKl8KSTn/VC2y2YIuLGedQ3OVbo0TfEvygAlF3QGAAKKtOCmGPNKA==} cpu: [x64] os: [darwin] requiresBuild: true dev: false optional: true - /turbo-darwin-arm64@1.8.8: - resolution: {integrity: sha512-ruGRI9nHxojIGLQv1TPgN7ud4HO4V8mFBwSgO6oDoZTNuk5ybWybItGR+yu6fni5vJoyMHXOYA2srnxvOc7hjQ==} + /turbo-darwin-arm64@1.10.7: + resolution: {integrity: sha512-WbJkvjU+6qkngp7K4EsswOriO3xrNQag7YEGRtfLoDdMTk4O4QTeU6sfg2dKfDsBpTidTvEDwgIYJhYVGzrz9Q==} cpu: [arm64] os: [darwin] requiresBuild: true dev: false optional: true - /turbo-linux-64@1.8.8: - resolution: {integrity: sha512-N/GkHTHeIQogXB1/6ZWfxHx+ubYeb8Jlq3b/3jnU4zLucpZzTQ8XkXIAfJG/TL3Q7ON7xQ8yGOyGLhHL7MpFRg==} + /turbo-linux-64@1.10.7: + resolution: {integrity: sha512-x1CF2CDP1pDz/J8/B2T0hnmmOQI2+y11JGIzNP0KtwxDM7rmeg3DDTtDM/9PwGqfPotN9iVGgMiMvBuMFbsLhg==} cpu: [x64] os: [linux] requiresBuild: true dev: false optional: true - /turbo-linux-arm64@1.8.8: - resolution: {integrity: sha512-hKqLbBHgUkYf2Ww8uBL9UYdBFQ5677a7QXdsFhONXoACbDUPvpK4BKlz3NN7G4NZ+g9dGju+OJJjQP0VXRHb5w==} + /turbo-linux-arm64@1.10.7: + resolution: {integrity: sha512-JtnBmaBSYbs7peJPkXzXxsRGSGBmBEIb6/kC8RRmyvPAMyqF8wIex0pttsI+9plghREiGPtRWv/lfQEPRlXnNQ==} cpu: [arm64] os: [linux] requiresBuild: true dev: false optional: true - /turbo-windows-64@1.8.8: - resolution: {integrity: sha512-2ndjDJyzkNslXxLt+PQuU21AHJWc8f6MnLypXy3KsN4EyX/uKKGZS0QJWz27PeHg0JS75PVvhfFV+L9t9i+Yyg==} + /turbo-windows-64@1.10.7: + resolution: {integrity: sha512-7A/4CByoHdolWS8dg3DPm99owfu1aY/W0V0+KxFd0o2JQMTQtoBgIMSvZesXaWM57z3OLsietFivDLQPuzE75w==} cpu: [x64] os: [win32] requiresBuild: true dev: false optional: true - /turbo-windows-arm64@1.8.8: - resolution: {integrity: sha512-xCA3oxgmW9OMqpI34AAmKfOVsfDljhD5YBwgs0ZDsn5h3kCHhC4x9W5dDk1oyQ4F5EXSH3xVym5/xl1J6WRpUg==} + /turbo-windows-arm64@1.10.7: + resolution: {integrity: sha512-D36K/3b6+hqm9IBAymnuVgyePktwQ+F0lSXr2B9JfAdFPBktSqGmp50JNC7pahxhnuCLj0Vdpe9RqfnJw5zATA==} cpu: [arm64] os: [win32] requiresBuild: true dev: false optional: true - /turbo@1.8.8: - resolution: {integrity: sha512-qYJ5NjoTX+591/x09KgsDOPVDUJfU9GoS+6jszQQlLp1AHrf1wRFA3Yps8U+/HTG03q0M4qouOfOLtRQP4QypA==} + /turbo@1.10.7: + resolution: {integrity: sha512-xm0MPM28TWx1e6TNC3wokfE5eaDqlfi0G24kmeHupDUZt5Wd0OzHFENEHMPqEaNKJ0I+AMObL6nbSZonZBV2HA==} hasBin: true requiresBuild: true optionalDependencies: - turbo-darwin-64: 1.8.8 - turbo-darwin-arm64: 1.8.8 - turbo-linux-64: 1.8.8 - turbo-linux-arm64: 1.8.8 - turbo-windows-64: 1.8.8 - turbo-windows-arm64: 1.8.8 + turbo-darwin-64: 1.10.7 + turbo-darwin-arm64: 1.10.7 + turbo-linux-64: 1.10.7 + turbo-linux-arm64: 1.10.7 + turbo-windows-64: 1.10.7 + turbo-windows-arm64: 1.10.7 dev: false /typanion@3.12.1: