@wordpress/dataviews 14.1.1-next.v.202604091042.0 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -1
- package/build/components/dataform-controls/array.cjs +2 -2
- package/build/components/dataform-controls/array.cjs.map +2 -2
- package/build/components/dataform-controls/date.cjs +16 -5
- package/build/components/dataform-controls/date.cjs.map +3 -3
- package/build/components/dataform-controls/datetime.cjs +6 -2
- package/build/components/dataform-controls/datetime.cjs.map +3 -3
- package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs +48 -0
- package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs.map +7 -0
- package/build/components/dataform-layouts/card/index.cjs.map +2 -2
- package/build/components/dataform-layouts/panel/summary-button.cjs +0 -1
- package/build/components/dataform-layouts/panel/summary-button.cjs.map +2 -2
- package/build/components/dataviews-context/index.cjs.map +1 -1
- package/build/components/dataviews-filters/search-widget.cjs +2 -7
- package/build/components/dataviews-filters/search-widget.cjs.map +2 -2
- package/build/components/dataviews-layouts/activity/activity-item.cjs +2 -3
- package/build/components/dataviews-layouts/activity/activity-item.cjs.map +2 -2
- package/build/components/dataviews-layouts/grid/composite-grid.cjs +2 -2
- package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
- package/build/components/dataviews-layouts/list/index.cjs +2 -2
- package/build/components/dataviews-layouts/list/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/picker-grid/index.cjs +3 -6
- package/build/components/dataviews-layouts/picker-grid/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/picker-table/index.cjs +15 -12
- package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/table/index.cjs +0 -1
- package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
- package/build/dataviews/index.cjs +10 -8
- package/build/dataviews/index.cjs.map +2 -2
- package/build/dataviews-picker/index.cjs +16 -9
- package/build/dataviews-picker/index.cjs.map +2 -2
- package/build/field-types/date.cjs +4 -1
- package/build/field-types/date.cjs.map +2 -2
- package/build/field-types/datetime.cjs +4 -1
- package/build/field-types/datetime.cjs.map +2 -2
- package/build/field-types/utils/get-is-valid.cjs +29 -24
- package/build/field-types/utils/get-is-valid.cjs.map +2 -2
- package/build/field-types/utils/is-valid-date-boundary.cjs +64 -0
- package/build/field-types/utils/is-valid-date-boundary.cjs.map +7 -0
- package/build/types/dataviews.cjs.map +1 -1
- package/build/types/field-api.cjs.map +1 -1
- package/build-module/components/dataform-controls/array.mjs +2 -2
- package/build-module/components/dataform-controls/array.mjs.map +2 -2
- package/build-module/components/dataform-controls/date.mjs +16 -5
- package/build-module/components/dataform-controls/date.mjs.map +2 -2
- package/build-module/components/dataform-controls/datetime.mjs +6 -2
- package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
- package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs +27 -0
- package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs.map +7 -0
- package/build-module/components/dataform-layouts/card/index.mjs.map +2 -2
- package/build-module/components/dataform-layouts/panel/summary-button.mjs +0 -1
- package/build-module/components/dataform-layouts/panel/summary-button.mjs.map +2 -2
- package/build-module/components/dataviews-context/index.mjs.map +1 -1
- package/build-module/components/dataviews-filters/search-widget.mjs +3 -13
- package/build-module/components/dataviews-filters/search-widget.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/activity/activity-item.mjs +2 -3
- package/build-module/components/dataviews-layouts/activity/activity-item.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +2 -2
- package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/list/index.mjs +2 -3
- package/build-module/components/dataviews-layouts/list/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/picker-grid/index.mjs +3 -6
- package/build-module/components/dataviews-layouts/picker-grid/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/picker-table/index.mjs +15 -12
- package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/table/index.mjs +0 -1
- package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
- package/build-module/dataviews/index.mjs +10 -8
- package/build-module/dataviews/index.mjs.map +2 -2
- package/build-module/dataviews-picker/index.mjs +16 -9
- package/build-module/dataviews-picker/index.mjs.map +2 -2
- package/build-module/field-types/date.mjs +4 -1
- package/build-module/field-types/date.mjs.map +2 -2
- package/build-module/field-types/datetime.mjs +4 -1
- package/build-module/field-types/datetime.mjs.map +2 -2
- package/build-module/field-types/utils/get-is-valid.mjs +29 -24
- package/build-module/field-types/utils/get-is-valid.mjs.map +2 -2
- package/build-module/field-types/utils/is-valid-date-boundary.mjs +38 -0
- package/build-module/field-types/utils/is-valid-date-boundary.mjs.map +7 -0
- package/build-style/style-rtl.css +12 -13
- package/build-style/style.css +12 -13
- package/build-types/components/dataform-controls/array.d.ts.map +1 -1
- package/build-types/components/dataform-controls/date.d.ts.map +1 -1
- package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
- package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts +16 -0
- package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts.map +1 -0
- package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
- package/build-types/components/dataform-layouts/panel/summary-button.d.ts.map +1 -1
- package/build-types/components/dataviews-context/index.d.ts +2 -2
- package/build-types/components/dataviews-context/index.d.ts.map +1 -1
- package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/activity/activity-item.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/list/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/picker-table/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
- package/build-types/dataform/stories/index.story.d.ts.map +1 -1
- package/build-types/dataform/stories/layout-regular.d.ts.map +1 -1
- package/build-types/dataform/stories/validation.d.ts.map +1 -1
- package/build-types/dataviews/index.d.ts +1 -1
- package/build-types/dataviews/index.d.ts.map +1 -1
- package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
- package/build-types/dataviews/stories/index.story.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-activity.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-grid.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-list.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-table.d.ts.map +1 -1
- package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
- package/build-types/dataviews-picker/index.d.ts +3 -2
- package/build-types/dataviews-picker/index.d.ts.map +1 -1
- package/build-types/dataviews-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/field-types/date.d.ts +3 -0
- package/build-types/field-types/date.d.ts.map +1 -1
- package/build-types/field-types/datetime.d.ts +3 -0
- package/build-types/field-types/datetime.d.ts.map +1 -1
- package/build-types/field-types/utils/get-is-valid.d.ts.map +1 -1
- package/build-types/field-types/utils/is-valid-date-boundary.d.ts +7 -0
- package/build-types/field-types/utils/is-valid-date-boundary.d.ts.map +1 -0
- package/build-types/types/dataviews.d.ts +8 -0
- package/build-types/types/dataviews.d.ts.map +1 -1
- package/build-types/types/field-api.d.ts +11 -9
- package/build-types/types/field-api.d.ts.map +1 -1
- package/build-wp/index.js +1173 -1017
- package/package.json +16 -16
- package/src/components/dataform-controls/array.tsx +3 -2
- package/src/components/dataform-controls/date.tsx +17 -2
- package/src/components/dataform-controls/datetime.tsx +15 -1
- package/src/components/dataform-controls/utils/use-disabled-date-matchers.ts +48 -0
- package/src/components/dataform-layouts/card/index.tsx +0 -3
- package/src/components/dataform-layouts/panel/style.scss +4 -5
- package/src/components/dataform-layouts/panel/summary-button.tsx +0 -1
- package/src/components/dataviews-context/index.ts +2 -2
- package/src/components/dataviews-filters/search-widget.tsx +4 -14
- package/src/components/dataviews-filters/style.scss +2 -2
- package/src/components/dataviews-layouts/activity/activity-item.tsx +2 -3
- package/src/components/dataviews-layouts/activity/style.scss +1 -1
- package/src/components/dataviews-layouts/grid/composite-grid.tsx +3 -3
- package/src/components/dataviews-layouts/grid/style.scss +1 -1
- package/src/components/dataviews-layouts/list/index.tsx +2 -3
- package/src/components/dataviews-layouts/list/style.scss +1 -1
- package/src/components/dataviews-layouts/picker-grid/index.tsx +5 -9
- package/src/components/dataviews-layouts/picker-grid/style.scss +1 -1
- package/src/components/dataviews-layouts/picker-table/index.tsx +9 -7
- package/src/components/dataviews-layouts/picker-table/style.scss +1 -1
- package/src/components/dataviews-layouts/table/index.tsx +0 -2
- package/src/dataform/stories/content.story.tsx +1 -1
- package/src/dataform/stories/data-adapter.tsx +6 -6
- package/src/dataform/stories/layout-card.tsx +8 -8
- package/src/dataform/stories/layout-details.tsx +5 -5
- package/src/dataform/stories/layout-panel.tsx +9 -9
- package/src/dataform/stories/layout-regular.tsx +16 -9
- package/src/dataform/stories/layout-row.tsx +9 -9
- package/src/dataform/stories/validation.tsx +25 -10
- package/src/dataviews/index.tsx +11 -7
- package/src/dataviews/stories/empty.tsx +6 -6
- package/src/dataviews/stories/fixtures.tsx +2 -2
- package/src/dataviews/stories/free-composition.tsx +10 -13
- package/src/dataviews/stories/infinite-scroll.tsx +4 -4
- package/src/dataviews/stories/layout-activity.tsx +7 -9
- package/src/dataviews/stories/layout-custom.tsx +1 -1
- package/src/dataviews/stories/layout-grid.tsx +5 -7
- package/src/dataviews/stories/layout-list.tsx +6 -8
- package/src/dataviews/stories/layout-table.tsx +5 -7
- package/src/dataviews/stories/minimal-ui.tsx +1 -1
- package/src/dataviews/stories/with-card.tsx +4 -7
- package/src/dataviews/style.scss +1 -1
- package/src/dataviews/test/dataviews.tsx +73 -6
- package/src/dataviews-picker/index.tsx +17 -7
- package/src/dataviews-picker/stories/index.story.tsx +1 -5
- package/src/dataviews-picker/test/dataviews-picker.tsx +79 -2
- package/src/field-types/date.tsx +3 -0
- package/src/field-types/datetime.tsx +3 -0
- package/src/field-types/stories/index.story.tsx +1 -1
- package/src/field-types/test/normalize-fields.ts +44 -0
- package/src/field-types/utils/get-is-valid.ts +44 -31
- package/src/field-types/utils/is-valid-date-boundary.ts +80 -0
- package/src/hooks/test/use-form-validity.ts +479 -0
- package/src/types/dataviews.ts +9 -0
- package/src/types/field-api.ts +11 -9
|
@@ -4,13 +4,42 @@
|
|
|
4
4
|
import type { Field, NormalizedRules } from '../../types';
|
|
5
5
|
import type { FieldType } from '../../types/private';
|
|
6
6
|
|
|
7
|
+
function supportsNumericRangeConstraint( type?: string ) {
|
|
8
|
+
return type === 'integer' || type === 'number';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function supportsDateRangeConstraint( type?: string ) {
|
|
12
|
+
return type === 'date' || type === 'datetime';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeRangeRule< Item >(
|
|
16
|
+
value: number | string | undefined,
|
|
17
|
+
fieldType: FieldType< Item >,
|
|
18
|
+
key: 'min' | 'max'
|
|
19
|
+
): NormalizedRules< Item >[ 'min' ] {
|
|
20
|
+
const validator = fieldType.validate[ key ];
|
|
21
|
+
if (
|
|
22
|
+
validator &&
|
|
23
|
+
( ( typeof value === 'number' &&
|
|
24
|
+
supportsNumericRangeConstraint( fieldType.type ) ) ||
|
|
25
|
+
( typeof value === 'string' &&
|
|
26
|
+
supportsDateRangeConstraint( fieldType.type ) ) )
|
|
27
|
+
) {
|
|
28
|
+
return { constraint: value, validate: validator } as NonNullable<
|
|
29
|
+
NormalizedRules< Item >[ typeof key ]
|
|
30
|
+
>;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
export default function getIsValid< Item >(
|
|
8
36
|
field: Field< Item >,
|
|
9
37
|
fieldType: FieldType< Item >
|
|
10
38
|
): NormalizedRules< Item > {
|
|
39
|
+
const rules = field.isValid;
|
|
11
40
|
let required;
|
|
12
41
|
if (
|
|
13
|
-
|
|
42
|
+
rules?.required === true &&
|
|
14
43
|
fieldType.validate.required !== undefined
|
|
15
44
|
) {
|
|
16
45
|
required = {
|
|
@@ -21,9 +50,9 @@ export default function getIsValid< Item >(
|
|
|
21
50
|
|
|
22
51
|
let elements;
|
|
23
52
|
if (
|
|
24
|
-
(
|
|
53
|
+
( rules?.elements === true ||
|
|
25
54
|
// elements is enabled unless the field opts-out
|
|
26
|
-
(
|
|
55
|
+
( rules?.elements === undefined &&
|
|
27
56
|
( !! field.elements || !! field.getElements ) ) ) &&
|
|
28
57
|
fieldType.validate.elements !== undefined
|
|
29
58
|
) {
|
|
@@ -33,62 +62,46 @@ export default function getIsValid< Item >(
|
|
|
33
62
|
};
|
|
34
63
|
}
|
|
35
64
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
typeof field.isValid?.min === 'number' &&
|
|
39
|
-
fieldType.validate.min !== undefined
|
|
40
|
-
) {
|
|
41
|
-
min = {
|
|
42
|
-
constraint: field.isValid.min,
|
|
43
|
-
validate: fieldType.validate.min,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let max;
|
|
48
|
-
if (
|
|
49
|
-
typeof field.isValid?.max === 'number' &&
|
|
50
|
-
fieldType.validate.max !== undefined
|
|
51
|
-
) {
|
|
52
|
-
max = {
|
|
53
|
-
constraint: field.isValid.max,
|
|
54
|
-
validate: fieldType.validate.max,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
65
|
+
const min = normalizeRangeRule( rules?.min, fieldType, 'min' );
|
|
66
|
+
const max = normalizeRangeRule( rules?.max, fieldType, 'max' );
|
|
57
67
|
|
|
68
|
+
const minLengthValue = rules?.minLength;
|
|
58
69
|
let minLength;
|
|
59
70
|
if (
|
|
60
|
-
typeof
|
|
71
|
+
typeof minLengthValue === 'number' &&
|
|
61
72
|
fieldType.validate.minLength !== undefined
|
|
62
73
|
) {
|
|
63
74
|
minLength = {
|
|
64
|
-
constraint:
|
|
75
|
+
constraint: minLengthValue,
|
|
65
76
|
validate: fieldType.validate.minLength,
|
|
66
77
|
};
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
const maxLengthValue = rules?.maxLength;
|
|
69
81
|
let maxLength;
|
|
70
82
|
if (
|
|
71
|
-
typeof
|
|
83
|
+
typeof maxLengthValue === 'number' &&
|
|
72
84
|
fieldType.validate.maxLength !== undefined
|
|
73
85
|
) {
|
|
74
86
|
maxLength = {
|
|
75
|
-
constraint:
|
|
87
|
+
constraint: maxLengthValue,
|
|
76
88
|
validate: fieldType.validate.maxLength,
|
|
77
89
|
};
|
|
78
90
|
}
|
|
79
91
|
|
|
92
|
+
const patternValue = rules?.pattern;
|
|
80
93
|
let pattern;
|
|
81
94
|
if (
|
|
82
|
-
|
|
95
|
+
patternValue !== undefined &&
|
|
83
96
|
fieldType.validate.pattern !== undefined
|
|
84
97
|
) {
|
|
85
98
|
pattern = {
|
|
86
|
-
constraint:
|
|
99
|
+
constraint: patternValue,
|
|
87
100
|
validate: fieldType.validate.pattern,
|
|
88
101
|
};
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
const custom =
|
|
104
|
+
const custom = rules?.custom ?? fieldType.validate.custom;
|
|
92
105
|
|
|
93
106
|
return {
|
|
94
107
|
required,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { isValid as isValidDate } from 'date-fns';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { getDate } from '@wordpress/date';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import type { NormalizedField } from '../../types';
|
|
15
|
+
|
|
16
|
+
type Boundary = 'min' | 'max';
|
|
17
|
+
|
|
18
|
+
function parseDateLike( value?: string ) {
|
|
19
|
+
if ( ! value ) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Pre-check to avoid passing unparseable strings to getDate,
|
|
24
|
+
// which uses moment.js and emits deprecation warnings.
|
|
25
|
+
if ( ! isValidDate( new Date( value ) ) ) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const parsed = getDate( value );
|
|
30
|
+
return parsed && isValidDate( parsed ) ? parsed : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validateDateLikeBoundary< Item >(
|
|
34
|
+
item: Item,
|
|
35
|
+
field: NormalizedField< Item >,
|
|
36
|
+
boundary: Boundary
|
|
37
|
+
): boolean {
|
|
38
|
+
const constraint = field.isValid[ boundary ]?.constraint;
|
|
39
|
+
if ( typeof constraint !== 'string' ) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const value = field.getValue( { item } );
|
|
44
|
+
const boundaryValue = Array.isArray( value )
|
|
45
|
+
? value[ boundary === 'min' ? 0 : value.length - 1 ]
|
|
46
|
+
: value;
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
boundaryValue === undefined ||
|
|
50
|
+
boundaryValue === null ||
|
|
51
|
+
boundaryValue === ''
|
|
52
|
+
) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const parsedConstraint = parseDateLike( constraint );
|
|
57
|
+
const parsedValue = parseDateLike( String( boundaryValue ) );
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
!! parsedConstraint &&
|
|
61
|
+
!! parsedValue &&
|
|
62
|
+
( boundary === 'min'
|
|
63
|
+
? parsedValue.getTime() >= parsedConstraint.getTime()
|
|
64
|
+
: parsedValue.getTime() <= parsedConstraint.getTime() )
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function isValidMinDate< Item >(
|
|
69
|
+
item: Item,
|
|
70
|
+
field: NormalizedField< Item >
|
|
71
|
+
): boolean {
|
|
72
|
+
return validateDateLikeBoundary( item, field, 'min' );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isValidMaxDate< Item >(
|
|
76
|
+
item: Item,
|
|
77
|
+
field: NormalizedField< Item >
|
|
78
|
+
): boolean {
|
|
79
|
+
return validateDateLikeBoundary( item, field, 'max' );
|
|
80
|
+
}
|
|
@@ -1039,6 +1039,485 @@ describe( 'useFormValidity', () => {
|
|
|
1039
1039
|
} );
|
|
1040
1040
|
} );
|
|
1041
1041
|
|
|
1042
|
+
describe( 'isValid.min (date)', () => {
|
|
1043
|
+
const MIN_MESSAGE = {
|
|
1044
|
+
min: {
|
|
1045
|
+
type: 'invalid',
|
|
1046
|
+
message: 'Value is below the minimum.',
|
|
1047
|
+
},
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
it( 'date is valid when value is at min', () => {
|
|
1051
|
+
const item = { id: 1, eventDate: '2026-04-01' };
|
|
1052
|
+
const fields: Field< {} >[] = [
|
|
1053
|
+
{
|
|
1054
|
+
id: 'eventDate',
|
|
1055
|
+
type: 'date',
|
|
1056
|
+
isValid: {
|
|
1057
|
+
min: '2026-04-01',
|
|
1058
|
+
},
|
|
1059
|
+
},
|
|
1060
|
+
];
|
|
1061
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1062
|
+
const {
|
|
1063
|
+
result: {
|
|
1064
|
+
current: { validity, isValid },
|
|
1065
|
+
},
|
|
1066
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1067
|
+
expect( validity ).toEqual( undefined );
|
|
1068
|
+
expect( isValid ).toBe( true );
|
|
1069
|
+
} );
|
|
1070
|
+
|
|
1071
|
+
it( 'date is invalid when value is before min', () => {
|
|
1072
|
+
const item = { id: 1, eventDate: '2026-03-15' };
|
|
1073
|
+
const fields: Field< {} >[] = [
|
|
1074
|
+
{
|
|
1075
|
+
id: 'eventDate',
|
|
1076
|
+
type: 'date',
|
|
1077
|
+
isValid: {
|
|
1078
|
+
min: '2026-04-01',
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
];
|
|
1082
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1083
|
+
const {
|
|
1084
|
+
result: {
|
|
1085
|
+
current: { validity, isValid },
|
|
1086
|
+
},
|
|
1087
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1088
|
+
expect( validity?.eventDate ).toEqual( MIN_MESSAGE );
|
|
1089
|
+
expect( isValid ).toBe( false );
|
|
1090
|
+
} );
|
|
1091
|
+
|
|
1092
|
+
it( 'date is valid when value is empty and min is defined', () => {
|
|
1093
|
+
const item = { id: 1, eventDate: undefined };
|
|
1094
|
+
const fields: Field< {} >[] = [
|
|
1095
|
+
{
|
|
1096
|
+
id: 'eventDate',
|
|
1097
|
+
type: 'date',
|
|
1098
|
+
isValid: {
|
|
1099
|
+
min: '2026-04-01',
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
];
|
|
1103
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1104
|
+
const {
|
|
1105
|
+
result: {
|
|
1106
|
+
current: { validity, isValid },
|
|
1107
|
+
},
|
|
1108
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1109
|
+
expect( validity ).toEqual( undefined );
|
|
1110
|
+
expect( isValid ).toBe( true );
|
|
1111
|
+
} );
|
|
1112
|
+
|
|
1113
|
+
it( 'date range: from date is validated against min', () => {
|
|
1114
|
+
const item = {
|
|
1115
|
+
id: 1,
|
|
1116
|
+
dateRange: [ '2026-03-15', '2026-04-10' ],
|
|
1117
|
+
};
|
|
1118
|
+
const fields: Field< {} >[] = [
|
|
1119
|
+
{
|
|
1120
|
+
id: 'dateRange',
|
|
1121
|
+
type: 'date',
|
|
1122
|
+
isValid: {
|
|
1123
|
+
min: '2026-04-01',
|
|
1124
|
+
},
|
|
1125
|
+
},
|
|
1126
|
+
];
|
|
1127
|
+
const form = { fields: [ 'dateRange' ] };
|
|
1128
|
+
const {
|
|
1129
|
+
result: {
|
|
1130
|
+
current: { validity, isValid },
|
|
1131
|
+
},
|
|
1132
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1133
|
+
expect( validity?.dateRange ).toEqual( MIN_MESSAGE );
|
|
1134
|
+
expect( isValid ).toBe( false );
|
|
1135
|
+
} );
|
|
1136
|
+
} );
|
|
1137
|
+
|
|
1138
|
+
describe( 'isValid.max (date)', () => {
|
|
1139
|
+
const MAX_MESSAGE = {
|
|
1140
|
+
max: {
|
|
1141
|
+
type: 'invalid',
|
|
1142
|
+
message: 'Value is above the maximum.',
|
|
1143
|
+
},
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
it( 'date is valid when value is at max', () => {
|
|
1147
|
+
const item = { id: 1, eventDate: '2026-04-20' };
|
|
1148
|
+
const fields: Field< {} >[] = [
|
|
1149
|
+
{
|
|
1150
|
+
id: 'eventDate',
|
|
1151
|
+
type: 'date',
|
|
1152
|
+
isValid: {
|
|
1153
|
+
max: '2026-04-20',
|
|
1154
|
+
},
|
|
1155
|
+
},
|
|
1156
|
+
];
|
|
1157
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1158
|
+
const {
|
|
1159
|
+
result: {
|
|
1160
|
+
current: { validity, isValid },
|
|
1161
|
+
},
|
|
1162
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1163
|
+
expect( validity ).toEqual( undefined );
|
|
1164
|
+
expect( isValid ).toBe( true );
|
|
1165
|
+
} );
|
|
1166
|
+
|
|
1167
|
+
it( 'date is invalid when value is after max', () => {
|
|
1168
|
+
const item = { id: 1, eventDate: '2026-05-01' };
|
|
1169
|
+
const fields: Field< {} >[] = [
|
|
1170
|
+
{
|
|
1171
|
+
id: 'eventDate',
|
|
1172
|
+
type: 'date',
|
|
1173
|
+
isValid: {
|
|
1174
|
+
max: '2026-04-20',
|
|
1175
|
+
},
|
|
1176
|
+
},
|
|
1177
|
+
];
|
|
1178
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1179
|
+
const {
|
|
1180
|
+
result: {
|
|
1181
|
+
current: { validity, isValid },
|
|
1182
|
+
},
|
|
1183
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1184
|
+
expect( validity?.eventDate ).toEqual( MAX_MESSAGE );
|
|
1185
|
+
expect( isValid ).toBe( false );
|
|
1186
|
+
} );
|
|
1187
|
+
|
|
1188
|
+
it( 'date range: to date is validated against max', () => {
|
|
1189
|
+
const item = {
|
|
1190
|
+
id: 1,
|
|
1191
|
+
dateRange: [ '2026-04-05', '2026-04-25' ],
|
|
1192
|
+
};
|
|
1193
|
+
const fields: Field< {} >[] = [
|
|
1194
|
+
{
|
|
1195
|
+
id: 'dateRange',
|
|
1196
|
+
type: 'date',
|
|
1197
|
+
isValid: {
|
|
1198
|
+
max: '2026-04-20',
|
|
1199
|
+
},
|
|
1200
|
+
},
|
|
1201
|
+
];
|
|
1202
|
+
const form = { fields: [ 'dateRange' ] };
|
|
1203
|
+
const {
|
|
1204
|
+
result: {
|
|
1205
|
+
current: { validity, isValid },
|
|
1206
|
+
},
|
|
1207
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1208
|
+
expect( validity?.dateRange ).toEqual( MAX_MESSAGE );
|
|
1209
|
+
expect( isValid ).toBe( false );
|
|
1210
|
+
} );
|
|
1211
|
+
} );
|
|
1212
|
+
|
|
1213
|
+
describe( 'isValid.min (datetime)', () => {
|
|
1214
|
+
const MIN_MESSAGE = {
|
|
1215
|
+
min: {
|
|
1216
|
+
type: 'invalid',
|
|
1217
|
+
message: 'Value is below the minimum.',
|
|
1218
|
+
},
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
it( 'datetime is valid when value is at min', () => {
|
|
1222
|
+
const item = {
|
|
1223
|
+
id: 1,
|
|
1224
|
+
createdAt: '2026-04-01T10:00:00.000Z',
|
|
1225
|
+
};
|
|
1226
|
+
const fields: Field< {} >[] = [
|
|
1227
|
+
{
|
|
1228
|
+
id: 'createdAt',
|
|
1229
|
+
type: 'datetime',
|
|
1230
|
+
isValid: {
|
|
1231
|
+
min: '2026-04-01T10:00:00.000Z',
|
|
1232
|
+
},
|
|
1233
|
+
},
|
|
1234
|
+
];
|
|
1235
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1236
|
+
const {
|
|
1237
|
+
result: {
|
|
1238
|
+
current: { validity, isValid },
|
|
1239
|
+
},
|
|
1240
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1241
|
+
expect( validity ).toEqual( undefined );
|
|
1242
|
+
expect( isValid ).toBe( true );
|
|
1243
|
+
} );
|
|
1244
|
+
|
|
1245
|
+
it( 'datetime is invalid when value is before min', () => {
|
|
1246
|
+
const item = {
|
|
1247
|
+
id: 1,
|
|
1248
|
+
createdAt: '2026-03-31T23:59:59.000Z',
|
|
1249
|
+
};
|
|
1250
|
+
const fields: Field< {} >[] = [
|
|
1251
|
+
{
|
|
1252
|
+
id: 'createdAt',
|
|
1253
|
+
type: 'datetime',
|
|
1254
|
+
isValid: {
|
|
1255
|
+
min: '2026-04-01T10:00:00.000Z',
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
];
|
|
1259
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1260
|
+
const {
|
|
1261
|
+
result: {
|
|
1262
|
+
current: { validity, isValid },
|
|
1263
|
+
},
|
|
1264
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1265
|
+
expect( validity?.createdAt ).toEqual( MIN_MESSAGE );
|
|
1266
|
+
expect( isValid ).toBe( false );
|
|
1267
|
+
} );
|
|
1268
|
+
|
|
1269
|
+
it( 'datetime is valid when min uses an offset-based ISO string', () => {
|
|
1270
|
+
const item = {
|
|
1271
|
+
id: 1,
|
|
1272
|
+
createdAt: '2026-04-01T09:30:00.000Z',
|
|
1273
|
+
};
|
|
1274
|
+
const fields: Field< {} >[] = [
|
|
1275
|
+
{
|
|
1276
|
+
id: 'createdAt',
|
|
1277
|
+
type: 'datetime',
|
|
1278
|
+
isValid: {
|
|
1279
|
+
min: '2026-04-01T10:00:00+02:00',
|
|
1280
|
+
},
|
|
1281
|
+
},
|
|
1282
|
+
];
|
|
1283
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1284
|
+
const {
|
|
1285
|
+
result: {
|
|
1286
|
+
current: { validity, isValid },
|
|
1287
|
+
},
|
|
1288
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1289
|
+
expect( validity ).toEqual( undefined );
|
|
1290
|
+
expect( isValid ).toBe( true );
|
|
1291
|
+
} );
|
|
1292
|
+
} );
|
|
1293
|
+
|
|
1294
|
+
describe( 'isValid.max (datetime)', () => {
|
|
1295
|
+
const MAX_MESSAGE = {
|
|
1296
|
+
max: {
|
|
1297
|
+
type: 'invalid',
|
|
1298
|
+
message: 'Value is above the maximum.',
|
|
1299
|
+
},
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
it( 'datetime is valid when value is at max', () => {
|
|
1303
|
+
const item = {
|
|
1304
|
+
id: 1,
|
|
1305
|
+
createdAt: '2026-04-30T23:59:59.000Z',
|
|
1306
|
+
};
|
|
1307
|
+
const fields: Field< {} >[] = [
|
|
1308
|
+
{
|
|
1309
|
+
id: 'createdAt',
|
|
1310
|
+
type: 'datetime',
|
|
1311
|
+
isValid: {
|
|
1312
|
+
max: '2026-04-30T23:59:59.000Z',
|
|
1313
|
+
},
|
|
1314
|
+
},
|
|
1315
|
+
];
|
|
1316
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1317
|
+
const {
|
|
1318
|
+
result: {
|
|
1319
|
+
current: { validity, isValid },
|
|
1320
|
+
},
|
|
1321
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1322
|
+
expect( validity ).toEqual( undefined );
|
|
1323
|
+
expect( isValid ).toBe( true );
|
|
1324
|
+
} );
|
|
1325
|
+
|
|
1326
|
+
it( 'datetime is invalid when value is after max', () => {
|
|
1327
|
+
const item = {
|
|
1328
|
+
id: 1,
|
|
1329
|
+
createdAt: '2026-05-01T00:00:00.000Z',
|
|
1330
|
+
};
|
|
1331
|
+
const fields: Field< {} >[] = [
|
|
1332
|
+
{
|
|
1333
|
+
id: 'createdAt',
|
|
1334
|
+
type: 'datetime',
|
|
1335
|
+
isValid: {
|
|
1336
|
+
max: '2026-04-30T23:59:59.000Z',
|
|
1337
|
+
},
|
|
1338
|
+
},
|
|
1339
|
+
];
|
|
1340
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1341
|
+
const {
|
|
1342
|
+
result: {
|
|
1343
|
+
current: { validity, isValid },
|
|
1344
|
+
},
|
|
1345
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1346
|
+
expect( validity?.createdAt ).toEqual( MAX_MESSAGE );
|
|
1347
|
+
expect( isValid ).toBe( false );
|
|
1348
|
+
} );
|
|
1349
|
+
|
|
1350
|
+
it( 'datetime is invalid when max uses an offset-based ISO string', () => {
|
|
1351
|
+
const item = {
|
|
1352
|
+
id: 1,
|
|
1353
|
+
createdAt: '2026-04-01T09:30:00.000Z',
|
|
1354
|
+
};
|
|
1355
|
+
const fields: Field< {} >[] = [
|
|
1356
|
+
{
|
|
1357
|
+
id: 'createdAt',
|
|
1358
|
+
type: 'datetime',
|
|
1359
|
+
isValid: {
|
|
1360
|
+
max: '2026-04-01T10:00:00+02:00',
|
|
1361
|
+
},
|
|
1362
|
+
},
|
|
1363
|
+
];
|
|
1364
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1365
|
+
const {
|
|
1366
|
+
result: {
|
|
1367
|
+
current: { validity, isValid },
|
|
1368
|
+
},
|
|
1369
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1370
|
+
expect( validity?.createdAt ).toEqual( MAX_MESSAGE );
|
|
1371
|
+
expect( isValid ).toBe( false );
|
|
1372
|
+
} );
|
|
1373
|
+
|
|
1374
|
+
it( 'datetime is invalid when value cannot be parsed', () => {
|
|
1375
|
+
const item = {
|
|
1376
|
+
id: 1,
|
|
1377
|
+
createdAt: 'not-a-date',
|
|
1378
|
+
};
|
|
1379
|
+
const fields: Field< {} >[] = [
|
|
1380
|
+
{
|
|
1381
|
+
id: 'createdAt',
|
|
1382
|
+
type: 'datetime',
|
|
1383
|
+
isValid: {
|
|
1384
|
+
max: '2026-04-30T23:59:59.000Z',
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
];
|
|
1388
|
+
const form = { fields: [ 'createdAt' ] };
|
|
1389
|
+
const {
|
|
1390
|
+
result: {
|
|
1391
|
+
current: { validity, isValid },
|
|
1392
|
+
},
|
|
1393
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1394
|
+
expect( validity?.createdAt ).toEqual( MAX_MESSAGE );
|
|
1395
|
+
expect( isValid ).toBe( false );
|
|
1396
|
+
} );
|
|
1397
|
+
} );
|
|
1398
|
+
|
|
1399
|
+
describe( 'isValid combined min and max (date)', () => {
|
|
1400
|
+
it( 'date is valid when value is within range', () => {
|
|
1401
|
+
const item = { id: 1, eventDate: '2026-04-10' };
|
|
1402
|
+
const fields: Field< {} >[] = [
|
|
1403
|
+
{
|
|
1404
|
+
id: 'eventDate',
|
|
1405
|
+
type: 'date',
|
|
1406
|
+
isValid: {
|
|
1407
|
+
min: '2026-04-01',
|
|
1408
|
+
max: '2026-04-30',
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
];
|
|
1412
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1413
|
+
const {
|
|
1414
|
+
result: {
|
|
1415
|
+
current: { validity, isValid },
|
|
1416
|
+
},
|
|
1417
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1418
|
+
expect( validity ).toEqual( undefined );
|
|
1419
|
+
expect( isValid ).toBe( true );
|
|
1420
|
+
} );
|
|
1421
|
+
|
|
1422
|
+
it( 'date is invalid when value is below min with both min and max', () => {
|
|
1423
|
+
const item = { id: 1, eventDate: '2026-03-15' };
|
|
1424
|
+
const fields: Field< {} >[] = [
|
|
1425
|
+
{
|
|
1426
|
+
id: 'eventDate',
|
|
1427
|
+
type: 'date',
|
|
1428
|
+
isValid: {
|
|
1429
|
+
min: '2026-04-01',
|
|
1430
|
+
max: '2026-04-30',
|
|
1431
|
+
},
|
|
1432
|
+
},
|
|
1433
|
+
];
|
|
1434
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1435
|
+
const {
|
|
1436
|
+
result: {
|
|
1437
|
+
current: { validity, isValid },
|
|
1438
|
+
},
|
|
1439
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1440
|
+
expect( validity?.eventDate ).toEqual( {
|
|
1441
|
+
min: {
|
|
1442
|
+
type: 'invalid',
|
|
1443
|
+
message: 'Value is below the minimum.',
|
|
1444
|
+
},
|
|
1445
|
+
} );
|
|
1446
|
+
expect( isValid ).toBe( false );
|
|
1447
|
+
} );
|
|
1448
|
+
|
|
1449
|
+
it( 'date is invalid when value is above max with both min and max', () => {
|
|
1450
|
+
const item = { id: 1, eventDate: '2026-05-15' };
|
|
1451
|
+
const fields: Field< {} >[] = [
|
|
1452
|
+
{
|
|
1453
|
+
id: 'eventDate',
|
|
1454
|
+
type: 'date',
|
|
1455
|
+
isValid: {
|
|
1456
|
+
min: '2026-04-01',
|
|
1457
|
+
max: '2026-04-30',
|
|
1458
|
+
},
|
|
1459
|
+
},
|
|
1460
|
+
];
|
|
1461
|
+
const form = { fields: [ 'eventDate' ] };
|
|
1462
|
+
const {
|
|
1463
|
+
result: {
|
|
1464
|
+
current: { validity, isValid },
|
|
1465
|
+
},
|
|
1466
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1467
|
+
expect( validity?.eventDate ).toEqual( {
|
|
1468
|
+
max: {
|
|
1469
|
+
type: 'invalid',
|
|
1470
|
+
message: 'Value is above the maximum.',
|
|
1471
|
+
},
|
|
1472
|
+
} );
|
|
1473
|
+
expect( isValid ).toBe( false );
|
|
1474
|
+
} );
|
|
1475
|
+
} );
|
|
1476
|
+
|
|
1477
|
+
describe( 'isValid empty array (date)', () => {
|
|
1478
|
+
it( 'empty array is valid for min validation', () => {
|
|
1479
|
+
const item = { id: 1, dateRange: [] as string[] };
|
|
1480
|
+
const fields: Field< {} >[] = [
|
|
1481
|
+
{
|
|
1482
|
+
id: 'dateRange',
|
|
1483
|
+
type: 'date',
|
|
1484
|
+
isValid: {
|
|
1485
|
+
min: '2026-04-01',
|
|
1486
|
+
},
|
|
1487
|
+
},
|
|
1488
|
+
];
|
|
1489
|
+
const form = { fields: [ 'dateRange' ] };
|
|
1490
|
+
const {
|
|
1491
|
+
result: {
|
|
1492
|
+
current: { validity, isValid },
|
|
1493
|
+
},
|
|
1494
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1495
|
+
expect( validity ).toEqual( undefined );
|
|
1496
|
+
expect( isValid ).toBe( true );
|
|
1497
|
+
} );
|
|
1498
|
+
|
|
1499
|
+
it( 'empty array is valid for max validation', () => {
|
|
1500
|
+
const item = { id: 1, dateRange: [] as string[] };
|
|
1501
|
+
const fields: Field< {} >[] = [
|
|
1502
|
+
{
|
|
1503
|
+
id: 'dateRange',
|
|
1504
|
+
type: 'date',
|
|
1505
|
+
isValid: {
|
|
1506
|
+
max: '2026-04-30',
|
|
1507
|
+
},
|
|
1508
|
+
},
|
|
1509
|
+
];
|
|
1510
|
+
const form = { fields: [ 'dateRange' ] };
|
|
1511
|
+
const {
|
|
1512
|
+
result: {
|
|
1513
|
+
current: { validity, isValid },
|
|
1514
|
+
},
|
|
1515
|
+
} = renderHook( () => useFormValidity( item, fields, form ) );
|
|
1516
|
+
expect( validity ).toEqual( undefined );
|
|
1517
|
+
expect( isValid ).toBe( true );
|
|
1518
|
+
} );
|
|
1519
|
+
} );
|
|
1520
|
+
|
|
1042
1521
|
describe( 'isValid.minLength', () => {
|
|
1043
1522
|
const MIN_LENGTH_MESSAGE = {
|
|
1044
1523
|
minLength: {
|
package/src/types/dataviews.ts
CHANGED
|
@@ -527,6 +527,15 @@ export type ViewPickerProps< Item > =
|
|
|
527
527
|
| ViewPickerTableProps< Item >;
|
|
528
528
|
|
|
529
529
|
export interface SupportedLayouts {
|
|
530
|
+
list?: Omit< ViewList, 'type' > | true;
|
|
531
|
+
grid?: Omit< ViewGrid, 'type' > | true;
|
|
532
|
+
table?: Omit< ViewTable, 'type' > | true;
|
|
533
|
+
activity?: Omit< ViewActivity, 'type' > | true;
|
|
534
|
+
pickerGrid?: Omit< ViewPickerGrid, 'type' > | true;
|
|
535
|
+
pickerTable?: Omit< ViewPickerTable, 'type' > | true;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export interface NormalizedSupportedLayouts {
|
|
530
539
|
list?: Omit< ViewList, 'type' >;
|
|
531
540
|
grid?: Omit< ViewGrid, 'type' >;
|
|
532
541
|
table?: Omit< ViewTable, 'type' >;
|