@wordpress/components 30.2.1-next.f34ab90e9.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/build/context/context-connect.js.map +1 -1
  3. package/build/tabs/styles.js +5 -5
  4. package/build/tabs/styles.js.map +1 -1
  5. package/build/utils/font-size.js.map +1 -1
  6. package/build/utils/get-valid-children.js.map +1 -1
  7. package/build/validated-form-controls/control-with-error.js +16 -16
  8. package/build/validated-form-controls/control-with-error.js.map +1 -1
  9. package/build-module/context/context-connect.js.map +1 -1
  10. package/build-module/tabs/styles.js +6 -6
  11. package/build-module/tabs/styles.js.map +1 -1
  12. package/build-module/utils/font-size.js.map +1 -1
  13. package/build-module/utils/get-valid-children.js.map +1 -1
  14. package/build-module/validated-form-controls/control-with-error.js +16 -16
  15. package/build-module/validated-form-controls/control-with-error.js.map +1 -1
  16. package/build-types/context/context-connect.d.ts +2 -2
  17. package/build-types/context/context-connect.d.ts.map +1 -1
  18. package/build-types/tabs/styles.d.ts.map +1 -1
  19. package/build-types/utils/font-size.d.ts +2 -2
  20. package/build-types/utils/font-size.d.ts.map +1 -1
  21. package/build-types/utils/get-valid-children.d.ts +2 -2
  22. package/build-types/utils/get-valid-children.d.ts.map +1 -1
  23. package/build-types/validated-form-controls/components/stories/overview.story.d.ts.map +1 -1
  24. package/build-types/validated-form-controls/control-with-error.d.ts.map +1 -1
  25. package/build-types/validated-form-controls/test/control-with-error.d.ts +2 -0
  26. package/build-types/validated-form-controls/test/control-with-error.d.ts.map +1 -0
  27. package/package.json +19 -19
  28. package/src/context/context-connect.ts +2 -2
  29. package/src/tabs/styles.ts +2 -1
  30. package/src/tools-panel/stories/index.story.tsx +3 -3
  31. package/src/utils/font-size.ts +2 -2
  32. package/src/utils/get-valid-children.ts +4 -2
  33. package/src/validated-form-controls/components/stories/overview.story.tsx +109 -27
  34. package/src/validated-form-controls/control-with-error.tsx +19 -18
  35. package/src/validated-form-controls/test/control-with-error.tsx +224 -0
  36. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,224 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen, waitFor, act } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { useState, useCallback } from '@wordpress/element';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { ValidatedInputControl } from '../components';
