diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index 490f6ad42c608c..14dbd6cb635a35 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -19,6 +19,7 @@ - DataForm: support validation in select control [#71665](https://github.com/WordPress/gutenberg/pull/71665) - DataForm: support validation in toggleGroup control. ([#71666](https://github.com/WordPress/gutenberg/pull/71666)) - DataForm: Add object configuration support for Edit property with some options. ([#71582](https://github.com/WordPress/gutenberg/pull/71582)) +- DataForm: Add summary field support for composed fields. ([#71614](https://github.com/WordPress/gutenberg/pull/71614)) ### Bug Fixes diff --git a/packages/dataviews/src/components/dataform/stories/index.story.tsx b/packages/dataviews/src/components/dataform/stories/index.story.tsx index 141488c0777309..4f687ed10bb984 100644 --- a/packages/dataviews/src/components/dataform/stories/index.story.tsx +++ b/packages/dataviews/src/components/dataform/stories/index.story.tsx @@ -41,7 +41,14 @@ type SamplePost = { address1?: string; address2?: string; city?: string; + comment_status?: string; + ping_status?: boolean; longDescription?: string; + origin?: string; + destination?: string; + flight_status?: string; + gate?: string; + seat?: string; }; const fields: Field< SamplePost >[] = [ @@ -184,6 +191,71 @@ const fields: Field< SamplePost >[] = [ rows: 5, }, }, + { + id: 'comment_status', + label: 'Comment Status', + type: 'text', + Edit: 'radio', + elements: [ + { value: 'open', label: 'Allow comments' }, + { value: 'closed', label: 'Comments closed' }, + ], + }, + { + id: 'ping_status', + label: 'Allow Pings/Trackbacks', + type: 'boolean', + }, + { + id: 'discussion', + label: 'Discussion', + type: 'text', + render: ( { item } ) => { + const commentLabel = + item.comment_status === 'open' + ? 'Allow comments' + : 'Comments closed'; + const pingLabel = item.ping_status + ? 'Pings enabled' + : 'Pings disabled'; + return ( + + { commentLabel }, { pingLabel } + + ); + }, + }, + { + id: 'origin', + label: 'Origin', + type: 'text', + }, + { + id: 'destination', + label: 'Destination', + type: 'text', + }, + { + id: 'flight_status', + label: 'Flight Status', + type: 'text', + Edit: 'radio', + elements: [ + { value: 'on-time', label: 'On Time' }, + { value: 'delayed', label: 'Delayed' }, + { value: 'cancelled', label: 'Cancelled' }, + ], + }, + { + id: 'gate', + label: 'Gate', + type: 'text', + }, + { + id: 'seat', + label: 'Seat', + type: 'text', + }, ]; const LayoutRegularComponent = ( { @@ -319,6 +391,13 @@ const LayoutPanelComponent = ( { address1: '123 Main St', address2: 'Apt 4B', city: 'New York', + comment_status: 'open', + ping_status: true, + origin: 'New York (JFK)', + destination: 'Los Angeles (LAX)', + flight_status: 'on-time', + gate: 'A12', + seat: '14F', } ); const form: Form = useMemo( () => { @@ -340,11 +419,34 @@ const LayoutPanelComponent = ( { 'filesize', 'dimensions', 'tags', + { + id: 'discussion', + label: 'Discussion', + children: [ 'comment_status', 'ping_status' ], + summary: 'discussion', + }, { id: 'address1', label: 'Combined Address', children: [ 'address1', 'address2', 'city' ], }, + { + id: 'flight_info', + label: 'Flight Information', + children: [ + 'origin', + 'destination', + 'flight_status', + 'gate', + ], + summary: [ 'origin', 'destination', 'flight_status' ], + }, + { + id: 'passenger_details', + label: 'Passenger Details', + children: [ 'author', 'seat' ], + summary: [ 'author', 'seat' ], + }, ], }; }, [ labelPosition, openAs ] ); diff --git a/packages/dataviews/src/dataforms-layouts/panel/dropdown.tsx b/packages/dataviews/src/dataforms-layouts/panel/dropdown.tsx index 1367cdb93105de..6d070f200d47b4 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/dropdown.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/dropdown.tsx @@ -9,7 +9,7 @@ import { Dropdown, Button, } from '@wordpress/components'; -import { sprintf, __, _x } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import { useMemo } from '@wordpress/element'; import { closeSmall } from '@wordpress/icons'; @@ -20,6 +20,7 @@ import type { Form, FormField, NormalizedField } from '../../types'; import { DataFormLayout } from '../data-form-layout'; import { isCombinedField } from '../is-combined-field'; import { DEFAULT_LAYOUT } from '../../normalize-form-fields'; +import SummaryButton from './summary-button'; function DropdownHeader( { title, @@ -55,6 +56,7 @@ function DropdownHeader( { function PanelDropdown< Item >( { fieldDefinition, + summaryFields, popoverAnchor, labelPosition = 'side', data, @@ -62,6 +64,7 @@ function PanelDropdown< Item >( { field, }: { fieldDefinition: NormalizedField< Item >; + summaryFields: NormalizedField< Item >[]; popoverAnchor: HTMLElement | null; labelPosition: 'side' | 'top' | 'none'; data: Item; @@ -107,29 +110,15 @@ function PanelDropdown< Item >( { tooltipPosition: 'middle left', } } renderToggle={ ( { isOpen, onToggle } ) => ( - + onClick={ onToggle } + aria-expanded={ isOpen } + /> ) } renderContent={ ( { onClose } ) => ( <> diff --git a/packages/dataviews/src/dataforms-layouts/panel/index.tsx b/packages/dataviews/src/dataforms-layouts/panel/index.tsx index e6a27570bf8b1f..1889c60429d630 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/index.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/index.tsx @@ -32,28 +32,49 @@ export default function FormPanelField< Item >( { onChange, }: FieldLayoutProps< Item > ) { const { fields } = useContext( DataFormContext ); - const fieldDefinition = fields.find( ( _field ) => { - // Default to the first simple child if it is a combined field. - if ( isCombinedField( field ) ) { - const simpleChildren = field.children.filter( - ( child ): child is string | SimpleFormField => - typeof child === 'string' || ! isCombinedField( child ) + const getSummaryFields = () => { + if ( ! isCombinedField( field ) ) { + const fieldDef = fields.find( + ( _field ) => _field.id === field.id ); + return fieldDef ? [ fieldDef ] : []; + } - if ( simpleChildren.length === 0 ) { - return false; - } + // Use summary field(s) if specified for combined fields + if ( field.summary ) { + const summaryIds = Array.isArray( field.summary ) + ? field.summary + : [ field.summary ]; + return summaryIds + .map( ( summaryId ) => + fields.find( ( _field ) => _field.id === summaryId ) + ) + .filter( ( _field ) => _field !== undefined ); + } - const firstChildFieldId = - typeof simpleChildren[ 0 ] === 'string' - ? simpleChildren[ 0 ] - : simpleChildren[ 0 ].id; + // Default to the first simple child + const simpleChildren = field.children.filter( + ( child ): child is string | SimpleFormField => + typeof child === 'string' || ! isCombinedField( child ) + ); - return _field.id === firstChildFieldId; + if ( simpleChildren.length === 0 ) { + return []; } - return _field.id === field.id; - } ); + const firstChildFieldId = + typeof simpleChildren[ 0 ] === 'string' + ? simpleChildren[ 0 ] + : simpleChildren[ 0 ].id; + + const fieldDef = fields.find( + ( _field ) => _field.id === firstChildFieldId + ); + return fieldDef ? [ fieldDef ] : []; + }; + + const summaryFields = getSummaryFields(); + const fieldDefinition = summaryFields[ 0 ]; // For backward compatibility // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. @@ -84,6 +105,7 @@ export default function FormPanelField< Item >( { ( { field={ field } popoverAnchor={ popoverAnchor } fieldDefinition={ fieldDefinition } + summaryFields={ summaryFields } data={ data } onChange={ onChange } labelPosition={ labelPosition } diff --git a/packages/dataviews/src/dataforms-layouts/panel/modal.tsx b/packages/dataviews/src/dataforms-layouts/panel/modal.tsx index d0dfe14fb99806..6e6cc2ca6e2474 100644 --- a/packages/dataviews/src/dataforms-layouts/panel/modal.tsx +++ b/packages/dataviews/src/dataforms-layouts/panel/modal.tsx @@ -7,7 +7,7 @@ import { Button, Modal, } from '@wordpress/components'; -import { __, sprintf, _x } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import { useState, useMemo } from '@wordpress/element'; /** @@ -17,6 +17,7 @@ import type { Form, FormField, NormalizedField } from '../../types'; import { DataFormLayout } from '../data-form-layout'; import { isCombinedField } from '../is-combined-field'; import { DEFAULT_LAYOUT } from '../../normalize-form-fields'; +import SummaryButton from './summary-button'; function ModalContent< Item >( { data, @@ -96,12 +97,14 @@ function ModalContent< Item >( { function PanelModal< Item >( { fieldDefinition, + summaryFields, labelPosition, data, onChange, field, }: { fieldDefinition: NormalizedField< Item >; + summaryFields: NormalizedField< Item >[]; labelPosition: 'side' | 'top' | 'none'; data: Item; onChange: ( value: any ) => void; @@ -126,29 +129,15 @@ function PanelModal< Item >( { return ( <> - + onClick={ () => setIsOpen( true ) } + aria-expanded={ isOpen } + /> { isOpen && ( ( { + summaryFields, + data, + labelPosition, + fieldLabel, + disabled, + onClick, + 'aria-expanded': ariaExpanded, +}: { + summaryFields: NormalizedField< Item >[]; + data: Item; + labelPosition: 'side' | 'top' | 'none'; + fieldLabel?: string; + disabled?: boolean; + onClick: () => void; + 'aria-expanded'?: boolean; +} ) { + return ( + + ); +} + +export default SummaryButton; diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 6f61fd41326cbf..8ea910db252f97 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -847,6 +847,7 @@ export type CombinedFormField = { description?: string; layout?: Layout; children: Array< FormField | string >; + summary?: string | string[]; }; export type FormField = SimpleFormField | CombinedFormField;