Skip to content

Commit 68bbf9c

Browse files
authored
fix: fetcher.submit failing with plain objects containing tagName pro… (#14534)
1 parent 4789692 commit 68bbf9c

File tree

3 files changed

+106
-1
lines changed

3 files changed

+106
-1
lines changed

.changeset/fluffy-walls-add.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix `fetcher.submit` failing with plain objects containing a `tagName` property
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import * as React from "react";
2+
import { render, fireEvent, screen } from "@testing-library/react";
3+
import "@testing-library/jest-dom";
4+
import {
5+
RouterProvider,
6+
createBrowserRouter,
7+
useFetcher,
8+
} from "../../index";
9+
import getWindow from "../utils/getWindow";
10+
11+
describe("fetcher.submit with tagName property", () => {
12+
it("should handle plain object with tagName property", async () => {
13+
let actionSpy = jest.fn();
14+
actionSpy.mockReturnValue({ ok: true });
15+
16+
let router = createBrowserRouter(
17+
[
18+
{
19+
path: "/",
20+
action: actionSpy,
21+
Component() {
22+
let fetcher = useFetcher();
23+
return (
24+
<button
25+
onClick={() =>
26+
fetcher.submit(
27+
{ tagName: "div", data: "test" },
28+
{ method: "post" }
29+
)
30+
}
31+
>
32+
Submit
33+
</button>
34+
);
35+
},
36+
},
37+
],
38+
{
39+
window: getWindow("/"),
40+
}
41+
);
42+
43+
render(<RouterProvider router={router} />);
44+
fireEvent.click(screen.getByText("Submit"));
45+
46+
expect(actionSpy).toHaveBeenCalled();
47+
let formData = await actionSpy.mock.calls[0][0].request.formData();
48+
expect(formData.get("tagName")).toBe("div");
49+
expect(formData.get("data")).toBe("test");
50+
});
51+
52+
it("should handle plain object with various HTML element-like properties", async () => {
53+
let actionSpy = jest.fn();
54+
actionSpy.mockReturnValue({ ok: true });
55+
56+
let router = createBrowserRouter(
57+
[
58+
{
59+
path: "/",
60+
action: actionSpy,
61+
Component() {
62+
let fetcher = useFetcher();
63+
return (
64+
<button
65+
onClick={() =>
66+
fetcher.submit(
67+
{
68+
tagName: "button",
69+
className: "test-class",
70+
id: "test-id",
71+
value: "test-value",
72+
},
73+
{ method: "post" }
74+
)
75+
}
76+
>
77+
Submit
78+
</button>
79+
);
80+
},
81+
},
82+
],
83+
{
84+
window: getWindow("/"),
85+
}
86+
);
87+
88+
render(<RouterProvider router={router} />);
89+
fireEvent.click(screen.getByText("Submit"));
90+
91+
expect(actionSpy).toHaveBeenCalled();
92+
let formData = await actionSpy.mock.calls[0][0].request.formData();
93+
expect(formData.get("tagName")).toBe("button");
94+
expect(formData.get("className")).toBe("test-class");
95+
expect(formData.get("id")).toBe("test-id");
96+
expect(formData.get("value")).toBe("test-value");
97+
});
98+
});

packages/react-router/lib/dom/dom.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export const defaultMethod: HTMLFormMethod = "get";
77
const defaultEncType: FormEncType = "application/x-www-form-urlencoded";
88

99
export function isHtmlElement(object: any): object is HTMLElement {
10-
return object != null && typeof object.tagName === "string";
10+
return (
11+
typeof HTMLElement !== "undefined" && object instanceof HTMLElement
12+
);
1113
}
1214

1315
export function isButtonElement(object: any): object is HTMLButtonElement {

0 commit comments

Comments
 (0)