From 2f49a88f841636d90707dcc3c4ac1e40687d9c80 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Fri, 4 Oct 2024 09:28:00 +0200 Subject: [PATCH 01/34] Add DataFormProvider for fields --- .../src/components/dataform-context/index.tsx | 40 +++++++++++++++++++ .../src/components/dataform/index.tsx | 19 ++++++++- .../dataforms-layouts/get-visible-fields.ts | 3 +- packages/dataviews/src/types.ts | 10 ++++- 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 packages/dataviews/src/components/dataform-context/index.tsx diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx new file mode 100644 index 0000000000000..c5a62bbe7c952 --- /dev/null +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { createContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import type { NormalizedField } from '../../types'; + +type DataFormContextType< Item > = { + getFieldDefinition: ( + field: string + ) => NormalizedField< Item > | undefined; +}; + +const DataFormContext = createContext< DataFormContextType< any > >( { + getFieldDefinition: () => undefined, +} ); + +export function DataFormProvider< Item >( { + fields, + children, +}: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { + // const context = useContext( DataFormContext ); + + function getFieldDefinition( field: string ) { + return fields.find( + ( fieldDefinition ) => fieldDefinition.id === field + ); + } + + return ( + + { children } + + ); +} + +export default DataFormContext; diff --git a/packages/dataviews/src/components/dataform/index.tsx b/packages/dataviews/src/components/dataform/index.tsx index 58f0bf06afb41..c1e3555060fd1 100644 --- a/packages/dataviews/src/components/dataform/index.tsx +++ b/packages/dataviews/src/components/dataform/index.tsx @@ -1,17 +1,34 @@ +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; + /** * Internal dependencies */ import type { DataFormProps } from '../../types'; import { getFormLayout } from '../../dataforms-layouts'; +import { DataFormProvider } from '../dataform-context'; +import { normalizeFields } from '../../normalize-fields'; export default function DataForm< Item >( { form, ...props }: DataFormProps< Item > ) { const layout = getFormLayout( form.type ?? 'regular' ); + + const normalizedFields = useMemo( + () => normalizeFields( props.fields ), + [ props.fields ] + ); + if ( ! layout ) { return null; } - return ; + return ( + + { ' ' } + + ); } diff --git a/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts b/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts index d95d59a88394e..4a87b4f3bdc60 100644 --- a/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts +++ b/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts @@ -6,11 +6,12 @@ import type { Field, CombinedFormField, NormalizedCombinedFormField, + FormField, } from '../types'; export function getVisibleFields< Item >( fields: Field< Item >[], - formFields: string[] = [], + formFields: FormField[] = [], combinedFields?: CombinedFormField< Item >[] ): Field< Item >[] { const visibleFields: Array< diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 71990f72d4eec..45eba01092213 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -543,12 +543,20 @@ export type NormalizedCombinedFormField< Item > = CombinedFormField< Item > & { Edit?: ComponentType< DataFormCombinedEditProps< Item > >; }; +export type FormField = + | string + | { + layout?: 'regular' | 'panel' | 'group'; + field?: string; + fields?: FormField[]; + }; + /** * The form configuration. */ export type Form< Item > = { type?: 'regular' | 'panel'; - fields?: string[]; + fields?: FormField[]; /** * The fields to combine. */ From 7fc515aa72279214daa87071ab91693f94f53bc5 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:20:22 +0000 Subject: [PATCH 02/34] Add dataform layout component and inline layout --- .../src/components/dataform-context/index.tsx | 15 +++-- .../src/components/dataform/index.tsx | 13 ++-- .../dataform/stories/index.story.tsx | 55 +++++++++-------- .../dataforms-layouts/data-form-layout.tsx | 61 +++++++++++++++++++ .../dataviews/src/dataforms-layouts/index.tsx | 24 +++++++- .../src/dataforms-layouts/inline/index.tsx | 47 ++++++++++++++ .../src/dataforms-layouts/panel/index.tsx | 57 ++++++++++++----- .../src/dataforms-layouts/regular/index.tsx | 35 ++++++++++- packages/dataviews/src/types.ts | 5 +- 9 files changed, 254 insertions(+), 58 deletions(-) create mode 100644 packages/dataviews/src/dataforms-layouts/data-form-layout.tsx create mode 100644 packages/dataviews/src/dataforms-layouts/inline/index.tsx diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx index c5a62bbe7c952..0b6dab93ddef8 100644 --- a/packages/dataviews/src/components/dataform-context/index.tsx +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { createContext } from '@wordpress/element'; +import { createContext, useCallback } from '@wordpress/element'; /** * Internal dependencies @@ -24,11 +24,14 @@ export function DataFormProvider< Item >( { }: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { // const context = useContext( DataFormContext ); - function getFieldDefinition( field: string ) { - return fields.find( - ( fieldDefinition ) => fieldDefinition.id === field - ); - } + const getFieldDefinition = useCallback( + ( field: string ) => { + return fields.find( + ( fieldDefinition ) => fieldDefinition.id === field + ); + }, + [ fields ] + ); return ( diff --git a/packages/dataviews/src/components/dataform/index.tsx b/packages/dataviews/src/components/dataform/index.tsx index c1e3555060fd1..ae03905957668 100644 --- a/packages/dataviews/src/components/dataform/index.tsx +++ b/packages/dataviews/src/components/dataform/index.tsx @@ -7,28 +7,31 @@ import { useMemo } from '@wordpress/element'; * Internal dependencies */ import type { DataFormProps } from '../../types'; -import { getFormLayout } from '../../dataforms-layouts'; import { DataFormProvider } from '../dataform-context'; import { normalizeFields } from '../../normalize-fields'; +import { DataFormLayout } from '../../dataforms-layouts/data-form-layout'; export default function DataForm< Item >( { form, ...props }: DataFormProps< Item > ) { - const layout = getFormLayout( form.type ?? 'regular' ); - const normalizedFields = useMemo( () => normalizeFields( props.fields ), [ props.fields ] ); - if ( ! layout ) { + if ( ! form.fields ) { return null; } return ( - { ' ' } + ); } diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index b59d79063200b..97292141d363b 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -7,7 +7,7 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import DataForm from '../index'; -import type { CombinedFormField, Field } from '../../../types'; +import type { Field, Form } from '../../../types'; type SamplePost = { title: string; @@ -27,8 +27,8 @@ const meta = { type: { control: { type: 'select' }, description: - 'Chooses the layout of the form. "regular" is the default layout.', - options: [ 'regular', 'panel' ], + 'Chooses the default layout of each field. "regular" is the default layout.', + options: [ 'regular', 'panel', 'inline' ], }, }, }; @@ -99,7 +99,11 @@ const fields = [ }, ] as Field< SamplePost >[]; -export const Default = ( { type }: { type: 'panel' | 'regular' } ) => { +export const Default = ( { + type, +}: { + type: 'panel' | 'regular' | 'inline'; +} ) => { const [ post, setPost ] = useState( { title: 'Hello, World!', order: 2, @@ -114,14 +118,18 @@ export const Default = ( { type }: { type: 'panel' | 'regular' } ) => { fields: [ 'title', 'order', + { + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], + }, 'author', 'reviewer', - 'status', 'password', 'date', 'birthdate', ], - }; + } as Form< SamplePost >; return ( @@ -143,33 +151,34 @@ export const Default = ( { type }: { type: 'panel' | 'regular' } ) => { const CombinedFieldsComponent = ( { type = 'regular', - combinedFieldDirection = 'vertical', }: { - type: 'panel' | 'regular'; - combinedFieldDirection: 'vertical' | 'horizontal'; + type: 'panel' | 'regular' | 'inline'; } ) => { - const [ post, setPost ] = useState( { + const [ post, setPost ] = useState< SamplePost >( { title: 'Hello, World!', order: 2, author: 1, status: 'draft', + reviewer: 'fulano', + date: '2021-01-01T12:00:00', + birthdate: '1950-02-23T12:00:00', } ); const form = { - fields: [ 'title', 'status_and_visibility', 'order', 'author' ], - combinedFields: [ + fields: [ + 'title', { - id: 'status_and_visibility', - label: 'Status & Visibility', - children: [ 'status', 'password' ], - direction: combinedFieldDirection, - render: ( { item } ) => item.status, + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], }, - ] as CombinedFormField< any >[], - }; + 'order', + 'author', + ], + } as Form< SamplePost >; return ( - data={ post } fields={ fields } form={ { @@ -191,11 +200,5 @@ export const CombinedFields = { render: CombinedFieldsComponent, argTypes: { ...meta.argTypes, - combinedFieldDirection: { - control: { type: 'select' }, - description: - 'Chooses the direction of the combined field. "vertical" is the default layout.', - options: [ 'vertical', 'horizontal' ], - }, }, }; diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx new file mode 100644 index 0000000000000..b6e484e5155a3 --- /dev/null +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -0,0 +1,61 @@ +/** + * WordPress dependencies + */ +import { __experimentalVStack as VStack } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import type { FormField } from '../types'; +import { getFormFieldLayout } from './index'; + +export function DataFormLayout< Item >( { + defaultLayout, + data, + fields, + onChange, + children, +}: { + defaultLayout?: string; + data: Item; + fields: FormField[]; + onChange: ( value: any ) => void; + children?: ( + FieldLayout: ( props: { + data: Item; + field: FormField; + onChange: ( value: any ) => void; + hideLabelFromVision?: boolean; + } ) => React.JSX.Element | null, + field: FormField + ) => React.JSX.Element; +} ) { + return ( + + { fields.map( ( field ) => { + const fieldLayoutId = + typeof field === 'string' ? defaultLayout : field.layout; + const FieldLayout = getFormFieldLayout( + fieldLayoutId ?? 'regular' + )?.component; + + if ( ! FieldLayout ) { + return null; + } + + if ( children ) { + return children( FieldLayout, field ); + } + + return ( + + ); + } ) } + + ); +} diff --git a/packages/dataviews/src/dataforms-layouts/index.tsx b/packages/dataviews/src/dataforms-layouts/index.tsx index 9434ea724ed4c..3d0152c2a9f48 100644 --- a/packages/dataviews/src/dataforms-layouts/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/index.tsx @@ -1,8 +1,9 @@ /** * Internal dependencies */ -import FormRegular from './regular'; -import FormPanel from './panel'; +import FormRegular, { FormRegularField } from './regular'; +import FormPanel, { FormPanelField } from './panel'; +import { FormInlineField } from './inline'; const FORM_LAYOUTS = [ { @@ -18,3 +19,22 @@ const FORM_LAYOUTS = [ export function getFormLayout( type: string ) { return FORM_LAYOUTS.find( ( layout ) => layout.type === type ); } + +const FORM_FIELD_LAYOUTS = [ + { + type: 'regular', + component: FormRegularField, + }, + { + type: 'panel', + component: FormPanelField, + }, + { + type: 'inline', + component: FormInlineField, + }, +]; + +export function getFormFieldLayout( type: string ) { + return FORM_FIELD_LAYOUTS.find( ( layout ) => layout.type === type ); +} diff --git a/packages/dataviews/src/dataforms-layouts/inline/index.tsx b/packages/dataviews/src/dataforms-layouts/inline/index.tsx new file mode 100644 index 0000000000000..571850220a26d --- /dev/null +++ b/packages/dataviews/src/dataforms-layouts/inline/index.tsx @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { __experimentalHStack as HStack } from '@wordpress/components'; +import { useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import type { FormField } from '../../types'; +import DataFormContext from '../../components/dataform-context'; + +interface FormFieldProps< Item > { + data: Item; + field: FormField; + onChange: ( value: any ) => void; +} + +export function FormInlineField< Item >( { + data, + field, + onChange, +}: FormFieldProps< Item > ) { + const { getFieldDefinition } = useContext( DataFormContext ); + const fieldDefinition = getFieldDefinition( + typeof field === 'string' ? field : field.id + ); + if ( ! fieldDefinition ) { + return null; + } + return ( + +
+ { fieldDefinition.label } +
+
+ +
+
+ ); +} diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index b74e5e4667d4b..34c7eedc420a5 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -9,8 +9,8 @@ import { Dropdown, Button, } from '@wordpress/components'; -import { useState, useMemo } from '@wordpress/element'; -import { sprintf, __, _x } from '@wordpress/i18n'; +import { sprintf, __ } from '@wordpress/i18n'; +import { useState, useMemo, useContext } from '@wordpress/element'; import { closeSmall } from '@wordpress/icons'; /** @@ -18,12 +18,14 @@ import { closeSmall } from '@wordpress/icons'; */ import { normalizeFields } from '../../normalize-fields'; import { getVisibleFields } from '../get-visible-fields'; -import type { DataFormProps, NormalizedField } from '../../types'; +import type { DataFormProps } from '../../types'; import FormFieldVisibility from '../../components/form-field-visibility'; +import DataFormContext from '../../components/dataform-context'; +import { DataFormLayout } from '../data-form-layout'; interface FormFieldProps< Item > { data: Item; - field: NormalizedField< Item >; + field: FormField; onChange: ( value: any ) => void; } @@ -57,11 +59,21 @@ function DropdownHeader( { ); } -function FormField< Item >( { +export function FormPanelField< Item >( { data, field, onChange, }: FormFieldProps< Item > ) { + const { getFieldDefinition } = useContext( DataFormContext ); + const fieldDefinition = getFieldDefinition( + typeof field === 'string' ? field : field.id + ); + const childrenFields = useMemo( () => { + if ( typeof field !== 'string' && field.fields ) { + return field.fields; + } + return [ field ]; + }, [ field ] ); // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >( @@ -80,13 +92,17 @@ function FormField< Item >( { [ popoverAnchor ] ); + if ( ! fieldDefinition ) { + return null; + } + return (
- { field.label } + { fieldDefinition.label }
( { aria-expanded={ isOpen } aria-label={ sprintf( // translators: %s: Field name. - _x( 'Edit %s', 'field' ), - field.label + __( 'Edit %s' ), + fieldDefinition.label ) } onClick={ onToggle } > - + ) } renderContent={ ( { onClose } ) => ( <> - + > + { ( FieldLayout, nestedField ) => ( + + ) } + ) } /> diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 6a340a50584df..4ff0149bac560 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -2,15 +2,46 @@ * WordPress dependencies */ import { __experimentalVStack as VStack } from '@wordpress/components'; -import { useMemo } from '@wordpress/element'; +import { useContext, useMemo } from '@wordpress/element'; /** * Internal dependencies */ import { normalizeFields } from '../../normalize-fields'; import { getVisibleFields } from '../get-visible-fields'; -import type { DataFormProps } from '../../types'; +import type { DataFormProps, FormField } from '../../types'; import FormFieldVisibility from '../../components/form-field-visibility'; +import DataFormContext from '../../components/dataform-context'; + +interface FormFieldProps< Item > { + data: Item; + field: FormField; + onChange: ( value: any ) => void; + hideLabelFromVision?: boolean; +} + +export function FormRegularField< Item >( { + data, + field, + onChange, + hideLabelFromVision, +}: FormFieldProps< Item > ) { + const { getFieldDefinition } = useContext( DataFormContext ); + const fieldDefinition = getFieldDefinition( + typeof field === 'string' ? field : field.id + ); + if ( ! fieldDefinition ) { + return null; + } + return ( + + ); +} export default function FormRegular< Item >( { data, diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 45eba01092213..82979d59e72a1 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -546,7 +546,8 @@ export type NormalizedCombinedFormField< Item > = CombinedFormField< Item > & { export type FormField = | string | { - layout?: 'regular' | 'panel' | 'group'; + id: string; + layout?: 'regular' | 'panel' | 'inline'; field?: string; fields?: FormField[]; }; @@ -555,7 +556,7 @@ export type FormField = * The form configuration. */ export type Form< Item > = { - type?: 'regular' | 'panel'; + type?: 'regular' | 'panel' | 'inline'; fields?: FormField[]; /** * The fields to combine. From 21907735b785cd1bb6124ba50b63c71befd0f354 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:30:12 +0000 Subject: [PATCH 03/34] Fix label in panel view --- packages/dataviews/src/dataforms-layouts/panel/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 34c7eedc420a5..657c936a1ba3f 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -151,6 +151,9 @@ export function FormPanelField< Item >( { data={ data } field={ nestedField } onChange={ onChange } + hideLabelFromVision={ + childrenFields.length < 2 + } /> ) } From 35fa64cc7c4818db6ae7ea7f9457f3a049a0e538 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:43:38 +0000 Subject: [PATCH 04/34] Remove unneeded line --- packages/dataviews/src/components/dataform-context/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx index 0b6dab93ddef8..7b59ef1fad296 100644 --- a/packages/dataviews/src/components/dataform-context/index.tsx +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -22,8 +22,6 @@ export function DataFormProvider< Item >( { fields, children, }: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { - // const context = useContext( DataFormContext ); - const getFieldDefinition = useCallback( ( field: string ) => { return fields.find( From c84817857877b1d75b7860b14ce1823764192e8d Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:49:59 +0000 Subject: [PATCH 05/34] Update `field` to FormField as well --- packages/dataviews/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 82979d59e72a1..2db60155c6c4f 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -548,7 +548,7 @@ export type FormField = | { id: string; layout?: 'regular' | 'panel' | 'inline'; - field?: string; + field?: FormField; fields?: FormField[]; }; From 7824f36b85a53ead002b3b8dba8561f86629c02d Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 12:48:40 +0100 Subject: [PATCH 06/34] Remove combinedFields usage --- .../dataform-combined-edit/index.tsx | 69 ------------------- .../dataform-combined-edit/style.scss | 16 ----- .../dataform/stories/index.story.tsx | 4 +- .../dataforms-layouts/data-form-layout.tsx | 17 ++++- .../dataforms-layouts/get-visible-fields.ts | 20 +----- .../src/dataforms-layouts/panel/index.tsx | 16 ++--- .../src/dataforms-layouts/regular/index.tsx | 10 +-- packages/dataviews/src/normalize-fields.ts | 34 +-------- packages/dataviews/src/style.scss | 1 - packages/dataviews/src/types.ts | 25 +------ packages/dataviews/src/validation.ts | 2 +- .../src/components/post-edit/index.js | 14 ++-- 12 files changed, 37 insertions(+), 191 deletions(-) delete mode 100644 packages/dataviews/src/components/dataform-combined-edit/index.tsx delete mode 100644 packages/dataviews/src/components/dataform-combined-edit/style.scss diff --git a/packages/dataviews/src/components/dataform-combined-edit/index.tsx b/packages/dataviews/src/components/dataform-combined-edit/index.tsx deleted file mode 100644 index 90a92ac861bdd..0000000000000 --- a/packages/dataviews/src/components/dataform-combined-edit/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * WordPress dependencies - */ -import { - __experimentalHStack as HStack, - __experimentalVStack as VStack, - __experimentalHeading as Heading, - __experimentalSpacer as Spacer, -} from '@wordpress/components'; - -/** - * Internal dependencies - */ -import type { DataFormCombinedEditProps, NormalizedField } from '../../types'; -import FormFieldVisibility from '../form-field-visibility'; - -function Header( { title }: { title: string } ) { - return ( - - - - { title } - - - - - ); -} - -function DataFormCombinedEdit< Item >( { - field, - data, - onChange, - hideLabelFromVision, -}: DataFormCombinedEditProps< Item > ) { - const className = 'dataforms-combined-edit'; - const visibleChildren = ( field.children ?? [] ) - .map( ( fieldId ) => field.fields.find( ( { id } ) => id === fieldId ) ) - .filter( - ( childField ): childField is NormalizedField< Item > => - !! childField - ); - const children = visibleChildren.map( ( child ) => { - return ( - -
- -
-
- ); - } ); - - const Stack = field.direction === 'horizontal' ? HStack : VStack; - - return ( - <> - { ! hideLabelFromVision &&
} - - { children } - - - ); -} - -export default DataFormCombinedEdit; diff --git a/packages/dataviews/src/components/dataform-combined-edit/style.scss b/packages/dataviews/src/components/dataform-combined-edit/style.scss deleted file mode 100644 index 97e052ed89798..0000000000000 --- a/packages/dataviews/src/components/dataform-combined-edit/style.scss +++ /dev/null @@ -1,16 +0,0 @@ -.dataforms-layouts-panel__field-dropdown { - .dataforms-combined-edit { - border: none; - padding: 0; - } -} - -.dataforms-combined-edit { - &__field { - flex: 1 1 auto; - } - - p.components-base-control__help:has(.components-checkbox-control__help) { - margin-top: $grid-unit-05; - } -} diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 97292141d363b..3c22359f6749c 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -129,7 +129,7 @@ export const Default = ( { 'date', 'birthdate', ], - } as Form< SamplePost >; + } as Form; return ( @@ -175,7 +175,7 @@ const CombinedFieldsComponent = ( { 'order', 'author', ], - } as Form< SamplePost >; + } as Form; return ( diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index b6e484e5155a3..a50cdee7d12e9 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -2,12 +2,14 @@ * WordPress dependencies */ import { __experimentalVStack as VStack } from '@wordpress/components'; +import { useContext } from '@wordpress/element'; /** * Internal dependencies */ import type { FormField } from '../types'; import { getFormFieldLayout } from './index'; +import DataFormContext from '../components/dataform-context'; export function DataFormLayout< Item >( { defaultLayout, @@ -30,6 +32,8 @@ export function DataFormLayout< Item >( { field: FormField ) => React.JSX.Element; } ) { + const { getFieldDefinition } = useContext( DataFormContext ); + return ( { fields.map( ( field ) => { @@ -43,13 +47,24 @@ export function DataFormLayout< Item >( { return null; } + const fieldId = typeof field === 'string' ? field : field.id; + const fieldDefinition = getFieldDefinition( fieldId ); + + if ( + ! fieldDefinition || + ( fieldDefinition.isVisible && + ! fieldDefinition.isVisible( data ) ) + ) { + return null; + } + if ( children ) { return children( FieldLayout, field ); } return ( ( fields: Field< Item >[], - formFields: FormField[] = [], - combinedFields?: CombinedFormField< Item >[] + formFields: FormField[] = [] ): Field< Item >[] { - const visibleFields: Array< - Field< Item > | NormalizedCombinedFormField< Item > - > = [ ...fields ]; - if ( combinedFields ) { - visibleFields.push( - ...normalizeCombinedFields( combinedFields, fields ) - ); - } + const visibleFields: Array< Field< Item > > = [ ...fields ]; return formFields .map( ( fieldId ) => visibleFields.find( ( { id } ) => id === fieldId ) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 657c936a1ba3f..1594abb85e68f 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -18,7 +18,7 @@ import { closeSmall } from '@wordpress/icons'; */ import { normalizeFields } from '../../normalize-fields'; import { getVisibleFields } from '../get-visible-fields'; -import type { DataFormProps } from '../../types'; +import type { DataFormProps, FormField } from '../../types'; import FormFieldVisibility from '../../components/form-field-visibility'; import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; @@ -173,14 +173,8 @@ export default function FormPanel< Item >( { }: DataFormProps< Item > ) { const visibleFields = useMemo( () => - normalizeFields( - getVisibleFields< Item >( - fields, - form.fields, - form.combinedFields - ) - ), - [ fields, form.fields, form.combinedFields ] + normalizeFields( getVisibleFields< Item >( fields, form.fields ) ), + [ fields, form.fields ] ); return ( @@ -192,9 +186,9 @@ export default function FormPanel< Item >( { data={ data } field={ field } > - diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 4ff0149bac560..2ddd883ca023d 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -51,14 +51,8 @@ export default function FormRegular< Item >( { }: DataFormProps< Item > ) { const visibleFields = useMemo( () => - normalizeFields( - getVisibleFields< Item >( - fields, - form.fields, - form.combinedFields - ) - ), - [ fields, form.fields, form.combinedFields ] + normalizeFields( getVisibleFields< Item >( fields, form.fields ) ), + [ fields, form.fields ] ); return ( diff --git a/packages/dataviews/src/normalize-fields.ts b/packages/dataviews/src/normalize-fields.ts index 562f29fcce84f..2ed87cbe11222 100644 --- a/packages/dataviews/src/normalize-fields.ts +++ b/packages/dataviews/src/normalize-fields.ts @@ -2,14 +2,8 @@ * Internal dependencies */ import getFieldTypeDefinition from './field-types'; -import type { - CombinedFormField, - Field, - NormalizedField, - NormalizedCombinedFormField, -} from './types'; +import type { Field, NormalizedField } from './types'; import { getControl } from './dataform-controls'; -import DataFormCombinedEdit from './components/dataform-combined-edit'; const getValueFromId = ( id: string ) => @@ -87,29 +81,3 @@ export function normalizeFields< Item >( }; } ); } - -/** - * Apply default values and normalize the fields config. - * - * @param combinedFields combined field list. - * @param fields Fields config. - * @return Normalized fields config. - */ -export function normalizeCombinedFields< Item >( - combinedFields: CombinedFormField< Item >[], - fields: Field< Item >[] -): NormalizedCombinedFormField< Item >[] { - return combinedFields.map( ( combinedField ) => { - return { - ...combinedField, - Edit: DataFormCombinedEdit, - fields: normalizeFields( - combinedField.children - .map( ( fieldId ) => - fields.find( ( { id } ) => id === fieldId ) - ) - .filter( ( field ): field is Field< Item > => !! field ) - ), - }; - } ); -} diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index 26c6ecea645f4..087e812fffa19 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -6,7 +6,6 @@ @import "./components/dataviews-item-actions/style.scss"; @import "./components/dataviews-selection-checkbox/style.scss"; @import "./components/dataviews-view-config/style.scss"; -@import "./components/dataform-combined-edit/style.scss"; @import "./dataviews-layouts/grid/style.scss"; @import "./dataviews-layouts/list/style.scss"; diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 2db60155c6c4f..c0863193b4ec0 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -527,46 +527,25 @@ export interface SupportedLayouts { table?: Omit< ViewTable, 'type' >; } -export interface CombinedFormField< Item > extends CombinedField { - render?: ComponentType< { item: Item } >; -} - -export interface DataFormCombinedEditProps< Item > { - field: NormalizedCombinedFormField< Item >; - data: Item; - onChange: ( value: Record< string, any > ) => void; - hideLabelFromVision?: boolean; -} - -export type NormalizedCombinedFormField< Item > = CombinedFormField< Item > & { - fields: NormalizedField< Item >[]; - Edit?: ComponentType< DataFormCombinedEditProps< Item > >; -}; - export type FormField = | string | { id: string; layout?: 'regular' | 'panel' | 'inline'; - field?: FormField; fields?: FormField[]; }; /** * The form configuration. */ -export type Form< Item > = { +export type Form = { type?: 'regular' | 'panel' | 'inline'; fields?: FormField[]; - /** - * The fields to combine. - */ - combinedFields?: CombinedFormField< Item >[]; }; export interface DataFormProps< Item > { data: Item; fields: Field< Item >[]; - form: Form< Item >; + form: Form; onChange: ( value: Record< string, any > ) => void; } diff --git a/packages/dataviews/src/validation.ts b/packages/dataviews/src/validation.ts index 0a6542da4e8d4..bcc9a15908ff5 100644 --- a/packages/dataviews/src/validation.ts +++ b/packages/dataviews/src/validation.ts @@ -16,7 +16,7 @@ import type { Field, Form } from './types'; export function isItemValid< Item >( item: Item, fields: Field< Item >[], - form: Form< Item > + form: Form ): boolean { const _fields = normalizeFields( fields.filter( ( { id } ) => !! form.fields?.includes( id ) ) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index a535eef4ce787..b86e0011c5bd0 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -72,6 +72,11 @@ function PostEditForm( { postType, postId } ) { fields: [ 'featured_media', 'title', + { + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], + }, 'status_and_visibility', 'author', 'date', @@ -83,15 +88,6 @@ function PostEditForm( { postType, postId } ) { ids.length === 1 || fieldsWithBulkEditSupport.includes( field ) ), - combinedFields: [ - { - id: 'status_and_visibility', - label: __( 'Status & Visibility' ), - children: [ 'status', 'password' ], - direction: 'vertical', - render: ( { item } ) => item.status, - }, - ], } ), [ ids ] ); From 01da3c574321a7473a1961fce9447b4a655bffcc Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 12:55:03 +0100 Subject: [PATCH 07/34] Remove old use of View --- .../dataviews/src/dataforms-layouts/index.tsx | 21 ++------- .../src/dataforms-layouts/inline/index.tsx | 2 +- .../src/dataforms-layouts/panel/index.tsx | 40 +---------------- .../src/dataforms-layouts/regular/index.tsx | 43 ++----------------- 4 files changed, 9 insertions(+), 97 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/index.tsx b/packages/dataviews/src/dataforms-layouts/index.tsx index 3d0152c2a9f48..32469b6ebf0a6 100644 --- a/packages/dataviews/src/dataforms-layouts/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/index.tsx @@ -1,24 +1,9 @@ /** * Internal dependencies */ -import FormRegular, { FormRegularField } from './regular'; -import FormPanel, { FormPanelField } from './panel'; -import { FormInlineField } from './inline'; - -const FORM_LAYOUTS = [ - { - type: 'regular', - component: FormRegular, - }, - { - type: 'panel', - component: FormPanel, - }, -]; - -export function getFormLayout( type: string ) { - return FORM_LAYOUTS.find( ( layout ) => layout.type === type ); -} +import FormRegularField from './regular'; +import FormPanelField from './panel'; +import FormInlineField from './inline'; const FORM_FIELD_LAYOUTS = [ { diff --git a/packages/dataviews/src/dataforms-layouts/inline/index.tsx b/packages/dataviews/src/dataforms-layouts/inline/index.tsx index 571850220a26d..e996f63d7c42c 100644 --- a/packages/dataviews/src/dataforms-layouts/inline/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/inline/index.tsx @@ -16,7 +16,7 @@ interface FormFieldProps< Item > { onChange: ( value: any ) => void; } -export function FormInlineField< Item >( { +export default function FormInlineField< Item >( { data, field, onChange, diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 1594abb85e68f..1227c923591f7 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -16,10 +16,7 @@ import { closeSmall } from '@wordpress/icons'; /** * Internal dependencies */ -import { normalizeFields } from '../../normalize-fields'; -import { getVisibleFields } from '../get-visible-fields'; -import type { DataFormProps, FormField } from '../../types'; -import FormFieldVisibility from '../../components/form-field-visibility'; +import type { FormField } from '../../types'; import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; @@ -59,7 +56,7 @@ function DropdownHeader( { ); } -export function FormPanelField< Item >( { +export default function FormPanelField< Item >( { data, field, onChange, @@ -164,36 +161,3 @@ export function FormPanelField< Item >( { ); } - -export default function FormPanel< Item >( { - data, - fields, - form, - onChange, -}: DataFormProps< Item > ) { - const visibleFields = useMemo( - () => - normalizeFields( getVisibleFields< Item >( fields, form.fields ) ), - [ fields, form.fields ] - ); - - return ( - - { visibleFields.map( ( field ) => { - return ( - - - - ); - } ) } - - ); -} diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 2ddd883ca023d..0eb4c5ab1e606 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -1,16 +1,12 @@ /** * WordPress dependencies */ -import { __experimentalVStack as VStack } from '@wordpress/components'; -import { useContext, useMemo } from '@wordpress/element'; +import { useContext } from '@wordpress/element'; /** * Internal dependencies */ -import { normalizeFields } from '../../normalize-fields'; -import { getVisibleFields } from '../get-visible-fields'; -import type { DataFormProps, FormField } from '../../types'; -import FormFieldVisibility from '../../components/form-field-visibility'; +import type { FormField } from '../../types'; import DataFormContext from '../../components/dataform-context'; interface FormFieldProps< Item > { @@ -20,7 +16,7 @@ interface FormFieldProps< Item > { hideLabelFromVision?: boolean; } -export function FormRegularField< Item >( { +export default function FormRegularField< Item >( { data, field, onChange, @@ -42,36 +38,3 @@ export function FormRegularField< Item >( { /> ); } - -export default function FormRegular< Item >( { - data, - fields, - form, - onChange, -}: DataFormProps< Item > ) { - const visibleFields = useMemo( - () => - normalizeFields( getVisibleFields< Item >( fields, form.fields ) ), - [ fields, form.fields ] - ); - - return ( - - { visibleFields.map( ( field ) => { - return ( - - - - ); - } ) } - - ); -} From 6eae56735647915d80cd2f97dfbf9e0889f9de5c Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 13:08:31 +0100 Subject: [PATCH 08/34] Add label and move field type check to 'getFieldDefinition' --- .../src/components/dataform-context/index.tsx | 22 ++++++++++++++----- .../src/dataforms-layouts/inline/index.tsx | 4 +--- .../src/dataforms-layouts/panel/index.tsx | 4 +--- .../src/dataforms-layouts/regular/index.tsx | 5 ++--- packages/dataviews/src/types.ts | 1 + .../src/components/post-edit/index.js | 1 + 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx index 7b59ef1fad296..49170747472e1 100644 --- a/packages/dataviews/src/components/dataform-context/index.tsx +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -6,11 +6,11 @@ import { createContext, useCallback } from '@wordpress/element'; /** * Internal dependencies */ -import type { NormalizedField } from '../../types'; +import type { FormField, NormalizedField } from '../../types'; type DataFormContextType< Item > = { getFieldDefinition: ( - field: string + field: FormField ) => NormalizedField< Item > | undefined; }; @@ -23,10 +23,22 @@ export function DataFormProvider< Item >( { children, }: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { const getFieldDefinition = useCallback( - ( field: string ) => { - return fields.find( - ( fieldDefinition ) => fieldDefinition.id === field + ( field: FormField ) => { + const fieldId = typeof field === 'string' ? field : field.id; + + const definition = fields.find( + ( fieldDefinition ) => fieldDefinition.id === fieldId ); + if ( definition ) { + return { + ...definition, + label: + typeof field !== 'string' && field.label + ? field.label + : definition.label, + }; + } + return undefined; }, [ fields ] ); diff --git a/packages/dataviews/src/dataforms-layouts/inline/index.tsx b/packages/dataviews/src/dataforms-layouts/inline/index.tsx index e996f63d7c42c..c71dc25b111f5 100644 --- a/packages/dataviews/src/dataforms-layouts/inline/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/inline/index.tsx @@ -22,9 +22,7 @@ export default function FormInlineField< Item >( { onChange, }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( - typeof field === 'string' ? field : field.id - ); + const fieldDefinition = getFieldDefinition( field ); if ( ! fieldDefinition ) { return null; } diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 1227c923591f7..dd3c32806fec7 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -62,9 +62,7 @@ export default function FormPanelField< Item >( { onChange, }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( - typeof field === 'string' ? field : field.id - ); + const fieldDefinition = getFieldDefinition( field ); const childrenFields = useMemo( () => { if ( typeof field !== 'string' && field.fields ) { return field.fields; diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 0eb4c5ab1e606..683aff2f62b98 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -23,12 +23,11 @@ export default function FormRegularField< Item >( { hideLabelFromVision, }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( - typeof field === 'string' ? field : field.id - ); + const fieldDefinition = getFieldDefinition( field ); if ( ! fieldDefinition ) { return null; } + return ( Date: Thu, 31 Oct 2024 13:48:55 +0100 Subject: [PATCH 09/34] Create types of each view --- .../src/dataforms-layouts/panel/index.tsx | 6 ++++- packages/dataviews/src/types.ts | 27 ++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index dd3c32806fec7..0a284ca88a552 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -64,7 +64,11 @@ export default function FormPanelField< Item >( { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); const childrenFields = useMemo( () => { - if ( typeof field !== 'string' && field.fields ) { + if ( + typeof field !== 'string' && + field.layout === 'panel' && + field.fields + ) { return field.fields; } return [ field ]; diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index dc6a7467307e0..43c216365fe92 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -527,14 +527,29 @@ export interface SupportedLayouts { table?: Omit< ViewTable, 'type' >; } +interface BaseFieldLayout { + id: string; + label?: string; +} + +export interface RegularFieldLayout extends BaseFieldLayout { + layout: 'regular'; +} + +export interface PanelFieldLayout extends BaseFieldLayout { + layout: 'panel'; + fields?: FormField[]; +} + +export interface InlineFieldLayout extends BaseFieldLayout { + layout: 'inline'; +} + export type FormField = | string - | { - id: string; - label?: string; - layout?: 'regular' | 'panel' | 'inline'; - fields?: FormField[]; - }; + | RegularFieldLayout + | PanelFieldLayout + | InlineFieldLayout; /** * The form configuration. From 9235f5e5fb650fd64c0ed362d62d7f1b53d8de90 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 14:10:43 +0100 Subject: [PATCH 10/34] Add sticky example --- .../dataform/stories/index.story.tsx | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 3c22359f6749c..b3889f59df065 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useState } from '@wordpress/element'; +import { ToggleControl } from '@wordpress/components'; /** * Internal dependencies @@ -97,6 +98,24 @@ const fields = [ return item.status !== 'private'; }, }, + { + id: 'sticky', + label: 'Sticky', + type: 'integer', + Edit: ( { field, onChange, data, hideLabelFromVision } ) => { + const { id, getValue } = field; + return ( + + onChange( { [ id ]: ! getValue( { item: data } ) } ) + } + /> + ); + }, + }, ] as Field< SamplePost >[]; export const Default = ( { @@ -112,6 +131,7 @@ export const Default = ( { reviewer: 'fulano', date: '2021-01-01T12:00:00', birthdate: '1950-02-23T12:00:00', + sticky: false, } ); const form = { @@ -119,9 +139,8 @@ export const Default = ( { 'title', 'order', { - id: 'status', - layout: 'panel', - fields: [ 'status', 'password' ], + id: 'sticky', + layout: type === 'regular' ? 'regular' : 'inline', }, 'author', 'reviewer', @@ -201,4 +220,7 @@ export const CombinedFields = { argTypes: { ...meta.argTypes, }, + args: { + type: 'panel', + }, }; From d017417bd2b3b544829ca3111547360fd051effb Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 14:13:27 +0100 Subject: [PATCH 11/34] Update combined fields story --- .../components/dataform/stories/index.story.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index b3889f59df065..35b884d305456 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -186,11 +186,15 @@ const CombinedFieldsComponent = ( { const form = { fields: [ 'title', - { - id: 'status', - layout: 'panel', - fields: [ 'status', 'password' ], - }, + ...( type === 'regular' + ? [ 'status', 'password' ] + : [ + { + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], + }, + ] ), 'order', 'author', ], From ecf42e89f4e0087244d2053c7dd82df72cb2bd62 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 16:51:07 +0100 Subject: [PATCH 12/34] Fix change I missed during rebase --- packages/dataviews/src/dataforms-layouts/panel/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 0a284ca88a552..a6e26ee7bb78c 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -9,7 +9,7 @@ import { Dropdown, Button, } from '@wordpress/components'; -import { sprintf, __ } from '@wordpress/i18n'; +import { sprintf, __, _x } from '@wordpress/i18n'; import { useState, useMemo, useContext } from '@wordpress/element'; import { closeSmall } from '@wordpress/icons'; @@ -121,7 +121,7 @@ export default function FormPanelField< Item >( { aria-expanded={ isOpen } aria-label={ sprintf( // translators: %s: Field name. - __( 'Edit %s' ), + _x( 'Edit %s', 'field' ), fieldDefinition.label ) } onClick={ onToggle } From 7cf260bb0765aa71ccc9ec2d26901438b37db318 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 4 Nov 2024 13:40:49 +0100 Subject: [PATCH 13/34] Remove old status_and_visibility field --- packages/edit-site/src/components/post-edit/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index e5dbc79e41ea8..08231d16df1ff 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -78,7 +78,6 @@ function PostEditForm( { postType, postId } ) { layout: 'panel', fields: [ 'status', 'password' ], }, - 'status_and_visibility', 'author', 'date', 'slug', From 3ec2fd1a085f9a0a6cbc4100dda65c36bfccf053 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 4 Nov 2024 13:50:16 +0100 Subject: [PATCH 14/34] Rename fields to children for clarity --- .../dataviews/src/components/dataform/stories/index.story.tsx | 2 +- packages/dataviews/src/dataforms-layouts/panel/index.tsx | 4 ++-- packages/dataviews/src/types.ts | 2 +- packages/edit-site/src/components/post-edit/index.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 35b884d305456..fecf607b8cfe5 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -192,7 +192,7 @@ const CombinedFieldsComponent = ( { { id: 'status', layout: 'panel', - fields: [ 'status', 'password' ], + children: [ 'status', 'password' ], }, ] ), 'order', diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index a6e26ee7bb78c..b6c42d9d64ba6 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -67,9 +67,9 @@ export default function FormPanelField< Item >( { if ( typeof field !== 'string' && field.layout === 'panel' && - field.fields + field.children ) { - return field.fields; + return field.children; } return [ field ]; }, [ field ] ); diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 43c216365fe92..e7cd66fe1846b 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -538,7 +538,7 @@ export interface RegularFieldLayout extends BaseFieldLayout { export interface PanelFieldLayout extends BaseFieldLayout { layout: 'panel'; - fields?: FormField[]; + children?: FormField[]; } export interface InlineFieldLayout extends BaseFieldLayout { diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index 08231d16df1ff..02e6f5ae6eba1 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -76,7 +76,7 @@ function PostEditForm( { postType, postId } ) { id: 'status', label: __( 'Status & Visibility' ), layout: 'panel', - fields: [ 'status', 'password' ], + children: [ 'status', 'password' ], }, 'author', 'date', From 8b96c7e1ec374870eac0e87c7520fa8515acf1dc Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 4 Nov 2024 16:59:36 +0100 Subject: [PATCH 15/34] Add children support to regular layout --- .../dataform/stories/index.story.tsx | 3 +- .../dataforms-layouts/data-form-layout.tsx | 4 +- .../src/dataforms-layouts/panel/index.tsx | 5 +- .../src/dataforms-layouts/regular/index.tsx | 47 ++++++++++++++++++- packages/dataviews/src/types.ts | 2 + 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index fecf607b8cfe5..d17e6bd02c600 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -186,12 +186,11 @@ const CombinedFieldsComponent = ( { const form = { fields: [ 'title', - ...( type === 'regular' + ...( type === 'inline' ? [ 'status', 'password' ] : [ { id: 'status', - layout: 'panel', children: [ 'status', 'password' ], }, ] ), diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index a50cdee7d12e9..bd7d53b431752 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -38,7 +38,9 @@ export function DataFormLayout< Item >( { { fields.map( ( field ) => { const fieldLayoutId = - typeof field === 'string' ? defaultLayout : field.layout; + typeof field !== 'string' && field.layout + ? field.layout + : defaultLayout; const FieldLayout = getFormFieldLayout( fieldLayoutId ?? 'regular' )?.component; diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index b6c42d9d64ba6..46ebc554f0724 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -24,6 +24,7 @@ interface FormFieldProps< Item > { data: Item; field: FormField; onChange: ( value: any ) => void; + defaultLayout?: string; } function DropdownHeader( { @@ -60,13 +61,14 @@ export default function FormPanelField< Item >( { data, field, onChange, + defaultLayout, }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); const childrenFields = useMemo( () => { if ( typeof field !== 'string' && - field.layout === 'panel' && + // field.layout === 'panel' && field.children ) { return field.children; @@ -139,6 +141,7 @@ export default function FormPanelField< Item >( { data={ data } fields={ childrenFields } onChange={ onChange } + defaultLayout={ defaultLayout } > { ( FieldLayout, nestedField ) => ( { data: Item; field: FormField; onChange: ( value: any ) => void; hideLabelFromVision?: boolean; + defaultLayout?: string; +} + +function Header( { title }: { title: string } ) { + return ( + + + + { title } + + + + + ); } export default function FormRegularField< Item >( { @@ -21,13 +42,37 @@ export default function FormRegularField< Item >( { field, onChange, hideLabelFromVision, + defaultLayout, }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); + const childrenFields = useMemo( () => { + if ( typeof field !== 'string' && field.children ) { + return field.children; + } + return []; + }, [ field ] ); + if ( ! fieldDefinition ) { return null; } + if ( childrenFields.length > 0 ) { + return ( + <> + { ! hideLabelFromVision && ( +
+ ) } + + + ); + } + return ( Date: Tue, 5 Nov 2024 13:17:14 +0100 Subject: [PATCH 16/34] Replace inline with labelPosition --- .../dataform/stories/index.story.tsx | 104 +++++++--- .../dataviews/src/dataforms-layouts/index.tsx | 5 - .../src/dataforms-layouts/inline/index.tsx | 45 ---- .../src/dataforms-layouts/panel/index.tsx | 194 +++++++++++------- .../src/dataforms-layouts/regular/index.tsx | 23 +++ .../src/dataforms-layouts/regular/style.scss | 24 +++ packages/dataviews/src/style.scss | 1 + packages/dataviews/src/types.ts | 32 +-- 8 files changed, 248 insertions(+), 180 deletions(-) delete mode 100644 packages/dataviews/src/dataforms-layouts/inline/index.tsx create mode 100644 packages/dataviews/src/dataforms-layouts/regular/style.scss diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index d17e6bd02c600..e202ce8acba8c 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useState } from '@wordpress/element'; +import { useMemo, useState } from '@wordpress/element'; import { ToggleControl } from '@wordpress/components'; /** @@ -29,7 +29,12 @@ const meta = { control: { type: 'select' }, description: 'Chooses the default layout of each field. "regular" is the default layout.', - options: [ 'regular', 'panel', 'inline' ], + options: [ 'regular', 'panel' ], + }, + labelPosition: { + control: { type: 'select' }, + description: 'Chooses the label position of the layout.', + options: [ 'default', 'top', 'side' ], }, }, }; @@ -120,8 +125,10 @@ const fields = [ export const Default = ( { type, + labelPosition, }: { - type: 'panel' | 'regular' | 'inline'; + type: 'panel' | 'regular'; + labelPosition: 'default' | 'top' | 'side'; } ) => { const [ post, setPost ] = useState( { title: 'Hello, World!', @@ -134,21 +141,37 @@ export const Default = ( { sticky: false, } ); - const form = { - fields: [ - 'title', - 'order', - { - id: 'sticky', - layout: type === 'regular' ? 'regular' : 'inline', - }, - 'author', - 'reviewer', - 'password', - 'date', - 'birthdate', - ], - } as Form; + 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; + } ), + } ), + [ type, labelPosition ] + ) as Form; return ( @@ -170,8 +193,10 @@ export const Default = ( { const CombinedFieldsComponent = ( { type = 'regular', + labelPosition, }: { - type: 'panel' | 'regular' | 'inline'; + type: 'panel' | 'regular'; + labelPosition: 'default' | 'top' | 'side'; } ) => { const [ post, setPost ] = useState< SamplePost >( { title: 'Hello, World!', @@ -183,21 +208,32 @@ const CombinedFieldsComponent = ( { birthdate: '1950-02-23T12:00:00', } ); - const form = { - fields: [ - 'title', - ...( type === 'inline' - ? [ 'status', 'password' ] - : [ - { - id: 'status', - children: [ 'status', 'password' ], - }, - ] ), - 'order', - 'author', - ], - } as Form; + 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; + } ), + } ), + [ type, labelPosition ] + ) as Form; return ( diff --git a/packages/dataviews/src/dataforms-layouts/index.tsx b/packages/dataviews/src/dataforms-layouts/index.tsx index 32469b6ebf0a6..5e4f3617d9c7d 100644 --- a/packages/dataviews/src/dataforms-layouts/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/index.tsx @@ -3,7 +3,6 @@ */ import FormRegularField from './regular'; import FormPanelField from './panel'; -import FormInlineField from './inline'; const FORM_FIELD_LAYOUTS = [ { @@ -14,10 +13,6 @@ const FORM_FIELD_LAYOUTS = [ type: 'panel', component: FormPanelField, }, - { - type: 'inline', - component: FormInlineField, - }, ]; export function getFormFieldLayout( type: string ) { diff --git a/packages/dataviews/src/dataforms-layouts/inline/index.tsx b/packages/dataviews/src/dataforms-layouts/inline/index.tsx deleted file mode 100644 index c71dc25b111f5..0000000000000 --- a/packages/dataviews/src/dataforms-layouts/inline/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - * WordPress dependencies - */ -import { __experimentalHStack as HStack } from '@wordpress/components'; -import { useContext } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import type { FormField } from '../../types'; -import DataFormContext from '../../components/dataform-context'; - -interface FormFieldProps< Item > { - data: Item; - field: FormField; - onChange: ( value: any ) => void; -} - -export default function FormInlineField< Item >( { - data, - field, - onChange, -}: FormFieldProps< Item > ) { - const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( field ); - if ( ! fieldDefinition ) { - return null; - } - return ( - -
- { fieldDefinition.label } -
-
- -
-
- ); -} diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 46ebc554f0724..95c43279655b0 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -8,6 +8,7 @@ import { __experimentalSpacer as Spacer, Dropdown, Button, + BaseControl, } from '@wordpress/components'; import { sprintf, __, _x } from '@wordpress/i18n'; import { useState, useMemo, useContext } from '@wordpress/element'; @@ -16,7 +17,7 @@ import { closeSmall } from '@wordpress/icons'; /** * Internal dependencies */ -import type { FormField } from '../../types'; +import type { FormField, NormalizedField } from '../../types'; import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; @@ -57,29 +58,27 @@ function DropdownHeader( { ); } -export default function FormPanelField< Item >( { +function PanelDropdown< Item >( { + fieldDefinition, + popoverAnchor, data, - field, onChange, - defaultLayout, -}: FormFieldProps< Item > ) { - const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( field ); + field, +}: { + fieldDefinition: NormalizedField< Item >; + popoverAnchor: HTMLElement | null; +} & FormFieldProps< Item > ) { const childrenFields = useMemo( () => { - if ( - typeof field !== 'string' && - // field.layout === 'panel' && - field.children - ) { + const isFieldObject = typeof field !== 'string'; + if ( isFieldObject && field.children ) { return field.children; } + if ( isFieldObject && field.id ) { + return [ field.id ]; + } return [ field ]; }, [ field ] ); - // Use internal state instead of a ref to make sure that the component - // re-renders when the popover's anchor updates. - const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >( - null - ); + // Memoize popoverProps to avoid returning a new object every time. const popoverProps = useMemo( () => ( { @@ -93,10 +92,109 @@ export default function FormPanelField< Item >( { [ popoverAnchor ] ); + return ( + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + { ( FieldLayout, nestedField ) => ( + + ) } + + + ) } + /> + ); +} + +export default function FormPanelField< Item >( { + data, + field, + onChange, + defaultLayout, +}: FormFieldProps< Item > ) { + const { getFieldDefinition } = useContext( DataFormContext ); + const fieldDefinition = getFieldDefinition( field ); + const labelPosition = + typeof field !== 'string' && field.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. + const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >( + null + ); + if ( ! fieldDefinition ) { return null; } + if ( labelPosition === 'top' ) { + return ( + + + { fieldDefinition.label } + +
+ +
+
+ ); + } + + // Defaults to label position side. return ( ( { { fieldDefinition.label }
- ( - - ) } - renderContent={ ( { onClose } ) => ( - <> - - - { ( FieldLayout, nestedField ) => ( - - ) } - - - ) } +
diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 2c741f9b7ec0a..113408f8ee6ab 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -46,6 +46,10 @@ 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 childrenFields = useMemo( () => { if ( typeof field !== 'string' && field.children ) { return field.children; @@ -73,6 +77,25 @@ export default function FormRegularField< Item >( { ); } + if ( labelPosition === 'side' ) { + return ( + +
+ { fieldDefinition.label } +
+
+ +
+
+ ); + } + return ( ; } -interface BaseFieldLayout { - id: string; - label?: string; -} - -export interface RegularFieldLayout extends BaseFieldLayout { - layout: 'regular'; - children?: FormField[]; -} - -export interface PanelFieldLayout extends BaseFieldLayout { - layout: 'panel'; - children?: FormField[]; -} - -export interface InlineFieldLayout extends BaseFieldLayout { - layout: 'inline'; - children?: FormField[]; -} - export type FormField = | string - | RegularFieldLayout - | PanelFieldLayout - | InlineFieldLayout; + | { + id: string; + label?: string; + layout?: 'regular' | 'panel'; + labelPosition?: 'side' | 'top'; + children?: FormField[]; + }; /** * The form configuration. */ export type Form = { - type?: 'regular' | 'panel' | 'inline'; + type?: 'regular' | 'panel'; fields?: FormField[]; }; From f3339026620d99fe54a2299caf235de5f585ed48 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 5 Nov 2024 14:08:11 +0100 Subject: [PATCH 17/34] 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 49170747472e1..e3973ad09b127 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 e202ce8acba8c..849703ef8ad2d 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 bd7d53b431752..086d795088507 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 b7cbd67978233..0000000000000 --- 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 95c43279655b0..067678754df2b 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 113408f8ee6ab..97693268ff78f 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 b242b45fec8e2..2bee90703ea31 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -527,22 +527,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 > { From 40963febb68d9137e3931673f27cf27586b7a042 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Wed, 6 Nov 2024 11:08:37 +0100 Subject: [PATCH 18/34] Update DataForm context to align more with DataViews context --- .../src/components/dataform-context/index.tsx | 33 +++---------------- .../dataform/stories/index.story.tsx | 1 + .../dataforms-layouts/data-form-layout.tsx | 10 +++++- .../src/dataforms-layouts/panel/index.tsx | 20 ++++++----- .../src/dataforms-layouts/regular/index.tsx | 11 ++++--- 5 files changed, 32 insertions(+), 43 deletions(-) diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx index e3973ad09b127..72fbf7e0f42ab 100644 --- a/packages/dataviews/src/components/dataform-context/index.tsx +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -1,50 +1,27 @@ /** * WordPress dependencies */ -import { createContext, useCallback } from '@wordpress/element'; +import { createContext } from '@wordpress/element'; /** * Internal dependencies */ -import type { FormField, NormalizedField } from '../../types'; +import type { NormalizedField } from '../../types'; type DataFormContextType< Item > = { - getFieldDefinition: ( - field: FormField | string - ) => NormalizedField< Item > | undefined; + fields: NormalizedField< Item >[]; }; const DataFormContext = createContext< DataFormContextType< any > >( { - getFieldDefinition: () => undefined, + fields: [], } ); export function DataFormProvider< Item >( { fields, children, }: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { - const getFieldDefinition = useCallback( - ( field: FormField | string ) => { - const fieldId = typeof field === 'string' ? field : field.id; - - const definition = fields.find( - ( fieldDefinition ) => fieldDefinition.id === fieldId - ); - if ( definition ) { - return { - ...definition, - label: - typeof field !== 'string' && field.label - ? field.label - : definition.label, - }; - } - return undefined; - }, - [ fields ] - ); - return ( - + { children } ); diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 849703ef8ad2d..9241ec62255fe 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -237,6 +237,7 @@ const CombinedFieldsComponent = ( { 'title', { id: 'status', + label: 'Status & Visibility', children: [ 'status', 'password' ], }, 'order', diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index 086d795088507..8a1892a98172d 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -32,7 +32,15 @@ export function DataFormLayout< Item >( { field: FormField ) => React.JSX.Element; } ) { - const { getFieldDefinition } = useContext( DataFormContext ); + const { fields: fieldDefinitions } = useContext( DataFormContext ); + + function getFieldDefinition( field: FormField | string ) { + const fieldId = typeof field === 'string' ? field : field.id; + + return fieldDefinitions.find( + ( fieldDefinition ) => fieldDefinition.id === fieldId + ); + } return ( diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 067678754df2b..fbe26ecef6045 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -68,6 +68,7 @@ function PanelDropdown< Item >( { fieldDefinition: NormalizedField< Item >; popoverAnchor: HTMLElement | null; } & FormFieldProps< Item > ) { + const fieldLabel = field.label ?? fieldDefinition.label; const childrenFields = useMemo( () => { if ( field.children ) { return field.children.map( ( child ) => { @@ -115,7 +116,7 @@ function PanelDropdown< Item >( { aria-label={ sprintf( // translators: %s: Field name. _x( 'Edit %s', 'field' ), - fieldDefinition.label + fieldLabel ) } onClick={ onToggle } > @@ -124,10 +125,7 @@ function PanelDropdown< Item >( { ) } renderContent={ ( { onClose } ) => ( <> - + ( { onChange, defaultLayout, }: FormFieldProps< Item > ) { - const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( field ); + const { fields } = useContext( DataFormContext ); + const fieldDefinition = fields.find( + ( fieldDef ) => fieldDef.id === field.id + ); const labelPosition = field.labelPosition ?? 'side'; // Use internal state instead of a ref to make sure that the component @@ -175,11 +175,13 @@ export default function FormPanelField< Item >( { return null; } + const fieldLabel = field.label ?? fieldDefinition.label; + if ( labelPosition === 'top' ) { return ( - { fieldDefinition.label } + { fieldLabel }
( { className="dataforms-layouts-panel__field" >
- { fieldDefinition.label } + { fieldLabel }
( { hideLabelFromVision, defaultLayout, }: FormFieldProps< Item > ) { - const { getFieldDefinition } = useContext( DataFormContext ); - const fieldDefinition = getFieldDefinition( field ); + const { fields } = useContext( DataFormContext ); + const fieldDefinition = fields.find( + ( fieldDef ) => fieldDef.id === field.id + ); const labelPosition = field.labelPosition ?? 'top'; const childrenFields = useMemo( () => { if ( typeof field !== 'string' && field.children ) { @@ -64,13 +66,12 @@ export default function FormRegularField< Item >( { if ( ! fieldDefinition ) { return null; } + const fieldLabel = field.label ?? fieldDefinition.label; if ( childrenFields.length > 0 ) { return ( <> - { ! hideLabelFromVision && ( -
- ) } + { ! hideLabelFromVision &&
} Date: Wed, 6 Nov 2024 15:20:02 +0100 Subject: [PATCH 19/34] Add seperated combined form field type --- .../dataform/stories/index.story.tsx | 7 +-- .../dataforms-layouts/data-form-layout.tsx | 24 +++++---- .../dataforms-layouts/is-combined-field.ts | 10 ++++ .../src/dataforms-layouts/panel/index.tsx | 51 +++++++++++++------ .../src/dataforms-layouts/regular/index.tsx | 29 ++++++----- packages/dataviews/src/types.ts | 14 +++-- 6 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 packages/dataviews/src/dataforms-layouts/is-combined-field.ts diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 9241ec62255fe..c0486e041a6c7 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -9,6 +9,7 @@ import { ToggleControl } from '@wordpress/components'; */ import DataForm from '../index'; import type { Field, Form, FormField } from '../../../types'; +import { isCombinedField } from '../../../dataforms-layouts/is-combined-field'; type SamplePost = { title: string; @@ -131,14 +132,14 @@ function toFormField( return formFields.map( ( field ) => { if ( typeof field === 'string' ) { return { - id: field, + field, layout: type, labelPosition: labelPosition === 'default' ? undefined : labelPosition, }; } else if ( typeof field !== 'string' && - field.children && + isCombinedField( field ) && type !== 'panel' ) { return { @@ -175,7 +176,7 @@ export const Default = ( { 'title', 'order', { - id: 'sticky', + field: 'sticky', layout: 'regular', labelPosition: type === 'regular' && labelPosition !== 'default' diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index 8a1892a98172d..76d0b575e37ad 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -7,9 +7,10 @@ import { useContext } from '@wordpress/element'; /** * Internal dependencies */ -import type { FormField } from '../types'; +import type { FormField, SimpleFormField } from '../types'; import { getFormFieldLayout } from './index'; import DataFormContext from '../components/dataform-context'; +import { isCombinedField } from './is-combined-field'; export function DataFormLayout< Item >( { defaultLayout, @@ -34,8 +35,8 @@ export function DataFormLayout< Item >( { } ) { const { fields: fieldDefinitions } = useContext( DataFormContext ); - function getFieldDefinition( field: FormField | string ) { - const fieldId = typeof field === 'string' ? field : field.id; + function getFieldDefinition( field: SimpleFormField | string ) { + const fieldId = typeof field === 'string' ? field : field.field; return fieldDefinitions.find( ( fieldDefinition ) => fieldDefinition.id === fieldId @@ -49,7 +50,7 @@ export function DataFormLayout< Item >( { typeof field !== 'string' ? field : { - id: field, + field, }; const fieldLayoutId = formField.layout ? formField.layout @@ -62,12 +63,14 @@ export function DataFormLayout< Item >( { return null; } - const fieldDefinition = getFieldDefinition( formField ); + const fieldDefinition = ! isCombinedField( formField ) + ? getFieldDefinition( formField ) + : undefined; if ( - ! fieldDefinition || - ( fieldDefinition.isVisible && - ! fieldDefinition.isVisible( data ) ) + fieldDefinition && + fieldDefinition.isVisible && + ! fieldDefinition.isVisible( data ) ) { return null; } @@ -76,9 +79,12 @@ export function DataFormLayout< Item >( { return children( FieldLayout, formField ); } + const key = isCombinedField( formField ) + ? formField.id + : formField.field; return ( { data: Item; @@ -32,7 +33,7 @@ function DropdownHeader( { title, onClose, }: { - title: string; + title?: string; onClose: () => void; } ) { return ( @@ -41,9 +42,11 @@ function DropdownHeader( { spacing={ 4 } > - - { title } - + { title && ( + + { title } + + ) } { onClose && (
@@ -221,6 +228,7 @@ export default function FormPanelField< Item >( { data={ data } onChange={ onChange } defaultLayout={ defaultLayout } + labelPosition={ labelPosition } /> ); } @@ -242,6 +250,7 @@ export default function FormPanelField< Item >( { data={ data } onChange={ onChange } defaultLayout={ defaultLayout } + labelPosition={ labelPosition } />
From 0f6399bb405c95d052d17e71ce1685a83ea47a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:13:19 +0100 Subject: [PATCH 27/34] Panel layout: do not style the label as a base control. --- packages/dataviews/src/dataforms-layouts/panel/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index da4febbb57858..2085420909688 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -201,9 +201,7 @@ export default function FormPanelField< Item >( { if ( labelPosition === 'top' ) { return ( - - { fieldLabel } - + { fieldLabel }
Date: Tue, 19 Nov 2024 10:20:06 +0100 Subject: [PATCH 28/34] Style for layout regular label --- .../src/dataforms-layouts/regular/index.tsx | 18 ++++++++++-------- .../src/dataforms-layouts/regular/style.scss | 6 ++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index e4f7007aecbd6..a20a167bb41b8 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -105,13 +105,15 @@ export default function FormRegularField< Item >( { } return ( - +
+ +
); } diff --git a/packages/dataviews/src/dataforms-layouts/regular/style.scss b/packages/dataviews/src/dataforms-layouts/regular/style.scss index b9dc52787636b..d94b804fdf1fe 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/style.scss +++ b/packages/dataviews/src/dataforms-layouts/regular/style.scss @@ -5,6 +5,12 @@ align-items: flex-start !important; } +.dataforms-layouts-regular__field .components-base-control__label { + font-size: inherit; + font-weight: normal; + text-transform: none; +} + .dataforms-layouts-regular__field-label { width: 38%; flex-shrink: 0; From 2b27db1f6a3d870986c64de2c84f9a898eb154dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:32:49 +0100 Subject: [PATCH 29/34] Panel: better spacing --- .../src/dataforms-layouts/panel/index.tsx | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 2085420909688..0b616d049bde7 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -8,7 +8,6 @@ import { __experimentalSpacer as Spacer, Dropdown, Button, - BaseControl, } from '@wordpress/components'; import { sprintf, __, _x } from '@wordpress/i18n'; import { useState, useMemo, useContext } from '@wordpress/element'; @@ -200,9 +199,14 @@ export default function FormPanelField< Item >( { if ( labelPosition === 'top' ) { return ( - - { fieldLabel } -
+ +
+ { fieldLabel } +
+
( { labelPosition={ labelPosition } />
- +
); } if ( labelPosition === 'none' ) { return ( - +
+ +
); } @@ -240,7 +246,7 @@ export default function FormPanelField< Item >( {
{ fieldLabel }
-
+
Date: Tue, 19 Nov 2024 11:44:44 +0100 Subject: [PATCH 30/34] Destructure all props --- .../dataviews/src/components/dataform/index.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/dataviews/src/components/dataform/index.tsx b/packages/dataviews/src/components/dataform/index.tsx index ae03905957668..15ca7490f6cc3 100644 --- a/packages/dataviews/src/components/dataform/index.tsx +++ b/packages/dataviews/src/components/dataform/index.tsx @@ -12,12 +12,14 @@ import { normalizeFields } from '../../normalize-fields'; import { DataFormLayout } from '../../dataforms-layouts/data-form-layout'; export default function DataForm< Item >( { + data, form, - ...props + fields, + onChange, }: DataFormProps< Item > ) { const normalizedFields = useMemo( - () => normalizeFields( props.fields ), - [ props.fields ] + () => normalizeFields( fields ), + [ fields ] ); if ( ! form.fields ) { @@ -27,10 +29,10 @@ export default function DataForm< Item >( { return ( ); From 695b6298dea3927cd26633a231d6acafc3c5cca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:02:29 +0100 Subject: [PATCH 31/34] DataFormLayout: props are data, form, onChange, children --- .../src/components/dataform/index.tsx | 7 +--- .../dataforms-layouts/data-form-layout.tsx | 12 +++--- .../src/dataforms-layouts/panel/index.tsx | 39 ++++++++++++------- .../src/dataforms-layouts/regular/index.tsx | 34 +++++++++------- 4 files changed, 50 insertions(+), 42 deletions(-) diff --git a/packages/dataviews/src/components/dataform/index.tsx b/packages/dataviews/src/components/dataform/index.tsx index 15ca7490f6cc3..b359ddba74381 100644 --- a/packages/dataviews/src/components/dataform/index.tsx +++ b/packages/dataviews/src/components/dataform/index.tsx @@ -28,12 +28,7 @@ export default function DataForm< Item >( { return ( - + ); } diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index acdc3007cc808..aeacfa6dcbc00 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -7,21 +7,19 @@ import { useContext } from '@wordpress/element'; /** * Internal dependencies */ -import type { FormField, SimpleFormField } from '../types'; +import type { Form, FormField, SimpleFormField } from '../types'; import { getFormFieldLayout } from './index'; import DataFormContext from '../components/dataform-context'; import { isCombinedField } from './is-combined-field'; export function DataFormLayout< Item >( { - defaultLayout, data, - fields, + form, onChange, children, }: { - defaultLayout?: string; data: Item; - fields: Array< FormField | string >; + form: Form; onChange: ( value: any ) => void; children?: ( FieldLayout: ( props: { @@ -45,7 +43,7 @@ export function DataFormLayout< Item >( { return ( - { fields.map( ( field ) => { + { form?.fields?.map( ( field ) => { const formField: FormField = typeof field !== 'string' ? field @@ -54,7 +52,7 @@ export function DataFormLayout< Item >( { }; const fieldLayoutId = formField.layout ? formField.layout - : defaultLayout; + : form.type; const FieldLayout = getFormFieldLayout( fieldLayoutId ?? 'regular' )?.component; diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 0b616d049bde7..ea2cc644e69a4 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -16,7 +16,12 @@ import { closeSmall } from '@wordpress/icons'; /** * Internal dependencies */ -import type { FormField, NormalizedField, SimpleFormField } from '../../types'; +import type { + Form, + FormField, + NormalizedField, + SimpleFormField, +} from '../../types'; import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; import { isCombinedField } from '../is-combined-field'; @@ -75,19 +80,25 @@ function PanelDropdown< Item >( { const fieldLabel = isCombinedField( field ) ? field.label : fieldDefinition?.label; - const childrenFields = useMemo( () => { + const form = useMemo( () => { if ( isCombinedField( field ) ) { - return field.children.map( ( child ) => { - if ( typeof child === 'string' ) { - return { - id: child, - }; - } - return child; - } ); + return { + type: 'panel', // TODO: fix. + fields: field.children.map( ( child ) => { + if ( typeof child === 'string' ) { + return { + id: child, + }; + } + return child; + } ), + }; } // If not explicit children return the field id itself. - return [ { id: field.id } ]; + return { + type: 'panel', // TODO: fix. + fields: [ { id: field.id } ], + }; }, [ field ] ); // Memoize popoverProps to avoid returning a new object every time. @@ -138,7 +149,7 @@ function PanelDropdown< Item >( { { ( FieldLayout, nestedField ) => ( @@ -147,9 +158,7 @@ function PanelDropdown< Item >( { data={ data } field={ nestedField } onChange={ onChange } - hideLabelFromVision={ - childrenFields.length < 2 - } + hideLabelFromVision={ form.fields.length < 2 } /> ) } diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index a20a167bb41b8..b6ffd71dd1311 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -12,7 +12,7 @@ import { /** * Internal dependencies */ -import type { FormField } from '../../types'; +import type { Form, FormField } from '../../types'; import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; import { isCombinedField } from '../is-combined-field'; @@ -47,19 +47,26 @@ export default function FormRegularField< Item >( { }: FormFieldProps< Item > ) { const { fields } = useContext( DataFormContext ); - const childrenFields = useMemo( () => { + const form = useMemo( () => { if ( isCombinedField( field ) ) { - return field.children.map( ( child ) => { - if ( typeof child === 'string' ) { - return { - id: child, - }; - } - return child; - } ); + return { + fields: field.children.map( ( child ) => { + if ( typeof child === 'string' ) { + return { + id: child, + }; + } + return child; + } ), + type: defaultLayout, + }; } - return []; - }, [ field ] ); + + return { + type: defaultLayout, + fields: [], + }; + }, [ defaultLayout, field ] ); if ( isCombinedField( field ) ) { return ( @@ -69,9 +76,8 @@ export default function FormRegularField< Item >( { ) } ); From fa872ee38260f3aab9383bf5416870ac064911bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:09:54 +0100 Subject: [PATCH 32/34] Extract FieldLayoutProps as a common interface for all layouts --- .../src/dataforms-layouts/panel/index.tsx | 29 ++++++++----------- .../src/dataforms-layouts/regular/index.tsx | 19 ++++-------- packages/dataviews/src/types.ts | 7 +++++ 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index ea2cc644e69a4..269b2bb418a85 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -19,6 +19,7 @@ import { closeSmall } from '@wordpress/icons'; import type { Form, FormField, + FieldLayoutProps, NormalizedField, SimpleFormField, } from '../../types'; @@ -26,13 +27,6 @@ import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; import { isCombinedField } from '../is-combined-field'; -interface FormFieldProps< Item > { - data: Item; - field: FormField; - onChange: ( value: any ) => void; - defaultLayout?: string; -} - function DropdownHeader( { title, onClose, @@ -68,22 +62,25 @@ function DropdownHeader( { function PanelDropdown< Item >( { fieldDefinition, popoverAnchor, + labelPosition = 'side', data, onChange, field, - labelPosition = 'side', }: { fieldDefinition: NormalizedField< Item >; popoverAnchor: HTMLElement | null; labelPosition: 'side' | 'top' | 'none'; -} & FormFieldProps< Item > ) { + data: Item; + onChange: ( value: any ) => void; + field: FormField; +} ) { const fieldLabel = isCombinedField( field ) ? field.label : fieldDefinition?.label; const form = useMemo( () => { if ( isCombinedField( field ) ) { return { - type: 'panel', // TODO: fix. + type: 'regular' as const, fields: field.children.map( ( child ) => { if ( typeof child === 'string' ) { return { @@ -96,7 +93,7 @@ function PanelDropdown< Item >( { } // If not explicit children return the field id itself. return { - type: 'panel', // TODO: fix. + type: 'regular' as const, fields: [ { id: field.id } ], }; }, [ field ] ); @@ -158,7 +155,9 @@ function PanelDropdown< Item >( { data={ data } field={ nestedField } onChange={ onChange } - hideLabelFromVision={ form.fields.length < 2 } + hideLabelFromVision={ + ( form?.fields ?? [] ).length < 2 + } /> ) } @@ -172,8 +171,7 @@ export default function FormPanelField< Item >( { data, field, onChange, - defaultLayout, -}: FormFieldProps< Item > ) { +}: FieldLayoutProps< Item > ) { const { fields } = useContext( DataFormContext ); const fieldDefinition = fields.find( ( fieldDef ) => { // Default to the first child if it is a combined field. @@ -222,7 +220,6 @@ export default function FormPanelField< Item >( { fieldDefinition={ fieldDefinition } data={ data } onChange={ onChange } - defaultLayout={ defaultLayout } labelPosition={ labelPosition } />
@@ -239,7 +236,6 @@ export default function FormPanelField< Item >( { fieldDefinition={ fieldDefinition } data={ data } onChange={ onChange } - defaultLayout={ defaultLayout } labelPosition={ labelPosition } />
@@ -262,7 +258,6 @@ export default function FormPanelField< Item >( { fieldDefinition={ fieldDefinition } data={ data } onChange={ onChange } - defaultLayout={ defaultLayout } labelPosition={ labelPosition } />
diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index b6ffd71dd1311..a3d90b807b5cd 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -12,19 +12,11 @@ import { /** * Internal dependencies */ -import type { Form, FormField } from '../../types'; +import type { Form, FieldLayoutProps } from '../../types'; import DataFormContext from '../../components/dataform-context'; import { DataFormLayout } from '../data-form-layout'; import { isCombinedField } from '../is-combined-field'; -interface FormFieldProps< Item > { - data: Item; - field: FormField; - onChange: ( value: any ) => void; - hideLabelFromVision?: boolean; - defaultLayout?: string; -} - function Header( { title }: { title: string } ) { return ( @@ -43,8 +35,7 @@ export default function FormRegularField< Item >( { field, onChange, hideLabelFromVision, - defaultLayout, -}: FormFieldProps< Item > ) { +}: FieldLayoutProps< Item > ) { const { fields } = useContext( DataFormContext ); const form = useMemo( () => { @@ -58,15 +49,15 @@ export default function FormRegularField< Item >( { } return child; } ), - type: defaultLayout, + type: 'regular' as const, }; } return { - type: defaultLayout, + type: 'regular' as const, fields: [], }; - }, [ defaultLayout, field ] ); + }, [ field ] ); if ( isCombinedField( field ) ) { return ( diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index c0f31d5b4b60b..5f50f2b636dfa 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -557,3 +557,10 @@ export interface DataFormProps< Item > { form: Form; onChange: ( value: Record< string, any > ) => void; } + +export interface FieldLayoutProps< Item > { + data: Item; + field: FormField; + onChange: ( value: any ) => void; + hideLabelFromVision?: boolean; +} From 3f709f9f538d4e1d9d5bcc0afd529859da744071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:37:35 +0100 Subject: [PATCH 33/34] Introduce normalizeFormFields & form.labelPosition --- .../dataforms-layouts/data-form-layout.tsx | 24 +++++------ .../dataviews/src/normalize-form-fields.ts | 42 +++++++++++++++++++ packages/dataviews/src/types.ts | 3 +- 3 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 packages/dataviews/src/normalize-form-fields.ts diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index aeacfa6dcbc00..08cc47f569eaf 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __experimentalVStack as VStack } from '@wordpress/components'; -import { useContext } from '@wordpress/element'; +import { useContext, useMemo } from '@wordpress/element'; /** * Internal dependencies @@ -11,6 +11,7 @@ import type { Form, FormField, SimpleFormField } from '../types'; import { getFormFieldLayout } from './index'; import DataFormContext from '../components/dataform-context'; import { isCombinedField } from './is-combined-field'; +import normalizeFormFields from '../normalize-form-fields'; export function DataFormLayout< Item >( { data, @@ -41,21 +42,16 @@ export function DataFormLayout< Item >( { ); } + const normalizedFormFields = useMemo( + () => normalizeFormFields( form ), + [ form ] + ); + return ( - { form?.fields?.map( ( field ) => { - const formField: FormField = - typeof field !== 'string' - ? field - : { - id: field, - }; - const fieldLayoutId = formField.layout - ? formField.layout - : form.type; - const FieldLayout = getFormFieldLayout( - fieldLayoutId ?? 'regular' - )?.component; + { normalizedFormFields.map( ( formField ) => { + const FieldLayout = getFormFieldLayout( formField.layout ) + ?.component; if ( ! FieldLayout ) { return null; diff --git a/packages/dataviews/src/normalize-form-fields.ts b/packages/dataviews/src/normalize-form-fields.ts new file mode 100644 index 0000000000000..3cd5f67564d7c --- /dev/null +++ b/packages/dataviews/src/normalize-form-fields.ts @@ -0,0 +1,42 @@ +/** + * Internal dependencies + */ +import type { Form } from './types'; + +interface NormalizedFormField { + id: string; + layout: 'regular' | 'panel'; + labelPosition: 'side' | 'top' | 'none'; +} + +export default function normalizeFormFields( + form: Form +): NormalizedFormField[] { + let layout: 'regular' | 'panel' = 'regular'; + if ( [ 'regular', 'panel' ].includes( form.type ?? '' ) ) { + layout = form.type as 'regular' | 'panel'; + } + + const labelPosition = + form.labelPosition ?? ( layout === 'regular' ? 'top' : 'side' ); + + return ( form.fields ?? [] ).map( ( field ) => { + if ( typeof field === 'string' ) { + return { + id: field, + layout, + labelPosition, + }; + } + + const fieldLayout = field.layout ?? layout; + const fieldLabelPosition = + field.labelPosition ?? + ( fieldLayout === 'regular' ? 'top' : 'side' ); + return { + ...field, + layout: fieldLayout, + labelPosition: fieldLabelPosition, + }; + } ); +} diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 5f50f2b636dfa..8c4276f2541ec 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -534,7 +534,7 @@ export type SimpleFormField = { }; export type CombinedFormField = { - id?: string; + id: string; label?: string; layout?: 'regular' | 'panel'; labelPosition?: 'side' | 'top' | 'none'; @@ -549,6 +549,7 @@ export type FormField = SimpleFormField | CombinedFormField; export type Form = { type?: 'regular' | 'panel'; fields?: Array< FormField | string >; + labelPosition?: 'side' | 'top' | 'none'; }; export interface DataFormProps< Item > { From a81372550cc6d000377b9b63f6e05b1413ff29ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:58:41 +0100 Subject: [PATCH 34/34] Storybook: remove toFormField --- .../dataform/stories/index.story.tsx | 117 ++++++------------ 1 file changed, 38 insertions(+), 79 deletions(-) diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 1a5ddebe66b9f..ecad2af43fb84 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -8,8 +8,7 @@ import { ToggleControl } from '@wordpress/components'; * Internal dependencies */ import DataForm from '../index'; -import type { Field, Form, FormField } from '../../../types'; -import { isCombinedField } from '../../../dataforms-layouts/is-combined-field'; +import type { Field, Form } from '../../../types'; type SamplePost = { title: string; @@ -30,12 +29,12 @@ const meta = { control: { type: 'select' }, description: 'Chooses the default layout of each field. "regular" is the default layout.', - options: [ 'regular', 'panel' ], + options: [ 'default', 'regular', 'panel' ], }, labelPosition: { control: { type: 'select' }, description: 'Chooses the label position of the layout.', - options: [ 'default', 'top', 'side' ], + options: [ 'default', 'top', 'side', 'none' ], }, }, }; @@ -124,39 +123,12 @@ 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' && - isCombinedField( field ) && - type !== 'panel' - ) { - return { - ...field, - children: toFormField( field.children, labelPosition, type ), - }; - } - return field; - } ); -} - export const Default = ( { type, labelPosition, }: { - type: 'panel' | 'regular'; - labelPosition: 'default' | 'top' | 'side'; + type: 'default' | 'regular' | 'panel'; + labelPosition: 'default' | 'top' | 'side' | 'none'; } ) => { const [ post, setPost ] = useState( { title: 'Hello, World!', @@ -171,27 +143,22 @@ export const Default = ( { const form = useMemo( () => ( { - fields: toFormField( - [ - 'title', - 'order', - { - id: 'sticky', - layout: 'regular', - labelPosition: - type === 'regular' && labelPosition !== 'default' - ? labelPosition - : 'side', - }, - 'author', - 'reviewer', - 'password', - 'date', - 'birthdate', - ], - labelPosition, - type - ), + type, + labelPosition, + fields: [ + 'title', + 'order', + { + id: 'sticky', + layout: 'regular', + labelPosition: 'side', + }, + 'author', + 'reviewer', + 'password', + 'date', + 'birthdate', + ], } ), [ type, labelPosition ] ) as Form; @@ -200,10 +167,7 @@ export const Default = ( { data={ post } fields={ fields } - form={ { - ...form, - type, - } } + form={ form } onChange={ ( edits ) => setPost( ( prev ) => ( { ...prev, @@ -215,11 +179,11 @@ export const Default = ( { }; const CombinedFieldsComponent = ( { - type = 'regular', + type, labelPosition, }: { - type: 'panel' | 'regular'; - labelPosition: 'default' | 'top' | 'side'; + type: 'default' | 'regular' | 'panel'; + labelPosition: 'default' | 'top' | 'side' | 'none'; } ) => { const [ post, setPost ] = useState< SamplePost >( { title: 'Hello, World!', @@ -233,20 +197,18 @@ const CombinedFieldsComponent = ( { const form = useMemo( () => ( { - fields: toFormField( - [ - 'title', - { - id: 'status', - label: 'Status & Visibility', - children: [ 'status', 'password' ], - }, - 'order', - 'author', - ], - labelPosition, - type - ), + type, + labelPosition, + fields: [ + 'title', + { + id: 'status', + label: 'Status & Visibility', + children: [ 'status', 'password' ], + }, + 'order', + 'author', + ], } ), [ type, labelPosition ] ) as Form; @@ -255,10 +217,7 @@ const CombinedFieldsComponent = ( { data={ post } fields={ fields } - form={ { - ...form, - type, - } } + form={ form } onChange={ ( edits ) => setPost( ( prev ) => ( { ...prev,