@wordpress/components 30.9.0 → 31.0.1-next.6deb34194.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.
Files changed (214) hide show
  1. package/CHANGELOG.md +25 -4
  2. package/build/alignment-matrix-control/cell.js +131 -3
  3. package/build/alignment-matrix-control/cell.js.map +4 -4
  4. package/build/alignment-matrix-control/index.js +134 -6
  5. package/build/alignment-matrix-control/index.js.map +3 -3
  6. package/build/angle-picker-control/angle-circle.js +119 -15
  7. package/build/angle-picker-control/angle-circle.js.map +4 -4
  8. package/build/angle-picker-control/index.js +12 -7
  9. package/build/angle-picker-control/index.js.map +3 -3
  10. package/build/dropdown-menu/index.js +1 -1
  11. package/build/dropdown-menu/index.js.map +2 -2
  12. package/build/menu/styles.js +17 -17
  13. package/build/menu/styles.js.map +2 -2
  14. package/build/menu-item/index.js +1 -1
  15. package/build/menu-item/index.js.map +2 -2
  16. package/build/notice/index.js +1 -1
  17. package/build/notice/index.js.map +2 -2
  18. package/build/snackbar/index.js +1 -1
  19. package/build/snackbar/index.js.map +1 -1
  20. package/build/validated-form-controls/components/checkbox-control.js +0 -10
  21. package/build/validated-form-controls/components/checkbox-control.js.map +2 -2
  22. package/build/validated-form-controls/components/combobox-control.js +1 -11
  23. package/build/validated-form-controls/components/combobox-control.js.map +2 -2
  24. package/build/validated-form-controls/components/custom-select-control.js +0 -10
  25. package/build/validated-form-controls/components/custom-select-control.js.map +2 -2
  26. package/build/validated-form-controls/components/form-token-field.js +2 -12
  27. package/build/validated-form-controls/components/form-token-field.js.map +2 -2
  28. package/build/validated-form-controls/components/input-control.js +0 -10
  29. package/build/validated-form-controls/components/input-control.js.map +2 -2
  30. package/build/validated-form-controls/components/number-control.js +0 -10
  31. package/build/validated-form-controls/components/number-control.js.map +2 -2
  32. package/build/validated-form-controls/components/radio-control.js +0 -10
  33. package/build/validated-form-controls/components/radio-control.js.map +2 -2
  34. package/build/validated-form-controls/components/range-control.js +0 -10
  35. package/build/validated-form-controls/components/range-control.js.map +2 -2
  36. package/build/validated-form-controls/components/select-control.js +0 -10
  37. package/build/validated-form-controls/components/select-control.js.map +2 -2
  38. package/build/validated-form-controls/components/text-control.js +0 -10
  39. package/build/validated-form-controls/components/text-control.js.map +2 -2
  40. package/build/validated-form-controls/components/textarea-control.js +0 -10
  41. package/build/validated-form-controls/components/textarea-control.js.map +2 -2
  42. package/build/validated-form-controls/components/toggle-control.js +0 -10
  43. package/build/validated-form-controls/components/toggle-control.js.map +2 -2
  44. package/build/validated-form-controls/components/toggle-group-control.js +0 -10
  45. package/build/validated-form-controls/components/toggle-group-control.js.map +2 -2
  46. package/build/validated-form-controls/control-with-error.js +53 -58
  47. package/build/validated-form-controls/control-with-error.js.map +2 -2
  48. package/build-module/alignment-matrix-control/cell.js +131 -3
  49. package/build-module/alignment-matrix-control/cell.js.map +3 -3
  50. package/build-module/alignment-matrix-control/index.js +134 -6
  51. package/build-module/alignment-matrix-control/index.js.map +3 -3
  52. package/build-module/angle-picker-control/angle-circle.js +109 -15
  53. package/build-module/angle-picker-control/angle-circle.js.map +3 -3
  54. package/build-module/angle-picker-control/index.js +12 -7
  55. package/build-module/angle-picker-control/index.js.map +2 -2
  56. package/build-module/dropdown-menu/index.js +1 -1
  57. package/build-module/dropdown-menu/index.js.map +2 -2
  58. package/build-module/menu/styles.js +17 -17
  59. package/build-module/menu/styles.js.map +2 -2
  60. package/build-module/menu-item/index.js +1 -1
  61. package/build-module/menu-item/index.js.map +2 -2
  62. package/build-module/notice/index.js +1 -1
  63. package/build-module/notice/index.js.map +2 -2
  64. package/build-module/snackbar/index.js +1 -1
  65. package/build-module/snackbar/index.js.map +1 -1
  66. package/build-module/validated-form-controls/components/checkbox-control.js +0 -10
  67. package/build-module/validated-form-controls/components/checkbox-control.js.map +2 -2
  68. package/build-module/validated-form-controls/components/combobox-control.js +1 -11
  69. package/build-module/validated-form-controls/components/combobox-control.js.map +2 -2
  70. package/build-module/validated-form-controls/components/custom-select-control.js +0 -10
  71. package/build-module/validated-form-controls/components/custom-select-control.js.map +2 -2
  72. package/build-module/validated-form-controls/components/form-token-field.js +2 -12
  73. package/build-module/validated-form-controls/components/form-token-field.js.map +2 -2
  74. package/build-module/validated-form-controls/components/input-control.js +0 -10
  75. package/build-module/validated-form-controls/components/input-control.js.map +2 -2
  76. package/build-module/validated-form-controls/components/number-control.js +0 -10
  77. package/build-module/validated-form-controls/components/number-control.js.map +2 -2
  78. package/build-module/validated-form-controls/components/radio-control.js +0 -10
  79. package/build-module/validated-form-controls/components/radio-control.js.map +2 -2
  80. package/build-module/validated-form-controls/components/range-control.js +0 -10
  81. package/build-module/validated-form-controls/components/range-control.js.map +2 -2
  82. package/build-module/validated-form-controls/components/select-control.js +0 -10
  83. package/build-module/validated-form-controls/components/select-control.js.map +2 -2
  84. package/build-module/validated-form-controls/components/text-control.js +0 -10
  85. package/build-module/validated-form-controls/components/text-control.js.map +2 -2
  86. package/build-module/validated-form-controls/components/textarea-control.js +0 -10
  87. package/build-module/validated-form-controls/components/textarea-control.js.map +2 -2
  88. package/build-module/validated-form-controls/components/toggle-control.js +0 -10
  89. package/build-module/validated-form-controls/components/toggle-control.js.map +2 -2
  90. package/build-module/validated-form-controls/components/toggle-group-control.js +0 -10
  91. package/build-module/validated-form-controls/components/toggle-group-control.js.map +2 -2
  92. package/build-module/validated-form-controls/control-with-error.js +53 -58
  93. package/build-module/validated-form-controls/control-with-error.js.map +2 -2
  94. package/build-style/style-rtl.css +21 -33
  95. package/build-style/style.css +21 -33
  96. package/build-types/alignment-matrix-control/cell.d.ts.map +1 -1
  97. package/build-types/alignment-matrix-control/index.d.ts.map +1 -1
  98. package/build-types/angle-picker-control/angle-circle.d.ts +1 -1
  99. package/build-types/angle-picker-control/angle-circle.d.ts.map +1 -1
  100. package/build-types/angle-picker-control/index.d.ts.map +1 -1
  101. package/build-types/notice/index.d.ts.map +1 -1
  102. package/build-types/notice/stories/index.story.d.ts.map +1 -1
  103. package/build-types/validated-form-controls/components/checkbox-control.d.ts +1 -1
  104. package/build-types/validated-form-controls/components/checkbox-control.d.ts.map +1 -1
  105. package/build-types/validated-form-controls/components/combobox-control.d.ts +2 -3
  106. package/build-types/validated-form-controls/components/combobox-control.d.ts.map +1 -1
  107. package/build-types/validated-form-controls/components/custom-select-control.d.ts +1 -2
  108. package/build-types/validated-form-controls/components/custom-select-control.d.ts.map +1 -1
  109. package/build-types/validated-form-controls/components/form-token-field.d.ts +1 -2
  110. package/build-types/validated-form-controls/components/form-token-field.d.ts.map +1 -1
  111. package/build-types/validated-form-controls/components/input-control.d.ts +1 -2
  112. package/build-types/validated-form-controls/components/input-control.d.ts.map +1 -1
  113. package/build-types/validated-form-controls/components/number-control.d.ts +1 -1
  114. package/build-types/validated-form-controls/components/number-control.d.ts.map +1 -1
  115. package/build-types/validated-form-controls/components/radio-control.d.ts +1 -1
  116. package/build-types/validated-form-controls/components/radio-control.d.ts.map +1 -1
  117. package/build-types/validated-form-controls/components/range-control.d.ts +1 -1
  118. package/build-types/validated-form-controls/components/range-control.d.ts.map +1 -1
  119. package/build-types/validated-form-controls/components/select-control.d.ts +2 -3
  120. package/build-types/validated-form-controls/components/select-control.d.ts.map +1 -1
  121. package/build-types/validated-form-controls/components/stories/checkbox-control.story.d.ts.map +1 -1
  122. package/build-types/validated-form-controls/components/stories/combobox-control.story.d.ts.map +1 -1
  123. package/build-types/validated-form-controls/components/stories/custom-select-control.story.d.ts.map +1 -1
  124. package/build-types/validated-form-controls/components/stories/form-token-field.story.d.ts.map +1 -1
  125. package/build-types/validated-form-controls/components/stories/input-control.story.d.ts.map +1 -1
  126. package/build-types/validated-form-controls/components/stories/number-control.story.d.ts.map +1 -1
  127. package/build-types/validated-form-controls/components/stories/overview.story.d.ts +7 -0
  128. package/build-types/validated-form-controls/components/stories/overview.story.d.ts.map +1 -1
  129. package/build-types/validated-form-controls/components/stories/radio-control.story.d.ts.map +1 -1
  130. package/build-types/validated-form-controls/components/stories/range-control.story.d.ts.map +1 -1
  131. package/build-types/validated-form-controls/components/stories/select-control.story.d.ts.map +1 -1
  132. package/build-types/validated-form-controls/components/stories/text-control.story.d.ts.map +1 -1
  133. package/build-types/validated-form-controls/components/stories/textarea-control.story.d.ts.map +1 -1
  134. package/build-types/validated-form-controls/components/stories/toggle-control.story.d.ts.map +1 -1
  135. package/build-types/validated-form-controls/components/stories/toggle-group-control.story.d.ts.map +1 -1
  136. package/build-types/validated-form-controls/components/text-control.d.ts +1 -1
  137. package/build-types/validated-form-controls/components/text-control.d.ts.map +1 -1
  138. package/build-types/validated-form-controls/components/textarea-control.d.ts +1 -1
  139. package/build-types/validated-form-controls/components/textarea-control.d.ts.map +1 -1
  140. package/build-types/validated-form-controls/components/toggle-control.d.ts +1 -1
  141. package/build-types/validated-form-controls/components/toggle-control.d.ts.map +1 -1
  142. package/build-types/validated-form-controls/components/toggle-group-control.d.ts +1 -1
  143. package/build-types/validated-form-controls/components/toggle-group-control.d.ts.map +1 -1
  144. package/build-types/validated-form-controls/components/types.d.ts +1 -9
  145. package/build-types/validated-form-controls/components/types.d.ts.map +1 -1
  146. package/build-types/validated-form-controls/control-with-error.d.ts +4 -5
  147. package/build-types/validated-form-controls/control-with-error.d.ts.map +1 -1
  148. package/package.json +20 -20
  149. package/src/alignment-matrix-control/cell.tsx +14 -3
  150. package/src/alignment-matrix-control/index.tsx +15 -6
  151. package/src/alignment-matrix-control/style.module.scss +84 -0
  152. package/src/angle-picker-control/angle-circle.tsx +27 -12
  153. package/src/angle-picker-control/index.tsx +8 -7
  154. package/src/angle-picker-control/style.module.scss +40 -0
  155. package/src/button/style.scss +1 -1
  156. package/src/dropdown-menu/index.tsx +1 -1
  157. package/src/dropdown-menu/style.scss +1 -1
  158. package/src/guide/style.scss +3 -3
  159. package/src/menu/styles.ts +2 -2
  160. package/src/menu-item/index.tsx +1 -1
  161. package/src/menu-item/test/__snapshots__/index.js.snap +4 -4
  162. package/src/modal/style.scss +5 -5
  163. package/src/notice/index.tsx +53 -46
  164. package/src/notice/stories/index.story.tsx +17 -1
  165. package/src/notice/style.scss +3 -20
  166. package/src/snackbar/index.tsx +1 -1
  167. package/src/validated-form-controls/components/checkbox-control.tsx +1 -14
  168. package/src/validated-form-controls/components/combobox-control.tsx +1 -14
  169. package/src/validated-form-controls/components/custom-select-control.tsx +1 -19
  170. package/src/validated-form-controls/components/form-token-field.tsx +2 -15
  171. package/src/validated-form-controls/components/input-control.tsx +1 -14
  172. package/src/validated-form-controls/components/number-control.tsx +1 -16
  173. package/src/validated-form-controls/components/radio-control.tsx +2 -18
  174. package/src/validated-form-controls/components/range-control.tsx +1 -14
  175. package/src/validated-form-controls/components/select-control.tsx +1 -23
  176. package/src/validated-form-controls/components/stories/checkbox-control.story.tsx +11 -20
  177. package/src/validated-form-controls/components/stories/combobox-control.story.tsx +8 -17
  178. package/src/validated-form-controls/components/stories/custom-select-control.story.tsx +8 -17
  179. package/src/validated-form-controls/components/stories/form-token-field.story.tsx +14 -26
  180. package/src/validated-form-controls/components/stories/input-control.story.tsx +25 -50
  181. package/src/validated-form-controls/components/stories/number-control.story.tsx +10 -19
  182. package/src/validated-form-controls/components/stories/overview.mdx +3 -3
  183. package/src/validated-form-controls/components/stories/overview.story.tsx +94 -79
  184. package/src/validated-form-controls/components/stories/radio-control.story.tsx +11 -20
  185. package/src/validated-form-controls/components/stories/range-control.story.tsx +8 -17
  186. package/src/validated-form-controls/components/stories/select-control.story.tsx +9 -18
  187. package/src/validated-form-controls/components/stories/text-control.story.tsx +11 -17
  188. package/src/validated-form-controls/components/stories/textarea-control.story.tsx +12 -16
  189. package/src/validated-form-controls/components/stories/toggle-control.story.tsx +11 -20
  190. package/src/validated-form-controls/components/stories/toggle-group-control.story.tsx +8 -17
  191. package/src/validated-form-controls/components/text-control.tsx +1 -14
  192. package/src/validated-form-controls/components/textarea-control.tsx +1 -14
  193. package/src/validated-form-controls/components/toggle-control.tsx +1 -14
  194. package/src/validated-form-controls/components/toggle-group-control.tsx +1 -14
  195. package/src/validated-form-controls/components/types.ts +1 -9
  196. package/src/validated-form-controls/control-with-error.tsx +57 -84
  197. package/src/validated-form-controls/style.scss +7 -7
  198. package/src/validated-form-controls/test/control-with-error.tsx +66 -5
  199. package/tsconfig.json +1 -0
  200. package/tsconfig.tsbuildinfo +1 -1
  201. package/build/alignment-matrix-control/styles.js +0 -105
  202. package/build/alignment-matrix-control/styles.js.map +0 -7
  203. package/build/angle-picker-control/styles/angle-picker-control-styles.js +0 -88
  204. package/build/angle-picker-control/styles/angle-picker-control-styles.js.map +0 -7
  205. package/build-module/alignment-matrix-control/styles.js +0 -67
  206. package/build-module/alignment-matrix-control/styles.js.map +0 -7
  207. package/build-module/angle-picker-control/styles/angle-picker-control-styles.js +0 -50
  208. package/build-module/angle-picker-control/styles/angle-picker-control-styles.js.map +0 -7
  209. package/build-types/alignment-matrix-control/styles.d.ts +0 -21
  210. package/build-types/alignment-matrix-control/styles.d.ts.map +0 -1
  211. package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts +0 -18
  212. package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts.map +0 -1
  213. package/src/alignment-matrix-control/styles.ts +0 -113
  214. package/src/angle-picker-control/styles/angle-picker-control-styles.tsx +0 -58
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { usePrevious } from '@wordpress/compose';
5
4
  import { __ } from '@wordpress/i18n';
