Skip to content

Commit

Permalink
Add field components to simplify the creation of forms with final-form
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesricky committed Aug 10, 2023
1 parent 3271c05 commit 3e15b81
Show file tree
Hide file tree
Showing 17 changed files with 317 additions and 22 deletions.
60 changes: 60 additions & 0 deletions .changeset/tame-bikes-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
"@comet/admin-color-picker": minor
"@comet/admin-date-time": minor
"@comet/admin": minor
---

Add field components to simplify the creation of forms with final-form.

- TextField
- TextAreaField
- SearchField
- SelectField
- CheckboxField
- SwitchField
- ColorField
- DateField
- DateRangeField
- TimeField
- TimeRangeField
- DateTimeField

**Example with TextField**

```tsx
// You can now do:
<TextField name="text" label="Text" />
```

```tsx
// Instead of:
<Field name="text" label="Text" component={FinalFormInput} />
```

**Example with SelectField**

```tsx
// You can now do:
<SelectField name="select" label="Select">
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</SelectField>
```

```tsx
// Instead of:
<Field name="select" label="Select">
{(props) => (
<FinalFormSelect {...props}>
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</FinalFormSelect>
)}
</Field>
```
10 changes: 10 additions & 0 deletions packages/admin/admin-color-picker/src/ColorField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Field, FieldProps } from "@comet/admin";
import * as React from "react";

import { FinalFormColorPicker } from "./FinalFormColorPicker";

export type ColorFieldProps = FieldProps<string, HTMLInputElement>;

export const ColorField = ({ ...restProps }: ColorFieldProps): React.ReactElement => {
return <Field component={FinalFormColorPicker} {...restProps} />;
};
1 change: 1 addition & 0 deletions packages/admin/admin-color-picker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { ColorField, ColorFieldProps } from "./ColorField";
export {
ColorPicker,
ColorPickerColorPreviewProps,
Expand Down
10 changes: 10 additions & 0 deletions packages/admin/admin-date-time/src/fields/DateField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Field, FieldProps } from "@comet/admin";
import * as React from "react";

import { FinalFormDatePicker } from "../FinalFormDatePicker";

export type DateFieldProps = FieldProps<Date, HTMLInputElement>;

export const DateField = ({ ...restProps }: DateFieldProps): React.ReactElement => {
return <Field component={FinalFormDatePicker} {...restProps} />;
};
11 changes: 11 additions & 0 deletions packages/admin/admin-date-time/src/fields/DateRangeField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Field, FieldProps } from "@comet/admin";
import * as React from "react";

import { DateRange } from "../DateRangePicker";
import { FinalFormDateRangePicker } from "../FinalFormDateRangePicker";

export type DateRangeFieldProps = FieldProps<DateRange, HTMLInputElement>;

export const DateRangeField = ({ ...restProps }: DateRangeFieldProps): React.ReactElement => {
return <Field component={FinalFormDateRangePicker} {...restProps} />;
};
10 changes: 10 additions & 0 deletions packages/admin/admin-date-time/src/fields/DateTimeField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Field, FieldProps } from "@comet/admin";
import * as React from "react";

import { FinalFormDateTimePicker } from "../FinalFormDateTimePicker";

export type DateTimeFieldProps = FieldProps<Date, HTMLInputElement>;

export const DateTimeField = ({ ...restProps }: DateTimeFieldProps): React.ReactElement => {
return <Field component={FinalFormDateTimePicker} {...restProps} />;
};
10 changes: 10 additions & 0 deletions packages/admin/admin-date-time/src/fields/TimeField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Field, FieldProps } from "@comet/admin";
import * as React from "react";

import { FinalFormTimePicker } from "../FinalFormTimePicker";

export type TimeFieldProps = FieldProps<string, HTMLInputElement>;

export const TimeField = ({ ...restProps }: TimeFieldProps): React.ReactElement => {
return <Field component={FinalFormTimePicker} {...restProps} />;
};
11 changes: 11 additions & 0 deletions packages/admin/admin-date-time/src/fields/TimeRangeField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Field, FieldProps } from "@comet/admin";
import * as React from "react";

import { FinalFormTimeRangePicker } from "../FinalFormTimeRangePicker";
import { TimeRange } from "../TimeRangePicker";

export type TimeRangeFieldProps = FieldProps<TimeRange, HTMLInputElement>;

