-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Field API: add format to date field type
#72999
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
Conversation
|
@jimjasson this is the follow-up about formats we talked about in #72756 |
|
Flaky tests detected in 7f735ad. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/19264526494
|
| * Format string for fields of type date. | ||
| * If not provided, defaults to WordPress date format settings. | ||
| */ | ||
| format?: string; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 😓
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'
}
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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":
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.
|
I've created #73154 to track implementation of formats across the Field API for any type. |
c3fe1e4 to
dc44210
Compare
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
displayFormat to date field type
|
I think this is ready! |
7f735ad to
22ab30a
Compare
|
|
||
| return { | ||
| ...field, | ||
| // TypeScript is unable to figure out that we're returning the proper types |
There was a problem hiding this comment.
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
...
},
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining!
ntsekouras
left a comment
There was a problem hiding this 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!
displayFormat to date field typeformat to date field type
22ab30a to
14a756b
Compare
ntsekouras
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
| format: {}, | ||
| }; | ||
|
|
||
| if ( field.type === 'date' ) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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
datefield 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
renderandEditfunctions, the current approach has limits:renderandEditcomponents are not.dateornumber.Related conversations and work:
numberfield #71797 (comment) @dinhtungduQuestions
formatin attribute definitions, use for Block Bindings #72716 for block bindings / block types. cc @cbravobernal and @ockham for awarenessmediaRolesin block config #72792 for semantic roles in blocks. @ntsekourasHow?
formatprop to the Field API.datefield doesn't provide a format, the WordPress default is used.renderprovided by the field type definition uses the format to display the date.date.Testing Instructions
npm run storybook:dev).