Skip to content

Commit

Permalink
Add FinalFormAsyncSelect, AsyncSelectField, and `FinalFormAsyncAu…
Browse files Browse the repository at this point in the history
…tocomplete` components (#2069)

Thin wrappers to ease using `useAsyncOptionsProps()` with
`FinalFormSelect` and `FinalFormAutocomplete`.

**Example**

Previously:

```tsx
const asyncOptionsProps = useAsyncOptionsProps(async () => {
    // Load options here
});

// ...

<Field component={FinalFormAsyncAutocomplete} {...asyncOptionsProps} />;
```

Now:

```tsx
<Field
    component={FinalFormAsyncAutocomplete}
    loadOptions={async () => {
        // Load options here
    }}
/>
```
  • Loading branch information
johnnyomair committed Jun 5, 2024
1 parent 3ee8c7a commit 16ffa7b
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 16 deletions.
32 changes: 32 additions & 0 deletions .changeset/fifty-lies-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
"@comet/admin": minor
---

Add `FinalFormAsyncSelect`, `AsyncSelectField`, and `FinalFormAsyncAutocomplete` components

Thin wrappers to ease using `useAsyncOptionsProps()` with `FinalFormSelect` and `FinalFormAutocomplete`.

**Example**

Previously:

```tsx
const asyncOptionsProps = useAsyncOptionsProps(async () => {
// Load options here
});

// ...

<Field component={FinalFormAsyncAutocomplete} {...asyncOptionsProps} />;
```

Now:

```tsx
<Field
component={FinalFormAsyncAutocomplete}
loadOptions={async () => {
// Load options here
}}
/>
```
22 changes: 22 additions & 0 deletions packages/admin/admin/src/form/FinalFormAsyncAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";

import { useAsyncOptionsProps } from "../hooks/useAsyncOptionsProps";
import FinalFormAutocomplete, { FinalFormAutocompleteProps } from "./Autocomplete";

export interface FinalFormAsyncAutocompleteProps<
T extends Record<string, any>,
Multiple extends boolean | undefined,
DisableClearable extends boolean | undefined,
FreeSolo extends boolean | undefined,
> extends FinalFormAutocompleteProps<T, Multiple, DisableClearable, FreeSolo> {
loadOptions: () => Promise<T[]>;
}

export function FinalFormAsyncAutocomplete<
T extends Record<string, any>,
Multiple extends boolean | undefined,
DisableClearable extends boolean | undefined,
FreeSolo extends boolean | undefined,
>({ loadOptions, ...rest }: FinalFormAsyncAutocompleteProps<T, Multiple, DisableClearable, FreeSolo>) {
return <FinalFormAutocomplete<T, Multiple, DisableClearable, FreeSolo> {...useAsyncOptionsProps(loadOptions)} {...rest} />;
}
13 changes: 13 additions & 0 deletions packages/admin/admin/src/form/FinalFormAsyncSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SelectProps } from "@mui/material";
import React from "react";

import { useAsyncOptionsProps } from "../hooks/useAsyncOptionsProps";
import { FinalFormSelect, FinalFormSelectProps } from "./FinalFormSelect";

export interface FinalFormAsyncSelectProps<T> extends FinalFormSelectProps<T>, Omit<SelectProps, "input"> {
loadOptions: () => Promise<T[]>;
}

export function FinalFormAsyncSelect<T>({ loadOptions, ...rest }: FinalFormAsyncSelectProps<T>) {
return <FinalFormSelect<T> {...useAsyncOptionsProps(loadOptions)} {...rest} />;
}
15 changes: 15 additions & 0 deletions packages/admin/admin/src/form/fields/AsyncSelectField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

import { Field, FieldProps } from "../Field";
import { FinalFormAsyncSelect } from "../FinalFormAsyncSelect";

export interface AsyncSelectFieldProps<Option> extends FieldProps<Option, HTMLSelectElement> {
loadOptions: () => Promise<Option[]>;
getOptionLabel?: (option: Option) => string;
getOptionValue?: (option: Option) => string;
clearable?: boolean;
}

