Skip to content

Commit

Permalink
Post Date: Allow user to pick Site Default, a suggested date format, …
Browse files Browse the repository at this point in the history
…or a custom date format (#39209)

* Post Date: Allow user to pick Site Default, or a suggested date format

* Update suggested formats to be more localizable

* Add support for custom formats

* Move is12Hour check to function

* Distinguish between 'long' and 'full' like Apple does

* Use a sample date in the dropdown so as to better illustrate the formats

* Split out DateFormatControl and update the Comment Date block

* Add basic tests for is12HourFormat

* Fix markdown link formatting

* Combine 'Format settings' and 'Link settings' into single 'Settings' panel

* WIP

* Use Default / Custom radio control

Re-jig DateFormatControl into DateFormatPicker. Users now select either
Default or Custom in a ratio control and, then, if they select Custom,
they can select a suggested date format or enter a manual one.

* Simplify suggested date formats

* Use date and time formats from @wordpress/date as a fallback

* Use 'Enter your own date format'

* Explain what null means in the doc comment

* Use ToggleControl for selecting a default date

* Use __experimental like a coward

* Move example of default format to new line

* Fix 'Link to post' label
  • Loading branch information
noisysocks authored Mar 18, 2022
1 parent 1f07e88 commit 5921283
Show file tree
Hide file tree
Showing 11 changed files with 355 additions and 83 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@wordpress/components": "file:../components",
"@wordpress/compose": "file:../compose",
"@wordpress/data": "file:../data",
"@wordpress/date": "file:../date",
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/dom": "file:../dom",
"@wordpress/element": "file:../element",
Expand Down
58 changes: 58 additions & 0 deletions packages/block-editor/src/components/date-format-picker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# DateFormatPicker

The `DateFormatPicker` component renders controls that let the user choose a
_date format_. That is, how they want their dates to be formatted.

A user can pick _Default_ to use the default date format (usually set at the
site level).

Otherwise, a user may choose a suggested date format or type in their own date
format by selecting _Custom_.

All date format strings should be in the format accepted by by the [`dateI18n`
function in
`@wordpress/date`](https://github.com/WordPress/gutenberg/tree/trunk/packages/date#datei18n).

## Usage

```jsx
import { DateFormatPicker } from '@wordpress/block-editor';

const Example = () => {
const [ format, setFormat ] = useState( null );
return (
<DateFormatPicker
format={ format }
defaultFormat={ 'M j, Y' }
onChange={ ( nextFormat ) =>
setFormat( nextFormat );
}
/>
);
};
```

## Props

### `format`

The current date format selected by the user. If `null`, _Default_ is selected.

- Type: `string|null`
- Required: Yes

### `defaultFormat`

The default format string. Used to show to the user what the date will look like
if _Default_ is selected.

- Type: `string`
- Required: Yes

### `onChange`

Called when the user makes a selection, or when the user types in a date format.
`null` indicates that _Default_ is selected.

- Type: `( format: string|null ) => void`
- Required: Yes
161 changes: 161 additions & 0 deletions packages/block-editor/src/components/date-format-picker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* External dependencies
*/
import { uniq } from 'lodash';

/**
* WordPress dependencies
*/
import { _x, __ } from '@wordpress/i18n';
import { dateI18n } from '@wordpress/date';
import { useState, createInterpolateElement } from '@wordpress/element';
import {
TextControl,
ExternalLink,
VisuallyHidden,
CustomSelectControl,
BaseControl,
ToggleControl,
} from '@wordpress/components';

// So that we can illustrate the different formats in the dropdown properly,
// show a date that has a day greater than 12 and a month with more than three
// letters. Here we're using 2022-01-25 which is when WordPress 5.9 was
// released.
const EXAMPLE_DATE = new Date( 2022, 0, 25 );

/**
* The `DateFormatPicker` component renders controls that let the user choose a
* _date format_. That is, how they want their dates to be formatted.
*
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/date-format-picker/README.md
*
* @param {Object} props
* @param {string|null} props.format The selected date
* format. If
* `null`,
* _Default_ is
* selected.
* @param {string} props.defaultFormat The date format that
* will be used if the
* user selects
* 'Default'.
* @param {( format: string|null ) => void} props.onChange Called when a
* selection is
* made. If `null`,
* _Default_ is
* selected.
*/
export default function DateFormatPicker( {
format,
defaultFormat,
onChange,
} ) {
return (
<fieldset className="block-editor-date-format-picker">
<VisuallyHidden as="legend">{ __( 'Date format' ) }</VisuallyHidden>
<ToggleControl
label={
<>
{ __( 'Default format' ) }
<span className="block-editor-date-format-picker__default-format-toggle-control__hint">
{ dateI18n( defaultFormat, EXAMPLE_DATE ) }
</span>
</>
}
checked={ ! format }
onChange={ ( checked ) =>
onChange( checked ? null : defaultFormat )
}
/>
{ format && (
<NonDefaultControls format={ format } onChange={ onChange } />
) }
</fieldset>
);
}

function NonDefaultControls( { format, onChange } ) {
// Suggest a short format, medium format, long format, and a standardised
// (YYYY-MM-DD) format. The short, medium, and long formats are localised as
// different languages have different ways of writing these. For example, 'F
// j, Y' (April 20, 2022) in American English (en_US) is 'j. F Y' (20. April
// 2022) in German (de). The resultant array is de-duplicated as some
// languages will use the same format string for short, medium, and long
// formats.
const suggestedFormats = uniq( [
'Y-m-d',
_x( 'n/j/Y', 'short date format' ),
_x( 'n/j/Y g:i A', 'short date format with time' ),
_x( 'M j, Y', 'medium date format' ),
_x( 'M j, Y g:i A', 'medium date format with time' ),
_x( 'F j, Y', 'long date format' ),
] );

const suggestedOptions = suggestedFormats.map(
( suggestedFormat, index ) => ( {
key: `suggested-${ index }`,
name: dateI18n( suggestedFormat, EXAMPLE_DATE ),
format: suggestedFormat,
} )
);
const customOption = {
key: 'custom',
name: __( 'Custom' ),
className:
'block-editor-date-format-picker__custom-format-select-control__custom-option',
__experimentalHint: __( 'Enter your own date format' ),
};

const [ isCustom, setIsCustom ] = useState(
() => !! format && ! suggestedFormats.includes( format )
);

return (
<>
<BaseControl className="block-editor-date-format-picker__custom-format-select-control">
<CustomSelectControl
label={ __( 'Choose a format' ) }
options={ [ ...suggestedOptions, customOption ] }
value={
isCustom
? customOption
: suggestedOptions.find(
( option ) => option.format === format
) ?? customOption
}
onChange={ ( { selectedItem } ) => {
if ( selectedItem === customOption ) {
setIsCustom( true );
} else {
setIsCustom( false );
onChange( selectedItem.format );
}
} }
/>
</BaseControl>
{ isCustom && (
<TextControl
label={ __( 'Custom format' ) }
hideLabelFromVision
help={ createInterpolateElement(
__(
'Enter a date or time <Link>format string</Link>.'
),
{
Link: (
<ExternalLink
href={ __(
'https://wordpress.org/support/article/formatting-date-and-time/'
) }
/>
),
}
) }
value={ format }
onChange={ ( value ) => onChange( value ) }
/>
) }
</>
);
}
31 changes: 31 additions & 0 deletions packages/block-editor/src/components/date-format-picker/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.block-editor-date-format-picker {
margin-bottom: $grid-unit-20;
}

.block-editor-date-format-picker__default-format-toggle-control__hint {
color: $gray-700;
display: block;
}

.block-editor-date-format-picker__custom-format-select-control {
&.components-base-control {
margin-bottom: 0;
}

.components-custom-select-control__button {
width: 100%;
}
}

.block-editor-date-format-picker__custom-format-select-control__custom-option {
border-top: 1px solid $gray-300;

&.has-hint {
grid-template-columns: auto 30px;
}

.components-custom-select-control__item-hint {
grid-row: 2;
text-align: left;
}
}
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export {
export { default as ColorPalette } from './color-palette';
export { default as ColorPaletteControl } from './color-palette/control';
export { default as ContrastChecker } from './contrast-checker';
export { default as __experimentalDateFormatPicker } from './date-format-picker';
export { default as __experimentalDuotoneControl } from './duotone-control';
export { default as __experimentalFontAppearanceControl } from './font-appearance-control';
export { default as __experimentalFontFamilyControl } from './font-family';
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
@import "./components/colors-gradients/style.scss";
@import "./components/contrast-checker/style.scss";
@import "./components/default-block-appender/style.scss";
@import "./components/date-format-picker/style.scss";
@import "./components/duotone-control/style.scss";
@import "./components/font-appearance-control/style.scss";
@import "./components/image-size-control/style.scss";
Expand Down
50 changes: 20 additions & 30 deletions packages/block-library/src/comment-date/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
* WordPress dependencies
*/
import { useEntityProp } from '@wordpress/core-data';
import { __experimentalGetSettings, dateI18n } from '@wordpress/date';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
PanelBody,
CustomSelectControl,
ToggleControl,
} from '@wordpress/components';
dateI18n,
__experimentalGetSettings as getDateSettings,
} from '@wordpress/date';
import {
InspectorControls,
useBlockProps,
__experimentalDateFormatPicker as DateFormatPicker,
} from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
import { __, _x } from '@wordpress/i18n';

/**
Expand All @@ -31,35 +34,22 @@ export default function Edit( {
} ) {
const blockProps = useBlockProps();
const [ date ] = useEntityProp( 'root', 'comment', 'date', commentId );
const [ siteDateFormat ] = useEntityProp( 'root', 'site', 'date_format' );

const settings = __experimentalGetSettings();
const formatOptions = Object.values( settings.formats ).map(
( formatOption ) => ( {
key: formatOption,
name: dateI18n( formatOption, date || new Date() ),
} )
const [ siteFormat = getDateSettings().formats.date ] = useEntityProp(
'root',
'site',
'date_format'
);
const resolvedFormat = format || siteDateFormat || settings.formats.date;

const inspectorControls = (
<InspectorControls>
<PanelBody title={ __( 'Format settings' ) }>
<CustomSelectControl
hideLabelFromVision
label={ __( 'Date Format' ) }
options={ formatOptions }
onChange={ ( { selectedItem } ) =>
setAttributes( {
format: selectedItem.key,
} )
<PanelBody title={ __( 'Settings' ) }>
<DateFormatPicker
format={ format }
defaultFormat={ siteFormat }
onChange={ ( nextFormat ) =>
setAttributes( { format: nextFormat } )
}
value={ formatOptions.find(
( option ) => option.key === resolvedFormat
) }
/>
</PanelBody>
<PanelBody title={ __( 'Link settings' ) }>
<ToggleControl
label={ __( 'Link to comment' ) }
onChange={ () => setAttributes( { isLink: ! isLink } ) }
Expand All @@ -82,7 +72,7 @@ export default function Edit( {

let commentDate = (
<time dateTime={ dateI18n( 'c', date ) }>
{ dateI18n( resolvedFormat, date ) }
{ dateI18n( format || siteFormat, date ) }
</time>
);

Expand Down
Loading

0 comments on commit 5921283

Please sign in to comment.