From da0b766e57665110a86786621948a524e84aa126 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Fri, 4 Oct 2024 09:28:00 +0200 Subject: [PATCH 01/44] 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 00000000000000..c5a62bbe7c9522 --- /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 58f0bf06afb414..c1e3555060fd19 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 d95d59a88394e4..4a87b4f3bdc600 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 2a335dce3af32b..1d9b2c5b1f28f5 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -536,12 +536,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 538ef3ed2e01c43d18fd3d0a941ffdc6cc06efe3 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:20:22 +0000 Subject: [PATCH 02/44] 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 | 67 ++++++++++++------- .../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 | 61 ++++++++++++----- .../src/dataforms-layouts/regular/index.tsx | 39 +++++++++-- packages/dataviews/src/types.ts | 5 +- 9 files changed, 270 insertions(+), 62 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 c5a62bbe7c9522..0b6dab93ddef89 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 c1e3555060fd19..ae039059576684 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 c929c21f1c21a9..1446384965e603 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 } from '../../../types'; +import type { Form } from '../../../types'; const meta = { title: 'DataViews/DataForm', @@ -16,13 +16,25 @@ 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' ], }, }, }; export default meta; +type SamplePost = { + title: string; + order: number; + author: number; + status: string; + reviewer: string; + date: string; + birthdate: string; + sampleField?: string; + password?: string; +}; + const fields = [ { id: 'title', @@ -84,7 +96,11 @@ const fields = [ }, ]; -export const Default = ( { type }: { type: 'panel' | 'regular' } ) => { +export const Default = ( { + type, +}: { + type: 'panel' | 'regular' | 'inline'; +} ) => { const [ post, setPost ] = useState( { title: 'Hello, World!', order: 2, @@ -99,13 +115,17 @@ export const Default = ( { type }: { type: 'panel' | 'regular' } ) => { fields: [ 'title', 'order', + { + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], + }, 'author', 'reviewer', - 'status', 'date', 'birthdate', ], - }; + } as Form< SamplePost >; return ( { 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={ { @@ -175,11 +196,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 00000000000000..b6e484e5155a33 --- /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 9434ea724ed4ca..3d0152c2a9f48c 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 00000000000000..571850220a26d5 --- /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 4a43d25436fe74..ba925ed4bfc002 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,11 +18,13 @@ 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, FormField } from '../../types'; +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; } @@ -56,11 +58,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 >( @@ -79,13 +91,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 ) => ( + + ) } + ) } /> @@ -156,10 +183,10 @@ export default function FormPanel< Item >( { { 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 57aa163b890e5f..66664a55c32296 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -2,14 +2,45 @@ * 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 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, @@ -33,10 +64,10 @@ export default function FormRegular< Item >( { { visibleFields.map( ( field ) => { return ( - ); diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 1d9b2c5b1f28f5..85f7d6d4e66e99 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -539,7 +539,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[]; }; @@ -548,7 +549,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 9cb2b26e9ddc5c454b487adf603ee690c8550b54 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:30:12 +0000 Subject: [PATCH 03/44] 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 ba925ed4bfc002..153b4d369edde3 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -150,6 +150,9 @@ export function FormPanelField< Item >( { data={ data } field={ nestedField } onChange={ onChange } + hideLabelFromVision={ + childrenFields.length < 2 + } /> ) } From c641723d671a7bfad955a44342b6fa011046c9c9 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:43:38 +0000 Subject: [PATCH 04/44] 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 0b6dab93ddef89..7b59ef1fad2965 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 a707275562bcb7cd82446759e701cc0de978449e Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:49:59 +0000 Subject: [PATCH 05/44] 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 85f7d6d4e66e99..2e5465761ba13d 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -541,7 +541,7 @@ export type FormField = | { id: string; layout?: 'regular' | 'panel' | 'inline'; - field?: string; + field?: FormField; fields?: FormField[]; }; From 93b8e535362be0268819575206de1fe5efe56078 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Wed, 30 Oct 2024 12:22:18 +0100 Subject: [PATCH 06/44] QuickEdit: implement Template field --- package-lock.json | 1 + .../src/components/post-edit/index.js | 5 + .../src/components/post-fields/index.js | 7 +- packages/fields/README.md | 4 + packages/fields/package.json | 1 + packages/fields/src/actions/utils.ts | 4 +- packages/fields/src/fields/index.ts | 1 + packages/fields/src/fields/template/index.ts | 22 ++ .../fields/src/fields/template/style.scss | 45 ++++ .../src/fields/template/template-edit.tsx | 224 ++++++++++++++++++ 10 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 packages/fields/src/fields/template/index.ts create mode 100644 packages/fields/src/fields/template/style.scss create mode 100644 packages/fields/src/fields/template/template-edit.tsx diff --git a/package-lock.json b/package-lock.json index a1060b1267af50..85f59d577a6bf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54447,6 +54447,7 @@ "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", "@wordpress/blob": "*", + "@wordpress/block-editor": "*", "@wordpress/blocks": "*", "@wordpress/components": "*", "@wordpress/compose": "*", diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index b9d3f60ef5da2b..0975cc87ab9c14 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -67,6 +67,11 @@ function PostEditForm( { postType, postId } ) { 'date', 'slug', 'comment_status', + { + id: 'template', + layout: 'inline', + fields: [ 'template' ], + }, ], }; diff --git a/packages/edit-site/src/components/post-fields/index.js b/packages/edit-site/src/components/post-fields/index.js index a921799aabbea2..587caaee372464 100644 --- a/packages/edit-site/src/components/post-fields/index.js +++ b/packages/edit-site/src/components/post-fields/index.js @@ -8,7 +8,11 @@ import clsx from 'clsx'; */ import { __, sprintf } from '@wordpress/i18n'; import { decodeEntities } from '@wordpress/html-entities'; -import { featuredImageField, slugField } from '@wordpress/fields'; +import { + featuredImageField, + slugField, + templateField, +} from '@wordpress/fields'; import { createInterpolateElement, useMemo, @@ -321,6 +325,7 @@ function usePostFields( viewType ) { }, }, slugField, + templateField, { id: 'comment_status', label: __( 'Discussion' ), diff --git a/packages/fields/README.md b/packages/fields/README.md index 0a891f9b07420c..a5c7a16a62eaa1 100644 --- a/packages/fields/README.md +++ b/packages/fields/README.md @@ -74,6 +74,10 @@ Undocumented declaration. Undocumented declaration. +### templateField + +Undocumented declaration. + ### titleField Undocumented declaration. diff --git a/packages/fields/package.json b/packages/fields/package.json index 43772cb41981a5..bf6561ab94d60a 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -34,6 +34,7 @@ "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", "@wordpress/blob": "*", + "@wordpress/block-editor": "*", "@wordpress/blocks": "*", "@wordpress/components": "*", "@wordpress/compose": "*", diff --git a/packages/fields/src/actions/utils.ts b/packages/fields/src/actions/utils.ts index 14e8edae4be048..df8b121aeec28a 100644 --- a/packages/fields/src/actions/utils.ts +++ b/packages/fields/src/actions/utils.ts @@ -30,7 +30,9 @@ export function isTemplateOrTemplatePart( return p.type === TEMPLATE_POST_TYPE || p.type === TEMPLATE_PART_POST_TYPE; } -export function getItemTitle( item: Post ) { +export function getItemTitle( item: { + title: string | { rendered: string } | { raw: string }; +} ) { if ( typeof item.title === 'string' ) { return decodeEntities( item.title ); } diff --git a/packages/fields/src/fields/index.ts b/packages/fields/src/fields/index.ts index 24655a3d866cfd..18576deb2c0f0f 100644 --- a/packages/fields/src/fields/index.ts +++ b/packages/fields/src/fields/index.ts @@ -2,3 +2,4 @@ export { default as slugField } from './slug'; export { default as titleField } from './title'; export { default as orderField } from './order'; export { default as featuredImageField } from './featured-image'; +export { default as templateField } from './template'; diff --git a/packages/fields/src/fields/template/index.ts b/packages/fields/src/fields/template/index.ts new file mode 100644 index 00000000000000..7315b4ba349b1c --- /dev/null +++ b/packages/fields/src/fields/template/index.ts @@ -0,0 +1,22 @@ +/** + * WordPress dependencies + */ +import type { Field } from '@wordpress/dataviews'; + +/** + * Internal dependencies + */ +import { __ } from '@wordpress/i18n'; +import type { BasePost } from '../../types'; +import { TemplateEdit } from './template-edit'; + +const templateField: Field< BasePost > = { + id: 'template', + type: 'text', + label: __( 'Template' ), + getValue: ( { item } ) => item.template, + Edit: TemplateEdit, + enableSorting: false, +}; + +export default templateField; diff --git a/packages/fields/src/fields/template/style.scss b/packages/fields/src/fields/template/style.scss new file mode 100644 index 00000000000000..833414e6fa2e13 --- /dev/null +++ b/packages/fields/src/fields/template/style.scss @@ -0,0 +1,45 @@ +.editor-post-template__swap-template-modal { + z-index: z-index(".editor-post-template__swap-template-modal"); +} + +.editor-post-template__swap-template-modal-content +.block-editor-block-patterns-list { + column-count: 2; + column-gap: $grid-unit-30; + + // Small top padding required to avoid cutting off the visible outline when hovering items + padding-top: $border-width-focus-fallback; + + @include break-medium() { + column-count: 3; + } + + @include break-wide() { + column-count: 4; + } + + .block-editor-block-patterns-list__list-item { + break-inside: avoid-column; + } +} + +.editor-post-template__dropdown { + .components-popover__content { + min-width: 240px; + } + .components-button.is-pressed, + .components-button.is-pressed:hover { + background: inherit; + color: inherit; + } +} + +.editor-post-template__create-form { + @include break-medium() { + width: $grid-unit * 40; + } +} + +.editor-post-template__classic-theme-dropdown { + padding: $grid-unit-10; +} diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx new file mode 100644 index 00000000000000..96286a55b93438 --- /dev/null +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -0,0 +1,224 @@ +/** + * WordPress dependencies + */ +import { useCallback, useMemo, useState } from '@wordpress/element'; +// @ts-ignore +import { parse } from '@wordpress/blocks'; +import type { WpTemplate } from '@wordpress/core-data'; +import { store as coreStore } from '@wordpress/core-data'; +import type { DataFormControlProps } from '@wordpress/dataviews'; + +/** + * Internal dependencies + */ +// @ts-expect-error block-editor is not typed correctly. +import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; +import { + Button, + Dropdown, + MenuGroup, + MenuItem, + Modal, +} from '@wordpress/components'; +import { useAsyncList } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; +import { decodeEntities } from '@wordpress/html-entities'; +import { __ } from '@wordpress/i18n'; +import { getItemTitle } from '../../actions/utils'; +import type { BasePost } from '../../types'; + +function useTemplates( postType: string ) { + return useSelect( + ( select ) => + select( coreStore ).getEntityRecords< WpTemplate >( + 'postType', + 'wp_template', + { + per_page: -1, + post_type: postType, + } + ) ?? [], + [ postType ] + ); +} + +export function useAllowSwitchingTemplates( { + postType, + postId, +}: { + postType: string; + postId: number; +} ): boolean { + return useSelect( + ( select ) => { + const { canUser, getEntityRecord, getEntityRecords } = + select( coreStore ); + const siteSettings = canUser( 'read', { + kind: 'root', + name: 'site', + } ) + ? // @ts-expect-error getEntityRecord is not typed correctly. + getEntityRecord< { + page_for_posts: number; + page_on_front: number; + } >( 'root', 'site' ) + : undefined; + const templates = getEntityRecords< WpTemplate >( + 'postType', + 'wp_template', + { + per_page: -1, + } + ); + const isPostsPage = +postId === siteSettings?.page_for_posts; + // If current page is set front page or posts page, we also need + // to check if the current theme has a template for it. If not + const isFrontPage = + postType === 'page' && + +postId === siteSettings?.page_on_front && + templates?.some( ( { slug } ) => slug === 'front-page' ); + return ! isPostsPage && ! isFrontPage; + }, + [ postId, postType ] + ); +} + +export const TemplateEdit = ( { + data, + field, + onChange, +}: DataFormControlProps< BasePost > ) => { + const { id } = field; + const postType = data.type; + const slug = data.slug; + + const allowSwitchingTemplate = useAllowSwitchingTemplates( { + postType: data.type, + postId: typeof data.id === 'number' ? data.id : parseInt( data.id, 10 ), + } ); + const templates = useTemplates( data.type ); + + const availableTemplates = useMemo( + () => + allowSwitchingTemplate + ? templates.filter( + ( template ) => + template.is_custom && + template.slug !== data.template && + !! template.content.raw // Skip empty templates. + ) + : [], + [ allowSwitchingTemplate, templates, data.template ] + ); + + const templatesAsPatterns = useMemo( + () => + availableTemplates.map( ( template ) => ( { + name: template.slug, + blocks: parse( template.content.raw ), + title: decodeEntities( template.title.rendered ), + id: template.id, + } ) ), + [ availableTemplates ] + ); + + const shownTemplates = useAsyncList( templatesAsPatterns ); + + const value = field.getValue( { item: data } ); + + const currentTemplate = useSelect( + ( select ) => { + const foundTemplate = templates?.find( + ( template ) => template.slug === value + ); + + if ( foundTemplate ) { + return foundTemplate; + } + + let slugToCheck; + // In `draft` status we might not have a slug available, so we use the `single` + // post type templates slug(ex page, single-post, single-product etc..). + // Pages do not need the `single` prefix in the slug to be prioritized + // through template hierarchy. + if ( slug ) { + slugToCheck = + postType === 'page' + ? `${ postType }-${ slug }` + : `single-${ postType }-${ slug }`; + } else { + slugToCheck = + postType === 'page' ? 'page' : `single-${ postType }`; + } + + if ( postType ) { + const templateId = select( coreStore ).getDefaultTemplateId( { + slug: slugToCheck, + } ); + + return select( coreStore ).getEntityRecord( + 'postType', + 'wp_template', + templateId + ); + } + }, + [ postType, slug, templates, value ] + ); + + const [ showModal, setShowModal ] = useState( false ); + + const onChangeControl = useCallback( + ( newValue: string ) => + onChange( { + [ id ]: newValue, + } ), + [ id, onChange ] + ); + + return ( +
+ ( + + ) } + renderContent={ () => ( + + setShowModal( true ) }> + { __( 'Swap template' ) } + + + ) } + /> + { showModal && ( + setShowModal( false ) } + overlayClassName="editor-post-template__swap-template-modal" + isFullScreen + > +
+ { + onChangeControl( template.id ); + } } + /> +
+
+ ) } +
+ ); +}; From ca3b59b4da6620e717650b98cabc0165a0787717 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Wed, 30 Oct 2024 14:49:54 +0100 Subject: [PATCH 07/44] improve style --- packages/base-styles/_z-index.scss | 1 + packages/edit-site/src/style.scss | 3 +- .../fields/src/fields/template/style.scss | 28 ++----------------- .../src/fields/template/template-edit.tsx | 4 +-- packages/fields/src/style.scss | 3 ++ packages/fields/src/styles.scss | 1 - 6 files changed, 10 insertions(+), 30 deletions(-) create mode 100644 packages/fields/src/style.scss delete mode 100644 packages/fields/src/styles.scss diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 7bb71732546053..6ea9736245af20 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -130,6 +130,7 @@ $z-layers: ( ".editor-action-modal": 1000001, ".editor-post-template__swap-template-modal": 1000001, ".edit-site-template-panel__replace-template-modal": 1000001, + ".fields-controls__template-modal": 1000001, // Note: The ConfirmDialog component's z-index is being set to 1000001 in packages/components/src/confirm-dialog/styles.ts // because it uses emotion and not sass. We need it to render on top its parent popover. diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index b2f5c826046b79..20626882e5fdfa 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -1,6 +1,5 @@ @import "../../dataviews/src/style.scss"; -@import "../../fields/src/styles.scss"; -@import "../../fields/src/fields/featured-image/style.scss"; +@import "../../fields/src/style.scss"; @import "./components/add-new-template/style.scss"; @import "./components/block-editor/style.scss"; diff --git a/packages/fields/src/fields/template/style.scss b/packages/fields/src/fields/template/style.scss index 833414e6fa2e13..a0c2fafec73893 100644 --- a/packages/fields/src/fields/template/style.scss +++ b/packages/fields/src/fields/template/style.scss @@ -1,9 +1,8 @@ -.editor-post-template__swap-template-modal { - z-index: z-index(".editor-post-template__swap-template-modal"); +.fields-controls__template-modal { + z-index: z-index(".fields-controls__template-modal"); } -.editor-post-template__swap-template-modal-content -.block-editor-block-patterns-list { +.fields-controls__template-content .block-editor-block-patterns-list { column-count: 2; column-gap: $grid-unit-30; @@ -22,24 +21,3 @@ break-inside: avoid-column; } } - -.editor-post-template__dropdown { - .components-popover__content { - min-width: 240px; - } - .components-button.is-pressed, - .components-button.is-pressed:hover { - background: inherit; - color: inherit; - } -} - -.editor-post-template__create-form { - @include break-medium() { - width: $grid-unit * 40; - } -} - -.editor-post-template__classic-theme-dropdown { - padding: $grid-unit-10; -} diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index 96286a55b93438..aee40d792afeff 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -204,10 +204,10 @@ export const TemplateEdit = ( { setShowModal( false ) } - overlayClassName="editor-post-template__swap-template-modal" + overlayClassName="fields-controls__template-modal" isFullScreen > -
+
Date: Wed, 30 Oct 2024 19:52:22 +0100 Subject: [PATCH 08/44] implement logic to reset to default template --- .../src/fields/template/template-edit.tsx | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index aee40d792afeff..e323da91bd4bdd 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -192,11 +192,29 @@ export const TemplateEdit = ( { : '' } ) } - renderContent={ () => ( + renderContent={ ( { onToggle } ) => ( - setShowModal( true ) }> + { + setShowModal( true ); + onToggle(); + } } + > { __( 'Swap template' ) } + { + // The default template in a post is indicated by an empty string + value !== '' && ( + { + onChangeControl( '' ); + onToggle(); + } } + > + { __( 'Use default template' ) } + + ) + } ) } /> @@ -212,8 +230,11 @@ export const TemplateEdit = ( { label={ __( 'Templates' ) } blockPatterns={ templatesAsPatterns } shownPatterns={ shownTemplates } - onClickPattern={ ( template: WpTemplate ) => { - onChangeControl( template.id ); + onClickPattern={ ( + template: ( typeof templatesAsPatterns )[ 0 ] + ) => { + onChangeControl( template.name ); + setShowModal( false ); } } />
From 5a623fbf0825298547b80f782e420610e6d61bab Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Wed, 30 Oct 2024 21:49:48 +0100 Subject: [PATCH 09/44] ensure that setting object is defined --- .../src/components/post-edit/index.js | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index 2041e36bfe2db1..ecaa6221e2dec8 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -20,6 +20,8 @@ import { privateApis as editorPrivateApis } from '@wordpress/editor'; import Page from '../page'; import usePostFields from '../post-fields'; import { unlock } from '../../lock-unlock'; +import usePatternSettings from '../page-patterns/use-pattern-settings'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; const { PostCardPanel } = unlock( editorPrivateApis ); @@ -139,17 +141,26 @@ function PostEditForm( { postType, postId } ) { } export function PostEdit( { postType, postId } ) { + const { ExperimentalBlockEditorProvider } = unlock( + blockEditorPrivateApis + ); + const settings = usePatternSettings(); + // Wrap everything in a block editor provider. + // This ensures 'styles' that are needed for the previews are synced + // from the site editor store to the block editor store. return ( - - { postId && ( - - ) } - { ! postId &&

{ __( 'Select a page to edit' ) }

} -
+ + + { postId && ( + + ) } + { ! postId &&

{ __( 'Select a page to edit' ) }

} +
+
); } From f9b1eef8586db56a0a7ace3b126ce29321d41c4a Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Fri, 4 Oct 2024 09:28:00 +0200 Subject: [PATCH 10/44] 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 00000000000000..c5a62bbe7c9522 --- /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 58f0bf06afb414..c1e3555060fd19 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 d95d59a88394e4..4a87b4f3bdc600 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 0ea0965704d18c..ce8f24e4bef33e 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -541,12 +541,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 db4cbd61f3748c9c212063c112f8177d3473f798 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:20:22 +0000 Subject: [PATCH 11/44] 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 c5a62bbe7c9522..0b6dab93ddef89 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 c1e3555060fd19..ae039059576684 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 b59d79063200bf..97292141d363b0 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 00000000000000..b6e484e5155a33 --- /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 9434ea724ed4ca..3d0152c2a9f48c 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 00000000000000..571850220a26d5 --- /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 b74e5e4667d4b1..34c7eedc420a52 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 6a340a50584df4..4ff0149bac560d 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 ce8f24e4bef33e..fa3529caf47713 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -544,7 +544,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[]; }; @@ -553,7 +554,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 2d7ee1b09d2999c865545b9ea5bf3a9b50ed81fa Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:30:12 +0000 Subject: [PATCH 12/44] 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 34c7eedc420a52..657c936a1ba3f9 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 2ba1ffb12659bd7b75c74f920265e7bf9c432619 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:43:38 +0000 Subject: [PATCH 13/44] 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 0b6dab93ddef89..7b59ef1fad2965 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 4384814be406645351ffc54f5790d11d6f25c790 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:49:59 +0000 Subject: [PATCH 14/44] 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 fa3529caf47713..b77168e66eddf2 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -546,7 +546,7 @@ export type FormField = | { id: string; layout?: 'regular' | 'panel' | 'inline'; - field?: string; + field?: FormField; fields?: FormField[]; }; From 8e4b0efaa4ce0afb971822869fb44361a303214d Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 12:48:40 +0100 Subject: [PATCH 15/44] 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 90a92ac861bdd1..00000000000000 --- 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 97e052ed897989..00000000000000 --- 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 97292141d363b0..3c22359f6749c6 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 b6e484e5155a33..a50cdee7d12e91 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 657c936a1ba3f9..1594abb85e68ff 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 4ff0149bac560d..2ddd883ca023d8 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 5ef219e45a4787..2d1cc0402bc206 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'; /** * Apply default values and normalize the fields config. @@ -72,29 +66,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 26c6ecea645f43..087e812fffa192 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 b77168e66eddf2..985e07a58d58ea 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -525,46 +525,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 41969a7960af65..cc0b031f6c96c6 100644 --- a/packages/dataviews/src/validation.ts +++ b/packages/dataviews/src/validation.ts @@ -7,7 +7,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 fbff29ed67afa1..6e3e061f38fb4c 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -73,6 +73,11 @@ function PostEditForm( { postType, postId } ) { fields: [ 'featured_media', 'title', + { + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], + }, 'status_and_visibility', 'author', 'date', @@ -84,15 +89,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 152c875408513bbbe6a31c6bea72094f257bc6d0 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 12:55:03 +0100 Subject: [PATCH 16/44] 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 3d0152c2a9f48c..32469b6ebf0a6f 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 571850220a26d5..e996f63d7c42c9 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 1594abb85e68ff..1227c923591f70 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 2ddd883ca023d8..0eb4c5ab1e6063 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 7cfb87d683386749e08c9e3db18e477ba24ec52b Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 13:08:31 +0100 Subject: [PATCH 17/44] 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 7b59ef1fad2965..49170747472e12 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 e996f63d7c42c9..c71dc25b111f5b 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 1227c923591f70..dd3c32806fec73 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 0eb4c5ab1e6063..683aff2f62b98a 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 18/44] 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 dd3c32806fec73..0a284ca88a5525 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 ccee384214474f..861f0c7a92f83a 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -525,14 +525,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 3e5a5846d34e02cd3ae3cf4bfc3caebf04ace035 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 14:10:43 +0100 Subject: [PATCH 19/44] 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 3c22359f6749c6..b3889f59df065b 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 58150efbc383931f0e000b78b253f4c92288c5bf Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 14:13:27 +0100 Subject: [PATCH 20/44] 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 b3889f59df065b..35b884d3054564 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 8698f0fe9bd4ea91f3b3a02274691fb8c5c750be Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Fri, 4 Oct 2024 09:28:00 +0200 Subject: [PATCH 21/44] 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 00000000000000..c5a62bbe7c9522 --- /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 58f0bf06afb414..c1e3555060fd19 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 d95d59a88394e4..4a87b4f3bdc600 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 0ea0965704d18c..ce8f24e4bef33e 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -541,12 +541,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 6a0598dcb97c4d99eed7998338696a9ae3057004 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:20:22 +0000 Subject: [PATCH 22/44] 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 c5a62bbe7c9522..0b6dab93ddef89 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 c1e3555060fd19..ae039059576684 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 b59d79063200bf..97292141d363b0 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 00000000000000..b6e484e5155a33 --- /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 9434ea724ed4ca..3d0152c2a9f48c 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 00000000000000..571850220a26d5 --- /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 b74e5e4667d4b1..34c7eedc420a52 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 6a340a50584df4..4ff0149bac560d 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 ce8f24e4bef33e..fa3529caf47713 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -544,7 +544,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[]; }; @@ -553,7 +554,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 bbae78854271400128852e0c72e56fb5f18750bc Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:30:12 +0000 Subject: [PATCH 23/44] 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 34c7eedc420a52..657c936a1ba3f9 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 e17c228256bbfa891de806281c669b6a5d774933 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:43:38 +0000 Subject: [PATCH 24/44] 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 0b6dab93ddef89..7b59ef1fad2965 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 05d750614089cf24c74a93f953ad5f768885e12c Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 28 Oct 2024 16:49:59 +0000 Subject: [PATCH 25/44] 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 fa3529caf47713..b77168e66eddf2 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -546,7 +546,7 @@ export type FormField = | { id: string; layout?: 'regular' | 'panel' | 'inline'; - field?: string; + field?: FormField; fields?: FormField[]; }; From 15b0f11898b635459dba646e3635bc183e935358 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 12:48:40 +0100 Subject: [PATCH 26/44] 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 90a92ac861bdd1..00000000000000 --- 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 97e052ed897989..00000000000000 --- 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 97292141d363b0..3c22359f6749c6 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 b6e484e5155a33..a50cdee7d12e91 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 657c936a1ba3f9..1594abb85e68ff 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 4ff0149bac560d..2ddd883ca023d8 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 5ef219e45a4787..2d1cc0402bc206 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'; /** * Apply default values and normalize the fields config. @@ -72,29 +66,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 26c6ecea645f43..087e812fffa192 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 b77168e66eddf2..985e07a58d58ea 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -525,46 +525,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 0a6542da4e8d40..bcc9a15908ff59 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 fbff29ed67afa1..6e3e061f38fb4c 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -73,6 +73,11 @@ function PostEditForm( { postType, postId } ) { fields: [ 'featured_media', 'title', + { + id: 'status', + layout: 'panel', + fields: [ 'status', 'password' ], + }, 'status_and_visibility', 'author', 'date', @@ -84,15 +89,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 a8d24623e1c4a0fc118377a3181deb76f11d8dc8 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 12:55:03 +0100 Subject: [PATCH 27/44] 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 3d0152c2a9f48c..32469b6ebf0a6f 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 571850220a26d5..e996f63d7c42c9 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 1594abb85e68ff..1227c923591f70 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 2ddd883ca023d8..0eb4c5ab1e6063 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 59677975a3cb455a7c0aee01122087ec5a9e8593 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 13:08:31 +0100 Subject: [PATCH 28/44] 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 7b59ef1fad2965..49170747472e12 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 e996f63d7c42c9..c71dc25b111f5b 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 1227c923591f70..dd3c32806fec73 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 0eb4c5ab1e6063..683aff2f62b98a 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 29/44] 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 dd3c32806fec73..0a284ca88a5525 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 ccee384214474f..861f0c7a92f83a 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -525,14 +525,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 a8f9e8203f9baa2c7fba2943b4213379928222a3 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 14:10:43 +0100 Subject: [PATCH 30/44] 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 3c22359f6749c6..b3889f59df065b 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 f9e86cb0775dc400c97f69568db902ec83d3c450 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 14:13:27 +0100 Subject: [PATCH 31/44] 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 b3889f59df065b..35b884d3054564 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 b05dbef2ff3cfa29b0113eefe2c3f0ab9db9e48e Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 31 Oct 2024 16:51:07 +0100 Subject: [PATCH 32/44] 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 0a284ca88a5525..a6e26ee7bb78c7 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 8c15a9be3e78f8d09070b0dd66c141002048ca17 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 4 Nov 2024 13:40:49 +0100 Subject: [PATCH 33/44] 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 08c60bbd9d55a2..d09440d9c983e8 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -79,7 +79,6 @@ function PostEditForm( { postType, postId } ) { layout: 'panel', fields: [ 'status', 'password' ], }, - 'status_and_visibility', 'author', 'date', 'slug', From 8ea852c623afebd4346517a4b8b42b06fccda6e3 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 4 Nov 2024 13:50:16 +0100 Subject: [PATCH 34/44] 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 35b884d3054564..fecf607b8cfe5a 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 a6e26ee7bb78c7..b6c42d9d64ba62 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 861f0c7a92f83a..a5d489055b7367 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -536,7 +536,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 d09440d9c983e8..5189e326887587 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -77,7 +77,7 @@ function PostEditForm( { postType, postId } ) { id: 'status', label: __( 'Status & Visibility' ), layout: 'panel', - fields: [ 'status', 'password' ], + children: [ 'status', 'password' ], }, 'author', 'date', From 0c67f64725552f0a266a4c3c13ae41e23f6dd4bb Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 4 Nov 2024 16:59:36 +0100 Subject: [PATCH 35/44] 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 fecf607b8cfe5a..d17e6bd02c600b 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 a50cdee7d12e91..bd7d53b431752d 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 b6c42d9d64ba62..46ebc554f0724c 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 36/44] 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 d17e6bd02c600b..e202ce8acba8cf 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 32469b6ebf0a6f..5e4f3617d9c7dd 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 c71dc25b111f5b..00000000000000 --- 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 46ebc554f0724c..95c43279655b05 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 2c741f9b7ec0a6..113408f8ee6abb 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 ddf996a583b00052e1e8e0e031c616eb026c0f37 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 5 Nov 2024 14:08:11 +0100 Subject: [PATCH 37/44] Remove field type checking within dataform layouts --- .../src/components/dataform-context/index.tsx | 4 +- .../dataform/stories/index.story.tsx | 110 ++++++++++-------- .../dataforms-layouts/data-form-layout.tsx | 24 ++-- .../dataforms-layouts/get-visible-fields.ts | 16 --- .../src/dataforms-layouts/panel/index.tsx | 23 ++-- .../src/dataforms-layouts/regular/index.tsx | 14 ++- packages/dataviews/src/types.ts | 18 ++- 7 files changed, 107 insertions(+), 102 deletions(-) delete mode 100644 packages/dataviews/src/dataforms-layouts/get-visible-fields.ts diff --git a/packages/dataviews/src/components/dataform-context/index.tsx b/packages/dataviews/src/components/dataform-context/index.tsx index 49170747472e12..e3973ad09b1272 100644 --- a/packages/dataviews/src/components/dataform-context/index.tsx +++ b/packages/dataviews/src/components/dataform-context/index.tsx @@ -10,7 +10,7 @@ import type { FormField, NormalizedField } from '../../types'; type DataFormContextType< Item > = { getFieldDefinition: ( - field: FormField + field: FormField | string ) => NormalizedField< Item > | undefined; }; @@ -23,7 +23,7 @@ export function DataFormProvider< Item >( { children, }: React.PropsWithChildren< { fields: NormalizedField< Item >[] } > ) { const getFieldDefinition = useCallback( - ( field: FormField ) => { + ( field: FormField | string ) => { const fieldId = typeof field === 'string' ? field : field.id; const definition = fields.find( diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index e202ce8acba8cf..849703ef8ad2d8 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -8,7 +8,7 @@ import { ToggleControl } from '@wordpress/components'; * Internal dependencies */ import DataForm from '../index'; -import type { Field, Form } from '../../../types'; +import type { Field, Form, FormField } from '../../../types'; type SamplePost = { title: string; @@ -123,6 +123,33 @@ const fields = [ }, ] as Field< SamplePost >[]; +function toFormField( + formFields: Array< string | FormField >, + labelPosition: 'default' | 'top' | 'side', + type: 'panel' | 'regular' +): FormField[] { + return formFields.map( ( field ) => { + if ( typeof field === 'string' ) { + return { + id: field, + layout: type, + labelPosition: + labelPosition === 'default' ? undefined : labelPosition, + }; + } else if ( + typeof field !== 'string' && + field.children && + type !== 'panel' + ) { + return { + ...field, + children: toFormField( field.children, labelPosition, type ), + }; + } + return field; + } ); +} + export const Default = ( { type, labelPosition, @@ -143,32 +170,27 @@ export const Default = ( { const form = useMemo( () => ( { - fields: [ - 'title', - 'order', - { - id: 'sticky', - layout: 'regular', - labelPosition: type === 'regular' ? labelPosition : 'side', - }, - 'author', - 'reviewer', - 'password', - 'date', - 'birthdate', - ].map( ( field ) => { - if ( - labelPosition !== 'default' && - typeof field === 'string' - ) { - return { - id: field, - layout: type, - labelPosition, - }; - } - return field; - } ), + fields: toFormField( + [ + 'title', + 'order', + { + id: 'sticky', + layout: 'regular', + labelPosition: + type === 'regular' && labelPosition !== 'default' + ? labelPosition + : 'side', + }, + 'author', + 'reviewer', + 'password', + 'date', + 'birthdate', + ], + labelPosition, + type + ), } ), [ type, labelPosition ] ) as Form; @@ -210,27 +232,19 @@ const CombinedFieldsComponent = ( { const form = useMemo( () => ( { - fields: [ - 'title', - { - id: 'status', - children: [ 'status', 'password' ], - }, - 'order', - 'author', - ].map( ( field ) => { - if ( - labelPosition !== 'default' && - typeof field === 'string' - ) { - return { - id: field, - layout: type, - labelPosition, - }; - } - return field; - } ), + fields: toFormField( + [ + 'title', + { + id: 'status', + children: [ 'status', 'password' ], + }, + 'order', + 'author', + ], + labelPosition, + type + ), } ), [ type, labelPosition ] ) as Form; diff --git a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx index bd7d53b431752d..086d7950885079 100644 --- a/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx +++ b/packages/dataviews/src/dataforms-layouts/data-form-layout.tsx @@ -20,7 +20,7 @@ export function DataFormLayout< Item >( { }: { defaultLayout?: string; data: Item; - fields: FormField[]; + fields: Array< FormField | string >; onChange: ( value: any ) => void; children?: ( FieldLayout: ( props: { @@ -37,10 +37,15 @@ export function DataFormLayout< Item >( { return ( { fields.map( ( field ) => { - const fieldLayoutId = - typeof field !== 'string' && field.layout - ? field.layout - : defaultLayout; + const formField: FormField = + typeof field !== 'string' + ? field + : { + id: field, + }; + const fieldLayoutId = formField.layout + ? formField.layout + : defaultLayout; const FieldLayout = getFormFieldLayout( fieldLayoutId ?? 'regular' )?.component; @@ -49,8 +54,7 @@ export function DataFormLayout< Item >( { return null; } - const fieldId = typeof field === 'string' ? field : field.id; - const fieldDefinition = getFieldDefinition( fieldId ); + const fieldDefinition = getFieldDefinition( formField ); if ( ! fieldDefinition || @@ -61,14 +65,14 @@ export function DataFormLayout< Item >( { } if ( children ) { - return children( FieldLayout, field ); + return children( FieldLayout, formField ); } return ( ); diff --git a/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts b/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts deleted file mode 100644 index b7cbd679782335..00000000000000 --- a/packages/dataviews/src/dataforms-layouts/get-visible-fields.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Internal dependencies - */ -import type { Field, FormField } from '../types'; - -export function getVisibleFields< Item >( - fields: Field< Item >[], - formFields: FormField[] = [] -): Field< Item >[] { - const visibleFields: Array< Field< Item > > = [ ...fields ]; - return formFields - .map( ( fieldId ) => - visibleFields.find( ( { id } ) => id === fieldId ) - ) - .filter( ( field ): field is Field< Item > => !! field ); -} diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index 95c43279655b05..067678754df2b1 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -69,14 +69,18 @@ function PanelDropdown< Item >( { popoverAnchor: HTMLElement | null; } & FormFieldProps< Item > ) { const childrenFields = useMemo( () => { - const isFieldObject = typeof field !== 'string'; - if ( isFieldObject && field.children ) { - return field.children; + if ( field.children ) { + return field.children.map( ( child ) => { + if ( typeof child === 'string' ) { + return { + id: child, + }; + } + return child; + } ); } - if ( isFieldObject && field.id ) { - return [ field.id ]; - } - return [ field ]; + // If not explicit children return the field id itself. + return [ { id: field.id } ]; }, [ field ] ); // Memoize popoverProps to avoid returning a new object every time. @@ -159,10 +163,7 @@ export default function FormPanelField< Item >( { }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); - const labelPosition = - typeof field !== 'string' && field.labelPosition - ? field.labelPosition - : 'side'; + const labelPosition = field.labelPosition ?? 'side'; // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. diff --git a/packages/dataviews/src/dataforms-layouts/regular/index.tsx b/packages/dataviews/src/dataforms-layouts/regular/index.tsx index 113408f8ee6abb..97693268ff78f8 100644 --- a/packages/dataviews/src/dataforms-layouts/regular/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/regular/index.tsx @@ -46,13 +46,17 @@ export default function FormRegularField< Item >( { }: FormFieldProps< Item > ) { const { getFieldDefinition } = useContext( DataFormContext ); const fieldDefinition = getFieldDefinition( field ); - const labelPosition = - typeof field !== 'string' && field.labelPosition - ? field.labelPosition - : 'top'; + const labelPosition = field.labelPosition ?? 'top'; const childrenFields = useMemo( () => { if ( typeof field !== 'string' && field.children ) { - return field.children; + return field.children.map( ( child ) => { + if ( typeof child === 'string' ) { + return { + id: child, + }; + } + return child; + } ); } return []; }, [ field ] ); diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 29eb2c2f7d7364..f0e4ebf0cd2f51 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -525,22 +525,20 @@ export interface SupportedLayouts { table?: Omit< ViewTable, 'type' >; } -export type FormField = - | string - | { - id: string; - label?: string; - layout?: 'regular' | 'panel'; - labelPosition?: 'side' | 'top'; - children?: FormField[]; - }; +export type FormField = { + id: string; + label?: string; + layout?: 'regular' | 'panel'; + labelPosition?: 'side' | 'top'; + children?: Array< FormField | string >; +}; /** * The form configuration. */ export type Form = { type?: 'regular' | 'panel'; - fields?: FormField[]; + fields?: Array< FormField | string >; }; export interface DataFormProps< Item > { From d698e141bb2db1f8717ec50fa72f32e16d68ac81 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Mon, 25 Nov 2024 17:39:24 +0100 Subject: [PATCH 38/44] fix template field registration --- packages/edit-site/src/components/post-edit/index.js | 3 ++- packages/editor/src/dataviews/store/private-actions.ts | 2 ++ 2 files changed, 4 insertions(+), 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 82a4686067e33a..5b78a8ef5d21a1 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -88,9 +88,10 @@ function PostEditForm( { postType, postId } ) { 'parent', 'comment_status', { + label: __( 'Template' ), + labelPosition: 'side', id: 'template', layout: 'regular', - labelPosition: 'side', }, ].filter( ( field ) => diff --git a/packages/editor/src/dataviews/store/private-actions.ts b/packages/editor/src/dataviews/store/private-actions.ts index 77ac131a8e2302..5cdb7ade8660d2 100644 --- a/packages/editor/src/dataviews/store/private-actions.ts +++ b/packages/editor/src/dataviews/store/private-actions.ts @@ -33,6 +33,7 @@ import { statusField, authorField, titleField, + templateField, } from '@wordpress/fields'; import duplicateTemplatePart from '../actions/duplicate-template-part'; @@ -169,6 +170,7 @@ export const registerPostTypeSchema = parentField, commentStatusField, passwordField, + templateField, ]; registry.batch( () => { From 69396c0d830532028929db3b07fe2582f8c618f7 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Mon, 25 Nov 2024 18:06:48 +0100 Subject: [PATCH 39/44] restore not necessary changes --- packages/block-library/src/image/block.json | 9 +-------- packages/block-library/src/post-content/block.json | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index 26835df9e856cd..16e31217476026 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -4,14 +4,7 @@ "name": "core/image", "title": "Image", "category": "media", - "usesContext": [ - "allowResize", - "imageCrop", - "fixedHeight", - "postId", - "postType", - "queryId" - ], + "usesContext": [ "allowResize", "imageCrop", "fixedHeight", "postId", "postType", "queryId" ], "description": "Insert an image to make a visual statement.", "keywords": [ "img", "photo", "picture" ], "textdomain": "default", diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json index e5d455b97a8a3d..ed9c47154b2f8e 100644 --- a/packages/block-library/src/post-content/block.json +++ b/packages/block-library/src/post-content/block.json @@ -69,4 +69,4 @@ }, "style": "wp-block-post-content", "editorStyle": "wp-block-post-content-editor" -} +} \ No newline at end of file From 704ebe593e604c6670ee74d20badb27439360bab Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Mon, 2 Dec 2024 15:44:19 +0100 Subject: [PATCH 40/44] wrap DataForm component --- .../src/components/post-edit/index.js | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index ce4eb9f6f04d22..47e61dbd6ec350 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -131,42 +131,40 @@ function PostEditForm( { postType, postId } ) { setMultiEdits( {} ); }, [ ids ] ); - return ( - - { ids.length === 1 && ( - - ) } - - - ); -} - -export function PostEdit( { postType, postId } ) { const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); const settings = usePatternSettings(); - // Wrap everything in a block editor provider. - // This ensures 'styles' that are needed for the previews are synced - // from the site editor store to the block editor store. + return ( - - { postId && ( - + + { ids.length === 1 && ( + ) } - { ! postId &&

{ __( 'Select a page to edit' ) }

} -
+ +
); } + +export function PostEdit( { postType, postId } ) { + return ( + + { postId && ( + + ) } + { ! postId &&

{ __( 'Select a page to edit' ) }

} +
+ ); +} From 7cf284378c54a2fdbf0c43b9a68ff085bc0b7b21 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Mon, 2 Dec 2024 15:45:09 +0100 Subject: [PATCH 41/44] not use hook --- .../src/fields/template/template-edit.tsx | 103 ++++++++---------- 1 file changed, 48 insertions(+), 55 deletions(-) diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index e323da91bd4bdd..4ced96acda6e56 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -26,21 +26,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; import { getItemTitle } from '../../actions/utils'; import type { BasePost } from '../../types'; - -function useTemplates( postType: string ) { - return useSelect( - ( select ) => - select( coreStore ).getEntityRecords< WpTemplate >( - 'postType', - 'wp_template', - { - per_page: -1, - post_type: postType, - } - ) ?? [], - [ postType ] - ); -} +import { unlock } from '../../lock-unlock'; export function useAllowSwitchingTemplates( { postType, @@ -51,32 +37,14 @@ export function useAllowSwitchingTemplates( { } ): boolean { return useSelect( ( select ) => { - const { canUser, getEntityRecord, getEntityRecords } = - select( coreStore ); - const siteSettings = canUser( 'read', { - kind: 'root', - name: 'site', - } ) - ? // @ts-expect-error getEntityRecord is not typed correctly. - getEntityRecord< { - page_for_posts: number; - page_on_front: number; - } >( 'root', 'site' ) - : undefined; - const templates = getEntityRecords< WpTemplate >( - 'postType', - 'wp_template', - { - per_page: -1, - } + const { getHomePage, getPostsPageId } = unlock( + select( coreStore ) ); - const isPostsPage = +postId === siteSettings?.page_for_posts; - // If current page is set front page or posts page, we also need - // to check if the current theme has a template for it. If not + + const isPostsPage = getPostsPageId() === +postId; const isFrontPage = - postType === 'page' && - +postId === siteSettings?.page_on_front && - templates?.some( ( { slug } ) => slug === 'front-page' ); + postType === 'page' && getHomePage()?.postId === +postId; + return ! isPostsPage && ! isFrontPage; }, [ postId, postType ] @@ -90,25 +58,50 @@ export const TemplateEdit = ( { }: DataFormControlProps< BasePost > ) => { const { id } = field; const postType = data.type; + const postId = + typeof data.id === 'number' ? data.id : parseInt( data.id, 10 ); const slug = data.slug; - const allowSwitchingTemplate = useAllowSwitchingTemplates( { - postType: data.type, - postId: typeof data.id === 'number' ? data.id : parseInt( data.id, 10 ), - } ); - const templates = useTemplates( data.type ); + const allowSwitchingTemplate = useSelect( + ( select ) => { + const { getHomePage, getPostsPageId } = unlock( + select( coreStore ) + ); - const availableTemplates = useMemo( - () => - allowSwitchingTemplate - ? templates.filter( - ( template ) => - template.is_custom && - template.slug !== data.template && - !! template.content.raw // Skip empty templates. - ) - : [], - [ allowSwitchingTemplate, templates, data.template ] + const isPostsPage = getPostsPageId() === +postId; + const isFrontPage = + postType === 'page' && getHomePage()?.postId === +postId; + + return ! isPostsPage && ! isFrontPage; + }, + [ postId, postType ] + ); + + const { availableTemplates, templates } = useSelect( + ( select ) => { + const allTemplates = + select( coreStore ).getEntityRecords< WpTemplate >( + 'postType', + 'wp_template', + { + per_page: -1, + post_type: postType, + } + ) ?? []; + + return { + templates: allTemplates, + availableTemplates: allowSwitchingTemplate + ? allTemplates.filter( + ( template ) => + template.is_custom && + template.slug !== data.template && + !! template.content.raw // Skip empty templates. + ) + : [], + }; + }, + [ allowSwitchingTemplate, data.template, postType ] ); const templatesAsPatterns = useMemo( From cbb8bda871392548d2595f40568cc95eaf6854e4 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Tue, 3 Dec 2024 10:35:03 +0100 Subject: [PATCH 42/44] improve dependency --- .../src/components/post-edit/index.js | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index 47e61dbd6ec350..e8f1dc75b084ab 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -136,20 +136,38 @@ function PostEditForm( { postType, postId } ) { ); const settings = usePatternSettings(); + /** + * The template field depends on the block editor settings. + * This is a workaround to ensure that the block editor settings are available. + */ + const fieldsWithDependency = useMemo( () => { + return fields.map( ( field ) => { + if ( field.id === 'template' ) { + return { + ...field, + Edit: ( data ) => ( + + + + ), + }; + } + return field; + } ); + }, [ fields, settings ] ); + return ( - - - { ids.length === 1 && ( - - ) } - - - + + { ids.length === 1 && ( + + ) } + + ); } From f891475bd3f69bcb66b1fdadff1e7173fdf5d424 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Tue, 3 Dec 2024 10:37:08 +0100 Subject: [PATCH 43/44] improve code style --- .../src/fields/template/template-edit.tsx | 50 ++++--------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index 4ced96acda6e56..c17364568a4578 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -28,29 +28,6 @@ import { getItemTitle } from '../../actions/utils'; import type { BasePost } from '../../types'; import { unlock } from '../../lock-unlock'; -export function useAllowSwitchingTemplates( { - postType, - postId, -}: { - postType: string; - postId: number; -} ): boolean { - return useSelect( - ( select ) => { - const { getHomePage, getPostsPageId } = unlock( - select( coreStore ) - ); - - const isPostsPage = getPostsPageId() === +postId; - const isFrontPage = - postType === 'page' && getHomePage()?.postId === +postId; - - return ! isPostsPage && ! isFrontPage; - }, - [ postId, postType ] - ); -} - export const TemplateEdit = ( { data, field, @@ -62,21 +39,6 @@ export const TemplateEdit = ( { typeof data.id === 'number' ? data.id : parseInt( data.id, 10 ); const slug = data.slug; - const allowSwitchingTemplate = useSelect( - ( select ) => { - const { getHomePage, getPostsPageId } = unlock( - select( coreStore ) - ); - - const isPostsPage = getPostsPageId() === +postId; - const isFrontPage = - postType === 'page' && getHomePage()?.postId === +postId; - - return ! isPostsPage && ! isFrontPage; - }, - [ postId, postType ] - ); - const { availableTemplates, templates } = useSelect( ( select ) => { const allTemplates = @@ -89,6 +51,16 @@ export const TemplateEdit = ( { } ) ?? []; + const { getHomePage, getPostsPageId } = unlock( + select( coreStore ) + ); + + const isPostsPage = getPostsPageId() === +postId; + const isFrontPage = + postType === 'page' && getHomePage()?.postId === +postId; + + const allowSwitchingTemplate = ! isPostsPage && ! isFrontPage; + return { templates: allTemplates, availableTemplates: allowSwitchingTemplate @@ -101,7 +73,7 @@ export const TemplateEdit = ( { : [], }; }, - [ allowSwitchingTemplate, data.template, postType ] + [ data.template, postId, postType ] ); const templatesAsPatterns = useMemo( From 32e1b31f9af398a2069aa8922ba3bd6df8a2e5ec Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Tue, 3 Dec 2024 11:38:40 +0100 Subject: [PATCH 44/44] add issue --- packages/edit-site/src/components/post-edit/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index e8f1dc75b084ab..9a99a987089c1a 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -139,6 +139,7 @@ function PostEditForm( { postType, postId } ) { /** * The template field depends on the block editor settings. * This is a workaround to ensure that the block editor settings are available. + * For more information, see: https://github.com/WordPress/gutenberg/issues/67521 */ const fieldsWithDependency = useMemo( () => { return fields.map( ( field ) => {