From 8350f7efb20c7041a3a9c72b20aecdfb7ea97d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Wed, 2 Nov 2022 23:06:31 +0100 Subject: [PATCH] useSelectedLayoutSegment at the current level (#42299) When `useSelectedLayoutSegment` is used on the current level or in a page it should return null. fixes #41879 fixes #41878 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) --- packages/next/client/components/navigation.ts | 4 +-- .../(group)/second/[...catchall]/page.js | 13 +++++-- .../first/layout.js | 11 ++++-- .../use-selected-layout-segment/layout.js | 11 ++++-- test/e2e/app-dir/index.test.ts | 34 ++++++++++++++++++- 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/packages/next/client/components/navigation.ts b/packages/next/client/components/navigation.ts index 39b3d7092521fb7..0d5f4dde633feb4 100644 --- a/packages/next/client/components/navigation.ts +++ b/packages/next/client/components/navigation.ts @@ -174,10 +174,10 @@ export function useSelectedLayoutSegments( */ export function useSelectedLayoutSegment( parallelRouteKey: string = 'children' -): string { +): string | null { const selectedLayoutSegments = useSelectedLayoutSegments(parallelRouteKey) if (selectedLayoutSegments.length === 0) { - throw new Error('No selected layout segment below the current level') + return null } return selectedLayoutSegments[0] diff --git a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/[dynamic]/(group)/second/[...catchall]/page.js b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/[dynamic]/(group)/second/[...catchall]/page.js index 470730ed05d66d3..5cda7e23cac8d81 100644 --- a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/[dynamic]/(group)/second/[...catchall]/page.js +++ b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/[dynamic]/(group)/second/[...catchall]/page.js @@ -1,11 +1,18 @@ 'use client' -import { useSelectedLayoutSegments } from 'next/navigation' +import { + useSelectedLayoutSegments, + useSelectedLayoutSegment, +} from 'next/navigation' export default function Page() { - const selectedLayoutSegment = useSelectedLayoutSegments() + const selectedLayoutSegments = useSelectedLayoutSegments() + const selectedLayoutSegment = useSelectedLayoutSegment() return ( -

{JSON.stringify(selectedLayoutSegment)}

+ <> +

{JSON.stringify(selectedLayoutSegments)}

+

{JSON.stringify(selectedLayoutSegment)}

+ ) } diff --git a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/layout.js b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/layout.js index 6db1338e8278364..e79045c5ed1e7be 100644 --- a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/layout.js +++ b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/first/layout.js @@ -1,13 +1,18 @@ 'use client' -import { useSelectedLayoutSegments } from 'next/navigation' +import { + useSelectedLayoutSegments, + useSelectedLayoutSegment, +} from 'next/navigation' export default function Layout({ children }) { - const selectedLayoutSegment = useSelectedLayoutSegments() + const selectedLayoutSegments = useSelectedLayoutSegments() + const selectedLayoutSegment = useSelectedLayoutSegment() return ( <> -

{JSON.stringify(selectedLayoutSegment)}

+

{JSON.stringify(selectedLayoutSegments)}

+

{JSON.stringify(selectedLayoutSegment)}

{children} ) diff --git a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/layout.js b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/layout.js index 5d7974142e40f6d..3eeae83e65da1be 100644 --- a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/layout.js +++ b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/layout.js @@ -1,13 +1,18 @@ 'use client' -import { useSelectedLayoutSegments } from 'next/navigation' +import { + useSelectedLayoutSegments, + useSelectedLayoutSegment, +} from 'next/navigation' export default function Layout({ children }) { - const selectedLayoutSegment = useSelectedLayoutSegments() + const selectedLayoutSegments = useSelectedLayoutSegments() + const selectedLayoutSegment = useSelectedLayoutSegment() return ( <> -

{JSON.stringify(selectedLayoutSegment)}

+

{JSON.stringify(selectedLayoutSegments)}

+

{JSON.stringify(selectedLayoutSegment)}

{children} ) diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index b589356462eebbf..9917364eb4ef5e1 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -1273,7 +1273,7 @@ describe('app dir', () => { } }) - describe('useSelectedLayoutSegment', () => { + describe('useSelectedLayoutSegments', () => { it.each` path | outerLayout | innerLayout ${'/hooks/use-selected-layout-segment/first'} | ${['first']} | ${[]} @@ -1303,6 +1303,38 @@ describe('app dir', () => { expect(JSON.parse($('#page-layout-segments').text())).toEqual([]) }) }) + + describe('useSelectedLayoutSegment', () => { + it.each` + path | outerLayout | innerLayout + ${'/hooks/use-selected-layout-segment/first'} | ${'first'} | ${null} + ${'/hooks/use-selected-layout-segment/first/slug1'} | ${'first'} | ${'slug1'} + ${'/hooks/use-selected-layout-segment/first/slug2/second/a/b'} | ${'first'} | ${'slug2'} + `( + 'should have the correct layout segment at $path', + async ({ path, outerLayout, innerLayout }) => { + const html = await renderViaHTTP(next.url, path) + const $ = cheerio.load(html) + + expect(JSON.parse($('#outer-layout-segment').text())).toEqual( + outerLayout + ) + expect(JSON.parse($('#inner-layout-segment').text())).toEqual( + innerLayout + ) + } + ) + + it('should return null in pages', async () => { + const html = await renderViaHTTP( + next.url, + '/hooks/use-selected-layout-segment/first/slug2/second/a/b' + ) + const $ = cheerio.load(html) + + expect(JSON.parse($('#page-layout-segment').text())).toEqual(null) + }) + }) }) if (isDev) {