From 34b7ee3b5fe61da756073e81e1d0687ae947333c Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 29 Jul 2025 14:39:31 +0200 Subject: [PATCH 01/12] feat: improve Select option types --- src/Fields/SelectField.tsx | 8 ++++---- src/Select/index.tsx | 26 ++++++++++---------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/Fields/SelectField.tsx b/src/Fields/SelectField.tsx index c4c10ae7..8f8c3c03 100644 --- a/src/Fields/SelectField.tsx +++ b/src/Fields/SelectField.tsx @@ -1,4 +1,4 @@ -import { BaseOptionType, DefaultOptionType } from 'antd/lib/select'; +import { DefaultOptionType } from 'antd/lib/select'; import React from 'react'; import { useController, @@ -9,7 +9,7 @@ import { UnpackNestedValue, } from 'react-hook-form'; -import { Select, SelectProps } from '../Select'; +import { GroupedOptionType, Select, SelectProps } from '../Select'; import { useFieldContext } from './FieldProvider'; import { FieldWrapper, FieldWrapperProps } from './FieldWrapper'; @@ -17,7 +17,7 @@ import { FieldWrapper, FieldWrapperProps } from './FieldWrapper'; type SelectFieldProps< TFieldValues extends FieldValues, TName extends FieldPath, - TOption extends BaseOptionType | DefaultOptionType, + TOption extends DefaultOptionType | GroupedOptionType, > = UseControllerProps & Pick, 'formItem'> & { component?: SelectProps< @@ -29,7 +29,7 @@ type SelectFieldProps< export function SelectField< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, - TOption extends BaseOptionType | DefaultOptionType = DefaultOptionType, + TOption extends DefaultOptionType | GroupedOptionType = DefaultOptionType, >({ formItem, component, diff --git a/src/Select/index.tsx b/src/Select/index.tsx index 2e21d9a5..707144fe 100644 --- a/src/Select/index.tsx +++ b/src/Select/index.tsx @@ -1,5 +1,5 @@ import { Select as AntdSelect, SelectProps as AntdSelectProps } from 'antd'; -import { BaseOptionType, DefaultOptionType } from 'antd/lib/select'; +import { DefaultOptionType } from 'antd/lib/select'; import { BaseSelectRef } from 'rc-select'; import React, { ForwardedRef, @@ -12,17 +12,17 @@ import styled from 'styled-components'; import { fontSizeFromTheme } from '../styled-utils'; +/** As described in https://4x.ant.design/components/select/#components-select-demo-optgroup. */ export type GroupedOptionType = { label: string; - options: BaseOptionType | DefaultOptionType; + options: DefaultOptionType; }; +export type { DefaultOptionType } from 'antd/lib/select'; + export type SelectProps< ValueType = unknown, - OptionType extends - | BaseOptionType - | DefaultOptionType - | GroupedOptionType = DefaultOptionType, + OptionType extends DefaultOptionType | GroupedOptionType = DefaultOptionType, > = AntdSelectProps & RefAttributes; const StyledSelect = styled(AntdSelect)` @@ -52,8 +52,8 @@ const StyledDropdown = styled.div` `; function SelectInner< - ValueType = unknown, - OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType, + ValueType, + OptionType extends DefaultOptionType | GroupedOptionType, >( { children, dropdownRender, ...props }: SelectProps, ref: ForwardedRef, @@ -78,15 +78,9 @@ function SelectInner< ); } -export const Select = forwardRef< - BaseSelectRef, - SelectProps ->(SelectInner) as unknown as (< +export const Select = forwardRef(SelectInner) as unknown as (< TValue = unknown, - TOption extends - | BaseOptionType - | DefaultOptionType - | GroupedOptionType = DefaultOptionType, + TOption extends DefaultOptionType | GroupedOptionType = DefaultOptionType, >( props: SelectProps & RefAttributes, ) => ReactElement) & { From d2e719f62284934184126a08ef2b006bba554010 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 29 Jul 2025 14:50:46 +0200 Subject: [PATCH 02/12] feat: export FilterOptionFunction --- src/Select/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Select/index.tsx b/src/Select/index.tsx index 707144fe..18f9cde8 100644 --- a/src/Select/index.tsx +++ b/src/Select/index.tsx @@ -20,6 +20,8 @@ export type GroupedOptionType = { export type { DefaultOptionType } from 'antd/lib/select'; +export type { FilterFunc as FilterOptionFunction } from 'rc-select/es/Select'; + export type SelectProps< ValueType = unknown, OptionType extends DefaultOptionType | GroupedOptionType = DefaultOptionType, From 7d508b77cfc30f0a3fe8463e76a6a9068079819d Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 29 Jul 2025 14:53:49 +0200 Subject: [PATCH 03/12] fix: options is an Array --- src/Select/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Select/index.tsx b/src/Select/index.tsx index 18f9cde8..fb553185 100644 --- a/src/Select/index.tsx +++ b/src/Select/index.tsx @@ -15,7 +15,7 @@ import { fontSizeFromTheme } from '../styled-utils'; /** As described in https://4x.ant.design/components/select/#components-select-demo-optgroup. */ export type GroupedOptionType = { label: string; - options: DefaultOptionType; + options: Array; }; export type { DefaultOptionType } from 'antd/lib/select'; From 9189668484d025309e6040f12ec7e9177ce67174 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 29 Jul 2025 15:36:11 +0200 Subject: [PATCH 04/12] fix: OptionType and GroupedOptionType with generics --- src/Fields/FieldWrapper.tsx | 17 ++++++++--------- src/Fields/SelectField.tsx | 34 ++++++++++++++++++--------------- src/Fields/index.stories.tsx | 2 +- src/Select/index.tsx | 37 +++++++++++++++++++++--------------- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/Fields/FieldWrapper.tsx b/src/Fields/FieldWrapper.tsx index f726a326..cefa8de1 100644 --- a/src/Fields/FieldWrapper.tsx +++ b/src/Fields/FieldWrapper.tsx @@ -5,7 +5,6 @@ import { UseControllerProps, FieldPathValue, FieldPath, - UnpackNestedValue, } from 'react-hook-form'; import { Form, FormItemProps } from '../Form'; @@ -14,20 +13,20 @@ import { useFieldContext } from './FieldProvider'; export type FieldWrapperProps< TFieldValues extends FieldValues, - TName extends FieldPath, + TFieldPath extends FieldPath, > = { children: ReactNode; - controller: UseControllerProps; - formItem?: FormItemProps< - UnpackNestedValue> - >; + controller: UseControllerProps; + formItem?: FormItemProps>; }; export function FieldWrapper< TFieldValues extends FieldValues, - TName extends FieldPath, ->(props: FieldWrapperProps) { - const { fieldState } = useController(props.controller); + TFieldPath extends FieldPath, +>(props: FieldWrapperProps) { + const { fieldState } = useController( + props.controller, + ); const { formItemProps } = useFieldContext(); diff --git a/src/Fields/SelectField.tsx b/src/Fields/SelectField.tsx index 8f8c3c03..df9206cc 100644 --- a/src/Fields/SelectField.tsx +++ b/src/Fields/SelectField.tsx @@ -1,4 +1,3 @@ -import { DefaultOptionType } from 'antd/lib/select'; import React from 'react'; import { useController, @@ -6,38 +5,43 @@ import { FieldPathValue, FieldValues, UseControllerProps, - UnpackNestedValue, } from 'react-hook-form'; -import { GroupedOptionType, Select, SelectProps } from '../Select'; +import { OptionType, GroupedOptionType, Select, SelectProps } from '../Select'; import { useFieldContext } from './FieldProvider'; import { FieldWrapper, FieldWrapperProps } from './FieldWrapper'; type SelectFieldProps< TFieldValues extends FieldValues, - TName extends FieldPath, - TOption extends DefaultOptionType | GroupedOptionType, -> = UseControllerProps & - Pick, 'formItem'> & { - component?: SelectProps< - UnpackNestedValue>, - TOption - >; + TFieldPath extends FieldPath, + TFieldPathValue extends FieldPathValue, + TOption extends + | OptionType + | GroupedOptionType, +> = UseControllerProps & + Pick, 'formItem'> & { + component?: SelectProps; }; export function SelectField< TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, - TOption extends DefaultOptionType | GroupedOptionType = DefaultOptionType, + TFieldPath extends FieldPath = FieldPath, + TFieldPathValue extends FieldPathValue< + TFieldValues, + TFieldPath + > = FieldPathValue, + TOption extends + | OptionType + | GroupedOptionType = OptionType, >({ formItem, component, ...controller -}: SelectFieldProps) { +}: SelectFieldProps) { const { field: { onChange, ...fieldProps }, - } = useController(controller); + } = useController(controller); const { disabled } = useFieldContext(); diff --git a/src/Fields/index.stories.tsx b/src/Fields/index.stories.tsx index 5e44de38..f5885d52 100644 --- a/src/Fields/index.stories.tsx +++ b/src/Fields/index.stories.tsx @@ -199,7 +199,7 @@ function AllFields() { label: 'Select', }} component={{ - options: ['a', 'b'].map(toFormInputOption), + options: (['a', 'b'] as const).map(toFormInputOption), }} /> ; +export type OptionType = { + label: ReactNode; + value: TValue; }; -export type { DefaultOptionType } from 'antd/lib/select'; +/** As described in https://4x.ant.design/components/select/#components-select-demo-optgroup. */ +export type GroupedOptionType = { + label: ReactNode; + options: Array>; +}; export type { FilterFunc as FilterOptionFunction } from 'rc-select/es/Select'; export type SelectProps< - ValueType = unknown, - OptionType extends DefaultOptionType | GroupedOptionType = DefaultOptionType, -> = AntdSelectProps & RefAttributes; + TValue = unknown, + TOption extends + | OptionType + | GroupedOptionType = OptionType, +> = AntdSelectProps & RefAttributes; const StyledSelect = styled(AntdSelect)` &, @@ -54,10 +59,10 @@ const StyledDropdown = styled.div` `; function SelectInner< - ValueType, - OptionType extends DefaultOptionType | GroupedOptionType, + TValue, + TOptionType extends OptionType | GroupedOptionType, >( - { children, dropdownRender, ...props }: SelectProps, + { children, dropdownRender, ...props }: SelectProps, ref: ForwardedRef, ) { const styledDropdownRender = useCallback( @@ -70,7 +75,7 @@ function SelectInner< ); return ( - + ref={ref} dropdownRender={styledDropdownRender} {...props} @@ -82,9 +87,11 @@ function SelectInner< export const Select = forwardRef(SelectInner) as unknown as (< TValue = unknown, - TOption extends DefaultOptionType | GroupedOptionType = DefaultOptionType, + TOptionType extends + | OptionType + | GroupedOptionType = OptionType, >( - props: SelectProps & RefAttributes, + props: SelectProps & RefAttributes, ) => ReactElement) & { Option: typeof AntdSelect.Option; OptGroup: typeof AntdSelect.OptGroup; From 37dd1424c683b3636150b31ae7ae37bf2090b203 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Wed, 30 Jul 2025 09:42:37 +0200 Subject: [PATCH 05/12] fix: allow disabled --- src/Fields/SelectField.tsx | 10 +++------- src/Select/index.stories.tsx | 30 +++++++++++++++++++++++++++++- src/Select/index.tsx | 23 +++++++++++------------ 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/Fields/SelectField.tsx b/src/Fields/SelectField.tsx index df9206cc..1f6466c8 100644 --- a/src/Fields/SelectField.tsx +++ b/src/Fields/SelectField.tsx @@ -7,7 +7,7 @@ import { UseControllerProps, } from 'react-hook-form'; -import { OptionType, GroupedOptionType, Select, SelectProps } from '../Select'; +import { Select, SelectProps, OptionType } from '../Select'; import { useFieldContext } from './FieldProvider'; import { FieldWrapper, FieldWrapperProps } from './FieldWrapper'; @@ -16,9 +16,7 @@ type SelectFieldProps< TFieldValues extends FieldValues, TFieldPath extends FieldPath, TFieldPathValue extends FieldPathValue, - TOption extends - | OptionType - | GroupedOptionType, + TOption extends OptionType, > = UseControllerProps & Pick, 'formItem'> & { component?: SelectProps; @@ -31,9 +29,7 @@ export function SelectField< TFieldValues, TFieldPath > = FieldPathValue, - TOption extends - | OptionType - | GroupedOptionType = OptionType, + TOption extends OptionType = OptionType, >({ formItem, component, diff --git a/src/Select/index.stories.tsx b/src/Select/index.stories.tsx index 6396e525..669c2bd8 100644 --- a/src/Select/index.stories.tsx +++ b/src/Select/index.stories.tsx @@ -31,7 +31,7 @@ export const Multiple: Story = function Multiple(args) { return ; }; -export const Group: Story = function Group(args) { +export const OptGroup: Story = function OptGroup(args) { return ( ); }; +export const OptionsGroup: Story = function OptionsGroups(args) { + return ( +