diff --git a/src/app/components/shell/router-helpers/page-routes.tsx b/src/app/components/shell/router-helpers/page-routes.tsx index 09691a6d7..a387e8f0a 100644 --- a/src/app/components/shell/router-helpers/page-routes.tsx +++ b/src/app/components/shell/router-helpers/page-routes.tsx @@ -10,6 +10,7 @@ import {assertDefined} from '~/helpers/data'; import {GeneralPageFromSlug} from '~/pages/general/general'; import {ImportedPage} from './page-loaders'; import {RouteAsPortalOrNot} from './portal-page-routes'; +import Error404 from '~/pages/404/404'; type PageData = FlexPageData & { meta: {slug: string}; @@ -53,13 +54,14 @@ export function DetailsRoutes() { ); } +// eslint-disable-next-line complexity export function OtherPageRoutes() { const dir = assertDefined(useParams().dir); const {'*': path} = useParams(); const {layoutParameters, setLayoutParameters} = useLayoutContext(); if (['books', 'textbooks'].includes(dir)) { - return ( + return path === '' ? : ( - loadable({ - loader: () => - loaders[ - layoutParameters.name as Exclude - ](), - loading: LoadingPlaceholder - }), - [layoutParameters.name] - ); const Layout = React.useCallback( ({children}: React.PropsWithChildren) => { + const layoutParameterName = layoutParameters.name; + + if (layoutParameterName === null) { + return
{children}
; + } + const LoadableLayout = loadable({ + loader: () => loaders[layoutParameterName](), + loading: LoadingPlaceholder + }); + // Avoids initial flash default -> landing - return layoutParameters.name === null ? ( -
{children}
- ) : ( + return ( {children} ); }, - [LoadableLayout, layoutParameters.data, layoutParameters.name] + [layoutParameters.data, layoutParameters.name] ); return {Layout, setLayoutParameters: updateIfNotEqual, layoutParameters}; diff --git a/src/app/pages/flex-page/flex-page.tsx b/src/app/pages/flex-page/flex-page.tsx index b375234d2..2b7025648 100644 --- a/src/app/pages/flex-page/flex-page.tsx +++ b/src/app/pages/flex-page/flex-page.tsx @@ -6,22 +6,32 @@ import './flex-page.scss'; export type FlexPageData = { meta?: {type: string}; - layout: [{type: LayoutName}]; + layout: [{type: LayoutName}?]; body: ContentBlockConfig[]; }; -export const isFlexPage = (data?: {meta?: FlexPageData['meta']}) => ( +export const isFlexPage = (data?: {meta?: FlexPageData['meta']}) => typeof data?.meta?.type === 'string' && - ['pages.FlexPage', 'pages.RootPage'].includes(data.meta.type) -); + ['pages.FlexPage', 'pages.RootPage'].includes(data.meta.type); function FlexPageBody({data}: {data: FlexPageData}) { return ; } -export function LayoutUsingData({data, children}: {data: FlexPageData, children: React.ReactNode}) { +function warnAndUseDefault() { + console.warn('No layout set for page'); + return 'default' as LayoutName; +} + +export function LayoutUsingData({ + data, + children +}: { + data: FlexPageData; + children: React.ReactNode; +}) { const {layoutParameters, setLayoutParameters} = useLayoutContext(); - const layoutName = data.layout[0]?.type; + const layoutName = data.layout[0]?.type || warnAndUseDefault(); if (layoutParameters.name !== layoutName) { setLayoutParameters({ diff --git a/test/src/components/shell.test.tsx b/test/src/components/shell.test.tsx index 29f734a67..821bf48d6 100644 --- a/test/src/components/shell.test.tsx +++ b/test/src/components/shell.test.tsx @@ -213,6 +213,14 @@ describe('shell', () => { await waitFor(() => expect(setPortal).toHaveBeenCalledWith('landing-page')); }); + it('routes books routes to portal/books', async () => { + setPortalPrefix('portal'); + BrowserRouter.mockImplementationOnce(({children}) => ( + {children} + )); + render(AppElement); + // Nothing can really be checked; code coverage in rex-portal + }); it('renders nothing when data is null', async () => { setPortalPrefix('/landing-page'); diff --git a/test/src/pages/details/common/featured-resources.test.tsx b/test/src/pages/details/common/featured-resources.test.tsx index 2cfc9a7b6..e229cd030 100644 --- a/test/src/pages/details/common/featured-resources.test.tsx +++ b/test/src/pages/details/common/featured-resources.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {render, screen} from '@testing-library/preact'; import FeaturedResourcesSection from '~/pages/details/common/featured-resources/featured-resources'; import ShellContextProvider from '../../../../helpers/shell-context'; -import {MemoryRouter} from 'react-router-dom'; +import MemoryRouter from '~/../../test/helpers/future-memory-router'; import userEvent from '@testing-library/user-event'; import {ResourceModel} from '~/pages/details/common/resource-box/resource-boxes'; diff --git a/test/src/pages/flex-page.test.tsx b/test/src/pages/flex-page.test.tsx index 518806a64..9abfc1a98 100644 --- a/test/src/pages/flex-page.test.tsx +++ b/test/src/pages/flex-page.test.tsx @@ -4,10 +4,11 @@ import {render, screen} from '@testing-library/preact'; import {describe, it} from '@jest/globals'; import userEvent from '@testing-library/user-event'; import MemoryRouter from '~/../../test/helpers/future-memory-router'; -import FlexPage from '~/pages/flex-page/flex-page'; +import FlexPage, {LayoutUsingData} from '~/pages/flex-page/flex-page'; import {CTALinkFields} from '~/pages/flex-page/blocks/CTABlock'; import {ContentBlockConfig} from '~/pages/flex-page/blocks/ContentBlock'; import {HeroBlockConfig} from '~/pages/flex-page/blocks/HeroBlock'; +import { LayoutContextProvider } from '~/contexts/layout'; const emptyTarget = { type: '', @@ -66,6 +67,25 @@ describe('flex-page', () => { render(); expect(screen.getAllByRole('img')).toHaveLength(1); }); + it('warns and renders with default when layout is not provided', () => { + const saveWarn = console.warn; + + console.warn = jest.fn(); + body = [heroBlock()]; + render( + + + + + content + + + + + ); + expect(console.warn).toHaveBeenCalledWith('No layout set for page'); + console.warn = saveWarn; + }); it('renders heroBlock with top image alignment', () => { const modBlock = heroBlock();