Skip to content

Commit

Permalink
Ensure Form contains splat portion of pathname when no action is spec…
Browse files Browse the repository at this point in the history
…ified (#10933)
  • Loading branch information
brophdawg11 authored Oct 16, 2023
1 parent a71b4e2 commit 908a40a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/splat-form-action.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-router-dom": patch
---

Ensure `<Form>` default action contains splat portion of pathname when no `action` is specified
40 changes: 39 additions & 1 deletion packages/react-router-dom/__tests__/data-browser-router-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2894,7 +2894,7 @@ function testDomRouter(
let { container } = render(<RouterProvider router={router} />);

expect(container.querySelector("form")?.getAttribute("action")).toBe(
"/foo?a=1"
"/foo/bar?a=1"
);
});

Expand Down Expand Up @@ -2937,6 +2937,44 @@ function testDomRouter(
"/foo"
);
});

it("includes splat portion of path when no action is specified (inline splat)", async () => {
let router = createTestRouter(
createRoutesFromElements(
<Route path="/">
<Route path="foo">
<Route path="*" element={<NoActionComponent />} />
</Route>
</Route>
),
{
window: getWindow("/foo/bar/baz"),
}
);
let { container } = render(<RouterProvider router={router} />);

expect(container.querySelector("form")?.getAttribute("action")).toBe(
"/foo/bar/baz"
);
});

it("includes splat portion of path when no action is specified (nested splat)", async () => {
let router = createTestRouter(
createRoutesFromElements(
<Route path="/">
<Route path="foo/*" element={<NoActionComponent />} />
</Route>
),
{
window: getWindow("/foo/bar/baz"),
}
);
let { container } = render(<RouterProvider router={router} />);

expect(container.querySelector("form")?.getAttribute("action")).toBe(
"/foo/bar/baz"
);
});
});

it("allows user to specify search params and hash", async () => {
Expand Down
14 changes: 8 additions & 6 deletions packages/react-router-dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1457,18 +1457,20 @@ export function useFormAction(
let { basename } = React.useContext(NavigationContext);
let routeContext = React.useContext(RouteContext);
invariant(routeContext, "useFormAction must be used inside a RouteContext");
let location = useLocation();

let [match] = routeContext.matches.slice(-1);
// Shallow clone path so we can modify it below, otherwise we modify the
// object referenced by useMemo inside useResolvedPath
let path = { ...useResolvedPath(action ? action : ".", { relative }) };
let path = {
...useResolvedPath(action != null ? action : location.pathname, {
relative,
}),
};

// Previously we set the default action to ".". The problem with this is that
// `useResolvedPath(".")` excludes search params of the resolved URL. This is
// the intended behavior of when "." is specifically provided as
// the form action, but inconsistent w/ browsers when the action is omitted.
// If no action was specified, browsers will persist current search params
// when determining the path, so match that behavior
// https://github.com/remix-run/remix/issues/927
let location = useLocation();
if (action == null) {
// Safe to write to this directly here since if action was undefined, we
// would have called useResolvedPath(".") which will never include a search
Expand Down

0 comments on commit 908a40a

Please sign in to comment.