diff --git a/code/frameworks/nextjs/README.md b/code/frameworks/nextjs/README.md index 1989da90c05e..d15ad4d4a98a 100644 --- a/code/frameworks/nextjs/README.md +++ b/code/frameworks/nextjs/README.md @@ -28,7 +28,7 @@ - [Set `nextjs.appDirectory` to `true`](#set-nextjsappdirectory-to-true) - [Overriding defaults](#overriding-defaults-1) - [Global Defaults](#global-defaults-1) - - [`useSelectedLayoutSegment` and `useSelectedLayoutSegments` hook](#useselectedlayoutsegment-and-useselectedlayoutsegments-hook) + - [`useSelectedLayoutSegment` `useSelectedLayoutSegments` and `useParams` hook](#useselectedlayoutsegment-useselectedlayoutsegments-and-useparams-hook) - [Default Navigation Context](#default-navigation-context) - [Actions Integration Caveats](#actions-integration-caveats-1) - [Next.js Head](#nextjs-head) @@ -503,9 +503,9 @@ export const parameters = { }; ``` -#### `useSelectedLayoutSegment` and `useSelectedLayoutSegments` hook +#### `useSelectedLayoutSegment` `useSelectedLayoutSegments` and `useParams` hook -The `useSelectedLayoutSegment` and `useSelectedLayoutSegments` hooks are supported in Storybook. You have to set the `nextjs.navigation.segments` parameter to return the segments you want to use. +The `useSelectedLayoutSegment` `useSelectedLayoutSegments` and `useParams` hooks are supported in Storybook. You have to set the `nextjs.navigation.segments` parameter to return the segments or the params you want to use. ```js // SomeComponentThatUsesTheNavigation.stories.js @@ -526,11 +526,46 @@ export default { export const Example = {}; // SomeComponentThatUsesTheNavigation.js -import { useSelectedLayoutSegment, useSelectedLayoutSegments } from 'next/navigation'; +import { useSelectedLayoutSegment, useSelectedLayoutSegments, useParams } from 'next/navigation'; export default function SomeComponentThatUsesTheNavigation() { const segment = useSelectedLayoutSegment(); // dashboard const segments = useSelectedLayoutSegments(); // ["dashboard", "analytics"] + const params = useParams(); // {} + ... +} +``` + +To use `useParams`, you have to use a two string elements array for a segment, the first array element is the param key and the second array element is the param value. + +```js +// SomeComponentThatUsesParams.stories.js +import SomeComponentThatUsesParams from './SomeComponentThatUsesParams'; + +export default { + component: SomeComponentThatUsesParams, + parameters: { + nextjs: { + appDirectory: true, + navigation: { + segments: [ + ['slug', 'hello'], + ['framework', 'nextjs'], + ] + }, + }, + }, +}; + +export const Example = {}; + +// SomeComponentThatUsesParams.js +import { useSelectedLayoutSegment, useSelectedLayoutSegments, useParams } from 'next/navigation'; + +export default function SomeComponentThatUsesParams() { + const segment = useSelectedLayoutSegment(); // hello + const segments = useSelectedLayoutSegments(); // ["hello", "nextjs"] + const params = useParams(); // { slug: "hello", framework: "nextjs" } ... } ``` diff --git a/code/frameworks/nextjs/src/routing/app-router-provider.tsx b/code/frameworks/nextjs/src/routing/app-router-provider.tsx index e285d5dbe2a1..ab09406a14d6 100644 --- a/code/frameworks/nextjs/src/routing/app-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/app-router-provider.tsx @@ -2,6 +2,7 @@ import React from 'react'; import type { LayoutRouterContext as TLayoutRouterContext, AppRouterContext as TAppRouterContext, + GlobalLayoutRouterContext as TGlobalLayoutRouterContext, } from 'next/dist/shared/lib/app-router-context'; import type { PathnameContext as TPathnameContext, @@ -21,17 +22,21 @@ let AppRouterContext: typeof TAppRouterContext; let LayoutRouterContext: typeof TLayoutRouterContext; let PathnameContext: typeof TPathnameContext; let SearchParamsContext: typeof TSearchParamsContext; +let GlobalLayoutRouterContext: typeof TGlobalLayoutRouterContext; try { AppRouterContext = require('next/dist/shared/lib/app-router-context').AppRouterContext; LayoutRouterContext = require('next/dist/shared/lib/app-router-context').LayoutRouterContext; PathnameContext = require('next/dist/shared/lib/hooks-client-context').PathnameContext; SearchParamsContext = require('next/dist/shared/lib/hooks-client-context').SearchParamsContext; + GlobalLayoutRouterContext = + require('next/dist/shared/lib/app-router-context').GlobalLayoutRouterContext; } catch { AppRouterContext = React.Fragment as any; LayoutRouterContext = React.Fragment as any; PathnameContext = React.Fragment as any; SearchParamsContext = React.Fragment as any; + GlobalLayoutRouterContext = React.Fragment as any; } type AppRouterProviderProps = { @@ -52,44 +57,62 @@ const getParallelRoutes = (segmentsList: Array): FlightRouterState => { const AppRouterProvider: React.FC = ({ children, action, routeParams }) => { const { pathname, query, segments = [], ...restRouteParams } = routeParams; + const tree: FlightRouterState = [pathname, { children: getParallelRoutes([...segments]) }]; + + // https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/app-router.tsx#L436 return ( - { - action('nextNavigation.refresh')(); - }, - ...restRouteParams, - }} - > + - - {children} - + { + action('nextNavigation.refresh')(); + }, + ...restRouteParams, + }} + > + + {children} + + + - + ); }; diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-js/Navigation.stories.jsx b/code/frameworks/nextjs/template/stories_nextjs-default-js/Navigation.stories.jsx index 12b66e3205d2..166567aa456c 100644 --- a/code/frameworks/nextjs/template/stories_nextjs-default-js/Navigation.stories.jsx +++ b/code/frameworks/nextjs/template/stories_nextjs-default-js/Navigation.stories.jsx @@ -2,6 +2,7 @@ import { useRouter, usePathname, useSearchParams, + useParams, useSelectedLayoutSegment, useSelectedLayoutSegments, } from 'next/navigation'; @@ -11,6 +12,7 @@ function Component() { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); + const params = useParams(); const segment = useSelectedLayoutSegment(); const segments = useSelectedLayoutSegments(); @@ -58,6 +60,16 @@ function Component() { ))} +
+ params:{' '} +
    + {Object.entries(params).map(([key, value]) => ( +
  • + {key}: {value} +
  • + ))} +
+
{routerActions.map(({ cb, name }) => (