@wordpress/components 30.1.0 → 30.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 +19 -3
- package/build/custom-gradient-picker/serializer.js +14 -0
- package/build/custom-gradient-picker/serializer.js.map +1 -1
- package/build/custom-gradient-picker/utils.js +12 -0
- package/build/custom-gradient-picker/utils.js.map +1 -1
- package/build/date-time/date/index.js +2 -4
- package/build/date-time/date/index.js.map +1 -1
- package/build/date-time/date/styles.js +41 -20
- package/build/date-time/date/styles.js.map +1 -1
- package/build/menu-item/index.js +1 -0
- package/build/menu-item/index.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 +57 -22
- 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/custom-gradient-picker/serializer.js +14 -0
- package/build-module/custom-gradient-picker/serializer.js.map +1 -1
- package/build-module/custom-gradient-picker/utils.js +12 -0
- package/build-module/custom-gradient-picker/utils.js.map +1 -1
- package/build-module/date-time/date/index.js +3 -4
- package/build-module/date-time/date/index.js.map +1 -1
- package/build-module/date-time/date/styles.js +39 -14
- package/build-module/date-time/date/styles.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/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 +57 -21
- 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 +37 -25
- package/build-style/style.css +37 -25
- package/build-types/confirm-dialog/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-gradient-picker/serializer.d.ts.map +1 -1
- package/build-types/custom-gradient-picker/utils.d.ts.map +1 -1
- package/build-types/date-time/date/index.d.ts.map +1 -1
- package/build-types/date-time/date/styles.d.ts +6 -0
- package/build-types/date-time/date/styles.d.ts.map +1 -1
- package/build-types/divider/stories/index.story.d.ts.map +1 -1
- package/build-types/elevation/stories/index.story.d.ts.map +1 -1
- package/build-types/form-token-field/stories/index.story.d.ts +10 -5
- package/build-types/form-token-field/stories/index.story.d.ts.map +1 -1
- package/build-types/gradient-picker/stories/index.story.d.ts +2 -1
- package/build-types/gradient-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/grid/stories/index.story.d.ts.map +1 -1
- package/build-types/h-stack/stories/index.story.d.ts.map +1 -1
- package/build-types/heading/stories/index.story.d.ts.map +1 -1
- package/build-types/input-control/stories/index.story.d.ts.map +1 -1
- package/build-types/item-group/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-item/index.d.ts.map +1 -1
- package/build-types/number-control/stories/index.story.d.ts.map +1 -1
- package/build-types/scrollable/stories/index.story.d.ts.map +1 -1
- package/build-types/spacer/stories/index.story.d.ts.map +1 -1
- package/build-types/surface/stories/index.story.d.ts.map +1 -1
- package/build-types/text/stories/index.story.d.ts.map +1 -1
- package/build-types/toggle-group-control/stories/index.story.d.ts.map +1 -1
- package/build-types/tools-panel/stories/index.story.d.ts.map +1 -1
- package/build-types/tree-grid/stories/index.story.d.ts.map +1 -1
- package/build-types/truncate/stories/index.story.d.ts.map +1 -1
- package/build-types/unit-control/stories/index.story.d.ts.map +1 -1
- package/build-types/v-stack/stories/index.story.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/validity-indicator.d.ts +5 -0
- package/build-types/validated-form-controls/validity-indicator.d.ts.map +1 -0
- package/build-types/view/stories/index.story.d.ts.map +1 -1
- package/build-types/z-stack/stories/index.story.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/button/style.scss +3 -3
- package/src/calendar/style.scss +22 -22
- package/src/confirm-dialog/stories/index.story.tsx +3 -2
- package/src/custom-gradient-picker/serializer.ts +14 -0
- package/src/custom-gradient-picker/test/serializer.ts +25 -0
- package/src/custom-gradient-picker/utils.ts +10 -0
- package/src/date-time/date/index.tsx +4 -3
- package/src/date-time/date/styles.ts +13 -20
- package/src/divider/stories/index.story.tsx +2 -1
- package/src/elevation/stories/index.story.tsx +2 -1
- package/src/form-token-field/stories/index.story.tsx +15 -7
- package/src/gradient-picker/stories/index.story.tsx +48 -0
- package/src/grid/stories/index.story.tsx +2 -1
- package/src/h-stack/stories/e2e/index.story.tsx +1 -1
- package/src/h-stack/stories/index.story.tsx +3 -2
- package/src/heading/stories/index.story.tsx +3 -2
- package/src/input-control/stories/index.story.tsx +3 -2
- package/src/item-group/stories/index.story.tsx +2 -1
- package/src/menu/stories/index.story.tsx +1 -1
- package/src/menu-item/index.tsx +1 -0
- package/src/number-control/stories/index.story.tsx +3 -2
- package/src/scrollable/stories/index.story.tsx +2 -1
- package/src/spacer/stories/index.story.tsx +2 -1
- package/src/surface/stories/index.story.tsx +2 -1
- package/src/text/stories/index.story.tsx +3 -2
- package/src/toggle-group-control/stories/index.story.tsx +3 -2
- package/src/tools-panel/stories/index.story.tsx +2 -1
- package/src/tree-grid/stories/index.story.tsx +3 -2
- package/src/truncate/stories/index.story.tsx +3 -2
- package/src/unit-control/stories/index.story.tsx +3 -2
- package/src/utils/theme-variables.scss +1 -0
- package/src/v-stack/stories/e2e/index.story.tsx +1 -1
- package/src/v-stack/stories/index.story.tsx +3 -2
- 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 +19 -7
- package/src/validated-form-controls/components/stories/combobox-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/custom-select-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/input-control.story.tsx +53 -19
- package/src/validated-form-controls/components/stories/number-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/overview.mdx +2 -2
- package/src/validated-form-controls/components/stories/overview.story.tsx +124 -16
- package/src/validated-form-controls/components/stories/radio-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/range-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/select-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/text-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/textarea-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/toggle-control.story.tsx +19 -7
- package/src/validated-form-controls/components/stories/toggle-group-control.story.tsx +19 -7
- 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 +77 -26
- package/src/validated-form-controls/style.scss +19 -5
- package/src/validated-form-controls/validity-indicator.tsx +48 -0
- package/src/view/stories/index.story.tsx +2 -1
- package/src/z-stack/stories/index.story.tsx +2 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -15,7 +15,8 @@ import { ValidatedSelectControl } from '../select-control';
|
|
|
15
15
|
import { formDecorator } from './story-utils';
|
|
16
16
|
|
|
17
17
|
const meta: Meta< typeof ValidatedSelectControl > = {
|
|
18
|
-
title: 'Components
|
|
18
|
+
title: 'Components/Selection & Input/Validated Form Controls/ValidatedSelectControl',
|
|
19
|
+
id: 'components-validatedselectcontrol',
|
|
19
20
|
component: ValidatedSelectControl,
|
|
20
21
|
tags: [ 'status-private' ],
|
|
21
22
|
decorators: formDecorator,
|
|
@@ -29,6 +30,12 @@ export default meta;
|
|
|
29
30
|
export const Default: StoryObj< typeof ValidatedSelectControl > = {
|
|
30
31
|
render: function Template( { onChange, ...args } ) {
|
|
31
32
|
const [ value, setValue ] = useState( '' );
|
|
33
|
+
const [ customValidity, setCustomValidity ] =
|
|
34
|
+
useState<
|
|
35
|
+
React.ComponentProps<
|
|
36
|
+
typeof ValidatedSelectControl
|
|
37
|
+
>[ 'customValidity' ]
|
|
38
|
+
>( undefined );
|
|
32
39
|
|
|
33
40
|
return (
|
|
34
41
|
<ValidatedSelectControl
|
|
@@ -38,6 +45,17 @@ export const Default: StoryObj< typeof ValidatedSelectControl > = {
|
|
|
38
45
|
setValue( newValue );
|
|
39
46
|
onChange?.( newValue );
|
|
40
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 }
|
|
41
59
|
/>
|
|
42
60
|
);
|
|
43
61
|
},
|
|
@@ -51,10 +69,4 @@ Default.args = {
|
|
|
51
69
|
{ value: '1', label: 'Option 1 (not allowed)' },
|
|
52
70
|
{ value: '2', label: 'Option 2' },
|
|
53
71
|
],
|
|
54
|
-
customValidator: ( value ) => {
|
|
55
|
-
if ( value === '1' ) {
|
|
56
|
-
return 'Option 1 is not allowed.';
|
|
57
|
-
}
|
|
58
|
-
return undefined;
|
|
59
|
-
},
|
|
60
72
|
};
|
|
@@ -15,7 +15,8 @@ import { formDecorator } from './story-utils';
|
|
|
15
15
|
import { ValidatedTextControl } from '../text-control';
|
|
16
16
|
|
|
17
17
|
const meta: Meta< typeof ValidatedTextControl > = {
|
|
18
|
-
title: 'Components
|
|
18
|
+
title: 'Components/Selection & Input/Validated Form Controls/ValidatedTextControl',
|
|
19
|
+
id: 'components-validatedtextcontrol',
|
|
19
20
|
component: ValidatedTextControl,
|
|
20
21
|
tags: [ 'status-private' ],
|
|
21
22
|
decorators: formDecorator,
|
|
@@ -29,6 +30,12 @@ export default meta;
|
|
|
29
30
|
export const Default: StoryObj< typeof ValidatedTextControl > = {
|
|
30
31
|
render: function Template( { onChange, ...args } ) {
|
|
31
32
|
const [ value, setValue ] = useState( '' );
|
|
33
|
+
const [ customValidity, setCustomValidity ] =
|
|
34
|
+
useState<
|
|
35
|
+
React.ComponentProps<
|
|
36
|
+
typeof ValidatedTextControl
|
|
37
|
+
>[ 'customValidity' ]
|
|
38
|
+
>( undefined );
|
|
32
39
|
|
|
33
40
|
return (
|
|
34
41
|
<ValidatedTextControl
|
|
@@ -38,6 +45,17 @@ export const Default: StoryObj< typeof ValidatedTextControl > = {
|
|
|
38
45
|
setValue( newValue );
|
|
39
46
|
onChange?.( newValue );
|
|
40
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 }
|
|
41
59
|
/>
|
|
42
60
|
);
|
|
43
61
|
},
|
|
@@ -46,10 +64,4 @@ Default.args = {
|
|
|
46
64
|
required: true,
|
|
47
65
|
label: 'Text',
|
|
48
66
|
help: "The word 'error' will trigger an error.",
|
|
49
|
-
customValidator: ( value ) => {
|
|
50
|
-
if ( value?.toString().toLowerCase() === 'error' ) {
|
|
51
|
-
return 'The word "error" is not allowed.';
|
|
52
|
-
}
|
|
53
|
-
return undefined;
|
|
54
|
-
},
|
|
55
67
|
};
|
|
@@ -14,7 +14,8 @@ import { formDecorator } from './story-utils';
|
|
|
14
14
|
import { ValidatedTextareaControl } from '../textarea-control';
|
|
15
15
|
|
|
16
16
|
const meta: Meta< typeof ValidatedTextareaControl > = {
|
|
17
|
-
title: 'Components
|
|
17
|
+
title: 'Components/Selection & Input/Validated Form Controls/ValidatedTextareaControl',
|
|
18
|
+
id: 'components-validatedtextareacontrol',
|
|
18
19
|
component: ValidatedTextareaControl,
|
|
19
20
|
tags: [ 'status-private' ],
|
|
20
21
|
decorators: formDecorator,
|
|
@@ -26,6 +27,12 @@ export default meta;
|
|
|
26
27
|
export const Default: StoryObj< typeof ValidatedTextareaControl > = {
|
|
27
28
|
render: function Template( { onChange, ...args } ) {
|
|
28
29
|
const [ value, setValue ] = useState( '' );
|
|
30
|
+
const [ customValidity, setCustomValidity ] =
|
|
31
|
+
useState<
|
|
32
|
+
React.ComponentProps<
|
|
33
|
+
typeof ValidatedTextareaControl
|
|
34
|
+
>[ 'customValidity' ]
|
|
35
|
+
>( undefined );
|
|
29
36
|
|
|
30
37
|
return (
|
|
31
38
|
<ValidatedTextareaControl
|
|
@@ -35,6 +42,17 @@ export const Default: StoryObj< typeof ValidatedTextareaControl > = {
|
|
|
35
42
|
onChange?.( newValue );
|
|
36
43
|
} }
|
|
37
44
|
value={ value }
|
|
45
|
+
onValidate={ ( v ) => {
|
|
46
|
+
if ( v?.toLowerCase() === 'error' ) {
|
|
47
|
+
setCustomValidity( {
|
|
48
|
+
type: 'invalid',
|
|
49
|
+
message: 'The word "error" is not allowed.',
|
|
50
|
+
} );
|
|
51
|
+
} else {
|
|
52
|
+
setCustomValidity( undefined );
|
|
53
|
+
}
|
|
54
|
+
} }
|
|
55
|
+
customValidity={ customValidity }
|
|
38
56
|
/>
|
|
39
57
|
);
|
|
40
58
|
},
|
|
@@ -43,10 +61,4 @@ Default.args = {
|
|
|
43
61
|
required: true,
|
|
44
62
|
label: 'Textarea',
|
|
45
63
|
help: 'The word "error" will trigger an error.',
|
|
46
|
-
customValidator: ( value ) => {
|
|
47
|
-
if ( value?.toLowerCase() === 'error' ) {
|
|
48
|
-
return 'The word "error" is not allowed.';
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
},
|
|
52
64
|
};
|
|
@@ -15,7 +15,8 @@ import { formDecorator } from './story-utils';
|
|
|
15
15
|
import { ValidatedToggleControl } from '../toggle-control';
|
|
16
16
|
|
|
17
17
|
const meta: Meta< typeof ValidatedToggleControl > = {
|
|
18
|
-
title: 'Components
|
|
18
|
+
title: 'Components/Selection & Input/Validated Form Controls/ValidatedToggleControl',
|
|
19
|
+
id: 'components-validatedtogglecontrol',
|
|
19
20
|
component: ValidatedToggleControl,
|
|
20
21
|
tags: [ 'status-private' ],
|
|
21
22
|
decorators: formDecorator,
|
|
@@ -29,6 +30,12 @@ export default meta;
|
|
|
29
30
|
export const Default: StoryObj< typeof ValidatedToggleControl > = {
|
|
30
31
|
render: function Template( { onChange, ...args } ) {
|
|
31
32
|
const [ checked, setChecked ] = useState( false );
|
|
33
|
+
const [ customValidity, setCustomValidity ] =
|
|
34
|
+
useState<
|
|
35
|
+
React.ComponentProps<
|
|
36
|
+
typeof ValidatedToggleControl
|
|
37
|
+
>[ 'customValidity' ]
|
|
38
|
+
>( undefined );
|
|
32
39
|
|
|
33
40
|
return (
|
|
34
41
|
<ValidatedToggleControl
|
|
@@ -38,6 +45,17 @@ export const Default: StoryObj< typeof ValidatedToggleControl > = {
|
|
|
38
45
|
setChecked( value );
|
|
39
46
|
onChange?.( value );
|
|
40
47
|
} }
|
|
48
|
+
onValidate={ ( v ) => {
|
|
49
|
+
if ( v ) {
|
|
50
|
+
setCustomValidity( {
|
|
51
|
+
type: 'invalid',
|
|
52
|
+
message: 'This toggle may not be enabled.',
|
|
53
|
+
} );
|
|
54
|
+
} else {
|
|
55
|
+
setCustomValidity( undefined );
|
|
56
|
+
}
|
|
57
|
+
} }
|
|
58
|
+
customValidity={ customValidity }
|
|
41
59
|
/>
|
|
42
60
|
);
|
|
43
61
|
},
|
|
@@ -46,10 +64,4 @@ Default.args = {
|
|
|
46
64
|
required: true,
|
|
47
65
|
label: 'Toggle',
|
|
48
66
|
help: 'This toggle may neither be enabled nor disabled.',
|
|
49
|
-
customValidator: ( value ) => {
|
|
50
|
-
if ( value ) {
|
|
51
|
-
return 'This toggle may not be enabled.';
|
|
52
|
-
}
|
|
53
|
-
return undefined;
|
|
54
|
-
},
|
|
55
67
|
};
|
|
@@ -16,7 +16,8 @@ import { ValidatedToggleGroupControl } from '../toggle-group-control';
|
|
|
16
16
|
import { ToggleGroupControlOption } from '../../../toggle-group-control';
|
|
17
17
|
|
|
18
18
|
const meta: Meta< typeof ValidatedToggleGroupControl > = {
|
|
19
|
-
title: 'Components
|
|
19
|
+
title: 'Components/Selection & Input/Validated Form Controls/ValidatedToggleGroupControl',
|
|
20
|
+
id: 'components-validatedtogglegroupcontrol',
|
|
20
21
|
component: ValidatedToggleGroupControl,
|
|
21
22
|
tags: [ 'status-private' ],
|
|
22
23
|
decorators: formDecorator,
|
|
@@ -35,6 +36,12 @@ export const Default: StoryObj< typeof ValidatedToggleGroupControl > = {
|
|
|
35
36
|
typeof ValidatedToggleGroupControl
|
|
36
37
|
>[ 'value' ]
|
|
37
38
|
>( '1' );
|
|
39
|
+
const [ customValidity, setCustomValidity ] =
|
|
40
|
+
useState<
|
|
41
|
+
React.ComponentProps<
|
|
42
|
+
typeof ValidatedToggleGroupControl
|
|
43
|
+
>[ 'customValidity' ]
|
|
44
|
+
>( undefined );
|
|
38
45
|
|
|
39
46
|
return (
|
|
40
47
|
<ValidatedToggleGroupControl
|
|
@@ -44,6 +51,17 @@ export const Default: StoryObj< typeof ValidatedToggleGroupControl > = {
|
|
|
44
51
|
setValue( newValue );
|
|
45
52
|
onChange?.( newValue );
|
|
46
53
|
} }
|
|
54
|
+
onValidate={ ( v ) => {
|
|
55
|
+
if ( v === '2' ) {
|
|
56
|
+
setCustomValidity( {
|
|
57
|
+
type: 'invalid',
|
|
58
|
+
message: 'Option 2 is not allowed.',
|
|
59
|
+
} );
|
|
60
|
+
} else {
|
|
61
|
+
setCustomValidity( undefined );
|
|
62
|
+
}
|
|
63
|
+
} }
|
|
64
|
+
customValidity={ customValidity }
|
|
47
65
|
/>
|
|
48
66
|
);
|
|
49
67
|
},
|
|
@@ -57,10 +75,4 @@ Default.args = {
|
|
|
57
75
|
<ToggleGroupControlOption value="2" key="2" label="Option 2" />,
|
|
58
76
|
],
|
|
59
77
|
help: 'Selecting option 2 will trigger an error.',
|
|
60
|
-
customValidator: ( value ) => {
|
|
61
|
-
if ( value === '2' ) {
|
|
62
|
-
return 'Option 2 is not allowed.';
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
},
|
|
66
78
|
};
|
|
@@ -17,7 +17,8 @@ type Value = TextControlProps[ 'value' ];
|
|
|
17
17
|
const UnforwardedValidatedTextControl = (
|
|
18
18
|
{
|
|
19
19
|
required,
|
|
20
|
-
|
|
20
|
+
onValidate,
|
|
21
|
+
customValidity,
|
|
21
22
|
onChange,
|
|
22
23
|
markWhenOptional,
|
|
23
24
|
...restProps
|
|
@@ -36,9 +37,10 @@ const UnforwardedValidatedTextControl = (
|
|
|
36
37
|
<ControlWithError
|
|
37
38
|
required={ required }
|
|
38
39
|
markWhenOptional={ markWhenOptional }
|
|
39
|
-
|
|
40
|
-
return
|
|
40
|
+
onValidate={ () => {
|
|
41
|
+
return onValidate?.( valueRef.current );
|
|
41
42
|
} }
|
|
43
|
+
customValidity={ customValidity }
|
|
42
44
|
getValidityTarget={ () => validityTargetRef.current }
|
|
43
45
|
>
|
|
44
46
|
<TextControl
|
|
@@ -17,7 +17,8 @@ type Value = TextareaControlProps[ 'value' ];
|
|
|
17
17
|
const UnforwardedValidatedTextareaControl = (
|
|
18
18
|
{
|
|
19
19
|
required,
|
|
20
|
-
|
|
20
|
+
onValidate,
|
|
21
|
+
customValidity,
|
|
21
22
|
onChange,
|
|
22
23
|
markWhenOptional,
|
|
23
24
|
...restProps
|
|
@@ -36,9 +37,10 @@ const UnforwardedValidatedTextareaControl = (
|
|
|
36
37
|
<ControlWithError
|
|
37
38
|
required={ required }
|
|
38
39
|
markWhenOptional={ markWhenOptional }
|
|
39
|
-
|
|
40
|
-
return
|
|
40
|
+
onValidate={ () => {
|
|
41
|
+
return onValidate?.( valueRef.current );
|
|
41
42
|
} }
|
|
43
|
+
customValidity={ customValidity }
|
|
42
44
|
getValidityTarget={ () => validityTargetRef.current }
|
|
43
45
|
>
|
|
44
46
|
<TextareaControl
|
|
@@ -19,7 +19,8 @@ type Value = ToggleControlProps[ 'checked' ];
|
|
|
19
19
|
const UnforwardedValidatedToggleControl = (
|
|
20
20
|
{
|
|
21
21
|
required,
|
|
22
|
-
|
|
22
|
+
onValidate,
|
|
23
|
+
customValidity,
|
|
23
24
|
onChange,
|
|
24
25
|
markWhenOptional,
|
|
25
26
|
...restProps
|
|
@@ -46,9 +47,10 @@ const UnforwardedValidatedToggleControl = (
|
|
|
46
47
|
<ControlWithError
|
|
47
48
|
required={ required }
|
|
48
49
|
markWhenOptional={ markWhenOptional }
|
|
49
|
-
|
|
50
|
-
return
|
|
50
|
+
onValidate={ () => {
|
|
51
|
+
return onValidate?.( valueRef.current );
|
|
51
52
|
} }
|
|
53
|
+
customValidity={ customValidity }
|
|
52
54
|
getValidityTarget={ () => validityTargetRef.current }
|
|
53
55
|
>
|
|
54
56
|
<ToggleControl
|
|
@@ -16,7 +16,8 @@ type Value = ToggleGroupControlProps[ 'value' ];
|
|
|
16
16
|
const UnforwardedValidatedToggleGroupControl = (
|
|
17
17
|
{
|
|
18
18
|
required,
|
|
19
|
-
|
|
19
|
+
onValidate,
|
|
20
|
+
customValidity,
|
|
20
21
|
onChange,
|
|
21
22
|
markWhenOptional,
|
|
22
23
|
...restProps
|
|
@@ -37,9 +38,10 @@ const UnforwardedValidatedToggleGroupControl = (
|
|
|
37
38
|
<ControlWithError
|
|
38
39
|
required={ required }
|
|
39
40
|
markWhenOptional={ markWhenOptional }
|
|
40
|
-
|
|
41
|
-
return
|
|
41
|
+
onValidate={ () => {
|
|
42
|
+
return onValidate?.( valueRef.current );
|
|
42
43
|
} }
|
|
44
|
+
customValidity={ customValidity }
|
|
43
45
|
getValidityTarget={ () => validityTargetRef.current }
|
|
44
46
|
>
|
|
45
47
|
<ToggleGroupControl
|
|
@@ -10,19 +10,28 @@ export type ValidatedControlProps< V > = {
|
|
|
10
10
|
*/
|
|
11
11
|
markWhenOptional?: boolean;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* This means the custom validator will be run _in addition_ to any other HTML attribute-based validation, and
|
|
16
|
-
* will be prioritized over any existing validity messages dictated by the HTML attributes.
|
|
17
|
-
* An empty string or `undefined` return value will clear any existing custom validity message.
|
|
13
|
+
* Optional callback to run when the input should be validated. Use this to set
|
|
14
|
+
* a `customValidity` as necessary.
|
|
18
15
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* Always prefer using standard HTML attributes like `required` and `min`/`max` over
|
|
17
|
+
* custom validators when possible, as they are simpler and have localized error messages built in.
|
|
18
|
+
*/
|
|
19
|
+
onValidate?: ( currentValue: V ) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Show a custom message based on the validation status.
|
|
21
22
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
23
|
+
* - When `type` is `invalid`, the message will be applied to the underlying element using the
|
|
24
|
+
* native [`setCustomValidity()` method](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity).
|
|
25
|
+
* This means the custom message will be prioritized over any existing validity messages
|
|
26
|
+
* triggered by HTML attribute-based validation.
|
|
27
|
+
* - When `type` is `validating` or `valid`, the custom validity message of the underlying element
|
|
28
|
+
* will be cleared. If there are no remaining validity messages triggered by HTML attribute-based validation,
|
|
29
|
+
* the message will be presented as a status indicator rather than an error. These indicators are intended
|
|
30
|
+
* for asynchronous validation calls that may take more than 1 second to complete.
|
|
31
|
+
* Otherwise, custom errors can simply be cleared by setting the `customValidity` prop to `undefined`.
|
|
24
32
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
customValidity?: {
|
|
34
|
+
type: 'validating' | 'valid' | 'invalid';
|
|
35
|
+
message: string;
|
|
36
|
+
};
|
|
28
37
|
};
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
|
-
import { error } from '@wordpress/icons';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* External dependencies
|
|
@@ -18,8 +17,8 @@ import {
|
|
|
18
17
|
* Internal dependencies
|
|
19
18
|
*/
|
|
20
19
|
import { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';
|
|
21
|
-
|
|
22
|
-
import
|
|
20
|
+
import type { ValidatedControlProps } from './components/types';
|
|
21
|
+
import { ValidityIndicator } from './validity-indicator';
|
|
23
22
|
|
|
24
23
|
function appendRequiredIndicator(
|
|
25
24
|
label: React.ReactNode,
|
|
@@ -61,7 +60,8 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
61
60
|
{
|
|
62
61
|
required,
|
|
63
62
|
markWhenOptional,
|
|
64
|
-
|
|
63
|
+
onValidate,
|
|
64
|
+
customValidity,
|
|
65
65
|
getValidityTarget,
|
|
66
66
|
children,
|
|
67
67
|
}: {
|
|
@@ -74,12 +74,10 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
74
74
|
*/
|
|
75
75
|
markWhenOptional?: boolean;
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* This message will be applied to the element returned by `getValidityTarget`.
|
|
80
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity
|
|
77
|
+
* The callback to run when the input should be validated.
|
|
81
78
|
*/
|
|
82
|
-
|
|
79
|
+
onValidate?: () => void;
|
|
80
|
+
customValidity?: ValidatedControlProps< unknown >[ 'customValidity' ];
|
|
83
81
|
/**
|
|
84
82
|
* A function that returns the actual element on which the validity data should be applied.
|
|
85
83
|
*/
|
|
@@ -92,6 +90,13 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
92
90
|
forwardedRef: React.ForwardedRef< HTMLDivElement >
|
|
93
91
|
) {
|
|
94
92
|
const [ errorMessage, setErrorMessage ] = useState< string | undefined >();
|
|
93
|
+
const [ statusMessage, setStatusMessage ] = useState<
|
|
94
|
+
| {
|
|
95
|
+
type: 'validating' | 'valid';
|
|
96
|
+
message?: string;
|
|
97
|
+
}
|
|
98
|
+
| undefined
|
|
99
|
+
>();
|
|
95
100
|
const [ isTouched, setIsTouched ] = useState( false );
|
|
96
101
|
|
|
97
102
|
// Ensure that error messages are visible after user attemps to submit a form
|
|
@@ -111,13 +116,58 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
111
116
|
};
|
|
112
117
|
} );
|
|
113
118
|
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
useEffect( () => {
|
|
120
|
+
if ( ! isTouched ) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
116
124
|
const validityTarget = getValidityTarget();
|
|
117
125
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
if ( ! customValidity?.type ) {
|
|
127
|
+
validityTarget?.setCustomValidity( '' );
|
|
128
|
+
setErrorMessage( validityTarget?.validationMessage );
|
|
129
|
+
setStatusMessage( undefined );
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
switch ( customValidity.type ) {
|
|
134
|
+
case 'validating': {
|
|
135
|
+
// Wait before showing a validating state.
|
|
136
|
+
const timer = setTimeout( () => {
|
|
137
|
+
setStatusMessage( {
|
|
138
|
+
type: 'validating',
|
|
139
|
+
message: customValidity.message,
|
|
140
|
+
} );
|
|
141
|
+
}, 1000 );
|
|
142
|
+
|
|
143
|
+
return () => clearTimeout( timer );
|
|
144
|
+
}
|
|
145
|
+
case 'valid': {
|
|
146
|
+
validityTarget?.setCustomValidity( '' );
|
|
147
|
+
setErrorMessage( validityTarget?.validationMessage );
|
|
148
|
+
|
|
149
|
+
setStatusMessage( {
|
|
150
|
+
type: 'valid',
|
|
151
|
+
message: customValidity.message,
|
|
152
|
+
} );
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
case 'invalid': {
|
|
156
|
+
validityTarget?.setCustomValidity(
|
|
157
|
+
customValidity.message ?? ''
|
|
158
|
+
);
|
|
159
|
+
setErrorMessage( validityTarget?.validationMessage );
|
|
160
|
+
|
|
161
|
+
setStatusMessage( undefined );
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}, [
|
|
166
|
+
isTouched,
|
|
167
|
+
customValidity?.type,
|
|
168
|
+
customValidity?.message,
|
|
169
|
+
getValidityTarget,
|
|
170
|
+
] );
|
|
121
171
|
|
|
122
172
|
const onBlur = ( event: React.FocusEvent< HTMLDivElement > ) => {
|
|
123
173
|
// Only consider "blurred from the component" if focus has fully left the wrapping div.
|
|
@@ -138,7 +188,7 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
138
188
|
return;
|
|
139
189
|
}
|
|
140
190
|
|
|
141
|
-
|
|
191
|
+
onValidate?.();
|
|
142
192
|
}
|
|
143
193
|
};
|
|
144
194
|
|
|
@@ -148,7 +198,7 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
148
198
|
// Only validate incrementally if the field has blurred at least once,
|
|
149
199
|
// or currently has an error message.
|
|
150
200
|
if ( isTouched || errorMessage ) {
|
|
151
|
-
|
|
201
|
+
onValidate?.();
|
|
152
202
|
}
|
|
153
203
|
};
|
|
154
204
|
|
|
@@ -156,7 +206,7 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
156
206
|
// Ensures that custom validators are triggered when the user submits by pressing Enter,
|
|
157
207
|
// without ever blurring the control.
|
|
158
208
|
if ( event.key === 'Enter' ) {
|
|
159
|
-
|
|
209
|
+
onValidate?.();
|
|
160
210
|
}
|
|
161
211
|
};
|
|
162
212
|
|
|
@@ -180,15 +230,16 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
180
230
|
} ) }
|
|
181
231
|
<div aria-live="polite">
|
|
182
232
|
{ errorMessage && (
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
{
|
|
191
|
-
|
|
233
|
+
<ValidityIndicator
|
|
234
|
+
type="invalid"
|
|
235
|
+
message={ errorMessage }
|
|
236
|
+
/>
|
|
237
|
+
) }
|
|
238
|
+
{ ! errorMessage && statusMessage && (
|
|
239
|
+
<ValidityIndicator
|
|
240
|
+
type={ statusMessage.type }
|
|
241
|
+
message={ statusMessage.message }
|
|
242
|
+
/>
|
|
192
243
|
) }
|
|
193
244
|
</div>
|
|
194
245
|
</div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
pointer-events: none;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
.components-validated-
|
|
48
|
+
.components-validated-control__indicator {
|
|
49
49
|
display: flex;
|
|
50
50
|
align-items: flex-start;
|
|
51
51
|
gap: 4px;
|
|
@@ -53,17 +53,31 @@
|
|
|
53
53
|
font-family: $font-family-body;
|
|
54
54
|
font-size: 0.75rem;
|
|
55
55
|
line-height: 16px; // matches the icon size
|
|
56
|
-
color: $
|
|
56
|
+
color: $components-color-gray-700;
|
|
57
57
|
animation:
|
|
58
|
-
components-validated-
|
|
58
|
+
components-validated-control__indicator-jump 0.2s
|
|
59
59
|
cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
|
60
|
+
|
|
61
|
+
&.is-invalid {
|
|
62
|
+
color: $alert-red;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&.is-valid {
|
|
66
|
+
color: color-mix(in srgb, $black 30%, $alert-green);
|
|
67
|
+
}
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
.components-validated-
|
|
70
|
+
.components-validated-control__indicator-icon {
|
|
63
71
|
flex-shrink: 0;
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
.components-validated-control__indicator-spinner {
|
|
75
|
+
margin: 2px;
|
|
76
|
+
width: $grid-unit-15;
|
|
77
|
+
height: $grid-unit-15;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@keyframes components-validated-control__indicator-jump {
|
|
67
81
|
0% {
|
|
68
82
|
transform: translateY(-4px);
|
|
69
83
|
opacity: 0;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { error, published } from '@wordpress/icons';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import Icon from '../icon';
|
|
15
|
+
import Spinner from '../spinner';
|
|
16
|
+
|
|
17
|
+
export function ValidityIndicator( {
|
|
18
|
+
type,
|
|
19
|
+
message,
|
|
20
|
+
}: {
|
|
21
|
+
type: 'validating' | 'valid' | 'invalid';
|
|
22
|
+
message?: string;
|
|
23
|
+
} ) {
|
|
24
|
+
const ICON = {
|
|
25
|
+
valid: published,
|
|
26
|
+
invalid: error,
|
|
27
|
+
};
|
|
28
|
+
return (
|
|
29
|
+
<p
|
|
30
|
+
className={ clsx(
|
|
31
|
+
'components-validated-control__indicator',
|
|
32
|
+
`is-${ type }`
|
|
33
|
+
) }
|
|
34
|
+
>
|
|
35
|
+
{ type === 'validating' ? (
|
|
36
|
+
<Spinner className="components-validated-control__indicator-spinner" />
|
|
37
|
+
) : (
|
|
38
|
+
<Icon
|
|
39
|
+
className="components-validated-control__indicator-icon"
|
|
40
|
+
icon={ ICON[ type ] }
|
|
41
|
+
size={ 16 }
|
|
42
|
+
fill="currentColor"
|
|
43
|
+
/>
|
|
44
|
+
) }
|
|
45
|
+
{ message }
|
|
46
|
+
</p>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -10,11 +10,12 @@ import { View } from '..';
|
|
|
10
10
|
|
|
11
11
|
const meta: Meta< typeof View > = {
|
|
12
12
|
component: View,
|
|
13
|
-
title: 'Components
|
|
13
|
+
title: 'Components/View',
|
|
14
14
|
argTypes: {
|
|
15
15
|
as: { control: false },
|
|
16
16
|
children: { control: { type: 'text' } },
|
|
17
17
|
},
|
|
18
|
+
tags: [ 'status-experimental' ],
|
|
18
19
|
parameters: {
|
|
19
20
|
controls: { expanded: true },
|
|
20
21
|
docs: { canvas: { sourceState: 'shown' } },
|