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

Quick edit: add Date as field and datetime as field type #64267

Merged
merged 24 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
902f757
Make date field visible in quick edit
oandregal Aug 5, 2024
903fc09
Render date as button that opens form
oandregal Aug 5, 2024
c93e0c2
Update story and allowed field types
oandregal Aug 6, 2024
f5490ff
Sort by date type
oandregal Aug 6, 2024
34b9c43
Add label and format date
oandregal Aug 6, 2024
f6c999d
Implement date & time controls separately
oandregal Aug 6, 2024
1c0132f
Add todo
oandregal Aug 6, 2024
41e7855
Update date & time properly
oandregal Aug 6, 2024
b6165fb
Pass the previous item to the onChange
oandregal Aug 6, 2024
4a9fa98
Remove unnecessary dependency
oandregal Aug 6, 2024
d2ce63c
Use TimePicker without labels, we will provide one
oandregal Aug 7, 2024
8ceca0d
Add changelog for components
oandregal Aug 7, 2024
95438a5
datetime edit: render as SelectControl if elements is present
oandregal Aug 7, 2024
b92423c
Refactor from withLabels to hideLabelFromVision
oandregal Aug 7, 2024
3f63503
Update changelog
oandregal Aug 7, 2024
594494f
Associate label with the timeinput component
oandregal Aug 7, 2024
020d4a6
Revert "Associate label with the timeinput component"
oandregal Aug 8, 2024
f193826
Use fieldset+legend
oandregal Aug 8, 2024
bd8a773
Fix rebase
oandregal Aug 8, 2024
9d41b3a
Omit hideLabelFromVision from DateTimePickerProps
oandregal Aug 8, 2024
e784559
Changelog: move items to Unreleased section
oandregal Aug 8, 2024
b796aaa
Fix prettier?
oandregal Aug 8, 2024
69946cc
Address feedback: do not export Fieldset, use browser component directly
oandregal Aug 9, 2024
931ece3
Fix rebase
oandregal Aug 9, 2024
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
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;
oandregal marked this conversation as resolved.
Show resolved Hide resolved
};

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 ) {
oandregal marked this conversation as resolved.
Show resolved Hide resolved
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
Loading