diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d71a0a88f..25f8a47f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ Our versioning strategy is as follows: ### 🛠 Breaking Change -* Editing Integration Support: ([#1776](https://github.com/Sitecore/jss/pull/1776))([#1792](https://github.com/Sitecore/jss/pull/1792))([#1773](https://github.com/Sitecore/jss/pull/1773))([#1797](https://github.com/Sitecore/jss/pull/1797))([#1800](https://github.com/Sitecore/jss/pull/1800))([#1803](https://github.com/Sitecore/jss/pull/1803))([#1806](https://github.com/Sitecore/jss/pull/1806))([#1809](https://github.com/Sitecore/jss/pull/1809))([#1814](https://github.com/Sitecore/jss/pull/1814))([#1816](https://github.com/Sitecore/jss/pull/1816))([#1819](https://github.com/Sitecore/jss/pull/1819))([#1828](https://github.com/Sitecore/jss/pull/1828))([#1835](https://github.com/Sitecore/jss/pull/1835))([#1849](https://github.com/Sitecore/jss/pull/1849))([#1831](https://github.com/Sitecore/jss/pull/1831))([#1854](https://github.com/Sitecore/jss/pull/1854)) +* Editing Integration Support: ([#1776](https://github.com/Sitecore/jss/pull/1776))([#1792](https://github.com/Sitecore/jss/pull/1792))([#1773](https://github.com/Sitecore/jss/pull/1773))([#1797](https://github.com/Sitecore/jss/pull/1797))([#1800](https://github.com/Sitecore/jss/pull/1800))([#1803](https://github.com/Sitecore/jss/pull/1803))([#1806](https://github.com/Sitecore/jss/pull/1806))([#1809](https://github.com/Sitecore/jss/pull/1809))([#1814](https://github.com/Sitecore/jss/pull/1814))([#1816](https://github.com/Sitecore/jss/pull/1816))([#1819](https://github.com/Sitecore/jss/pull/1819))([#1828](https://github.com/Sitecore/jss/pull/1828))([#1835](https://github.com/Sitecore/jss/pull/1835))([#1849](https://github.com/Sitecore/jss/pull/1849))([#1831](https://github.com/Sitecore/jss/pull/1831))([#1853](https://github.com/Sitecore/jss/pull/1853))([#1854](https://github.com/Sitecore/jss/pull/1854)) * `[sitecore-jss-react]` Introduces `PlaceholderMetadata` component which supports the hydration of chromes on Pages by rendering the components and placeholders with required metadata. * `[sitecore-jss]` Chromes are hydrated based on the basis of new `editMode` property derived from LayoutData, which is defined as an enum consisting of `metadata` and `chromes`. * `ComponentConsumerProps` is removed. You might need to reuse `WithSitecoreContextProps` type. @@ -47,6 +47,7 @@ Our versioning strategy is as follows: * `[sitecore-jss]` Introduced `GraphQLEditingService` class to fetch editing data in Metadata Edit Mode. * `[templates/nextjs-xmcloud]` Introduced _/lib/graphql-editing-service_ to fetch editing data in Metadata Edit Mode. * `[templates/nextjs-xmcloud]` Added a new _page-props-factory/plugins/preview-mode_ plugin to handle both Chromes and Metadata Edit Mode. + * `[sitecore-jss]` layout-personalizer will mark components hidden by personalization by setting 'Hidden Rendering' component name in Metadata edit mode * `[sitecore-jss]` Introduced _/editing_ submodule that contains all editing related functionality. Editing utils are now available in _/editing_ submodule. Editing utils exported from _/utils_ marked as deprecated. ([#1806](https://github.com/Sitecore/jss/pull/1806)) * `[sitecore-jss-nextjs]` EditingRenderMiddleware `resolvePageUrl` function now accepts an object `(args: { serverUrl?: string; itemPath: string }) => string` instead of multiple parameters `(serverUrl: string, itemPath: string) => string`. `serverUrl` is now optional and omitted when Metadata Edit Mode is used. * `[templates/nextjs]` `[sitecore-jss-nextjs]` `[sitecore-jss]` Remove Partial rendering implementation as it will not be used by Pages in its current implementation - includes removing of EditingComponentPlaceholder component, few constants associated with it and RenderingType enum ([#1821](https://github.com/Sitecore/jss/pull/1821)) diff --git a/packages/sitecore-jss-angular/src/components/hidden-rendering.component.ts b/packages/sitecore-jss-angular/src/components/hidden-rendering.component.ts index a332aa1836..facecfa9d1 100644 --- a/packages/sitecore-jss-angular/src/components/hidden-rendering.component.ts +++ b/packages/sitecore-jss-angular/src/components/hidden-rendering.component.ts @@ -8,5 +8,3 @@ export class HiddenRenderingComponent { return 'background-image: linear-gradient(45deg, #ffffff 25%, #dcdcdc 25%, #dcdcdc 50%, #ffffff 50%, #ffffff 75%, #dcdcdc 75%, #dcdcdc 100%); background-size: 3px 3px; display: block; height: 100px;'; } } - -export const HIDDEN_RENDERING_NAME = 'Hidden Rendering'; diff --git a/packages/sitecore-jss-angular/src/components/placeholder.component.ts b/packages/sitecore-jss-angular/src/components/placeholder.component.ts index 1683afac3d..b34c614164 100644 --- a/packages/sitecore-jss-angular/src/components/placeholder.component.ts +++ b/packages/sitecore-jss-angular/src/components/placeholder.component.ts @@ -39,7 +39,7 @@ import { PLACEHOLDER_HIDDEN_RENDERING_COMPONENT, PLACEHOLDER_MISSING_COMPONENT_COMPONENT, } from '../services/placeholder.token'; -import { HIDDEN_RENDERING_NAME } from './hidden-rendering.component'; +import { constants } from '@sitecore-jss/sitecore-jss'; import { PlaceholderLoadingDirective } from './placeholder-loading.directive'; import { RenderEachDirective } from './render-each.directive'; import { RenderEmptyDirective } from './render-empty.directive'; @@ -286,7 +286,8 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr private _renderEmbeddedComponent(rendering: ComponentFactoryResult, data: Data, index: number) { if ( - (rendering.componentDefinition as ComponentRendering).componentName === HIDDEN_RENDERING_NAME + (rendering.componentDefinition as ComponentRendering).componentName === + constants.HIDDEN_RENDERING_NAME ) { rendering.componentImplementation = this.hiddenRenderingComponent; } diff --git a/packages/sitecore-jss-react/src/components/HiddenRendering.tsx b/packages/sitecore-jss-react/src/components/HiddenRendering.tsx index e9f914db75..66e5632f8f 100644 --- a/packages/sitecore-jss-react/src/components/HiddenRendering.tsx +++ b/packages/sitecore-jss-react/src/components/HiddenRendering.tsx @@ -8,5 +8,3 @@ const styles = { }; export const HiddenRendering = () =>
; - -export const HIDDEN_RENDERING_NAME = 'Hidden Rendering'; diff --git a/packages/sitecore-jss-react/src/components/Placeholder.test.tsx b/packages/sitecore-jss-react/src/components/Placeholder.test.tsx index ebc2b5eabf..485e4fc384 100644 --- a/packages/sitecore-jss-react/src/components/Placeholder.test.tsx +++ b/packages/sitecore-jss-react/src/components/Placeholder.test.tsx @@ -2,7 +2,7 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable react/prop-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ComponentRendering, EditMode, RouteData } from '@sitecore-jss/sitecore-jss/layout'; +import { ComponentRendering, RouteData } from '@sitecore-jss/sitecore-jss/layout'; import { expect } from 'chai'; import { mount, shallow } from 'enzyme'; import PropTypes from 'prop-types'; diff --git a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx index 749c334450..cff5643817 100644 --- a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx +++ b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx @@ -10,8 +10,9 @@ import { HtmlElementRendering, EditMode, } from '@sitecore-jss/sitecore-jss/layout'; +import { constants } from '@sitecore-jss/sitecore-jss'; import { convertAttributesToReactProps } from '../utils'; -import { HiddenRendering, HIDDEN_RENDERING_NAME } from './HiddenRendering'; +import { HiddenRendering } from './HiddenRendering'; import { FEaaSComponent, FEAAS_COMPONENT_RENDERING_NAME } from './FEaaSComponent'; import { FEaaSWrapper, FEAAS_WRAPPER_RENDERING_NAME } from './FEaaSWrapper'; import { BYOCComponent, BYOC_COMPONENT_RENDERING_NAME } from './BYOCComponent'; @@ -243,7 +244,7 @@ export class PlaceholderCommon extends React.Compone let component; - if (componentRendering.componentName === HIDDEN_RENDERING_NAME) { + if (componentRendering.componentName === constants.HIDDEN_RENDERING_NAME) { component = hiddenRenderingComponent ?? HiddenRendering; isEmpty = true; } else if (!componentRendering.componentName) { diff --git a/packages/sitecore-jss-vue/src/components/HiddenRendering.ts b/packages/sitecore-jss-vue/src/components/HiddenRendering.ts index f13cc33d3b..1f0c7ce9b9 100644 --- a/packages/sitecore-jss-vue/src/components/HiddenRendering.ts +++ b/packages/sitecore-jss-vue/src/components/HiddenRendering.ts @@ -12,5 +12,3 @@ export const HiddenRendering = defineComponent({ }); }, }); - -export const HIDDEN_RENDERING_NAME = 'Hidden Rendering'; diff --git a/packages/sitecore-jss-vue/src/components/PlaceholderCommon.ts b/packages/sitecore-jss-vue/src/components/PlaceholderCommon.ts index 67910dcc64..201d294842 100644 --- a/packages/sitecore-jss-vue/src/components/PlaceholderCommon.ts +++ b/packages/sitecore-jss-vue/src/components/PlaceholderCommon.ts @@ -7,9 +7,10 @@ import { RouteData, } from '@sitecore-jss/sitecore-jss/layout'; import { resetEditorChromes } from '@sitecore-jss/sitecore-jss/editing'; +import { constants } from '@sitecore-jss/sitecore-jss'; import { Component, h, VNode, DefineComponent, ref, onMounted } from 'vue'; import { MissingComponent } from './MissingComponent'; -import { HiddenRendering, HIDDEN_RENDERING_NAME } from './HiddenRendering'; +import { HiddenRendering } from './HiddenRendering'; import { ComponentFactory } from './sharedTypes'; export interface PlaceholderProps { @@ -116,7 +117,7 @@ export function getVNodesForRenderingData( let component: any; - if (rendering.componentName === HIDDEN_RENDERING_NAME) { + if (rendering.componentName === constants.HIDDEN_RENDERING_NAME) { component = hiddenRenderingComponent || HiddenRendering; } else { component = getComponentForRendering(rendering, componentFactory); diff --git a/packages/sitecore-jss/src/constants.ts b/packages/sitecore-jss/src/constants.ts index 7fd8f42154..7a974f39c7 100644 --- a/packages/sitecore-jss/src/constants.ts +++ b/packages/sitecore-jss/src/constants.ts @@ -19,3 +19,5 @@ export const JSS_MODE = { export const siteNameError = 'The siteName cannot be empty'; export const SITECORE_EDGE_URL_DEFAULT = 'https://edge-platform.sitecorecloud.io'; + +export const HIDDEN_RENDERING_NAME = 'Hidden Rendering'; diff --git a/packages/sitecore-jss/src/graphql-request-client.test.ts b/packages/sitecore-jss/src/graphql-request-client.test.ts index 767629682d..82a858fdad 100644 --- a/packages/sitecore-jss/src/graphql-request-client.test.ts +++ b/packages/sitecore-jss/src/graphql-request-client.test.ts @@ -345,7 +345,7 @@ describe('GraphQLRequestClient', () => { } }); - describe('Retrayable status codes', () => { + describe('Retryable status codes', () => { const retryableStatusCodeThrowError = async (statusCode: number) => { nock('http://jssnextweb') .post('/graphql') diff --git a/packages/sitecore-jss/src/personalize/layout-personalizer.test.ts b/packages/sitecore-jss/src/personalize/layout-personalizer.test.ts index fc6db5ee01..397017d565 100644 --- a/packages/sitecore-jss/src/personalize/layout-personalizer.test.ts +++ b/packages/sitecore-jss/src/personalize/layout-personalizer.test.ts @@ -16,6 +16,7 @@ import { mountain_bike_audience, city_bike_audience, } from '../test-data/personalizeData'; +import { HIDDEN_RENDERING_NAME } from '../constants'; const { personalizeLayout, personalizePlaceholder, personalizeComponent } = personalize; @@ -340,7 +341,7 @@ describe('layout-personalizer', () => { ).to.deep.equal({}); }); - it('should return null when variantVariant is hidden', () => { + it('should return null when variant is hidden', () => { const variant = 'mountain_bike_audience'; const personalizedComponentResult = personalizeComponent( (variantIsHidden as unknown) as ComponentRenderingWithExperiences, @@ -349,7 +350,7 @@ describe('layout-personalizer', () => { expect(personalizedComponentResult).to.equal(null); }); - it('should return null when variantVariant and componentName is undefined', () => { + it('should return null when variant and componentName is undefined', () => { const variant = 'test'; const personalizedComponentResult = personalizeComponent( (withoutComponentName as unknown) as ComponentRenderingWithExperiences, @@ -359,6 +360,26 @@ describe('layout-personalizer', () => { }); }); + it('should return HIDDEN_RENDERING variant in metadata edit mode when non-default variant is hidden', () => { + const variant = 'mountain_bike_audience'; + const personalizedComponentResult = personalizeComponent( + (variantIsHidden as unknown) as ComponentRenderingWithExperiences, + [variant], + true + ); + expect(personalizedComponentResult?.componentName).to.equal(HIDDEN_RENDERING_NAME); + }); + + it('should return HIDDEN_RENDERING variant in metadata edit mode when default variant is hidden', () => { + const variant = 'will-not-match'; + const personalizedComponentResult = personalizeComponent( + (withoutComponentName as unknown) as ComponentRenderingWithExperiences, + [variant], + true + ); + expect(personalizedComponentResult?.componentName).to.equal(HIDDEN_RENDERING_NAME); + }); + describe('with multiple variant Ids', () => { const testComponent = structuredClone(component); diff --git a/packages/sitecore-jss/src/personalize/layout-personalizer.ts b/packages/sitecore-jss/src/personalize/layout-personalizer.ts index b370813446..9f1d7138bb 100644 --- a/packages/sitecore-jss/src/personalize/layout-personalizer.ts +++ b/packages/sitecore-jss/src/personalize/layout-personalizer.ts @@ -1,10 +1,17 @@ +import { HIDDEN_RENDERING_NAME } from '../constants'; import { LayoutServiceData, ComponentRendering, HtmlElementRendering, PlaceholdersData, + EditMode, } from './../layout/models'; +const hiddenRenderingVariant = { + componentName: HIDDEN_RENDERING_NAME, + experiences: {}, +}; + export type ComponentRenderingWithExperiences = ComponentRendering & { experiences: { [name: string]: ComponentRenderingWithExperiences }; }; @@ -26,12 +33,15 @@ export function personalizeLayout( if (Object.keys(placeholders ?? {}).length === 0) { return; } + const metadataEditing = + layout.sitecore.context.pageEditing && layout.sitecore.context.editMode === EditMode.Metadata; if (placeholders) { Object.keys(placeholders).forEach((placeholder) => { - placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], [ - variantId, - ...(componentVariantIds || []), - ]); + placeholders[placeholder] = personalizePlaceholder( + placeholders[placeholder], + [variantId, ...(componentVariantIds || [])], + metadataEditing + ); }); } return placeholders; @@ -40,25 +50,33 @@ export function personalizeLayout( /** * @param {Array} components components within placeholder * @param {string[]} variantIds variant ids + * @param {boolean} metadataEditing indicates if page is rendered in metadata edit mode * @returns {Array} components with personalization applied */ export function personalizePlaceholder( components: Array, - variantIds: string[] + variantIds: string[], + metadataEditing?: boolean ): Array { return components .map((component) => { const rendering = component as ComponentRendering; if ((rendering as ComponentRenderingWithExperiences).experiences !== undefined) { - return personalizeComponent(rendering as ComponentRenderingWithExperiences, variantIds) as - | ComponentRendering - | HtmlElementRendering; + return personalizeComponent( + rendering as ComponentRenderingWithExperiences, + variantIds, + metadataEditing + ) as ComponentRendering | HtmlElementRendering; } else if (rendering.placeholders) { const placeholders = rendering.placeholders as PlaceholdersData; Object.keys(placeholders).forEach((placeholder) => { - placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], variantIds); + placeholders[placeholder] = personalizePlaceholder( + placeholders[placeholder], + variantIds, + metadataEditing + ); }); } @@ -70,23 +88,35 @@ export function personalizePlaceholder( /** * @param {ComponentRenderingWithExperiences} component component with experiences * @param {string[]} variantIds variant ids + * @param {boolean} metadataEditing indicates if page is rendered in metadata edit mode * @returns {ComponentRendering | null} component with personalization applied or null if hidden */ export function personalizeComponent( component: ComponentRenderingWithExperiences, - variantIds: string[] + variantIds: string[], + metadataEditing?: boolean ): ComponentRendering | null { // Check if we have a page/component experience matching any of the variants (there should be at most 1) const match = Object.keys(component.experiences).find((variantId) => variantIds.includes(variantId) ); const variant = match && component.experiences[match]; - if (variant === undefined && component.componentName === undefined) { + + // variant and componentName can be undefined or null + if (!variant && !component.componentName) { // DEFAULT IS HIDDEN - return null; + if (metadataEditing) { + component = hiddenRenderingVariant; + } else { + return null; + } } else if (variant && variant.componentName === null && variant.dataSource === null) { // VARIANT IS HIDDEN - return null; + if (metadataEditing) { + component = hiddenRenderingVariant; + } else { + return null; + } } else if (variant) { component = variant; }