export const TimeRangeField = ({ ...restProps }: TimeRangeFieldProps): React.ReactElement => {
return <Field component={FinalFormTimeRangePicker} {...restProps} />;
};
5 changes: 5 additions & 0 deletions packages/admin/admin-date-time/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export { DatePicker, DatePickerProps } from "./DatePicker";
export { DateRange, DateRangePicker, DateRangePickerProps } from "./DateRangePicker";
export { DateTimePicker, DateTimePickerComponentsProps, DateTimePickerProps } from "./DateTimePicker";
export { DateField, DateFieldProps } from "./fields/DateField";
export { DateRangeField, DateRangeFieldProps } from "./fields/DateRangeField";
export { DateTimeField, DateTimeFieldProps } from "./fields/DateTimeField";
export { TimeField, TimeFieldProps } from "./fields/TimeField";
export { TimeRangeField, TimeRangeFieldProps } from "./fields/TimeRangeField";
export { FinalFormDatePicker, FinalFormDatePickerProps } from "./FinalFormDatePicker";
export { FinalFormDateRangePicker, FinalFormDateRangePickerProps } from "./FinalFormDateRangePicker";
export { FinalFormDateTimePicker, FinalFormDateTimePickerProps } from "./FinalFormDateTimePicker";
Expand Down
96 changes: 74 additions & 22 deletions packages/admin/admin-stories/src/admin/form/AllFieldComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
CheckboxField,
Field,
FieldContainer,
FinalFormCheckbox,
Expand All @@ -7,8 +8,15 @@ import {
FinalFormSearchTextField,
FinalFormSelect,
FinalFormSwitch,
SearchField,
SelectField,
SwitchField,
TextAreaField,
TextField,
} from "@comet/admin";
import { Button, Card, CardContent, FormControlLabel, MenuItem } from "@mui/material";
import { ColorField } from "@comet/admin-color-picker";
import { DateField, DateRangeField, DateTimeField, TimeField, TimeRangeField } from "@comet/admin-date-time";
import { Button, Card, CardContent, CardHeader, FormControlLabel, Grid, Link, MenuItem } from "@mui/material";
import { storiesOf } from "@storybook/react";
import * as React from "react";
import { Form } from "react-final-form";
Expand All @@ -21,17 +29,63 @@ function Story() {
];

return (
<div style={{ width: "500px" }}>
<Form
onSubmit={(values) => {
alert(JSON.stringify(values, undefined, 2));
}}
initialValues={{ checkbox: false, radio: "foo", switch: false }}
render={({ handleSubmit, values }) => (
<>
<Card variant="outlined">
<CardContent>
<form onSubmit={handleSubmit}>
<Form
onSubmit={(values) => {
alert(JSON.stringify(values, undefined, 2));
}}
initialValues={{ multiSelect: [] }}
render={({ handleSubmit, values }) => (
<form onSubmit={handleSubmit}>
<Grid container mb={2} spacing={2}>
<Grid item md={6}>
<Card variant="outlined">
<CardHeader title="Common Field-Components" titleTypographyProps={{ variant: "h3" }} />
<CardContent>
<TextField name="text" label="Text" fullWidth />
<TextAreaField name="textarea" label="TextArea" fullWidth />
<SearchField name="search" label="Search" fullWidth />
<SelectField name="select" label="Select" fullWidth>
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</SelectField>
<CheckboxField
name="singleCheckboxWithLink"
label={
<>
Single checkbox with a{" "}
<Link href="https://www.comet-dxp.com" target="_blank">
link
</Link>{" "}
inside the label.
</>
}
fullWidth
/>
<SwitchField name="switch" label={values.switch ? "On" : "Off"} fieldLabel="Switch" />
</CardContent>
</Card>
</Grid>
<Grid item md={6}>
<Card variant="outlined">
<CardHeader title="Special Field-Components" titleTypographyProps={{ variant: "h3" }} />
<CardContent>
<DateField name="date" label="Date" fullWidth />
<DateRangeField name="dateRange" label="Date Range" fullWidth />
<TimeField name="time" label="Time" fullWidth />
<TimeRangeField name="timeRange" label="Time Range" fullWidth />
<DateTimeField name="dateTime" label="Date Time" fullWidth />
<ColorField name="hexColor" label="Color (hex)" fullWidth />
<ColorField name="rgbaColor" label="Color (rgba)" colorFormat="rgba" fullWidth />
</CardContent>
</Card>
</Grid>
<Grid item md={6}>
<Card variant="outlined">
<CardHeader title="Field & FinalForm-Components" titleTypographyProps={{ variant: "h3" }} />
<CardContent>
<Field name="input" label="FinalFormInput" fullWidth component={FinalFormInput} />
<Field name="search" label="FinalFormSearchTextField" component={FinalFormSearchTextField} />
<Field
Expand Down Expand Up @@ -73,16 +127,14 @@ function Story() {
<Button color="primary" variant="contained" onClick={handleSubmit}>
Submit
</Button>
</form>
</CardContent>
</Card>
<div>
<pre>{JSON.stringify(values, undefined, 2)}</pre>
</div>
</>
)}
/>
</div>
</CardContent>
</Card>
</Grid>
</Grid>
<pre>{JSON.stringify(values, undefined, 2)}</pre>
</form>
)}
/>
);
}

Expand Down
24 changes: 24 additions & 0 deletions packages/admin/admin/src/form/fields/CheckboxField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FormControlLabel, FormControlLabelProps } from "@mui/material";
import * as React from "react";

import { FinalFormCheckbox, FinalFormCheckboxProps } from "../Checkbox";
import { Field, FieldProps } from "../Field";

