Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sitecore-jss][nextjs] Ensure hidden rendering is displayed when variant is hidden in metadata editing #1853

Merged
merged 10 commits into from
Jul 25, 2024
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
* 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))([#1853](https://github.com/Sitecore/jss/pull/1853))
* `[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.
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,3 @@ const styles = {
};

export const HiddenRendering = () => <div style={styles} />;

export const HIDDEN_RENDERING_NAME = 'Hidden Rendering';
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -243,7 +244,7 @@ export class PlaceholderCommon<T extends PlaceholderProps> 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) {
Expand Down
2 changes: 0 additions & 2 deletions packages/sitecore-jss-vue/src/components/HiddenRendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ export const HiddenRendering = defineComponent({
});
},
});

export const HIDDEN_RENDERING_NAME = 'Hidden Rendering';
5 changes: 3 additions & 2 deletions packages/sitecore-jss-vue/src/components/PlaceholderCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions packages/sitecore-jss/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 1 addition & 1 deletion packages/sitecore-jss/src/graphql-request-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ describe('GraphQLRequestClient', () => {
}
});

describe('Retrayable status codes', () => {
describe('Retryable status codes', () => {
const retryableStatusCodeThrowError = async (statusCode: number) => {
nock('http://jssnextweb')
.post('/graphql')
Expand Down
25 changes: 23 additions & 2 deletions packages/sitecore-jss/src/personalize/layout-personalizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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);

Expand Down
56 changes: 43 additions & 13 deletions packages/sitecore-jss/src/personalize/layout-personalizer.ts
Original file line number Diff line number Diff line change
@@ -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 };
};
Expand All @@ -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;
Expand All @@ -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<ComponentRendering | HtmlElementRendering>} components with personalization applied
*/
export function personalizePlaceholder(
components: Array<ComponentRendering | HtmlElementRendering>,
variantIds: string[]
variantIds: string[],
metadataEditing?: boolean
): Array<ComponentRendering | HtmlElementRendering> {
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
);
});
}

Expand All @@ -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;
}
Expand Down