@wordpress/components 30.2.1-next.0f6f9d12c.0 → 30.2.2-next.e256d081a.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/context/context-connect.js.map +1 -1
- package/build/menu-item/index.js +1 -0
- package/build/menu-item/index.js.map +1 -1
- package/build/tabs/styles.js +5 -5
- package/build/tabs/styles.js.map +1 -1
- package/build/utils/font-size.js.map +1 -1
- package/build/utils/get-valid-children.js.map +1 -1
- package/build/validated-form-controls/components/checkbox-control.js +5 -3
- package/build/validated-form-controls/components/checkbox-control.js.map +1 -1
- package/build/validated-form-controls/components/combobox-control.js +5 -3
- package/build/validated-form-controls/components/combobox-control.js.map +1 -1
- package/build/validated-form-controls/components/custom-select-control.js +5 -3
- package/build/validated-form-controls/components/custom-select-control.js.map +1 -1
- package/build/validated-form-controls/components/input-control.js +5 -3
- package/build/validated-form-controls/components/input-control.js.map +1 -1
- package/build/validated-form-controls/components/number-control.js +5 -3
- package/build/validated-form-controls/components/number-control.js.map +1 -1
- package/build/validated-form-controls/components/radio-control.js +5 -3
- package/build/validated-form-controls/components/radio-control.js.map +1 -1
- package/build/validated-form-controls/components/range-control.js +5 -3
- package/build/validated-form-controls/components/range-control.js.map +1 -1
- package/build/validated-form-controls/components/select-control.js +5 -3
- package/build/validated-form-controls/components/select-control.js.map +1 -1
- package/build/validated-form-controls/components/text-control.js +5 -3
- package/build/validated-form-controls/components/text-control.js.map +1 -1
- package/build/validated-form-controls/components/textarea-control.js +5 -3
- package/build/validated-form-controls/components/textarea-control.js.map +1 -1
- package/build/validated-form-controls/components/toggle-control.js +5 -3
- package/build/validated-form-controls/components/toggle-control.js.map +1 -1
- package/build/validated-form-controls/components/toggle-group-control.js +5 -3
- package/build/validated-form-controls/components/toggle-group-control.js.map +1 -1
- package/build/validated-form-controls/components/types.js.map +1 -1
- package/build/validated-form-controls/control-with-error.js +70 -35
- package/build/validated-form-controls/control-with-error.js.map +1 -1
- package/build/validated-form-controls/validity-indicator.js +45 -0
- package/build/validated-form-controls/validity-indicator.js.map +1 -0
- package/build-module/context/context-connect.js.map +1 -1
- package/build-module/menu-item/index.js +1 -0
- package/build-module/menu-item/index.js.map +1 -1
- package/build-module/tabs/styles.js +6 -6
- package/build-module/tabs/styles.js.map +1 -1
- package/build-module/utils/font-size.js.map +1 -1
- package/build-module/utils/get-valid-children.js.map +1 -1
- package/build-module/validated-form-controls/components/checkbox-control.js +5 -3
- package/build-module/validated-form-controls/components/checkbox-control.js.map +1 -1
- package/build-module/validated-form-controls/components/combobox-control.js +5 -3
- package/build-module/validated-form-controls/components/combobox-control.js.map +1 -1
- package/build-module/validated-form-controls/components/custom-select-control.js +5 -3
- package/build-module/validated-form-controls/components/custom-select-control.js.map +1 -1
- package/build-module/validated-form-controls/components/input-control.js +5 -3
- package/build-module/validated-form-controls/components/input-control.js.map +1 -1
- package/build-module/validated-form-controls/components/number-control.js +5 -3
- package/build-module/validated-form-controls/components/number-control.js.map +1 -1
- package/build-module/validated-form-controls/components/radio-control.js +5 -3
- package/build-module/validated-form-controls/components/radio-control.js.map +1 -1
- package/build-module/validated-form-controls/components/range-control.js +5 -3
- package/build-module/validated-form-controls/components/range-control.js.map +1 -1
- package/build-module/validated-form-controls/components/select-control.js +5 -3
- package/build-module/validated-form-controls/components/select-control.js.map +1 -1
- package/build-module/validated-form-controls/components/text-control.js +5 -3
- package/build-module/validated-form-controls/components/text-control.js.map +1 -1
- package/build-module/validated-form-controls/components/textarea-control.js +5 -3
- package/build-module/validated-form-controls/components/textarea-control.js.map +1 -1
- package/build-module/validated-form-controls/components/toggle-control.js +5 -3
- package/build-module/validated-form-controls/components/toggle-control.js.map +1 -1
- package/build-module/validated-form-controls/components/toggle-group-control.js +5 -3
- package/build-module/validated-form-controls/components/toggle-group-control.js.map +1 -1
- package/build-module/validated-form-controls/components/types.js.map +1 -1
- package/build-module/validated-form-controls/control-with-error.js +70 -34
- package/build-module/validated-form-controls/control-with-error.js.map +1 -1
- package/build-module/validated-form-controls/validity-indicator.js +37 -0
- package/build-module/validated-form-controls/validity-indicator.js.map +1 -0
- package/build-style/style-rtl.css +34 -22
- package/build-style/style.css +34 -22
- package/build-types/context/context-connect.d.ts +2 -2
- package/build-types/context/context-connect.d.ts.map +1 -1
- package/build-types/menu-item/index.d.ts.map +1 -1
- package/build-types/tabs/styles.d.ts.map +1 -1
- package/build-types/utils/font-size.d.ts +2 -2
- package/build-types/utils/font-size.d.ts.map +1 -1
- package/build-types/utils/get-valid-children.d.ts +2 -2
- package/build-types/utils/get-valid-children.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/checkbox-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/combobox-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/custom-select-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/input-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/number-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/radio-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/range-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/select-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/checkbox-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/combobox-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/custom-select-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/input-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/number-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/overview.story.d.ts +13 -0
- package/build-types/validated-form-controls/components/stories/overview.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/radio-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/range-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/select-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/text-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/textarea-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/toggle-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/stories/toggle-group-control.story.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/text-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/textarea-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/toggle-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/toggle-group-control.d.ts.map +1 -1
- package/build-types/validated-form-controls/components/types.d.ts +21 -10
- package/build-types/validated-form-controls/components/types.d.ts.map +1 -1
- package/build-types/validated-form-controls/control-with-error.d.ts +4 -5
- package/build-types/validated-form-controls/control-with-error.d.ts.map +1 -1
- package/build-types/validated-form-controls/test/control-with-error.d.ts +2 -0
- package/build-types/validated-form-controls/test/control-with-error.d.ts.map +1 -0
- package/build-types/validated-form-controls/validity-indicator.d.ts +5 -0
- package/build-types/validated-form-controls/validity-indicator.d.ts.map +1 -0
- package/package.json +19 -19
- package/src/calendar/style.scss +22 -22
- package/src/context/context-connect.ts +2 -2
- package/src/menu-item/index.tsx +1 -0
- package/src/tabs/styles.ts +2 -1
- package/src/tools-panel/stories/index.story.tsx +3 -3
- package/src/utils/font-size.ts +2 -2
- package/src/utils/get-valid-children.ts +4 -2
- package/src/utils/theme-variables.scss +1 -0
- package/src/validated-form-controls/components/checkbox-control.tsx +5 -3
- package/src/validated-form-controls/components/combobox-control.tsx +5 -3
- package/src/validated-form-controls/components/custom-select-control.tsx +5 -3
- package/src/validated-form-controls/components/input-control.tsx +5 -3
- package/src/validated-form-controls/components/number-control.tsx +5 -3
- package/src/validated-form-controls/components/radio-control.tsx +5 -3
- package/src/validated-form-controls/components/range-control.tsx +5 -3
- package/src/validated-form-controls/components/select-control.tsx +5 -3
- package/src/validated-form-controls/components/stories/checkbox-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/combobox-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/custom-select-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/input-control.story.tsx +51 -18
- package/src/validated-form-controls/components/stories/number-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/overview.mdx +1 -1
- package/src/validated-form-controls/components/stories/overview.story.tsx +207 -17
- package/src/validated-form-controls/components/stories/radio-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/range-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/select-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/text-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/textarea-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/toggle-control.story.tsx +17 -6
- package/src/validated-form-controls/components/stories/toggle-group-control.story.tsx +17 -6
- package/src/validated-form-controls/components/text-control.tsx +5 -3
- package/src/validated-form-controls/components/textarea-control.tsx +5 -3
- package/src/validated-form-controls/components/toggle-control.tsx +5 -3
- package/src/validated-form-controls/components/toggle-group-control.tsx +5 -3
- package/src/validated-form-controls/components/types.ts +21 -12
- package/src/validated-form-controls/control-with-error.tsx +93 -41
- package/src/validated-form-controls/style.scss +19 -5
- package/src/validated-form-controls/test/control-with-error.tsx +224 -0
- package/src/validated-form-controls/validity-indicator.tsx +48 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -35,6 +35,12 @@ export const Default: StoryObj< typeof ValidatedComboboxControl > = {
|
|
|
35
35
|
typeof ValidatedComboboxControl
|
|
36
36
|
>[ 'value' ]
|
|
37
37
|
>();
|
|
38
|
+
const [ customValidity, setCustomValidity ] =
|
|
39
|
+
useState<
|
|
40
|
+
React.ComponentProps<
|
|
41
|
+
typeof ValidatedComboboxControl
|
|
42
|
+
>[ 'customValidity' ]
|
|
43
|
+
>( undefined );
|
|
38
44
|
|
|
39
45
|
return (
|
|
40
46
|
<ValidatedComboboxControl
|
|
@@ -44,6 +50,17 @@ export const Default: StoryObj< typeof ValidatedComboboxControl > = {
|
|
|
44
50
|
setValue( newValue );
|
|
45
51
|
onChange?.( newValue );
|
|
46
52
|
} }
|
|
53
|
+
onValidate={ ( v ) => {
|
|
54
|
+
if ( v === 'a' ) {
|
|
55
|
+
setCustomValidity( {
|
|
56
|
+
type: 'invalid',
|
|
57
|
+
message: 'Option A is not allowed.',
|
|
58
|
+
} );
|
|
59
|
+
} else {
|
|
60
|
+
setCustomValidity( undefined );
|
|
61
|
+
}
|
|
62
|
+
} }
|
|
63
|
+
customValidity={ customValidity }
|
|
47
64
|
/>
|
|
48
65
|
);
|
|
49
66
|
},
|
|
@@ -56,10 +73,4 @@ Default.args = {
|
|
|
56
73
|
{ value: 'a', label: 'Option A (not allowed)' },
|
|
57
74
|
{ value: 'b', label: 'Option B' },
|
|
58
75
|
],
|
|
59
|
-
customValidator: ( value ) => {
|
|
60
|
-
if ( value === 'a' ) {
|
|
61
|
-
return 'Option A is not allowed.';
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
},
|
|
65
76
|
};
|
|
@@ -35,6 +35,12 @@ export const Default: StoryObj< typeof ValidatedCustomSelectControl > = {
|
|
|
35
35
|
typeof ValidatedCustomSelectControl
|
|
36
36
|
>[ 'value' ]
|
|
37
37
|
>();
|
|
38
|
+
const [ customValidity, setCustomValidity ] =
|
|
39
|
+
useState<
|
|
40
|
+
React.ComponentProps<
|
|
41
|
+
typeof ValidatedCustomSelectControl
|
|
42
|
+
>[ 'customValidity' ]
|
|
43
|
+
>( undefined );
|
|
38
44
|
|
|
39
45
|
return (
|
|
40
46
|
<ValidatedCustomSelectControl
|
|
@@ -44,6 +50,17 @@ export const Default: StoryObj< typeof ValidatedCustomSelectControl > = {
|
|
|
44
50
|
setValue( newValue.selectedItem );
|
|
45
51
|
onChange?.( newValue );
|
|
46
52
|
} }
|
|
53
|
+
onValidate={ ( v ) => {
|
|
54
|
+
if ( v?.key === 'a' ) {
|
|
55
|
+
setCustomValidity( {
|
|
56
|
+
type: 'invalid',
|
|
57
|
+
message: 'Option A is not allowed.',
|
|
58
|
+
} );
|
|
59
|
+
} else {
|
|
60
|
+
setCustomValidity( undefined );
|
|
61
|
+
}
|
|
62
|
+
} }
|
|
63
|
+
customValidity={ customValidity }
|
|
47
64
|
/>
|
|
48
65
|
);
|
|
49
66
|
},
|
|
@@ -56,10 +73,4 @@ Default.args = {
|
|
|
56
73
|
{ key: 'a', name: 'Option A (not allowed)' },
|
|
57
74
|
{ key: 'b', name: 'Option B' },
|
|
58
75
|
],
|
|
59
|
-
customValidator: ( value ) => {
|
|
60
|
-
if ( value?.key === 'a' ) {
|
|
61
|
-
return 'Option A is not allowed.';
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
},
|
|
65
76
|
};
|
|
@@ -46,6 +46,12 @@ export const Default: StoryObj< typeof ValidatedInputControl > = {
|
|
|
46
46
|
useState<
|
|
47
47
|
React.ComponentProps< typeof ValidatedInputControl >[ 'value' ]
|
|
48
48
|
>( '' );
|
|
49
|
+
const [ customValidity, setCustomValidity ] =
|
|
50
|
+
useState<
|
|
51
|
+
React.ComponentProps<
|
|
52
|
+
typeof ValidatedInputControl
|
|
53
|
+
>[ 'customValidity' ]
|
|
54
|
+
>( undefined );
|
|
49
55
|
|
|
50
56
|
return (
|
|
51
57
|
<ValidatedInputControl
|
|
@@ -55,6 +61,17 @@ export const Default: StoryObj< typeof ValidatedInputControl > = {
|
|
|
55
61
|
setValue( newValue );
|
|
56
62
|
onChange?.( newValue, ...rest );
|
|
57
63
|
} }
|
|
64
|
+
onValidate={ ( v ) => {
|
|
65
|
+
if ( v?.toLowerCase() === 'error' ) {
|
|
66
|
+
setCustomValidity( {
|
|
67
|
+
type: 'invalid',
|
|
68
|
+
message: 'The word "error" is not allowed.',
|
|
69
|
+
} );
|
|
70
|
+
} else {
|
|
71
|
+
setCustomValidity( undefined );
|
|
72
|
+
}
|
|
73
|
+
} }
|
|
74
|
+
customValidity={ customValidity }
|
|
58
75
|
/>
|
|
59
76
|
);
|
|
60
77
|
},
|
|
@@ -63,12 +80,6 @@ Default.args = {
|
|
|
63
80
|
required: true,
|
|
64
81
|
label: 'Input',
|
|
65
82
|
help: 'The word "error" will trigger an error.',
|
|
66
|
-
customValidator: ( value ) => {
|
|
67
|
-
if ( value?.toLowerCase() === 'error' ) {
|
|
68
|
-
return 'The word "error" is not allowed.';
|
|
69
|
-
}
|
|
70
|
-
return undefined;
|
|
71
|
-
},
|
|
72
83
|
};
|
|
73
84
|
|
|
74
85
|
/**
|
|
@@ -83,6 +94,12 @@ export const Password: StoryObj< typeof ValidatedInputControl > = {
|
|
|
83
94
|
React.ComponentProps< typeof ValidatedInputControl >[ 'value' ]
|
|
84
95
|
>( '' );
|
|
85
96
|
const [ visible, setVisible ] = useState( false );
|
|
97
|
+
const [ customValidity, setCustomValidity ] =
|
|
98
|
+
useState<
|
|
99
|
+
React.ComponentProps<
|
|
100
|
+
typeof ValidatedInputControl
|
|
101
|
+
>[ 'customValidity' ]
|
|
102
|
+
>( undefined );
|
|
86
103
|
|
|
87
104
|
return (
|
|
88
105
|
<ValidatedInputControl
|
|
@@ -105,6 +122,34 @@ export const Password: StoryObj< typeof ValidatedInputControl > = {
|
|
|
105
122
|
setValue( newValue );
|
|
106
123
|
onChange?.( newValue, ...rest );
|
|
107
124
|
} }
|
|
125
|
+
onValidate={ ( v ) => {
|
|
126
|
+
if ( ! /\d/.test( v ?? '' ) ) {
|
|
127
|
+
setCustomValidity( {
|
|
128
|
+
type: 'invalid',
|
|
129
|
+
message:
|
|
130
|
+
'Password must include at least one number.',
|
|
131
|
+
} );
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if ( ! /[A-Z]/.test( v ?? '' ) ) {
|
|
135
|
+
setCustomValidity( {
|
|
136
|
+
type: 'invalid',
|
|
137
|
+
message:
|
|
138
|
+
'Password must include at least one capital letter.',
|
|
139
|
+
} );
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if ( ! /[!@£$%^&*#]/.test( v ?? '' ) ) {
|
|
143
|
+
setCustomValidity( {
|
|
144
|
+
type: 'invalid',
|
|
145
|
+
message:
|
|
146
|
+
'Password must include at least one symbol.',
|
|
147
|
+
} );
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
setCustomValidity( undefined );
|
|
151
|
+
} }
|
|
152
|
+
customValidity={ customValidity }
|
|
108
153
|
/>
|
|
109
154
|
);
|
|
110
155
|
},
|
|
@@ -114,18 +159,6 @@ Password.args = {
|
|
|
114
159
|
label: 'Password',
|
|
115
160
|
help: 'Minimum 8 characters, include a number, capital letter, and symbol (!@£$%^&*#).',
|
|
116
161
|
minLength: 8,
|
|
117
|
-
customValidator: ( value ) => {
|
|
118
|
-
if ( ! /\d/.test( value ?? '' ) ) {
|
|
119
|
-
return 'Password must include at least one number.';
|
|
120
|
-
}
|
|
121
|
-
if ( ! /[A-Z]/.test( value ?? '' ) ) {
|
|
122
|
-
return 'Password must include at least one capital letter.';
|
|
123
|
-
}
|
|
124
|
-
if ( ! /[!@£$%^&*#]/.test( value ?? '' ) ) {
|
|
125
|
-
return 'Password must include at least one symbol.';
|
|
126
|
-
}
|
|
127
|
-
return undefined;
|
|
128
|
-
},
|
|
129
162
|
};
|
|
130
163
|
Password.argTypes = {
|
|
131
164
|
suffix: { control: false },
|
|
@@ -37,6 +37,12 @@ export const Default: StoryObj< typeof ValidatedNumberControl > = {
|
|
|
37
37
|
useState<
|
|
38
38
|
React.ComponentProps< typeof ValidatedNumberControl >[ 'value' ]
|
|
39
39
|
>();
|
|
40
|
+
const [ customValidity, setCustomValidity ] =
|
|
41
|
+
useState<
|
|
42
|
+
React.ComponentProps<
|
|
43
|
+
typeof ValidatedNumberControl
|
|
44
|
+
>[ 'customValidity' ]
|
|
45
|
+
>( undefined );
|
|
40
46
|
|
|
41
47
|
return (
|
|
42
48
|
<ValidatedNumberControl
|
|
@@ -46,6 +52,17 @@ export const Default: StoryObj< typeof ValidatedNumberControl > = {
|
|
|
46
52
|
setValue( newValue );
|
|
47
53
|
onChange?.( newValue, ...rest );
|
|
48
54
|
} }
|
|
55
|
+
onValidate={ ( v ) => {
|
|
56
|
+
if ( v && parseInt( v.toString(), 10 ) % 2 !== 0 ) {
|
|
57
|
+
setCustomValidity( {
|
|
58
|
+
type: 'invalid',
|
|
59
|
+
message: 'Choose an even number.',
|
|
60
|
+
} );
|
|
61
|
+
} else {
|
|
62
|
+
setCustomValidity( undefined );
|
|
63
|
+
}
|
|
64
|
+
} }
|
|
65
|
+
customValidity={ customValidity }
|
|
49
66
|
/>
|
|
50
67
|
);
|
|
51
68
|
},
|
|
@@ -54,10 +71,4 @@ Default.args = {
|
|
|
54
71
|
required: true,
|
|
55
72
|
label: 'Number',
|
|
56
73
|
help: 'Odd numbers are not allowed.',
|
|
57
|
-
customValidator: ( value ) => {
|
|
58
|
-
if ( value && parseInt( value.toString(), 10 ) % 2 !== 0 ) {
|
|
59
|
-
return 'Choose an even number.';
|
|
60
|
-
}
|
|
61
|
-
return undefined;
|
|
62
|
-
},
|
|
63
74
|
};
|
|
@@ -16,7 +16,7 @@ We are still gathering feedback and iterating. Please get in touch with `@WordPr
|
|
|
16
16
|
|
|
17
17
|
Component APIs are the same as the underlying WordPress components, with the addition of some optional props:
|
|
18
18
|
|
|
19
|
-
<ArgTypes of={ ValidatedInputControl } include={ [ 'required', 'markWhenOptional', '
|
|
19
|
+
<ArgTypes of={ ValidatedInputControl } include={ [ 'required', 'markWhenOptional', 'onValidate', 'customValidity' ] } />
|
|
20
20
|
|
|
21
21
|
## Implementation
|
|
22
22
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
5
|
+
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* WordPress dependencies
|
|
8
9
|
*/
|
|
9
|
-
import
|
|
10
|
+
import { useRef, useCallback, useState } from '@wordpress/element';
|
|
11
|
+
import { debounce } from '@wordpress/compose';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Internal dependencies
|
|
@@ -32,6 +34,18 @@ export const WithMultipleControls: Story = {
|
|
|
32
34
|
render: function Template() {
|
|
33
35
|
const [ text, setText ] = useState( '' );
|
|
34
36
|
const [ text2, setText2 ] = useState( '' );
|
|
37
|
+
const [ customValidity, setCustomValidity ] =
|
|
38
|
+
useState<
|
|
39
|
+
React.ComponentProps<
|
|
40
|
+
typeof ValidatedInputControl
|
|
41
|
+
>[ 'customValidity' ]
|
|
42
|
+
>( undefined );
|
|
43
|
+
const [ customValidity2, setCustomValidity2 ] =
|
|
44
|
+
useState<
|
|
45
|
+
React.ComponentProps<
|
|
46
|
+
typeof ValidatedInputControl
|
|
47
|
+
>[ 'customValidity' ]
|
|
48
|
+
>( undefined );
|
|
35
49
|
|
|
36
50
|
return (
|
|
37
51
|
<>
|
|
@@ -40,12 +54,17 @@ export const WithMultipleControls: Story = {
|
|
|
40
54
|
required
|
|
41
55
|
value={ text }
|
|
42
56
|
help="The word 'error' will trigger an error."
|
|
43
|
-
|
|
57
|
+
onValidate={ ( value ) => {
|
|
44
58
|
if ( value?.toLowerCase() === 'error' ) {
|
|
45
|
-
|
|
59
|
+
setCustomValidity( {
|
|
60
|
+
type: 'invalid',
|
|
61
|
+
message: 'The word "error" is not allowed.',
|
|
62
|
+
} );
|
|
63
|
+
} else {
|
|
64
|
+
setCustomValidity( undefined );
|
|
46
65
|
}
|
|
47
|
-
return undefined;
|
|
48
66
|
} }
|
|
67
|
+
customValidity={ customValidity }
|
|
49
68
|
onChange={ ( value ) => setText( value ?? '' ) }
|
|
50
69
|
/>
|
|
51
70
|
<ValidatedInputControl
|
|
@@ -53,13 +72,18 @@ export const WithMultipleControls: Story = {
|
|
|
53
72
|
required
|
|
54
73
|
value={ text2 }
|
|
55
74
|
help="The word 'error' will trigger an error."
|
|
56
|
-
|
|
75
|
+
onValidate={ ( value ) => {
|
|
57
76
|
if ( value?.toLowerCase() === 'error' ) {
|
|
58
|
-
|
|
77
|
+
setCustomValidity2( {
|
|
78
|
+
type: 'invalid',
|
|
79
|
+
message: 'The word "error" is not allowed.',
|
|
80
|
+
} );
|
|
81
|
+
} else {
|
|
82
|
+
setCustomValidity2( undefined );
|
|
59
83
|
}
|
|
60
|
-
return undefined;
|
|
61
84
|
} }
|
|
62
85
|
onChange={ ( value ) => setText2( value ?? '' ) }
|
|
86
|
+
customValidity={ customValidity2 }
|
|
63
87
|
/>
|
|
64
88
|
</>
|
|
65
89
|
);
|
|
@@ -73,7 +97,12 @@ export const WithMultipleControls: Story = {
|
|
|
73
97
|
export const WithHelpTextReplacement: Story = {
|
|
74
98
|
render: function Template() {
|
|
75
99
|
const [ text, setText ] = useState( '' );
|
|
76
|
-
const [
|
|
100
|
+
const [ customValidity, setCustomValidity ] =
|
|
101
|
+
useState<
|
|
102
|
+
React.ComponentProps<
|
|
103
|
+
typeof ValidatedInputControl
|
|
104
|
+
>[ 'customValidity' ]
|
|
105
|
+
>( undefined );
|
|
77
106
|
|
|
78
107
|
return (
|
|
79
108
|
<ValidatedInputControl
|
|
@@ -81,20 +110,181 @@ export const WithHelpTextReplacement: Story = {
|
|
|
81
110
|
required
|
|
82
111
|
value={ text }
|
|
83
112
|
help={
|
|
84
|
-
|
|
113
|
+
customValidity
|
|
85
114
|
? undefined
|
|
86
115
|
: 'The word "error" is not allowed.'
|
|
87
116
|
}
|
|
88
|
-
|
|
117
|
+
onValidate={ ( value ) => {
|
|
89
118
|
if ( value?.toLowerCase() === 'error' ) {
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
setCustomValidity( {
|
|
120
|
+
type: 'invalid',
|
|
121
|
+
message: 'The word "error" is not allowed.',
|
|
122
|
+
} );
|
|
123
|
+
} else {
|
|
124
|
+
setCustomValidity( undefined );
|
|
92
125
|
}
|
|
93
|
-
setHasCustomError( false );
|
|
94
|
-
return undefined;
|
|
95
126
|
} }
|
|
96
127
|
onChange={ ( value ) => setText( value ?? '' ) }
|
|
128
|
+
customValidity={ customValidity }
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* To provide feedback from server-side validation, the `customValidity` prop can be used
|
|
136
|
+
* to show additional status indicators while waiting for the server response,
|
|
137
|
+
* and after the response is received.
|
|
138
|
+
*
|
|
139
|
+
* These indicators are intended for asynchronous validation calls that may take more than 1 second to complete.
|
|
140
|
+
* They may be unnecessary when responses are generally quick.
|
|
141
|
+
*/
|
|
142
|
+
export const AsyncValidation: StoryObj< typeof ValidatedInputControl > = {
|
|
143
|
+
render: function Template( { ...args } ) {
|
|
144
|
+
const [ text, setText ] = useState( '' );
|
|
145
|
+
const [ customValidity, setCustomValidity ] =
|
|
146
|
+
useState<
|
|
147
|
+
React.ComponentProps<
|
|
148
|
+
typeof ValidatedInputControl
|
|
149
|
+
>[ 'customValidity' ]
|
|
150
|
+
>( undefined );
|
|
151
|
+
|
|
152
|
+
const timeoutRef = useRef< ReturnType< typeof setTimeout > >();
|
|
153
|
+
const previousValidationValueRef = useRef< unknown >( '' );
|
|
154
|
+
|
|
155
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
156
|
+
const debouncedValidate = useCallback(
|
|
157
|
+
debounce( ( v ) => {
|
|
158
|
+
if ( v === previousValidationValueRef.current ) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
previousValidationValueRef.current = v;
|
|
163
|
+
|
|
164
|
+
setCustomValidity( {
|
|
165
|
+
type: 'validating',
|
|
166
|
+
message: 'Validating...',
|
|
167
|
+
} );
|
|
168
|
+
|
|
169
|
+
clearTimeout( timeoutRef.current );
|
|
170
|
+
timeoutRef.current = setTimeout( () => {
|
|
171
|
+
if ( v?.toString().toLowerCase() === 'error' ) {
|
|
172
|
+
setCustomValidity( {
|
|
173
|
+
type: 'invalid',
|
|
174
|
+
message: 'The word "error" is not allowed.',
|
|
175
|
+
} );
|
|
176
|
+
} else {
|
|
177
|
+
setCustomValidity( {
|
|
178
|
+
type: 'valid',
|
|
179
|
+
message: 'Validated',
|
|
180
|
+
} );
|
|
181
|
+
}
|
|
182
|
+
}, 1500 );
|
|
183
|
+
}, 500 ),
|
|
184
|
+
[]
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<ValidatedInputControl
|
|
189
|
+
{ ...args }
|
|
190
|
+
value={ text }
|
|
191
|
+
onChange={ ( newValue ) => {
|
|
192
|
+
setText( newValue ?? '' );
|
|
193
|
+
} }
|
|
194
|
+
onValidate={ debouncedValidate }
|
|
195
|
+
customValidity={ customValidity }
|
|
97
196
|
/>
|
|
98
197
|
);
|
|
99
198
|
},
|
|
199
|
+
args: {
|
|
200
|
+
label: 'Text',
|
|
201
|
+
help: 'The word "error" will trigger an error asynchronously.',
|
|
202
|
+
required: true,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Not exported - Only for testing purposes.
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
208
|
+
const AsyncValidationWithTest: StoryObj< typeof ValidatedInputControl > = {
|
|
209
|
+
...AsyncValidation,
|
|
210
|
+
play: async ( { canvasElement } ) => {
|
|
211
|
+
const canvas = within( canvasElement );
|
|
212
|
+
await userEvent.click( canvas.getByRole( 'textbox' ) );
|
|
213
|
+
await userEvent.type( canvas.getByRole( 'textbox' ), 'valid text', {
|
|
214
|
+
delay: 10,
|
|
215
|
+
} );
|
|
216
|
+
await userEvent.tab();
|
|
217
|
+
|
|
218
|
+
await waitFor(
|
|
219
|
+
() => {
|
|
220
|
+
expect( canvas.getByText( 'Validated' ) ).toBeVisible();
|
|
221
|
+
},
|
|
222
|
+
{ timeout: 2500 }
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 500 ) );
|
|
226
|
+
await userEvent.clear( canvas.getByRole( 'textbox' ) );
|
|
227
|
+
|
|
228
|
+
// Should show validating state when transitioning from valid to invalid.
|
|
229
|
+
await waitFor(
|
|
230
|
+
() => {
|
|
231
|
+
expect( canvas.getByText( 'Validating...' ) ).toBeVisible();
|
|
232
|
+
},
|
|
233
|
+
{ timeout: 2500 }
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
await waitFor(
|
|
237
|
+
() => {
|
|
238
|
+
expect(
|
|
239
|
+
canvas.getByText( 'Please fill out this field.' )
|
|
240
|
+
).toBeVisible();
|
|
241
|
+
},
|
|
242
|
+
{ timeout: 2500 }
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Should not show validating state if there were no changes
|
|
246
|
+
// after a valid/invalid state was already shown.
|
|
247
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 1500 ) );
|
|
248
|
+
await expect(
|
|
249
|
+
canvas.queryByText( 'Validating...' )
|
|
250
|
+
).not.toBeInTheDocument();
|
|
251
|
+
|
|
252
|
+
await userEvent.type( canvas.getByRole( 'textbox' ), 'e', {
|
|
253
|
+
delay: 10,
|
|
254
|
+
} );
|
|
255
|
+
|
|
256
|
+
// Should not show valid state if server has not yet responded.
|
|
257
|
+
await expect(
|
|
258
|
+
canvas.queryByText( 'Validated' )
|
|
259
|
+
).not.toBeInTheDocument();
|
|
260
|
+
|
|
261
|
+
// Should show validating state when transitioning from invalid to valid.
|
|
262
|
+
await waitFor(
|
|
263
|
+
() => {
|
|
264
|
+
expect( canvas.getByText( 'Validating...' ) ).toBeVisible();
|
|
265
|
+
},
|
|
266
|
+
{ timeout: 2500 }
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
await waitFor(
|
|
270
|
+
() => {
|
|
271
|
+
expect( canvas.getByText( 'Validated' ) ).toBeVisible();
|
|
272
|
+
},
|
|
273
|
+
{ timeout: 2500 }
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) );
|
|
277
|
+
await userEvent.type( canvas.getByRole( 'textbox' ), 'rror', {
|
|
278
|
+
delay: 10,
|
|
279
|
+
} );
|
|
280
|
+
|
|
281
|
+
await waitFor(
|
|
282
|
+
() => {
|
|
283
|
+
expect(
|
|
284
|
+
canvas.getByText( 'The word "error" is not allowed.' )
|
|
285
|
+
).toBeVisible();
|
|
286
|
+
},
|
|
287
|
+
{ timeout: 2500 }
|
|
288
|
+
);
|
|
289
|
+
},
|
|
100
290
|
};
|
|
@@ -35,6 +35,12 @@ export const Default: StoryObj< typeof ValidatedRadioControl > = {
|
|
|
35
35
|
typeof ValidatedRadioControl
|
|
36
36
|
>[ 'selected' ]
|
|
37
37
|
>();
|
|
38
|
+
const [ customValidity, setCustomValidity ] =
|
|
39
|
+
useState<
|
|
40
|
+
React.ComponentProps<
|
|
41
|
+
typeof ValidatedRadioControl
|
|
42
|
+
>[ 'customValidity' ]
|
|
43
|
+
>( undefined );
|
|
38
44
|
|
|
39
45
|
return (
|
|
40
46
|
<ValidatedRadioControl
|
|
@@ -44,6 +50,17 @@ export const Default: StoryObj< typeof ValidatedRadioControl > = {
|
|
|
44
50
|
setSelected( value );
|
|
45
51
|
onChange?.( value );
|
|
46
52
|
} }
|
|
53
|
+
onValidate={ ( v ) => {
|
|
54
|
+
if ( v === 'b' ) {
|
|
55
|
+
setCustomValidity( {
|
|
56
|
+
type: 'invalid',
|
|
57
|
+
message: 'Option B is not allowed.',
|
|
58
|
+
} );
|
|
59
|
+
} else {
|
|
60
|
+
setCustomValidity( undefined );
|
|
61
|
+
}
|
|
62
|
+
} }
|
|
63
|
+
customValidity={ customValidity }
|
|
47
64
|
/>
|
|
48
65
|
);
|
|
49
66
|
},
|
|
@@ -56,10 +73,4 @@ Default.args = {
|
|
|
56
73
|
{ label: 'Option A', value: 'a' },
|
|
57
74
|
{ label: 'Option B (not allowed)', value: 'b' },
|
|
58
75
|
],
|
|
59
|
-
customValidator: ( value ) => {
|
|
60
|
-
if ( value === 'b' ) {
|
|
61
|
-
return 'Option B is not allowed.';
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
},
|
|
65
76
|
};
|
|
@@ -33,6 +33,12 @@ export const Default: StoryObj< typeof ValidatedRangeControl > = {
|
|
|
33
33
|
useState<
|
|
34
34
|
React.ComponentProps< typeof ValidatedRangeControl >[ 'value' ]
|
|
35
35
|
>();
|
|
36
|
+
const [ customValidity, setCustomValidity ] =
|
|
37
|
+
useState<
|
|
38
|
+
React.ComponentProps<
|
|
39
|
+
typeof ValidatedRangeControl
|
|
40
|
+
>[ 'customValidity' ]
|
|
41
|
+
>( undefined );
|
|
36
42
|
|
|
37
43
|
return (
|
|
38
44
|
<ValidatedRangeControl
|
|
@@ -42,6 +48,17 @@ export const Default: StoryObj< typeof ValidatedRangeControl > = {
|
|
|
42
48
|
setValue( newValue );
|
|
43
49
|
onChange?.( newValue );
|
|
44
50
|
} }
|
|
51
|
+
onValidate={ ( v ) => {
|
|
52
|
+
if ( v && v % 2 !== 0 ) {
|
|
53
|
+
setCustomValidity( {
|
|
54
|
+
type: 'invalid',
|
|
55
|
+
message: 'Choose an even number.',
|
|
56
|
+
} );
|
|
57
|
+
} else {
|
|
58
|
+
setCustomValidity( undefined );
|
|
59
|
+
}
|
|
60
|
+
} }
|
|
61
|
+
customValidity={ customValidity }
|
|
45
62
|
/>
|
|
46
63
|
);
|
|
47
64
|
},
|
|
@@ -52,10 +69,4 @@ Default.args = {
|
|
|
52
69
|
help: 'Odd numbers are not allowed.',
|
|
53
70
|
min: 0,
|
|
54
71
|
max: 20,
|
|
55
|
-
customValidator: ( value ) => {
|
|
56
|
-
if ( value && value % 2 !== 0 ) {
|
|
57
|
-
return 'Choose an even number.';
|
|
58
|
-
}
|
|
59
|
-
return undefined;
|
|
60
|
-
},
|
|
61
72
|
};
|
|
@@ -30,6 +30,12 @@ export default meta;
|
|
|
30
30
|
export const Default: StoryObj< typeof ValidatedSelectControl > = {
|
|
31
31
|
render: function Template( { onChange, ...args } ) {
|
|
32
32
|
const [ value, setValue ] = useState( '' );
|
|
33
|
+
const [ customValidity, setCustomValidity ] =
|
|
34
|
+
useState<
|
|
35
|
+
React.ComponentProps<
|
|
36
|
+
typeof ValidatedSelectControl
|
|
37
|
+
>[ 'customValidity' ]
|
|
38
|
+
>( undefined );
|
|
33
39
|
|
|
34
40
|
return (
|
|
35
41
|
<ValidatedSelectControl
|
|
@@ -39,6 +45,17 @@ export const Default: StoryObj< typeof ValidatedSelectControl > = {
|
|
|
39
45
|
setValue( newValue );
|
|
40
46
|
onChange?.( newValue );
|
|
41
47
|
} }
|
|
48
|
+
onValidate={ ( v ) => {
|
|
49
|
+
if ( v === '1' ) {
|
|
50
|
+
setCustomValidity( {
|
|
51
|
+
type: 'invalid',
|
|
52
|
+
message: 'Option 1 is not allowed.',
|
|
53
|
+
} );
|
|
54
|
+
} else {
|
|
55
|
+
setCustomValidity( undefined );
|
|
56
|
+
}
|
|
57
|
+
} }
|
|
58
|
+
customValidity={ customValidity }
|
|
42
59
|
/>
|
|
43
60
|
);
|
|
44
61
|
},
|
|
@@ -52,10 +69,4 @@ Default.args = {
|
|
|
52
69
|
{ value: '1', label: 'Option 1 (not allowed)' },
|
|
53
70
|
{ value: '2', label: 'Option 2' },
|
|
54
71
|
],
|
|
55
|
-
customValidator: ( value ) => {
|
|
56
|
-
if ( value === '1' ) {
|
|
57
|
-
return 'Option 1 is not allowed.';
|
|
58
|
-
}
|
|
59
|
-
return undefined;
|
|
60
|
-
},
|
|
61
72
|
};
|
|
@@ -30,6 +30,12 @@ export default meta;
|
|
|
30
30
|
export const Default: StoryObj< typeof ValidatedTextControl > = {
|
|
31
31
|
render: function Template( { onChange, ...args } ) {
|
|
32
32
|
const [ value, setValue ] = useState( '' );
|
|
33
|
+
const [ customValidity, setCustomValidity ] =
|
|
34
|
+
useState<
|
|
35
|
+
React.ComponentProps<
|
|
36
|
+
typeof ValidatedTextControl
|
|
37
|
+
>[ 'customValidity' ]
|
|
38
|
+
>( undefined );
|
|
33
39
|
|
|
34
40
|
return (
|
|
35
41
|
<ValidatedTextControl
|
|
@@ -39,6 +45,17 @@ export const Default: StoryObj< typeof ValidatedTextControl > = {
|
|
|
39
45
|
setValue( newValue );
|
|
40
46
|
onChange?.( newValue );
|
|
41
47
|
} }
|
|
48
|
+
onValidate={ ( v ) => {
|
|
49
|
+
if ( v?.toString().toLowerCase() === 'error' ) {
|
|
50
|
+
setCustomValidity( {
|
|
51
|
+
type: 'invalid',
|
|
52
|
+
message: 'The word "error" is not allowed.',
|
|
53
|
+
} );
|
|
54
|
+
} else {
|
|
55
|
+
setCustomValidity( undefined );
|
|
56
|
+
}
|
|
57
|
+
} }
|
|
58
|
+
customValidity={ customValidity }
|
|
42
59
|
/>
|
|
43
60
|
);
|
|
44
61
|
},
|
|
@@ -47,10 +64,4 @@ Default.args = {
|
|
|
47
64
|
required: true,
|
|
48
65
|
label: 'Text',
|
|
49
66
|
help: "The word 'error' will trigger an error.",
|
|
50
|
-
customValidator: ( value ) => {
|
|
51
|
-
if ( value?.toString().toLowerCase() === 'error' ) {
|
|
52
|
-
return 'The word "error" is not allowed.';
|
|
53
|
-
}
|
|
54
|
-
return undefined;
|
|
55
|
-
},
|
|
56
67
|
};
|