From 7c9f23a8b1d66a415d78aa1203bb1dd5591a38e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Miernik?= Date: Thu, 10 Sep 2020 15:43:44 +0200 Subject: [PATCH 1/4] Implemented custom AutoField prototype. --- .../uniforms-unstyled/__tests__/AutoField.tsx | 21 ++++++ packages/uniforms-unstyled/__tests__/index.ts | 1 + packages/uniforms-unstyled/src/AutoField.tsx | 73 +++++++------------ packages/uniforms-unstyled/src/index.ts | 2 +- packages/uniforms/src/createAutoField.tsx | 33 +++++++++ packages/uniforms/src/index.ts | 1 + website/lib/universal.tsx | 1 + 7 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 packages/uniforms/src/createAutoField.tsx diff --git a/packages/uniforms-unstyled/__tests__/AutoField.tsx b/packages/uniforms-unstyled/__tests__/AutoField.tsx index 782325fef..edab3d2b6 100644 --- a/packages/uniforms-unstyled/__tests__/AutoField.tsx +++ b/packages/uniforms-unstyled/__tests__/AutoField.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { AutoField, + AutoFieldContext, BoolField, DateField, ListField, @@ -115,3 +116,23 @@ test(' - uses Component (props)', () => { expect(Component).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (context)', () => { + const FieldA = jest.fn(() => null); + const FieldB = jest.fn(() => null); + + const element = ( + (props['data-component'] === 'A' ? FieldA : FieldB)} + > + <> + + + + + ); + mount(element, createContext({ x: { type: String } })); + + expect(FieldA).toHaveBeenCalledTimes(1); + expect(FieldB).toHaveBeenCalledTimes(1); +}); diff --git a/packages/uniforms-unstyled/__tests__/index.ts b/packages/uniforms-unstyled/__tests__/index.ts index e8c50cae7..e904a35b3 100644 --- a/packages/uniforms-unstyled/__tests__/index.ts +++ b/packages/uniforms-unstyled/__tests__/index.ts @@ -4,6 +4,7 @@ it('exports everything', () => { expect(unstyled).toEqual({ AutoFields: expect.any(Function), AutoField: expect.any(Function), + AutoFieldContext: expect.any(Object), AutoForm: expect.any(Function), BaseForm: expect.any(Function), BoolField: expect.any(Function), diff --git a/packages/uniforms-unstyled/src/AutoField.tsx b/packages/uniforms-unstyled/src/AutoField.tsx index b27f6dfe9..a882b3a50 100644 --- a/packages/uniforms-unstyled/src/AutoField.tsx +++ b/packages/uniforms-unstyled/src/AutoField.tsx @@ -1,6 +1,5 @@ import invariant from 'invariant'; -import { ComponentType, createElement } from 'react'; -import { Override, connectField, useField } from 'uniforms'; +import { createAutoField } from 'uniforms'; import BoolField from './BoolField'; import DateField from './DateField'; @@ -11,53 +10,31 @@ import RadioField from './RadioField'; import SelectField from './SelectField'; import TextField from './TextField'; -export type AutoFieldProps = Override< - Record, - { - component?: ComponentType | ReturnType; - name: string; - } ->; - -export default function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; - const { allowedValues, checkboxes, fieldType } = props; - let { component } = props; +export type AutoFieldProps = Parameters[0]; - if (component === undefined) { - if (allowedValues) { - if (checkboxes && fieldType !== Array) { - component = RadioField; - } else { - component = SelectField; - } - } else { - switch (fieldType) { - case Array: - component = ListField; - break; - case Boolean: - component = BoolField; - break; - case Date: - component = DateField; - break; - case Number: - component = NumField; - break; - case Object: - component = NestField; - break; - case String: - component = TextField; - break; - } +export const { AutoField, AutoFieldContext } = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } - invariant(component, 'Unsupported field type: %s', fieldType); - } + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; } - return 'options' in component && component.options?.kind === 'leaf' - ? createElement(component.Component, props) - : createElement(component, originalProps); -} + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; diff --git a/packages/uniforms-unstyled/src/index.ts b/packages/uniforms-unstyled/src/index.ts index 942c9729c..d7ce14617 100644 --- a/packages/uniforms-unstyled/src/index.ts +++ b/packages/uniforms-unstyled/src/index.ts @@ -1,4 +1,4 @@ -export { default as AutoField, AutoFieldProps } from './AutoField'; +export * from './AutoField'; export { default as AutoFields, AutoFieldsProps } from './AutoFields'; export { default as AutoForm } from './AutoForm'; export { default as BaseForm } from './BaseForm'; diff --git a/packages/uniforms/src/createAutoField.tsx b/packages/uniforms/src/createAutoField.tsx new file mode 100644 index 000000000..970c0d801 --- /dev/null +++ b/packages/uniforms/src/createAutoField.tsx @@ -0,0 +1,33 @@ +import invariant from 'invariant'; +import { ComponentType, createContext, createElement, useContext } from 'react'; + +import { connectField } from './connectField'; +import { Override } from './types'; +import { useField } from './useField'; + +type AutoFieldProps = Override< + Record, + { component?: Component; name: string } +>; + +type Component = ComponentType | ReturnType; + +type ComponentDetector = (props: ReturnType[0]) => Component; + +export function createAutoField(defaultComponentDetector: ComponentDetector) { + const context = createContext(defaultComponentDetector); + + function AutoField(originalProps: AutoFieldProps) { + const props = useField(originalProps.name, originalProps)[0]; + const componentDetector = useContext(context); + const component = props.component ?? componentDetector(props); + + invariant(component, 'AutoField received no component for: %s', props.name); + + return 'options' in component && component.options?.kind === 'leaf' + ? createElement(component.Component, props) + : createElement(component, originalProps); + } + + return { AutoField, AutoFieldContext: context }; +} diff --git a/packages/uniforms/src/index.ts b/packages/uniforms/src/index.ts index b85330ecd..dc72f1e79 100644 --- a/packages/uniforms/src/index.ts +++ b/packages/uniforms/src/index.ts @@ -7,6 +7,7 @@ export * from './ValidatedQuickForm'; export * from './changedKeys'; export * from './connectField'; export * from './context'; +export * from './createAutoField'; export * from './filterDOMProps'; export * from './joinName'; export * from './randomIds'; diff --git a/website/lib/universal.tsx b/website/lib/universal.tsx index c4ac28ed2..2f3b09c1b 100644 --- a/website/lib/universal.tsx +++ b/website/lib/universal.tsx @@ -44,6 +44,7 @@ export const themeContext = createContext('unstyled'); function _createThemedComponent(component: keyof typeof unstyled) { return function ThemedComponent(props: Record) { const theme = useContext(themeContext); + // @ts-expect-error: AutoFieldContext available only in uniforms-unstyled right now. const Component = themes[theme][component]; return ; }; From 0a8cb8285f8b5fdc64e182478faf80c0d9c66d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Miernik?= Date: Mon, 21 Dec 2020 11:51:00 +0100 Subject: [PATCH 2/4] Updated proposal. --- .../uniforms-unstyled/__tests__/AutoField.tsx | 5 ++--- packages/uniforms-unstyled/__tests__/index.ts | 1 - packages/uniforms-unstyled/src/AutoField.tsx | 2 +- packages/uniforms-unstyled/src/index.ts | 2 +- packages/uniforms/src/createAutoField.tsx | 20 ++++++++++++------- website/lib/universal.tsx | 1 - 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/uniforms-unstyled/__tests__/AutoField.tsx b/packages/uniforms-unstyled/__tests__/AutoField.tsx index edab3d2b6..964c8c184 100644 --- a/packages/uniforms-unstyled/__tests__/AutoField.tsx +++ b/packages/uniforms-unstyled/__tests__/AutoField.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { AutoField, - AutoFieldContext, BoolField, DateField, ListField, @@ -122,14 +121,14 @@ test(' - uses Component (context)', () => { const FieldB = jest.fn(() => null); const element = ( - (props['data-component'] === 'A' ? FieldA : FieldB)} > <> - + ); mount(element, createContext({ x: { type: String } })); diff --git a/packages/uniforms-unstyled/__tests__/index.ts b/packages/uniforms-unstyled/__tests__/index.ts index e904a35b3..e8c50cae7 100644 --- a/packages/uniforms-unstyled/__tests__/index.ts +++ b/packages/uniforms-unstyled/__tests__/index.ts @@ -4,7 +4,6 @@ it('exports everything', () => { expect(unstyled).toEqual({ AutoFields: expect.any(Function), AutoField: expect.any(Function), - AutoFieldContext: expect.any(Object), AutoForm: expect.any(Function), BaseForm: expect.any(Function), BoolField: expect.any(Function), diff --git a/packages/uniforms-unstyled/src/AutoField.tsx b/packages/uniforms-unstyled/src/AutoField.tsx index a882b3a50..33e2d1ea8 100644 --- a/packages/uniforms-unstyled/src/AutoField.tsx +++ b/packages/uniforms-unstyled/src/AutoField.tsx @@ -12,7 +12,7 @@ import TextField from './TextField'; export type AutoFieldProps = Parameters[0]; -export const { AutoField, AutoFieldContext } = createAutoField(props => { +const AutoField = createAutoField(props => { if (props.allowedValues) { return props.checkboxes && props.fieldType !== Array ? RadioField diff --git a/packages/uniforms-unstyled/src/index.ts b/packages/uniforms-unstyled/src/index.ts index d7ce14617..942c9729c 100644 --- a/packages/uniforms-unstyled/src/index.ts +++ b/packages/uniforms-unstyled/src/index.ts @@ -1,4 +1,4 @@ -export * from './AutoField'; +export { default as AutoField, AutoFieldProps } from './AutoField'; export { default as AutoFields, AutoFieldsProps } from './AutoFields'; export { default as AutoForm } from './AutoForm'; export { default as BaseForm } from './BaseForm'; diff --git a/packages/uniforms/src/createAutoField.tsx b/packages/uniforms/src/createAutoField.tsx index 970c0d801..43f55d7fd 100644 --- a/packages/uniforms/src/createAutoField.tsx +++ b/packages/uniforms/src/createAutoField.tsx @@ -2,7 +2,7 @@ import invariant from 'invariant'; import { ComponentType, createContext, createElement, useContext } from 'react'; import { connectField } from './connectField'; -import { Override } from './types'; +import { Context, Override } from './types'; import { useField } from './useField'; type AutoFieldProps = Override< @@ -12,22 +12,28 @@ type AutoFieldProps = Override< type Component = ComponentType | ReturnType; -type ComponentDetector = (props: ReturnType[0]) => Component; +type ComponentDetector = ( + props: ReturnType[0], + uniforms: Context, +) => Component; export function createAutoField(defaultComponentDetector: ComponentDetector) { const context = createContext(defaultComponentDetector); - function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; + function AutoField(rawProps: AutoFieldProps) { + const [props, uniforms] = useField(rawProps.name, rawProps); const componentDetector = useContext(context); - const component = props.component ?? componentDetector(props); + const component = props.component ?? componentDetector(props, uniforms); invariant(component, 'AutoField received no component for: %s', props.name); return 'options' in component && component.options?.kind === 'leaf' ? createElement(component.Component, props) - : createElement(component, originalProps); + : createElement(component, rawProps); } - return { AutoField, AutoFieldContext: context }; + AutoField.componentDetectorContext = context; + AutoField.defaultComponentDetector = defaultComponentDetector; + + return AutoField; } diff --git a/website/lib/universal.tsx b/website/lib/universal.tsx index 2f3b09c1b..c4ac28ed2 100644 --- a/website/lib/universal.tsx +++ b/website/lib/universal.tsx @@ -44,7 +44,6 @@ export const themeContext = createContext('unstyled'); function _createThemedComponent(component: keyof typeof unstyled) { return function ThemedComponent(props: Record) { const theme = useContext(themeContext); - // @ts-expect-error: AutoFieldContext available only in uniforms-unstyled right now. const Component = themes[theme][component]; return ; }; From fe3c49f36bbf08bf5dae6bdec6888df6aa2f6801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Miernik?= Date: Thu, 31 Dec 2020 11:08:32 +0100 Subject: [PATCH 3/4] Updated other themes. --- .../uniforms-antd/__tests__/AutoField.tsx | 20 +++++ packages/uniforms-antd/src/AutoField.tsx | 73 +++++++------------ .../__tests__/AutoField.tsx | 20 +++++ .../uniforms-bootstrap3/src/AutoField.tsx | 73 +++++++------------ .../__tests__/AutoField.tsx | 20 +++++ .../uniforms-bootstrap4/src/AutoField.tsx | 73 +++++++------------ .../uniforms-material/__tests__/AutoField.tsx | 20 +++++ packages/uniforms-material/src/AutoField.tsx | 73 +++++++------------ .../uniforms-semantic/__tests__/AutoField.tsx | 22 +++++- packages/uniforms-semantic/src/AutoField.tsx | 73 +++++++------------ 10 files changed, 226 insertions(+), 241 deletions(-) diff --git a/packages/uniforms-antd/__tests__/AutoField.tsx b/packages/uniforms-antd/__tests__/AutoField.tsx index d36f391a1..8a9c8626b 100644 --- a/packages/uniforms-antd/__tests__/AutoField.tsx +++ b/packages/uniforms-antd/__tests__/AutoField.tsx @@ -115,3 +115,23 @@ test(' - uses Component (props)', () => { expect(Component).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (context)', () => { + const FieldA = jest.fn(() => null); + const FieldB = jest.fn(() => null); + + const element = ( + (props['data-component'] === 'A' ? FieldA : FieldB)} + > + <> + + + + + ); + mount(element, createContext({ x: { type: String } })); + + expect(FieldA).toHaveBeenCalledTimes(1); + expect(FieldB).toHaveBeenCalledTimes(1); +}); diff --git a/packages/uniforms-antd/src/AutoField.tsx b/packages/uniforms-antd/src/AutoField.tsx index b27f6dfe9..33e2d1ea8 100644 --- a/packages/uniforms-antd/src/AutoField.tsx +++ b/packages/uniforms-antd/src/AutoField.tsx @@ -1,6 +1,5 @@ import invariant from 'invariant'; -import { ComponentType, createElement } from 'react'; -import { Override, connectField, useField } from 'uniforms'; +import { createAutoField } from 'uniforms'; import BoolField from './BoolField'; import DateField from './DateField'; @@ -11,53 +10,31 @@ import RadioField from './RadioField'; import SelectField from './SelectField'; import TextField from './TextField'; -export type AutoFieldProps = Override< - Record, - { - component?: ComponentType | ReturnType; - name: string; - } ->; - -export default function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; - const { allowedValues, checkboxes, fieldType } = props; - let { component } = props; +export type AutoFieldProps = Parameters[0]; - if (component === undefined) { - if (allowedValues) { - if (checkboxes && fieldType !== Array) { - component = RadioField; - } else { - component = SelectField; - } - } else { - switch (fieldType) { - case Array: - component = ListField; - break; - case Boolean: - component = BoolField; - break; - case Date: - component = DateField; - break; - case Number: - component = NumField; - break; - case Object: - component = NestField; - break; - case String: - component = TextField; - break; - } +const AutoField = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } - invariant(component, 'Unsupported field type: %s', fieldType); - } + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; } - return 'options' in component && component.options?.kind === 'leaf' - ? createElement(component.Component, props) - : createElement(component, originalProps); -} + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; diff --git a/packages/uniforms-bootstrap3/__tests__/AutoField.tsx b/packages/uniforms-bootstrap3/__tests__/AutoField.tsx index 56cc1ceb0..4a751466a 100644 --- a/packages/uniforms-bootstrap3/__tests__/AutoField.tsx +++ b/packages/uniforms-bootstrap3/__tests__/AutoField.tsx @@ -115,3 +115,23 @@ test(' - uses Component (props)', () => { expect(Component).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (context)', () => { + const FieldA = jest.fn(() => null); + const FieldB = jest.fn(() => null); + + const element = ( + (props['data-component'] === 'A' ? FieldA : FieldB)} + > + <> + + + + + ); + mount(element, createContext({ x: { type: String } })); + + expect(FieldA).toHaveBeenCalledTimes(1); + expect(FieldB).toHaveBeenCalledTimes(1); +}); diff --git a/packages/uniforms-bootstrap3/src/AutoField.tsx b/packages/uniforms-bootstrap3/src/AutoField.tsx index b27f6dfe9..33e2d1ea8 100644 --- a/packages/uniforms-bootstrap3/src/AutoField.tsx +++ b/packages/uniforms-bootstrap3/src/AutoField.tsx @@ -1,6 +1,5 @@ import invariant from 'invariant'; -import { ComponentType, createElement } from 'react'; -import { Override, connectField, useField } from 'uniforms'; +import { createAutoField } from 'uniforms'; import BoolField from './BoolField'; import DateField from './DateField'; @@ -11,53 +10,31 @@ import RadioField from './RadioField'; import SelectField from './SelectField'; import TextField from './TextField'; -export type AutoFieldProps = Override< - Record, - { - component?: ComponentType | ReturnType; - name: string; - } ->; - -export default function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; - const { allowedValues, checkboxes, fieldType } = props; - let { component } = props; +export type AutoFieldProps = Parameters[0]; - if (component === undefined) { - if (allowedValues) { - if (checkboxes && fieldType !== Array) { - component = RadioField; - } else { - component = SelectField; - } - } else { - switch (fieldType) { - case Array: - component = ListField; - break; - case Boolean: - component = BoolField; - break; - case Date: - component = DateField; - break; - case Number: - component = NumField; - break; - case Object: - component = NestField; - break; - case String: - component = TextField; - break; - } +const AutoField = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } - invariant(component, 'Unsupported field type: %s', fieldType); - } + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; } - return 'options' in component && component.options?.kind === 'leaf' - ? createElement(component.Component, props) - : createElement(component, originalProps); -} + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; diff --git a/packages/uniforms-bootstrap4/__tests__/AutoField.tsx b/packages/uniforms-bootstrap4/__tests__/AutoField.tsx index b47fa6bd9..ec77e512f 100644 --- a/packages/uniforms-bootstrap4/__tests__/AutoField.tsx +++ b/packages/uniforms-bootstrap4/__tests__/AutoField.tsx @@ -115,3 +115,23 @@ test(' - uses Component (props)', () => { expect(Component).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (context)', () => { + const FieldA = jest.fn(() => null); + const FieldB = jest.fn(() => null); + + const element = ( + (props['data-component'] === 'A' ? FieldA : FieldB)} + > + <> + + + + + ); + mount(element, createContext({ x: { type: String } })); + + expect(FieldA).toHaveBeenCalledTimes(1); + expect(FieldB).toHaveBeenCalledTimes(1); +}); diff --git a/packages/uniforms-bootstrap4/src/AutoField.tsx b/packages/uniforms-bootstrap4/src/AutoField.tsx index b27f6dfe9..33e2d1ea8 100644 --- a/packages/uniforms-bootstrap4/src/AutoField.tsx +++ b/packages/uniforms-bootstrap4/src/AutoField.tsx @@ -1,6 +1,5 @@ import invariant from 'invariant'; -import { ComponentType, createElement } from 'react'; -import { Override, connectField, useField } from 'uniforms'; +import { createAutoField } from 'uniforms'; import BoolField from './BoolField'; import DateField from './DateField'; @@ -11,53 +10,31 @@ import RadioField from './RadioField'; import SelectField from './SelectField'; import TextField from './TextField'; -export type AutoFieldProps = Override< - Record, - { - component?: ComponentType | ReturnType; - name: string; - } ->; - -export default function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; - const { allowedValues, checkboxes, fieldType } = props; - let { component } = props; +export type AutoFieldProps = Parameters[0]; - if (component === undefined) { - if (allowedValues) { - if (checkboxes && fieldType !== Array) { - component = RadioField; - } else { - component = SelectField; - } - } else { - switch (fieldType) { - case Array: - component = ListField; - break; - case Boolean: - component = BoolField; - break; - case Date: - component = DateField; - break; - case Number: - component = NumField; - break; - case Object: - component = NestField; - break; - case String: - component = TextField; - break; - } +const AutoField = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } - invariant(component, 'Unsupported field type: %s', fieldType); - } + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; } - return 'options' in component && component.options?.kind === 'leaf' - ? createElement(component.Component, props) - : createElement(component, originalProps); -} + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; diff --git a/packages/uniforms-material/__tests__/AutoField.tsx b/packages/uniforms-material/__tests__/AutoField.tsx index 5a5d69a03..e237ac4bc 100644 --- a/packages/uniforms-material/__tests__/AutoField.tsx +++ b/packages/uniforms-material/__tests__/AutoField.tsx @@ -115,3 +115,23 @@ test(' - uses Component (props)', () => { expect(Component).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (context)', () => { + const FieldA = jest.fn(() => null); + const FieldB = jest.fn(() => null); + + const element = ( + (props['data-component'] === 'A' ? FieldA : FieldB)} + > + <> + + + + + ); + mount(element, createContext({ x: { type: String } })); + + expect(FieldA).toHaveBeenCalledTimes(1); + expect(FieldB).toHaveBeenCalledTimes(1); +}); diff --git a/packages/uniforms-material/src/AutoField.tsx b/packages/uniforms-material/src/AutoField.tsx index ac88af206..33e2d1ea8 100644 --- a/packages/uniforms-material/src/AutoField.tsx +++ b/packages/uniforms-material/src/AutoField.tsx @@ -1,6 +1,5 @@ import invariant from 'invariant'; -import { ComponentType, createElement } from 'react'; -import { connectField, Override, useField } from 'uniforms'; +import { createAutoField } from 'uniforms'; import BoolField from './BoolField'; import DateField from './DateField'; @@ -11,53 +10,31 @@ import RadioField from './RadioField'; import SelectField from './SelectField'; import TextField from './TextField'; -export type AutoFieldProps = Override< - Record, - { - component?: ComponentType | ReturnType; - name: string; - } ->; - -export default function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; - const { allowedValues, checkboxes, fieldType } = props; - let { component } = props; +export type AutoFieldProps = Parameters[0]; - if (component === undefined) { - if (allowedValues) { - if (checkboxes && fieldType !== Array) { - component = RadioField; - } else { - component = SelectField; - } - } else { - switch (fieldType) { - case Array: - component = ListField; - break; - case Boolean: - component = BoolField; - break; - case Date: - component = DateField; - break; - case Number: - component = NumField; - break; - case Object: - component = NestField; - break; - case String: - component = TextField; - break; - } +const AutoField = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } - invariant(component, 'Unsupported field type: %s', fieldType); - } + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; } - return 'options' in component && component.options?.kind === 'leaf' - ? createElement(component.Component, props) - : createElement(component, originalProps); -} + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; diff --git a/packages/uniforms-semantic/__tests__/AutoField.tsx b/packages/uniforms-semantic/__tests__/AutoField.tsx index 196faf0cc..ceb5240e8 100644 --- a/packages/uniforms-semantic/__tests__/AutoField.tsx +++ b/packages/uniforms-semantic/__tests__/AutoField.tsx @@ -107,7 +107,7 @@ test(' - detects Component (model)', () => { expect(Component).toHaveBeenCalledTimes(1); }); -test(' - detects Component (specified)', () => { +test(' - uses Component (props)', () => { const Component = jest.fn(() => null); const element = ; @@ -115,3 +115,23 @@ test(' - detects Component (specified)', () => { expect(Component).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (context)', () => { + const FieldA = jest.fn(() => null); + const FieldB = jest.fn(() => null); + + const element = ( + (props['data-component'] === 'A' ? FieldA : FieldB)} + > + <> + + + + + ); + mount(element, createContext({ x: { type: String } })); + + expect(FieldA).toHaveBeenCalledTimes(1); + expect(FieldB).toHaveBeenCalledTimes(1); +}); diff --git a/packages/uniforms-semantic/src/AutoField.tsx b/packages/uniforms-semantic/src/AutoField.tsx index ac88af206..33e2d1ea8 100644 --- a/packages/uniforms-semantic/src/AutoField.tsx +++ b/packages/uniforms-semantic/src/AutoField.tsx @@ -1,6 +1,5 @@ import invariant from 'invariant'; -import { ComponentType, createElement } from 'react'; -import { connectField, Override, useField } from 'uniforms'; +import { createAutoField } from 'uniforms'; import BoolField from './BoolField'; import DateField from './DateField'; @@ -11,53 +10,31 @@ import RadioField from './RadioField'; import SelectField from './SelectField'; import TextField from './TextField'; -export type AutoFieldProps = Override< - Record, - { - component?: ComponentType | ReturnType; - name: string; - } ->; - -export default function AutoField(originalProps: AutoFieldProps) { - const props = useField(originalProps.name, originalProps)[0]; - const { allowedValues, checkboxes, fieldType } = props; - let { component } = props; +export type AutoFieldProps = Parameters[0]; - if (component === undefined) { - if (allowedValues) { - if (checkboxes && fieldType !== Array) { - component = RadioField; - } else { - component = SelectField; - } - } else { - switch (fieldType) { - case Array: - component = ListField; - break; - case Boolean: - component = BoolField; - break; - case Date: - component = DateField; - break; - case Number: - component = NumField; - break; - case Object: - component = NestField; - break; - case String: - component = TextField; - break; - } +const AutoField = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } - invariant(component, 'Unsupported field type: %s', fieldType); - } + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; } - return 'options' in component && component.options?.kind === 'leaf' - ? createElement(component.Component, props) - : createElement(component, originalProps); -} + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; From 72fff8118456719471177eb77a68b5a8e8b4761b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Miernik?= Date: Wed, 3 Feb 2021 13:16:52 +0100 Subject: [PATCH 4/4] Added additional tests. --- packages/uniforms-antd/__tests__/AutoField.tsx | 9 +++++++++ packages/uniforms-bootstrap3/__tests__/AutoField.tsx | 9 +++++++++ packages/uniforms-bootstrap4/__tests__/AutoField.tsx | 9 +++++++++ packages/uniforms-material/__tests__/AutoField.tsx | 9 +++++++++ packages/uniforms-semantic/__tests__/AutoField.tsx | 9 +++++++++ packages/uniforms-unstyled/__tests__/AutoField.tsx | 9 +++++++++ 6 files changed, 54 insertions(+) diff --git a/packages/uniforms-antd/__tests__/AutoField.tsx b/packages/uniforms-antd/__tests__/AutoField.tsx index 8a9c8626b..a432c2ce5 100644 --- a/packages/uniforms-antd/__tests__/AutoField.tsx +++ b/packages/uniforms-antd/__tests__/AutoField.tsx @@ -135,3 +135,12 @@ test(' - uses Component (context)', () => { expect(FieldA).toHaveBeenCalledTimes(1); expect(FieldB).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (invalid)', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}); + expect(() => { + const element = ; + mount(element, createContext({ x: { type: Symbol } })); + }).toThrow(/Unsupported field type:/); + spy.mockRestore(); +}); diff --git a/packages/uniforms-bootstrap3/__tests__/AutoField.tsx b/packages/uniforms-bootstrap3/__tests__/AutoField.tsx index 4a751466a..d26e7a3f7 100644 --- a/packages/uniforms-bootstrap3/__tests__/AutoField.tsx +++ b/packages/uniforms-bootstrap3/__tests__/AutoField.tsx @@ -135,3 +135,12 @@ test(' - uses Component (context)', () => { expect(FieldA).toHaveBeenCalledTimes(1); expect(FieldB).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (invalid)', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}); + expect(() => { + const element = ; + mount(element, createContext({ x: { type: Symbol } })); + }).toThrow(/Unsupported field type:/); + spy.mockRestore(); +}); diff --git a/packages/uniforms-bootstrap4/__tests__/AutoField.tsx b/packages/uniforms-bootstrap4/__tests__/AutoField.tsx index ec77e512f..dd8ba92f3 100644 --- a/packages/uniforms-bootstrap4/__tests__/AutoField.tsx +++ b/packages/uniforms-bootstrap4/__tests__/AutoField.tsx @@ -135,3 +135,12 @@ test(' - uses Component (context)', () => { expect(FieldA).toHaveBeenCalledTimes(1); expect(FieldB).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (invalid)', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}); + expect(() => { + const element = ; + mount(element, createContext({ x: { type: Symbol } })); + }).toThrow(/Unsupported field type:/); + spy.mockRestore(); +}); diff --git a/packages/uniforms-material/__tests__/AutoField.tsx b/packages/uniforms-material/__tests__/AutoField.tsx index e237ac4bc..02d0e3d47 100644 --- a/packages/uniforms-material/__tests__/AutoField.tsx +++ b/packages/uniforms-material/__tests__/AutoField.tsx @@ -135,3 +135,12 @@ test(' - uses Component (context)', () => { expect(FieldA).toHaveBeenCalledTimes(1); expect(FieldB).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (invalid)', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}); + expect(() => { + const element = ; + mount(element, createContext({ x: { type: Symbol } })); + }).toThrow(/Unsupported field type:/); + spy.mockRestore(); +}); diff --git a/packages/uniforms-semantic/__tests__/AutoField.tsx b/packages/uniforms-semantic/__tests__/AutoField.tsx index ceb5240e8..28b7de9fe 100644 --- a/packages/uniforms-semantic/__tests__/AutoField.tsx +++ b/packages/uniforms-semantic/__tests__/AutoField.tsx @@ -135,3 +135,12 @@ test(' - uses Component (context)', () => { expect(FieldA).toHaveBeenCalledTimes(1); expect(FieldB).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (invalid)', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}); + expect(() => { + const element = ; + mount(element, createContext({ x: { type: Symbol } })); + }).toThrow(/Unsupported field type:/); + spy.mockRestore(); +}); diff --git a/packages/uniforms-unstyled/__tests__/AutoField.tsx b/packages/uniforms-unstyled/__tests__/AutoField.tsx index 964c8c184..2bd06da5e 100644 --- a/packages/uniforms-unstyled/__tests__/AutoField.tsx +++ b/packages/uniforms-unstyled/__tests__/AutoField.tsx @@ -135,3 +135,12 @@ test(' - uses Component (context)', () => { expect(FieldA).toHaveBeenCalledTimes(1); expect(FieldB).toHaveBeenCalledTimes(1); }); + +test(' - uses Component (invalid)', () => { + const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {}); + expect(() => { + const element = ; + mount(element, createContext({ x: { type: Symbol } })); + }).toThrow(/Unsupported field type:/); + spy.mockRestore(); +});