Skip to content

Commit

Permalink
Refine react-final-form input typings to be optional (#41)
Browse files Browse the repository at this point in the history
* refine inputs typings

* oops, this one too
  • Loading branch information
ncovercash authored Dec 21, 2023
1 parent 7c8e22c commit a4193df
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 62 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ This is a set of TypeScript typings for the [Stripes](https://github.com/folio-o

`react-final-form` support is currently experimental. Stripes has form fields which can optionally be controlled via react-final-form or left uncontrolled. However, in RFF, [FieldRenderProps](https://github.com/final-form/react-final-form/blob/09ccc607b3e8843addd7cbde7a84a82866587000/typescript/index.d.ts#L43-L50) are defined problematically:

- `input` and `meta` are **required**, whereas Stripes should have them be optional, meaning we cannot require them;
- `input` and `meta` are **required**, whereas Stripes should have them be optional (for non-RFF use), meaning we cannot require them;
- the `<Field>` definition requires the `component` to require `input` and `meta`, meaning we cannot leave them optional; and
- the props also allow any other prop to be passed through and overwrites their types as `any`, erasing any specific component typings.

With this current situation, there's not too much we can do to make everyone happy. Therefore, this aspect of the typings is experimental and may change in the future.
With this current situation, there's not too much we can do to make everyone happy. Therefore, **this aspect of the typings is experimental and may change in the future**.

Currently, the recommended way to use Stripes components in react-final-form without sacrificing typings is via the render prop or as a child:

```tsx
```jsx
import { Field } from 'react-final-form';
import { TextField } from '@folio/stripes/components';

Expand All @@ -30,7 +30,6 @@ import { TextField } from '@folio/stripes/components';
name="foo"
render={({ input, meta }) => (
<TextField
// pass RFF props only; with the `[otherProp: string]: any;` in `FieldRenderProps`, doing a spread suppresses any warnings for missing props
input={input}
meta={meta}
// other TextField specific props
Expand All @@ -44,7 +43,6 @@ import { TextField } from '@folio/stripes/components';
<Field name="foo">
{({ input, meta }) => (
<TextField
// pass RFF props only; with the `[otherProp: string]: any;` in `FieldRenderProps`, doing a spread suppresses any warnings for missing props
input={input}
meta={meta}
// other TextField specific props
Expand Down
12 changes: 4 additions & 8 deletions components/lib/Checkbox/Checkbox.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import {
RefObject,
} from 'react';

export interface CheckboxProps
extends AriaAttributes,
InputHTMLAttributes<HTMLInputElement> {
export interface CheckboxProps extends AriaAttributes, InputHTMLAttributes<HTMLInputElement> {
/** If the field should automatically focus on mount */
autoFocus?: boolean;
/** If the checkbox is checked */
Expand All @@ -34,9 +32,7 @@ export interface CheckboxProps
/** Add a class to the inner input */
innerClass?: string;
/** Reference to the inner input */
inputRef?:
| RefObject<HTMLInputElement>
| ((node: HTMLInputElement | null) => void);
inputRef?: RefObject<HTMLInputElement> | ((node: HTMLInputElement | null) => void);
/** The checkbox's label */
label?: string;
/** Add a class to the label */
Expand All @@ -61,8 +57,8 @@ export interface CheckboxProps
warning?: string | ReactNode;

// TODO: reference react-final-form FieldRenderProps<boolean | string[]>
input: any;
meta: any;
input?: any;
meta?: any;
}

/**
Expand Down
12 changes: 3 additions & 9 deletions components/lib/Datepicker/Datepicker.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { Moment } from 'moment';
import Popper from 'popper.js';
import {
AriaAttributes,
Component,
FocusEventHandler,
ReactNode,
RefObject,
} from 'react';
import { AriaAttributes, Component, FocusEventHandler, ReactNode, RefObject } from 'react';
import { Merge } from 'type-fest';
import { TextFieldProps } from '../TextField';

Expand Down Expand Up @@ -72,8 +66,8 @@ export interface DatepickerProps extends AriaAttributes {
outputFormatter?: (date: Moment) => string;

// TODO: reference react-final-form FieldRenderProps<string>
input: any;
meta: any;
input?: any;
meta?: any;
}

/**
Expand Down
30 changes: 10 additions & 20 deletions components/lib/MultiSelection/MultiSelection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ export interface MultiSelectionDefaultOptionType<ValueType = never> {
* Props each action may accept
* @see https://github.com/folio-org/stripes-components/tree/master/lib/MultiSelection#actions
*/
export interface MultiSelectionActionItemProps<
OptionType = MultiSelectionDefaultOptionType
> {
export interface MultiSelectionActionItemProps<OptionType = MultiSelectionDefaultOptionType> {
/** The search term */
filterValue: string;
/** If this search was an exact match */
Expand All @@ -24,9 +22,7 @@ export interface MultiSelectionActionItemProps<
renderedItems: ReadonlyArray<OptionType>;
}

export interface MultiSelectionProps<
OptionType = MultiSelectionDefaultOptionType
> {
export interface MultiSelectionProps<OptionType = MultiSelectionDefaultOptionType> {
/**
* Custom actions, such as a "New" row
* @see https://github.com/folio-org/stripes-components/tree/master/lib/MultiSelection#actions
Expand Down Expand Up @@ -59,13 +55,10 @@ export interface MultiSelectionProps<
/** A custom filter function, either directly gives results or does something async */
filter?: (
filterText: string | undefined,
list: OptionType[]
list: OptionType[],
) => { renderedItems: OptionType[]; exactMatch?: boolean } | Promise<void>;
/** A custom formatter to render each option */
formatter?: (props: {
option: OptionType;
searchTerm: string | undefined;
}) => ReactNode;
formatter?: (props: { option: OptionType; searchTerm: string | undefined }) => ReactNode;
/** Adds a custom ID to the control */
id?: string;
/** If true, adds valid styles to the field */
Expand Down Expand Up @@ -95,16 +88,13 @@ export interface MultiSelectionProps<
/** The selected objects */
value?: OptionType[];
/** Same as {@link formatter}, `formatter` should probably be used instead. */
valueFormatter?: (
option: OptionType,
searchTerm: string | undefined
) => ReactNode;
valueFormatter?: (option: OptionType, searchTerm: string | undefined) => ReactNode;
/** Inline feedback for the user indicating a validation warning */
warning?: ReactNode;

// TODO: reference react-final-form FieldRenderProps<OptionType[]>
input: any;
meta: any;
input?: any;
meta?: any;
}

/**
Expand All @@ -127,6 +117,6 @@ export interface MultiSelectionProps<
* dataOptions={optionList}
* />
*/
export default class MultiSelection<
OptionType = MultiSelectionDefaultOptionType
> extends Component<MultiSelectionProps<OptionType>> {}
export default class MultiSelection<OptionType = MultiSelectionDefaultOptionType> extends Component<
MultiSelectionProps<OptionType>
> {}
8 changes: 3 additions & 5 deletions components/lib/RadioButton/RadioButton.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
InputHTMLAttributes,
} from 'react';

export interface RadioButtonProps
extends AriaAttributes,
InputHTMLAttributes<HTMLInputElement> {
export interface RadioButtonProps extends AriaAttributes, InputHTMLAttributes<HTMLInputElement> {
/** If the field should automatically focus on mount */
autoFocus?: boolean;
/** If the radio button is centered */
Expand Down Expand Up @@ -54,8 +52,8 @@ export interface RadioButtonProps
warning?: string;

// TODO: reference react-final-form FieldRenderProps<boolean | string[]>
input: any;
meta: any;
input?: any;
meta?: any;
}

/**
Expand Down
8 changes: 3 additions & 5 deletions components/lib/Select/Select.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ export interface SelectBaseProps<ValueType> {
warning?: ReactNode;

// TODO: reference react-final-form FieldRenderProps<OptionType[]>
input: any;
meta: any;
input?: any;
meta?: any;
}

export type SelectProps<ValueType> = RequireExactlyOne<
Expand Down Expand Up @@ -91,6 +91,4 @@ export type SelectProps<ValueType> = RequireExactlyOne<
* ]}
* />
*/
export default class Select<ValueType = never> extends Component<
SelectProps<ValueType>
> {}
export default class Select<ValueType = never> extends Component<SelectProps<ValueType>> {}
10 changes: 5 additions & 5 deletions components/lib/TextField/TextField.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ export interface TextFieldProps<FieldType extends string | number = string>
warning?: ReactNode;

// TODO: reference react-final-form FieldRenderProps<string|number>
input: any;
meta: any;
input?: any;
meta?: any;
}

/**
Expand All @@ -112,6 +112,6 @@ export interface TextFieldProps<FieldType extends string | number = string>
* onChange={this.handleChange}
* />
*/
export default class TextField<
Type extends string | number = string
> extends Component<TextFieldProps<Type>> {}
export default class TextField<Type extends string | number = string> extends Component<
TextFieldProps<Type>
> {}
11 changes: 6 additions & 5 deletions components/lib/Timepicker/Timepicker.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import Popper from 'popper.js';
import { AriaAttributes, ComponentType, ReactNode, RefObject } from 'react';
import { FieldRenderProps } from 'react-final-form';
import { IntlShape } from 'react-intl';

export interface TimepickerProps
extends AriaAttributes,
FieldRenderProps<string> {
export interface TimepickerProps extends AriaAttributes {
/** If the field should auto-focus on mount */
autoFocus?: boolean;
/** Disables the input field */
Expand Down Expand Up @@ -36,7 +33,7 @@ export interface TimepickerProps
value: string | undefined,
timezone: string,
timeFormat: string,
intl: IntlShape
intl: IntlShape,
) => string;
/** Where the overlay should be placed in relation to the field */
placement?: Popper.Placement;
Expand All @@ -54,6 +51,10 @@ export interface TimepickerProps
usePortal?: boolean;
/** The field's value */
value?: string;

// TODO: reference react-final-form FieldRenderProps<string>
input?: any;
meta?: any;
}

/**
Expand Down

0 comments on commit a4193df

Please sign in to comment.