Skip to content

Conversation

@oandregal
Copy link
Member

@oandregal oandregal commented Nov 5, 2025

Part of #73154

What?

This PR explores introducing formats to the Field API, leveraging the existing WordPress format for dates. A format defines how a field is visualized in a few places: render, filters, edit.

Screen.Recording.2025-11-05.at.11.53.44.mov

This PR is in draft state and only implements a format for the date field type.

Why?

Data can be displayed in multiple formats depending on the situation. While field authors can already control how a field is displayed by using the render and Edit functions, the current approach has limits:

  • We want the Field API to be serializable, and fields with render and Edit components are not.
  • Formatting data in different ways is a basic need, specially for locale-sensible types such as date or number.
  • Field authors cannot currently control how a filter value is displayed.

Related conversations and work:

Questions

How?

  • Introduce a format prop to the Field API.
  • Field normalization: if a date field doesn't provide a format, the WordPress default is used.
  • The default render provided by the field type definition uses the format to display the date.
  • The filter summary uses the format to display the date.
  • Allow users to configure the format in the story for date.

Testing Instructions

  • Run the storybook locally (npm run storybook:dev).
  • Go to the "DataViews > Field Types > date" story and interact with the formatting control.

@oandregal oandregal self-assigned this Nov 5, 2025
@oandregal oandregal added [Type] Enhancement A suggestion for improvement. [Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond labels Nov 5, 2025
@oandregal
Copy link
Member Author

@jimjasson this is the follow-up about formats we talked about in #72756

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

Flaky tests detected in 7f735ad.
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/19264526494
📝 Reported issues:

* Format string for fields of type date.
* If not provided, defaults to WordPress date format settings.
*/
format?: string;
Copy link
Member Author

Choose a reason for hiding this comment

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

There's a conversation about JSON Schema, Field API, block types, etc. in this thread. We may want to rename this to displayFormat so that format is free to pick in the future in case we want the Field API to be aligned with the JSON Schema prop names.

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 I like this approach. I also think "date" and "datetime" from JSON schema are just special case formats (we might be able to share the same property for it)

Copy link
Contributor

Choose a reason for hiding this comment

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

Why did we switch to displayFormat? format is better and aligns with JSON Schema?

Copy link
Member Author

Choose a reason for hiding this comment

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

Switched precisely to leave format free to pick, in case we wanted to align with JSON Schema. If that happens, format has a different meaning (e.g., date) and can't be a display format (e.g., F j, Y). I thought I had clarified that in my summary 😓

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess for me, this is not a different meaning, it's the same thing, the JSON schema values are just some special case formats.

I also don't really see us having both "format" and "displayFormat", it's too confusing, so I'd prefer if we just use "format" here.

Copy link
Member Author

Choose a reason for hiding this comment

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

If we end up adopting JSON Schema for the field API, it'd look like this:

{
  id: 'publishDate',
  type: 'string',
  format: 'date',
  displayFormat: {
    weekStartsOn: 'monday'
    date: 'F j, y'
  }
}

Similarly, for numbers, etc.:

{
  id: 'publishDate',
  type: 'string',
  format: 'number',
  displayFormat: {
    decimalSeparator: ',',
    thousandSeparator: '.',
  }
}

They are two separate things, and we cannot conflate them. Not sure what you have in mind, but I don't see how a single format string, as defined in the JSON Schema, can handle everything.

I'm fine with renaming it back format if we understand it'll be a breaking change to rename it to displayFormat later, if we converge with the JSON Schema.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, so for me, embracing JSON Schema or not is not about strictly following it, it's about whether regular JSON schema should be supported or not. Our format will need to allow more regardless.

So these two options are acceptable IMO:

1- Our types differ from JSON Schema (so don't really support JSON Schema)

{
  id: 'publishDate',
  type: 'date',
  format: {
    weekStartsOn: 'monday'
    date: 'F j, y'
  }
}

and

{
  id: 'publishDate',
  type: 'number',
  format: {
    decimalSeparator: ',',
    thousandSeparator: '.',
  }
}

2- Our types match JSON Schema, support JSON Schema formats but also support more.

{
  id: 'publishDate',
  type: 'date',
  format: {
    weekStartsOn: 'monday'
    date: 'F j, y'
  }
}

and also support

{
  id: 'publishDate',
  type: 'date',
  format: 'datetime'
}

or

{
  id: 'publishDate',
  type: 'date',
  format: 'F j, y'
}

Copy link
Contributor

@ntsekouras ntsekouras Nov 12, 2025

Choose a reason for hiding this comment

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

I agree with Riad here to keep format for fields API and specifically his first example.

{
  id: 'publishDate',
  type: 'date',
  format: {
    weekStartsOn: 'monday'
    date: 'F j, y'
  }
}

We'll need more than one prop (like the example) and the format from JSON is coupled with the type, and is not the same thing with the kind of format config we want to keep.

I'm not sure we can end up eventually using format like core does because it would mean tons of extra code to support the things we want for fields API, by also having the limitations of an old JSON Schema for back compat. Unless I've misunderstood that we cannot upgrade the JSON Schema version and that might change plans.

Copy link
Member Author

Choose a reason for hiding this comment

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

22ab30a renames the prop back to format, as originally suggested in this PR.

As per the summary here, if we ever switch to JSON Schema it'll be a conflict due to how types work. But there'd be a few other breaking changes anyway (elements->enum, label->title, etc.). We can discuss how to approach that if/when we cross that bridge.

* Format string for fields of type date.
* If not provided, defaults to WordPress date format settings.
*/
format?: string;
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious how the format string scales. It works perfectly for date/datetime, but I'm not sure about other data types. Take the number as an example. If we want to use the built-in Intl.NumberFormat, we'll need an object, not a string.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, format is type-specific. We need to do something along the lines of EditConfig. It can also be an object for dates if we aim "Settings > General":

Screenshot 2025-11-11 at 09 55 33

So, as a first step, I think I'd start with an object and having those configurations as props, but I'd leave out timezone for now. That doesn't feel like a format, but rather an extra piece of data that's better dealt with in getValue/setValue.

@oandregal oandregal mentioned this pull request Nov 11, 2025
4 tasks
@oandregal
Copy link
Member Author

I've created #73154 to track implementation of formats across the Field API for any type.

@oandregal oandregal force-pushed the try/field-api-formats branch from c3fe1e4 to dc44210 Compare November 11, 2025 10:36
@oandregal oandregal marked this pull request as ready for review November 11, 2025 10:41
@github-actions
Copy link

github-actions bot commented Nov 11, 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: oandregal <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: dinhtungdu <[email protected]>
Co-authored-by: ntsekouras <[email protected]>

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

@oandregal oandregal changed the title Field API: explore formats Field API: add displayFormat to date field type Nov 11, 2025
@oandregal
Copy link
Member Author

I think this is ready!

@oandregal oandregal force-pushed the try/field-api-formats branch from 7f735ad to 22ab30a Compare November 12, 2025 09:30

return {
...field,
// TypeScript is unable to figure out that we're returning the proper types
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there any problems with TS if we kept baseField as before and added something like:

format:
		field.type !== 'date'
			? {}
			: {
					date:
						field.format?.date !== undefined &&
						...,
					weekStartsOn:
						field.format?.weekStartsOn !== undefined
							...
				},

Copy link
Member Author

Choose a reason for hiding this comment

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

This is one of those cases where we need to decide between type hints vs. code maintainability (while maintaining runtime safety in both scenarios).

Field author writes:

{
  type: 'int', // This is "int", not "date".
  format: {
    weekStartsOn: 'monday'
  }
}

If we want type hints (typescript alerting that this is invalid because that format is only valid for dates, we need these types, which is what I had:

export type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
	type: 'date';
	format: Required< FormatDate >;
};

export type NormalizedFieldGeneric< Item > = NormalizedFieldBase< Item > & {
	type?: Exclude< FieldType, 'date' >;
	format: {};
};

export type NormalizedField< Item > =
	| NormalizedFieldDate< Item >
	| NormalizedFieldGeneric< Item >;

That's a discriminated union, meaning that the value of format depends on the value of type. In a situation like this, we can't use the code you shared. The reason is that TypeScript does static analysis, does not run your code, so this is invalid code:

const format = field.type !== 'date' ? {} : { date: /* ... */, weekStartsOn: /**/ };

return {
  ...field,
  type, // For TypeScript, this can be 'int', **or** 'date', **or** 'text', etc.
  format // For TypeScript, this can be an empty object **or** a date format.
};

TypeScript is unable to narrow the types properly. So we have two options: create two return branches (like I had before 484f3fe) or ignoring TypeScript and do:

return {
  ...field,
  type,
  format
} as NormalizedField<Item> // Just trust me, it's good.

I don't want to forcefully coerce the return object because that removes the value TypeScript provides: telling you when there are errors. This removes runtime safety for code convenience, which I don't find a good trade-off.

So, we have two options: having two separate code paths (which makes code harder to maintain) or a single one at the expense of type hints. In 484f3fe I switched to a single code path as per your request.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, I remembered why I did the separate types/branches: any component that uses a date field type should be able to operate with a date format (and not an empty one). So by having a single branch in normalizeFields we are required to test format (or coerce it) in any other component. Not a good trade-off.

Let me revert 484f3fe and trying to find a way to make the two branches more readable.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for explaining!

Copy link
Contributor

@ntsekouras ntsekouras left a comment

Choose a reason for hiding this comment

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

Left a few comments, but this looks good in general. Thanks!

@oandregal oandregal changed the title Field API: add displayFormat to date field type Field API: add format to date field type Nov 12, 2025
@oandregal oandregal force-pushed the try/field-api-formats branch from 22ab30a to 14a756b Compare November 13, 2025 11:16
@oandregal oandregal requested a review from gigitux as a code owner November 13, 2025 11:16
Copy link
Contributor

@ntsekouras ntsekouras left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@ntsekouras ntsekouras merged commit aaa6ad0 into trunk Nov 13, 2025
40 of 41 checks passed
@ntsekouras ntsekouras deleted the try/field-api-formats branch November 13, 2025 14:44
@github-actions github-actions bot added this to the Gutenberg 22.2 milestone Nov 13, 2025
format: {},
};

if ( field.type === 'date' ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we move this to a "normalizeField" function within each type? Or just avoid the need for the normalization in the first place.

These "utils" functions shouldn't have any field.type === something
I'm a bit worried that all these edge cases are piling up instead of actually being used to improve the field types definitions 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.

This is a good call, I've started to look into this at #73387

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.

5 participants