Skip to content

Commit

Permalink
[sitecore-jss][nextjs] Ensure hidden rendering is displayed when vari…
Browse files Browse the repository at this point in the history
…ant is hidden in metadata editing (#1853)

* move HIDDEN_RENDERING_NAME to base pacakage constants
* personalize into hidden rendering for hidden variant in metadata mode
* ensure null and undefined scenarios work for default hidden variant
  • Loading branch information
art-alexeyenko authored Jul 25, 2024
1 parent 5df02f8 commit d45567d
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 30 deletions.
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))([#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.
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

0 comments on commit d45567d

Please sign in to comment.