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

[Nextjs][Personalize] Productize layout response transform #962

Merged
merged 10 commits into from
Apr 1, 2022

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GetServerSidePropsContext, GetStaticPropsContext } from 'next';
import { Plugin } from '..';
import { personalizeLayout } from 'lib/layout-personalizer';
import { personalizeLayout } from '@sitecore-jss/sitecore-jss-nextjs';
import { SitecorePageProps } from 'lib/page-props';

class PersonalizePlugin implements Plugin {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { NextRequest } from 'next/server';
import type { NextRequest, NextFetchEvent } from 'next/server';
import middleware from 'lib/middleware';

// eslint-disable-next-line
export default async function (req: NextRequest) {
return middleware(req);
export default async function (req: NextRequest, ev: NextFetchEvent) {
return middleware(req, ev);
}
1 change: 1 addition & 0 deletions packages/sitecore-jss-nextjs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export {
RestDictionaryService,
RestDictionaryServiceConfig,
} from '@sitecore-jss/sitecore-jss/i18n';
export { personalizeLayout } from '@sitecore-jss/sitecore-jss/personalize';
export {
RobotsQueryResult,
GraphQLRobotsService,
Expand Down
1 change: 1 addition & 0 deletions packages/sitecore-jss/personalize.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types/personalize/index';
1 change: 1 addition & 0 deletions packages/sitecore-jss/personalize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/cjs/personalize/index');
1 change: 1 addition & 0 deletions packages/sitecore-jss/src/personalize/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { personalizeLayout } from './layout-personalizer';
68 changes: 68 additions & 0 deletions packages/sitecore-jss/src/personalize/layout-personalizer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { expect } from 'chai';
import * as personalize from './layout-personalizer';
import { ComponentRenderingWithExperiences } from './layout-personalizer';
import {
layoutData,
componentsArray,
component,
componentsWithExperiencesArray,
componentWithExperiences,
layoutDataWithoutPlaceholder,
withoutComponentName,
segmentIsNull,
} from '../test-data/personalizeData';

const { personalizeLayout, personalizePlaceholder, personalizeComponent } = personalize;

describe('layout-personalizer', () => {
describe('personalizeLayout', () => {
it('should not return anything', () => {
const segment = 'test';
const personalizedLayoutResult = personalizeLayout(layoutData, segment);
expect(personalizedLayoutResult).to.equal(undefined);
});

it('should return undefined if no placeholders', () => {
const segment = 'test';
const personalizedLayoutResult = personalizeLayout(layoutDataWithoutPlaceholder, segment);
expect(personalizedLayoutResult).to.equal(undefined);
});
});

describe('personalizePlaceholder', () => {
it('should return array of personalized components', () => {
const segment = 'mountain_bike_audirnce';
const personalizedPlaceholderResult = personalizePlaceholder(componentsArray, segment);
expect(personalizedPlaceholderResult).to.deep.equal(componentsWithExperiencesArray);
});
});

describe('personalizeComponent', () => {
it('should return personalized component', () => {
const segment = 'mountain_bike_audirnce';
const personalizedComponentResult = personalizeComponent(
(component as unknown) as ComponentRenderingWithExperiences,
segment
);
expect(personalizedComponentResult).to.deep.equal(componentWithExperiences);
});

it('should return null when segmentVariant is null', () => {
const segment = 'mountain_bike_audirnce';
const personalizedComponentResult = personalizeComponent(
(segmentIsNull as unknown) as ComponentRenderingWithExperiences,
segment
);
expect(personalizedComponentResult).to.equal(null);
});

it('should return null when segmentVariant and componentName is undefined', () => {
const segment = 'test';
const personalizedComponentResult = personalizeComponent(
(withoutComponentName as unknown) as ComponentRenderingWithExperiences,
segment
);
expect(personalizedComponentResult).to.equal(null);
});
});
});
73 changes: 73 additions & 0 deletions packages/sitecore-jss/src/personalize/layout-personalizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { LayoutServiceData, ComponentRendering, HtmlElementRendering } from './../layout/models';

// NULL means Hidden by this experience
export type ComponentRenderingWithExperiences = ComponentRendering & {
experiences: { [name: string]: ComponentRenderingWithExperiences | null };
};

// recursive go through all placeholders/components and check experiences node, replace default with object from specific experience
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
/**
* @param {LayoutServiceData} layout Layout data
* @param {string} segment segmentId
*/
export function personalizeLayout(layout: LayoutServiceData, segment: string): void {
const placeholders = layout.sitecore.route?.placeholders;
if (Object.keys(placeholders ?? {}).length === 0) {
return;
}
if (placeholders) {
Object.keys(placeholders).forEach((placeholder) => {
placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], segment);
});
}
}

/**
* @param {Array} components components within placeholder
* @param {string} segment segmentId
*/
export function personalizePlaceholder(
components: Array<ComponentRendering | HtmlElementRendering>,
segment: string
): Array<ComponentRendering | HtmlElementRendering> {
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
return components.map((_, i) =>
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
(<ComponentRenderingWithExperiences>components[i]).experiences !== undefined
? (personalizeComponent(<ComponentRenderingWithExperiences>components[i], segment) as
| ComponentRendering
| HtmlElementRendering)
: components[i]
);
}

/**
* @param {ComponentRenderingWithExperiences} component component with experiences
* @param {string} segment segmentId
*/
export function personalizeComponent(
component: ComponentRenderingWithExperiences,
segment: string
): ComponentRendering | null {
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
const segmentVariant = component.experiences[segment];
if (segmentVariant === undefined && component.componentName === undefined) {
// DEFAULT IS HIDDEN
return null;
} else if (Object.keys(segmentVariant ?? {}).length === 0) {
// HIDDEN
return null;
} else if (segmentVariant) {
component = segmentVariant;
}

if (!component.placeholders) return component;

Object.keys(component?.placeholders).forEach((placeholder) => {
if (component.placeholders) {
component.placeholders[placeholder] = personalizePlaceholder(
component.placeholders[placeholder],
segment
);
}
});

return component;
}
Loading