diff --git a/.buildkite/urls/expected_200_urls.txt b/.buildkite/urls/expected_200_urls.txt index 3910cef585..791809064c 100644 --- a/.buildkite/urls/expected_200_urls.txt +++ b/.buildkite/urls/expected_200_urls.txt @@ -59,7 +59,7 @@ /events/racial-difference-in-the-anglophone-caribbean /exhibitions/tranquillity /guides/archives-at-wellcome-collection -/pages/the-history-and-context-of-our-collections +/collections/the-history-and-context-of-our-collections /projects/regarding-forests-touring-exhibition /seasons/on-happiness /series/the-disabled-lockdown-experience @@ -76,7 +76,7 @@ # This is the RawMinds page, which has an ID very similar to the # Schools page, but it shouldn't be redirected. See the router in # the content app, where we enable case-sensitive routing. -/pages/youth-projects +/get-involved/youth-projects # This page won't serve anything especially useful, but we want to # make sure it won't throw a 500 error diff --git a/common/services/prismic/link-resolver.test.ts b/common/services/prismic/link-resolver.test.ts index f95a40094f..280966e1ef 100644 --- a/common/services/prismic/link-resolver.test.ts +++ b/common/services/prismic/link-resolver.test.ts @@ -21,7 +21,7 @@ it('resolves exhibition guides to /guides/exhibitions/{id}', () => { test.each([ { doc: { type: 'articles', uid: '1' }, path: '/articles/1' }, - { doc: { type: 'pages', uid: '1' }, path: '/pages/1' }, + { doc: { type: 'pages', uid: '1' }, path: '/1' }, { doc: { type: 'not a thing', uid: '1' }, path: '/' }, ])('$doc resolves to $path', ({ doc, path }) => { expect(linkResolver(doc)).toBe(path); diff --git a/common/services/prismic/link-resolver.ts b/common/services/prismic/link-resolver.ts index 8cb1cd136f..39c4fc96c5 100644 --- a/common/services/prismic/link-resolver.ts +++ b/common/services/prismic/link-resolver.ts @@ -1,3 +1,5 @@ +import { SiteSection } from '@weco/common/views/components/PageLayout/PageLayout'; + import { isContentType } from './content-types'; type Props = { @@ -13,10 +15,12 @@ type DataProps = { type: string; }; }; + siteSection: SiteSection; }; function linkResolver(doc: Props | DataProps): string { const { uid, type } = doc; + if (!uid) return '/'; if (type === 'webcomics') return `/articles/${uid}`; if (type === 'webcomic-series') return `/series/${uid}`; @@ -41,6 +45,13 @@ function linkResolver(doc: Props | DataProps): string { } } + if (type === 'pages') { + if ('siteSection' in doc) { + return `${doc.siteSection}/${uid}`; + } + return `/${uid}`; + } + if (isContentType(type)) { return `/${type}/${uid}`; } diff --git a/content/webapp/components/EventsSearchResults/index.tsx b/content/webapp/components/EventsSearchResults/index.tsx index ff9f642469..d0b29935c0 100644 --- a/content/webapp/components/EventsSearchResults/index.tsx +++ b/content/webapp/components/EventsSearchResults/index.tsx @@ -91,7 +91,7 @@ const EventsSearchResults: FunctionComponent = ({ events }: Props) => { return ( {croppedImage && ( diff --git a/content/webapp/components/FeaturedCard/FeaturedCard.tsx b/content/webapp/components/FeaturedCard/FeaturedCard.tsx index f60eda78aa..96159926be 100644 --- a/content/webapp/components/FeaturedCard/FeaturedCard.tsx +++ b/content/webapp/components/FeaturedCard/FeaturedCard.tsx @@ -82,7 +82,7 @@ export function convertItemToFeaturedCardProps( }, labels: item.labels, link: { - url: linkResolver({ uid: item.uid, type: item.type }), + url: linkResolver(item), text: item.title, }, }; diff --git a/content/webapp/components/StoriesGrid/index.tsx b/content/webapp/components/StoriesGrid/index.tsx index f1b4c15cff..199bb9f0bc 100644 --- a/content/webapp/components/StoriesGrid/index.tsx +++ b/content/webapp/components/StoriesGrid/index.tsx @@ -123,7 +123,7 @@ const StoriesGrid: FunctionComponent = ({ diff --git a/content/webapp/pages/[uid].tsx b/content/webapp/pages/[uid].tsx index f5b327e003..4e41f8bfa7 100644 --- a/content/webapp/pages/[uid].tsx +++ b/content/webapp/pages/[uid].tsx @@ -12,6 +12,7 @@ export const getServerSideProps: GetServerSideProps< return page.getServerSideProps({ ...context, query: { pageId: context.query.uid }, + params: { siteSection: 'orphan' }, }); }; diff --git a/content/webapp/pages/pages/[pageId].tsx b/content/webapp/pages/pages/[pageId].tsx index cc1de7a86f..ef2493f573 100644 --- a/content/webapp/pages/pages/[pageId].tsx +++ b/content/webapp/pages/pages/[pageId].tsx @@ -111,7 +111,7 @@ export const getServerSideProps: GetServerSideProps< const { pageId } = context.query; const siteSection = toMaybeString(context.params?.siteSection); - if (!looksLikePrismicId(pageId)) { + if (!looksLikePrismicId(pageId) || !siteSection) { return { notFound: true }; } const client = createClient(context); @@ -120,25 +120,15 @@ export const getServerSideProps: GetServerSideProps< ? context.resolvedUrl : undefined; - const pageDocument = await fetchPage(client, pageId); + const pageDocument = await fetchPage( + client, + pageId, + isSiteSection(siteSection) || siteSection === 'orphan' + ? siteSection + : undefined + ); if (isNotUndefined(pageDocument)) { - // If it does not have a tag and the route hasn't specified one either, - // it's an orphan, continue rendering. - const pageTagHasSection = pageDocument.tags.find(t => isSiteSection(t)); - if (siteSection || pageTagHasSection) { - // If it does, it has to be a valid one - const tagIsValidSiteSection = isSiteSection(siteSection); - // and the same one passed by the route page. - const isSameSectionAsRoute = pageDocument.tags.find( - t => t === siteSection - ); - - // otherwise return not found. else, continue rendering. - if (!tagIsValidSiteSection || !isSameSectionAsRoute) - return { notFound: true }; - } - const serverData = await getServerData(context); const page = transformPage(pageDocument); diff --git a/content/webapp/services/prismic/fetch/events.ts b/content/webapp/services/prismic/fetch/events.ts index 67344dbfaa..3be66386c7 100644 --- a/content/webapp/services/prismic/fetch/events.ts +++ b/content/webapp/services/prismic/fetch/events.ts @@ -87,7 +87,7 @@ type FetchEventsQueryParams = { availableOnline?: boolean; page?: number; pageSize?: number; - orderings?: (prismic.Ordering | string)[]; + orderings?: prismic.Ordering[]; }; const startField = 'my.events.times.startDateTime'; diff --git a/content/webapp/services/prismic/fetch/index.ts b/content/webapp/services/prismic/fetch/index.ts index e38a895a6d..1b3ddd774f 100644 --- a/content/webapp/services/prismic/fetch/index.ts +++ b/content/webapp/services/prismic/fetch/index.ts @@ -11,6 +11,7 @@ import { PaginatedResults } from '@weco/common/services/prismic/types'; import { deserialiseDates as deserialiseJsonDates } from '@weco/common/utils/json'; import { toMaybeString } from '@weco/common/utils/routes'; import { isString } from '@weco/common/utils/type-guards'; +import { SiteSection } from '@weco/common/views/components/PageLayout/PageLayout'; export type GetServerSidePropsPrismicClient = { type: 'GetServerSidePropsPrismicClient'; @@ -153,7 +154,10 @@ export function fetcher( getByUid: async ( { client }: GetServerSidePropsPrismicClient, - uid: string + uid: string, + // 'orphan' is only ever used in this context, + // so I'm keeping it separate from SiteSection which is used in other contexts. + siteSection?: SiteSection | 'orphan' ): Promise => { try { const primaryContentType = toMaybeString(contentType); @@ -165,6 +169,9 @@ export function fetcher( uid, { fetchLinks, + filters: siteSection + ? [prismic.filter.any('document.tags', [siteSection])] + : [], } ); diff --git a/content/webapp/services/prismic/fetch/pages.ts b/content/webapp/services/prismic/fetch/pages.ts index 2fba98a9ee..f7c4b5e756 100644 --- a/content/webapp/services/prismic/fetch/pages.ts +++ b/content/webapp/services/prismic/fetch/pages.ts @@ -1,6 +1,7 @@ import * as prismic from '@prismicio/client'; import { PagesDocument as RawPagesDocument } from '@weco/common/prismicio-types'; +import { SiteSection } from '@weco/common/views/components/PageLayout/PageLayout'; import { labelsFields } from '@weco/content/services/prismic/fetch-links'; import { articleFormatsFetchLinks, @@ -59,11 +60,14 @@ const pagesFetcher = fetcher(['pages'], fetchLinks); export const fetchPages = pagesFetcher.getByType; export const fetchPage = async ( client: GetServerSidePropsPrismicClient, - id: string + id: string, + // 'orphan' is only ever used in this context, + // so I'm keeping it separate from SiteSection which is used in other contexts. + siteSection?: SiteSection | 'orphan' ): Promise => { // #11240 once redirects are in place we should only fetch by uid const pageDocument = - (await pagesFetcher.getByUid(client, id)) || + (await pagesFetcher.getByUid(client, id, siteSection)) || (await pagesFetcher.getById(client, id)); return pageDocument; diff --git a/content/webapp/services/prismic/transformers/pages.ts b/content/webapp/services/prismic/transformers/pages.ts index ec932a7654..372669e735 100644 --- a/content/webapp/services/prismic/transformers/pages.ts +++ b/content/webapp/services/prismic/transformers/pages.ts @@ -55,8 +55,6 @@ export function transformPage(document: RawPagesDocument): Page { }) : []; - // TODO (tagging): This is just for now, we will be implementing a proper site tagging - // strategy for this later const siteSections = headerLinks.map(link => link.siteSection); const siteSection = document.tags.find(tag => siteSections.includes(tag as SiteSection) diff --git a/content/webapp/services/prismic/types/index.ts b/content/webapp/services/prismic/types/index.ts index 75c96e0ee0..bc240c3316 100644 --- a/content/webapp/services/prismic/types/index.ts +++ b/content/webapp/services/prismic/types/index.ts @@ -459,7 +459,7 @@ export type StructuredSearchQuery = { tags: string[]; tag: string[]; pageSize: number; - orderings: string[]; + orderings: prismic.Ordering[]; // content type specific 'article-series': string[]; }; diff --git a/content/webapp/types/card.ts b/content/webapp/types/card.ts index f36c9fd01d..e89f3f6e99 100644 --- a/content/webapp/types/card.ts +++ b/content/webapp/types/card.ts @@ -1,5 +1,6 @@ import { getCrop, ImageType } from '@weco/common/model/image'; import linkResolver from '@weco/common/services/prismic/link-resolver'; +import { SiteSection } from '@weco/common/views/components/PageLayout/PageLayout'; import { ArticleBasic } from '@weco/content/types/articles'; import { Book } from '@weco/content/types/books'; import { EventSeries } from '@weco/content/types/event-series'; @@ -26,6 +27,7 @@ export type Card = { image?: ImageType; link?: string; order?: number; + siteSection?: SiteSection; }; export function convertItemToCardProps( @@ -60,13 +62,16 @@ export function convertItemToCardProps( id: item.id, uid: item.uid, type: item.type, + siteSection: 'siteSection' in item ? item.siteSection : undefined, data: { relatedDocument: item.relatedDocument }, } : { id: item.id, uid: item.uid, type: item.type, + siteSection: 'siteSection' in item ? item.siteSection : undefined, }; + return { type: 'card', format: format as never, // TODO: This is now warning for use of any, need to specify type correctly @@ -96,5 +101,6 @@ export function convertItemToCardProps( } : undefined, link: (item.promo && item.promo.link) || linkResolver(linkData), + siteSection: 'siteSection' in item ? item.siteSection : undefined, }; } diff --git a/playwright/test/helpers/contexts.ts b/playwright/test/helpers/contexts.ts index 472c3d0a95..aa6fb04fbf 100644 --- a/playwright/test/helpers/contexts.ts +++ b/playwright/test/helpers/contexts.ts @@ -301,7 +301,7 @@ const mediaOffice = async ( page: Page ): Promise => { await context.addCookies(requiredCookies); - await gotoWithoutCache(`${baseUrl}/pages/WuxrKCIAAP9h3hmw`, page); // alias is /press but it doesn't work locally + await gotoWithoutCache(`${baseUrl}/about-us/media-office`, page); }; const isMobile = (page: Page): boolean => diff --git a/playwright/test/homepage.test.ts b/playwright/test/homepage.test.ts index 3dda945ee0..21c8785d60 100644 --- a/playwright/test/homepage.test.ts +++ b/playwright/test/homepage.test.ts @@ -27,7 +27,7 @@ test('(2) | Cookie banner displays on first visit from anywhere, except the cook await gotoWithoutCache(`${baseUrl}/visit-us`, page); await expect(cookieBanner).toBeAttached(); - await gotoWithoutCache(`${baseUrl}/cookie-policy`, page); + await gotoWithoutCache(`${baseUrl}/about-us/cookie-policy`, page); await expect(cookieBanner).not.toBeAttached(); }); @@ -35,7 +35,7 @@ test('(3) | Cookie banner only displays if CookieControl cookie has not already context, page, }) => { - await gotoWithoutCache(`${baseUrl}/pages/WuxrKCIAAP9h3hmw`, page); + await gotoWithoutCache(`${baseUrl}/about-us/media-office`, page); const cookieBanner = await page.getByLabel('Our website uses cookies'); await expect(cookieBanner).toBeAttached();