Skip to content

Commit 3c8334c

Browse files
committed
feat(Field): add casting property
1 parent 856a1a9 commit 3c8334c

File tree

7 files changed

+84
-7
lines changed

7 files changed

+84
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@cube-dev/ui-kit': minor
3+
---
4+
5+
Add casting property to Field component to cast Field value to different type that input allows

src/components/forms/Form/ComplexForm.stories.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,40 @@ const ComplexErrorTemplate: StoryFn<typeof Form> = (args) => {
203203
);
204204
};
205205

206+
const CastingTemplate: StoryFn<typeof Form> = (args) => {
207+
const [form] = Form.useForm();
208+
209+
return (
210+
<>
211+
<Form
212+
form={form}
213+
{...args}
214+
defaultValues={{
215+
select: 3,
216+
}}
217+
onSubmit={(v) => {
218+
console.log('onSubmit:', v);
219+
}}
220+
onValuesChange={(v) => {
221+
console.log('onChange', v);
222+
}}
223+
>
224+
<Select
225+
name="select"
226+
label="Select field"
227+
tooltip="Additional field description"
228+
casting="number"
229+
>
230+
<Item key="1">One</Item>
231+
<Item key="2">Two</Item>
232+
<Item key="3">Three</Item>
233+
</Select>
234+
<Submit>Submit</Submit>
235+
</Form>
236+
</>
237+
);
238+
};
239+
206240
const Template: StoryFn<typeof Form> = (args) => {
207241
const [form] = Form.useForm();
208242

@@ -405,3 +439,5 @@ UnknownErrorMessage.play = async ({ canvasElement }) => {
405439
expect(alertElement).not.toBeInTheDocument();
406440
});
407441
};
442+
443+
export const ValueCasting = CastingTemplate.bind({});

src/components/forms/Form/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type CubeFieldData<Name extends string, Value> = {
88
readonly name: Name;
99
errors: ReactNode[];
1010
value?: Value;
11-
inputValue?: Value;
11+
inputValue?: Value | string | undefined | null;
1212
touched?: boolean;
1313
rules?: any[];
1414
validating?: boolean;

src/components/forms/Form/use-field/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { CubeFormInstance } from '../use-form';
1010
import { Props } from '../../../../tasty';
1111

12-
export interface CubeFieldProps<T extends FieldTypes> {
12+
export interface CubeFieldProps<T extends FieldTypes, K = unknown> {
1313
/** The initial value of the input. */
1414
defaultValue?: any;
1515
/** The unique ID of the field */
@@ -35,6 +35,12 @@ export interface CubeFieldProps<T extends FieldTypes> {
3535
/** Message for the field. Some additional information or error notice */
3636
message?: ReactNode;
3737
labelProps?: Props;
38+
casting?:
39+
| 'number'
40+
| [
41+
(inputValue: K) => string | null | undefined,
42+
(outputValue: string) => K | null | undefined,
43+
];
3844
}
3945

4046
export type FieldReturnValue<T extends FieldTypes> = {

src/components/forms/Form/use-field/use-field.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,16 @@ export function useField<T extends FieldTypes, Props extends CubeFieldProps<T>>(
5959
validationDelay,
6060
showValid,
6161
shouldUpdate,
62+
casting,
6263
} = props;
6364

65+
if (casting === 'number') {
66+
casting = [
67+
(inputValue: unknown) => String(inputValue) ?? null,
68+
(outputValue: string) => Number(outputValue) ?? null,
69+
];
70+
}
71+
6472
if (rules && rules.length && validationDelay) {
6573
rules.unshift(delayValidationRule(validationDelay));
6674
}
@@ -135,6 +143,14 @@ export function useField<T extends FieldTypes, Props extends CubeFieldProps<T>>(
135143

136144
const field = form.getFieldInstance(fieldName);
137145

146+
if (
147+
casting?.[1] &&
148+
typeof val === 'string' &&
149+
typeof casting?.[1] === 'function'
150+
) {
151+
val = casting[1](val);
152+
}
153+
138154
if (shouldUpdate) {
139155
const fieldsValue = form.getFieldsValue();
140156

@@ -172,11 +188,17 @@ export function useField<T extends FieldTypes, Props extends CubeFieldProps<T>>(
172188
}
173189
});
174190

191+
let inputValue = field?.inputValue;
192+
193+
if (casting?.[0] && inputValue) {
194+
inputValue = casting[0](inputValue);
195+
}
196+
175197
return useMemo(
176198
() => ({
177199
id: fieldId,
178200
name: fieldName,
179-
value: field?.inputValue,
201+
value: inputValue,
180202
validateTrigger,
181203
form,
182204
field,

src/components/forms/wrapper.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function wrapWithField<T extends WrapWithFieldProps>(
2222
labelStyles,
2323
isRequired,
2424
isDisabled,
25+
casting,
2526
necessityIndicator,
2627
message,
2728
messageStyles,
@@ -40,13 +41,13 @@ export function wrapWithField<T extends WrapWithFieldProps>(
4041
return (
4142
<FieldWrapper
4243
{...{
43-
labelPosition,
4444
label,
4545
extra,
46-
styles,
46+
labelPosition,
47+
labelStyles,
4748
isRequired,
4849
isDisabled,
49-
labelStyles,
50+
casting,
5051
necessityIndicator,
5152
labelProps,
5253
fieldProps,
@@ -58,6 +59,7 @@ export function wrapWithField<T extends WrapWithFieldProps>(
5859
tooltip,
5960
isHidden,
6061
labelSuffix,
62+
styles,
6163
children,
6264
Component: component,
6365
ref,

src/shared/form.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type ValidationState = 'invalid' | 'valid';
1313
/** On which event perform the validation for the field */
1414
export type ValidateTrigger = 'onBlur' | 'onChange' | 'onSubmit';
1515

16-
export interface FieldBaseProps extends FormBaseProps {
16+
export interface FieldBaseProps<T = unknown> extends FormBaseProps {
1717
/** The field name */
1818
name?: string;
1919
/** The label of the field */
@@ -59,6 +59,12 @@ export interface FieldBaseProps extends FormBaseProps {
5959
insideForm?: boolean;
6060
fieldProps?: Props;
6161
messageStyles?: Styles;
62+
casting?:
63+
| 'number'
64+
| [
65+
(inputValue: T) => string | null | undefined,
66+
(outputValue: string) => T | null | undefined,
67+
];
6268
}
6369

6470
export interface FormBaseProps {

0 commit comments

Comments
 (0)