export interface CheckboxFieldProps extends FieldProps<string, HTMLInputElement> {
fieldLabel?: string;
componentsProps?: {
formControlLabel?: FormControlLabelProps;
finalFormCheckbox?: FinalFormCheckboxProps;
};
}

export const CheckboxField = ({ fieldLabel, label, componentsProps = {}, ...restProps }: CheckboxFieldProps): React.ReactElement => {
const { formControlLabel: formControlLabelProps, finalFormCheckbox: finalFormCheckboxProps } = componentsProps;
return (
<Field type="checkbox" label={fieldLabel} {...restProps}>
{(props) => (
<FormControlLabel label={label} control={<FinalFormCheckbox {...props} {...finalFormCheckboxProps} />} {...formControlLabelProps} />
)}
</Field>
);
};
10 changes: 10 additions & 0 deletions packages/admin/admin/src/form/fields/SearchField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";

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

export type SearchFieldProps = FieldProps<string, HTMLInputElement>;

export const SearchField = ({ ...restProps }: SearchFieldProps): React.ReactElement => {
return <Field component={FinalFormSearchTextField} {...restProps} />;
};
31 changes: 31 additions & 0 deletions packages/admin/admin/src/form/fields/SelectField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";

import { Field, FieldProps } from "../Field";
import { FinalFormSelect, FinalFormSelectProps } from "../FinalFormSelect";

type SelectFieldPropsToExtendFrom<Value extends string | number> = FieldProps<Value, HTMLSelectElement>;

// Remove `children` from the interface. Omit cannot be used here because `FieldProps` contains an index signature.
type SelectFieldPropsToExtendFromWithoutChildren<Value extends string | number> = {
[K in keyof SelectFieldPropsToExtendFrom<Value> as K extends "children" ? never : K]: SelectFieldPropsToExtendFrom<Value>[K];
};

export interface SelectFieldProps<Value extends string | number> extends SelectFieldPropsToExtendFromWithoutChildren<Value> {
children: ReturnType<Required<SelectFieldPropsToExtendFrom<Value>>["children"]>;
componentsProps?: {
finalFormSelect?: FinalFormSelectProps<Value>;
};
}

export function SelectField<Value extends string | number>({ componentsProps = {}, children, ...restProps }: SelectFieldProps<Value>) {
const { finalFormSelect: finalFormSelectProps } = componentsProps;
return (
<Field {...restProps}>
{(props) => (
<FinalFormSelect<Value> {...props} {...finalFormSelectProps}>
{children}
</FinalFormSelect>
)}
</Field>
);
}
24 changes: 24 additions & 0 deletions packages/admin/admin/src/form/fields/SwitchField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FormControlLabel, FormControlLabelProps } from "@mui/material";
import * as React from "react";

import { Field, FieldProps } from "../Field";
import { FinalFormSwitch, FinalFormSwitchProps } from "../Switch";

export interface SwitchFieldProps extends FieldProps<string, HTMLInputElement> {
fieldLabel?: string;
componentsProps?: {
formControlLabel?: FormControlLabelProps;
finalFormSwitch?: FinalFormSwitchProps;
};
}

export const SwitchField = ({ fieldLabel, label, componentsProps = {}, ...restProps }: SwitchFieldProps): React.ReactElement => {
const { formControlLabel: formControlLabelProps, finalFormSwitch: finalFormSwitchProps } = componentsProps;
return (
<Field type="checkbox" label={fieldLabel} {...restProps}>
{(props) => (
<FormControlLabel label={label} control={<FinalFormSwitch {...props} {...finalFormSwitchProps} />} {...formControlLabelProps} />
)}
</Field>
);
};
10 changes: 10 additions & 0 deletions packages/admin/admin/src/form/fields/TextAreaField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";

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

export type TextAreaFieldProps = FieldProps<string, HTMLTextAreaElement>;

export const TextAreaField = ({ ...restProps }: TextAreaFieldProps): React.ReactElement => {
return <Field type="textarea" multiline rows={3} component={FinalFormInput} {...restProps} />;
};
10 changes: 10 additions & 0 deletions packages/admin/admin/src/form/fields/TextField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";

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

export type TextFieldProps = FieldProps<string, HTMLInputElement>;

export const TextField = ({ ...restProps }: TextFieldProps): React.ReactElement => {
return <Field component={FinalFormInput} {...restProps} />;
};
6 changes: 6 additions & 0 deletions packages/admin/admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ 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 { 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 { FinalFormContext, FinalFormContextProvider, FinalFormContextProviderProps, useFinalFormContext } from "./form/FinalFormContextProvider";
export { FinalFormInput, FinalFormInputProps } from "./form/FinalFormInput";
export { FinalFormRangeInput, FinalFormRangeInputClassKey, FinalFormRangeInputProps } from "./form/FinalFormRangeInput";
Expand Down

0 comments on commit 3e15b81

Please sign in to comment.