diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index 30cdcb6566e0ab..62601c114ad395 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -31,6 +31,7 @@ ### Breaking changes - DataViews: rename `groupByField` to `groupBy.field` to allow control over both the field and the direction of the grouping. [#72780](https://github.com/WordPress/gutenberg/pull/72780) +- Types: FieldType is now FieldTypeName. [#73546](https://github.com/WordPress/gutenberg/pull/73546) ## 10.3.0 (2025-11-12) diff --git a/packages/dataviews/src/components/dataform/index.tsx b/packages/dataviews/src/components/dataform/index.tsx index 321af6f9302669..65f4aad3fb7c5e 100644 --- a/packages/dataviews/src/components/dataform/index.tsx +++ b/packages/dataviews/src/components/dataform/index.tsx @@ -8,7 +8,7 @@ import { useMemo } from '@wordpress/element'; */ import type { DataFormProps } from '../../types'; import { DataFormProvider } from '../dataform-context'; -import normalizeFields from '../../field-types/utils/normalize-fields'; +import normalizeFields from '../../field-types'; import { DataFormLayout } from '../../dataform-layouts/data-form-layout'; import normalizeForm from '../../dataform-layouts/normalize-form'; diff --git a/packages/dataviews/src/components/dataviews-picker/index.tsx b/packages/dataviews/src/components/dataviews-picker/index.tsx index a97bb40825978f..11e64ee9b21312 100644 --- a/packages/dataviews/src/components/dataviews-picker/index.tsx +++ b/packages/dataviews/src/components/dataviews-picker/index.tsx @@ -29,7 +29,7 @@ import DataViewsViewConfig, { DataviewsViewConfigDropdown, ViewTypeMenu, } from '../dataviews-view-config'; -import normalizeFields from '../../field-types/utils/normalize-fields'; +import normalizeFields from '../../field-types'; import type { ActionButton, Field, View, SupportedLayouts } from '../../types'; import type { SelectionOrUpdater } from '../../types/private'; type ItemWithId = { id: string }; diff --git a/packages/dataviews/src/components/dataviews/index.tsx b/packages/dataviews/src/components/dataviews/index.tsx index 07cda5d91bd9b7..b3200b36b1045a 100644 --- a/packages/dataviews/src/components/dataviews/index.tsx +++ b/packages/dataviews/src/components/dataviews/index.tsx @@ -30,7 +30,7 @@ import DataViewsViewConfig, { DataviewsViewConfigDropdown, ViewTypeMenu, } from '../dataviews-view-config'; -import normalizeFields from '../../field-types/utils/normalize-fields'; +import normalizeFields from '../../field-types'; import type { Action, Field, View, SupportedLayouts } from '../../types'; import type { SelectionOrUpdater } from '../../types/private'; type ItemWithId = { id: string }; diff --git a/packages/dataviews/src/field-types/array.tsx b/packages/dataviews/src/field-types/array.tsx index 50fc08b519a645..4120a15626d29e 100644 --- a/packages/dataviews/src/field-types/array.tsx +++ b/packages/dataviews/src/field-types/array.tsx @@ -6,110 +6,72 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; +import type { DataViewRenderFieldProps, Rules, SortDirection } from '../types'; +import type { FieldType } from '../types/private'; import { OPERATOR_IS_ALL, OPERATOR_IS_ANY, OPERATOR_IS_NONE, OPERATOR_IS_NOT_ALL, } from '../constants'; -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 defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ]; -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 isValid: Rules< any > = { + elements: true, + custom: ( item: any, normalizedField ) => { + const value = normalizedField.getValue( { item } ); - 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; + if ( + ! [ undefined, '', null ].includes( value ) && + ! Array.isArray( value ) + ) { + return __( 'Value must be an array.' ); } - 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, normalizedField ) => { - const value = normalizedField.getValue( { item } ); + // Only allow strings for now. Can be extended to other types in the future. + if ( ! value.every( ( v: any ) => typeof v === 'string' ) ) { + return __( 'Every value must be a string.' ); + } - if ( - ! [ undefined, '', null ].includes( value ) && - ! Array.isArray( value ) - ) { - return __( 'Value must be an array.' ); - } + return null; + }, +}; - // Only allow strings for now. Can be extended to other types in the future. - if ( ! value.every( ( v: any ) => typeof v === 'string' ) ) { - return __( 'Every value must be a string.' ); - } +const sort = ( a: any, b: any, direction: SortDirection ) => { + // Sort arrays by length, then alphabetically by joined string + const arrA = Array.isArray( a ) ? a : []; + const arrB = Array.isArray( b ) ? b : []; + if ( arrA.length !== arrB.length ) { + return direction === 'asc' + ? arrA.length - arrB.length + : arrB.length - arrA.length; + } - return null; - }, - }; + const joinedA = arrA.join( ',' ); + const joinedB = arrB.join( ',' ); + return direction === 'asc' + ? joinedA.localeCompare( joinedB ) + : joinedB.localeCompare( joinedA ); +}; - 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: {}, - }; -} +export default { + type: 'array', + render, + Edit: 'array', + sort, + isValid, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ], + validOperators: [ + OPERATOR_IS_ANY, + OPERATOR_IS_NONE, + OPERATOR_IS_ALL, + OPERATOR_IS_NOT_ALL, + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/boolean.tsx b/packages/dataviews/src/field-types/boolean.tsx index 3c1ab3059f4dd6..7e2275e2ae7880 100644 --- a/packages/dataviews/src/field-types/boolean.tsx +++ b/packages/dataviews/src/field-types/boolean.tsx @@ -6,21 +6,10 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; +import type { DataViewRenderFieldProps, Rules, SortDirection } from '../types'; +import type { FieldType } from '../types/private'; import RenderFromElements from './utils/render-from-elements'; import { OPERATOR_IS, OPERATOR_IS_NOT } from '../constants'; -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 > ) { if ( field.hasElements ) { @@ -38,76 +27,48 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) { return null; } -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 isValid: Rules< any > = { + elements: true, + custom: ( item: any, normalizedField ) => { + const value = normalizedField.getValue( { item } ); - const sort = ( a: any, b: any, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - const boolA = Boolean( valueA ); - const boolB = Boolean( valueB ); - - if ( boolA === boolB ) { - return 0; - } - - // In ascending order, false comes before true - if ( direction === 'asc' ) { - return boolA ? 1 : -1; + if ( + ! [ undefined, '', null ].includes( value ) && + ! [ true, false ].includes( value ) + ) { + return __( 'Value must be true, false, or undefined' ); } - // In descending order, true comes before false - return boolA ? -1 : 1; - }; - - const isValid: Rules< Item > = { - elements: true, - custom: ( item: any, normalizedField ) => { - const value = normalizedField.getValue( { item } ); + return null; + }, +}; - if ( - ! [ undefined, '', null ].includes( value ) && - ! [ true, false ].includes( value ) - ) { - return __( 'Value must be true, false, or undefined' ); - } +const sort = ( a: any, b: any, direction: SortDirection ) => { + const boolA = Boolean( a ); + const boolB = Boolean( b ); - return null; - }, - }; - - const defaultOperators: Operator[] = [ OPERATOR_IS, OPERATOR_IS_NOT ]; + if ( boolA === boolB ) { + return 0; + } - const validOperators: Operator[] = [ OPERATOR_IS, OPERATOR_IS_NOT ]; + // In ascending order, false comes before true + if ( direction === 'asc' ) { + return boolA ? 1 : -1; + } - return { - id: field.id, - type: 'boolean', - 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, 'checkbox' ), - 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: {}, - }; -} + // In descending order, true comes before false + return boolA ? -1 : 1; +}; + +export default { + type: 'boolean', + render, + Edit: 'checkbox', + sort, + isValid, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ], + validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/color.tsx b/packages/dataviews/src/field-types/color.tsx index 907c62f8ee0620..2aed013d600bd7 100644 --- a/packages/dataviews/src/field-types/color.tsx +++ b/packages/dataviews/src/field-types/color.tsx @@ -11,14 +11,8 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; +import type { DataViewRenderFieldProps, Rules, SortDirection } from '../types'; +import type { FieldType } from '../types/private'; import RenderFromElements from './utils/render-from-elements'; import { OPERATOR_IS, @@ -26,11 +20,6 @@ import { OPERATOR_IS_NONE, OPERATOR_IS_NOT, } from '../constants'; -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 > ) { if ( field.hasElements ) { @@ -61,90 +50,64 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) { ); } -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 isValid: Rules< any > = { + elements: true, + custom: ( item: any, normalizedField ) => { + const value = normalizedField.getValue( { item } ); - const sort = ( valueA: any, valueB: any, direction: SortDirection ) => { - // Convert colors to HSL for better sorting - const colorA = colord( valueA ); - const colorB = colord( valueB ); - - if ( ! colorA.isValid() && ! colorB.isValid() ) { - return 0; - } - if ( ! colorA.isValid() ) { - return direction === 'asc' ? 1 : -1; - } - if ( ! colorB.isValid() ) { - return direction === 'asc' ? -1 : 1; - } - - // Sort by hue, then saturation, then lightness - const hslA = colorA.toHsl(); - const hslB = colorB.toHsl(); - - if ( hslA.h !== hslB.h ) { - return direction === 'asc' ? hslA.h - hslB.h : hslB.h - hslA.h; - } - if ( hslA.s !== hslB.s ) { - return direction === 'asc' ? hslA.s - hslB.s : hslB.s - hslA.s; + if ( + ! [ undefined, '', null ].includes( value ) && + ! colord( value ).isValid() + ) { + return __( 'Value must be a valid color.' ); } - return direction === 'asc' ? hslA.l - hslB.l : hslB.l - hslA.l; - }; - const isValid: Rules< Item > = { - elements: true, - custom: ( item: any, normalizedField ) => { - const value = normalizedField.getValue( { item } ); + return null; + }, +}; - if ( - ! [ undefined, '', null ].includes( value ) && - ! colord( value ).isValid() - ) { - return __( 'Value must be a valid color.' ); - } +const sort = ( a: any, b: any, direction: SortDirection ) => { + // Convert colors to HSL for better sorting + const colorA = colord( a ); + const colorB = colord( b ); - return null; - }, - }; + if ( ! colorA.isValid() && ! colorB.isValid() ) { + return 0; + } + if ( ! colorA.isValid() ) { + return direction === 'asc' ? 1 : -1; + } + if ( ! colorB.isValid() ) { + return direction === 'asc' ? -1 : 1; + } - const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ]; + // Sort by hue, then saturation, then lightness + const hslA = colorA.toHsl(); + const hslB = colorB.toHsl(); - const validOperators: Operator[] = [ + if ( hslA.h !== hslB.h ) { + return direction === 'asc' ? hslA.h - hslB.h : hslB.h - hslA.h; + } + if ( hslA.s !== hslB.s ) { + return direction === 'asc' ? hslA.s - hslB.s : hslB.s - hslA.s; + } + return direction === 'asc' ? hslA.l - hslB.l : hslB.l - hslA.l; +}; + +export default { + type: 'color', + render, + Edit: 'color', + sort, + isValid, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ], + validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT, OPERATOR_IS_ANY, OPERATOR_IS_NONE, - ]; - - return { - id: field.id, - type: 'color', - 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, 'color' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/date.tsx b/packages/dataviews/src/field-types/date.tsx index dcd8b7d513e08c..48aacaa28eb845 100644 --- a/packages/dataviews/src/field-types/date.tsx +++ b/packages/dataviews/src/field-types/date.tsx @@ -10,11 +10,9 @@ import type { DataViewRenderFieldProps, Field, FormatDate, - NormalizedField, - Operator, - Rules, SortDirection, } from '../types'; +import type { FieldType } from '../types/private'; import RenderFromElements from './utils/render-from-elements'; import { OPERATOR_ON, @@ -28,13 +26,8 @@ import { OPERATOR_BETWEEN, DAYS_OF_WEEK, } from '../constants'; -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 getFormat( field: Field< any > ): Required< FormatDate > { +function getFormat< Item >( field: Field< Item > ): Required< FormatDate > { return { date: field.format?.date !== undefined && @@ -74,26 +67,25 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) { return dateI18n( format.date, getDate( value ) ); } -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 isValid: Rules< Item > = { - elements: true, - custom: () => null, - }; +const sort = ( a: any, b: any, direction: SortDirection ) => { + const timeA = new Date( a ).getTime(); + const timeB = new Date( b ).getTime(); - const sort = ( a: Item, b: Item, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - const timeA = new Date( valueA ).getTime(); - const timeB = new Date( valueB ).getTime(); + return direction === 'asc' ? timeA - timeB : timeB - timeA; +}; - return direction === 'asc' ? timeA - timeB : timeB - timeA; - }; - - const defaultOperators: Operator[] = [ +export default { + type: 'date', + render, + Edit: 'date', + sort, + isValid: { + elements: true, + custom: () => null, + }, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_ON, OPERATOR_NOT_ON, OPERATOR_BEFORE, @@ -103,9 +95,8 @@ export default function normalizeField< Item >( OPERATOR_IN_THE_PAST, OPERATOR_OVER, OPERATOR_BETWEEN, - ]; - - const validOperators: Operator[] = [ + ], + validOperators: [ OPERATOR_ON, OPERATOR_NOT_ON, OPERATOR_BEFORE, @@ -115,33 +106,6 @@ export default function normalizeField< Item >( OPERATOR_IN_THE_PAST, OPERATOR_OVER, OPERATOR_BETWEEN, - ]; - - return { - id: field.id, - type: 'date', - 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, 'date' ), - 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: getFormat( field ), - }; -} + ], + getFormat, +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/datetime.tsx b/packages/dataviews/src/field-types/datetime.tsx index bdf424c1ec57a3..6edd6eb74e66cb 100644 --- a/packages/dataviews/src/field-types/datetime.tsx +++ b/packages/dataviews/src/field-types/datetime.tsx @@ -1,14 +1,8 @@ /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; +import type { DataViewRenderFieldProps, SortDirection } from '../types'; +import type { FieldType } from '../types/private'; import RenderFromElements from './utils/render-from-elements'; import parseDateTime from './utils/parse-date-time'; import { @@ -21,11 +15,6 @@ import { OPERATOR_IN_THE_PAST, OPERATOR_OVER, } from '../constants'; -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 > ) { if ( field.elements ) { @@ -45,26 +34,25 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) { } } -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 isValid: Rules< Item > = { - elements: true, - custom: () => null, - }; +const sort = ( a: any, b: any, direction: SortDirection ) => { + const timeA = new Date( a ).getTime(); + const timeB = new Date( b ).getTime(); - const sort = ( a: Item, b: Item, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - const timeA = new Date( valueA ).getTime(); - const timeB = new Date( valueB ).getTime(); + return direction === 'asc' ? timeA - timeB : timeB - timeA; +}; - return direction === 'asc' ? timeA - timeB : timeB - timeA; - }; - - const defaultOperators: Operator[] = [ +export default { + type: 'datetime', + render, + Edit: 'datetime', + sort, + isValid: { + elements: true, + custom: () => null, + }, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_ON, OPERATOR_NOT_ON, OPERATOR_BEFORE, @@ -73,9 +61,8 @@ export default function normalizeField< Item >( OPERATOR_AFTER_INC, OPERATOR_IN_THE_PAST, OPERATOR_OVER, - ]; - - const validOperators: Operator[] = [ + ], + validOperators: [ OPERATOR_ON, OPERATOR_NOT_ON, OPERATOR_BEFORE, @@ -84,33 +71,6 @@ export default function normalizeField< Item >( OPERATOR_AFTER_INC, OPERATOR_IN_THE_PAST, OPERATOR_OVER, - ]; - - return { - id: field.id, - type: 'datetime', - 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, 'datetime' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/email.tsx b/packages/dataviews/src/field-types/email.tsx index ad4e03fa6b0f49..dfb1f85f3eea85 100644 --- a/packages/dataviews/src/field-types/email.tsx +++ b/packages/dataviews/src/field-types/email.tsx @@ -6,15 +6,8 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; -import RenderFromElements from './utils/render-from-elements'; +import type { Rules } from '../types'; +import type { FieldType } from '../types/private'; import { OPERATOR_IS, OPERATOR_IS_ALL, @@ -26,58 +19,40 @@ import { OPERATOR_NOT_CONTAINS, OPERATOR_STARTS_WITH, } from '../constants'; -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'; +import render from './utils/render-default'; +import sort from './utils/sort-text'; // Email validation regex based on HTML5 spec // https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address const emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; -function render( { item, field }: DataViewRenderFieldProps< any > ) { - return field.hasElements ? ( - - ) : ( - field.getValue( { item } ) - ); -} +const isValid: Rules< any > = { + elements: true, + custom: ( item: any, normalizedField ) => { + const value = normalizedField.getValue( { item } ); -export default function normalizeField< Item >( - field: Field< Item > -): NormalizedField< Item > { - const getValue = field.getValue || getValueFromId( field.id ); - const setValue = field.setValue || setValueFromId( field.id ); + if ( + ! [ undefined, '', null ].includes( value ) && + ! emailRegex.test( value ) + ) { + return __( 'Value must be a valid email address.' ); + } - const sort = ( a: any, b: any, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - return direction === 'asc' - ? valueA.localeCompare( valueB ) - : valueB.localeCompare( valueA ); - }; + return null; + }, +}; - const isValid: Rules< Item > = { - elements: true, - custom: ( item: any, normalizedField ) => { - const value = normalizedField.getValue( { item } ); - - if ( - ! [ undefined, '', null ].includes( value ) && - ! emailRegex.test( value ) - ) { - return __( 'Value must be a valid email address.' ); - } - - return null; - }, - }; - - const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ]; - - const validOperators: Operator[] = [ +export default { + type: 'email', + render, + Edit: 'email', + sort, + isValid, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ], + validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT, OPERATOR_CONTAINS, @@ -88,33 +63,6 @@ export default function normalizeField< Item >( OPERATOR_IS_NONE, OPERATOR_IS_ALL, OPERATOR_IS_NOT_ALL, - ]; - - return { - id: field.id, - type: 'email', - 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, 'email' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/index.tsx b/packages/dataviews/src/field-types/index.tsx index 4534a2ff3e6d6f..70ced3904843b8 100644 --- a/packages/dataviews/src/field-types/index.tsx +++ b/packages/dataviews/src/field-types/index.tsx @@ -2,13 +2,17 @@ * Internal dependencies */ import type { - DataViewRenderFieldProps, Field, - FieldType, + FieldTypeName, NormalizedField, - Operator, SortDirection, } from '../types'; +import type { FieldType } from '../types/private'; +import { getControl } from '../dataform-controls'; +import getFilterBy from './utils/get-filter-by'; +import getValueFromId from './utils/get-value-from-id'; +import hasElements from './utils/has-elements'; +import setValueFromId from './utils/set-value-from-id'; import { default as email } from './email'; import { default as integer } from './integer'; import { default as number } from './number'; @@ -22,143 +26,93 @@ import { default as password } from './password'; import { default as telephone } from './telephone'; import { default as color } from './color'; import { default as url } from './url'; -import RenderFromElements from './utils/render-from-elements'; -import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from '../constants'; -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 normalizeField< Item >( - field: Field< Item > -): NormalizedField< Item > { - const getValue = field.getValue || getValueFromId( field.id ); - const setValue = field.setValue || setValueFromId( field.id ); - - const isValid = { - elements: true, - custom: () => null, - }; - - const sort = ( a: any, b: any, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - - if ( typeof valueA === 'number' && typeof valueB === 'number' ) { - return direction === 'asc' ? valueA - valueB : valueB - valueA; - } - - return direction === 'asc' - ? valueA.localeCompare( valueB ) - : valueB.localeCompare( valueA ); - }; - - const render = ( { - item, - field: normalizedField, - }: DataViewRenderFieldProps< Item > ) => { - return normalizedField.hasElements ? ( - - ) : ( - normalizedField.getValue( { item } ) - ); - }; - - const defaultOperators: Operator[] = [ OPERATOR_IS, OPERATOR_IS_NOT ]; - const validOperators: Operator[] = ALL_OPERATORS; - - return { - id: field.id, - // type — it does not have a type - 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, null ), - 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: {}, - }; -} +import { default as noType } from './no-type'; /** * - * @param {FieldType} type The field type definition to get. + * @param {FieldTypeName} type The field type definition to get. * * @return A field type definition. */ -export default function getNormalizeFieldFunction< Item >( - type?: FieldType -): ( field: Field< Item > ) => NormalizedField< Item > { - if ( 'email' === type ) { - return email; - } - - if ( 'integer' === type ) { - return integer; - } - - if ( 'number' === type ) { - return number; - } - - if ( 'text' === type ) { - return text; - } - - if ( 'datetime' === type ) { - return datetime; - } - - if ( 'date' === type ) { - return date; - } - - if ( 'boolean' === type ) { - return boolean; - } - - if ( 'media' === type ) { - return media; - } - - if ( 'array' === type ) { - return array; - } - - if ( 'password' === type ) { - return password; - } - - if ( 'telephone' === type ) { - return telephone; - } - - if ( 'color' === type ) { - return color; - } - - if ( 'url' === type ) { - return url; +function getFieldTypeByName< Item >( type?: FieldTypeName ): FieldType< Item > { + const found = [ + email, + integer, + number, + text, + datetime, + date, + boolean, + media, + array, + password, + telephone, + color, + url, + ].find( ( fieldType ) => fieldType?.type === type ); + + if ( !! found ) { + return found; } // This is a fallback for fields that don't provide a type. - // It can be removed when the field.type is mandatory. - return normalizeField; + // It can be removed when/if the field.type becomes mandatory. + return noType; +} + +/** + * Apply default values and normalize the fields config. + * + * @param fields Fields config. + * @return Normalized fields config. + */ +export default function normalizeFields< Item >( + fields: Field< Item >[] +): NormalizedField< Item >[] { + return fields.map( ( field ) => { + const defaultProps = getFieldTypeByName< Item >( field.type ); + + const getValue = field.getValue || getValueFromId( field.id ); + const sort = function ( a: any, b: any, direction: SortDirection ) { + const aValue = getValue( { item: a } ); + const bValue = getValue( { item: b } ); + return field.sort + ? field.sort( aValue, bValue, direction ) + : defaultProps.sort( aValue, bValue, direction ); + }; + + return { + id: field.id, + label: field.label || field.id, + header: field.header || field.label || field.id, + description: field.description, + placeholder: field.placeholder, + getValue, + setValue: field.setValue || setValueFromId( field.id ), + elements: field.elements, + getElements: field.getElements, + hasElements: hasElements( field ), + isVisible: field.isVisible, + enableHiding: field.enableHiding ?? true, + readOnly: field.readOnly ?? false, + // The type provides defaults for the following props + type: defaultProps.type, + render: field.render ?? defaultProps.render, + Edit: getControl( field, defaultProps.Edit ), + sort, + enableSorting: field.enableSorting ?? defaultProps.enableSorting, + enableGlobalSearch: + field.enableGlobalSearch ?? defaultProps.enableGlobalSearch, + isValid: { + ...defaultProps.isValid, + ...field.isValid, + }, + filterBy: getFilterBy( + field, + defaultProps.defaultOperators, + defaultProps.validOperators + ), + format: defaultProps.getFormat( field ), + }; + } ); } diff --git a/packages/dataviews/src/field-types/integer.tsx b/packages/dataviews/src/field-types/integer.tsx index 19864a1fab7ae8..96b9d1e17b8dd1 100644 --- a/packages/dataviews/src/field-types/integer.tsx +++ b/packages/dataviews/src/field-types/integer.tsx @@ -6,15 +6,8 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; -import RenderFromElements from './utils/render-from-elements'; +import type { Rules } from '../types'; +import type { FieldType } from '../types/private'; import { OPERATOR_IS, OPERATOR_IS_NOT, @@ -28,47 +21,33 @@ import { OPERATOR_IS_NOT_ALL, OPERATOR_BETWEEN, } from '../constants'; -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'; +import render from './utils/render-default'; +import sort from './utils/sort-number'; -function render( { item, field }: DataViewRenderFieldProps< any > ) { - return field.hasElements ? ( - - ) : ( - field.getValue( { item } ) - ); -} +const isValid: Rules< any > = { + elements: true, + custom: ( item: any, normalizedField ) => { + const value = normalizedField.getValue( { item } ); + if ( + ! [ undefined, '', null ].includes( value ) && + ! Number.isInteger( value ) + ) { + return __( 'Value must be an integer.' ); + } -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 isValid: Rules< Item > = { - elements: true, - custom: ( item: any, normalizedField ) => { - const value = normalizedField.getValue( { item } ); - if ( - ! [ undefined, '', null ].includes( value ) && - ! Number.isInteger( value ) - ) { - return __( 'Value must be an integer.' ); - } + return null; + }, +}; - return null; - }, - }; - - const sort = ( a: Item, b: Item, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - return direction === 'asc' ? valueA - valueB : valueB - valueA; - }; - - const defaultOperators: Operator[] = [ +export default { + type: 'integer', + render, + Edit: 'integer', + sort, + isValid, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT, OPERATOR_LESS_THAN, @@ -76,9 +55,8 @@ export default function normalizeField< Item >( OPERATOR_LESS_THAN_OR_EQUAL, OPERATOR_GREATER_THAN_OR_EQUAL, OPERATOR_BETWEEN, - ]; - - const validOperators: Operator[] = [ + ], + validOperators: [ // Single-selection OPERATOR_IS, OPERATOR_IS_NOT, @@ -92,33 +70,6 @@ export default function normalizeField< Item >( OPERATOR_IS_NONE, OPERATOR_IS_ALL, OPERATOR_IS_NOT_ALL, - ]; - - return { - id: field.id, - type: 'integer', - 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, 'integer' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/media.tsx b/packages/dataviews/src/field-types/media.tsx index 6e2ba665110189..9e668b7ca22437 100644 --- a/packages/dataviews/src/field-types/media.tsx +++ b/packages/dataviews/src/field-types/media.tsx @@ -1,55 +1,20 @@ /** * Internal dependencies */ -import type { Field, NormalizedField, Rules } from '../types'; -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 type { FieldType } from '../types/private'; -function sort() { - return 0; -} - -function render() { - return null; -} - -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 isValid: Rules< Item > = { +export default { + type: 'media', + render: () => null, + Edit: null, + sort: () => 0, + isValid: { elements: true, custom: () => null, - }; - - return { - id: field.id, - type: 'media', - 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, null ), - sort: field.sort ?? sort, - isValid: { - ...isValid, - ...field.isValid, - }, - isVisible: field.isVisible, - enableSorting: field.enableSorting ?? false, - enableGlobalSearch: field.enableGlobalSearch ?? false, - enableHiding: field.enableHiding ?? true, - readOnly: field.readOnly ?? false, - filterBy: false, - format: {}, - }; -} + }, + enableSorting: false, + enableGlobalSearch: false, + defaultOperators: [], + validOperators: [], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/no-type.tsx b/packages/dataviews/src/field-types/no-type.tsx new file mode 100644 index 00000000000000..f9c978559b9e59 --- /dev/null +++ b/packages/dataviews/src/field-types/no-type.tsx @@ -0,0 +1,33 @@ +/** + * Internal dependencies + */ +import type { SortDirection } from '../types'; +import type { FieldType } from '../types/private'; +import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from '../constants'; +import render from './utils/render-default'; +import sortText from './utils/sort-text'; +import sortNumber from './utils/sort-number'; + +const sort = ( a: any, b: any, direction: SortDirection ) => { + if ( typeof a === 'number' && typeof b === 'number' ) { + return sortNumber( a, b, direction ); + } + + return sortText( a, b, direction ); +}; + +export default { + // type: no type for this one + render, + Edit: null, + sort, + isValid: { + elements: true, + custom: () => null, + }, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ], + validOperators: ALL_OPERATORS, + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/number.tsx b/packages/dataviews/src/field-types/number.tsx index ca6d54fbeb352e..15e5ea29870c04 100644 --- a/packages/dataviews/src/field-types/number.tsx +++ b/packages/dataviews/src/field-types/number.tsx @@ -6,14 +6,8 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; +import type { DataViewRenderFieldProps, Rules } from '../types'; +import type { FieldType } from '../types/private'; import { OPERATOR_IS, OPERATOR_IS_NOT, @@ -28,11 +22,7 @@ import { OPERATOR_BETWEEN, } from '../constants'; import RenderFromElements from './utils/render-from-elements'; -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'; +import sort from './utils/sort-number'; function isEmpty( value: unknown ): value is '' | undefined | null { return value === '' || value === undefined || value === null; @@ -51,31 +41,28 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) { return null; } -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 isValid: Rules< Item > = { - elements: true, - custom: ( item: any, normalizedField ) => { - const value = normalizedField.getValue( { item } ); +const isValid: Rules< any > = { + elements: true, + custom: ( item: any, normalizedField ) => { + const value = normalizedField.getValue( { item } ); - if ( ! isEmpty( value ) && ! Number.isFinite( value ) ) { - return __( 'Value must be a number.' ); - } + if ( ! isEmpty( value ) && ! Number.isFinite( value ) ) { + return __( 'Value must be a number.' ); + } - return null; - }, - }; + return null; + }, +}; - const sort = ( a: Item, b: Item, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - return direction === 'asc' ? valueA - valueB : valueB - valueA; - }; - - const defaultOperators: Operator[] = [ +export default { + type: 'number', + render, + Edit: 'number', + sort, + isValid, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT, OPERATOR_LESS_THAN, @@ -83,9 +70,8 @@ export default function normalizeField< Item >( OPERATOR_LESS_THAN_OR_EQUAL, OPERATOR_GREATER_THAN_OR_EQUAL, OPERATOR_BETWEEN, - ]; - - const validOperators: Operator[] = [ + ], + validOperators: [ // Single-selection OPERATOR_IS, OPERATOR_IS_NOT, @@ -99,33 +85,6 @@ export default function normalizeField< Item >( OPERATOR_IS_NONE, OPERATOR_IS_ALL, OPERATOR_IS_NOT_ALL, - ]; - - return { - id: field.id, - type: 'number', - 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, 'number' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/password.tsx b/packages/dataviews/src/field-types/password.tsx index ec8c5ea8dc5758..b091998e183026 100644 --- a/packages/dataviews/src/field-types/password.tsx +++ b/packages/dataviews/src/field-types/password.tsx @@ -1,24 +1,9 @@ /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Rules, - SortDirection, -} from '../types'; +import type { DataViewRenderFieldProps } from '../types'; +import type { FieldType } from '../types/private'; import RenderFromElements from './utils/render-from-elements'; -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'; - -/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ -function sort( _valueA: any, _valueB: any, _direction: SortDirection ) { - // Passwords should not be sortable for security reasons - return 0; -} function render( { item, field }: DataViewRenderFieldProps< any > ) { return field.hasElements ? ( @@ -28,41 +13,18 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) { ); } -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 isValid: Rules< Item > = { +export default { + type: 'password', + render, + Edit: 'password', + sort: () => 0, // Passwords should not be sortable for security reasons + isValid: { elements: true, custom: () => null, - }; - - return { - id: field.id, - type: 'password', - 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, 'password' ), - sort: field.sort ?? sort, - isValid: { - ...isValid, - ...field.isValid, - }, - isVisible: field.isVisible, - enableSorting: field.enableSorting ?? false, - enableGlobalSearch: field.enableGlobalSearch ?? false, - enableHiding: field.enableHiding ?? true, - readOnly: field.readOnly ?? false, - filterBy: false, - format: {}, - }; -} + }, + enableSorting: false, + enableGlobalSearch: false, + defaultOperators: [], + validOperators: [], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/telephone.tsx b/packages/dataviews/src/field-types/telephone.tsx index 19d2040c820de0..545a324c34164d 100644 --- a/packages/dataviews/src/field-types/telephone.tsx +++ b/packages/dataviews/src/field-types/telephone.tsx @@ -1,15 +1,7 @@ /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; -import RenderFromElements from './utils/render-from-elements'; +import type { FieldType } from '../types/private'; import { OPERATOR_IS, OPERATOR_IS_ALL, @@ -21,41 +13,22 @@ import { OPERATOR_NOT_CONTAINS, OPERATOR_STARTS_WITH, } from '../constants'; -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'; +import render from './utils/render-default'; +import sort from './utils/sort-text'; -function render( { item, field }: DataViewRenderFieldProps< any > ) { - return field.hasElements ? ( - - ) : ( - field.getValue( { item } ) - ); -} - -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 isValid: Rules< Item > = { +export default { + type: 'telephone', + render, + Edit: 'telephone', + sort, + isValid: { elements: true, custom: () => null, - }; - - const sort = ( a: any, b: any, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - return direction === 'asc' - ? valueA.localeCompare( valueB ) - : valueB.localeCompare( valueA ); - }; - - const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ]; - - const validOperators: Operator[] = [ + }, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ], + validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT, OPERATOR_CONTAINS, @@ -66,33 +39,6 @@ export default function normalizeField< Item >( OPERATOR_IS_NONE, OPERATOR_IS_ALL, OPERATOR_IS_NOT_ALL, - ]; - - return { - id: field.id, - type: 'telephone', - 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, 'telephone' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/text.tsx b/packages/dataviews/src/field-types/text.tsx index 036863ff8ad058..1c1c8c17e2bf58 100644 --- a/packages/dataviews/src/field-types/text.tsx +++ b/packages/dataviews/src/field-types/text.tsx @@ -1,15 +1,7 @@ /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; -import RenderFromElements from './utils/render-from-elements'; +import type { FieldType } from '../types/private'; import { OPERATOR_CONTAINS, OPERATOR_IS, @@ -21,41 +13,22 @@ import { OPERATOR_NOT_CONTAINS, OPERATOR_STARTS_WITH, } from '../constants'; -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'; +import render from './utils/render-default'; +import sort from './utils/sort-text'; -function render( { item, field }: DataViewRenderFieldProps< any > ) { - return field.hasElements ? ( - - ) : ( - field.getValue( { item } ) - ); -} - -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 isValid: Rules< Item > = { +export default { + type: 'text', + render, + Edit: 'text', + sort, + isValid: { elements: true, custom: () => null, - }; - - const sort = ( a: Item, b: Item, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - return direction === 'asc' - ? valueA.localeCompare( valueB ) - : valueB.localeCompare( valueA ); - }; - - const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ]; - - const validOperators: Operator[] = [ + }, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ], + validOperators: [ // Single selection OPERATOR_IS, OPERATOR_IS_NOT, @@ -67,33 +40,6 @@ export default function normalizeField< Item >( OPERATOR_IS_NONE, OPERATOR_IS_ALL, OPERATOR_IS_NOT_ALL, - ]; - - return { - id: field.id, - type: 'text', - 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, 'text' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/url.tsx b/packages/dataviews/src/field-types/url.tsx index d43dcfd885e57f..efcd07ab21143a 100644 --- a/packages/dataviews/src/field-types/url.tsx +++ b/packages/dataviews/src/field-types/url.tsx @@ -1,15 +1,7 @@ /** * Internal dependencies */ -import type { - DataViewRenderFieldProps, - Field, - NormalizedField, - Operator, - Rules, - SortDirection, -} from '../types'; -import RenderFromElements from './utils/render-from-elements'; +import type { FieldType } from '../types/private'; import { OPERATOR_IS, OPERATOR_IS_ALL, @@ -21,41 +13,22 @@ import { OPERATOR_NOT_CONTAINS, OPERATOR_STARTS_WITH, } from '../constants'; -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'; +import render from './utils/render-default'; +import sort from './utils/sort-text'; -function render( { item, field }: DataViewRenderFieldProps< any > ) { - return field.hasElements ? ( - - ) : ( - field.getValue( { item } ) - ); -} - -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 isValid: Rules< Item > = { +export default { + type: 'url', + render, + Edit: 'url', + sort, + isValid: { elements: true, custom: () => null, - }; - - const sort = ( a: any, b: any, direction: SortDirection ) => { - const valueA = getValue( { item: a } ); - const valueB = getValue( { item: b } ); - return direction === 'asc' - ? valueA.localeCompare( valueB ) - : valueB.localeCompare( valueA ); - }; - - const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ]; - - const validOperators: Operator[] = [ + }, + enableSorting: true, + enableGlobalSearch: false, + defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ], + validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT, OPERATOR_CONTAINS, @@ -66,33 +39,6 @@ export default function normalizeField< Item >( OPERATOR_IS_NONE, OPERATOR_IS_ALL, OPERATOR_IS_NOT_ALL, - ]; - - return { - id: field.id, - type: 'url', - 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, 'url' ), - 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: {}, - }; -} + ], + getFormat: () => ( {} ), +} satisfies FieldType< any >; diff --git a/packages/dataviews/src/field-types/utils/get-filter-by.ts b/packages/dataviews/src/field-types/utils/get-filter-by.ts index 333ed12b959365..f26ea4b5caa7e3 100644 --- a/packages/dataviews/src/field-types/utils/get-filter-by.ts +++ b/packages/dataviews/src/field-types/utils/get-filter-by.ts @@ -12,38 +12,20 @@ function getFilterBy< Item >( return false; } - if ( typeof field.filterBy === 'object' ) { - let operators = field.filterBy.operators; - - // Assign default values if no operator was provided. - if ( ! operators || ! Array.isArray( operators ) ) { - operators = defaultOperators; - } - - // Make sure only valid operators are included. - operators = operators.filter( ( operator ) => - validOperators.includes( operator ) - ); - - // If no operators are left at this point, - // the filters should be disabled. - if ( operators.length === 0 ) { - return false; - } - - return { - isPrimary: !! field.filterBy.isPrimary, - operators, - }; - } - - if ( defaultOperators.length === 0 ) { + const operators = + field.filterBy?.operators?.filter( ( op ) => + validOperators.includes( op ) + ) ?? defaultOperators; + + // If no operators are left at this point, + // the filters should be disabled. + if ( operators.length === 0 ) { return false; } return { - isPrimary: false, - operators: defaultOperators, + isPrimary: !! field.filterBy?.isPrimary, + operators, }; } diff --git a/packages/dataviews/src/field-types/utils/normalize-fields.ts b/packages/dataviews/src/field-types/utils/normalize-fields.ts deleted file mode 100644 index 0abb1f55f7332c..00000000000000 --- a/packages/dataviews/src/field-types/utils/normalize-fields.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * External dependencies - */ - -/** - * Internal dependencies - */ -import getNormalizeFieldFunction from '..'; -import type { Field, NormalizedField } from '../../types'; - -/** - * Apply default values and normalize the fields config. - * - * @param fields Fields config. - * @return Normalized fields config. - */ -export default function normalizeFields< Item >( - fields: Field< Item >[] -): NormalizedField< Item >[] { - return fields.map( ( field ) => { - const normalize = getNormalizeFieldFunction< Item >( field.type ); - - return normalize( field ); - } ); -} diff --git a/packages/dataviews/src/field-types/utils/render-default.tsx b/packages/dataviews/src/field-types/utils/render-default.tsx new file mode 100644 index 00000000000000..bb9ad44589ebad --- /dev/null +++ b/packages/dataviews/src/field-types/utils/render-default.tsx @@ -0,0 +1,16 @@ +/** + * Internal dependencies + */ +import type { DataViewRenderFieldProps } from '../../types'; +import RenderFromElements from './render-from-elements'; + +export default function render( { + item, + field, +}: DataViewRenderFieldProps< any > ) { + return field.hasElements ? ( + + ) : ( + field.getValue( { item } ) + ); +} diff --git a/packages/dataviews/src/field-types/utils/sort-number.ts b/packages/dataviews/src/field-types/utils/sort-number.ts new file mode 100644 index 00000000000000..1cdff01d963857 --- /dev/null +++ b/packages/dataviews/src/field-types/utils/sort-number.ts @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +import type { SortDirection } from '../../types'; + +export default ( a: any, b: any, direction: SortDirection ) => { + return direction === 'asc' ? a - b : b - a; +}; diff --git a/packages/dataviews/src/field-types/utils/sort-text.ts b/packages/dataviews/src/field-types/utils/sort-text.ts new file mode 100644 index 00000000000000..ba4d55dfb60909 --- /dev/null +++ b/packages/dataviews/src/field-types/utils/sort-text.ts @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +import type { SortDirection } from '../../types'; + +export default ( a: any, b: any, direction: SortDirection ) => { + return direction === 'asc' ? a.localeCompare( b ) : b.localeCompare( a ); +}; diff --git a/packages/dataviews/src/hooks/use-form-validity.ts b/packages/dataviews/src/hooks/use-form-validity.ts index 27bf9d5f5937bf..d068bfca50ceee 100644 --- a/packages/dataviews/src/hooks/use-form-validity.ts +++ b/packages/dataviews/src/hooks/use-form-validity.ts @@ -13,7 +13,7 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import normalizeFields from '../field-types/utils/normalize-fields'; +import normalizeFields from '../field-types'; import normalizeForm from '../dataform-layouts/normalize-form'; import type { Field, diff --git a/packages/dataviews/src/test/normalize-fields.ts b/packages/dataviews/src/test/normalize-fields.ts index 807f615841ed38..b38b9b5fcac3d1 100644 --- a/packages/dataviews/src/test/normalize-fields.ts +++ b/packages/dataviews/src/test/normalize-fields.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import normalizeFields from '../field-types/utils/normalize-fields'; +import normalizeFields from '../field-types'; import type { Field } from '../types'; describe( 'normalizeFields: default getValue', () => { diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts index a7b40bb90b5c20..bb3fcf52da5be8 100644 --- a/packages/dataviews/src/types/field-api.ts +++ b/packages/dataviews/src/types/field-api.ts @@ -61,7 +61,7 @@ export type Operator = | 'inThePast' | 'over'; -export type FieldType = +export type FieldTypeName = | 'text' | 'integer' | 'number' @@ -118,7 +118,7 @@ export type EditConfigText = { * Edit configuration for other control types (excluding 'text' and 'textarea'). */ export type EditConfigGeneric = { - control: Exclude< FieldType, 'text' | 'textarea' >; + control: Exclude< FieldTypeName, 'text' | 'textarea' >; }; /** @@ -137,7 +137,7 @@ export type Field< Item > = { /** * Type of the fields. */ - type?: FieldType; + type?: FieldTypeName; /** * The unique identifier of the field. @@ -244,8 +244,6 @@ export type Field< Item > = { format?: FormatDate; }; -export type NormalizedFormat = Required< FormatDate > | {}; - /** * Format for date fields: * diff --git a/packages/dataviews/src/types/private.ts b/packages/dataviews/src/types/private.ts index d2d453e63599c9..18941d97cd89a1 100644 --- a/packages/dataviews/src/types/private.ts +++ b/packages/dataviews/src/types/private.ts @@ -1,2 +1,23 @@ +/** + * Internal dependencies + */ +import type { Field, FormatDate, NormalizedField, Operator } from './field-api'; + export type SelectionOrUpdater = string[] | ( ( prev: string[] ) => string[] ); export type SetSelection = ( selection: SelectionOrUpdater ) => void; +export type FieldType< Item > = Pick< + NormalizedField< Item >, + | 'type' + | 'render' + | 'sort' + | 'isValid' + | 'enableSorting' + | 'enableGlobalSearch' +> & { + Edit: string | null; + validOperators: Operator[]; + defaultOperators: Operator[]; + getFormat: ( + field: Field< Item > + ) => Record< string, any > | Required< FormatDate >; +}; diff --git a/packages/dataviews/src/utils/filter-sort-and-paginate.ts b/packages/dataviews/src/utils/filter-sort-and-paginate.ts index 362d80ab68a4bc..fa273ac8366138 100644 --- a/packages/dataviews/src/utils/filter-sort-and-paginate.ts +++ b/packages/dataviews/src/utils/filter-sort-and-paginate.ts @@ -36,7 +36,7 @@ import { OPERATOR_IN_THE_PAST, OPERATOR_OVER, } from '../constants'; -import normalizeFields from '../field-types/utils/normalize-fields'; +import normalizeFields from '../field-types'; import type { Field, View } from '../types'; function normalizeSearchInput( input = '' ) {