diff --git a/packages/core/cms/faststore/content-types.json b/packages/core/cms/faststore/content-types.json index c6b8581ac7..d0ad9d0cc7 100644 --- a/packages/core/cms/faststore/content-types.json +++ b/packages/core/cms/faststore/content-types.json @@ -6,6 +6,20 @@ "configurationSchemaSets": [], "isSingleton": true }, + { + "id": "globalHeaderSections", + "name": "Global Header Sections", + "scopes": ["global"], + "configurationSchemaSets": [], + "isSingleton": true + }, + { + "id": "globalFooterSections", + "name": "Global Footer Sections", + "scopes": ["global"], + "configurationSchemaSets": [], + "isSingleton": true + }, { "id": "landingPage", "name": "Landing Page", diff --git a/packages/core/src/components/cms/GlobalSections.tsx b/packages/core/src/components/cms/GlobalSections.tsx index d2f5411691..c3891fd42c 100644 --- a/packages/core/src/components/cms/GlobalSections.tsx +++ b/packages/core/src/components/cms/GlobalSections.tsx @@ -3,21 +3,24 @@ import storeConfig from 'discovery.config' import { type PageContentType, getPage } from 'src/server/cms' export const GLOBAL_SECTIONS_CONTENT_TYPE = 'globalSections' +export const GLOBAL_SECTIONS_HEADER_CONTENT_TYPE = 'globalHeaderSections' +export const GLOBAL_SECTIONS_FOOTER_CONTENT_TYPE = 'globalFooterSections' export type GlobalSectionsData = { sections: Section[] } -export const getGlobalSectionsData = async ( - previewData: Locator +export const getGlobalSectionsByType = async ( + previewData: Locator, + contentType: string ): Promise => { if (storeConfig.cms.data) { const cmsData = JSON.parse(storeConfig.cms.data) - const page = cmsData[GLOBAL_SECTIONS_CONTENT_TYPE][0] + const page = cmsData[contentType][0] if (page) { const pageData = getPage({ - contentType: GLOBAL_SECTIONS_CONTENT_TYPE, + contentType: contentType, documentId: page.documentId, versionId: page.versionId, }) @@ -27,10 +30,28 @@ export const getGlobalSectionsData = async ( } const pageData = getPage({ - ...(previewData?.contentType === GLOBAL_SECTIONS_CONTENT_TYPE && - previewData), - contentType: GLOBAL_SECTIONS_CONTENT_TYPE, + ...(previewData?.contentType === contentType && previewData), + contentType: contentType, }) return pageData } + +export const getGlobalSectionsData = ( + previewData: Locator +): Promise[] => { + const globalSections = getGlobalSectionsByType( + previewData, + GLOBAL_SECTIONS_CONTENT_TYPE + ) + const globalHeaderSections = getGlobalSectionsByType( + previewData, + GLOBAL_SECTIONS_HEADER_CONTENT_TYPE + ) + const globalFooterSections = getGlobalSectionsByType( + previewData, + GLOBAL_SECTIONS_FOOTER_CONTENT_TYPE + ) + + return [globalSections, globalHeaderSections, globalFooterSections] +} diff --git a/packages/core/src/experimental/searchServerSideFunctions/getServerSideProps.ts b/packages/core/src/experimental/searchServerSideFunctions/getServerSideProps.ts index 092b3e989d..6a7f2d601d 100644 --- a/packages/core/src/experimental/searchServerSideFunctions/getServerSideProps.ts +++ b/packages/core/src/experimental/searchServerSideFunctions/getServerSideProps.ts @@ -1,10 +1,11 @@ import type { GetServerSideProps } from 'next' import type { SearchPageProps } from './getStaticProps' -import { getGlobalSectionsData } from 'src/components/cms/GlobalSections' -import { type SearchContentType, getPage } from 'src/server/cms' import type { Locator } from '@vtex/client-cms' import storeConfig from 'discovery.config' +import { getGlobalSectionsData } from 'src/components/cms/GlobalSections' +import { type SearchContentType, getPage } from 'src/server/cms' +import { injectGlobalSections } from 'src/server/cms/global' export const getServerSideProps: GetServerSideProps< SearchPageProps, @@ -14,26 +15,62 @@ export const getServerSideProps: GetServerSideProps< const { previewData, query, res } = context const searchTerm = (query.q as string)?.split('+').join(' ') - const globalSections = await getGlobalSectionsData(previewData) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) if (storeConfig.cms.data) { const cmsData = JSON.parse(storeConfig.cms.data) const page = cmsData['search'][0] if (page) { - const pageData = await getPage({ - contentType: 'search', - documentId: page.documentId, - versionId: page.versionId, + const [ + pageData, + globalSections, + globalSectionsHeader, + globalSectionsFooter, + ] = await Promise.all([ + getPage({ + contentType: 'search', + documentId: page.documentId, + versionId: page.versionId, + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, }) return { - props: { page: pageData, globalSections, searchTerm }, + props: { + page: pageData, + globalSections: globalSectionsResult, + searchTerm, + }, } } } - const page = await getPage({ - ...(previewData?.contentType === 'search' ? previewData : null), - contentType: 'search', + const [page, globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + getPage({ + ...(previewData?.contentType === 'search' ? previewData : null), + contentType: 'search', + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, }) res.setHeader( @@ -44,7 +81,7 @@ export const getServerSideProps: GetServerSideProps< return { props: { page, - globalSections, + globalSections: globalSectionsResult, searchTerm, }, } diff --git a/packages/core/src/experimental/searchServerSideFunctions/getStaticProps.ts b/packages/core/src/experimental/searchServerSideFunctions/getStaticProps.ts index 193cd565d2..a58a60eee5 100644 --- a/packages/core/src/experimental/searchServerSideFunctions/getStaticProps.ts +++ b/packages/core/src/experimental/searchServerSideFunctions/getStaticProps.ts @@ -1,11 +1,12 @@ +import type { Locator } from '@vtex/client-cms' +import storeConfig from 'discovery.config' import type { GetStaticProps } from 'next' import { getGlobalSectionsData, type GlobalSectionsData, } from 'src/components/cms/GlobalSections' -import { type SearchContentType, getPage } from 'src/server/cms' -import type { Locator } from '@vtex/client-cms' -import storeConfig from 'discovery.config' +import { getPage, type SearchContentType } from 'src/server/cms' +import { injectGlobalSections } from 'src/server/cms/global' export type SearchPageProps = { page: SearchContentType @@ -13,8 +14,8 @@ export type SearchPageProps = { searchTerm?: string } -/* - Depending on the value of the storeConfig.experimental.enableSearchSSR flag, the function used will be getServerSideProps (./getServerSideProps). +/* + Depending on the value of the storeConfig.experimental.enableSearchSSR flag, the function used will be getServerSideProps (./getServerSideProps). Our CLI that does this process of converting from getStaticProps to getServerSideProps. */ export const getStaticProps: GetStaticProps< @@ -24,34 +25,66 @@ export const getStaticProps: GetStaticProps< > = async (context) => { const { previewData } = context - const globalSections = await getGlobalSectionsData(previewData) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) if (storeConfig.cms.data) { const cmsData = JSON.parse(storeConfig.cms.data) const page = cmsData['search'][0] if (page) { - const pageData = await getPage({ - contentType: 'search', - documentId: page.documentId, - versionId: page.versionId, + const [ + pageData, + globalSections, + globalSectionsHeader, + globalSectionsFooter, + ] = await Promise.all([ + getPage({ + contentType: 'search', + documentId: page.documentId, + versionId: page.versionId, + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, }) return { - props: { page: pageData, globalSections }, + props: { page: pageData, globalSections: globalSectionsResult }, } } } - const page = await getPage({ - ...(previewData?.contentType === 'search' ? previewData : null), - contentType: 'search', + const [page, globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + getPage({ + ...(previewData?.contentType === 'search' ? previewData : null), + contentType: 'search', + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, }) return { props: { page, - globalSections, + globalSections: globalSectionsResult, }, } } diff --git a/packages/core/src/pages/404.tsx b/packages/core/src/pages/404.tsx index b2afb5c210..2bed3cffd5 100644 --- a/packages/core/src/pages/404.tsx +++ b/packages/core/src/pages/404.tsx @@ -10,9 +10,10 @@ import { import { default as GLOBAL_COMPONENTS } from 'src/components/cms/global/Components' import RenderSections from 'src/components/cms/RenderSections' import { OverriddenDefaultEmptyState as EmptyState } from 'src/components/sections/EmptyState/OverriddenDefaultEmptyState' -import PLUGINS_COMPONENTS from 'src/plugins' import CUSTOM_COMPONENTS from 'src/customizations/src/components' +import PLUGINS_COMPONENTS from 'src/plugins' import { type PageContentType, getPage } from 'src/server/cms' +import { injectGlobalSections } from 'src/server/cms/global' /* A list of components that can be used in the CMS. */ const COMPONENTS: Record> = { @@ -56,16 +57,31 @@ export const getStaticProps: GetStaticProps< Record, Locator > = async ({ previewData }) => { - const [page, globalSections] = await Promise.all([ - getPage({ - ...(previewData?.contentType === '404' && previewData), - contentType: '404', - }), - getGlobalSectionsData(previewData), - ]) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const [page, globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + getPage({ + ...(previewData?.contentType === '404' && previewData), + contentType: '404', + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) return { - props: { page, globalSections }, + props: { page, globalSections: globalSectionsResult }, } } diff --git a/packages/core/src/pages/500.tsx b/packages/core/src/pages/500.tsx index 55b3453c1e..537cd07ad4 100644 --- a/packages/core/src/pages/500.tsx +++ b/packages/core/src/pages/500.tsx @@ -10,9 +10,10 @@ import { import { default as GLOBAL_COMPONENTS } from 'src/components/cms/global/Components' import RenderSections from 'src/components/cms/RenderSections' import { OverriddenDefaultEmptyState as EmptyState } from 'src/components/sections/EmptyState/OverriddenDefaultEmptyState' -import PLUGINS_COMPONENTS from 'src/plugins' import CUSTOM_COMPONENTS from 'src/customizations/src/components' +import PLUGINS_COMPONENTS from 'src/plugins' import { type PageContentType, getPage } from 'src/server/cms' +import { injectGlobalSections } from 'src/server/cms/global' /* A list of components that can be used in the CMS. */ const COMPONENTS: Record> = { @@ -57,16 +58,31 @@ export const getStaticProps: GetStaticProps< Record, Locator > = async ({ previewData }) => { - const [page, globalSections] = await Promise.all([ - getPage({ - ...(previewData?.contentType === '500' && previewData), - contentType: '500', - }), - getGlobalSectionsData(previewData), - ]) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const [page, globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + getPage({ + ...(previewData?.contentType === '500' && previewData), + contentType: '500', + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) return { - props: { page, globalSections }, + props: { page, globalSections: globalSectionsResult }, } } diff --git a/packages/core/src/pages/[...slug].tsx b/packages/core/src/pages/[...slug].tsx index d1d79effdb..77d5b1018c 100644 --- a/packages/core/src/pages/[...slug].tsx +++ b/packages/core/src/pages/[...slug].tsx @@ -24,6 +24,7 @@ import ProductListingPage, { } from 'src/components/templates/ProductListingPage' import { getRedirect } from 'src/sdk/redirects' import type { PageContentType } from 'src/server/cms' +import { injectGlobalSections } from 'src/server/cms/global' import { getPLP, type PLPContentType } from 'src/server/cms/plp' import { getDynamicContent } from 'src/utils/dynamicContent' @@ -102,23 +103,39 @@ export const getStaticProps: GetStaticProps< const slug = params?.slug.join('/') ?? '' const rewrites = (await storeConfig.rewrites?.()) ?? [] - const [landingPagePromise, globalSectionsPromise] = [ - getLandingPageBySlug(slug, previewData), - getGlobalSectionsData(previewData), - ] + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const landingPagePromise = getLandingPageBySlug(slug, previewData) const landingPage = await landingPagePromise if (landingPage) { - const [serverData, globalSections] = await Promise.all([ + const [ + serverData, + globalSections, + globalSectionsHeader, + globalSectionsFooter, + ] = await Promise.all([ getDynamicContent({ pageType: slug }), globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, ]) + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) + return { props: { page: landingPage, - globalSections, + globalSections: globalSectionsResult, type: 'page', slug, serverData, @@ -126,7 +143,13 @@ export const getStaticProps: GetStaticProps< } } - const [{ data, errors = [] }, cmsPage, globalSections] = await Promise.all([ + const [ + { data, errors = [] }, + cmsPage, + globalSections, + globalSectionsHeader, + globalSectionsFooter, + ] = await Promise.all([ execute< ServerCollectionPageQueryQueryVariables, ServerCollectionPageQueryQuery @@ -136,6 +159,8 @@ export const getStaticProps: GetStaticProps< }), getPLP(slug, previewData, rewrites), globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, ]) const notFound = errors.find(isNotFoundError) @@ -161,11 +186,17 @@ export const getStaticProps: GetStaticProps< throw errors[0] } + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) + return { props: { data, page: cmsPage, - globalSections, + globalSections: globalSectionsResult, type: 'plp', key: slug, }, diff --git a/packages/core/src/pages/[slug]/p.tsx b/packages/core/src/pages/[slug]/p.tsx index 3769f8db36..70a8cd929c 100644 --- a/packages/core/src/pages/[slug]/p.tsx +++ b/packages/core/src/pages/[slug]/p.tsx @@ -35,6 +35,7 @@ import { import { getOfferUrl, useOffer } from 'src/sdk/offer' import PageProvider, { type PDPContext } from 'src/sdk/overrides/PageProvider' import { useProductQuery } from 'src/sdk/product/useProductQuery' +import { injectGlobalSections } from 'src/server/cms/global' import { getPDP, type PDPContentType } from 'src/server/cms/pdp' type StoreConfig = typeof storeConfig & { @@ -260,12 +261,26 @@ export const getStaticProps: GetStaticProps< Locator > = async ({ params, previewData }) => { const slug = params?.slug ?? '' - const [searchResult, globalSections] = await Promise.all([ + + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const [ + searchResult, + globalSections, + globalSectionsHeader, + globalSectionsFooter, + ] = await Promise.all([ execute({ variables: { locator: [{ key: 'slug', value: slug }] }, operation: query, }), - getGlobalSectionsData(previewData), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, ]) const { data, errors = [] } = searchResult @@ -305,13 +320,19 @@ export const getStaticProps: GetStaticProps< url: canonical, } + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) + return { props: { data, ...cmsPage, meta, offers, - globalSections, + globalSections: globalSectionsResult, key: seo.canonical, }, revalidate: (storeConfig as StoreConfig).experimental.revalidate ?? false, diff --git a/packages/core/src/pages/account.tsx b/packages/core/src/pages/account.tsx index 122d1d9a35..1f98def2bf 100644 --- a/packages/core/src/pages/account.tsx +++ b/packages/core/src/pages/account.tsx @@ -10,6 +10,7 @@ import { import RenderSections from 'src/components/cms/RenderSections' import { default as GLOBAL_COMPONENTS } from 'src/components/cms/global/Components' import CUSTOM_COMPONENTS from 'src/customizations/src/components' +import { injectGlobalSections } from 'src/server/cms/global' import storeConfig from '../../discovery.config' type Props = { @@ -44,10 +45,27 @@ export const getStaticProps: GetStaticProps< Record, Locator > = async ({ previewData }) => { - const globalSections = await getGlobalSectionsData(previewData) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const [globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) return { - props: { globalSections }, + props: { globalSections: globalSectionsResult }, } } diff --git a/packages/core/src/pages/checkout.tsx b/packages/core/src/pages/checkout.tsx index 08f1be26d1..53e7bb1de2 100644 --- a/packages/core/src/pages/checkout.tsx +++ b/packages/core/src/pages/checkout.tsx @@ -12,6 +12,7 @@ import { import CUSTOM_COMPONENTS from 'src/customizations/src/components' import RenderSections from 'src/components/cms/RenderSections' +import { injectGlobalSections } from 'src/server/cms/global' import storeConfig from '../../discovery.config' type Props = { @@ -46,10 +47,27 @@ export const getStaticProps: GetStaticProps< Record, Locator > = async ({ previewData }) => { - const globalSections = await getGlobalSectionsData(previewData) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const [globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) return { - props: { globalSections }, + props: { globalSections: globalSectionsResult }, } } diff --git a/packages/core/src/pages/index.tsx b/packages/core/src/pages/index.tsx index 6c7d85d98a..701e065fe0 100644 --- a/packages/core/src/pages/index.tsx +++ b/packages/core/src/pages/index.tsx @@ -12,6 +12,7 @@ import { } from 'src/components/cms/GlobalSections' import COMPONENTS from 'src/components/cms/home/Components' import PageProvider from 'src/sdk/overrides/PageProvider' +import { injectGlobalSections } from 'src/server/cms/global' import { getDynamicContent } from 'src/utils/dynamicContent' import storeConfig from '../../discovery.config' @@ -147,7 +148,12 @@ export const getStaticProps: GetStaticProps< Record, Locator > = async ({ previewData }) => { - const globalSectionsPromise = getGlobalSectionsData(previewData) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + const serverDataPromise = getDynamicContent({ pageType: 'home' }) let cmsPage = null @@ -165,14 +171,29 @@ export const getStaticProps: GetStaticProps< ...(previewData?.contentType === 'home' && previewData), contentType: 'home', }) - const [page, globalSections, serverData] = await Promise.all([ + + const [ + page, + globalSections, + globalSectionsHeader, + globalSectionsFooter, + serverData, + ] = await Promise.all([ pagePromise, globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, serverDataPromise, ]) + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) + return { - props: { page, globalSections, serverData }, + props: { page, globalSections: globalSectionsResult, serverData }, } } diff --git a/packages/core/src/pages/login.tsx b/packages/core/src/pages/login.tsx index 961130543e..68d5d08bc4 100644 --- a/packages/core/src/pages/login.tsx +++ b/packages/core/src/pages/login.tsx @@ -11,9 +11,10 @@ import { } from 'src/components/cms/GlobalSections' import RenderSections from 'src/components/cms/RenderSections' import { OverriddenDefaultEmptyState as EmptyState } from 'src/components/sections/EmptyState/OverriddenDefaultEmptyState' -import PLUGINS_COMPONENTS from 'src/plugins' import CUSTOM_COMPONENTS from 'src/customizations/src/components' +import PLUGINS_COMPONENTS from 'src/plugins' import { type PageContentType, getPage } from 'src/server/cms' +import { injectGlobalSections } from 'src/server/cms/global' import storeConfig from '../../discovery.config' /* A list of components that can be used in the CMS. */ @@ -69,16 +70,31 @@ export const getStaticProps: GetStaticProps< Record, Locator > = async ({ previewData }) => { - const [page, globalSections] = await Promise.all([ - getPage({ - ...(previewData?.contentType === 'login' && previewData), - contentType: 'login', - }), - getGlobalSectionsData(previewData), - ]) + const [ + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ] = getGlobalSectionsData(previewData) + + const [page, globalSections, globalSectionsHeader, globalSectionsFooter] = + await Promise.all([ + getPage({ + ...(previewData?.contentType === 'login' && previewData), + contentType: 'login', + }), + globalSectionsPromise, + globalSectionsHeaderPromise, + globalSectionsFooterPromise, + ]) + + const globalSectionsResult = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) return { - props: { page, globalSections }, + props: { page, globalSections: globalSectionsResult }, } } diff --git a/packages/core/src/sdk/error/ChildrenSectionNotFoundError/ChildrenSectionNotFoundError.ts b/packages/core/src/sdk/error/ChildrenSectionNotFoundError/ChildrenSectionNotFoundError.ts new file mode 100644 index 0000000000..eddb46c952 --- /dev/null +++ b/packages/core/src/sdk/error/ChildrenSectionNotFoundError/ChildrenSectionNotFoundError.ts @@ -0,0 +1,6 @@ +export default class ChildrenSectionNotFoundError extends Error { + constructor(message: string) { + super(message) + this.name = 'ChildrenSectionNotFoundError' + } +} diff --git a/packages/core/src/sdk/error/ChildrenSectionNotFoundError/index.ts b/packages/core/src/sdk/error/ChildrenSectionNotFoundError/index.ts new file mode 100644 index 0000000000..96eb3defbb --- /dev/null +++ b/packages/core/src/sdk/error/ChildrenSectionNotFoundError/index.ts @@ -0,0 +1 @@ +export { default } from './ChildrenSectionNotFoundError' diff --git a/packages/core/src/server/cms/global.ts b/packages/core/src/server/cms/global.ts new file mode 100644 index 0000000000..ea71b81436 --- /dev/null +++ b/packages/core/src/server/cms/global.ts @@ -0,0 +1,38 @@ +import type { GlobalSectionsData } from 'src/components/cms/GlobalSections' +import ChildrenSectionNotFoundError from 'src/sdk/error/ChildrenSectionNotFoundError' + +type InjectGlobalSectionsProps = { + globalSections: GlobalSectionsData + globalSectionsHeader: GlobalSectionsData + globalSectionsFooter: GlobalSectionsData +} + +export function injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, +}: InjectGlobalSectionsProps) { + const childrenIndex = globalSections.sections.findIndex( + (section) => section.name === 'Children' + ) + + if (childrenIndex === -1) { + throw new ChildrenSectionNotFoundError( + 'Children section in Global Sections content type was not found. Please add a Children Section.' + ) + } + + const headerSections = globalSectionsHeader?.sections || [] + const footerSections = globalSectionsFooter?.sections || [] + + return { + ...globalSections, + sections: [ + ...globalSections.sections.slice(0, childrenIndex), + ...headerSections, + globalSections.sections[childrenIndex], + ...footerSections, + ...globalSections.sections.slice(childrenIndex + 1), + ], + } +} diff --git a/packages/core/src/server/cms/index.ts b/packages/core/src/server/cms/index.ts index 87cb6a907e..a6d91130eb 100644 --- a/packages/core/src/server/cms/index.ts +++ b/packages/core/src/server/cms/index.ts @@ -122,7 +122,7 @@ export const getPage = async (options: Options) => { throw new MissingContentError(options) } - if (pages.length !== 1) { + if (pages.length > 1) { throw new MultipleContentError(options) } diff --git a/packages/core/test/server/cms/global.test.ts b/packages/core/test/server/cms/global.test.ts new file mode 100644 index 0000000000..d28eabd730 --- /dev/null +++ b/packages/core/test/server/cms/global.test.ts @@ -0,0 +1,86 @@ +import type { GlobalSectionsData } from '../../../src/components/cms/GlobalSections' +import { injectGlobalSections } from '../../../src/server/cms/global' + +describe('hCMS - Multiple Global Section content type', () => { + describe('injectGlobalSections', () => { + it('should inject globalSectionsHeader before Children and globalSectionsFooter after', () => { + const globalSections: GlobalSectionsData = { + sections: [ + { name: 'Navbar', data: {} }, + { name: 'Children', data: {} }, + { name: 'Footer', data: {} }, + ], + } + + const globalSectionsHeader: GlobalSectionsData = { + sections: [{ name: 'ExtraHeader', data: {} }], + } + + const globalSectionsFooter: GlobalSectionsData = { + sections: [{ name: 'ExtraFooter', data: {} }], + } + + const result = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) + + expect(result.sections).toEqual([ + { name: 'Navbar', data: {} }, // Before Children + { name: 'ExtraHeader', data: {} }, // Injected header before Children + { name: 'Children', data: {} }, // Children + { name: 'ExtraFooter', data: {} }, // Injected footer after Children + { name: 'Footer', data: {} }, // Remaining sections + ]) + }) + + it('should maintain order if globalSectionsHeader or globalSectionsFooter are empty', () => { + const globalSections: GlobalSectionsData = { + sections: [ + { name: 'Navbar', data: {} }, + { name: 'Children', data: {} }, + { name: 'Footer', data: {} }, + ], + } + + const globalSectionsHeader: GlobalSectionsData = { + ...globalSections, + sections: [], + } + const globalSectionsFooter: GlobalSectionsData = { + ...globalSections, + sections: [], + } + + const result = injectGlobalSections({ + globalSections, + globalSectionsHeader, + globalSectionsFooter, + }) + + expect(result.sections).toEqual(globalSections.sections) + }) + + it('should throw an error when the "Children" section is missing', () => { + const globalSections: GlobalSectionsData = { + sections: [ + { name: 'Navbar', data: {} }, + { name: 'Footer', data: {} }, + ], + } + + expect(() => + injectGlobalSections({ + globalSections, + globalSectionsHeader: { + sections: [], + }, + globalSectionsFooter: { + sections: [], + }, + }) + ).toThrow(Error) + }) + }) +})