Skip to content

Commit

Permalink
Merge branch 'update/dataform_combined_fields_api' of github.com:Word…
Browse files Browse the repository at this point in the history
…Press/gutenberg into add/template-form
  • Loading branch information
gigitux committed Oct 29, 2024
2 parents 648c458 + a707275 commit 111e9f1
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 56 deletions.
41 changes: 41 additions & 0 deletions packages/dataviews/src/components/dataform-context/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* WordPress dependencies
*/
import { createContext, useCallback } 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 getFieldDefinition = useCallback(
( field: string ) => {
return fields.find(
( fieldDefinition ) => fieldDefinition.id === field
);
},
[ fields ]
);

return (
<DataFormContext.Provider value={ { getFieldDefinition } }>
{ children }
</DataFormContext.Provider>
);
}

export default DataFormContext;
28 changes: 24 additions & 4 deletions packages/dataviews/src/components/dataform/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
/**
* 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';
import { DataFormLayout } from '../../dataforms-layouts/data-form-layout';

export default function DataForm< Item >( {
form,
...props
}: DataFormProps< Item > ) {
const layout = getFormLayout( form.type ?? 'regular' );
if ( ! layout ) {
const normalizedFields = useMemo(
() => normalizeFields( props.fields ),
[ props.fields ]
);

if ( ! form.fields ) {
return null;
}

return <layout.component form={ form } { ...props } />;
return (
<DataFormProvider fields={ normalizedFields }>
<DataFormLayout
defaultLayout={ form.type }
data={ props.data }
fields={ form.fields }
onChange={ props.onChange }
/>
</DataFormProvider>
);
}
67 changes: 41 additions & 26 deletions packages/dataviews/src/components/dataform/stories/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -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,
Expand All @@ -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 (
<DataForm
Expand All @@ -127,33 +147,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 (
<DataForm
<DataForm< SamplePost >
data={ post }
fields={ fields }
form={ {
Expand All @@ -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' ],
},
},
};
61 changes: 61 additions & 0 deletions packages/dataviews/src/dataforms-layouts/data-form-layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<VStack spacing={ 2 }>
{ 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 (
<FieldLayout
key={ typeof field === 'string' ? field : field.id }
data={ data }
field={ field }
onChange={ onChange }
/>
);
} ) }
</VStack>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
24 changes: 22 additions & 2 deletions packages/dataviews/src/dataforms-layouts/index.tsx
Original file line number Diff line number Diff line change
@@ -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 = [
{
Expand All @@ -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 );
}
47 changes: 47 additions & 0 deletions packages/dataviews/src/dataforms-layouts/inline/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<HStack className="dataforms-layouts-panel__field">
<div className="dataforms-layouts-panel__field-label">
{ fieldDefinition.label }
</div>
<div>
<fieldDefinition.Edit
key={ fieldDefinition.id }
data={ data }
field={ fieldDefinition }
onChange={ onChange }
hideLabelFromVision
/>
</div>
</HStack>
);
}
Loading

0 comments on commit 111e9f1

Please sign in to comment.