From 4b32230ab70e58a7bb19b62242fa7ef029e7040e Mon Sep 17 00:00:00 2001 From: Thomas Kellermeier Date: Thu, 20 Feb 2025 10:04:40 +0100 Subject: [PATCH 1/5] test: remove outdated non functional test --- .../src/utils/withComponentWrapper.spec.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/experience-builder-sdk/src/utils/withComponentWrapper.spec.tsx b/packages/experience-builder-sdk/src/utils/withComponentWrapper.spec.tsx index b3eece12e..3df41cf19 100644 --- a/packages/experience-builder-sdk/src/utils/withComponentWrapper.spec.tsx +++ b/packages/experience-builder-sdk/src/utils/withComponentWrapper.spec.tsx @@ -15,17 +15,6 @@ describe('withComponentWrapper', () => { describe('when component is wrapped', () => { const WrappedButton = withComponentWrapper(MyButton); - it('can wrap a component with a custom tag', () => { - const WrappedButtonSpan = withComponentWrapper(MyButton, { wrapContainerTag: 'span' }); - - const { container } = render( - Click me, - ); - - expect(container.firstChild).toHaveClass('my-span'); - expect(container.firstChild).toHaveTextContent('Click me'); - }); - it('classes get added to the correct elements', () => { const { container, getByRole } = render( Date: Thu, 20 Feb 2025 14:06:08 +0100 Subject: [PATCH 2/5] feat: only provide isInExpEditorMode if activated in options [SPA-2607] (#1008) * feat: only provide isInExpEditorMode if activated in options * fix: undo brownout of old options * fix: adjust test for isInExpEditorMode --- packages/core/src/types.ts | 5 +++++ .../src/hooks/useComponentProps.spec.ts | 20 +++++++++++++++++-- .../src/hooks/useComponentProps.ts | 4 +++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 908447984..291b261e5 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -121,6 +121,11 @@ export type ComponentRegistration = { component: React.ElementType; definition: ComponentDefinition; options?: { + /** + * If true, the component receives the optional boolean property `isInExpEditorMode` to + * render different content between editor and delivery mode. + */ + enableCustomEditorView?: boolean; wrapComponent?: boolean; /** @deprecated use wrapContainer instead */ wrapContainerTag?: keyof JSX.IntrinsicElements; diff --git a/packages/visual-editor/src/hooks/useComponentProps.spec.ts b/packages/visual-editor/src/hooks/useComponentProps.spec.ts index 602ad24e4..99a9f6ca8 100644 --- a/packages/visual-editor/src/hooks/useComponentProps.spec.ts +++ b/packages/visual-editor/src/hooks/useComponentProps.spec.ts @@ -324,7 +324,7 @@ describe('useComponentProps', () => { }, ); - it('should return isInExpEditorMode as true for custom components', () => { + it('should return isInExpEditorMode as true for custom components when flag is enabled', () => { const { result } = renderHook(() => useComponentProps({ node, @@ -333,13 +333,29 @@ describe('useComponentProps', () => { renderDropzone, definition, userIsDragging, - options: { wrapComponent: false }, + options: { wrapComponent: false, enableCustomEditorView: true }, }), ); expect(result.current.componentProps.isInExpEditorMode).toBe(true); }); + it('should not return isInExpEditorMode when the flag is not enabled', () => { + const { result } = renderHook(() => + useComponentProps({ + node, + areEntitiesFetched, + resolveDesignValue, + renderDropzone, + definition, + userIsDragging, + options: { wrapComponent: false }, + }), + ); + + expect(result.current.componentProps.isInExpEditorMode).toBeUndefined(); + }); + it('should return unbound values in componentProps for structural components', () => { vi.mock('@/store/editor', () => ({ useEditorStore: () => ({ diff --git a/packages/visual-editor/src/hooks/useComponentProps.ts b/packages/visual-editor/src/hooks/useComponentProps.ts index 051dcd40a..eb34f0cbf 100644 --- a/packages/visual-editor/src/hooks/useComponentProps.ts +++ b/packages/visual-editor/src/hooks/useComponentProps.ts @@ -314,7 +314,9 @@ export const useComponentProps = ({ const customComponentProps: ResolvedComponentProps = { ...sharedProps, - isInExpEditorMode: true, + // Allows custom components to render differently in the editor. This needs to be activated + // through options as the component has to be aware of this prop to not cause any React warnings. + ...(options?.enableCustomEditorView ? { isInExpEditorMode: true } : {}), ...sanitizeNodeProps(props), }; From dc814775819f00e6f81f04c28d088f9ec47318c8 Mon Sep 17 00:00:00 2001 From: Chaoste Date: Thu, 20 Feb 2025 15:56:25 +0000 Subject: [PATCH 3/5] chore(release): updated release notes and package versions [ci skip] --- lerna.json | 2 +- package-lock.json | 12 ++++++------ packages/components/package.json | 2 +- packages/core/package.json | 2 +- .../package.json | 2 +- packages/experience-builder-sdk/package.json | 2 +- packages/validators/package.json | 2 +- packages/visual-editor/package.json | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lerna.json b/lerna.json index bdf21b282..225927b57 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "npmClient": "npm", - "version": "1.31.1", + "version": "1.32.0-beta.0", "command": { "version": { "allowBranch": ["main", "next", "development"], diff --git a/package-lock.json b/package-lock.json index 7b639d998..b8d2418ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44122,7 +44122,7 @@ }, "packages/components": { "name": "@contentful/experiences-components-react", - "version": "1.31.1", + "version": "1.32.0-beta.0", "license": "MIT", "dependencies": { "@contentful/experiences-core": "file:../core", @@ -44212,7 +44212,7 @@ }, "packages/core": { "name": "@contentful/experiences-core", - "version": "1.31.1", + "version": "1.32.0-beta.0", "license": "MIT", "dependencies": { "@contentful/experiences-validators": "file:../validators", @@ -44302,7 +44302,7 @@ } }, "packages/create-contentful-studio-experiences": { - "version": "1.31.1", + "version": "1.32.0-beta.0", "license": "MIT", "dependencies": { "@clack/prompts": "^0.7.0", @@ -44392,7 +44392,7 @@ }, "packages/experience-builder-sdk": { "name": "@contentful/experiences-sdk-react", - "version": "1.31.1", + "version": "1.32.0-beta.0", "license": "MIT", "dependencies": { "@contentful/experiences-components-react": "file:../components", @@ -45162,7 +45162,7 @@ }, "packages/validators": { "name": "@contentful/experiences-validators", - "version": "1.31.1", + "version": "1.32.0-beta.0", "dependencies": { "zod": "^3.22.4" }, @@ -45175,7 +45175,7 @@ }, "packages/visual-editor": { "name": "@contentful/experiences-visual-editor-react", - "version": "1.31.1", + "version": "1.32.0-beta.0", "dependencies": { "@contentful/experiences-components-react": "file:../components", "@contentful/experiences-core": "file:../core", diff --git a/packages/components/package.json b/packages/components/package.json index 3926aef2e..31c33b692 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-components-react", - "version": "1.31.1", + "version": "1.32.0-beta.0", "description": "A basic set of components to use with Studio Experiences", "homepage": "https://github.com/contentful/experience-builder/tree/next/packages/components#readme", "repository": { diff --git a/packages/core/package.json b/packages/core/package.json index b47ad9312..bcd4505be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-core", - "version": "1.31.1", + "version": "1.32.0-beta.0", "description": "", "main": "dist/index.js", "module": "dist/index.js", diff --git a/packages/create-contentful-studio-experiences/package.json b/packages/create-contentful-studio-experiences/package.json index dfe0d0e04..6eef968e0 100644 --- a/packages/create-contentful-studio-experiences/package.json +++ b/packages/create-contentful-studio-experiences/package.json @@ -1,6 +1,6 @@ { "name": "create-contentful-studio-experiences", - "version": "1.31.1", + "version": "1.32.0-beta.0", "description": "A CLI tool to get up and running with Contentful Studio Experiences quickly", "homepage": "https://github.com/contentful/experience-builder/tree/next/packages/create-contentful-studio-experiences#readme", "repository": { diff --git a/packages/experience-builder-sdk/package.json b/packages/experience-builder-sdk/package.json index 6394634e5..d3e32885a 100644 --- a/packages/experience-builder-sdk/package.json +++ b/packages/experience-builder-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-sdk-react", - "version": "1.31.1", + "version": "1.32.0-beta.0", "main": "./dist/index.js", "module": "./dist/index.js", "typings": "./dist/src/index.d.ts", diff --git a/packages/validators/package.json b/packages/validators/package.json index d1991feaf..fcd2afce1 100644 --- a/packages/validators/package.json +++ b/packages/validators/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-validators", - "version": "1.31.1", + "version": "1.32.0-beta.0", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/visual-editor/package.json b/packages/visual-editor/package.json index 2122577b3..858b2b2a7 100644 --- a/packages/visual-editor/package.json +++ b/packages/visual-editor/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-visual-editor-react", - "version": "1.31.1", + "version": "1.32.0-beta.0", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", From 36a479f782ec0bf0ede6cc7d587594c9e1e0656a Mon Sep 17 00:00:00 2001 From: Chaoste Date: Thu, 20 Feb 2025 16:22:58 +0000 Subject: [PATCH 4/5] chore(release): updated release notes and package versions [ci skip] --- CHANGELOG.md | 4 ++++ lerna.json | 2 +- package-lock.json | 12 ++++++------ packages/components/CHANGELOG.md | 4 ++++ packages/components/package.json | 2 +- packages/core/CHANGELOG.md | 4 ++++ packages/core/package.json | 2 +- .../CHANGELOG.md | 4 ++++ .../package.json | 2 +- packages/experience-builder-sdk/CHANGELOG.md | 4 ++++ packages/experience-builder-sdk/package.json | 2 +- packages/validators/CHANGELOG.md | 4 ++++ packages/validators/package.json | 2 +- packages/visual-editor/CHANGELOG.md | 4 ++++ packages/visual-editor/package.json | 2 +- 15 files changed, 41 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c86f9dfa..a8320fd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package @contentful/experience + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package @contentful/experience diff --git a/lerna.json b/lerna.json index 225927b57..235439067 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "npmClient": "npm", - "version": "1.32.0-beta.0", + "version": "1.32.0", "command": { "version": { "allowBranch": ["main", "next", "development"], diff --git a/package-lock.json b/package-lock.json index b8d2418ee..8ae40f7ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44122,7 +44122,7 @@ }, "packages/components": { "name": "@contentful/experiences-components-react", - "version": "1.32.0-beta.0", + "version": "1.32.0", "license": "MIT", "dependencies": { "@contentful/experiences-core": "file:../core", @@ -44212,7 +44212,7 @@ }, "packages/core": { "name": "@contentful/experiences-core", - "version": "1.32.0-beta.0", + "version": "1.32.0", "license": "MIT", "dependencies": { "@contentful/experiences-validators": "file:../validators", @@ -44302,7 +44302,7 @@ } }, "packages/create-contentful-studio-experiences": { - "version": "1.32.0-beta.0", + "version": "1.32.0", "license": "MIT", "dependencies": { "@clack/prompts": "^0.7.0", @@ -44392,7 +44392,7 @@ }, "packages/experience-builder-sdk": { "name": "@contentful/experiences-sdk-react", - "version": "1.32.0-beta.0", + "version": "1.32.0", "license": "MIT", "dependencies": { "@contentful/experiences-components-react": "file:../components", @@ -45162,7 +45162,7 @@ }, "packages/validators": { "name": "@contentful/experiences-validators", - "version": "1.32.0-beta.0", + "version": "1.32.0", "dependencies": { "zod": "^3.22.4" }, @@ -45175,7 +45175,7 @@ }, "packages/visual-editor": { "name": "@contentful/experiences-visual-editor-react", - "version": "1.32.0-beta.0", + "version": "1.32.0", "dependencies": { "@contentful/experiences-components-react": "file:../components", "@contentful/experiences-core": "file:../core", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index ea09d2143..8061ee29d 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package @contentful/experiences-components-react + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package @contentful/experiences-components-react diff --git a/packages/components/package.json b/packages/components/package.json index 31c33b692..5ba8f7405 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-components-react", - "version": "1.32.0-beta.0", + "version": "1.32.0", "description": "A basic set of components to use with Studio Experiences", "homepage": "https://github.com/contentful/experience-builder/tree/next/packages/components#readme", "repository": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 162f43172..bd44bb522 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package @contentful/experiences-core + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package @contentful/experiences-core diff --git a/packages/core/package.json b/packages/core/package.json index bcd4505be..9550878bc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-core", - "version": "1.32.0-beta.0", + "version": "1.32.0", "description": "", "main": "dist/index.js", "module": "dist/index.js", diff --git a/packages/create-contentful-studio-experiences/CHANGELOG.md b/packages/create-contentful-studio-experiences/CHANGELOG.md index 72dc436c3..aa10b2db4 100644 --- a/packages/create-contentful-studio-experiences/CHANGELOG.md +++ b/packages/create-contentful-studio-experiences/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package create-contentful-studio-experiences + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package create-contentful-studio-experiences diff --git a/packages/create-contentful-studio-experiences/package.json b/packages/create-contentful-studio-experiences/package.json index 6eef968e0..f13cdaecf 100644 --- a/packages/create-contentful-studio-experiences/package.json +++ b/packages/create-contentful-studio-experiences/package.json @@ -1,6 +1,6 @@ { "name": "create-contentful-studio-experiences", - "version": "1.32.0-beta.0", + "version": "1.32.0", "description": "A CLI tool to get up and running with Contentful Studio Experiences quickly", "homepage": "https://github.com/contentful/experience-builder/tree/next/packages/create-contentful-studio-experiences#readme", "repository": { diff --git a/packages/experience-builder-sdk/CHANGELOG.md b/packages/experience-builder-sdk/CHANGELOG.md index 6c86e4757..c5071cca3 100644 --- a/packages/experience-builder-sdk/CHANGELOG.md +++ b/packages/experience-builder-sdk/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package @contentful/experiences-sdk-react + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package @contentful/experiences-sdk-react diff --git a/packages/experience-builder-sdk/package.json b/packages/experience-builder-sdk/package.json index d3e32885a..11d0f5424 100644 --- a/packages/experience-builder-sdk/package.json +++ b/packages/experience-builder-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-sdk-react", - "version": "1.32.0-beta.0", + "version": "1.32.0", "main": "./dist/index.js", "module": "./dist/index.js", "typings": "./dist/src/index.d.ts", diff --git a/packages/validators/CHANGELOG.md b/packages/validators/CHANGELOG.md index a921b8478..44e3d926e 100644 --- a/packages/validators/CHANGELOG.md +++ b/packages/validators/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package @contentful/experiences-validators + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package @contentful/experiences-validators diff --git a/packages/validators/package.json b/packages/validators/package.json index fcd2afce1..c1c8257eb 100644 --- a/packages/validators/package.json +++ b/packages/validators/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-validators", - "version": "1.32.0-beta.0", + "version": "1.32.0", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/visual-editor/CHANGELOG.md b/packages/visual-editor/CHANGELOG.md index dc66ca32a..ef7b75e78 100644 --- a/packages/visual-editor/CHANGELOG.md +++ b/packages/visual-editor/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.32.0](https://github.com/contentful/experience-builder/compare/v1.32.0-beta.0...v1.32.0) (2025-02-20) + +**Note:** Version bump only for package @contentful/experiences-visual-editor-react + ## [1.31.1](https://github.com/contentful/experience-builder/compare/v1.31.1-beta.0...v1.31.1) (2025-02-19) **Note:** Version bump only for package @contentful/experiences-visual-editor-react diff --git a/packages/visual-editor/package.json b/packages/visual-editor/package.json index 858b2b2a7..f83df0076 100644 --- a/packages/visual-editor/package.json +++ b/packages/visual-editor/package.json @@ -1,6 +1,6 @@ { "name": "@contentful/experiences-visual-editor-react", - "version": "1.32.0-beta.0", + "version": "1.32.0", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", From 53c66f856189602ef77bdc90d42c4c6822bd9002 Mon Sep 17 00:00:00 2001 From: Chase Poirier Date: Thu, 20 Feb 2025 10:23:48 -0700 Subject: [PATCH 5/5] feat(prebinding): schema updates and rendering of prebinding mappings in delivery mode (#1014) * chore: update validators package for prebinding [LUMOS-556] (#998) * chore: update validators package for prebinding * chore: add .strict to novalue type * Revert "chore: add .strict to novalue type" This reverts commit 6822184ae24d6d834eeeb3a3cf4d7418058816e5. * chore: add .strict to novalue type * feat(prebinding): resolve prebindings in delivery mode (#1006) * chore(prebinding): add contentType to patternPropertySchema (#1007) --- package-lock.json | 1 + packages/core/src/types.ts | 1 + .../src/core/preview/assemblyUtils.ts | 43 +++- .../src/utils/prebindingUtils.spec.ts | 236 ++++++++++++++++++ .../src/utils/prebindingUtils.ts | 56 +++++ .../src/schemas/v2023_09_28/experience.ts | 15 +- packages/validators/src/types.ts | 1 + 7 files changed, 338 insertions(+), 15 deletions(-) create mode 100644 packages/experience-builder-sdk/src/utils/prebindingUtils.spec.ts create mode 100644 packages/experience-builder-sdk/src/utils/prebindingUtils.ts diff --git a/package-lock.json b/package-lock.json index 8ae40f7ad..3f8b21577 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2507,6 +2507,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 291b261e5..a4e3f638c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -31,6 +31,7 @@ export type { DesignValue, UnboundValue, BoundValue, + NoValue, ComponentValue, ComponentDefinitionPropertyType as ComponentDefinitionVariableType, PatternProperty, diff --git a/packages/experience-builder-sdk/src/core/preview/assemblyUtils.ts b/packages/experience-builder-sdk/src/core/preview/assemblyUtils.ts index f51107b9f..919bb84c6 100644 --- a/packages/experience-builder-sdk/src/core/preview/assemblyUtils.ts +++ b/packages/experience-builder-sdk/src/core/preview/assemblyUtils.ts @@ -4,18 +4,22 @@ import type { ComponentTreeNode, DesignValue, ExperienceComponentSettings, + PatternProperty, } from '@contentful/experiences-core/types'; +import { resolvePrebindingPath, shouldUsePrebinding } from '../../utils/prebindingUtils'; /** While unfolding the assembly definition on the instance, this function will replace all * ComponentValue in the definitions tree with the actual value on the instance. */ export const deserializeAssemblyNode = ({ node, componentInstanceVariables, - assemblyVariableDefinitions, + componentSettings, + patternProperties, }: { node: ComponentTreeNode; componentInstanceVariables: ComponentTreeNode['variables']; - assemblyVariableDefinitions: ExperienceComponentSettings['variableDefinitions']; + componentSettings: ExperienceComponentSettings; + patternProperties: Record; }): ComponentTreeNode => { const variables: Record = {}; @@ -24,12 +28,33 @@ export const deserializeAssemblyNode = ({ if (variable.type === 'ComponentValue') { const componentValueKey = variable.key; const instanceProperty = componentInstanceVariables[componentValueKey]; - const variableDefinition = assemblyVariableDefinitions?.[componentValueKey]; + const variableDefinition = componentSettings.variableDefinitions?.[componentValueKey]; const defaultValue = variableDefinition?.defaultValue; - // For assembly, we look up the variable in the assembly instance and - // replace the ComponentValue with that one. - if (instanceProperty?.type === 'UnboundValue') { + const usePrebinding = shouldUsePrebinding({ + componentSettings, + componentValueKey, + patternProperties, + variable: instanceProperty, + }); + + if (usePrebinding) { + const path = resolvePrebindingPath({ + componentSettings, + componentValueKey, + patternProperties, + }); + + if (path) { + variables[variableName] = { + type: 'BoundValue', + path, + }; + } + + // For assembly, we look up the variable in the assembly instance and + // replace the ComponentValue with that one. + } else if (instanceProperty?.type === 'UnboundValue') { variables[variableName] = { type: 'UnboundValue', key: instanceProperty.key, @@ -65,7 +90,8 @@ export const deserializeAssemblyNode = ({ deserializeAssemblyNode({ node: child, componentInstanceVariables, - assemblyVariableDefinitions, + componentSettings, + patternProperties, }), ); @@ -114,7 +140,8 @@ export const resolveAssembly = ({ children: componentFields.componentTree.children, }, componentInstanceVariables: node.variables, - assemblyVariableDefinitions: componentFields.componentSettings!.variableDefinitions, + componentSettings: componentFields.componentSettings!, + patternProperties: node.patternProperties || {}, }); entityStore.addAssemblyUnboundValues(componentFields.unboundValues); diff --git a/packages/experience-builder-sdk/src/utils/prebindingUtils.spec.ts b/packages/experience-builder-sdk/src/utils/prebindingUtils.spec.ts new file mode 100644 index 000000000..dca46f557 --- /dev/null +++ b/packages/experience-builder-sdk/src/utils/prebindingUtils.spec.ts @@ -0,0 +1,236 @@ +import { shouldUsePrebinding, resolvePrebindingPath } from './prebindingUtils'; + +import { + ComponentPropertyValue, + ExperienceComponentSettings, + PatternProperty, +} from '@contentful/experiences-validators'; + +describe('shouldUsePrebinding', () => { + it('should return true when all conditions are met', () => { + const componentValueKey = 'testKey'; + const componentSettings = { + patternPropertyDefinitions: { + testPatternPropertyDefinitionId: {}, + }, + variableMappings: { + testKey: { + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties = { + testPatternPropertyDefinitionId: {}, + } as unknown as Record; + const variable = { + type: 'NoValue', + } as unknown as ComponentPropertyValue; + + const result = shouldUsePrebinding({ + componentValueKey, + componentSettings, + patternProperties, + variable, + }); + + expect(result).toBe(true); + }); + + it('should return false when patternPropertyDefinition is missing', () => { + const componentValueKey = 'testKey'; + const componentSettings = { + patternPropertyDefinitions: {}, + variableMappings: { + testKey: { + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = { + testPatternPropertyDefinitionId: {}, + } as unknown as Record; + const variable = { + type: 'NoValue', + } as unknown as ComponentPropertyValue; + + const result = shouldUsePrebinding({ + componentValueKey, + componentSettings, + patternProperties, + variable, + }); + + expect(result).toBe(false); + }); + + it('should return false when patternProperty is missing', () => { + const componentValueKey = 'testKey'; + const componentSettings: ExperienceComponentSettings = { + patternPropertyDefinitions: { + testPatternPropertyDefinitionId: {}, + }, + variableMappings: { + testKey: { + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = {}; + const variable = { + type: 'NoValue', + } as unknown as ComponentPropertyValue; + + const result = shouldUsePrebinding({ + componentValueKey, + componentSettings, + patternProperties, + variable, + }); + + expect(result).toBe(false); + }); + + it('should return false when variableMapping is missing', () => { + const componentValueKey = 'testKey'; + const componentSettings = { + patternPropertyDefinitions: { + testPatternPropertyDefinitionId: {}, + }, + variableMappings: {}, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = { + testPatternPropertyDefinitionId: { + path: '/entries/testEntry', + type: 'BoundValue', + contenType: 'testContentType', + }, + }; + const variable = { + type: 'NoValue', + } as unknown as ComponentPropertyValue; + + const result = shouldUsePrebinding({ + componentValueKey, + componentSettings, + patternProperties, + variable, + }); + + expect(result).toBe(false); + }); +}); + +describe('resolvePrebindingPath', () => { + it('should return the correct path when all conditions are met', () => { + const componentValueKey = 'testKey'; + const componentSettings = { + patternPropertyDefinitions: {}, + variableDefinitions: {}, + variableMappings: { + testKey: { + type: 'ContentTypeMapping', + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + pathsByContentType: { + testContentType: { path: '/fields/testField' }, + }, + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = { + testPatternPropertyDefinitionId: { + path: '/entries/testEntry', + type: 'BoundValue', + contenType: 'testContentType', + }, + }; + + const result = resolvePrebindingPath({ + componentValueKey, + componentSettings, + patternProperties, + }); + + expect(result).toBe('/entries/testEntry/fields/testField'); + }); + + it('should return an empty string when variableMapping is missing', () => { + const componentValueKey = 'testKey'; + const componentSettings: ExperienceComponentSettings = { + variableMappings: {}, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = {}; + + const result = resolvePrebindingPath({ + componentValueKey, + componentSettings, + patternProperties, + }); + + expect(result).toBe(''); + }); + + it('should return an empty string when patternProperties is missing', () => { + const componentValueKey = 'testKey'; + const componentSettings: ExperienceComponentSettings = { + variableMappings: { + testKey: { + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = {}; + + const result = resolvePrebindingPath({ + componentValueKey, + componentSettings, + patternProperties, + }); + + expect(result).toBe(''); + }); + + it('should return an empty string when entityOrAsset is not an Entry', () => { + const componentValueKey = 'testKey'; + const componentSettings: ExperienceComponentSettings = { + variableMappings: { + testKey: { + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = { + testPatternPropertyDefinitionId: { path: '/entries/testEntry' }, + } as unknown as Record; + + const result = resolvePrebindingPath({ + componentValueKey, + componentSettings, + patternProperties, + }); + + expect(result).toBe(''); + }); + + it('should return an empty string when fieldPath is missing', () => { + const componentValueKey = 'testKey'; + const componentSettings: ExperienceComponentSettings = { + variableMappings: { + testKey: { + patternPropertyDefinitionId: 'testPatternPropertyDefinitionId', + pathsByContentType: {}, + }, + }, + } as unknown as ExperienceComponentSettings; + const patternProperties: Record = { + testPatternPropertyDefinitionId: { path: '/entries/testEntry' }, + } as unknown as Record; + + const result = resolvePrebindingPath({ + componentValueKey, + componentSettings, + patternProperties, + }); + + expect(result).toBe(''); + }); +}); diff --git a/packages/experience-builder-sdk/src/utils/prebindingUtils.ts b/packages/experience-builder-sdk/src/utils/prebindingUtils.ts new file mode 100644 index 000000000..be0a2d686 --- /dev/null +++ b/packages/experience-builder-sdk/src/utils/prebindingUtils.ts @@ -0,0 +1,56 @@ +import { + ComponentPropertyValue, + ExperienceComponentSettings, + PatternProperty, +} from '@contentful/experiences-validators'; + +export const shouldUsePrebinding = ({ + componentValueKey, + componentSettings, + patternProperties, + variable, +}: { + componentValueKey: string; + componentSettings: ExperienceComponentSettings; + patternProperties: Record; + variable: ComponentPropertyValue; +}) => { + const { patternPropertyDefinitions, variableMappings } = componentSettings; + + const variableMapping = variableMappings?.[componentValueKey]; + + const patternPropertyDefinition = + patternPropertyDefinitions?.[variableMapping?.patternPropertyDefinitionId || '']; + const patternProperty = patternProperties?.[variableMapping?.patternPropertyDefinitionId || '']; + + const isValidForPrebinding = + !!patternPropertyDefinition && !!patternProperty && !!variableMapping; + + return isValidForPrebinding && variable?.type === 'NoValue'; +}; + +export const resolvePrebindingPath = ({ + componentValueKey, + componentSettings, + patternProperties, +}: { + componentValueKey: string; + componentSettings: ExperienceComponentSettings; + patternProperties: Record; +}) => { + const variableMapping = componentSettings.variableMappings?.[componentValueKey]; + + if (!variableMapping) return ''; + + const patternProperty = patternProperties?.[variableMapping.patternPropertyDefinitionId]; + + if (!patternProperty) return ''; + + const contentType = patternProperty.contenType; + + const fieldPath = variableMapping?.pathsByContentType?.[contentType]?.path; + + if (!fieldPath) return ''; + + return patternProperty.path + fieldPath; +}; diff --git a/packages/validators/src/schemas/v2023_09_28/experience.ts b/packages/validators/src/schemas/v2023_09_28/experience.ts index 5b5c0ffb9..d570fd974 100644 --- a/packages/validators/src/schemas/v2023_09_28/experience.ts +++ b/packages/validators/src/schemas/v2023_09_28/experience.ts @@ -88,7 +88,7 @@ const ComponentValueSchema = z // TODO: finalize schema structure before release // https://contentful.atlassian.net/browse/LUMOS-523 -const EmptyObjectSchema = z.object({ type: z.undefined() }); +const NoValueSchema = z.object({ type: z.literal('NoValue') }).strict(); const ComponentPropertyValueSchema = z.discriminatedUnion('type', [ DesignValueSchema, @@ -96,7 +96,7 @@ const ComponentPropertyValueSchema = z.discriminatedUnion('type', [ UnboundValueSchema, HyperlinkValueSchema, ComponentValueSchema, - EmptyObjectSchema, + NoValueSchema, ]); export type ComponentPropertyValue = z.infer; @@ -114,13 +114,12 @@ const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema // TODO: finalize schema structure before release // https://contentful.atlassian.net/browse/LUMOS-523 const PatternPropertyDefinitionSchema = z.object({ - defaultValue: z.union([ - z.object({ + defaultValue: z + .object({ path: z.string(), type: z.literal('BoundValue'), - }), - z.null(), - ]), + }) + .optional(), contentTypes: z.record(z.string(), z.any()), }); @@ -134,6 +133,7 @@ const PatternPropertyDefinitionsSchema = z.record( const PatternPropertySchema = z.object({ type: z.literal('BoundValue'), path: z.string(), + contenType: z.string(), }); const PatternPropertysSchema = z.record(propertyKeySchema, PatternPropertySchema); @@ -322,6 +322,7 @@ export type Breakpoint = z.infer; export type PrimitiveValue = z.infer; export type DesignValue = z.infer; export type BoundValue = z.infer; +export type NoValue = z.infer; export type UnboundValue = z.infer; export type HyperlinkValue = z.infer; export type ComponentValue = z.infer; diff --git a/packages/validators/src/types.ts b/packages/validators/src/types.ts index ab0b92317..2b2e6303e 100644 --- a/packages/validators/src/types.ts +++ b/packages/validators/src/types.ts @@ -10,6 +10,7 @@ export type { ComponentPropertyValue, ComponentTreeNode, PrimitiveValue, + NoValue, DesignValue, UnboundValue, BoundValue,