@@ -195,4 +195,169 @@ describe('ReactDOMFizzForm', () => {
195
195
'Prop `action` did not match. Server: "action" Client: "function action(formData) {}"' ,
196
196
) ;
197
197
} ) ;
198
+
199
+ // @gate enableFormActions
200
+ it ( 'should reset form fields after you update away from hydrated function' , async ( ) => {
201
+ const formRef = React . createRef ( ) ;
202
+ const inputRef = React . createRef ( ) ;
203
+ const buttonRef = React . createRef ( ) ;
204
+ function action ( formData ) { }
205
+ function App ( { isUpdate} ) {
206
+ return (
207
+ < form
208
+ action = { isUpdate ? 'action' : action }
209
+ ref = { formRef }
210
+ method = { isUpdate ? 'POST' : null } >
211
+ < input
212
+ type = "submit"
213
+ formAction = { isUpdate ? 'action' : action }
214
+ ref = { inputRef }
215
+ formTarget = { isUpdate ? 'elsewhere' : null }
216
+ />
217
+ < button
218
+ formAction = { isUpdate ? 'action' : action }
219
+ ref = { buttonRef }
220
+ formEncType = { isUpdate ? 'multipart/form-data' : null }
221
+ />
222
+ </ form >
223
+ ) ;
224
+ }
225
+
226
+ const stream = await ReactDOMServer . renderToReadableStream ( < App /> ) ;
227
+ await readIntoContainer ( stream ) ;
228
+ let root ;
229
+ await act ( async ( ) => {
230
+ root = ReactDOMClient . hydrateRoot ( container , < App /> ) ;
231
+ } ) ;
232
+ await act ( async ( ) => {
233
+ root . render ( < App isUpdate = { true } /> ) ;
234
+ } ) ;
235
+ expect ( formRef . current . getAttribute ( 'action' ) ) . toBe ( 'action' ) ;
236
+ expect ( formRef . current . hasAttribute ( 'encType' ) ) . toBe ( false ) ;
237
+ expect ( formRef . current . getAttribute ( 'method' ) ) . toBe ( 'POST' ) ;
238
+ expect ( formRef . current . hasAttribute ( 'target' ) ) . toBe ( false ) ;
239
+
240
+ expect ( inputRef . current . getAttribute ( 'formAction' ) ) . toBe ( 'action' ) ;
241
+ expect ( inputRef . current . hasAttribute ( 'name' ) ) . toBe ( false ) ;
242
+ expect ( inputRef . current . hasAttribute ( 'formEncType' ) ) . toBe ( false ) ;
243
+ expect ( inputRef . current . hasAttribute ( 'formMethod' ) ) . toBe ( false ) ;
244
+ expect ( inputRef . current . getAttribute ( 'formTarget' ) ) . toBe ( 'elsewhere' ) ;
245
+
246
+ expect ( buttonRef . current . getAttribute ( 'formAction' ) ) . toBe ( 'action' ) ;
247
+ expect ( buttonRef . current . hasAttribute ( 'name' ) ) . toBe ( false ) ;
248
+ expect ( buttonRef . current . getAttribute ( 'formEncType' ) ) . toBe (
249
+ 'multipart/form-data' ,
250
+ ) ;
251
+ expect ( buttonRef . current . hasAttribute ( 'formMethod' ) ) . toBe ( false ) ;
252
+ expect ( buttonRef . current . hasAttribute ( 'formTarget' ) ) . toBe ( false ) ;
253
+ } ) ;
254
+
255
+ // @gate enableFormActions
256
+ it ( 'should reset form fields after you remove a hydrated function' , async ( ) => {
257
+ const formRef = React . createRef ( ) ;
258
+ const inputRef = React . createRef ( ) ;
259
+ const buttonRef = React . createRef ( ) ;
260
+ function action ( formData ) { }
261
+ function App ( { isUpdate} ) {
262
+ return (
263
+ < form action = { isUpdate ? undefined : action } ref = { formRef } >
264
+ < input
265
+ type = "submit"
266
+ formAction = { isUpdate ? undefined : action }
267
+ ref = { inputRef }
268
+ />
269
+ < button formAction = { isUpdate ? undefined : action } ref = { buttonRef } />
270
+ </ form >
271
+ ) ;
272
+ }
273
+
274
+ const stream = await ReactDOMServer . renderToReadableStream ( < App /> ) ;
275
+ await readIntoContainer ( stream ) ;
276
+ let root ;
277
+ await act ( async ( ) => {
278
+ root = ReactDOMClient . hydrateRoot ( container , < App /> ) ;
279
+ } ) ;
280
+ await act ( async ( ) => {
281
+ root . render ( < App isUpdate = { true } /> ) ;
282
+ } ) ;
283
+ expect ( formRef . current . hasAttribute ( 'action' ) ) . toBe ( false ) ;
284
+ expect ( formRef . current . hasAttribute ( 'encType' ) ) . toBe ( false ) ;
285
+ expect ( formRef . current . hasAttribute ( 'method' ) ) . toBe ( false ) ;
286
+ expect ( formRef . current . hasAttribute ( 'target' ) ) . toBe ( false ) ;
287
+
288
+ expect ( inputRef . current . hasAttribute ( 'formAction' ) ) . toBe ( false ) ;
289
+ expect ( inputRef . current . hasAttribute ( 'name' ) ) . toBe ( false ) ;
290
+ expect ( inputRef . current . hasAttribute ( 'formEncType' ) ) . toBe ( false ) ;
291
+ expect ( inputRef . current . hasAttribute ( 'formMethod' ) ) . toBe ( false ) ;
292
+ expect ( inputRef . current . hasAttribute ( 'formTarget' ) ) . toBe ( false ) ;
293
+
294
+ expect ( buttonRef . current . hasAttribute ( 'formAction' ) ) . toBe ( false ) ;
295
+ expect ( buttonRef . current . hasAttribute ( 'name' ) ) . toBe ( false ) ;
296
+ expect ( buttonRef . current . hasAttribute ( 'formEncType' ) ) . toBe ( false ) ;
297
+ expect ( buttonRef . current . hasAttribute ( 'formMethod' ) ) . toBe ( false ) ;
298
+ expect ( buttonRef . current . hasAttribute ( 'formTarget' ) ) . toBe ( false ) ;
299
+ } ) ;
300
+
301
+ // @gate enableFormActions
302
+ it ( 'should restore the form fields even if they were incorrectly set' , async ( ) => {
303
+ const formRef = React . createRef ( ) ;
304
+ const inputRef = React . createRef ( ) ;
305
+ const buttonRef = React . createRef ( ) ;
306
+ function action ( formData ) { }
307
+ function App ( { isUpdate} ) {
308
+ return (
309
+ < form
310
+ action = { isUpdate ? 'action' : action }
311
+ ref = { formRef }
312
+ method = "DELETE" >
313
+ < input
314
+ type = "submit"
315
+ formAction = { isUpdate ? 'action' : action }
316
+ ref = { inputRef }
317
+ formTarget = "elsewhere"
318
+ />
319
+ < button
320
+ formAction = { isUpdate ? 'action' : action }
321
+ ref = { buttonRef }
322
+ formEncType = "text/plain"
323
+ />
324
+ </ form >
325
+ ) ;
326
+ }
327
+
328
+ // Specifying the extra form fields are a DEV error, but we expect it
329
+ // to eventually still be patched up after an update.
330
+ await expect ( async ( ) => {
331
+ const stream = await ReactDOMServer . renderToReadableStream ( < App /> ) ;
332
+ await readIntoContainer ( stream ) ;
333
+ } ) . toErrorDev ( [
334
+ 'Cannot specify a encType or method for a form that specifies a function as the action.' ,
335
+ 'Cannot specify a formTarget for a button that specifies a function as a formAction.' ,
336
+ ] ) ;
337
+ let root ;
338
+ await expect ( async ( ) => {
339
+ await act ( async ( ) => {
340
+ root = ReactDOMClient . hydrateRoot ( container , < App /> ) ;
341
+ } ) ;
342
+ } ) . toErrorDev ( [ 'Prop `formTarget` did not match.' ] ) ;
343
+ await act ( async ( ) => {
344
+ root . render ( < App isUpdate = { true } /> ) ;
345
+ } ) ;
346
+ expect ( formRef . current . getAttribute ( 'action' ) ) . toBe ( 'action' ) ;
347
+ expect ( formRef . current . hasAttribute ( 'encType' ) ) . toBe ( false ) ;
348
+ expect ( formRef . current . getAttribute ( 'method' ) ) . toBe ( 'DELETE' ) ;
349
+ expect ( formRef . current . hasAttribute ( 'target' ) ) . toBe ( false ) ;
350
+
351
+ expect ( inputRef . current . getAttribute ( 'formAction' ) ) . toBe ( 'action' ) ;
352
+ expect ( inputRef . current . hasAttribute ( 'name' ) ) . toBe ( false ) ;
353
+ expect ( inputRef . current . hasAttribute ( 'formEncType' ) ) . toBe ( false ) ;
354
+ expect ( inputRef . current . hasAttribute ( 'formMethod' ) ) . toBe ( false ) ;
355
+ expect ( inputRef . current . getAttribute ( 'formTarget' ) ) . toBe ( 'elsewhere' ) ;
356
+
357
+ expect ( buttonRef . current . getAttribute ( 'formAction' ) ) . toBe ( 'action' ) ;
358
+ expect ( buttonRef . current . hasAttribute ( 'name' ) ) . toBe ( false ) ;
359
+ expect ( buttonRef . current . getAttribute ( 'formEncType' ) ) . toBe ( 'text/plain' ) ;
360
+ expect ( buttonRef . current . hasAttribute ( 'formMethod' ) ) . toBe ( false ) ;
361
+ expect ( buttonRef . current . hasAttribute ( 'formTarget' ) ) . toBe ( false ) ;
362
+ } ) ;
198
363
} ) ;
0 commit comments