Skip to content

Commit dd1ecf3

Browse files
bartlomiejuclaude
andauthored
fix: don't intercept forms without explicit f-partial inside f-client-nav (#3722)
Fixes #3473 --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent caeebd8 commit dd1ecf3

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

packages/fresh/src/runtime/client/partials.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,21 @@ document.addEventListener("submit", async (e) => {
231231
return;
232232
}
233233

234+
const hasExplicitPartial = e.submitter?.hasAttribute(PARTIAL_ATTR) ||
235+
e.submitter?.hasAttribute("formaction") ||
236+
el.hasAttribute(PARTIAL_ATTR) ||
237+
el.hasAttribute("action");
238+
234239
const rawPartialUrl = e.submitter?.getAttribute(PARTIAL_ATTR) ??
235240
e.submitter?.getAttribute("formaction") ??
236241
el.getAttribute(PARTIAL_ATTR) ?? el.action;
237242
const rawActionUrl = e.submitter?.getAttribute("formaction") ?? el.action;
238243

239-
if (rawPartialUrl !== "") {
244+
// Only intercept forms that explicitly opt in to partial navigation
245+
// via f-partial, formaction, or an explicit action attribute. Without
246+
// this check, every form inside f-client-nav would be intercepted
247+
// because el.action is always non-empty (defaults to the current URL).
248+
if (hasExplicitPartial && rawPartialUrl !== "") {
240249
e.preventDefault();
241250

242251
const partialUrl = new URL(rawPartialUrl, location.href);

packages/fresh/tests/partials_test.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,49 @@ Deno.test({
18581858
},
18591859
});
18601860

1861+
Deno.test({
1862+
name: "partials - form without action inside f-client-nav not intercepted",
1863+
fn: async () => {
1864+
const app = testApp()
1865+
.post("/", (ctx) => {
1866+
return ctx.render(
1867+
<Doc>
1868+
<p class="submitted">form submitted normally</p>
1869+
</Doc>,
1870+
);
1871+
})
1872+
.get("/", (ctx) => {
1873+
return ctx.render(
1874+
<Doc>
1875+
<div f-client-nav>
1876+
<form method="post">
1877+
<input name="name" value="foo" />
1878+
<Partial name="foo">
1879+
<p class="init">init</p>
1880+
</Partial>
1881+
<button type="submit" class="update">
1882+
update
1883+
</button>
1884+
</form>
1885+
</div>
1886+
</Doc>,
1887+
);
1888+
});
1889+
1890+
await withBrowserApp(app, async (page, address) => {
1891+
await page.goto(address, { waitUntil: "load" });
1892+
await page.locator(".init").wait();
1893+
1894+
// Form should do a full page navigation, not a partial update
1895+
await Promise.all([
1896+
page.waitForNavigation(),
1897+
page.locator(".update").click(),
1898+
]);
1899+
await page.locator(".submitted").wait();
1900+
});
1901+
},
1902+
});
1903+
18611904
Deno.test({
18621905
name: "partials - submit form redirect",
18631906
fn: async () => {

0 commit comments

Comments
 (0)