export function AsyncSelectField<Option>(props: AsyncSelectFieldProps<Option>) {
return <Field component={FinalFormAsyncSelect} {...props} />;
}
3 changes: 3 additions & 0 deletions packages/admin/admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,15 @@ export { FinalFormAutocomplete, FinalFormAutocompleteProps } from "./form/Autoco
export { FinalFormCheckbox, FinalFormCheckboxProps } from "./form/Checkbox";
export { Field, FieldProps } from "./form/Field";
export { FieldContainer, FieldContainerClassKey, FieldContainerComponent, FieldContainerProps } from "./form/FieldContainer";
export { AsyncSelectField, AsyncSelectFieldProps } from "./form/fields/AsyncSelectField";
export { CheckboxField, CheckboxFieldProps } from "./form/fields/CheckboxField";
export { SearchField, SearchFieldProps } from "./form/fields/SearchField";
export { SelectField, SelectFieldProps } from "./form/fields/SelectField";
export { SwitchField, SwitchFieldProps } from "./form/fields/SwitchField";
export { TextAreaField, TextAreaFieldProps } from "./form/fields/TextAreaField";
export { TextField, TextFieldProps } from "./form/fields/TextField";
export { FinalFormAsyncAutocomplete, FinalFormAsyncAutocompleteProps } from "./form/FinalFormAsyncAutocomplete";
export { FinalFormAsyncSelect, FinalFormAsyncSelectProps } from "./form/FinalFormAsyncSelect";
export { FinalFormContext, FinalFormContextProvider, FinalFormContextProviderProps, useFinalFormContext } from "./form/FinalFormContextProvider";
export { FinalFormFileSelect, FinalFormFileSelectClassKey, FinalFormFileSelectProps } from "./form/FinalFormFileSelect";
export { FinalFormInput, FinalFormInputProps } from "./form/FinalFormInput";
Expand Down
10 changes: 10 additions & 0 deletions storybook/src/admin/form/AllFieldComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AsyncSelectField,
CheckboxField,
Field,
FieldContainer,
Expand Down Expand Up @@ -51,6 +52,15 @@ function Story() {
</MenuItem>
))}
</SelectField>
<AsyncSelectField
name="asyncSelect"
label="Async Select"
loadOptions={async () => {
return new Promise<typeof options>((resolve) => setTimeout(() => resolve(options), 1000));
}}
getOptionLabel={(option) => option.label}
fullWidth
/>
<CheckboxField
name="singleCheckboxWithLink"
label={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ It doesn't have any built-in search functionality.
## FinalFormSelect

`FinalFormSelect` provides a select with multiple options.
It's possible to load the options asynchronously using the `useAsyncOptionsProps()` hook.
For async options, use the special `FinalFormAsyncSelect` component that uses the `useAsyncOptionsProps()` hook internally.
It expects the value to be an object to be able to display the current value without having to load the async options.

<Canvas>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
Field,
FieldContainer,
FinalForm,
FinalFormAsyncAutocomplete,
FinalFormAsyncSelect,
FinalFormAutocomplete,
FinalFormCheckbox,
FinalFormInput,
Expand All @@ -10,7 +12,6 @@ import {
FinalFormSearchTextField,
FinalFormSelect,
FinalFormSwitch,
useAsyncOptionsProps,
} from "@comet/admin";
import { Button, FormControlLabel } from "@mui/material";
import { storiesOf } from "@storybook/react";
Expand Down Expand Up @@ -82,10 +83,6 @@ storiesOf("stories/form/FinalForm Fields", module)
[],
);

const acAsyncProps = useAsyncOptionsProps<Option>(async () => {
return new Promise((resolve) => setTimeout(() => resolve(options), 3000));
});

return (
<FinalForm
mode="add"
Expand All @@ -104,8 +101,10 @@ storiesOf("stories/form/FinalForm Fields", module)
fullWidth
/>
<Field
component={FinalFormAutocomplete}
{...acAsyncProps}
component={FinalFormAsyncAutocomplete}
loadOptions={async () => {
return new Promise((resolve) => setTimeout(() => resolve(options), 3000));
}}
getOptionLabel={(option: Option) => option.label}
isOptionEqualToValue={(option: Option, value: Option) => option.value === value.value}
name="autocompleteAsync"
Expand Down Expand Up @@ -146,10 +145,6 @@ storiesOf("stories/form/FinalForm Fields", module)
[],
);

const selectAsyncProps = useAsyncOptionsProps<Option>(async () => {
return new Promise((resolve) => setTimeout(() => resolve(options), 500));
});

return (
<FinalForm
mode="add"
Expand All @@ -170,12 +165,11 @@ storiesOf("stories/form/FinalForm Fields", module)
fullWidth
/>
<Field
component={FinalFormSelect}
component={FinalFormAsyncSelect}
getOptionLabel={(option: Option) => option.label}
getOptionSelected={(option: Option, value: Option) => {
return option.value === value.value;
loadOptions={async () => {
return new Promise((resolve) => setTimeout(() => resolve(options), 500));
}}
{...selectAsyncProps}
name="selectAsync"
label="SelectAsync"
fullWidth
Expand Down

0 comments on commit 16ffa7b

Please sign in to comment.