Skip to content

Commit

Permalink
TimePicker: Add dateOrder prop to sort day, month, and year (#62481)
Browse files Browse the repository at this point in the history
* TimePicker: Add `dateOrder` prop to sort day, month, and year

* Add changelog

* Update unit tests

* Fix grammar

* Fix changelog

* Refactoring

* Update packages/block-library/src/post-date/edit.js

Co-authored-by: Lena Morita <[email protected]>

* Revert unwanted changes

* `dateOrder` prop overwrites `is12Hour` prop

* Add unit test

* Clarify the documentation

* Restore unit test

* Improve test comment

* Remove unnecessary is12Hour prop

* Improve doc

Co-authored-by: Lena Morita <[email protected]>

* Simplify logic

* Restore `is12Hour` prop

* Adjust prop position

* Remove `dateOrder` prop default value from `DateTimePicker`

---------

Co-authored-by: t-hamano <[email protected]>
Co-authored-by: tyxla <[email protected]>
Co-authored-by: mirka <[email protected]>
Co-authored-by: carstingaxion <[email protected]>
Co-authored-by: ramonjd <[email protected]>
  • Loading branch information
6 people authored Jul 8, 2024
1 parent 2030d48 commit bcf2b35
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 40 deletions.
6 changes: 5 additions & 1 deletion packages/block-library/src/post-date/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
ToggleControl,
PanelBody,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { __, _x, sprintf } from '@wordpress/i18n';
import { edit } from '@wordpress/icons';
import { DOWN } from '@wordpress/keycodes';
import { useSelect } from '@wordpress/data';
Expand Down Expand Up @@ -128,6 +128,10 @@ export default function PostDateEdit( {
siteTimeFormat
) }
onClose={ onClose }
dateOrder={
/* translators: Order of day, month, and year. Available formats are 'dmy', 'mdy', and 'ymd'. */
_x( 'dmy', 'date order' )
}
/>
) }
renderToggle={ ( { isOpen, onToggle } ) => {
Expand Down
14 changes: 6 additions & 8 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
- `Tooltip`: Add support for `className` prop ([#63157](https://github.com/WordPress/gutenberg/pull/63157)).
- `Toolbar`: Add support for `vertical` orientation ([#60123](https://github.com/WordPress/gutenberg/pull/60123)).
- `BaseControl`: forward ref on `VisualLabel` ([#63169](https://github.com/WordPress/gutenberg/pull/63169)).
- `ToolbarButton`: Deprecate `isDisabled` prop and merge with `disabled` ([#63101](https://github.com/WordPress/gutenberg/pull/63101)).
- `Button`: Stabilize `__experimentalIsFocusable` prop as `accessibleWhenDisabled` ([#62282](https://github.com/WordPress/gutenberg/pull/62282)).
- `ToolbarButton`: Always keep focusable when disabled ([#63102](https://github.com/WordPress/gutenberg/pull/63102)).
- `ProgressBar`: Fix indeterminate RTL support. ([#63129](https://github.com/WordPress/gutenberg/pull/63129)).
- `RangeControl`: Fix RTL support for custom marks ([#63198](https://github.com/WordPress/gutenberg/pull/63198)).
- `TimePicker`: Add `dateOrder` prop ([#62481](https://github.com/WordPress/gutenberg/pull/62481)).

### Bug Fixes

Expand All @@ -16,14 +22,6 @@
- Fix inaccessibly disabled `Button`s ([#62306](https://github.com/WordPress/gutenberg/pull/62306)).
- `TimePicker`: Fix time zone overflow ([#63209](https://github.com/WordPress/gutenberg/pull/63209)).

### Enhancements

- `ToolbarButton`: Deprecate `isDisabled` prop and merge with `disabled` ([#63101](https://github.com/WordPress/gutenberg/pull/63101)).
- `Button`: Stabilize `__experimentalIsFocusable` prop as `accessibleWhenDisabled` ([#62282](https://github.com/WordPress/gutenberg/pull/62282)).
- `ToolbarButton`: Always keep focusable when disabled ([#63102](https://github.com/WordPress/gutenberg/pull/63102)).
- `ProgressBar`: Fix indeterminate RTL support. ([#63129](https://github.com/WordPress/gutenberg/pull/63129)).
- `RangeControl`: Fix RTL support for custom marks ([#63198](https://github.com/WordPress/gutenberg/pull/63198)).

### Internal

- `Allow ariakit and framer motion imports in the components package. ([#63123](https://github.com/WordPress/gutenberg/pull/63123))
Expand Down
8 changes: 8 additions & 0 deletions packages/components/src/date-time/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ Whether we use a 12-hour clock. With a 12-hour clock, an AM/PM widget is display
- Required: No
- Default: false

### `dateOrder`: `'dmy' | 'mdy' | 'ymd'`

The order of day, month, and year. This prop overrides the time format determined by `is12Hour` prop.

- Type: `string`
- Required: No
- Default: `'dmy'`

### `isInvalidDate`: `( date: Date ) => boolean`

A callback function which receives a Date object representing a day as an argument, and should return a Boolean to signify if the day is valid or not.
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/date-time/date-time/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function UnforwardedDateTimePicker(
{
currentDate,
is12Hour,
dateOrder,
isInvalidDate,
onMonthPreviewed = noop,
onChange,
Expand All @@ -39,6 +40,7 @@ function UnforwardedDateTimePicker(
currentTime={ currentDate }
onChange={ onChange }
is12Hour={ is12Hour }
dateOrder={ dateOrder }
/>
<DatePicker
currentDate={ currentDate }
Expand Down
77 changes: 46 additions & 31 deletions packages/components/src/date-time/time/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
import { TIMEZONELESS_FORMAT } from '../constants';
import { TimeInput } from '../time-input';

const VALID_DATE_ORDERS = [ 'dmy', 'mdy', 'ymd' ];

/**
* TimePicker is a React component that renders a clock for time selection.
*
Expand All @@ -58,6 +60,7 @@ export function TimePicker( {
is12Hour,
currentTime,
onChange,
dateOrder: dateOrderProp,
}: TimePickerProps ) {
const [ date, setDate ] = useState( () =>
// Truncate the date at the minutes, see: #15495.
Expand Down Expand Up @@ -116,6 +119,7 @@ export function TimePicker( {

const dayField = (
<DayInput
key="day"
className="components-datetime__time-field components-datetime__time-field-day" // Unused, for backwards compatibility.
label={ __( 'Day' ) }
hideLabelFromVision
Expand All @@ -134,7 +138,7 @@ export function TimePicker( {
);

const monthField = (
<MonthSelectWrapper>
<MonthSelectWrapper key="month">
<SelectControl
className="components-datetime__time-field components-datetime__time-field-month" // Unused, for backwards compatibility.
label={ __( 'Month' ) }
Expand Down Expand Up @@ -165,6 +169,46 @@ export function TimePicker( {
</MonthSelectWrapper>
);

const yearField = (
<YearInput
key="year"
className="components-datetime__time-field components-datetime__time-field-year" // Unused, for backwards compatibility.
label={ __( 'Year' ) }
hideLabelFromVision
__next40pxDefaultSize
value={ year }
step={ 1 }
min={ 1 }
max={ 9999 }
required
spinControls="none"
isPressEnterToChange
isDragEnabled={ false }
isShiftStepEnabled={ false }
onChange={ buildNumberControlChangeCallback( 'year' ) }
__unstableStateReducer={ buildPadInputStateReducer( 4 ) }
/>
);

const defaultDateOrder = is12Hour ? 'mdy' : 'dmy';
const dateOrder =
dateOrderProp && VALID_DATE_ORDERS.includes( dateOrderProp )
? dateOrderProp
: defaultDateOrder;

const fields = dateOrder.split( '' ).map( ( field ) => {
switch ( field ) {
case 'd':
return dayField;
case 'm':
return monthField;
case 'y':
return yearField;
default:
return null;
}
} );

return (
<Wrapper
className="components-datetime__time" // Unused, for backwards compatibility.
Expand Down Expand Up @@ -201,36 +245,7 @@ export function TimePicker( {
<HStack
className="components-datetime__time-wrapper" // Unused, for backwards compatibility.
>
{ is12Hour ? (
<>
{ monthField }
{ dayField }
</>
) : (
<>
{ dayField }
{ monthField }
</>
) }
<YearInput
className="components-datetime__time-field components-datetime__time-field-year" // Unused, for backwards compatibility.
label={ __( 'Year' ) }
hideLabelFromVision
__next40pxDefaultSize
value={ year }
step={ 1 }
min={ 1 }
max={ 9999 }
required
spinControls="none"
isPressEnterToChange
isDragEnabled={ false }
isShiftStepEnabled={ false }
onChange={ buildNumberControlChangeCallback( 'year' ) }
__unstableStateReducer={ buildPadInputStateReducer(
4
) }
/>
{ fields }
</HStack>
</Fieldset>
</Wrapper>
Expand Down
61 changes: 61 additions & 0 deletions packages/components/src/date-time/time/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,67 @@ describe( 'TimePicker', () => {
expect( monthInputIndex < dayInputIndex ).toBe( true );
} );

it( 'Should change layouts/orders when `dateOrder` prop is passed', () => {
const onChangeSpy = jest.fn();

render(
<form aria-label="form">
<TimePicker
currentTime="1986-10-18T11:00:00"
onChange={ onChangeSpy }
dateOrder="ymd"
/>
</form>
);

const form = screen.getByRole( 'form' ) as HTMLFormElement;

const yearInputIndex = Array.from( form.elements ).indexOf(
screen.getByLabelText( 'Year' )
);

const monthInputIndex = Array.from( form.elements ).indexOf(
screen.getByLabelText( 'Month' )
);
const dayInputIndex = Array.from( form.elements ).indexOf(
screen.getByLabelText( 'Day' )
);

expect( monthInputIndex > yearInputIndex ).toBe( true );
expect( dayInputIndex > monthInputIndex ).toBe( true );
} );

it( 'Should ignore `is12Hour` prop setting when `dateOrder` prop is explicitly passed', () => {
const onChangeSpy = jest.fn();

render(
<form aria-label="form">
<TimePicker
currentTime="1986-10-18T11:00:00"
onChange={ onChangeSpy }
dateOrder="ymd"
is12Hour
/>
</form>
);

const form = screen.getByRole( 'form' ) as HTMLFormElement;

const yearInputIndex = Array.from( form.elements ).indexOf(
screen.getByLabelText( 'Year' )
);

const monthInputIndex = Array.from( form.elements ).indexOf(
screen.getByLabelText( 'Month' )
);
const dayInputIndex = Array.from( form.elements ).indexOf(
screen.getByLabelText( 'Day' )
);

expect( monthInputIndex > yearInputIndex ).toBe( true );
expect( dayInputIndex > monthInputIndex ).toBe( true );
} );

it( 'Should set a time when passed a null currentTime', () => {
const onChangeSpy = jest.fn();

Expand Down
8 changes: 8 additions & 0 deletions packages/components/src/date-time/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ export type TimePickerProps = {
*/
is12Hour?: boolean;

/**
* The order of day, month, and year. This prop overrides the time format
* determined by `is12Hour` prop.
*
* @default 'dmy'
*/
dateOrder?: 'dmy' | 'mdy' | 'ymd';

/**
* The function called when a new time has been selected. It is passed the
* time as an argument.
Expand Down
5 changes: 5 additions & 0 deletions packages/editor/src/components/post-schedule/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { parseISO, endOfMonth, startOfMonth } from 'date-fns';
* WordPress dependencies
*/
import { getSettings } from '@wordpress/date';
import { _x } from '@wordpress/i18n';
import { useDispatch, useSelect } from '@wordpress/data';
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
import { useState, useMemo } from '@wordpress/element';
Expand Down Expand Up @@ -98,6 +99,10 @@ export function PrivatePostSchedule( {
currentDate={ postDate }
onChange={ onUpdateDate }
is12Hour={ is12HourTime }
dateOrder={
/* translators: Order of day, month, and year. Available formats are 'dmy', 'mdy', and 'ymd'. */
_x( 'dmy', 'date order' )
}
events={ events }
onMonthPreviewed={ ( date ) =>
setPreviewedMonth( parseISO( date ) )
Expand Down

0 comments on commit bcf2b35

Please sign in to comment.