From ddf996a583b00052e1e8e0e031c616eb026c0f37 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 5 Nov 2024 14:08:11 +0100 Subject: [PATCH] Remove field type checking within dataform layouts --- .../src/components/dataform-context/index.tsx | 4 +- .../dataform/stories/index.story.tsx | 110 ++++++++++-------- .../dataforms-layouts/data-form-layout.tsx | 24 ++-- .../dataforms-layouts/get-visible-fields.ts | 16 --- .../src/dataforms-layouts/panel/index.tsx | 23 ++-- .../src/dataforms-layouts/regular/index.tsx | 14 ++- packages/dataviews/src/types.ts | 18 ++- 7 files changed, 107 insertions(+), 102 deletions(-) delete mode 100644 packages/dataviews/src/dataforms-layouts/get-visible-fields.ts diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx index 49170747472e12..e3973ad09b1272 100644 --- a/packages/dataviews/src/components/dataform-context/index.tsx +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -10,7 +10,7 @@ import type { FormField, NormalizedField } from '../../types'; type DataFormContextType< Item > = { getFieldDefinition: ( - field: FormField + field: FormField | string ) => NormalizedField< Item > | undefined; }; @@ -23,7 +23,7 @@ export function DataFormProvider< Item >( { children, }: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { const getFieldDefinition = useCallback( - ( field: FormField ) => { + ( field: FormField | string ) => { const fieldId = typeof field === 'string' ? field : field.id; const definition = fields.find( diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index e202ce8acba8cf..849703ef8ad2d8 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -8,7 +8,7 @@ import { ToggleControl } from '@wordpress/components'; * Internal dependencies */ import DataForm from '../index'; -import type { Field, Form } from '../../../types'; +import type { Field, Form, FormField } from '../../../types'; type SamplePost = { title: string; @@ -123,6 +123,33 @@ const fields = [ }, ] as Field< SamplePost >[]; +function toFormField( + formFields: Array< string | FormField >, + labelPosition: 'default' | 'top' | 'side', + type: 'panel' | 'regular' +): FormField[] { + return formFields.map( ( field ) => { + if ( typeof field === 'string' ) { + return { + id: field, + layout: type, + labelPosition: + labelPosition === 'default' ? undefined : labelPosition, + }; + } else if ( + typeof field !== 'string' && + field.children && + type !== 'panel' + ) { + return { + ...field, + children: toFormField( field.children, labelPosition, type ), + }; + } + return field; + } ); +} + export const Default = ( { type, labelPosition, @@ -143,32 +170,27 @@ export const Default = ( { const form = useMemo( () => ( { - fields: [ - 'title', - 'order', - { - id: 'sticky', - layout: 'regular', - labelPosition: type === 'regular' ? labelPosition : 'side', - }, - 'author', - 'reviewer', - 'password', - 'date', - 'birthdate', - ].map( ( field ) => { - if ( - labelPosition !== 'default' && - typeof field === 'string' - ) { - return { - id: field, - layout: type, - labelPosition, - }; - } - return field; - } ), + fields: toFormField( + [ + 'title', + 'order', + { + id: 'sticky', + layout: 'regular', + labelPosition: + type === 'regular' && labelPosition !== 'default' + ? labelPosition + : 'side', + }, + 'author', + 'reviewer', + 'password', + 'date', + 'birthdate', + ], + labelPosition, + type + ), } ), [ type, labelPosition ] ) as Form; @@ -210,27 +232,19 @@ const CombinedFieldsComponent = ( { const form = useMemo( () => ( { - fields: [ - 'title', - { - id: 'status', - children: [ 'status', 'password' ], - }, - 'order', - 'author', - ].map( ( field ) => { - if ( - labelPosition !== 'default' && - typeof field === 'string' - ) { - return { - id: field, - layout: type, - labelPosition, - }; - } - return field; - } ), + fields: toFormField( + [ + 'title', + { + id: 'status', + children: [ 'status', 'password' ], + }, + 'order', + 'author', + ], + labelPosition, + type + ), } ), [ type, labelPosition ] ) as Form; diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index bd7d53b431752d..086d7950885079 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -20,7 +20,7 @@ export function DataFormLayout< Item >( { }: { defaultLayout?: string; data: Item; - fields: FormField[]; + fields: Array< FormField | string >; onChange: ( value: any ) => void; children?: ( FieldLayout: ( props: { @@ -37,10 +37,15 @@ export function DataFormLayout< Item >( { return ( { fields.map( ( field ) => { - const fieldLayoutId = - typeof field !== 'string' && field.layout - ? field.layout - : defaultLayout; + const formField: FormField = + typeof field !== 'string' + ? field + : { + id: field, + }; + const fieldLayoutId = formField.layout + ? formField.layout + : defaultLayout; const FieldLayout = getFormFieldLayout( fieldLayoutId ?? 'regular' )?.component; @@ -49,8 +54,7 @@ export function DataFormLayout< Item >( { return null; } - const fieldId = typeof field === 'string' ? field : field.id; - const fieldDefinition = getFieldDefinition( fieldId ); + const fieldDefinition = getFieldDefinition( formField ); if ( ! fieldDefinition || @@ -61,14 +65,14 @@ export function DataFormLayout< Item >( { } if ( children ) { - return children( FieldLayout, field ); + return children( FieldLayout, formField ); } return ( ); diff --git a/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts b/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts deleted file mode 100644 index b7cbd679782335..00000000000000 --- a/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Internal dependencies - */ -import type { Field, FormField } from '../types'; - -export function getVisibleFields< Item >( - fields: Field< Item >[], - formFields: FormField[] = [] -): Field< Item >[] { - const visibleFields: Array< Field< Item > > = [ ...fields ]; - return formFields - .map( ( fieldId ) => - visibleFields.find( ( { id } ) => id === fieldId ) - ) - .filter( ( field ): field is Field< Item > => !! field ); -} diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 95c43279655b05..067678754df2b1 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -69,14 +69,18 @@ function PanelDropdown< Item >( { popoverAnchor: HTMLElement | null; } & FormFieldProps< Item > ) { const childrenFields = useMemo( () => { - const isFieldObject = typeof field !== 'string'; - if ( isFieldObject && field.children ) { - return field.children; + if ( field.children ) { + return field.children.map( ( child ) => { + if ( typeof child === 'string' ) { + return { + id: child, + }; + } + return child; + } ); } - if ( isFieldObject && field.id ) { - return [ field.id ]; - } - return [ field ]; + // If not explicit children return the field id itself. + return [ { id: field.id } ]; }, [ field ] ); // Memoize popoverProps to avoid returning a new object every time. @@ -159,10 +163,7 @@ export default function FormPanelField< Item >( { }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); - const labelPosition = - typeof field !== 'string' && field.labelPosition - ? field.labelPosition - : 'side'; + const labelPosition = field.labelPosition ?? 'side'; // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 113408f8ee6abb..97693268ff78f8 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -46,13 +46,17 @@ export default function FormRegularField< Item >( { }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); - const labelPosition = - typeof field !== 'string' && field.labelPosition - ? field.labelPosition - : 'top'; + const labelPosition = field.labelPosition ?? 'top'; const childrenFields = useMemo( () => { if ( typeof field !== 'string' && field.children ) { - return field.children; + return field.children.map( ( child ) => { + if ( typeof child === 'string' ) { + return { + id: child, + }; + } + return child; + } ); } return []; }, [ field ] ); diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 29eb2c2f7d7364..f0e4ebf0cd2f51 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -525,22 +525,20 @@ export interface SupportedLayouts { table?: Omit< ViewTable, 'type' >; } -export type FormField = - | string - | { - id: string; - label?: string; - layout?: 'regular' | 'panel'; - labelPosition?: 'side' | 'top'; - children?: FormField[]; - }; +export type FormField = { + id: string; + label?: string; + layout?: 'regular' | 'panel'; + labelPosition?: 'side' | 'top'; + children?: Array< FormField | string >; +}; /** * The form configuration. */ export type Form = { type?: 'regular' | 'panel'; - fields?: FormField[]; + fields?: Array< FormField | string >; }; export interface DataFormProps< Item > {