Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataForm - Add combined fields support #65399

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from

Conversation

louwie17
Copy link
Contributor

@louwie17 louwie17 commented Sep 17, 2024

What?

This PR adds support for combinedFields layout support to the DataForm panel view. This follows a similar configuration as combinedFields in DataViews.

cc: @youknowriad, @oandregal

Why?

This is needed to allow fields multiple fields to be rendered as part of a single row, similar to how currently the the post status field works within the site editor. This will be needed for the DataForms as well.

Part of #64519 and an initial step to implementing the Password field.

How?

This adds a layout option to the form config that accepts a combinedFields array. A combined field can contain children and direction, it also accepts a custom render function to display the data. This follows the same format as introduced here for DataViews.

Open questions:

  • In the design the field label is different from the top label within the modal, do we provide a separate config for this?
  • How would this look in the regular view?

An alternative to consider, is not to alter this through the layout, but provide a layout type as part of the field type definition that supports a direction and children.

{
  type: 'layout',
  children: [ 'title', 'status' ],
  direction: 'vertical'
}

Testing Instructions

  1. Run storybook: npm run storybook:dev
  2. Go to the DataViews > DataForm > Combined Fields story
  3. See the combined field label Status & Visibility and the two fields underneath ( status & password )
  4. Now change the form type to panel
  5. Select the Status & Visibility field, it should render a status and password field.
  6. Edits should be correctly reflected within status & password fields across the form types.

Testing Instructions for Keyboard

Screenshots or screencast

showcase-combined-fields.mp4

Copy link

github-actions bot commented Sep 17, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: louwie17 <[email protected]>
Co-authored-by: youknowriad <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

label: 'Status & Visibility',
children: [ 'status', 'password' ],
direction: 'vertical',
render: ( { item } ) => item.status,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this function about?

The thing that I'm wondering personally is whether for "forms" these are combined fields or more things like "groups"...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having more example of how this translates in actual forms and panels would help make the right abstraction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this function about?

Its suppose to be the same as the render function of the field config, but its acting more like the getValue function.

The thing that I'm wondering personally is whether for "forms" these are combined fields or more things like "groups"...
I think having more example of how this translates in actual forms and panels would help make the right abstraction.

Groups would probably fit this use case better, the main example is the status field that includes the status radio control, posts schedule, the password field and also the sticky checkbox:

password-conditional.mp4

We could also create a single status field that includes all of this logic, we did have to remove the radio dependency here:

{
label: __( 'Status' ),
id: 'status',
type: 'text',
elements: STATUSES,
render: PostStatusField,
Edit: 'radio',
enableSorting: false,
filterBy: {
operators: [ OPERATOR_IS_ANY ],
},
},

This is mostly a use case for the panel view. For the regular form one could render multiple regular data forms to create the grouping. Although there would be some additional use cases like the column use case for displaying two fields beside each other ( we have a similar use case in the product form for regular and sale price ):
Screenshot 2024-09-18 at 10 35 38 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing that I'm wondering personally is whether for "forms" these are combined fields or more things like "groups"...

Would you envision "groups" to be like field types that accept children, or act in a similar manner as the combinedField API?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its suppose to be the same as the render function of the field config, but its acting more like the getValue function.

Do you think we can guess this from the "fields" or is the "render" function mandatory here? I'm also curious how the "combined fields" or "groups" feature translate in regular forms. What's the output there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can guess this from the "fields" or is the "render" function mandatory here?

In this specific example it only shows the status value in the panel view so the render or a getValue would be mandatory.
If we wanted, we could guess it from the fields and show a comma delimited list of the nested field values.
The panel view is an exception here as it separates the edit view from the visual.

I'm also curious how the "combined fields" or "groups" feature translate in regular forms. What's the output there?

In regular forms the main difference would be a title for the grouped fields and the option to display them in a column layout. Similar to the JSON Forms layout examples: https://jsonforms.io/examples/layouts/#group

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In regular forms the main difference would be a title for the grouped fields and the option to display them in a column layout. Similar to the JSON Forms layout examples: https://jsonforms.io/examples/layouts/#group

I think that makes sense. Is it already working in this PR? I think my main concern here is to avoid special cases for layouts (panel or default)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't update the default data form yet, let me update it to support that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated it to support the regular data form type as well as part of this commit: 42131f3

Copy link

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Type-related labels to choose from: [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Technical Prototype, [Type] Performance, [Type] Project Management, [Type] Regression, [Type] Security, [Type] WP Core Ticket, Backport from WordPress Core.
  • Labels found: .

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

Copy link
Contributor

@gigitux gigitux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Overall, it looks good. I left a few minor comments!

Comment on lines +11 to +29
export function getVisibleFields(
fields: Field< any >[],
formFields: string[] = [],
combinedFields?: CombinedFormField< any >[]
): Field< any >[] {
const visibleFields: Array<
Field< any > | NormalizedCombinedFormField< any >
> = [ ...fields ];
if ( combinedFields ) {
visibleFields.push(
...normalizeCombinedFields( combinedFields, fields )
);
}
return formFields
.map( ( fieldId ) =>
visibleFields.find( ( { id } ) => id === fieldId )
)
.filter( ( field ): field is Field< any > => !! field );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest maintaining consistency with the current typing by using Item instead of any.

Suggested change
export function getVisibleFields(
fields: Field< any >[],
formFields: string[] = [],
combinedFields?: CombinedFormField< any >[]
): Field< any >[] {
const visibleFields: Array<
Field< any > | NormalizedCombinedFormField< any >
> = [ ...fields ];
if ( combinedFields ) {
visibleFields.push(
...normalizeCombinedFields( combinedFields, fields )
);
}
return formFields
.map( ( fieldId ) =>
visibleFields.find( ( { id } ) => id === fieldId )
)
.filter( ( field ): field is Field< any > => !! field );
}
export function getVisibleFields<Item>(
fields: Field< Item >[],
formFields: string[] = [],
combinedFields?: CombinedFormField< Item >[]
): Field< Item >[] {
const visibleFields: Array<
Field< Item > | NormalizedCombinedFormField< Item >
> = [ ...fields ];
if ( combinedFields ) {
visibleFields.push(
...normalizeCombinedFields( combinedFields, fields )
);
}
return formFields
.map( ( fieldId ) =>
visibleFields.find( ( { id } ) => id === fieldId )
)
.filter( ( field ): field is Field< Item > => !! field );
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a minor, but I noticed that the panel to customize props doesn't appear on the new story:

image

const children = visibleChildren.map( ( child, index ) => {
return (
<div className="dataforms-combined-edit__field" key={ child.id }>
{ index !== 0 && hideLabelFromVision && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check necessary?

.map( ( fieldId ) =>
fields.find( ( { id } ) => id === fieldId )
)
.filter( ( field ): field is Field< any > => !! field )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter( ( field ): field is Field< any > => !! field )
.filter( ( field ): field is Field< Item > => !! field )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants