Skip to content

Dataforms: Add object configuration support for Edit property with prefix/suffix options#71582

Merged
jorgefilipecosta merged 14 commits intotrunkfrom
add/field-config-object
Sep 16, 2025
Merged

Dataforms: Add object configuration support for Edit property with prefix/suffix options#71582
jorgefilipecosta merged 14 commits intotrunkfrom
add/field-config-object

Conversation

@jorgefilipecosta
Copy link
Member

This PR extends the DataForm Edit property to support object configuration, enabling advanced control options like configuring textarea rows, text input prefixes, and suffixes. This provides a more flexible and declarative API for customizing form controls.

Fixes: #71560

Changes Made

Core Infrastructure:

  • New EditConfig type: Added support for object-based Edit configuration with type, rows, prefix, suffix, and extensible properties
  • Enhanced control resolution: Extended getControl() function to handle object configurations via new createConfiguredControl() HOC pattern
  • Type safety: Full TypeScript support with proper type guards and validation

Control Enhancements:

  • Textarea control: Added rows configuration support for customizable textarea height
  • Text control: Added prefix and suffix support for input decorations
  • Backward compatibility: All existing string/component Edit values continue to work unchanged

Documentation & Examples:

  • Comprehensive test cases: Added example fields demonstrating textarea with custom rows, text with prefix, text with suffix, and text with both prefix and suffix
  • Type definitions: Well-documented interface with clear usage examples

Usage Examples

const fields = [
  // Textarea with custom rows
  {
    id: 'longDescription',
    type: 'text',
    Edit: {
      type: 'textarea',
      rows: 5
    }
  },
  
  // Text with prefix
  {
    id: 'price',
    type: 'text',
    Edit: {
      type: 'text',
      prefix: <DollarIcon />
    }
  },
  
  // Text with suffix
  {
    id: 'percentage',
    type: 'text',
    Edit: {
      type: 'text',
      suffix: <span>%</span>
    }
  },
  
  // Text with both prefix and suffix
  {
    id: 'priceWithCurrency',
    type: 'text',
    Edit: {
      type: 'text',
      prefix: <DollarIcon />,
      suffix: <span>USD</span>
    }
  }
]

Technical Details

  • HOC Pattern: Uses Higher-Order Component pattern to wrap base controls with configuration
  • Type Guards: Includes isEditConfig() type guard for runtime type checking
  • Extensible Design: The EditConfig type supports additional properties via index signature
  • Performance: No impact on existing functionality, configurations only applied when needed

Testing

Go to http://localhost:50240/?path=/story/dataviews-dataform--default, and verify the fields Long Description, Price with Prefix, Percentage with Suffix, Price with Prefix and Suffix, work as expected.

@github-actions
Copy link

github-actions bot commented Sep 10, 2025

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: jorgefilipecosta <[email protected]>
Co-authored-by: andrewserong <[email protected]>
Co-authored-by: dinhtungdu <[email protected]>
Co-authored-by: oandregal <[email protected]>
Co-authored-by: mirka <[email protected]>

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

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

The story's working nicely for me:

image

Just left a comment about the types, and whether we might want to make them more granular so that they're aware of the control being used and which props are available for that particular control. Also in the discussion on the linked issue, it sounds like folks were leaning toward using a control key instead of type?

Otherwise, I'm liking the direction of this, feels like a good path forward to me!

Comment on lines 162 to 187
/**
* Edit configuration object for advanced control options.
*/
export type EditConfig = {
/**
* The type of control to use.
*/
type: string;
/**
* Number of rows for textarea controls.
*/
rows?: number;
/**
* Prefix component for text controls.
*/
prefix?: React.ComponentType | React.ReactElement;
/**
* Suffix component for text controls.
*/
suffix?: React.ReactElement;
/**
* Additional configuration properties.
*/
[ key: string ]: any;
};

Copy link
Contributor

Choose a reason for hiding this comment

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

For the types, should we go more granular, as suggested in #71560 (comment)? For example, prefix shouldn't be allowed on the textarea control, and rows should only be allowed on the textarea control.

@t-hamano t-hamano added the [Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond label Sep 11, 2025
Comment on lines 182 to 185
/**
* Additional configuration properties.
*/
[ key: string ]: any;
Copy link
Member

Choose a reason for hiding this comment

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

Based on the conversation in the linked issue, I don’t think we want this, right? I believe we want to carefully consider adding new properties to avoid competing with the existing Field API.

type: 'text',
Edit: 'textarea',
},
{
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for thinking of updating the examples. Can we move this to the field-types story instead? We can update the field type text to include examples with all of this (as well as any other field type affected).

The textarea doesn't have an equivalent field type, but we can add it to the field type text as well.

@github-actions
Copy link

github-actions bot commented Sep 11, 2025

Flaky tests detected in 079f1c5.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/17760639707
📝 Reported issues:

Comment on lines 12 to 13
prefix,
suffix,
Copy link
Member

Choose a reason for hiding this comment

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

The render has a config object that includes extra information for certain fields (e.g., when a field is marked as "media" it gets the sizes from the layout). Can we follow suit here as well?

createConfiguredControl would pass the proper config object to the underlying control depending on its type (all the EditConfig properties but type).

Comment on lines 191 to 193
export type EditConfigGeneric = {
type: Exclude< FieldType, 'text' | 'textarea' >;
};
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this one?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it makes sense to have the exclusion and be speciic so IDE show the correct values when we are over EditConfigGeneric.

* Edit configuration for textarea controls.
*/
export type EditConfigTextarea = {
type: 'textarea';
Copy link
Member

Choose a reason for hiding this comment

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

What others suggested: just rename from type to control to avoid overusing type.

Comment on lines 25 to 26
icon: prefix,
suffix,
Copy link
Member

Choose a reason for hiding this comment

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

Can we normalize the ValidatedText component (and the EditConfig type) so it exposes prefix and suffix props and both are the same type?

Both should work the same (receive an icon component or an element). I've noticed prefix receives an icon, but suffix receives an element. I'm leaning to allow an element through any prefix/suffix, but I'd like to gather @mirka thoughts on this.

Copy link
Member

Choose a reason for hiding this comment

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

The advantage of being an element is that it can be interactive. For example, a consumer could pass a prefix/suffix that opens a modal on click to do something, ala units control. This is something that could help address some use cases, but I wonder if it's a pattern we want to promote/support.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi @oandregal the code was updated now we have simple suffix and prefix props.

Copy link
Member

@mirka mirka Sep 11, 2025

Choose a reason for hiding this comment

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

I'm leaning to allow an element through any prefix/suffix

👍

I believe you've noticed how we want different paddings depending on the slot content — icons and controls look good with 8px padding, plain text with 12px, and then sometimes you could want a full-bleed background color in the slot, which will call for 0px padding (not that common though).

So there will be a few ways to deal with that here. Either:

  • Bake that into the DataForm as another configuration (e.g. slotType: 'control').
  • Recommend consumers to use the wrapper components.
  • Document the suggested padding values in the DataForm documentation and expect the consumers to apply those values manually in CSS.

In either case I think DataForm itself now needs to have sufficient documentation on how to apply standard paddings according to the slot content. If that sounds like too much, I think it's fair to draw a line and say we're not going to support that level of customization out of the box 😅

Copy link
Member

Choose a reason for hiding this comment

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

And as a heads up, separately from this InputControl case, similar responsibility boundary issues are likely to pop up in custom rendered select controls and combobox controls, where there will be a lot of possible customization options at the component level. I know we have concerns about back compat and feature bloat in the DataForm system, so we'll definitely need to draw a hard line somewhere in exactly how much the DataForm is going to support in its own API.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good points @mirka, I guess for this initial version it is better to keep it simple and allow prefix and suffix but don't expose the different padding configurations, and leave it to the consumers to use InputControl...Wrapper explicitly as it is the most flexible and simple approach.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the context, Lena!

Agreed to using the slots without further Edit config. @jorgefilipecosta when you add the README for this Edit config, could you add a note about this? I think just what the input component uses would be fine (copy/paste from the input's stories for prefix and suffix):

By default, the prefix/suffix is aligned with the edge of the input border, with no padding. If you want to apply standard padding in accordance with the size variant, wrap the element in the provided component.

@mirka, I don't see that we explain the differences between the variants (icon vs control), and, as far as I read, it doesn't make any difference. Is that correct?

Copy link
Member

Choose a reason for hiding this comment

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

I don't see that we explain the differences between the variants (icon vs control), and, as far as I read, it doesn't make any difference. Is that correct?

We do explain variants in the story and in the prop docs, if that's what you're referring to.

And yes the spacings for icon and control are the same. I just separated the variant names for ergonomics. (In v2, they may be merged into a single variant with a more generic name like minimal.)

@jorgefilipecosta
Copy link
Member Author

Hi @oandregal thank you for the reviews, I think I addressed everything.

Comment on lines 32 to 46
const DollarPrefix = () => (
<InputControlPrefixWrapper variant="control">
<span>$</span>
</InputControlPrefixWrapper>
);
const PercentSuffix = () => (
<InputControlSuffixWrapper variant="control">
<span>%</span>
</InputControlSuffixWrapper>
);
const USDSuffix = () => (
<InputControlSuffixWrapper variant="control">
<span>USD</span>
</InputControlSuffixWrapper>
);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: These are text content, not controls, so they would use the "default" variant. Otherwise they will look a bit off.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, it was fixed ☺️

<span>$</span>
</InputControlPrefixWrapper>
);
const PercentSuffix = () => (
Copy link
Member

Choose a reason for hiding this comment

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

Can we make one of these use an icon to demonstrate the usage of the icon variant?

Copy link
Member

@oandregal oandregal left a comment

Choose a reason for hiding this comment

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

I'd prefer if we move the story examples to the FieldTypes story.

Other than that, this is working great. Thanks, Jorge!

@jorgefilipecosta jorgefilipecosta force-pushed the add/field-config-object branch 2 times, most recently from b9d80d1 to a9620ed Compare September 15, 2025 16:28
@oandregal
Copy link
Member

There's a CI failure because every DataViews PR needs a changelog entry, with a link to the PR.

);
};

export const TextWithPrefixSuffix = ( {
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this story? The new fields are going to be picked up by the text fields story. I'd prefer to keep the stories organized by field type.

Copy link
Member Author

Choose a reason for hiding this comment

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

It was removed 👍

@jorgefilipecosta jorgefilipecosta merged commit 9e76de1 into trunk Sep 16, 2025
70 checks passed
@jorgefilipecosta jorgefilipecosta deleted the add/field-config-object branch September 16, 2025 09:44
@github-actions github-actions bot added this to the Gutenberg 21.7 milestone Sep 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow consumers to configure the bundled Edit control

6 participants