diff --git a/packages/optimizely-cms-sdk/src/infer.ts b/packages/optimizely-cms-sdk/src/infer.ts index 1f0c8d13..042b5109 100644 --- a/packages/optimizely-cms-sdk/src/infer.ts +++ b/packages/optimizely-cms-sdk/src/infer.ts @@ -33,6 +33,7 @@ import { PublicRawFileAsset, PublicVideoAsset, } from './model/assets.js'; +import { DisplayTemplate } from './model/displayTemplates.js'; /** Forces Intellisense to resolve types */ export type Prettify = { @@ -200,9 +201,25 @@ type InferFromContentType = Prettify< InferredBase & InferProps & InferExperience & InferSection >; +/** Infers the TypeScript type for a display setting */ +type InferFromDisplayTemplate = + T extends { settings: infer S } + ? { + [K in keyof S]: + S[K] extends { choices: Record; editor: infer E } + ? E extends 'select' + ? keyof S[K]['choices'] + : E extends 'checkbox' + ? 'true' | 'false' + : never + : never; + } + : {}; + /** Infers the Graph response types of `T`. `T` can be a content type or a property */ // prettier-ignore export type Infer = - T extends AnyContentType ? InferFromContentType -: T extends AnyProperty ? InferFromProperty -: unknown; + T extends DisplayTemplate ? InferFromDisplayTemplate + : T extends AnyContentType ? InferFromContentType + : T extends AnyProperty ? InferFromProperty + : unknown; diff --git a/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts b/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts index 129c548d..8cbf2782 100644 --- a/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts +++ b/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts @@ -33,4 +33,4 @@ describe('parseDisplaySettings', () => { const result = parseDisplaySettings(input); expect(result).toEqual(undefined); }); -}); +}); \ No newline at end of file diff --git a/packages/optimizely-cms-sdk/src/model/displayTemplates.ts b/packages/optimizely-cms-sdk/src/model/displayTemplates.ts index b7126954..e15a5e83 100644 --- a/packages/optimizely-cms-sdk/src/model/displayTemplates.ts +++ b/packages/optimizely-cms-sdk/src/model/displayTemplates.ts @@ -1,4 +1,4 @@ -import { DisplaySettingsType } from '../infer.js'; +import { DisplaySettingsType, Infer } from '../infer.js'; import { BaseTypes } from './contentTypes.js'; // section node types @@ -64,17 +64,17 @@ export type DisplayTemplate = T & { export function parseDisplaySettings( displaySettings?: DisplaySettingsType[] | null -): Record | undefined { +): Record | undefined { if (!displaySettings) { return undefined; // Return undefined if displaySettings is not provided } - const result: Record = {}; // Initialize an empty object + const result: Record = {}; // Initialize an empty object // Iterate over the input array - for (const item of displaySettings) { + for (const { key, value } of displaySettings) { // Assign the value to the key in the result object - result[item.key] = item.value; + result[key] = value === 'true' ? true : value === 'false' ? false : value; } return result; diff --git a/packages/optimizely-cms-sdk/src/model/index.ts b/packages/optimizely-cms-sdk/src/model/index.ts index a2a83944..2c2f86ab 100644 --- a/packages/optimizely-cms-sdk/src/model/index.ts +++ b/packages/optimizely-cms-sdk/src/model/index.ts @@ -12,7 +12,7 @@ export function contentType( /** Defines a Optimizely CMS display template */ export function displayTemplate( options: T -): T & { __type: 'displayTemplate' } { +): DisplayTemplate { return { ...options, __type: 'displayTemplate' }; } diff --git a/packages/optimizely-cms-sdk/src/react/server.tsx b/packages/optimizely-cms-sdk/src/react/server.tsx index bb64a54e..3a7c1513 100644 --- a/packages/optimizely-cms-sdk/src/react/server.tsx +++ b/packages/optimizely-cms-sdk/src/react/server.tsx @@ -11,10 +11,11 @@ import { DisplaySettingsType, ExperienceCompositionNode, InferredContentReference, + Infer, } from '../infer.js'; import { isComponentNode } from '../util/baseTypeUtil.js'; import { parseDisplaySettings } from '../model/displayTemplates.js'; -import { getDisplayTemplateTag } from '../model/displayTemplateRegistry.js'; +import { getDisplayTemplate, getDisplayTemplateTag } from '../model/displayTemplateRegistry.js'; import { isDev } from '../util/environment.js'; import { appendToken } from '../util/preview.js'; @@ -89,7 +90,7 @@ type OptimizelyComponentProps = { __composition?: ExperienceCompositionNode; }; - displaySettings?: Record; + displaySettings?: Record; }; export async function OptimizelyComponent({ @@ -127,12 +128,12 @@ export type StructureContainerProps = { node: ExperienceStructureNode; children: React.ReactNode; index?: number; - displaySettings?: Record; + displaySettings?: Record; }; export type ComponentContainerProps = { node: ExperienceComponentNode; children: React.ReactNode; - displaySettings?: Record; + displaySettings?: Record; }; export type StructureContainer = ( props: StructureContainerProps @@ -150,7 +151,13 @@ export function OptimizelyExperience({ }) { return nodes.map((node) => { const tag = getDisplayTemplateTag(node.displayTemplateKey); - const parsedDisplaySettings = parseDisplaySettings(node.displaySettings); + const template = node.displayTemplateKey + ? getDisplayTemplate(node.displayTemplateKey) + : null; + + const parsedDisplaySettings = template + ? parseDisplaySettings(node.displaySettings) + : undefined; if (isComponentNode(node)) { const Wrapper = ComponentWrapper ?? React.Fragment; @@ -251,7 +258,13 @@ export function OptimizelyGridSection({ return nodes.map((node, i) => { const tag = getDisplayTemplateTag(node.displayTemplateKey); - const parsedDisplaySettings = parseDisplaySettings(node.displaySettings); + const template = node.displayTemplateKey + ? getDisplayTemplate(node.displayTemplateKey) + : null; + + const parsedDisplaySettings = template + ? parseDisplaySettings(node.displaySettings) + : undefined; if (isComponentNode(node)) { return ( diff --git a/samples/nextjs-template/src/components/Article.tsx b/samples/nextjs-template/src/components/Article.tsx index 734136a0..c83cda72 100644 --- a/samples/nextjs-template/src/components/Article.tsx +++ b/samples/nextjs-template/src/components/Article.tsx @@ -1,4 +1,4 @@ -import { contentType, Infer } from '@optimizely/cms-sdk'; +import { contentType, displayTemplate, Infer } from '@optimizely/cms-sdk'; import { getPreviewUtils } from '@optimizely/cms-sdk/react/server'; export const ArticleContentType = contentType({ @@ -21,17 +21,37 @@ export const ArticleContentType = contentType({ }, }); +export const TeaserDisplayTemplate = displayTemplate({ + key: 'TeaserDisplayTemplate', + displayName: 'TeaserDisplayTemplate', + isDefault: false, + baseType: '_component', + settings: { + orientation: { + editor: 'select', + displayName: 'Teaser Orientation', + sortOrder: 0, + choices: { + vertical: { displayName: 'Vertical', sortOrder: 1 }, + horizontal: { displayName: 'Horizontal', sortOrder: 2 }, + }, + }, + }, +}); + type Props = { opti: Infer; + displaySettings?: Infer; }; -export default function Article({ opti }: Props) { +export default function Article({ opti, displaySettings }: Props) { const { pa } = getPreviewUtils(opti); return (

{opti.heading}

{opti.subtitle}

+

{displaySettings?.orientation}

); } +