Skip to content
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

Post Date: Allow user to pick Site Default, a suggested date format, or a custom date format #39209

Merged
merged 20 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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 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
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 ) }
/>
) }
</>
);
}
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