Skip to content

Support a callback function for unstable_viewTransition props #11716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chatty-wolves-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-router-dom": minor
---

Support a callback function for `Link`/`Form` `unstable_viewTransition` prop
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@
"none": "17.3 kB"
},
"packages/react-router-dom/dist/react-router-dom.production.min.js": {
"none": "17.2 kB"
"none": "17.4 kB"
},
"packages/react-router-dom/dist/umd/react-router-dom.production.min.js": {
"none": "23.6 kB"
"none": "23.7 kB"
}
},
"pnpm": {
Expand Down
106 changes: 105 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 @@ -7384,7 +7384,7 @@ function testDomRouter(
});

describe("view transitions", () => {
it("applies view transitions to navigations when opted in", async () => {
it("applies view transitions to navigations when opted in via boolean prop", async () => {
let testWindow = getWindow("/");
let spy = jest.fn((cb) => {
cb();
Expand Down Expand Up @@ -7465,6 +7465,110 @@ function testDomRouter(
await waitFor(() => screen.getByText("D"));
expect(spy).toHaveBeenCalledTimes(2);
});

it("applies view transitions to navigations when opted in via function callback", async () => {
let testWindow = getWindow("/");
let spy = jest.fn((cb) => {
cb();
return {
ready: Promise.resolve(),
finished: Promise.resolve(),
updateCallbackDone: Promise.resolve(),
skipTransition: () => {},
};
});
testWindow.document.startViewTransition = spy;

let router = createTestRouter(
[
{
path: "/",
Component() {
return (
<div>
<Link
to="/a"
unstable_viewTransition={({ pathname }) =>
pathname === "/b"
}
>
/a
</Link>
<Link
to="/b"
unstable_viewTransition={({ pathname }) =>
pathname === "/b"
}
>
/b
</Link>
<Form
action="/c"
unstable_viewTransition={({ pathname }) =>
pathname === "/d"
}
>
<button type="submit">/c</button>
</Form>
<Form
action="/d"
unstable_viewTransition={({ pathname }) =>
pathname === "/d"
}
>
<button type="submit">/d</button>
</Form>
<Outlet />
</div>
);
},
children: [
{
index: true,
Component: () => <h1>Home</h1>,
},
{
path: "a",
Component: () => <h1>A</h1>,
},
{
path: "b",
Component: () => <h1>B</h1>,
},
{
path: "c",
action: () => null,
Component: () => <h1>C</h1>,
},
{
path: "d",
action: () => null,
Component: () => <h1>D</h1>,
},
],
},
],
{ window: testWindow }
);
render(<RouterProvider router={router} />);

expect(screen.getByText("Home")).toBeDefined();
fireEvent.click(screen.getByText("/a"));
await waitFor(() => screen.getByText("A"));
expect(spy).not.toHaveBeenCalled();

fireEvent.click(screen.getByText("/b"));
await waitFor(() => screen.getByText("B"));
expect(spy).toHaveBeenCalledTimes(1);

fireEvent.click(screen.getByText("/c"));
await waitFor(() => screen.getByText("C"));
expect(spy).toHaveBeenCalledTimes(1);

fireEvent.click(screen.getByText("/d"));
await waitFor(() => screen.getByText("D"));
expect(spy).toHaveBeenCalledTimes(2);
});
});
});
}
Expand Down
27 changes: 21 additions & 6 deletions packages/react-router-dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import type {
RouterState,
RouterSubscriber,
BlockerFunction,
Path,
} from "@remix-run/router";
import {
createRouter,
Expand All @@ -66,6 +67,7 @@ import {
UNSAFE_warning as warning,
matchPath,
IDLE_FETCHER,
parsePath,
} from "@remix-run/router";

import type {
Expand Down Expand Up @@ -922,7 +924,7 @@ export interface LinkProps
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
to: To;
unstable_viewTransition?: boolean;
unstable_viewTransition?: boolean | ((path: Path) => boolean);
}

const isBrowser =
Expand Down Expand Up @@ -1068,7 +1070,9 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
// Conditional usage is OK here because the usage of a data router is static
// eslint-disable-next-line react-hooks/rules-of-hooks
useViewTransitionState(path) &&
unstable_viewTransition === true;
typeof unstable_viewTransition === "function"
? unstable_viewTransition(path) === true
: unstable_viewTransition === true;

let toPathname = navigator.encodeLocation
? navigator.encodeLocation(path).pathname
Expand Down Expand Up @@ -1247,7 +1251,7 @@ export interface FormProps extends SharedFormProps {
/**
* Enable view transitions on this Form navigation
*/
unstable_viewTransition?: boolean;
unstable_viewTransition?: boolean | ((path: Path) => boolean);
}

type HTMLSubmitEvent = React.BaseSyntheticEvent<
Expand Down Expand Up @@ -1307,7 +1311,15 @@ export const Form = React.forwardRef<HTMLFormElement, FormProps>(
state,
relative,
preventScrollReset,
unstable_viewTransition,
unstable_viewTransition:
typeof unstable_viewTransition === "function"
? unstable_viewTransition({
pathname: "",
search: "",
hash: "",
...parsePath(formAction),
}) === true
: unstable_viewTransition === true,
});
};

Expand Down Expand Up @@ -1409,7 +1421,7 @@ export function useLinkClickHandler<E extends Element = HTMLAnchorElement>(
state?: any;
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
unstable_viewTransition?: boolean;
unstable_viewTransition?: LinkProps["unstable_viewTransition"];
} = {}
): (event: React.MouseEvent<E, MouseEvent>) => void {
let navigate = useNavigate();
Expand All @@ -1433,7 +1445,10 @@ export function useLinkClickHandler<E extends Element = HTMLAnchorElement>(
state,
preventScrollReset,
relative,
unstable_viewTransition,
unstable_viewTransition:
typeof unstable_viewTransition === "function"
? unstable_viewTransition(path)
: unstable_viewTransition === true,
});
}
},
Expand Down