Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dataviews/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Field API: move validation to the field type. [#73642](https://github.com/WordPress/gutenberg/pull/73642)
- DataForm: add support for `min`/`max` and `minLength`/`maxLength` validation for relevant controls. [#73465](https://github.com/WordPress/gutenberg/pull/73465)
- Field API: display formats for `number` and `integer` types. [#73644](https://github.com/WordPress/gutenberg/pull/73644)
- Field API: add display format for `datetime` type. [#73924](https://github.com/WordPress/gutenberg/pull/73924)
- DataViews: Update padding to 24px for consistency. [#73334](https://github.com/WordPress/gutenberg/pull/73334)
- DataViews: Simplify list layout field color styles. [#73884](https://github.com/WordPress/gutenberg/pull/73884)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import type {
Operator,
Option,
View,
NormalizedFieldDatetime,
} from '../../types';
import useElements from '../../hooks/use-elements';
import parseDateTime from '../../field-types/utils/parse-date-time';
Expand Down Expand Up @@ -223,7 +224,11 @@ export default function Filter( {
try {
const dateValue = parseDateTime( label );
if ( dateValue !== null ) {
label = dateValue.toLocaleString();
label = dateI18n(
( field as NormalizedFieldDatetime< any > ).format
.datetime,
getDate( label )
);
}
} catch ( e ) {
label = filterInView.value;
Expand Down
9 changes: 6 additions & 3 deletions packages/dataviews/src/dataform-controls/datetime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { getSettings } from '@wordpress/date';
/**
* Internal dependencies
*/
import type { DataFormControlProps } from '../types';
import type { DataFormControlProps, FormatDatetime } from '../types';
import { OPERATOR_IN_THE_PAST, OPERATOR_OVER } from '../constants';
import RelativeDateControl from './utils/relative-date-control';
import getCustomValidity from './utils/get-custom-validity';
Expand Down Expand Up @@ -147,9 +147,12 @@ function CalendarDateTimeControl< Item >( {
[ onChangeCallback ]
);

const { format: fieldFormat } = field;
const weekStartsOn =
( fieldFormat as FormatDatetime ).weekStartsOn ??
getSettings().l10n.startOfWeek;
const {
timezone: { string: timezoneString },
l10n: { startOfWeek },
} = getSettings();

const displayLabel =
Expand All @@ -176,7 +179,7 @@ function CalendarDateTimeControl< Item >( {
month={ calendarMonth }
onMonthChange={ setCalendarMonth }
timeZone={ timezoneString || undefined }
weekStartsOn={ startOfWeek }
weekStartsOn={ weekStartsOn }
/>
{ /* Manual datetime input */ }
<ValidatedInputControl
Expand Down
2 changes: 1 addition & 1 deletion packages/dataviews/src/field-types/date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) {
}

const value = field.getValue( { item } );
if ( ! value ) {
if ( [ '', undefined, null ].includes( value ) ) {
return '';
}

Expand Down
49 changes: 41 additions & 8 deletions packages/dataviews/src/field-types/datetime.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
/**
* WordPress dependencies
*/
import { dateI18n, getDate, getSettings } from '@wordpress/date';

/**
* Internal dependencies
*/
import type { DataViewRenderFieldProps, SortDirection } from '../types';
import type {
DataViewRenderFieldProps,
Field,
FormatDatetime,
SortDirection,
} from '../types';
import type { FieldType } from '../types/private';
import RenderFromElements from './utils/render-from-elements';
import parseDateTime from './utils/parse-date-time';
import isValidElements from './utils/is-valid-elements';
import {
OPERATOR_ON,
Expand All @@ -15,6 +24,7 @@ import {
OPERATOR_AFTER_INC,
OPERATOR_IN_THE_PAST,
OPERATOR_OVER,
DAYS_OF_WEEK,
} from '../constants';
import isValidRequired from './utils/is-valid-required';

Expand All @@ -28,12 +38,19 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) {
return null;
}

try {
const dateValue = parseDateTime( value );
return dateValue?.toLocaleString();
} catch ( error ) {
return null;
// If the field type is datetime, we've already normalized the format,
// and so it's safe to tell TypeScript to trust us ("as Required<Format>").
//
// There're no runtime paths where this render function is called with a non-datetime field,
// but TypeScript is unable to infer this, hence the type assertion.
let format: Required< FormatDatetime >;
if ( field.type !== 'datetime' ) {
format = getFormat( {} as Field< any > );
} else {
format = field.format as Required< FormatDatetime >;
}

return dateI18n( format.datetime, getDate( value ) );
}

const sort = ( a: any, b: any, direction: SortDirection ) => {
Expand All @@ -43,6 +60,22 @@ const sort = ( a: any, b: any, direction: SortDirection ) => {
return direction === 'asc' ? timeA - timeB : timeB - timeA;
};

function getFormat< Item >( field: Field< Item > ): Required< FormatDatetime > {
const fieldFormat = field.format as FormatDatetime | undefined;
return {
datetime:
fieldFormat?.datetime !== undefined &&
typeof fieldFormat.datetime === 'string'
? fieldFormat.datetime
: getSettings().formats.datetime,
weekStartsOn:
fieldFormat?.weekStartsOn !== undefined &&
DAYS_OF_WEEK.includes( fieldFormat?.weekStartsOn )
? fieldFormat.weekStartsOn
: getSettings().l10n.startOfWeek,
};
}

export default {
type: 'datetime',
render,
Expand Down Expand Up @@ -70,7 +103,7 @@ export default {
OPERATOR_IN_THE_PAST,
OPERATOR_OVER,
],
getFormat: () => ( {} ),
getFormat,
validate: {
required: isValidRequired,
elements: isValidElements,
Expand Down
56 changes: 54 additions & 2 deletions packages/dataviews/src/stories/field-types.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -890,13 +890,39 @@ export const DateTimeComponent = ( {
type,
Edit,
asyncElements,
formatDatetime,
formatWeekStartsOn,
}: {
type: PanelTypes;
Edit: ControlTypes;
asyncElements: boolean;
formatDatetime?: string;
formatWeekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
} ) => {
const datetimeFields = fields.filter( ( field ) =>
field.id.startsWith( 'datetime' )
const datetimeFields = useMemo(
() =>
fields
.filter( ( field ) => field.id.startsWith( 'datetime' ) )
.map( ( field ) => {
if ( formatDatetime || formatWeekStartsOn !== undefined ) {
const format: {
datetime?: string;
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
} = {};
if ( formatDatetime ) {
format.datetime = formatDatetime;
}
if ( formatWeekStartsOn !== undefined ) {
format.weekStartsOn = formatWeekStartsOn;
}
return {
...field,
format,
};
}
return field;
} ),
[ fields, formatDatetime, formatWeekStartsOn ]
);

return (
Expand All @@ -909,6 +935,32 @@ export const DateTimeComponent = ( {
);
};
DateTimeComponent.storyName = 'datetime';
DateTimeComponent.args = {
formatDatetime: '',
formatWeekStartsOn: undefined,
};
DateTimeComponent.argTypes = {
formatDatetime: {
control: 'text',
description:
'Custom PHP date format string (e.g., "M j, Y g:i a" for "Jan 1, 2021 2:30 pm"). Leave empty to use WordPress default.',
},
formatWeekStartsOn: {
control: 'select',
options: {
Default: undefined,
Sunday: 0,
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6,
},
description:
'Day that the week starts on. Leave as Default to use WordPress default.',
},
};

export const DateComponent = ( {
type,
Expand Down
25 changes: 22 additions & 3 deletions packages/dataviews/src/types/field-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,27 @@ export type Field< Item > = {
/**
* Display format configuration for fields.
*/
format?: FormatDate | FormatNumber | FormatInteger;
format?: FormatDate | FormatDatetime | FormatNumber | FormatInteger;
};

/**
* Format for datetime fields:
*
* - datetime: the format string (e.g., "M j, Y g:i a" for "Jan 1, 2021 2:30 pm").
* - weekStartsOn: to specify the first day of the week (0 for 'sunday', 1 for 'monday', etc.).
*
* If not provided, defaults to WordPress date format settings.
*/
export type FormatDatetime = {
datetime?: string;
weekStartsOn?: DayNumber;
};

/**
* Format for date fields:
*
* - date: the format string (e.g., 'F j, Y' for WordPress default format like 'March 10, 2023')
* - weekStartsOn: to specify the first day of the week ('sunday', 'monday', etc.).
* - date: the format string (e.g., 'F j, Y' for 'March 10, 2023')
* - weekStartsOn: to specify the first day of the week (0 for 'sunday', 1 for 'monday', etc.).
*
* If not provided, defaults to WordPress date format settings.
*/
Expand Down Expand Up @@ -333,6 +346,11 @@ type NormalizedFieldBase< Item > = Omit< Field< Item >, 'Edit' | 'isValid' > & {
format: {};
};

export type NormalizedFieldDatetime< Item > = NormalizedFieldBase< Item > & {
type: 'datetime';
format: Required< FormatDatetime >;
};

export type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
type: 'date';
format: Required< FormatDate >;
Expand All @@ -351,6 +369,7 @@ export type NormalizedFieldInteger< Item > = NormalizedFieldBase< Item > & {
export type NormalizedField< Item > =
| NormalizedFieldBase< Item >
| NormalizedFieldDate< Item >
| NormalizedFieldDatetime< Item >
| NormalizedFieldNumber< Item >
| NormalizedFieldInteger< Item >;

Expand Down
2 changes: 2 additions & 0 deletions packages/dataviews/src/types/private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
CustomValidator,
Field,
FormatDate,
FormatDatetime,
FormatInteger,
FormatNumber,
NormalizedField,
Expand All @@ -26,6 +27,7 @@ export type FieldType< Item > = Pick<
) =>
| Record< string, any >
| Required< FormatDate >
| Required< FormatDatetime >
| Required< FormatNumber >
| Required< FormatInteger >;
validate: {
Expand Down
4 changes: 4 additions & 0 deletions packages/date/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

## Bug Fixes

- Fixed incorrect spacing for the time format. It was `g: i` (`14: 30`), and it's now `g:i` (`14:30`). See [#73924](https://github.com/WordPress/gutenberg/pull/73924).

## 5.36.0 (2025-11-26)

### Bug Fixes
Expand Down
6 changes: 3 additions & 3 deletions packages/date/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ let settings: DateSettings = {
startOfWeek: 0,
},
formats: {
time: 'g: i a',
time: 'g:i a',
date: 'F j, Y',
datetime: 'F j, Y g: i a',
datetimeAbbreviated: 'M j, Y g: i a',
datetime: 'F j, Y g:i a',
datetimeAbbreviated: 'M j, Y g:i a',
},
timezone: { offset: 0, offsetFormatted: '0', string: '', abbr: '' },
};
Expand Down
Loading