6
5
  import {
7
6
  cloneElement,
@@ -13,7 +12,6 @@ import {
13
12
  /**
14
13
  * Internal dependencies
15
14
  */
16
- import { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';
17
15
  import type { ValidatedControlProps } from './components/types';
18
16
  import { ValidityIndicator } from './validity-indicator';
19
17
 
@@ -38,6 +36,7 @@ function appendRequiredIndicator(
38
36
  }
39
37
  return label;
40
38
  }
39
+ const VALIDITY_VISIBLE_ATTRIBUTE = 'data-validity-visible';
41
40
 
42
41
  /**
43
42
  * HTML elements that support the Constraint Validation API.
@@ -57,7 +56,6 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
57
56
  {
58
57
  required,
59
58
  markWhenOptional,
60
- onValidate,
61
59
  customValidity,
62
60
  getValidityTarget,
63
61
  children,
@@ -70,11 +68,7 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
70
68
  * Label the control as "optional" when _not_ `required`, instead of the inverse.
71
69
  */
72
70
  markWhenOptional?: boolean;
73
- /**
74
- * The callback to run when the input should be validated.
75
- */
76
- onValidate?: () => void;
77
- customValidity?: ValidatedControlProps< unknown >[ 'customValidity' ];
71
+ customValidity?: ValidatedControlProps[ 'customValidity' ];
78
72
  /**
79
73
  * A function that returns the actual element on which the validity data should be applied.
80
74
  */
@@ -99,31 +93,24 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
99
93
  }
100
94
  | undefined
101
95
  >();
96
+ const [ showMessage, setShowMessage ] = useState( false );
102
97
  const [ isTouched, setIsTouched ] = useState( false );
103
- const previousCustomValidityType = usePrevious( customValidity?.type );
104
98
 
105
- // Ensure that error messages are visible after user attemps to submit a form
106
- // with multiple invalid fields.
99
+ // Ensure that error messages are visible when an `invalid` event is triggered,
100
+ // e.g. when a form is submitted or reportValidity() is called.
107
101
  useEffect( () => {
108
102
  const validityTarget = getValidityTarget();
109
- const showValidationMessage = () =>
110
- setErrorMessage( validityTarget?.validationMessage );
111
-
112
- validityTarget?.addEventListener( 'invalid', showValidationMessage );
113
-
114
- return () => {
115
- validityTarget?.removeEventListener(
116
- 'invalid',
117
- showValidationMessage
118
- );
103
+ const handler = () => {
104
+ setShowMessage( true );
105
+ validityTarget?.setAttribute( VALIDITY_VISIBLE_ATTRIBUTE, '' );
119
106
  };
120
- } );
121
107
 
122
- useEffect( (): ReturnType< React.EffectCallback > => {
123
- if ( ! isTouched ) {
124
- return;
125
- }
108
+ validityTarget?.addEventListener( 'invalid', handler );
109
+ return () => validityTarget?.removeEventListener( 'invalid', handler );
110
+ }, [ getValidityTarget ] );
126
111
 
112
+ // Handle validity messages.
113
+ useEffect( () => {
127
114
  const validityTarget = getValidityTarget();
128
115
 
129
116
  if ( ! customValidity?.type ) {
@@ -135,26 +122,16 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
135
122
 
136
123
  switch ( customValidity.type ) {
137
124
  case 'validating': {
138
- // Wait before showing a validating state.
139
- const timer = setTimeout( () => {
140
- validityTarget?.setCustomValidity( '' );
141
- setErrorMessage( undefined );
142
-
143
- setStatusMessage( {
144
- type: 'validating',
145
- message: customValidity.message,
146
- } );
147
- }, 1000 );
148
-
149
- return () => clearTimeout( timer );
125
+ validityTarget?.setCustomValidity( '' );
126
+ setErrorMessage( undefined );
127
+
128
+ setStatusMessage( {
129
+ type: 'validating',
130
+ message: customValidity.message,
131
+ } );
132
+ break;
150
133
  }
151
134
  case 'valid': {
152
- // Ensures that we wait for any async responses before showing
153
- // a synchronously valid state.
154
- if ( previousCustomValidityType === 'valid' ) {
155
- break;
156
- }
157
-
158
135
  validityTarget?.setCustomValidity( '' );
159
136
  setErrorMessage( validityTarget?.validationMessage );
160
137
 
@@ -174,14 +151,28 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
174
151
  break;
175
152
  }
176
153
  }
177
- }, [
178
- isTouched,
179
- customValidity?.type,
180
- customValidity?.message,
181
- getValidityTarget,
182
- previousCustomValidityType,
183
- ] );
154
+ }, [ customValidity, getValidityTarget ] );
155
+
156
+ // Show messages if field has been touched (i.e. has blurred at least once),
157
+ // or validation has been triggered by the consumer/user.
158
+ useEffect( (): ReturnType< React.EffectCallback > => {
159
+ if ( ! isTouched || showMessage ) {
160
+ return;
161
+ }
184
162
 
163
+ if ( customValidity?.type === 'validating' ) {
164
+ // Don't show validating indicators for quick calls that take less than 1 sec.
165
+ const timer = setTimeout( () => {
166
+ setShowMessage( true );
167
+ }, 1000 );
168
+
169
+ return () => clearTimeout( timer );
170
+ }
171
+
172
+ setShowMessage( true );
173
+ }, [ isTouched, customValidity?.type, showMessage ] );
174
+
175
+ // Mark blurred fields as touched.
185
176
  const onBlur = ( event: React.FocusEvent< HTMLDivElement > ) => {
186
177
  if ( isTouched ) {
187
178
  return;
@@ -194,36 +185,32 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
194
185
  ! event.currentTarget.contains( event.relatedTarget )
195
186
  ) {
196
187
  setIsTouched( true );
197
- onValidate?.();
188
+ getValidityTarget()?.setAttribute( VALIDITY_VISIBLE_ATTRIBUTE, '' );
198
189
  }
199
190
  };
200
191
 
201
- const onChange = ( ...args: unknown[] ) => {
202
- children.props.onChange?.( ...args );
203
-
204
- // Only validate incrementally if the field has blurred at least once,
205
- // or currently has an error message.
206
- if ( isTouched || errorMessage ) {
207
- onValidate?.();
192
+ const message = () => {
193
+ if ( errorMessage ) {
194
+ return (
195
+ <ValidityIndicator type="invalid" message={ errorMessage } />
196
+ );
208
197
  }
209
- };
210
-
211
- const onKeyDown = ( event: React.KeyboardEvent< HTMLDivElement > ) => {
212
- // Ensures that custom validators are triggered when the user submits by pressing Enter,
213
- // without ever blurring the control.
214
- if ( event.key === 'Enter' ) {
215
- onValidate?.();
198
+ if ( statusMessage?.type ) {
199
+ return (
200
+ <ValidityIndicator
201
+ type={ statusMessage.type }
202
+ message={ statusMessage.message }
203
+ />
204
+ );
216
205
  }
206
+ return null;
217
207
  };
218
208
 
219
209
  return (
220
- // Disable reason: Just listening to a bubbled event, not for interaction.
221
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
222
210
  <div
223
211
  className="components-validated-control"
224
212
  ref={ forwardedRef }
225
213
  onBlur={ onBlur }
226
- onKeyDown={ withIgnoreIMEEvents( onKeyDown ) }
227
214
  >
228
215
  { cloneElement( children, {
229
216
  label: appendRequiredIndicator(
@@ -231,23 +218,9 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
231
218
  required,
232
219
  markWhenOptional
233
220
  ),
234
- onChange,
235
221
  required,
236
222
  } ) }
237
- <div aria-live="polite">
238
- { errorMessage && (
239
- <ValidityIndicator
240
- type="invalid"
241
- message={ errorMessage }
242
- />
243
- ) }
244
- { ! errorMessage && statusMessage && (
245
- <ValidityIndicator
246
- type={ statusMessage.type }
247
- message={ statusMessage.message }
248
- />
249
- ) }
250
- </div>
223
+ <div aria-live="polite">{ showMessage && message() }</div>
251
224
  </div>
252
225
  );
253
226
  }
@@ -4,14 +4,14 @@
4
4
 
5
5
  .components-validated-control {
6
6
  // For components based on InputBase
7
- &:has(:is(input, select):user-invalid)
7
+ &:has(:is(input, select):invalid[data-validity-visible])
8
8
  .components-input-control__backdrop {
9
9
  --wp-components-color-accent: #{$alert-red};
10
10
  border-color: $alert-red;
11
11
  }
12
12
 
13
13
  // For TextControl, TextareaControl
14
- :is(textarea, input[type="text"]):user-invalid {
14
+ :is(textarea, input[type="text"]):invalid[data-validity-visible] {
15
15
  --wp-admin-theme-color: #{$alert-red};
16
16
  --wp-components-color-accent: #{$alert-red};
17
17
  border-color: $alert-red;
@@ -19,9 +19,9 @@
19
19
 
20
20
  // For ComboboxControl
21
21
  .components-combobox-control__suggestions-container:has(
22
- input:user-invalid
22
+ input:invalid[data-validity-visible]
23
23
  ):not(:has([aria-expanded="true"])) {
24
- border-color: $alert-red;
24
+ --wp-components-color-accent: #{$alert-red};
25
25
  }
26
26
  }
27
27
 
@@ -29,18 +29,18 @@
29
29
  position: relative;
30
30
 
31
31
  // For CustomSelectControl
32
- &:has(select:user-invalid) .components-input-control__backdrop {
32
+ &:has(select:invalid[data-validity-visible]) .components-input-control__backdrop {
33
33
  --wp-components-color-accent: #{$alert-red};
34
34
  border-color: $alert-red;
35
35
  }
36
36
 
37
37
  // For ToggleGroupControl
38
- &:has(input[type="radio"]:invalid) {
38
+ &:has(input[type="radio"]:invalid[data-validity-visible]) {
39
39
  --wp-components-color-accent: #{$alert-red};
40
40
  }
41
41
 
42
42
  // For FormTokenField
43
- &:has(input:user-invalid) .components-form-token-field__input-container:not(:has([aria-expanded="true"])) {
43
+ &:has(input:invalid[data-validity-visible]) .components-form-token-field__input-container:not(:has([aria-expanded="true"])) {
44
44
  --wp-components-color-accent: #{$alert-red};
45
45
  border-color: $alert-red;
46
46
  }
@@ -38,7 +38,7 @@ describe( 'ControlWithError', () => {
38
38
  >[ 'customValidity' ]
39
39
  >( undefined );
40
40
 
41
- const onValidate = useCallback(
41
+ const onChange = useCallback(
42
42
  ( value?: string ) => {
43
43
  setCustomValidity( {
44
44
  type: 'validating',
@@ -59,6 +59,8 @@ describe( 'ControlWithError', () => {
59
59
  } );
60
60
  }
61
61
  }, serverDelayMs );
62
+
63
+ setText( value ?? '' );
62
64
  },
63
65
  [ serverDelayMs ]
64
66
  );
@@ -67,10 +69,7 @@ describe( 'ControlWithError', () => {
67
69
  <ValidatedInputControl
68
70
  label="Text"
69
71
  value={ text }
70
- onChange={ ( newValue ) => {
71
- setText( newValue ?? '' );
72
- } }
73
- onValidate={ onValidate }
72
+ onChange={ onChange }
74
73
  customValidity={ customValidity }
75
74
  { ...restProps }
76
75
  />
@@ -221,4 +220,66 @@ describe( 'ControlWithError', () => {
221
220
  } );
222
221
  } );
223
222
  } );
223
+
224
+ describe( 'Form submission', () => {
225
+ const CustomValidatedInputControl = ( {
226
+ ...restProps
227
+ }: React.ComponentProps< typeof ValidatedInputControl > ) => {
228
+ const [ customValidity, setCustomValidity ] =
229
+ useState<
230
+ React.ComponentProps<
231
+ typeof ValidatedInputControl
232
+ >[ 'customValidity' ]
233
+ >( undefined );
234
+ return (
235
+ <ValidatedInputControl
236
+ onChange={ ( value ) =>
237
+ value === 'error'
238
+ ? setCustomValidity( {
239
+ type: 'invalid',
240
+ message: 'The word "error" is not allowed.',
241
+ } )
242
+ : setCustomValidity( undefined )
243
+ }
244
+ customValidity={ customValidity }
245
+ { ...restProps }
246
+ />
247
+ );
248
+ };
249
+
250
+ it( 'should show custom validity messages regardless of "touched" state if parent form is submitted', async () => {
251
+ const user = userEvent.setup();
252
+ const onSubmit = jest.fn();
253
+ render(
254
+ <form onSubmit={ onSubmit }>
255
+ <CustomValidatedInputControl label="Text" />
256
+ <button type="submit">Submit</button>
257
+ </form>
258
+ );
259
+
260
+ const input = screen.getByRole< HTMLInputElement >( 'textbox', {
261
+ name: 'Text',
262
+ } );
263
+
264
+ // User has interacted, but not blurred
265
+ await user.type( input, 'error' );
266
+ await user.keyboard( '{enter}' );
267
+
268
+ // Input is marked as invalid at the HTML level
269
+ await waitFor( () => {
270
+ expect( input.checkValidity() ).toBe( false );
271
+ } );
272
+ expect( input.validationMessage ).toBe(
273
+ 'The word "error" is not allowed.'
274
+ );
275
+
276
+ // Field is showing the error message
277
+ expect(
278
+ screen.getByText( 'The word "error" is not allowed.' )
279
+ ).toBeVisible();
280
+
281
+ // Form is not submitted
282
+ expect( onSubmit ).not.toHaveBeenCalled();
283
+ } );
284
+ } );
224
285
  } );
package/tsconfig.json CHANGED
@@ -5,6 +5,7 @@
5
5
  "types": [
6
6
  "gutenberg-env",
7
7
  "gutenberg-test-env",
8
+ "css-modules",
8
9
  "jest",
9
10
  "@testing-library/jest-dom"
10
11
  ]