Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 packages/dataviews/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Enhancements

- Simplify field normalization and types. [#73387](https://github.com/WordPress/gutenberg/pull/73387)
- DataViews table layout: make checkboxes permanently visible when bulk actions are available. [#73245](https://github.com/WordPress/gutenberg/pull/73245)
- Documentation: surface better the `type` property in the documentation. [#73349](https://github.com/WordPress/gutenberg/pull/73349)
- DataViews: Make sticky elements (table headers, footer, actions column) inherit background colors from parent container. This allows DataViews instances to seamlessly adapt to containers with custom background colors. [#73240](https://github.com/WordPress/gutenberg/pull/73240)
Expand Down
2 changes: 1 addition & 1 deletion packages/dataviews/src/components/dataform/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useMemo } from '@wordpress/element';
*/
import type { DataFormProps } from '../../types';
import { DataFormProvider } from '../dataform-context';
import normalizeFields from '../../utils/normalize-fields';
import normalizeFields from '../../field-types/utils/normalize-fields';
import { DataFormLayout } from '../../dataform-layouts/data-form-layout';
import normalizeForm from '../../dataform-layouts/normalize-form';

Expand Down
12 changes: 8 additions & 4 deletions packages/dataviews/src/components/dataviews-filters/filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import { useRef, createInterpolateElement } from '@wordpress/element';
import { closeSmall } from '@wordpress/icons';
import { dateI18n, getDate } from '@wordpress/date';

const ENTER = 'Enter';
const SPACE = ' ';

/**
* Internal dependencies
*/
Expand Down Expand Up @@ -57,6 +54,7 @@ import {
import type {
Filter,
NormalizedField,
NormalizedFieldDate,
NormalizedFilter,
Operator,
Option,
Expand All @@ -65,6 +63,9 @@ import type {
import useElements from '../../hooks/use-elements';
import parseDateTime from '../../field-types/utils/parse-date-time';

const ENTER = 'Enter';
const SPACE = ' ';

interface FilterTextProps {
activeElements: Option[];
filterInView?: Filter;
Expand Down Expand Up @@ -503,7 +504,10 @@ export default function Filter( {
try {
const dateValue = parseDateTime( label );
if ( dateValue !== null ) {
label = dateI18n( field.format.date, getDate( label ) );
label = dateI18n(
( field as NormalizedFieldDate< any > ).format.date,
getDate( label )
);
}
} catch ( e ) {
label = filterInView.value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import DataViewsViewConfig, {
DataviewsViewConfigDropdown,
ViewTypeMenu,
} from '../dataviews-view-config';
import normalizeFields from '../../utils/normalize-fields';
import normalizeFields from '../../field-types/utils/normalize-fields';
import type { ActionButton, Field, View, SupportedLayouts } from '../../types';
import type { SelectionOrUpdater } from '../../types/private';
type ItemWithId = { id: string };
Expand Down
2 changes: 1 addition & 1 deletion packages/dataviews/src/components/dataviews/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import DataViewsViewConfig, {
DataviewsViewConfigDropdown,
ViewTypeMenu,
} from '../dataviews-view-config';
import normalizeFields from '../../utils/normalize-fields';
import normalizeFields from '../../field-types/utils/normalize-fields';
import type { Action, Field, View, SupportedLayouts } from '../../types';
import type { SelectionOrUpdater } from '../../types/private';
type ItemWithId = { id: string };
Expand Down
17 changes: 13 additions & 4 deletions packages/dataviews/src/dataform-controls/date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ import { unlock } from '../lock-unlock';
import type {
DataFormControlProps,
FieldValidity,
FormatDate,
NormalizedField,
} from '../types';
import getCustomValidity from './utils/get-custom-validity';
import { weekStartsOnToNumber } from '../utils/week-starts-on';
import { weekStartsOnToNumber } from '../field-types/utils/week-starts-on';

const { DateCalendar, DateRangeCalendar } = unlock( componentsPrivateApis );

Expand Down Expand Up @@ -271,9 +272,13 @@ function CalendarDateControl< Item >( {
null
);

let weekStartsOn;
let weekStartsOn = getSettings().l10n.startOfWeek;
if ( type === 'date' ) {
weekStartsOn = weekStartsOnToNumber( fieldFormat.weekStartsOn );
// If the field type is date, we've already normalized the format,
Comment thread
ntsekouras marked this conversation as resolved.
// and so it's safe to tell TypeScript to trust us ("as Required<Format>").
weekStartsOn = weekStartsOnToNumber(
( fieldFormat as Required< FormatDate > ).weekStartsOn
);
}

const fieldValue = getValue( { item: data } );
Expand Down Expand Up @@ -437,7 +442,11 @@ function CalendarDateRangeControl< Item >( {

let weekStartsOn;
if ( type === 'date' ) {
weekStartsOn = weekStartsOnToNumber( fieldFormat.weekStartsOn );
// If the field type is date, we've already normalized the format,
// and so it's safe to tell TypeScript to trust us ("as Required<Format>").
weekStartsOn = weekStartsOnToNumber(
( fieldFormat as Required< FormatDate > ).weekStartsOn
);
}

const onChangeCallback = useCallback(
Expand Down
28 changes: 11 additions & 17 deletions packages/dataviews/src/dataform-controls/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ import type { ComponentType } from 'react';
/**
* Internal dependencies
*/
import type {
DataFormControlProps,
Field,
FieldTypeDefinition,
EditConfig,
} from '../types';
import type { DataFormControlProps, Field, EditConfig } from '../types';
import checkbox from './checkbox';
import datetime from './datetime';
import date from './date';
Expand All @@ -29,7 +24,7 @@ import toggleGroup from './toggle-group';
import array from './array';
import color from './color';
import password from './password';
import hasElements from '../utils/has-elements';
import hasElements from '../field-types/utils/has-elements';

interface FormControls {
[ key: string ]: ComponentType< DataFormControlProps< any > >;
Expand Down Expand Up @@ -64,6 +59,9 @@ function isEditConfig( value: any ): value is EditConfig {
function createConfiguredControl( config: EditConfig ) {
const { control, ...controlConfig } = config;
const BaseControlType = getControlByType( control );
if ( BaseControlType === null ) {
return null;
}

return function ConfiguredControl< Item >(
props: DataFormControlProps< Item >
Expand All @@ -74,8 +72,8 @@ function createConfiguredControl( config: EditConfig ) {

export function getControl< Item >(
field: Field< Item >,
fieldTypeDefinition: FieldTypeDefinition< Item >
) {
fallback: string | null
): ComponentType< DataFormControlProps< Item > > | null {
if ( typeof field.Edit === 'function' ) {
return field.Edit;
}
Expand All @@ -92,21 +90,17 @@ export function getControl< Item >(
return getControlByType( 'select' );
}

if ( typeof fieldTypeDefinition.Edit === 'string' ) {
return getControlByType( fieldTypeDefinition.Edit );
}

if ( isEditConfig( fieldTypeDefinition.Edit ) ) {
return createConfiguredControl( fieldTypeDefinition.Edit );
if ( fallback === null ) {
return null;
}

return fieldTypeDefinition.Edit;
return getControlByType( fallback );
}

export function getControlByType( type: string ) {
if ( Object.keys( FORM_CONTROLS ).includes( type ) ) {
return FORM_CONTROLS[ type ];
}

throw 'Control ' + type + ' not found';
return null;
}
113 changes: 74 additions & 39 deletions packages/dataviews/src/field-types/array.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,66 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
SortDirection,
FieldTypeDefinition,
Field,
NormalizedField,
Operator,
Rules,
SortDirection,
} from '../types';
import {
OPERATOR_IS_ALL,
OPERATOR_IS_ANY,
OPERATOR_IS_NONE,
OPERATOR_IS_NOT_ALL,
} from '../constants';

// Sort arrays by length, then alphabetically by joined string
function sort( valueA: any, valueB: any, direction: SortDirection ) {
const arrA = Array.isArray( valueA ) ? valueA : [];
const arrB = Array.isArray( valueB ) ? valueB : [];
if ( arrA.length !== arrB.length ) {
return direction === 'asc'
? arrA.length - arrB.length
: arrB.length - arrA.length;
}

const joinedA = arrA.join( ',' );
const joinedB = arrB.join( ',' );
return direction === 'asc'
? joinedA.localeCompare( joinedB )
: joinedB.localeCompare( joinedA );
}
import { getControl } from '../dataform-controls';
import hasElements from './utils/has-elements';
import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';

function render( { item, field }: DataViewRenderFieldProps< any > ) {
const value = field.getValue( { item } ) || [];
return value.join( ', ' );
}

const arrayFieldType: FieldTypeDefinition< any > = {
sort,
isValid: {
const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The changes for all field types are about converting a (template) object into a function that returns the actual (normalized) object.

const validOperators: Operator[] = [
OPERATOR_IS_ANY,
OPERATOR_IS_NONE,
OPERATOR_IS_ALL,
OPERATOR_IS_NOT_ALL,
];

export default function normalizeField< Item >(
field: Field< Item >
): NormalizedField< Item > {
const getValue = field.getValue || getValueFromId( field.id );
const setValue = field.setValue || setValueFromId( field.id );

const sort = ( a: any, b: any, direction: SortDirection ) => {
// Sort arrays by length, then alphabetically by joined string
const valueA = getValue( a );
const valueB = getValue( b );
const arrA = Array.isArray( valueA ) ? valueA : [];
const arrB = Array.isArray( valueB ) ? valueB : [];
if ( arrA.length !== arrB.length ) {
return direction === 'asc'
? arrA.length - arrB.length
: arrB.length - arrA.length;
}

const joinedA = arrA.join( ',' );
const joinedB = arrB.join( ',' );
return direction === 'asc'
? joinedA.localeCompare( joinedB )
: joinedB.localeCompare( joinedA );
};

const isValid: Rules< Item > = {
elements: true,
custom: ( item: any, field: NormalizedField< any > ) => {
const value = field.getValue( { item } );
custom: ( item: any, normalizedField ) => {
const value = normalizedField.getValue( { item } );

if (
! [ undefined, '', null ].includes( value ) &&
Expand All @@ -62,19 +83,33 @@ const arrayFieldType: FieldTypeDefinition< any > = {

return null;
},
},
Edit: 'array', // Use array control
render,
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
validOperators: [
OPERATOR_IS_ANY,
OPERATOR_IS_NONE,
OPERATOR_IS_ALL,
OPERATOR_IS_NOT_ALL,
],
},
};
};

export default arrayFieldType;
return {
id: field.id,
type: 'array',
label: field.label || field.id,
header: field.header || field.label || field.id,
description: field.description,
placeholder: field.placeholder,
getValue,
setValue,
elements: field.elements,
getElements: field.getElements,
hasElements: hasElements( field ),
render: field.render ?? render,
Edit: getControl( field, 'array' ),
sort: field.sort ?? sort,
isValid: {
...isValid,
...field.isValid,
},
isVisible: field.isVisible,
enableSorting: field.enableSorting ?? true,
enableGlobalSearch: field.enableGlobalSearch ?? false,
enableHiding: field.enableHiding ?? true,
readOnly: field.readOnly ?? false,
filterBy: getFilterBy( field, defaultOperators, validOperators ),
format: {},
};
}
Loading
Loading