Skip to content

Commit c5f1dfa

Browse files
committed
Fix escaping in action error URL
1 parent 31034b6 commit c5f1dfa

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

packages/react-dom-bindings/src/client/ReactDOMComponent.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ function setProp(
501501
// eslint-disable-next-line no-script-url
502502
"javascript:throw new Error('" +
503503
'A React form was unexpectedly submitted. If you called form.submit() manually, ' +
504-
"consider using form.requestSubmit() instead. If you're trying to use " +
504+
"consider using form.requestSubmit() instead. If you\\'re trying to use " +
505505
'event.stopPropagation() in a submit event handler, consider also calling ' +
506506
'event.preventDefault().' +
507507
"')",

packages/react-dom/src/__tests__/ReactDOMForm-test.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ describe('ReactDOMForm', () => {
190190
(submitter && submitter.getAttribute('formaction')) || form.action;
191191
if (!/\s*javascript:/i.test(action)) {
192192
throw new Error('Navigate to: ' + action);
193+
} else {
194+
Function(action.substr(11))();
193195
}
194196
});
195197
}
@@ -922,4 +924,66 @@ describe('ReactDOMForm', () => {
922924
await act(() => resolveText('Wait'));
923925
assertLog(['Async action finished', 'No pending action']);
924926
});
927+
928+
function emulateForceSubmit(submitter) {
929+
const form = submitter.form || submitter;
930+
const action =
931+
(submitter && submitter.getAttribute('formaction')) || form.action;
932+
if (!/\s*javascript:/i.test(action)) {
933+
throw new Error('Navigate to: ' + action);
934+
} else {
935+
Function(action.substr(11))();
936+
}
937+
}
938+
939+
// @gate enableFormActions
940+
it('should error if submitting a form manually', async () => {
941+
const ref = React.createRef();
942+
let foo;
943+
944+
function action(formData) {
945+
foo = formData.get('foo');
946+
}
947+
948+
let error = null;
949+
let result = null;
950+
951+
function emulateForceSubmit(submitter) {
952+
const form = submitter.form || submitter;
953+
const action =
954+
(submitter && submitter.getAttribute('formaction')) || form.action;
955+
if (!/\s*javascript:/i.test(action)) {
956+
throw new Error('Navigate to: ' + action);
957+
} else {
958+
try {
959+
result = Function(action.substr(11))();
960+
} catch (x) {
961+
error = x;
962+
}
963+
}
964+
}
965+
966+
const root = ReactDOMClient.createRoot(container);
967+
await act(async () => {
968+
root.render(
969+
<form
970+
action={action}
971+
ref={ref}
972+
onSubmit={e => {
973+
e.preventDefault();
974+
emulateForceSubmit(e.target);
975+
}}>
976+
<input type="text" name="foo" defaultValue="bar" />
977+
</form>,
978+
);
979+
});
980+
981+
// This submits the form, which gets blocked and then resubmitted. It's a somewhat
982+
// common idiom but we don't support this pattern unless it uses requestSubmit().
983+
await submit(ref.current);
984+
expect(result).toBe(null);
985+
expect(error.message).toContain(
986+
'A React form was unexpectedly submitted. If you called form.submit()',
987+
);
988+
});
925989
});

0 commit comments

Comments
 (0)