Skip to content

Commit

Permalink
Quick edit: add Date as field and datetime as field type (#64267)
Browse files Browse the repository at this point in the history
Co-authored-by: oandregal <[email protected]>
Co-authored-by: ntsekouras <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: mirka <[email protected]>
Co-authored-by: tyxla <[email protected]>
Co-authored-by: jameskoster <[email protected]>
  • Loading branch information
7 people authored Aug 9, 2024
1 parent b11bdc0 commit 0ceac8f
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 18 deletions.
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- `TimePicker`: add `hideLabelFromVision` prop ([#64267](https://github.com/WordPress/gutenberg/pull/64267)).

## 28.5.0 (2024-08-07)

### Bug Fixes
Expand Down
38 changes: 26 additions & 12 deletions packages/components/src/date-time/time/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import BaseControl from '../../base-control';
import { VisuallyHidden } from '../../visually-hidden';
import SelectControl from '../../select-control';
import TimeZone from './timezone';
import type { TimeInputValue, TimePickerProps } from '../types';
Expand Down Expand Up @@ -61,6 +62,7 @@ export function TimePicker( {
currentTime,
onChange,
dateOrder: dateOrderProp,
hideLabelFromVision = false,
}: TimePickerProps ) {
const [ date, setDate ] = useState( () =>
// Truncate the date at the minutes, see: #15495.
Expand Down Expand Up @@ -219,12 +221,18 @@ export function TimePicker( {
className="components-datetime__time" // Unused, for backwards compatibility.
>
<Fieldset>
<BaseControl.VisualLabel
as="legend"
className="components-datetime__time-legend" // Unused, for backwards compatibility.
>
{ __( 'Time' ) }
</BaseControl.VisualLabel>
{ hideLabelFromVision ? (
<VisuallyHidden as="legend">
{ __( 'Time' ) }
</VisuallyHidden>
) : (
<BaseControl.VisualLabel
as="legend"
className="components-datetime__time-legend" // Unused, for backwards compatibility.
>
{ __( 'Time' ) }
</BaseControl.VisualLabel>
) }
<HStack
className="components-datetime__time-wrapper" // Unused, for backwards compatibility.
>
Expand All @@ -241,12 +249,18 @@ export function TimePicker( {
</HStack>
</Fieldset>
<Fieldset>
<BaseControl.VisualLabel
as="legend"
className="components-datetime__time-legend" // Unused, for backwards compatibility.
>
{ __( 'Date' ) }
</BaseControl.VisualLabel>
{ hideLabelFromVision ? (
<VisuallyHidden as="legend">
{ __( 'Date' ) }
</VisuallyHidden>
) : (
<BaseControl.VisualLabel
as="legend"
className="components-datetime__time-legend" // Unused, for backwards compatibility.
>
{ __( 'Date' ) }
</BaseControl.VisualLabel>
) }
<HStack
className="components-datetime__time-wrapper" // Unused, for backwards compatibility.
>
Expand Down
12 changes: 11 additions & 1 deletion packages/components/src/date-time/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export type TimePickerProps = {
* time as an argument.
*/
onChange?: ( time: string ) => void;

/**
* If true, the label will only be visible to screen readers.
*
* @default false
*/
hideLabelFromVision?: boolean;
};

export type TimeInputValue = {
Expand Down Expand Up @@ -130,7 +137,10 @@ export type DatePickerProps = {
};

export type DateTimePickerProps = Omit< DatePickerProps, 'onChange' > &
Omit< TimePickerProps, 'currentTime' | 'onChange' > & {
Omit<
TimePickerProps,
'currentTime' | 'onChange' | 'hideLabelFromVision'
> & {
/**
* The function called when a new date or time has been selected. It is
* passed the date and time as an argument.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ const fields = [
label: 'Order',
type: 'integer' as const,
},
{
id: 'date',
label: 'Date',
type: 'datetime' as const,
},
{
id: 'birthdate',
label: 'Date as options',
type: 'datetime' as const,
elements: [
{ value: '1970-02-23T12:00:00', label: "Jane's birth date" },
{ value: '1950-02-23T12:00:00', label: "John's birth date" },
],
},
{
id: 'author',
label: 'Author',
Expand All @@ -59,10 +73,12 @@ export const Default = ( { type }: { type: 'panel' | 'regular' } ) => {
order: 2,
author: 1,
status: 'draft',
date: '2021-01-01T12:00:00',
birthdate: '1950-02-23T12:00:00',
} );

const form = {
fields: [ 'title', 'order', 'author', 'status' ],
fields: [ 'title', 'order', 'author', 'status', 'date', 'birthdate' ],
};

return (
Expand Down
16 changes: 16 additions & 0 deletions packages/dataviews/src/components/dataviews/stories/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const data = [
type: 'Not a planet',
categories: [ 'Space', 'NASA' ],
satellites: 0,
date: '2021-01-01T00:00:00Z',
},
{
id: 2,
Expand All @@ -32,6 +33,7 @@ export const data = [
type: 'Not a planet',
categories: [ 'Space' ],
satellites: 0,
date: '2019-01-02T00:00:00Z',
},
{
id: 3,
Expand All @@ -41,6 +43,7 @@ export const data = [
type: 'Not a planet',
categories: [ 'NASA' ],
satellites: 0,
date: '2025-01-03T00:00:00Z',
},
{
id: 4,
Expand All @@ -50,6 +53,7 @@ export const data = [
type: 'Ice giant',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 14,
date: '2020-01-01T00:00:00Z',
},
{
id: 5,
Expand All @@ -59,6 +63,7 @@ export const data = [
type: 'Terrestrial',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 0,
date: '2020-01-02T01:00:00Z',
},
{
id: 6,
Expand All @@ -68,6 +73,7 @@ export const data = [
type: 'Terrestrial',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 0,
date: '2020-01-02T00:00:00Z',
},
{
id: 7,
Expand All @@ -77,6 +83,7 @@ export const data = [
type: 'Terrestrial',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 1,
date: '2023-01-03T00:00:00Z',
},
{
id: 8,
Expand All @@ -86,6 +93,7 @@ export const data = [
type: 'Terrestrial',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 2,
date: '2020-01-01T00:00:00Z',
},
{
id: 9,
Expand All @@ -95,6 +103,7 @@ export const data = [
type: 'Gas giant',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 95,
date: '2017-01-01T00:01:00Z',
},
{
id: 10,
Expand All @@ -104,6 +113,7 @@ export const data = [
type: 'Gas giant',
categories: [ 'Space', 'Planet', 'Solar system' ],
satellites: 146,
date: '2020-02-01T00:02:00Z',
},
{
id: 11,
Expand All @@ -113,6 +123,7 @@ export const data = [
type: 'Ice giant',
categories: [ 'Space', 'Ice giant', 'Solar system' ],
satellites: 28,
date: '2020-03-01T00:00:00Z',
},
];

Expand Down Expand Up @@ -175,6 +186,11 @@ export const fields = [
enableHiding: false,
enableGlobalSearch: true,
},
{
id: 'date',
label: 'Date',
type: 'datetime',
},
{
label: 'Type',
id: 'type',
Expand Down
95 changes: 95 additions & 0 deletions packages/dataviews/src/field-types/datetime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* WordPress dependencies
*/
import { BaseControl, TimePicker, SelectControl } from '@wordpress/components';
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import type {
SortDirection,
ValidationContext,
DataFormControlProps,
} from '../types';

function sort( a: any, b: any, direction: SortDirection ) {
const timeA = new Date( a ).getTime();
const timeB = new Date( b ).getTime();

return direction === 'asc' ? timeA - timeB : timeB - timeA;
}

function isValid( value: any, context?: ValidationContext ) {
if ( context?.elements ) {
const validValues = context?.elements.map( ( f ) => f.value );
if ( ! validValues.includes( value ) ) {
return false;
}
}

return true;
}

function Edit< Item >( {
data,
field,
onChange,
}: DataFormControlProps< Item > ) {
const { id, label } = field;
const value = field.getValue( { item: data } );

const onChangeControl = useCallback(
( newValue: string | null ) =>
onChange( ( prevItem: Item ) => ( {
...prevItem,
[ id ]: newValue,
} ) ),
[ id, onChange ]
);

if ( field.elements ) {
const elements = [
/*
* Value can be undefined when:
*
* - the field is not required
* - in bulk editing
*
*/
{ label: __( 'Select item' ), value: '' },
...field.elements,
];

return (
<SelectControl
label={ label }
value={ value }
options={ elements }
onChange={ onChangeControl }
__next40pxDefaultSize
__nextHasNoMarginBottom
/>
);
}

return (
<fieldset>
<BaseControl.VisualLabel as="legend">
{ label }
</BaseControl.VisualLabel>
<TimePicker
currentTime={ value }
onChange={ onChangeControl }
hideLabelFromVision
/>
</fieldset>
);
}

export default {
sort,
isValid,
Edit,
};
5 changes: 5 additions & 0 deletions packages/dataviews/src/field-types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type { FieldType, SortDirection, ValidationContext } from '../types';
import { default as integer } from './integer';
import { default as text } from './text';
import { default as datetime } from './datetime';

/**
*
Expand All @@ -20,6 +21,10 @@ export default function getFieldTypeDefinition( type?: FieldType ) {
return text;
}

if ( 'datetime' === type ) {
return datetime;
}

return {
sort: ( a: any, b: any, direction: SortDirection ) => {
if ( typeof a === 'number' && typeof b === 'number' ) {
Expand Down
28 changes: 28 additions & 0 deletions packages/dataviews/src/test/filter-and-sort-data-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,34 @@ describe( 'sorting', () => {
expect( result[ 1 ].title ).toBe( 'Neptune' );
} );

it( 'should sort datetime field types', () => {
const { data: resultDesc } = filterSortAndPaginate(
data,
{
sort: { field: 'date', direction: 'desc' },
},
fields
);
expect( resultDesc ).toHaveLength( 11 );
expect( resultDesc[ 0 ].title ).toBe( 'NASA' );
expect( resultDesc[ 1 ].title ).toBe( 'Earth' );
expect( resultDesc[ 9 ].title ).toBe( 'Space' );
expect( resultDesc[ 10 ].title ).toBe( 'Jupiter' );

const { data: resultAsc } = filterSortAndPaginate(
data,
{
sort: { field: 'date', direction: 'asc' },
},
fields
);
expect( resultAsc ).toHaveLength( 11 );
expect( resultAsc[ 0 ].title ).toBe( 'Jupiter' );
expect( resultAsc[ 1 ].title ).toBe( 'Space' );
expect( resultAsc[ 9 ].title ).toBe( 'Earth' );
expect( resultAsc[ 10 ].title ).toBe( 'NASA' );
} );

it( 'should sort untyped fields if the value is a number', () => {
const { data: result } = filterSortAndPaginate(
data,
Expand Down
2 changes: 1 addition & 1 deletion packages/dataviews/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export type Operator =
| 'isAll'
| 'isNotAll';

export type FieldType = 'text' | 'integer';
export type FieldType = 'text' | 'integer' | 'datetime';

export type ValidationContext = {
elements?: Option[];
Expand Down
Loading

1 comment on commit 0ceac8f

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 0ceac8f.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/10315263934
📝 Reported issues:

Please sign in to comment.