Skip to content

Commit

Permalink
Merge pull request #26874 from storybookjs/yann/fix-nextjs-14.2-usepa…
Browse files Browse the repository at this point in the history
…rams

Nextjs: Support next 14.2 useParams functionality
  • Loading branch information
yannbf committed Apr 18, 2024
2 parents 38afbe6 + fb24aef commit 316779b
Show file tree
Hide file tree
Showing 10 changed files with 890 additions and 643 deletions.
6 changes: 3 additions & 3 deletions code/frameworks/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,6 @@
"typescript": "^5.3.2",
"webpack": "^5.65.0"
},
"optionalDependencies": {
"sharp": "^0.33.3"
},
"peerDependencies": {
"next": "^13.5.0 || ^14.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
Expand All @@ -148,6 +145,9 @@
"optional": true
}
},
"optionalDependencies": {
"sharp": "^0.33.3"
},
"engines": {
"node": ">=18.0.0"
},
Expand Down
128 changes: 81 additions & 47 deletions code/frameworks/nextjs/src/routing/app-router-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import {
LayoutRouterContext,
AppRouterContext,
Expand All @@ -7,7 +7,10 @@ import {
import {
PathnameContext,
SearchParamsContext,
PathParamsContext,
} from 'next/dist/shared/lib/hooks-client-context.shared-runtime';
import { type Params } from 'next/dist/shared/lib/router/utils/route-matcher';
import { PAGE_SEGMENT_KEY } from 'next/dist/shared/lib/segment';
import type { FlightRouterState } from 'next/dist/server/app-render/types';
import type { RouteParams } from './types';

Expand All @@ -16,6 +19,32 @@ type AppRouterProviderProps = {
routeParams: RouteParams;
};

// Since Next 14.2.x
// https://github.com/vercel/next.js/pull/60708/files#diff-7b6239af735eba0c401e1a0db1a04dd4575c19a031934f02d128cf3ac813757bR106
function getSelectedParams(currentTree: FlightRouterState, params: Params = {}): Params {
const parallelRoutes = currentTree[1];

for (const parallelRoute of Object.values(parallelRoutes)) {
const segment = parallelRoute[0];
const isDynamicParameter = Array.isArray(segment);
const segmentValue = isDynamicParameter ? segment[1] : segment;
if (!segmentValue || segmentValue.startsWith(PAGE_SEGMENT_KEY)) continue;

// Ensure catchAll and optional catchall are turned into an array
const isCatchAll = isDynamicParameter && (segment[2] === 'c' || segment[2] === 'oc');

if (isCatchAll) {
params[segment[0]] = segment[1].split('/');
} else if (isDynamicParameter) {
params[segment[0]] = segment[1];
}

params = getSelectedParams(parallelRoute, params);
}

return params;
}

const getParallelRoutes = (segmentsList: Array<string>): FlightRouterState => {
const segment = segmentsList.shift();

Expand All @@ -34,62 +63,67 @@ export const AppRouterProvider: React.FC<React.PropsWithChildren<AppRouterProvid
const { pathname, query, segments = [], ...restRouteParams } = routeParams;

const tree: FlightRouterState = [pathname, { children: getParallelRoutes([...segments]) }];
const pathParams = useMemo(() => {
return getSelectedParams(tree);
}, [tree]);

// https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/app-router.tsx#L436
return (
<PathnameContext.Provider value={pathname}>
<SearchParamsContext.Provider value={new URLSearchParams(query)}>
<GlobalLayoutRouterContext.Provider
value={{
changeByServerResponse() {
// NOOP
},
buildId: 'storybook',
tree,
focusAndScrollRef: {
apply: false,
hashFragment: null,
segmentPaths: [tree],
onlyHashChange: false,
},
nextUrl: pathname,
}}
>
<AppRouterContext.Provider
<PathParamsContext.Provider value={pathParams}>
<PathnameContext.Provider value={pathname}>
<SearchParamsContext.Provider value={new URLSearchParams(query)}>
<GlobalLayoutRouterContext.Provider
value={{
push(...args) {
action('nextNavigation.push')(...args);
},
replace(...args) {
action('nextNavigation.replace')(...args);
},
forward(...args) {
action('nextNavigation.forward')(...args);
},
back(...args) {
action('nextNavigation.back')(...args);
},
prefetch(...args) {
action('nextNavigation.prefetch')(...args);
changeByServerResponse() {
// NOOP
},
refresh: () => {
action('nextNavigation.refresh')();
buildId: 'storybook',
tree,
focusAndScrollRef: {
apply: false,
hashFragment: null,
segmentPaths: [tree],
onlyHashChange: false,
},
...restRouteParams,
nextUrl: pathname,
}}
>
<LayoutRouterContext.Provider
<AppRouterContext.Provider
value={{
childNodes: new Map(),
tree,
url: pathname,
push(...args) {
action('nextNavigation.push')(...args);
},
replace(...args) {
action('nextNavigation.replace')(...args);
},
forward(...args) {
action('nextNavigation.forward')(...args);
},
back(...args) {
action('nextNavigation.back')(...args);
},
prefetch(...args) {
action('nextNavigation.prefetch')(...args);
},
refresh: () => {
action('nextNavigation.refresh')();
},
...restRouteParams,
}}
>
{children}
</LayoutRouterContext.Provider>
</AppRouterContext.Provider>
</GlobalLayoutRouterContext.Provider>
</SearchParamsContext.Provider>
</PathnameContext.Provider>
<LayoutRouterContext.Provider
value={{
childNodes: new Map(),
tree,
url: pathname,
}}
>
{children}
</LayoutRouterContext.Provider>
</AppRouterContext.Provider>
</GlobalLayoutRouterContext.Provider>
</SearchParamsContext.Provider>
</PathnameContext.Provider>
</PathParamsContext.Provider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default {
};

export const Default = {
play: async ({ canvasElement }) => {
play: async () => {
await waitFor(() => expect(document.title).toEqual('Next.js Head Title'));
await expect(document.querySelectorAll('meta[property="og:title"]')).toHaveLength(1);
await expect(document.querySelector('meta[property="og:title"]').content).toEqual(
Expand Down

This file was deleted.

0 comments on commit 316779b

Please sign in to comment.