diff --git a/packages/react-dom-bindings/src/events/__tests__/form-action-submitters.test.js b/packages/react-dom-bindings/src/events/__tests__/form-action-submitters.test.js new file mode 100644 index 0000000000000..e49db1d43715c --- /dev/null +++ b/packages/react-dom-bindings/src/events/__tests__/form-action-submitters.test.js @@ -0,0 +1,77 @@ +// @jest-environment jsdom + +import * as React from 'react'; +import * as ReactDOMClient from 'react-dom/client'; +import {act} from 'internal-test-utils'; + +describe('form submitter with input[name="id"]', () => { + let container; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + delete window.__SUBMIT_ACTION__; + }); + + afterEach(() => { + document.body.removeChild(container); + container = null; + delete window.__SUBMIT_ACTION__; + }); + + it('includes submitter when input[name="id"] exists (button inside form)', async () => { + function App() { + async function action(fd) { + window.__SUBMIT_ACTION__ = fd.get('action'); + } + return ( +
+ + +
+ ); + } + + await act(async () => { + const root = ReactDOMClient.createRoot(container); + root.render(); + }); + + await act(async () => { + container.querySelector('button').click(); + }); + + expect(window.__SUBMIT_ACTION__).toBe('save'); + }); + + it('includes submitter when using external button with form attribute', async () => { + function AppExternal() { + async function action(fd) { + window.__SUBMIT_ACTION__ = fd.get('action'); + } + return ( + <> +
+ +
+ + + ); + } + + await act(async () => { + const root = ReactDOMClient.createRoot(container); + root.render(); + }); + + await act(async () => { + container.querySelector('button').click(); + }); + + expect(window.__SUBMIT_ACTION__).toBe('save'); + }); +}); diff --git a/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js b/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js index 496b55e15e79b..37d602eb6207e 100644 --- a/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js +++ b/packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js @@ -60,9 +60,16 @@ function createFormDataWithSubmitter( const temp = submitter.ownerDocument.createElement('input'); temp.name = submitter.name; temp.value = submitter.value; - if (form.id) { - temp.setAttribute('form', form.id); + + // Use the form element's *id attribute*. A named control like + // can clobber the `form.id` property to point to that control instead of the + // element's id attribute. + const formId = + typeof form.getAttribute === 'function' ? form.getAttribute('id') : null; + if (formId) { + temp.setAttribute('form', formId); } + (submitter.parentNode: any).insertBefore(temp, submitter); const formData = new FormData(form); (temp.parentNode: any).removeChild(temp);