16
+
17
+ describe( 'ControlWithError', () => {
18
+ describe( 'Async Validation', () => {
19
+ beforeEach( () => {
20
+ jest.useFakeTimers();
21
+ } );
22
+
23
+ afterEach( () => {
24
+ jest.useRealTimers();
25
+ } );
26
+
27
+ const AsyncValidatedInputControl = ( {
28
+ serverDelayMs,
29
+ ...restProps
30
+ }: {
31
+ serverDelayMs: number;
32
+ } & React.ComponentProps< typeof ValidatedInputControl > ) => {
33
+ const [ text, setText ] = useState( '' );
34
+ const [ customValidity, setCustomValidity ] =
35
+ useState<
36
+ React.ComponentProps<
37
+ typeof ValidatedInputControl
38
+ >[ 'customValidity' ]
39
+ >( undefined );
40
+
41
+ const onValidate = useCallback(
42
+ ( value?: string ) => {
43
+ setCustomValidity( {
44
+ type: 'validating',
45
+ message: 'Validating...',
46
+ } );
47
+
48
+ // Simulate delayed server response
49
+ setTimeout( () => {
50
+ if ( value?.toLowerCase() === 'error' ) {
51
+ setCustomValidity( {
52
+ type: 'invalid',
53
+ message: 'The word "error" is not allowed.',
54
+ } );
55
+ } else {
56
+ setCustomValidity( {
57
+ type: 'valid',
58
+ message: 'Validated',
59
+ } );
60
+ }
61
+ }, serverDelayMs );
62
+ },
63
+ [ serverDelayMs ]
64
+ );
65
+
66
+ return (
67
+ <ValidatedInputControl
68
+ label="Text"
69
+ value={ text }
70
+ onChange={ ( newValue ) => {
71
+ setText( newValue ?? '' );
72
+ } }
73
+ onValidate={ onValidate }
74
+ customValidity={ customValidity }
75
+ { ...restProps }
76
+ />
77
+ );
78
+ };
79
+
80
+ it( 'should not show "validating" state if it takes less than 1000ms', async () => {
81
+ const user = userEvent.setup( {
82
+ advanceTimers: jest.advanceTimersByTime,
83
+ } );
84
+ render( <AsyncValidatedInputControl serverDelayMs={ 500 } /> );
85
+
86
+ const input = screen.getByRole( 'textbox' );
87
+
88
+ await user.type( input, 'valid text' );
89
+
90
+ // Blur to trigger validation
91
+ await user.tab();
92
+
93
+ // Fast-forward to right before the server response
94
+ act( () => jest.advanceTimersByTime( 499 ) );
95
+
96
+ // The validating state should not be shown
97
+ await waitFor( () => {
98
+ expect(
99
+ screen.queryByText( 'Validating...' )
100
+ ).not.toBeInTheDocument();
101
+ } );
102
+
103
+ // Fast-forward past the server delay to show validation result
104
+ act( () => jest.advanceTimersByTime( 1 ) );
105
+
106
+ await waitFor( () => {
107
+ expect( screen.getByText( 'Validated' ) ).toBeVisible();
108
+ } );
109
+ } );
110
+
111
+ it( 'should show "validating" state if it takes more than 1000ms', async () => {
112
+ const user = userEvent.setup( {
113
+ advanceTimers: jest.advanceTimersByTime,
114
+ } );
115
+ render( <AsyncValidatedInputControl serverDelayMs={ 1200 } /> );
116
+
117
+ const input = screen.getByRole( 'textbox' );
118
+
119
+ await user.type( input, 'valid text' );
120
+
121
+ // Blur to trigger validation
122
+ await user.tab();
123
+
124
+ // Initially, no validating message should be shown (before 1s delay)
125
+ expect(
126
+ screen.queryByText( 'Validating...' )
127
+ ).not.toBeInTheDocument();
128
+
129
+ // Fast-forward past the 1s delay to show validating state
130
+ act( () => jest.advanceTimersByTime( 1000 ) );
131
+
132
+ await waitFor( () => {
133
+ expect( screen.getByText( 'Validating...' ) ).toBeVisible();
134
+ } );
135
+
136
+ // Fast-forward past the server delay to show validation result
137
+ act( () => jest.advanceTimersByTime( 200 ) );
138
+
139
+ await waitFor( () => {
140
+ expect( screen.getByText( 'Validated' ) ).toBeVisible();
141
+ } );
142
+
143
+ // Test error case
144
+ await user.clear( input );
145
+ await user.type( input, 'error' );
146
+
147
+ // Blur to trigger validation
148
+ await user.tab();
149
+
150
+ act( () => jest.advanceTimersByTime( 1000 ) );
151
+
152
+ await waitFor( () => {
153
+ expect( screen.getByText( 'Validating...' ) ).toBeVisible();
154
+ } );
155
+
156
+ act( () => jest.advanceTimersByTime( 200 ) );
157
+
158
+ await waitFor( () => {
159
+ expect(
160
+ screen.getByText( 'The word "error" is not allowed.' )
161
+ ).toBeVisible();
162
+ } );
163
+
164
+ // Test editing after error
165
+ await user.type( input, '{backspace}' );
166
+
167
+ act( () => jest.advanceTimersByTime( 1000 ) );
168
+
169
+ await waitFor( () => {
170
+ expect( screen.getByText( 'Validating...' ) ).toBeVisible();
171
+ } );
172
+
173
+ act( () => jest.advanceTimersByTime( 200 ) );
174
+
175
+ await waitFor( () => {
176
+ expect( screen.getByText( 'Validated' ) ).toBeVisible();
177
+ } );
178
+ } );
179
+
180
+ it( 'should not show a "valid" state until the server response is received, even if locally valid', async () => {
181
+ const user = userEvent.setup( {
182
+ advanceTimers: jest.advanceTimersByTime,
183
+ } );
184
+ render(
185
+ <AsyncValidatedInputControl serverDelayMs={ 1200 } required />
186
+ );
187
+
188
+ const input = screen.getByRole( 'textbox' );
189
+
190
+ await user.type( input, 'valid text' );
191
+
192
+ await user.tab();
193
+ act( () => jest.advanceTimersByTime( 1200 ) );
194
+
195
+ await waitFor( () => {
196
+ expect( screen.getByText( 'Validated' ) ).toBeVisible();
197
+ } );
198
+
199
+ await user.clear( input );
200
+
201
+ act( () => jest.advanceTimersByTime( 1000 ) );
202
+
203
+ await waitFor( () => {
204
+ expect(
205
+ screen.getByText( 'Constraints not satisfied' )
206
+ ).toBeVisible();
207
+ } );
208
+
209
+ await user.type( input, 'error' );
210
+
211
+ act( () => jest.advanceTimersByTime( 200 ) );
212
+
213
+ expect( screen.queryByText( 'Validated' ) ).not.toBeInTheDocument();
214
+
215
+ act( () => jest.advanceTimersByTime( 1000 ) );
216
+
217
+ await waitFor( () => {
218
+ expect(
219
+ screen.getByText( 'The word "error" is not allowed.' )
220
+ ).toBeVisible();
221
+ } );
222
+ } );
223
+ } );
224
+ } );