From 82581e7633ed83b676c3e7be048cb513fbfc6b58 Mon Sep 17 00:00:00 2001
From: Juan Pinilla <jp.pinilla91@gmail.com>
Date: Mon, 22 Jan 2024 20:54:56 -0500
Subject: [PATCH 1/2] Fix missing form data when the submitter is outside the
 form

Fixes https://github.com/facebook/react/issues/27391

`form-associated elements` can be associated with `<form>`s anywhere in the document (even if the element is outside the `<form>`), and as with any submitter, the `name` and `value` are expected to be reflected in the final `FormData`.
---
 .../events/plugins/FormActionEventPlugin.js   |  4 +++
 .../src/__tests__/ReactDOMForm-test.js        | 30 ++++++++++++++-----
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js b/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js
index d862ae678f38..7f2fa5a4f540 100644
--- a/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js
+++ b/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js
@@ -92,6 +92,10 @@ function extractEvents(
       const temp = submitter.ownerDocument.createElement('input');
       temp.name = submitter.name;
       temp.value = submitter.value;
+      const isOutsideForm = !form.contains(submitter);
+      if (isOutsideForm) {
+        temp.setAttribute('form', form.id);
+      }
       (submitter.parentNode: any).insertBefore(temp, submitter);
       formData = new FormData(form);
       (temp.parentNode: any).removeChild(temp);
diff --git a/packages/react-dom/src/__tests__/ReactDOMForm-test.js b/packages/react-dom/src/__tests__/ReactDOMForm-test.js
index 123619d2908c..f37f1f5c3c2a 100644
--- a/packages/react-dom/src/__tests__/ReactDOMForm-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMForm-test.js
@@ -481,6 +481,7 @@ describe('ReactDOMForm', () => {
   it('can read the clicked button in the formdata event', async () => {
     const inputRef = React.createRef();
     const buttonRef = React.createRef();
+    const outsideButtonRef = React.createRef();
     let button;
     let title;
 
@@ -492,14 +493,24 @@ describe('ReactDOMForm', () => {
     const root = ReactDOMClient.createRoot(container);
     await act(async () => {
       root.render(
-        <form action={action}>
-          <input type="text" name="title" defaultValue="hello" />
-          <input type="submit" name="button" value="save" />
-          <input type="submit" name="button" value="delete" ref={inputRef} />
-          <button name="button" value="edit" ref={buttonRef}>
-            Edit
+        <>
+          <button
+            form="form"
+            name="button"
+            value="outside"
+            ref={outsideButtonRef}>
+            Edit from outside
           </button>
-        </form>,
+          <form id="form" action={action}>
+            <input type="text" name="title" defaultValue="hello" />
+            <input type="submit" name="button" value="save" />
+            <input type="submit" name="button" value="delete" ref={inputRef} />
+            <button name="button" value="edit" ref={buttonRef}>
+              Edit
+            </button>
+          </form>
+          ,
+        </>,
       );
     });
 
@@ -520,6 +531,11 @@ describe('ReactDOMForm', () => {
     expect(button).toBe('edit');
     expect(title).toBe('hello');
 
+    await submit(outsideButtonRef.current);
+
+    expect(button).toBe('outside');
+    expect(title).toBe('hello');
+
     // Ensure that the type field got correctly restored
     expect(inputRef.current.getAttribute('type')).toBe('submit');
     expect(buttonRef.current.getAttribute('type')).toBe(null);

From 0b4abb203a39b51b7e3310cebb9262126729fd7c Mon Sep 17 00:00:00 2001
From: Juan Pinilla <jp.pinilla91@gmail.com>
Date: Wed, 1 May 2024 15:36:38 -0500
Subject: [PATCH 2/2] use simplier validation to include form attribute

---
 .../events/plugins/FormActionEventPlugin.js   |  3 +--
 .../src/__tests__/ReactDOMForm-test.js        | 19 +++++++++++--------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js b/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js
index 7f2fa5a4f540..8f874e2cdb8c 100644
--- a/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js
+++ b/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js
@@ -92,8 +92,7 @@ function extractEvents(
       const temp = submitter.ownerDocument.createElement('input');
       temp.name = submitter.name;
       temp.value = submitter.value;
-      const isOutsideForm = !form.contains(submitter);
-      if (isOutsideForm) {
+      if (form.id) {
         temp.setAttribute('form', form.id);
       }
       (submitter.parentNode: any).insertBefore(temp, submitter);
diff --git a/packages/react-dom/src/__tests__/ReactDOMForm-test.js b/packages/react-dom/src/__tests__/ReactDOMForm-test.js
index f37f1f5c3c2a..5db3b2fd8a5b 100644
--- a/packages/react-dom/src/__tests__/ReactDOMForm-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMForm-test.js
@@ -494,14 +494,7 @@ describe('ReactDOMForm', () => {
     await act(async () => {
       root.render(
         <>
-          <button
-            form="form"
-            name="button"
-            value="outside"
-            ref={outsideButtonRef}>
-            Edit from outside
-          </button>
-          <form id="form" action={action}>
+          <form action={action}>
             <input type="text" name="title" defaultValue="hello" />
             <input type="submit" name="button" value="save" />
             <input type="submit" name="button" value="delete" ref={inputRef} />
@@ -509,6 +502,16 @@ describe('ReactDOMForm', () => {
               Edit
             </button>
           </form>
+          <form id="form" action={action}>
+            <input type="text" name="title" defaultValue="hello" />
+          </form>
+          <button
+            form="form"
+            name="button"
+            value="outside"
+            ref={outsideButtonRef}>
+            Button outside form
+          </button>
           ,
         </>,
       );
