From 171440163d1e169af3e476fdec6f4b855c4f7957 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 8 Nov 2023 16:27:57 +0800 Subject: [PATCH 1/7] feat: improved event response for input elements BREAKING CHANGE: puts back the value from `string` to `number`, but the value remains unchanged!!! --- src/utils/valueUtil.ts | 53 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/utils/valueUtil.ts b/src/utils/valueUtil.ts index eb64966aa..33f4b3680 100644 --- a/src/utils/valueUtil.ts +++ b/src/utils/valueUtil.ts @@ -94,8 +94,57 @@ export function isSimilar(source: SimilarObject, target: SimilarObject) { export function defaultGetValueFromEvent(valuePropName: string, ...args: EventArgs) { const event = args[0]; - if (event && event.target && typeof event.target === 'object' && valuePropName in event.target) { - return (event.target as HTMLInputElement)[valuePropName]; + + /** + * `target` is the element that triggered the event (e.g., the user clicked on) + * `currentTarget` is the element that the event listener is attached to. + */ + const nodeName = (event?.target?.nodeName ?? '').toLowerCase(); + + if (nodeName === 'input') { + const type = (event.target.type ?? 'text').toLowerCase(); + + if (['checkbox', 'radio'].includes(type)) { + return event.target.checked; + } + + // `datetime` Obsolete + if (['number', 'range'].includes(type)) { + // https://caniuse.com/?search=valueAsNumber, support IE11+ + return event.target.valueAsNumber ?? event.target.value; + } + + /** + * Problems with backfilling the data collected, so it is not processed here + * @see https://devlog.willcodefor.beer/pages/use-valueasnumber-and-valueasdate-on-inputs/ + * `datetime` Obsolete + */ + // if (['date', 'datetime-local'].includes(type)) { + // const _value = { + // timestamp: event.target.valueAsNumber, + // date: event.target.valueAsDate, + // formatted: event.target.value, + // } + // } + + if (type === 'file') { + return event.target.files; + } + + /** + * text password search email url week month tel color time + * [submit, reset, button, image, hidden] ?? i dont care :) + */ + // return event.target.value; // valuePropName default is 'value' + } + + // because `valuePropName` default is `value` + // if (['textarea', 'select'].includes(nodeName)) { + // return event.target.value; + // } + + if (typeof event?.target === 'object' && valuePropName in event.target) { + return event.target[valuePropName]; } return event; From ba6a23dc574458d05f23e15b8c616ead3d5b1617 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 8 Nov 2023 17:22:27 +0800 Subject: [PATCH 2/7] warn: BREAKING CHANGE case --- tests/legacy/dynamic-binding.test.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/legacy/dynamic-binding.test.tsx b/tests/legacy/dynamic-binding.test.tsx index 7aead6a14..8c7a18a22 100644 --- a/tests/legacy/dynamic-binding.test.tsx +++ b/tests/legacy/dynamic-binding.test.tsx @@ -44,10 +44,11 @@ describe('legacy.dynamic-binding', () => { rerender(); - expect(getInput(container, '#text')?.value).toBe('456'); - expect(form.current?.getFieldValue('name')).toBe('456'); + expect(getInput(container, '#text')?.value).toBe("456"); + expect(form.current?.getFieldValue('name')).toBe(456); const values = await form.current?.validateFields(); - expect(values.name).toBe('456'); + expect(values.name).toBe(456); + expect(typeof values.name).toBe('number'); }); // [Legacy] We do not remove value in Field Form @@ -141,9 +142,10 @@ describe('legacy.dynamic-binding', () => { rerender(); expect(getInput(container, '#text')?.value).toBe('456'); - expect(form.current?.getFieldValue(['name', 'xxx'])).toBe('456'); + expect(form.current?.getFieldValue(['name', 'xxx'])).toBe(456); const values = await form.current?.validateFields(); - expect(values.name.xxx).toBe('456'); + expect(values.name.xxx).toBe(456); + expect(typeof values.name.xxx).toBe('number'); }); it('input with different keys', async () => { @@ -178,10 +180,11 @@ describe('legacy.dynamic-binding', () => { rerender(); expect(getInput(container, '#text')?.value).toBe('456'); - expect(form.current?.getFieldValue('name')).toBe('456'); + expect(form.current?.getFieldValue('name')).toBe(456); const values = await form.current?.validateFields(); - expect(values.name).toBe('456'); + expect(values.name).toBe(456); + expect(typeof values.name).toBe('number'); }); it('submit without removed fields', async () => { From ad91671e710042c1519be7b476bd5805d4c298c9 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 8 Nov 2023 18:00:00 +0800 Subject: [PATCH 3/7] docs: update docs --- docs/demo/value-form-event.md | 4 + docs/examples/value-form-event.tsx | 197 +++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 docs/demo/value-form-event.md create mode 100644 docs/examples/value-form-event.tsx diff --git a/docs/demo/value-form-event.md b/docs/demo/value-form-event.md new file mode 100644 index 000000000..d8b00535a --- /dev/null +++ b/docs/demo/value-form-event.md @@ -0,0 +1,4 @@ +## ValueFromEvent + + + diff --git a/docs/examples/value-form-event.tsx b/docs/examples/value-form-event.tsx new file mode 100644 index 000000000..5cde5167a --- /dev/null +++ b/docs/examples/value-form-event.tsx @@ -0,0 +1,197 @@ +import Form, { Field } from 'rc-field-form'; +import React from 'react'; +import Input from './components/Input'; + +const Divider = (props: React.PropsWithChildren) => ( +
+ 🔽🔽🔽 + {props.children} + 🔽🔽🔽 +
+) + +interface CustomValue { + timestamp: number, + date: Date, + formatted: string, +} + +type FormData = { + // Input + text?: string; // default + + // ========== 🔽 event.target.checked + checkbox?: boolean; + radio?: boolean; + // ========== 🔽 event.target.valueAsNumber + number?: number; + range?: number; + // ========== 🔽 event.target.files + file?: FileList; + + // ========== 🔽 event.target.value(default)!!! + password?: string; + search?: string; + email?: string; + url?: string; + tel?: string; + date?: number; + time?: string; + dateTimeLocal?: string; + week?: string; + month?: string; + color?: string; + + // Select + select?: string; + // Textarea + textarea?: string; + + // Custom + custom?: CustomValue; +}; + + +function App() { + const [form] = Form.useForm(); + + const initialValues: FormData = { + url: 'https://github.com/react-component/field-form', + email: 'wxh16144@users.noreply.github.com', + } + + return ( +
{ + console.log('Finish:', values); + }} + onFieldsChange={fields => { + console.error('fields:', fields); + }} + > + name="text"> + + + + + event.target.checked + + name="checkbox"> + + + + name="radio"> + + + + event.target.valueAsNumber + + name="number"> + + + + name="range"> + + + + event.target.files + + name="file"> + + + + event.target.value default!!! + + name="password"> + + + + name="search"> + + + + name="email"> + + + + name="url"> + + + + name="tel"> + + + + name="date"> + + + + name="dateTimeLocal"> + + + + name="time"> + + + + name="week"> + + + + name="month"> + + + + name="color"> + + + + {/* Select */} + name="select"> + + + +
+ + {/* Textarea */} + name="textarea"> +