From cfc93cf2caa25fd0126587c0e96cf08e0532da6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Tue, 18 Nov 2025 10:59:26 +0100
Subject: [PATCH 01/15] Implement getFormat in all FieldTypeDefinitions
---
packages/dataviews/src/field-types/array.tsx | 4 +++-
.../dataviews/src/field-types/boolean.tsx | 4 +++-
packages/dataviews/src/field-types/color.tsx | 6 +++--
packages/dataviews/src/field-types/date.tsx | 22 +++++++++++++++++--
.../dataviews/src/field-types/datetime.tsx | 4 +++-
packages/dataviews/src/field-types/email.tsx | 6 +++--
packages/dataviews/src/field-types/index.tsx | 2 ++
.../dataviews/src/field-types/integer.tsx | 6 +++--
packages/dataviews/src/field-types/media.tsx | 3 ++-
packages/dataviews/src/field-types/number.tsx | 6 +++--
.../dataviews/src/field-types/password.tsx | 4 +++-
.../dataviews/src/field-types/telephone.tsx | 4 +++-
packages/dataviews/src/field-types/text.tsx | 4 +++-
packages/dataviews/src/field-types/url.tsx | 4 +++-
packages/dataviews/src/types/field-api.ts | 4 ++++
15 files changed, 65 insertions(+), 18 deletions(-)
diff --git a/packages/dataviews/src/field-types/array.tsx b/packages/dataviews/src/field-types/array.tsx
index a4e78871a34681..2c596570690839 100644
--- a/packages/dataviews/src/field-types/array.tsx
+++ b/packages/dataviews/src/field-types/array.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
NormalizedField,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import {
OPERATOR_IS_ALL,
@@ -65,6 +66,7 @@ const arrayFieldType: FieldTypeDefinition< any > = {
},
Edit: 'array', // Use array control
render,
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
diff --git a/packages/dataviews/src/field-types/boolean.tsx b/packages/dataviews/src/field-types/boolean.tsx
index 6e58c059f31e2b..060b1c813b9a09 100644
--- a/packages/dataviews/src/field-types/boolean.tsx
+++ b/packages/dataviews/src/field-types/boolean.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
NormalizedField,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import { OPERATOR_IS, OPERATOR_IS_NOT } from '../constants';
@@ -65,6 +66,7 @@ export default {
return null;
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ],
diff --git a/packages/dataviews/src/field-types/color.tsx b/packages/dataviews/src/field-types/color.tsx
index a293e77f3a6280..ab612f56619493 100644
--- a/packages/dataviews/src/field-types/color.tsx
+++ b/packages/dataviews/src/field-types/color.tsx
@@ -13,9 +13,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
- NormalizedField,
FieldTypeDefinition,
+ NormalizedField,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -101,6 +102,7 @@ export default {
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
diff --git a/packages/dataviews/src/field-types/date.tsx b/packages/dataviews/src/field-types/date.tsx
index fa23f660c4b464..3900b16d171e22 100644
--- a/packages/dataviews/src/field-types/date.tsx
+++ b/packages/dataviews/src/field-types/date.tsx
@@ -1,15 +1,18 @@
/**
* WordPress dependencies
*/
-import { dateI18n, getDate } from '@wordpress/date';
+import { dateI18n, getDate, getSettings } from '@wordpress/date';
/**
* Internal dependencies
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
+ DayString,
+ Field,
FieldTypeDefinition,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -23,6 +26,7 @@ import {
OPERATOR_OVER,
OPERATOR_BETWEEN,
} from '../constants';
+import { DAYS_OF_WEEK, numberToWeekStartsOn } from '../utils/week-starts-on';
function sort( a: any, b: any, direction: SortDirection ) {
const timeA = new Date( a ).getTime();
@@ -60,6 +64,20 @@ export default {
return dateI18n( field.format.date, getDate( value ) );
},
enableSorting: true,
+ getFormat: ( field: Field< any > ): NormalizedFormat => {
+ return {
+ date:
+ field.format?.date !== undefined &&
+ typeof field.format.date === 'string'
+ ? field.format.date
+ : getSettings().formats.date,
+ weekStartsOn:
+ field.format?.weekStartsOn !== undefined &&
+ DAYS_OF_WEEK.includes( field.format?.weekStartsOn as DayString )
+ ? field.format.weekStartsOn
+ : numberToWeekStartsOn( getSettings().l10n.startOfWeek ),
+ };
+ },
filterBy: {
defaultOperators: [
OPERATOR_ON,
diff --git a/packages/dataviews/src/field-types/datetime.tsx b/packages/dataviews/src/field-types/datetime.tsx
index c857c38c9389d3..2dfba4edb30b75 100644
--- a/packages/dataviews/src/field-types/datetime.tsx
+++ b/packages/dataviews/src/field-types/datetime.tsx
@@ -3,8 +3,9 @@
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import parseDateTime from './utils/parse-date-time';
@@ -50,6 +51,7 @@ export default {
return null;
}
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [
diff --git a/packages/dataviews/src/field-types/email.tsx b/packages/dataviews/src/field-types/email.tsx
index 8af0d443a9a24f..7f911461abfe8e 100644
--- a/packages/dataviews/src/field-types/email.tsx
+++ b/packages/dataviews/src/field-types/email.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
- NormalizedField,
FieldTypeDefinition,
+ NormalizedField,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -61,6 +62,7 @@ export default {
field.getValue( { item } )
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
diff --git a/packages/dataviews/src/field-types/index.tsx b/packages/dataviews/src/field-types/index.tsx
index 8db2da452da65a..e61b5e74124059 100644
--- a/packages/dataviews/src/field-types/index.tsx
+++ b/packages/dataviews/src/field-types/index.tsx
@@ -5,6 +5,7 @@ import type {
DataViewRenderFieldProps,
FieldType,
FieldTypeDefinition,
+ NormalizedFormat,
SortDirection,
} from '../types';
import { default as email } from './email';
@@ -108,6 +109,7 @@ export default function getFieldTypeDefinition< Item >(
field.getValue( { item } )
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ],
diff --git a/packages/dataviews/src/field-types/integer.tsx b/packages/dataviews/src/field-types/integer.tsx
index 7674cdb9d3329c..212eb3bca10651 100644
--- a/packages/dataviews/src/field-types/integer.tsx
+++ b/packages/dataviews/src/field-types/integer.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
- NormalizedField,
FieldTypeDefinition,
+ NormalizedField,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -55,6 +56,7 @@ export default {
field.getValue( { item } )
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [
diff --git a/packages/dataviews/src/field-types/media.tsx b/packages/dataviews/src/field-types/media.tsx
index 62fd76458a0ffa..7d4e684629a3f6 100644
--- a/packages/dataviews/src/field-types/media.tsx
+++ b/packages/dataviews/src/field-types/media.tsx
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
-import type { FieldTypeDefinition } from '../types';
+import type { FieldTypeDefinition, NormalizedFormat } from '../types';
function sort() {
return 0;
@@ -15,6 +15,7 @@ export default {
},
Edit: null,
render: () => null,
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: false,
filterBy: false,
} satisfies FieldTypeDefinition< any >;
diff --git a/packages/dataviews/src/field-types/number.tsx b/packages/dataviews/src/field-types/number.tsx
index d968c6bc66a519..ad533f0bdb94d8 100644
--- a/packages/dataviews/src/field-types/number.tsx
+++ b/packages/dataviews/src/field-types/number.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
- NormalizedField,
FieldTypeDefinition,
+ NormalizedField,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import {
OPERATOR_IS,
@@ -62,6 +63,7 @@ export default {
return null;
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [
diff --git a/packages/dataviews/src/field-types/password.tsx b/packages/dataviews/src/field-types/password.tsx
index 6a9d5b95273a75..6ed19430088310 100644
--- a/packages/dataviews/src/field-types/password.tsx
+++ b/packages/dataviews/src/field-types/password.tsx
@@ -3,8 +3,9 @@
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
+ SortDirection,
+ NormalizedFormat,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -28,6 +29,7 @@ export default {
'••••••••'
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: false,
filterBy: false,
} satisfies FieldTypeDefinition< any >;
diff --git a/packages/dataviews/src/field-types/telephone.tsx b/packages/dataviews/src/field-types/telephone.tsx
index 7fe98248f0ba59..9498b5a87f5681 100644
--- a/packages/dataviews/src/field-types/telephone.tsx
+++ b/packages/dataviews/src/field-types/telephone.tsx
@@ -3,8 +3,9 @@
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
+ SortDirection,
+ NormalizedFormat,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -39,6 +40,7 @@ export default {
field.getValue( { item } )
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
diff --git a/packages/dataviews/src/field-types/text.tsx b/packages/dataviews/src/field-types/text.tsx
index 5cb5944248d0c3..fd79e83730c55a 100644
--- a/packages/dataviews/src/field-types/text.tsx
+++ b/packages/dataviews/src/field-types/text.tsx
@@ -3,8 +3,9 @@
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -39,6 +40,7 @@ export default {
field.getValue( { item } )
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
diff --git a/packages/dataviews/src/field-types/url.tsx b/packages/dataviews/src/field-types/url.tsx
index 8d24ff96fc1f04..6fd1c50ad2987d 100644
--- a/packages/dataviews/src/field-types/url.tsx
+++ b/packages/dataviews/src/field-types/url.tsx
@@ -3,8 +3,9 @@
*/
import type {
DataViewRenderFieldProps,
- SortDirection,
FieldTypeDefinition,
+ NormalizedFormat,
+ SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -39,6 +40,7 @@ export default {
field.getValue( { item } )
);
},
+ getFormat: (): NormalizedFormat => ( {} ),
enableSorting: true,
filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts
index 6a0cc90bbde59f..109b013d39208c 100644
--- a/packages/dataviews/src/types/field-api.ts
+++ b/packages/dataviews/src/types/field-api.ts
@@ -136,6 +136,8 @@ export type FieldTypeDefinition< Item > = {
*/
filterBy: FilterConfigForType | false;
+ getFormat: ( field: Field< Item > ) => NormalizedFormat;
+
/**
* Whether the field is readOnly.
* If `true`, the value will be rendered using the `render` callback.
@@ -315,6 +317,8 @@ export type Field< Item > = {
format?: FormatDate;
};
+export type NormalizedFormat = Required< FormatDate > | {};
+
/**
* Format for date fields:
*
From 8e08c82696f2d2b410d5d8b55c9f045533413c2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Tue, 18 Nov 2025 11:00:31 +0100
Subject: [PATCH 02/15] Use getFormat in normalizeFields
---
.../dataviews/src/utils/normalize-fields.ts | 24 +------------------
1 file changed, 1 insertion(+), 23 deletions(-)
diff --git a/packages/dataviews/src/utils/normalize-fields.ts b/packages/dataviews/src/utils/normalize-fields.ts
index 42fda2e86a2e01..30e0e16fd96be3 100644
--- a/packages/dataviews/src/utils/normalize-fields.ts
+++ b/packages/dataviews/src/utils/normalize-fields.ts
@@ -3,17 +3,11 @@
*/
import type { FunctionComponent } from 'react';
-/**
- * WordPress dependencies
- */
-import { getSettings } from '@wordpress/date';
-
/**
* Internal dependencies
*/
import getFieldTypeDefinition from '../field-types';
import type {
- DayString,
DataViewRenderFieldProps,
Field,
FieldTypeDefinition,
@@ -27,7 +21,6 @@ import {
SINGLE_SELECTION_OPERATORS,
} from '../constants';
import hasElements from './has-elements';
-import { numberToWeekStartsOn, DAYS_OF_WEEK } from './week-starts-on';
const getValueFromId =
( id: string ) =>
@@ -225,22 +218,7 @@ export default function normalizeFields< Item >(
};
if ( field.type === 'date' ) {
- const format = {
- date:
- field.format?.date !== undefined &&
- typeof field.format.date === 'string'
- ? field.format.date
- : getSettings().formats.date,
- weekStartsOn:
- field.format?.weekStartsOn !== undefined &&
- DAYS_OF_WEEK.includes(
- field.format?.weekStartsOn as DayString
- )
- ? field.format.weekStartsOn
- : numberToWeekStartsOn(
- getSettings().l10n.startOfWeek
- ),
- };
+ const format = fieldTypeDefinition.getFormat( field );
return {
...baseField,
From 379c335af9d09220c9bc136a0ef4bf8635ab2d92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Tue, 18 Nov 2025 11:47:01 +0100
Subject: [PATCH 03/15] Tell TypeScript the format is proper
---
packages/dataviews/src/types/field-api.ts | 2 +-
packages/dataviews/src/utils/normalize-fields.ts | 10 ++++++++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts
index 109b013d39208c..5f82475a4498f2 100644
--- a/packages/dataviews/src/types/field-api.ts
+++ b/packages/dataviews/src/types/field-api.ts
@@ -327,7 +327,7 @@ export type NormalizedFormat = Required< FormatDate > | {};
*
* If not provided, defaults to WordPress date format settings.
*/
-type FormatDate = {
+export type FormatDate = {
date?: string;
weekStartsOn?: DayString;
};
diff --git a/packages/dataviews/src/utils/normalize-fields.ts b/packages/dataviews/src/utils/normalize-fields.ts
index 30e0e16fd96be3..43b859ccd34628 100644
--- a/packages/dataviews/src/utils/normalize-fields.ts
+++ b/packages/dataviews/src/utils/normalize-fields.ts
@@ -13,6 +13,7 @@ import type {
FieldTypeDefinition,
NormalizedFilterByConfig,
NormalizedField,
+ FormatDate,
} from '../types';
import { getControl } from '../dataform-controls';
import {
@@ -218,7 +219,9 @@ export default function normalizeFields< Item >(
};
if ( field.type === 'date' ) {
- const format = fieldTypeDefinition.getFormat( field );
+ const format = fieldTypeDefinition.getFormat(
+ field
+ ) as Required< FormatDate >;
return {
...baseField,
@@ -227,6 +230,9 @@ export default function normalizeFields< Item >(
};
}
- return { ...baseField, type: field.type, format: {} };
+ return {
+ ...baseField,
+ type: field.type,
+ };
} );
}
From dc5f7132ca061b80e83e3aef2fc8c43efe0c6129 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 11:13:08 +0100
Subject: [PATCH 04/15] Export normalize function
---
.../src/components/dataform/index.tsx | 2 +-
.../src/components/dataviews-picker/index.tsx | 2 +-
.../src/components/dataviews/index.tsx | 2 +-
.../dataviews/src/dataform-controls/date.tsx | 2 +-
.../dataviews/src/dataform-controls/index.tsx | 30 +--
packages/dataviews/src/field-types/array.tsx | 77 ++++--
.../dataviews/src/field-types/boolean.tsx | 91 ++++---
packages/dataviews/src/field-types/color.tsx | 124 +++++----
packages/dataviews/src/field-types/date.tsx | 170 ++++++++-----
.../dataviews/src/field-types/datetime.tsx | 130 ++++++----
packages/dataviews/src/field-types/email.tsx | 103 +++++---
packages/dataviews/src/field-types/index.tsx | 106 +++++---
.../dataviews/src/field-types/integer.tsx | 125 +++++----
packages/dataviews/src/field-types/media.tsx | 56 ++++-
packages/dataviews/src/field-types/number.tsx | 131 ++++++----
.../dataviews/src/field-types/password.tsx | 71 ++++--
.../dataviews/src/field-types/telephone.tsx | 100 +++++---
packages/dataviews/src/field-types/text.tsx | 102 +++++---
packages/dataviews/src/field-types/url.tsx | 100 +++++---
.../src/field-types/utils/get-filter-by.ts | 49 ++++
.../field-types/utils/get-value-from-id.ts | 17 ++
.../{ => field-types}/utils/has-elements.ts | 2 +-
.../src/field-types/utils/normalize-fields.ts | 25 ++
.../field-types/utils/set-value-from-id.ts | 17 ++
.../{ => field-types}/utils/week-starts-on.ts | 2 +-
.../dataviews/src/hooks/use-form-validity.ts | 2 +-
.../dataviews/src/test/normalize-fields.ts | 2 +-
.../src/utils/filter-sort-and-paginate.ts | 2 +-
.../dataviews/src/utils/normalize-fields.ts | 238 ------------------
29 files changed, 1122 insertions(+), 758 deletions(-)
create mode 100644 packages/dataviews/src/field-types/utils/get-filter-by.ts
create mode 100644 packages/dataviews/src/field-types/utils/get-value-from-id.ts
rename packages/dataviews/src/{ => field-types}/utils/has-elements.ts (82%)
create mode 100644 packages/dataviews/src/field-types/utils/normalize-fields.ts
create mode 100644 packages/dataviews/src/field-types/utils/set-value-from-id.ts
rename packages/dataviews/src/{ => field-types}/utils/week-starts-on.ts (93%)
delete mode 100644 packages/dataviews/src/utils/normalize-fields.ts
diff --git a/packages/dataviews/src/components/dataform/index.tsx b/packages/dataviews/src/components/dataform/index.tsx
index f9d9e3025e730e..321af6f9302669 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 '../../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';
diff --git a/packages/dataviews/src/components/dataviews-picker/index.tsx b/packages/dataviews/src/components/dataviews-picker/index.tsx
index df831a79a9e506..a97bb40825978f 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 '../../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 };
diff --git a/packages/dataviews/src/components/dataviews/index.tsx b/packages/dataviews/src/components/dataviews/index.tsx
index 9d5870174f9db7..07cda5d91bd9b7 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 '../../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 };
diff --git a/packages/dataviews/src/dataform-controls/date.tsx b/packages/dataviews/src/dataform-controls/date.tsx
index ae17fd3bd65869..dd97fd7ebcf8c8 100644
--- a/packages/dataviews/src/dataform-controls/date.tsx
+++ b/packages/dataviews/src/dataform-controls/date.tsx
@@ -51,7 +51,7 @@ import type {
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 );
diff --git a/packages/dataviews/src/dataform-controls/index.tsx b/packages/dataviews/src/dataform-controls/index.tsx
index 1f351d85d592b2..b95256e310a7af 100644
--- a/packages/dataviews/src/dataform-controls/index.tsx
+++ b/packages/dataviews/src/dataform-controls/index.tsx
@@ -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';
@@ -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 > >;
@@ -55,15 +50,18 @@ const FORM_CONTROLS: FormControls = {
toggleGroup,
};
-function isEditConfig( value: any ): value is EditConfig {
+export function isEditConfig( value: any ): value is EditConfig {
return (
value && typeof value === 'object' && typeof value.control === 'string'
);
}
-function createConfiguredControl( config: EditConfig ) {
+export function createConfiguredControl( config: EditConfig ) {
const { control, ...controlConfig } = config;
const BaseControlType = getControlByType( control );
+ if ( BaseControlType === null ) {
+ return null;
+ }
return function ConfiguredControl< Item >(
props: DataFormControlProps< Item >
@@ -74,7 +72,7 @@ function createConfiguredControl( config: EditConfig ) {
export function getControl< Item >(
field: Field< Item >,
- fieldTypeDefinition: FieldTypeDefinition< Item >
+ fallback: string | null
) {
if ( typeof field.Edit === 'function' ) {
return field.Edit;
@@ -92,15 +90,11 @@ 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 ) {
@@ -108,5 +102,5 @@ export function getControlByType( type: string ) {
return FORM_CONTROLS[ type ];
}
- throw 'Control ' + type + ' not found';
+ return null;
}
diff --git a/packages/dataviews/src/field-types/array.tsx b/packages/dataviews/src/field-types/array.tsx
index 2c596570690839..9015f14360130e 100644
--- a/packages/dataviews/src/field-types/array.tsx
+++ b/packages/dataviews/src/field-types/array.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
NormalizedField,
- NormalizedFormat,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import {
@@ -19,6 +20,11 @@ import {
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';
// Sort arrays by length, then alphabetically by joined string
function sort( valueA: any, valueB: any, direction: SortDirection ) {
@@ -42,12 +48,24 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) {
return value.join( ', ' );
}
-const arrayFieldType: FieldTypeDefinition< any > = {
- sort,
- isValid: {
+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< 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 ) &&
@@ -63,20 +81,33 @@ const arrayFieldType: FieldTypeDefinition< any > = {
return null;
},
- },
- Edit: 'array', // Use array control
- render,
- getFormat: (): NormalizedFormat => ( {} ),
- 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/boolean.tsx b/packages/dataviews/src/field-types/boolean.tsx
index 060b1c813b9a09..677184df2a9f9c 100644
--- a/packages/dataviews/src/field-types/boolean.tsx
+++ b/packages/dataviews/src/field-types/boolean.tsx
@@ -8,13 +8,19 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
NormalizedField,
- NormalizedFormat,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
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 sort( a: any, b: any, direction: SortDirection ) {
const boolA = Boolean( a );
@@ -33,12 +39,31 @@ function sort( a: any, b: any, direction: SortDirection ) {
return boolA ? -1 : 1;
}
-export default {
- sort,
- isValid: {
+function render( { item, field }: DataViewRenderFieldProps< any > ) {
+ if ( field.hasElements ) {
+ return ;
+ }
+
+ if ( field.getValue( { item } ) === true ) {
+ return __( 'True' );
+ }
+
+ if ( field.getValue( { item } ) === false ) {
+ return __( 'False' );
+ }
+
+ 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, field: NormalizedField< any > ) => {
- const value = field.getValue( { item } );
+ custom: ( item: any, normalizedField ) => {
+ const value = normalizedField.getValue( { item } );
if (
! [ undefined, '', null ].includes( value ) &&
@@ -49,27 +74,37 @@ export default {
return null;
},
- },
- Edit: 'checkbox',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- if ( field.hasElements ) {
- return ;
- }
+ };
- if ( field.getValue( { item } ) === true ) {
- return __( 'True' );
- }
+ const defaultOperators: Operator[] = [ OPERATOR_IS, OPERATOR_IS_NOT ];
- if ( field.getValue( { item } ) === false ) {
- return __( 'False' );
- }
+ const validOperators: Operator[] = [ OPERATOR_IS, OPERATOR_IS_NOT ];
- return null;
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ],
- validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ],
- },
-} satisfies FieldTypeDefinition< any >;
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/color.tsx b/packages/dataviews/src/field-types/color.tsx
index ab612f56619493..025b7b8bf0ada5 100644
--- a/packages/dataviews/src/field-types/color.tsx
+++ b/packages/dataviews/src/field-types/color.tsx
@@ -13,9 +13,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
NormalizedField,
- NormalizedFormat,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -25,6 +26,11 @@ 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 sort( valueA: any, valueB: any, direction: SortDirection ) {
// Convert colors to HSL for better sorting
@@ -54,12 +60,44 @@ function sort( valueA: any, valueB: any, direction: SortDirection ) {
return direction === 'asc' ? hslA.l - hslB.l : hslB.l - hslA.l;
}
-export default {
- sort,
- isValid: {
+function render( { item, field }: DataViewRenderFieldProps< any > ) {
+ if ( field.hasElements ) {
+ return ;
+ }
+
+ const value = field.getValue( { item } );
+
+ if ( ! value || ! colord( value ).isValid() ) {
+ return value;
+ }
+
+ // Render color with visual preview
+ return (
+
+ );
+}
+
+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, field: NormalizedField< any > ) => {
- const value = field.getValue( { item } );
+ custom: ( item: any, normalizedField ) => {
+ const value = normalizedField.getValue( { item } );
if (
! [ undefined, '', null ].includes( value ) &&
@@ -70,42 +108,42 @@ export default {
return null;
},
- },
- Edit: 'color',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- if ( field.hasElements ) {
- return ;
- }
+ };
- const value = field.getValue( { item } );
+ const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];
- if ( ! value || ! colord( value ).isValid() ) {
- return value;
- }
+ const validOperators: Operator[] = [
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_IS_ANY,
+ OPERATOR_IS_NONE,
+ ];
- // Render color with visual preview
- return (
-
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
- validOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ],
- },
-} satisfies FieldTypeDefinition< any >;
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/date.tsx b/packages/dataviews/src/field-types/date.tsx
index 3900b16d171e22..89b93efd9d3828 100644
--- a/packages/dataviews/src/field-types/date.tsx
+++ b/packages/dataviews/src/field-types/date.tsx
@@ -10,8 +10,10 @@ import type {
DataViewRenderFieldProps,
DayString,
Field,
- FieldTypeDefinition,
- NormalizedFormat,
+ FormatDate,
+ NormalizedField,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -26,7 +28,12 @@ import {
OPERATOR_OVER,
OPERATOR_BETWEEN,
} from '../constants';
-import { DAYS_OF_WEEK, numberToWeekStartsOn } from '../utils/week-starts-on';
+import { DAYS_OF_WEEK, numberToWeekStartsOn } from './utils/week-starts-on';
+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 sort( a: any, b: any, direction: SortDirection ) {
const timeA = new Date( a ).getTime();
@@ -35,71 +42,102 @@ function sort( a: any, b: any, direction: SortDirection ) {
return direction === 'asc' ? timeA - timeB : timeB - timeA;
}
-export default {
- sort,
- Edit: 'date',
- isValid: {
+function getFormat( field: Field< any > ): Required< FormatDate > {
+ return {
+ date:
+ field.format?.date !== undefined &&
+ typeof field.format.date === 'string'
+ ? field.format.date
+ : getSettings().formats.date,
+ weekStartsOn:
+ field.format?.weekStartsOn !== undefined &&
+ DAYS_OF_WEEK.includes( field.format?.weekStartsOn as DayString )
+ ? field.format.weekStartsOn
+ : numberToWeekStartsOn( getSettings().l10n.startOfWeek ),
+ };
+}
+
+function render( { item, field }: DataViewRenderFieldProps< any > ) {
+ if ( field.hasElements ) {
+ return ;
+ }
+
+ const value = field.getValue( { item } );
+ if ( ! value ) {
+ return '';
+ }
+
+ // Not all fields have format, but date fields do.
+ //
+ // At runtime, this method will never be called for non-date fields.
+ // However, the type system does not know this, so we need to check it.
+ // There's an opportunity here to improve the type system.
+ if ( field.type !== 'date' ) {
+ return '';
+ }
+
+ return dateI18n( field.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,
- },
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- if ( field.hasElements ) {
- return ;
- }
+ };
- const value = field.getValue( { item } );
- if ( ! value ) {
- return '';
- }
+ const defaultOperators: Operator[] = [
+ OPERATOR_ON,
+ OPERATOR_NOT_ON,
+ OPERATOR_BEFORE,
+ OPERATOR_AFTER,
+ OPERATOR_BEFORE_INC,
+ OPERATOR_AFTER_INC,
+ OPERATOR_IN_THE_PAST,
+ OPERATOR_OVER,
+ OPERATOR_BETWEEN,
+ ];
- // Not all fields have format, but date fields do.
- //
- // At runtime, this method will never be called for non-date fields.
- // However, the type system does not know this, so we need to check it.
- // There's an opportunity here to improve the type system.
- if ( field.type !== 'date' ) {
- return '';
- }
+ const validOperators: Operator[] = [
+ OPERATOR_ON,
+ OPERATOR_NOT_ON,
+ OPERATOR_BEFORE,
+ OPERATOR_AFTER,
+ OPERATOR_BEFORE_INC,
+ OPERATOR_AFTER_INC,
+ OPERATOR_IN_THE_PAST,
+ OPERATOR_OVER,
+ OPERATOR_BETWEEN,
+ ];
- return dateI18n( field.format.date, getDate( value ) );
- },
- enableSorting: true,
- getFormat: ( field: Field< any > ): NormalizedFormat => {
- return {
- date:
- field.format?.date !== undefined &&
- typeof field.format.date === 'string'
- ? field.format.date
- : getSettings().formats.date,
- weekStartsOn:
- field.format?.weekStartsOn !== undefined &&
- DAYS_OF_WEEK.includes( field.format?.weekStartsOn as DayString )
- ? field.format.weekStartsOn
- : numberToWeekStartsOn( getSettings().l10n.startOfWeek ),
- };
- },
- filterBy: {
- defaultOperators: [
- OPERATOR_ON,
- OPERATOR_NOT_ON,
- OPERATOR_BEFORE,
- OPERATOR_AFTER,
- OPERATOR_BEFORE_INC,
- OPERATOR_AFTER_INC,
- OPERATOR_IN_THE_PAST,
- OPERATOR_OVER,
- OPERATOR_BETWEEN,
- ],
- validOperators: [
- OPERATOR_ON,
- OPERATOR_NOT_ON,
- OPERATOR_BEFORE,
- OPERATOR_AFTER,
- OPERATOR_BEFORE_INC,
- OPERATOR_AFTER_INC,
- OPERATOR_IN_THE_PAST,
- OPERATOR_OVER,
- OPERATOR_BETWEEN,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ 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 ),
+ };
+}
diff --git a/packages/dataviews/src/field-types/datetime.tsx b/packages/dataviews/src/field-types/datetime.tsx
index 2dfba4edb30b75..dc7ac51c77eabd 100644
--- a/packages/dataviews/src/field-types/datetime.tsx
+++ b/packages/dataviews/src/field-types/datetime.tsx
@@ -3,8 +3,10 @@
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
- NormalizedFormat,
+ Field,
+ NormalizedField,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -19,6 +21,11 @@ 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 sort( a: any, b: any, direction: SortDirection ) {
const timeA = new Date( a ).getTime();
@@ -27,52 +34,81 @@ function sort( a: any, b: any, direction: SortDirection ) {
return direction === 'asc' ? timeA - timeB : timeB - timeA;
}
-export default {
- sort,
- isValid: {
+function render( { item, field }: DataViewRenderFieldProps< any > ) {
+ if ( field.elements ) {
+ return ;
+ }
+
+ const value = field.getValue( { item } );
+ if ( [ '', undefined, null ].includes( value ) ) {
+ return null;
+ }
+
+ try {
+ const dateValue = parseDateTime( value );
+ return dateValue?.toLocaleString();
+ } catch ( error ) {
+ 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: () => null,
- },
- Edit: 'datetime',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- if ( field.elements ) {
- return ;
- }
+ };
+
+ const defaultOperators: Operator[] = [
+ OPERATOR_ON,
+ OPERATOR_NOT_ON,
+ OPERATOR_BEFORE,
+ OPERATOR_AFTER,
+ OPERATOR_BEFORE_INC,
+ OPERATOR_AFTER_INC,
+ OPERATOR_IN_THE_PAST,
+ OPERATOR_OVER,
+ ];
- const value = field.getValue( { item } );
- if ( [ '', undefined, null ].includes( value ) ) {
- return null;
- }
+ const validOperators: Operator[] = [
+ OPERATOR_ON,
+ OPERATOR_NOT_ON,
+ OPERATOR_BEFORE,
+ OPERATOR_AFTER,
+ OPERATOR_BEFORE_INC,
+ OPERATOR_AFTER_INC,
+ OPERATOR_IN_THE_PAST,
+ OPERATOR_OVER,
+ ];
- try {
- const dateValue = parseDateTime( value );
- return dateValue?.toLocaleString();
- } catch ( error ) {
- return null;
- }
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [
- OPERATOR_ON,
- OPERATOR_NOT_ON,
- OPERATOR_BEFORE,
- OPERATOR_AFTER,
- OPERATOR_BEFORE_INC,
- OPERATOR_AFTER_INC,
- OPERATOR_IN_THE_PAST,
- OPERATOR_OVER,
- ],
- validOperators: [
- OPERATOR_ON,
- OPERATOR_NOT_ON,
- OPERATOR_BEFORE,
- OPERATOR_AFTER,
- OPERATOR_BEFORE_INC,
- OPERATOR_AFTER_INC,
- OPERATOR_IN_THE_PAST,
- OPERATOR_OVER,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/email.tsx b/packages/dataviews/src/field-types/email.tsx
index 7f911461abfe8e..5febdb5a00e4a1 100644
--- a/packages/dataviews/src/field-types/email.tsx
+++ b/packages/dataviews/src/field-types/email.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
NormalizedField,
- NormalizedFormat,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -25,6 +26,11 @@ 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';
function sort( valueA: any, valueB: any, direction: SortDirection ) {
return direction === 'asc'
@@ -37,12 +43,23 @@ function sort( valueA: any, valueB: any, direction: SortDirection ) {
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])?)*$/;
-export default {
- sort,
- isValid: {
+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 > = {
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 ) &&
@@ -53,30 +70,48 @@ export default {
return null;
},
- },
- Edit: 'email',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- return field.hasElements ? (
-
- ) : (
- field.getValue( { item } )
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
- validOperators: [
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_CONTAINS,
- OPERATOR_NOT_CONTAINS,
- OPERATOR_STARTS_WITH,
- // Multiple selection
- OPERATOR_IS_ANY,
- OPERATOR_IS_NONE,
- OPERATOR_IS_ALL,
- OPERATOR_IS_NOT_ALL,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];
+
+ const validOperators: Operator[] = [
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_CONTAINS,
+ OPERATOR_NOT_CONTAINS,
+ OPERATOR_STARTS_WITH,
+ // Multiple selection
+ OPERATOR_IS_ANY,
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/index.tsx b/packages/dataviews/src/field-types/index.tsx
index e61b5e74124059..e6c943f0677b23 100644
--- a/packages/dataviews/src/field-types/index.tsx
+++ b/packages/dataviews/src/field-types/index.tsx
@@ -3,9 +3,10 @@
*/
import type {
DataViewRenderFieldProps,
+ Field,
FieldType,
- FieldTypeDefinition,
- NormalizedFormat,
+ NormalizedField,
+ Operator,
SortDirection,
} from '../types';
import { default as email } from './email';
@@ -23,6 +24,75 @@ 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 ) => {
+ if ( typeof a === 'number' && typeof b === 'number' ) {
+ return direction === 'asc' ? a - b : b - a;
+ }
+
+ return direction === 'asc'
+ ? a.localeCompare( b )
+ : b.localeCompare( a );
+ };
+
+ 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: {},
+ };
+}
/**
*
@@ -32,7 +102,7 @@ import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from '../constants';
*/
export default function getFieldTypeDefinition< Item >(
type?: FieldType
-): FieldTypeDefinition< Item > {
+): ( field: Field< Item > ) => NormalizedField< Item > {
if ( 'email' === type ) {
return email;
}
@@ -87,33 +157,5 @@ export default function getFieldTypeDefinition< Item >(
// This is a fallback for fields that don't provide a type.
// It can be removed when the field.type is mandatory.
- return {
- sort: ( a: any, b: any, direction: SortDirection ) => {
- if ( typeof a === 'number' && typeof b === 'number' ) {
- return direction === 'asc' ? a - b : b - a;
- }
-
- return direction === 'asc'
- ? a.localeCompare( b )
- : b.localeCompare( a );
- },
- isValid: {
- elements: true,
- custom: () => null,
- },
- Edit: null,
- render: ( { item, field }: DataViewRenderFieldProps< Item > ) => {
- return field.hasElements ? (
-
- ) : (
- field.getValue( { item } )
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS, OPERATOR_IS_NOT ],
- validOperators: ALL_OPERATORS,
- },
- };
+ return normalizeField;
}
diff --git a/packages/dataviews/src/field-types/integer.tsx b/packages/dataviews/src/field-types/integer.tsx
index 212eb3bca10651..b76d559ee78f95 100644
--- a/packages/dataviews/src/field-types/integer.tsx
+++ b/packages/dataviews/src/field-types/integer.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
NormalizedField,
- NormalizedFormat,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -27,17 +28,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';
function sort( a: any, b: any, direction: SortDirection ) {
return direction === 'asc' ? a - b : b - a;
}
-export default {
- sort,
- isValid: {
+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 > = {
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 ) &&
! Number.isInteger( value )
@@ -47,41 +64,59 @@ export default {
return null;
},
- },
- Edit: 'integer',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- return field.hasElements ? (
-
- ) : (
- field.getValue( { item } )
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_LESS_THAN,
- OPERATOR_GREATER_THAN,
- OPERATOR_LESS_THAN_OR_EQUAL,
- OPERATOR_GREATER_THAN_OR_EQUAL,
- OPERATOR_BETWEEN,
- ],
- validOperators: [
- // Single-selection
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_LESS_THAN,
- OPERATOR_GREATER_THAN,
- OPERATOR_LESS_THAN_OR_EQUAL,
- OPERATOR_GREATER_THAN_OR_EQUAL,
- OPERATOR_BETWEEN,
- // Multiple-selection
- OPERATOR_IS_ANY,
- OPERATOR_IS_NONE,
- OPERATOR_IS_ALL,
- OPERATOR_IS_NOT_ALL,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ const defaultOperators: Operator[] = [
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_LESS_THAN,
+ OPERATOR_GREATER_THAN,
+ OPERATOR_LESS_THAN_OR_EQUAL,
+ OPERATOR_GREATER_THAN_OR_EQUAL,
+ OPERATOR_BETWEEN,
+ ];
+
+ const validOperators: Operator[] = [
+ // Single-selection
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_LESS_THAN,
+ OPERATOR_GREATER_THAN,
+ OPERATOR_LESS_THAN_OR_EQUAL,
+ OPERATOR_GREATER_THAN_OR_EQUAL,
+ OPERATOR_BETWEEN,
+ // Multiple-selection
+ OPERATOR_IS_ANY,
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/media.tsx b/packages/dataviews/src/field-types/media.tsx
index 7d4e684629a3f6..6e2ba665110189 100644
--- a/packages/dataviews/src/field-types/media.tsx
+++ b/packages/dataviews/src/field-types/media.tsx
@@ -1,21 +1,55 @@
/**
* Internal dependencies
*/
-import type { FieldTypeDefinition, NormalizedFormat } from '../types';
+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';
function sort() {
return 0;
}
-export default {
- sort,
- isValid: {
+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 > = {
elements: true,
custom: () => null,
- },
- Edit: null,
- render: () => null,
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: false,
- filterBy: false,
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/number.tsx b/packages/dataviews/src/field-types/number.tsx
index ad533f0bdb94d8..966290104ad9b6 100644
--- a/packages/dataviews/src/field-types/number.tsx
+++ b/packages/dataviews/src/field-types/number.tsx
@@ -8,9 +8,10 @@ import { __ } from '@wordpress/i18n';
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
NormalizedField,
- NormalizedFormat,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import {
@@ -27,6 +28,11 @@ 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';
function sort( a: any, b: any, direction: SortDirection ) {
return direction === 'asc' ? a - b : b - a;
@@ -36,12 +42,28 @@ function isEmpty( value: unknown ): value is '' | undefined | null {
return value === '' || value === undefined || value === null;
}
-export default {
- sort,
- isValid: {
+function render( { item, field }: DataViewRenderFieldProps< any > ) {
+ if ( field.hasElements ) {
+ return ;
+ }
+
+ const value = field.getValue( { item } );
+ if ( ! [ null, undefined ].includes( value ) ) {
+ return Number( value ).toFixed( 2 );
+ }
+
+ 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, field: NormalizedField< any > ) => {
- const value = field.getValue( { item } );
+ custom: ( item: any, normalizedField ) => {
+ const value = normalizedField.getValue( { item } );
if ( ! isEmpty( value ) && ! Number.isFinite( value ) ) {
return __( 'Value must be a number.' );
@@ -49,46 +71,59 @@ export default {
return null;
},
- },
- Edit: 'number',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- if ( field.hasElements ) {
- ;
- }
+ };
- const value = field.getValue( { item } );
- if ( ! [ null, undefined ].includes( value ) ) {
- return Number( value ).toFixed( 2 );
- }
+ const defaultOperators: Operator[] = [
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_LESS_THAN,
+ OPERATOR_GREATER_THAN,
+ OPERATOR_LESS_THAN_OR_EQUAL,
+ OPERATOR_GREATER_THAN_OR_EQUAL,
+ OPERATOR_BETWEEN,
+ ];
- return null;
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_LESS_THAN,
- OPERATOR_GREATER_THAN,
- OPERATOR_LESS_THAN_OR_EQUAL,
- OPERATOR_GREATER_THAN_OR_EQUAL,
- OPERATOR_BETWEEN,
- ],
- validOperators: [
- // Single-selection
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_LESS_THAN,
- OPERATOR_GREATER_THAN,
- OPERATOR_LESS_THAN_OR_EQUAL,
- OPERATOR_GREATER_THAN_OR_EQUAL,
- OPERATOR_BETWEEN,
- // Multiple-selection
- OPERATOR_IS_ANY,
- OPERATOR_IS_NONE,
- OPERATOR_IS_ALL,
- OPERATOR_IS_NOT_ALL,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ const validOperators: Operator[] = [
+ // Single-selection
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_LESS_THAN,
+ OPERATOR_GREATER_THAN,
+ OPERATOR_LESS_THAN_OR_EQUAL,
+ OPERATOR_GREATER_THAN_OR_EQUAL,
+ OPERATOR_BETWEEN,
+ // Multiple-selection
+ OPERATOR_IS_ANY,
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/password.tsx b/packages/dataviews/src/field-types/password.tsx
index 6ed19430088310..ec8c5ea8dc5758 100644
--- a/packages/dataviews/src/field-types/password.tsx
+++ b/packages/dataviews/src/field-types/password.tsx
@@ -3,33 +3,66 @@
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
+ NormalizedField,
+ Rules,
SortDirection,
- NormalizedFormat,
} from '../types';
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 ) {
+function sort( _valueA: any, _valueB: any, _direction: SortDirection ) {
// Passwords should not be sortable for security reasons
return 0;
}
-export default {
- sort,
- isValid: {
+function render( { item, field }: DataViewRenderFieldProps< any > ) {
+ return field.hasElements ? (
+
+ ) : (
+ '••••••••'
+ );
+}
+
+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,
- },
- Edit: 'password',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- return field.hasElements ? (
-
- ) : (
- '••••••••'
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: false,
- filterBy: false,
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/telephone.tsx b/packages/dataviews/src/field-types/telephone.tsx
index 9498b5a87f5681..612638e5cbb376 100644
--- a/packages/dataviews/src/field-types/telephone.tsx
+++ b/packages/dataviews/src/field-types/telephone.tsx
@@ -3,9 +3,11 @@
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
+ Field,
+ NormalizedField,
+ Operator,
+ Rules,
SortDirection,
- NormalizedFormat,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
import {
@@ -19,6 +21,11 @@ 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';
function sort( valueA: any, valueB: any, direction: SortDirection ) {
return direction === 'asc'
@@ -26,35 +33,64 @@ function sort( valueA: any, valueB: any, direction: SortDirection ) {
: valueB.localeCompare( valueA );
}
-export default {
- sort,
- isValid: {
+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 > = {
elements: true,
custom: () => null,
- },
- Edit: 'telephone',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- return field.hasElements ? (
-
- ) : (
- field.getValue( { item } )
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
- validOperators: [
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_CONTAINS,
- OPERATOR_NOT_CONTAINS,
- OPERATOR_STARTS_WITH,
- // Multiple selection
- OPERATOR_IS_ANY,
- OPERATOR_IS_NONE,
- OPERATOR_IS_ALL,
- OPERATOR_IS_NOT_ALL,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];
+
+ const validOperators: Operator[] = [
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_CONTAINS,
+ OPERATOR_NOT_CONTAINS,
+ OPERATOR_STARTS_WITH,
+ // Multiple selection
+ OPERATOR_IS_ANY,
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/text.tsx b/packages/dataviews/src/field-types/text.tsx
index fd79e83730c55a..65dbcd7ec15b4b 100644
--- a/packages/dataviews/src/field-types/text.tsx
+++ b/packages/dataviews/src/field-types/text.tsx
@@ -3,8 +3,10 @@
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
- NormalizedFormat,
+ Field,
+ NormalizedField,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -19,6 +21,11 @@ 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';
function sort( valueA: any, valueB: any, direction: SortDirection ) {
return direction === 'asc'
@@ -26,36 +33,65 @@ function sort( valueA: any, valueB: any, direction: SortDirection ) {
: valueB.localeCompare( valueA );
}
-export default {
- sort,
- isValid: {
+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 > = {
elements: true,
custom: () => null,
- },
- Edit: 'text',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- return field.hasElements ? (
-
- ) : (
- field.getValue( { item } )
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
- validOperators: [
- // Single selection
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_CONTAINS,
- OPERATOR_NOT_CONTAINS,
- OPERATOR_STARTS_WITH,
- // Multiple selection
- OPERATOR_IS_ANY,
- OPERATOR_IS_NONE,
- OPERATOR_IS_ALL,
- OPERATOR_IS_NOT_ALL,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];
+
+ const validOperators: Operator[] = [
+ // Single selection
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_CONTAINS,
+ OPERATOR_NOT_CONTAINS,
+ OPERATOR_STARTS_WITH,
+ // Multiple selection
+ OPERATOR_IS_ANY,
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/url.tsx b/packages/dataviews/src/field-types/url.tsx
index 6fd1c50ad2987d..724d7a311dd29b 100644
--- a/packages/dataviews/src/field-types/url.tsx
+++ b/packages/dataviews/src/field-types/url.tsx
@@ -3,8 +3,10 @@
*/
import type {
DataViewRenderFieldProps,
- FieldTypeDefinition,
- NormalizedFormat,
+ Field,
+ NormalizedField,
+ Operator,
+ Rules,
SortDirection,
} from '../types';
import RenderFromElements from './utils/render-from-elements';
@@ -19,6 +21,11 @@ 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';
function sort( valueA: any, valueB: any, direction: SortDirection ) {
return direction === 'asc'
@@ -26,35 +33,64 @@ function sort( valueA: any, valueB: any, direction: SortDirection ) {
: valueB.localeCompare( valueA );
}
-export default {
- sort,
- isValid: {
+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 > = {
elements: true,
custom: () => null,
- },
- Edit: 'url',
- render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
- return field.hasElements ? (
-
- ) : (
- field.getValue( { item } )
- );
- },
- getFormat: (): NormalizedFormat => ( {} ),
- enableSorting: true,
- filterBy: {
- defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
- validOperators: [
- OPERATOR_IS,
- OPERATOR_IS_NOT,
- OPERATOR_CONTAINS,
- OPERATOR_NOT_CONTAINS,
- OPERATOR_STARTS_WITH,
- // Multiple selection
- OPERATOR_IS_ANY,
- OPERATOR_IS_NONE,
- OPERATOR_IS_ALL,
- OPERATOR_IS_NOT_ALL,
- ],
- },
-} satisfies FieldTypeDefinition< any >;
+ };
+
+ const defaultOperators: Operator[] = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];
+
+ const validOperators: Operator[] = [
+ OPERATOR_IS,
+ OPERATOR_IS_NOT,
+ OPERATOR_CONTAINS,
+ OPERATOR_NOT_CONTAINS,
+ OPERATOR_STARTS_WITH,
+ // Multiple selection
+ OPERATOR_IS_ANY,
+ 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: {},
+ };
+}
diff --git a/packages/dataviews/src/field-types/utils/get-filter-by.ts b/packages/dataviews/src/field-types/utils/get-filter-by.ts
new file mode 100644
index 00000000000000..a8c82c8e605eca
--- /dev/null
+++ b/packages/dataviews/src/field-types/utils/get-filter-by.ts
@@ -0,0 +1,49 @@
+/**
+ * Internal dependencies
+ */
+import type { Field, Operator, NormalizedFilterByConfig } from '../../types';
+
+function getFilterBy< Item >(
+ field: Field< Item >,
+ defaultOperators: Operator[],
+ validOperators: Operator[]
+): NormalizedFilterByConfig | false {
+ if ( field.filterBy === false ) {
+ 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 ) {
+ return false;
+ }
+
+ return {
+ operators: defaultOperators,
+ };
+}
+
+export default getFilterBy;
diff --git a/packages/dataviews/src/field-types/utils/get-value-from-id.ts b/packages/dataviews/src/field-types/utils/get-value-from-id.ts
new file mode 100644
index 00000000000000..558f42af6065e7
--- /dev/null
+++ b/packages/dataviews/src/field-types/utils/get-value-from-id.ts
@@ -0,0 +1,17 @@
+const getValueFromId =
+ ( id: string ) =>
+ ( { item }: { item: any } ) => {
+ const path = id.split( '.' );
+ let value = item;
+ for ( const segment of path ) {
+ if ( value.hasOwnProperty( segment ) ) {
+ value = value[ segment ];
+ } else {
+ value = undefined;
+ }
+ }
+
+ return value;
+ };
+
+export default getValueFromId;
diff --git a/packages/dataviews/src/utils/has-elements.ts b/packages/dataviews/src/field-types/utils/has-elements.ts
similarity index 82%
rename from packages/dataviews/src/utils/has-elements.ts
rename to packages/dataviews/src/field-types/utils/has-elements.ts
index 9fcee088f35a69..ae1414f649ccb8 100644
--- a/packages/dataviews/src/utils/has-elements.ts
+++ b/packages/dataviews/src/field-types/utils/has-elements.ts
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
-import type { Field } from '../types/field-api';
+import type { Field } from '../../types/field-api';
export default function hasElements< Item >( field: Field< Item > ): boolean {
return (
diff --git a/packages/dataviews/src/field-types/utils/normalize-fields.ts b/packages/dataviews/src/field-types/utils/normalize-fields.ts
new file mode 100644
index 00000000000000..0f564ea6ba9458
--- /dev/null
+++ b/packages/dataviews/src/field-types/utils/normalize-fields.ts
@@ -0,0 +1,25 @@
+/**
+ * External dependencies
+ */
+
+/**
+ * Internal dependencies
+ */
+import getFieldTypeDefinition 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 = getFieldTypeDefinition< Item >( field.type );
+
+ return normalize( field );
+ } );
+}
diff --git a/packages/dataviews/src/field-types/utils/set-value-from-id.ts b/packages/dataviews/src/field-types/utils/set-value-from-id.ts
new file mode 100644
index 00000000000000..8419d0b0ea3cc7
--- /dev/null
+++ b/packages/dataviews/src/field-types/utils/set-value-from-id.ts
@@ -0,0 +1,17 @@
+const setValueFromId =
+ ( id: string ) =>
+ ( { value }: { value: any } ) => {
+ const path = id.split( '.' );
+ const result: any = {};
+ let current = result;
+
+ for ( const segment of path.slice( 0, -1 ) ) {
+ current[ segment ] = {};
+ current = current[ segment ];
+ }
+
+ current[ path.at( -1 )! ] = value;
+ return result;
+ };
+
+export default setValueFromId;
diff --git a/packages/dataviews/src/utils/week-starts-on.ts b/packages/dataviews/src/field-types/utils/week-starts-on.ts
similarity index 93%
rename from packages/dataviews/src/utils/week-starts-on.ts
rename to packages/dataviews/src/field-types/utils/week-starts-on.ts
index 60651023751289..eb8576756dab88 100644
--- a/packages/dataviews/src/utils/week-starts-on.ts
+++ b/packages/dataviews/src/field-types/utils/week-starts-on.ts
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
-import type { DayNumber, DayString } from '../types/field-api';
+import type { DayNumber, DayString } from '../../types/field-api';
export const DAYS_OF_WEEK: DayString[] = [
'sunday',
diff --git a/packages/dataviews/src/hooks/use-form-validity.ts b/packages/dataviews/src/hooks/use-form-validity.ts
index 1167c9af763644..3c7d2a3d6fb02a 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 '../utils/normalize-fields';
+import normalizeFields from '../field-types/utils/normalize-fields';
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 946abeff64cf82..ffdc0916dfd6e0 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 '../utils/normalize-fields';
+import normalizeFields from '../field-types/utils/normalize-fields';
import type { Field } from '../types';
describe( 'normalizeFields: default getValue', () => {
diff --git a/packages/dataviews/src/utils/filter-sort-and-paginate.ts b/packages/dataviews/src/utils/filter-sort-and-paginate.ts
index 5738fa331fc689..362d80ab68a4bc 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 './normalize-fields';
+import normalizeFields from '../field-types/utils/normalize-fields';
import type { Field, View } from '../types';
function normalizeSearchInput( input = '' ) {
diff --git a/packages/dataviews/src/utils/normalize-fields.ts b/packages/dataviews/src/utils/normalize-fields.ts
deleted file mode 100644
index 43b859ccd34628..00000000000000
--- a/packages/dataviews/src/utils/normalize-fields.ts
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * External dependencies
- */
-import type { FunctionComponent } from 'react';
-
-/**
- * Internal dependencies
- */
-import getFieldTypeDefinition from '../field-types';
-import type {
- DataViewRenderFieldProps,
- Field,
- FieldTypeDefinition,
- NormalizedFilterByConfig,
- NormalizedField,
- FormatDate,
-} from '../types';
-import { getControl } from '../dataform-controls';
-import {
- ALL_OPERATORS,
- OPERATOR_BETWEEN,
- SINGLE_SELECTION_OPERATORS,
-} from '../constants';
-import hasElements from './has-elements';
-
-const getValueFromId =
- ( id: string ) =>
- ( { item }: { item: any } ) => {
- const path = id.split( '.' );
- let value = item;
- for ( const segment of path ) {
- if ( value.hasOwnProperty( segment ) ) {
- value = value[ segment ];
- } else {
- value = undefined;
- }
- }
-
- return value;
- };
-
-const setValueFromId =
- ( id: string ) =>
- ( { value }: { value: any } ) => {
- const path = id.split( '.' );
- const result: any = {};
- let current = result;
-
- for ( const segment of path.slice( 0, -1 ) ) {
- current[ segment ] = {};
- current = current[ segment ];
- }
-
- current[ path.at( -1 )! ] = value;
- return result;
- };
-
-function getFilterBy< Item >(
- field: Field< Item >,
- fieldTypeDefinition: FieldTypeDefinition< Item >
-): NormalizedFilterByConfig | false {
- if ( field.filterBy === false ) {
- 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 = !! fieldTypeDefinition.filterBy
- ? fieldTypeDefinition.filterBy.defaultOperators
- : [];
- }
-
- // Make sure only valid operators are included.
- let validOperators = ALL_OPERATORS;
- if ( typeof fieldTypeDefinition.filterBy === 'object' ) {
- validOperators = fieldTypeDefinition.filterBy.validOperators;
- }
- operators = operators.filter( ( operator ) =>
- validOperators.includes( operator )
- );
-
- // The `between` operator is not supported when elements are provided.
- if ( hasElements( field ) && operators.includes( OPERATOR_BETWEEN ) ) {
- operators = operators.filter(
- ( operator ) => operator !== OPERATOR_BETWEEN
- );
- }
-
- // Do not allow mixing single & multiselection operators.
- // Remove multiselection operators if any of the single selection ones is present.
- const hasSingleSelectionOperator = operators.some( ( operator ) =>
- SINGLE_SELECTION_OPERATORS.includes( operator )
- );
- if ( hasSingleSelectionOperator ) {
- operators = operators.filter( ( operator ) =>
- // The 'Between' operator is unique as it can be combined with single selection operators.
- [ ...SINGLE_SELECTION_OPERATORS, OPERATOR_BETWEEN ].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 ( fieldTypeDefinition.filterBy === false ) {
- return false;
- }
-
- let defaultOperators = fieldTypeDefinition.filterBy.defaultOperators;
- // The `between` operator is not supported when elements are provided.
- if (
- hasElements( field ) &&
- defaultOperators.includes( OPERATOR_BETWEEN )
- ) {
- defaultOperators = defaultOperators.filter(
- ( operator ) => operator !== OPERATOR_BETWEEN
- );
- }
-
- return {
- operators: defaultOperators,
- };
-}
-
-/**
- * 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 fieldTypeDefinition = getFieldTypeDefinition< Item >(
- field.type
- );
- const getValue = field.getValue || getValueFromId( field.id );
- const setValue = field.setValue || setValueFromId( field.id );
-
- const sort =
- field.sort ??
- function sort( a, b, direction ) {
- return fieldTypeDefinition.sort(
- getValue( { item: a } ),
- getValue( { item: b } ),
- direction
- );
- };
-
- const isValid = {
- ...fieldTypeDefinition.isValid,
- ...field.isValid,
- };
-
- const Edit = getControl( field, fieldTypeDefinition );
-
- const render =
- field.render ??
- function render( {
- item,
- field: renderedField,
- }: DataViewRenderFieldProps< Item > ) {
- return (
- fieldTypeDefinition.render as FunctionComponent<
- DataViewRenderFieldProps< Item >
- >
- )( { item, field: renderedField } );
- };
-
- const filterBy = getFilterBy( field, fieldTypeDefinition );
-
- /**
- * NormalizedField is a discriminated union type: the shape of the format property
- * depends on the type property. For example, for the 'date' type, the format
- * contains date or weekStartsOn — which are not valid for other types.
- *
- * Being type and format interdependent, we need to write the code
- * in a way that TypeScript is able to statically infer the types.
- * That's why we have a return branch for every item in the union type.
- *
- * See a longer explanation with examples at
- * https://github.com/WordPress/gutenberg/pull/72999#discussion_r2523145453
- */
- const { type, ...fieldWithoutType } = field;
-
- const baseField = {
- ...fieldWithoutType,
- label: field.label || field.id,
- header: field.header || field.label || field.id,
- getValue,
- setValue,
- render,
- sort,
- isValid,
- Edit,
- hasElements: hasElements( field ),
- enableHiding: field.enableHiding ?? true,
- enableSorting:
- field.enableSorting ??
- fieldTypeDefinition.enableSorting ??
- true,
- filterBy,
- readOnly: field.readOnly ?? fieldTypeDefinition.readOnly ?? false,
- format: {},
- };
-
- if ( field.type === 'date' ) {
- const format = fieldTypeDefinition.getFormat(
- field
- ) as Required< FormatDate >;
-
- return {
- ...baseField,
- type: 'date',
- format,
- };
- }
-
- return {
- ...baseField,
- type: field.type,
- };
- } );
-}
From 1cf3064c5837681da326f718a9972aad140d4374 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 12:03:17 +0100
Subject: [PATCH 05/15] Remove the concept of field type definition
---
packages/dataviews/src/field-types/index.tsx | 2 +-
.../src/field-types/utils/normalize-fields.ts | 4 +-
packages/dataviews/src/types/field-api.ts | 59 -------------------
3 files changed, 3 insertions(+), 62 deletions(-)
diff --git a/packages/dataviews/src/field-types/index.tsx b/packages/dataviews/src/field-types/index.tsx
index e6c943f0677b23..fada223253f5f3 100644
--- a/packages/dataviews/src/field-types/index.tsx
+++ b/packages/dataviews/src/field-types/index.tsx
@@ -100,7 +100,7 @@ function normalizeField< Item >(
*
* @return A field type definition.
*/
-export default function getFieldTypeDefinition< Item >(
+export default function getNormalizeFieldFunction< Item >(
type?: FieldType
): ( field: Field< Item > ) => NormalizedField< Item > {
if ( 'email' === type ) {
diff --git a/packages/dataviews/src/field-types/utils/normalize-fields.ts b/packages/dataviews/src/field-types/utils/normalize-fields.ts
index 0f564ea6ba9458..0abb1f55f7332c 100644
--- a/packages/dataviews/src/field-types/utils/normalize-fields.ts
+++ b/packages/dataviews/src/field-types/utils/normalize-fields.ts
@@ -5,7 +5,7 @@
/**
* Internal dependencies
*/
-import getFieldTypeDefinition from '..';
+import getNormalizeFieldFunction from '..';
import type { Field, NormalizedField } from '../../types';
/**
@@ -18,7 +18,7 @@ export default function normalizeFields< Item >(
fields: Field< Item >[]
): NormalizedField< Item >[] {
return fields.map( ( field ) => {
- const normalize = getFieldTypeDefinition< Item >( field.type );
+ const normalize = getNormalizeFieldFunction< Item >( field.type );
return normalize( field );
} );
diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts
index 5f82475a4498f2..b8e5b6051cfbb3 100644
--- a/packages/dataviews/src/types/field-api.ts
+++ b/packages/dataviews/src/types/field-api.ts
@@ -52,18 +52,6 @@ export interface NormalizedFilterByConfig {
isPrimary?: boolean;
}
-interface FilterConfigForType {
- /**
- * What operators are used by default.
- */
- defaultOperators: Operator[];
-
- /**
- * What operators are supported by the field.
- */
- validOperators: Operator[];
-}
-
export type Operator =
| 'is'
| 'isNot'
@@ -103,53 +91,6 @@ export type FieldType =
| 'url'
| 'array';
-/**
- * An abstract interface for Field based on the field type.
- */
-export type FieldTypeDefinition< Item > = {
- /**
- * Callback used to sort the field.
- */
- sort: ( a: Item, b: Item, direction: SortDirection ) => number;
-
- /**
- * Callback used to validate the field.
- */
- isValid: Rules< Item >;
-
- /**
- * Callback used to render an edit control for the field or control name.
- */
- Edit:
- | ComponentType< DataFormControlProps< Item > >
- | string
- | EditConfig
- | null;
-
- /**
- * Callback used to render the field.
- */
- render: ComponentType< DataViewRenderFieldProps< Item > >;
-
- /**
- * The filter config for the field.
- */
- filterBy: FilterConfigForType | false;
-
- getFormat: ( field: Field< Item > ) => NormalizedFormat;
-
- /**
- * Whether the field is readOnly.
- * If `true`, the value will be rendered using the `render` callback.
- */
- readOnly?: boolean;
-
- /**
- * Whether the field is sortable.
- */
- enableSorting: boolean;
-};
-
export type Rules< Item > = {
required?: boolean;
elements?: boolean;
From 92a1eef9a7d6ba252c0d6f06b82e9463b154686e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 12:14:35 +0100
Subject: [PATCH 06/15] Simplify normalized field
---
.../src/field-types/utils/get-filter-by.ts | 5 ++-
packages/dataviews/src/types/field-api.ts | 37 ++-----------------
2 files changed, 7 insertions(+), 35 deletions(-)
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 a8c82c8e605eca..333ed12b959365 100644
--- a/packages/dataviews/src/field-types/utils/get-filter-by.ts
+++ b/packages/dataviews/src/field-types/utils/get-filter-by.ts
@@ -1,13 +1,13 @@
/**
* Internal dependencies
*/
-import type { Field, Operator, NormalizedFilterByConfig } from '../../types';
+import type { Field, FilterByConfig, Operator } from '../../types';
function getFilterBy< Item >(
field: Field< Item >,
defaultOperators: Operator[],
validOperators: Operator[]
-): NormalizedFilterByConfig | false {
+): Required< FilterByConfig > | false {
if ( field.filterBy === false ) {
return false;
}
@@ -42,6 +42,7 @@ function getFilterBy< Item >(
}
return {
+ isPrimary: false,
operators: defaultOperators,
};
}
diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts
index b8e5b6051cfbb3..c887de785c2a25 100644
--- a/packages/dataviews/src/types/field-api.ts
+++ b/packages/dataviews/src/types/field-api.ts
@@ -37,21 +37,6 @@ export interface FilterByConfig {
isPrimary?: boolean;
}
-export interface NormalizedFilterByConfig {
- /**
- * The list of operators supported by the field.
- */
- operators: Operator[];
-
- /**
- * Whether it is a primary filter.
- *
- * A primary filter is always visible and is not listed in the "Add filter" component,
- * except for the list layout where it behaves like a secondary filter.
- */
- isPrimary?: boolean;
-}
-
export type Operator =
| 'is'
| 'isNot'
@@ -282,20 +267,11 @@ export type DayString =
| 'friday'
| 'saturday';
-type NormalizedFieldBase< Item > = Omit< Field< Item >, 'Edit' > & {
- label: string;
- header: string | ReactElement;
- getValue: ( args: { item: Item } ) => any;
- setValue: ( args: { item: Item; value: any } ) => DeepPartial< Item >;
- render: ComponentType< DataViewRenderFieldProps< Item > >;
+type NormalizedFieldBase< Item > = Required< Field< Item > > & {
Edit: ComponentType< DataFormControlProps< Item > > | null;
+ filterBy: Required< FilterByConfig > | false;
+ format: {};
hasElements: boolean;
- sort: ( a: Item, b: Item, direction: SortDirection ) => number;
- isValid: Rules< Item >;
- enableHiding: boolean;
- enableSorting: boolean;
- filterBy: NormalizedFilterByConfig | false;
- readOnly: boolean;
};
type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
@@ -303,13 +279,8 @@ type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
format: Required< FormatDate >;
};
-type NormalizedFieldGeneric< Item > = NormalizedFieldBase< Item > & {
- type?: Exclude< FieldType, 'date' >;
- format: {};
-};
-
export type NormalizedField< Item > =
- | NormalizedFieldGeneric< Item >
+ | NormalizedFieldBase< Item >
| NormalizedFieldDate< Item >;
/**
From 741b019f30ac81fb6bd300c8d41753b252694ecd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 12:17:04 +0100
Subject: [PATCH 07/15] Add changelog
---
packages/dataviews/CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md
index 710b46241230d4..4a5b6b09c4be28 100644
--- a/packages/dataviews/CHANGELOG.md
+++ b/packages/dataviews/CHANGELOG.md
@@ -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)
From 6a05ef68bfa83c96f4f1fbf262d860a00d579344 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 13:43:12 +0100
Subject: [PATCH 08/15] NormalizedField: refactor, there are optional props
---
packages/dataviews/src/dataform-controls/index.tsx | 2 +-
packages/dataviews/src/types/field-api.ts | 14 ++++++++++++--
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/packages/dataviews/src/dataform-controls/index.tsx b/packages/dataviews/src/dataform-controls/index.tsx
index b95256e310a7af..0c11f7a1225843 100644
--- a/packages/dataviews/src/dataform-controls/index.tsx
+++ b/packages/dataviews/src/dataform-controls/index.tsx
@@ -73,7 +73,7 @@ export function createConfiguredControl( config: EditConfig ) {
export function getControl< Item >(
field: Field< Item >,
fallback: string | null
-) {
+): ComponentType< DataFormControlProps< Item > > | null {
if ( typeof field.Edit === 'function' ) {
return field.Edit;
}
diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts
index c887de785c2a25..0f888af7cbb8b1 100644
--- a/packages/dataviews/src/types/field-api.ts
+++ b/packages/dataviews/src/types/field-api.ts
@@ -267,11 +267,21 @@ export type DayString =
| 'friday'
| 'saturday';
-type NormalizedFieldBase< Item > = Required< Field< Item > > & {
+type NormalizedFieldBase< Item > = Omit< Field< Item >, 'Edit' > & {
+ label: string;
+ header: string | ReactElement;
+ getValue: ( args: { item: Item } ) => any;
+ setValue: ( args: { item: Item; value: any } ) => DeepPartial< Item >;
+ render: ComponentType< DataViewRenderFieldProps< Item > >;
Edit: ComponentType< DataFormControlProps< Item > > | null;
+ hasElements: boolean;
+ sort: ( a: Item, b: Item, direction: SortDirection ) => number;
+ isValid: Rules< Item >;
+ enableHiding: boolean;
+ enableSorting: boolean;
filterBy: Required< FilterByConfig > | false;
+ readOnly: boolean;
format: {};
- hasElements: boolean;
};
type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
From 407ad4265b5966d17e5770d0a673235ce607c395 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 14:11:09 +0100
Subject: [PATCH 09/15] Fix FormatDate issues
---
packages/dataviews/src/dataform-controls/date.tsx | 13 +++++++++++--
packages/dataviews/src/field-types/date.tsx | 15 +++++++++------
packages/dataviews/src/types/field-api.ts | 2 +-
3 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/packages/dataviews/src/dataform-controls/date.tsx b/packages/dataviews/src/dataform-controls/date.tsx
index dd97fd7ebcf8c8..2fba6822b7322f 100644
--- a/packages/dataviews/src/dataform-controls/date.tsx
+++ b/packages/dataviews/src/dataform-controls/date.tsx
@@ -48,6 +48,7 @@ import { unlock } from '../lock-unlock';
import type {
DataFormControlProps,
FieldValidity,
+ FormatDate,
NormalizedField,
} from '../types';
import getCustomValidity from './utils/get-custom-validity';
@@ -273,7 +274,11 @@ function CalendarDateControl< 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").
+ weekStartsOn = weekStartsOnToNumber(
+ ( fieldFormat as Required< FormatDate > ).weekStartsOn
+ );
}
const fieldValue = getValue( { item: data } );
@@ -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").
+ weekStartsOn = weekStartsOnToNumber(
+ ( fieldFormat as Required< FormatDate > ).weekStartsOn
+ );
}
const onChangeCallback = useCallback(
diff --git a/packages/dataviews/src/field-types/date.tsx b/packages/dataviews/src/field-types/date.tsx
index 89b93efd9d3828..ae6afcd46a94d4 100644
--- a/packages/dataviews/src/field-types/date.tsx
+++ b/packages/dataviews/src/field-types/date.tsx
@@ -67,16 +67,19 @@ function render( { item, field }: DataViewRenderFieldProps< any > ) {
return '';
}
- // Not all fields have format, but date fields do.
+ // 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").
//
- // At runtime, this method will never be called for non-date fields.
- // However, the type system does not know this, so we need to check it.
- // There's an opportunity here to improve the type system.
+ // There're no runtime paths where this render function is called with a non-date field,
+ // but TypeScript is unable to infer this, hence the type assertion.
+ let format: Required< FormatDate >;
if ( field.type !== 'date' ) {
- return '';
+ format = getFormat( field as Field< any > );
+ } else {
+ format = field.format as Required< FormatDate >;
}
- return dateI18n( field.format.date, getDate( value ) );
+ return dateI18n( format.weekStartsOn, getDate( value ) );
}
export default function normalizeField< Item >(
diff --git a/packages/dataviews/src/types/field-api.ts b/packages/dataviews/src/types/field-api.ts
index 0f888af7cbb8b1..31f1011eeaa454 100644
--- a/packages/dataviews/src/types/field-api.ts
+++ b/packages/dataviews/src/types/field-api.ts
@@ -284,7 +284,7 @@ type NormalizedFieldBase< Item > = Omit< Field< Item >, 'Edit' > & {
format: {};
};
-type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
+export type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
type: 'date';
format: Required< FormatDate >;
};
From 96b68c0db5bdabaf4a7eae002d6102150fc4948b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 14:17:38 +0100
Subject: [PATCH 10/15] Revert unnecessary exports
---
packages/dataviews/src/dataform-controls/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/dataviews/src/dataform-controls/index.tsx b/packages/dataviews/src/dataform-controls/index.tsx
index 0c11f7a1225843..a2b97ee3a8016c 100644
--- a/packages/dataviews/src/dataform-controls/index.tsx
+++ b/packages/dataviews/src/dataform-controls/index.tsx
@@ -50,13 +50,13 @@ const FORM_CONTROLS: FormControls = {
toggleGroup,
};
-export function isEditConfig( value: any ): value is EditConfig {
+function isEditConfig( value: any ): value is EditConfig {
return (
value && typeof value === 'object' && typeof value.control === 'string'
);
}
-export function createConfiguredControl( config: EditConfig ) {
+function createConfiguredControl( config: EditConfig ) {
const { control, ...controlConfig } = config;
const BaseControlType = getControlByType( control );
if ( BaseControlType === null ) {
From 93d0632c31bde47ea06c7913922341a30f68e30b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 15:25:05 +0100
Subject: [PATCH 11/15] Fix normalizeFields test
---
packages/dataviews/src/test/normalize-fields.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/packages/dataviews/src/test/normalize-fields.ts b/packages/dataviews/src/test/normalize-fields.ts
index ffdc0916dfd6e0..7b1296e00c8305 100644
--- a/packages/dataviews/src/test/normalize-fields.ts
+++ b/packages/dataviews/src/test/normalize-fields.ts
@@ -165,7 +165,10 @@ describe( 'normalizeFields: default getValue', () => {
];
const normalizedFields = normalizeFields( fields );
const result = normalizedFields[ 0 ].filterBy;
- expect( result ).toStrictEqual( { operators: [ 'is', 'isNot' ] } );
+ expect( result ).toStrictEqual( {
+ isPrimary: false,
+ operators: [ 'is', 'isNot' ],
+ } );
} );
it( 'returns the default field type definition if undefined for untyped field (for primary filters)', () => {
const fields: Field< {} >[] = [
@@ -194,6 +197,7 @@ describe( 'normalizeFields: default getValue', () => {
const normalizedFields = normalizeFields( fields );
const result = normalizedFields[ 0 ].filterBy;
expect( result ).toStrictEqual( {
+ isPrimary: false,
operators: [
'is',
'isNot',
@@ -216,6 +220,7 @@ describe( 'normalizeFields: default getValue', () => {
const normalizedFields = normalizeFields( fields );
const result = normalizedFields[ 0 ].filterBy;
expect( result ).toStrictEqual( {
+ isPrimary: false,
operators: [
'is',
'isNot',
From 49d43ca89b50d0a4b39be80236f6ca5c91f885a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Wed, 19 Nov 2025 18:06:56 +0100
Subject: [PATCH 12/15] NormalizedFieldDate
---
.../src/components/dataviews-filters/filter.tsx | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/packages/dataviews/src/components/dataviews-filters/filter.tsx b/packages/dataviews/src/components/dataviews-filters/filter.tsx
index bb21598b43b225..f41c50dd1e6ba1 100644
--- a/packages/dataviews/src/components/dataviews-filters/filter.tsx
+++ b/packages/dataviews/src/components/dataviews-filters/filter.tsx
@@ -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
*/
@@ -57,6 +54,7 @@ import {
import type {
Filter,
NormalizedField,
+ NormalizedFieldDate,
NormalizedFilter,
Operator,
Option,
@@ -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;
@@ -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;
From c336ab3d015f70704add0bccb77905ec76765766 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Thu, 20 Nov 2025 10:40:42 +0100
Subject: [PATCH 13/15] Fix sorting
---
packages/dataviews/src/field-types/array.tsx | 36 ++++++------
.../dataviews/src/field-types/boolean.tsx | 37 ++++++------
packages/dataviews/src/field-types/color.tsx | 57 ++++++++++---------
packages/dataviews/src/field-types/date.tsx | 16 +++---
.../dataviews/src/field-types/datetime.tsx | 16 +++---
packages/dataviews/src/field-types/email.tsx | 15 +++--
packages/dataviews/src/field-types/index.tsx | 11 ++--
.../dataviews/src/field-types/integer.tsx | 10 ++--
packages/dataviews/src/field-types/number.tsx | 10 ++--
.../dataviews/src/field-types/telephone.tsx | 14 +++--
packages/dataviews/src/field-types/text.tsx | 14 +++--
packages/dataviews/src/field-types/url.tsx | 14 +++--
.../src/stories/dataviews.fixtures.tsx | 1 +
13 files changed, 139 insertions(+), 112 deletions(-)
diff --git a/packages/dataviews/src/field-types/array.tsx b/packages/dataviews/src/field-types/array.tsx
index 9015f14360130e..50fc08b519a645 100644
--- a/packages/dataviews/src/field-types/array.tsx
+++ b/packages/dataviews/src/field-types/array.tsx
@@ -26,23 +26,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-// 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 );
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
const value = field.getValue( { item } ) || [];
return value.join( ', ' );
@@ -62,6 +45,25 @@ export default function normalizeField< 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, normalizedField ) => {
diff --git a/packages/dataviews/src/field-types/boolean.tsx b/packages/dataviews/src/field-types/boolean.tsx
index 677184df2a9f9c..3c1ab3059f4dd6 100644
--- a/packages/dataviews/src/field-types/boolean.tsx
+++ b/packages/dataviews/src/field-types/boolean.tsx
@@ -22,23 +22,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( a: any, b: any, direction: SortDirection ) {
- const boolA = Boolean( a );
- const boolB = Boolean( b );
-
- if ( boolA === boolB ) {
- return 0;
- }
-
- // In ascending order, false comes before true
- if ( direction === 'asc' ) {
- return boolA ? 1 : -1;
- }
-
- // In descending order, true comes before false
- return boolA ? -1 : 1;
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
if ( field.hasElements ) {
return ;
@@ -60,6 +43,26 @@ export default function normalizeField< 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 ) => {
+ 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;
+ }
+
+ // In descending order, true comes before false
+ return boolA ? -1 : 1;
+ };
+
const isValid: Rules< Item > = {
elements: true,
custom: ( item: any, normalizedField ) => {
diff --git a/packages/dataviews/src/field-types/color.tsx b/packages/dataviews/src/field-types/color.tsx
index 025b7b8bf0ada5..907c62f8ee0620 100644
--- a/packages/dataviews/src/field-types/color.tsx
+++ b/packages/dataviews/src/field-types/color.tsx
@@ -32,34 +32,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function 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;
- }
- return direction === 'asc' ? hslA.l - hslB.l : hslB.l - hslA.l;
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
if ( field.hasElements ) {
return ;
@@ -94,6 +66,35 @@ export default function normalizeField< Item >(
): NormalizedField< Item > {
const getValue = field.getValue || getValueFromId( field.id );
const setValue = field.setValue || setValueFromId( field.id );
+
+ 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;
+ }
+ return direction === 'asc' ? hslA.l - hslB.l : hslB.l - hslA.l;
+ };
+
const isValid: Rules< Item > = {
elements: true,
custom: ( item: any, normalizedField ) => {
diff --git a/packages/dataviews/src/field-types/date.tsx b/packages/dataviews/src/field-types/date.tsx
index ae6afcd46a94d4..c6cb5ba3ad59d4 100644
--- a/packages/dataviews/src/field-types/date.tsx
+++ b/packages/dataviews/src/field-types/date.tsx
@@ -35,13 +35,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-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 getFormat( field: Field< any > ): Required< FormatDate > {
return {
date:
@@ -92,6 +85,15 @@ export default function normalizeField< Item >(
custom: () => null,
};
+ 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;
+ };
+
const defaultOperators: Operator[] = [
OPERATOR_ON,
OPERATOR_NOT_ON,
diff --git a/packages/dataviews/src/field-types/datetime.tsx b/packages/dataviews/src/field-types/datetime.tsx
index dc7ac51c77eabd..bdf424c1ec57a3 100644
--- a/packages/dataviews/src/field-types/datetime.tsx
+++ b/packages/dataviews/src/field-types/datetime.tsx
@@ -27,13 +27,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-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 render( { item, field }: DataViewRenderFieldProps< any > ) {
if ( field.elements ) {
return ;
@@ -62,6 +55,15 @@ export default function normalizeField< Item >(
custom: () => null,
};
+ 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;
+ };
+
const defaultOperators: Operator[] = [
OPERATOR_ON,
OPERATOR_NOT_ON,
diff --git a/packages/dataviews/src/field-types/email.tsx b/packages/dataviews/src/field-types/email.tsx
index 5febdb5a00e4a1..ad4e03fa6b0f49 100644
--- a/packages/dataviews/src/field-types/email.tsx
+++ b/packages/dataviews/src/field-types/email.tsx
@@ -32,12 +32,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( valueA: any, valueB: any, direction: SortDirection ) {
- return direction === 'asc'
- ? valueA.localeCompare( valueB )
- : valueB.localeCompare( valueA );
-}
-
// Email validation regex based on HTML5 spec
// https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
const emailRegex =
@@ -56,6 +50,15 @@ export default function normalizeField< 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 ) => {
+ const valueA = getValue( { item: a } );
+ const valueB = getValue( { item: b } );
+ return direction === 'asc'
+ ? valueA.localeCompare( valueB )
+ : valueB.localeCompare( valueA );
+ };
+
const isValid: Rules< Item > = {
elements: true,
custom: ( item: any, normalizedField ) => {
diff --git a/packages/dataviews/src/field-types/index.tsx b/packages/dataviews/src/field-types/index.tsx
index fada223253f5f3..4534a2ff3e6d6f 100644
--- a/packages/dataviews/src/field-types/index.tsx
+++ b/packages/dataviews/src/field-types/index.tsx
@@ -42,13 +42,16 @@ function normalizeField< Item >(
};
const sort = ( a: any, b: any, direction: SortDirection ) => {
- if ( typeof a === 'number' && typeof b === 'number' ) {
- return direction === 'asc' ? a - b : b - a;
+ 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'
- ? a.localeCompare( b )
- : b.localeCompare( a );
+ ? valueA.localeCompare( valueB )
+ : valueB.localeCompare( valueA );
};
const render = ( {
diff --git a/packages/dataviews/src/field-types/integer.tsx b/packages/dataviews/src/field-types/integer.tsx
index b76d559ee78f95..19864a1fab7ae8 100644
--- a/packages/dataviews/src/field-types/integer.tsx
+++ b/packages/dataviews/src/field-types/integer.tsx
@@ -34,10 +34,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( a: any, b: any, direction: SortDirection ) {
- return direction === 'asc' ? a - b : b - a;
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
return field.hasElements ? (
@@ -66,6 +62,12 @@ export default function normalizeField< Item >(
},
};
+ 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[] = [
OPERATOR_IS,
OPERATOR_IS_NOT,
diff --git a/packages/dataviews/src/field-types/number.tsx b/packages/dataviews/src/field-types/number.tsx
index 966290104ad9b6..ca6d54fbeb352e 100644
--- a/packages/dataviews/src/field-types/number.tsx
+++ b/packages/dataviews/src/field-types/number.tsx
@@ -34,10 +34,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( a: any, b: any, direction: SortDirection ) {
- return direction === 'asc' ? a - b : b - a;
-}
-
function isEmpty( value: unknown ): value is '' | undefined | null {
return value === '' || value === undefined || value === null;
}
@@ -73,6 +69,12 @@ export default function normalizeField< Item >(
},
};
+ 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[] = [
OPERATOR_IS,
OPERATOR_IS_NOT,
diff --git a/packages/dataviews/src/field-types/telephone.tsx b/packages/dataviews/src/field-types/telephone.tsx
index 612638e5cbb376..19d2040c820de0 100644
--- a/packages/dataviews/src/field-types/telephone.tsx
+++ b/packages/dataviews/src/field-types/telephone.tsx
@@ -27,12 +27,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( valueA: any, valueB: any, direction: SortDirection ) {
- return direction === 'asc'
- ? valueA.localeCompare( valueB )
- : valueB.localeCompare( valueA );
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
return field.hasElements ? (
@@ -51,6 +45,14 @@ export default function normalizeField< Item >(
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[] = [
diff --git a/packages/dataviews/src/field-types/text.tsx b/packages/dataviews/src/field-types/text.tsx
index 65dbcd7ec15b4b..036863ff8ad058 100644
--- a/packages/dataviews/src/field-types/text.tsx
+++ b/packages/dataviews/src/field-types/text.tsx
@@ -27,12 +27,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( valueA: any, valueB: any, direction: SortDirection ) {
- return direction === 'asc'
- ? valueA.localeCompare( valueB )
- : valueB.localeCompare( valueA );
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
return field.hasElements ? (
@@ -51,6 +45,14 @@ export default function normalizeField< Item >(
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[] = [
diff --git a/packages/dataviews/src/field-types/url.tsx b/packages/dataviews/src/field-types/url.tsx
index 724d7a311dd29b..d43dcfd885e57f 100644
--- a/packages/dataviews/src/field-types/url.tsx
+++ b/packages/dataviews/src/field-types/url.tsx
@@ -27,12 +27,6 @@ import getValueFromId from './utils/get-value-from-id';
import setValueFromId from './utils/set-value-from-id';
import getFilterBy from './utils/get-filter-by';
-function sort( valueA: any, valueB: any, direction: SortDirection ) {
- return direction === 'asc'
- ? valueA.localeCompare( valueB )
- : valueB.localeCompare( valueA );
-}
-
function render( { item, field }: DataViewRenderFieldProps< any > ) {
return field.hasElements ? (
@@ -51,6 +45,14 @@ export default function normalizeField< Item >(
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[] = [
diff --git a/packages/dataviews/src/stories/dataviews.fixtures.tsx b/packages/dataviews/src/stories/dataviews.fixtures.tsx
index 88928639d6458a..86349786fdd3dc 100644
--- a/packages/dataviews/src/stories/dataviews.fixtures.tsx
+++ b/packages/dataviews/src/stories/dataviews.fixtures.tsx
@@ -1425,6 +1425,7 @@ export const fields: Field< SpaceObject >[] = [
},
{
label: 'Type',
+ // type: 'text',
id: 'type',
enableHiding: false,
elements: [
From 2fedda20948f30f07f3a9189ea91e0893723adb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Thu, 20 Nov 2025 17:20:51 +0100
Subject: [PATCH 14/15] Remove leftover
---
packages/dataviews/src/stories/dataviews.fixtures.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/dataviews/src/stories/dataviews.fixtures.tsx b/packages/dataviews/src/stories/dataviews.fixtures.tsx
index 86349786fdd3dc..88928639d6458a 100644
--- a/packages/dataviews/src/stories/dataviews.fixtures.tsx
+++ b/packages/dataviews/src/stories/dataviews.fixtures.tsx
@@ -1425,7 +1425,6 @@ export const fields: Field< SpaceObject >[] = [
},
{
label: 'Type',
- // type: 'text',
id: 'type',
enableHiding: false,
elements: [
From 893b71a932ddb7429e86d3d05bc3548ab741950a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Maneiro?=
<583546+oandregal@users.noreply.github.com>
Date: Thu, 20 Nov 2025 17:33:12 +0100
Subject: [PATCH 15/15] Use WordPress defaults when field type is not date
---
packages/dataviews/src/dataform-controls/date.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/dataviews/src/dataform-controls/date.tsx b/packages/dataviews/src/dataform-controls/date.tsx
index 2fba6822b7322f..64cf4139d7a1f0 100644
--- a/packages/dataviews/src/dataform-controls/date.tsx
+++ b/packages/dataviews/src/dataform-controls/date.tsx
@@ -272,7 +272,7 @@ function CalendarDateControl< Item >( {
null
);
- let weekStartsOn;
+ let weekStartsOn = getSettings().l10n.startOfWeek;
if ( type === 'date' ) {
// 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").