Skip to content

Commit

Permalink
feat: Provide reduced header layout for configurator pages (#13949)
Browse files Browse the repository at this point in the history
Provides a reduced layout, which does not contain elements like search and miniCart, in order to not distract the end-user while configuring. 
This change is optional and needs to explicitly activated in a customer's app module as of 4.x. The internal app activates it by importing the respective layout modules in the VC and CPQ feature modules.
closes #13807
  • Loading branch information
ChristophHi authored Oct 5, 2021
1 parent 9bd802a commit bd79df1
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CmsConfig, provideDefaultConfig } from '@spartacus/core';
import { provideDefaultConfig } from '@spartacus/core';
import {
CmsPageGuard,
HamburgerMenuModule,
LayoutConfig,
PageLayoutComponent,
} from '@spartacus/storefront';
import { defaultCpqInteractiveRoutingConfig } from './default-cpq-interactive-routing-config';
Expand All @@ -30,7 +31,7 @@ import { defaultCpqInteractiveRoutingConfig } from './default-cpq-interactive-ro
],
providers: [
provideDefaultConfig(defaultCpqInteractiveRoutingConfig),
provideDefaultConfig(<CmsConfig>{
provideDefaultConfig(<LayoutConfig>{
layoutSlots: {
CpqConfigurationTemplate: {
header: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { NgModule } from '@angular/core';
import { provideDefaultConfig } from '@spartacus/core';
import { LayoutConfig, PAGE_LAYOUT_HANDLER } from '@spartacus/storefront';
import { CpqConfiguratorPageLayoutHandler } from './cpq-configurator-page-layout-handler';

/**
* Contains the layout configuration for the CPQ configurator pages. This configuration is
* optional as of version 4.2, and reduces the components that are rendered in the header section.
* It needs to be explicitly imported, otherwise the default configuration
* from VariantConfiguratorInteractiveModule is active
*/
@NgModule({
providers: [
provideDefaultConfig(<LayoutConfig>{
layoutSlots: {
CpqConfigurationTemplate: {
header: {
md: {
slots: ['SiteLogo', 'MiniCart'],
},
xs: {
slots: ['SiteLogo', 'MiniCart'],
},
},

navigation: {
lg: { slots: [] },
slots: ['CpqConfigMenu'],
},

lg: {
slots: [
'CpqConfigHeader',
'CpqConfigBanner',
'CpqConfigMenu',
'CpqConfigContent',
'CpqConfigOverviewBanner',
'CpqConfigOverviewContent',
'CpqConfigBottombar',
],
},

slots: [
'CpqConfigHeader',
'CpqConfigBanner',
'CpqConfigContent',
'CpqConfigOverviewBanner',
'CpqConfigOverviewContent',
'CpqConfigBottombar',
],
},
},
}),

{
provide: PAGE_LAYOUT_HANDLER,
useExisting: CpqConfiguratorPageLayoutHandler,
multi: true,
},
],
})
export class CpqConfiguratorLayoutModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Type } from '@angular/core';
import { TestBed, waitForAsync } from '@angular/core/testing';
import {
ConfiguratorModelUtils,
ConfiguratorRouter,
ConfiguratorRouterExtractorService,
} from '@spartacus/product-configurator/common';
import { cold } from 'jasmine-marbles';
import { Observable, of } from 'rxjs';

import { CpqConfiguratorPageLayoutHandler } from './cpq-configurator-page-layout-handler';

const standardRouterData: ConfiguratorRouter.Data = {
owner: ConfiguratorModelUtils.createInitialOwner(),
};
let routerData: ConfiguratorRouter.Data;
class MockRouterExtractorService {
extractRouterData(): Observable<ConfiguratorRouter.Data> {
return of(routerData);
}
}
const headerSlots = ['SiteLogo', 'MiniCart'];
const headerSlotsIncludingPreHeader = ['PreHeader', 'SiteLogo', 'MiniCart'];
const contentSlots = [
'CpqConfigHeader',
'CpqConfigBanner',
'CpqConfigMenu',
'CpqConfigContent',
'CpqConfigOverviewBanner',
'CpqConfigOverviewContent',
'CpqConfigBottombar',
];
const pageTemplateCpq = 'CpqConfigurationTemplate';
const pageTemplateOther = 'OtherTemplate';
const sectionHeader = 'header';
const sectionContent = 'content';

describe('CpqConfiguratorPageLayoutHandler', () => {
let classUnderTest: CpqConfiguratorPageLayoutHandler;

beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
providers: [
{
provide: ConfiguratorRouterExtractorService,
useClass: MockRouterExtractorService,
},
],
}).compileComponents();
})
);
beforeEach(() => {
classUnderTest = TestBed.inject(
CpqConfiguratorPageLayoutHandler as Type<CpqConfiguratorPageLayoutHandler>
);
});

it('should create service', () => {
expect(classUnderTest).toBeDefined();
});

it('should not touch slots for section different than header', () => {
let slots$ = cold('-a', {
a: contentSlots,
});
const handledSlots$ = classUnderTest.handle(
slots$,
pageTemplateCpq,
sectionContent
);
expect(handledSlots$).toBeObservable(slots$);
});

it('should change slots for header section in cpq template in case we are on configuration page', () => {
routerData = {
...standardRouterData,
pageType: ConfiguratorRouter.PageType.CONFIGURATION,
};
let slots$ = cold('-a-a', {
a: headerSlots,
});
const handledSlots$ = classUnderTest.handle(
slots$,
pageTemplateCpq,
sectionHeader
);
expect(handledSlots$).toBeObservable(
cold('-a-a', {
a: headerSlotsIncludingPreHeader,
})
);
});

it('should not change slots for header section in cpq template in case we are on overview page', () => {
routerData = {
...standardRouterData,
pageType: ConfiguratorRouter.PageType.OVERVIEW,
};
let slots$ = cold('-aaa', {
a: headerSlots,
});
const handledSlots$ = classUnderTest.handle(
slots$,
pageTemplateCpq,
sectionHeader
);
expect(handledSlots$).toBeObservable(slots$);
});

it('should not change slots for header section in other template', () => {
routerData = {
...standardRouterData,
pageType: ConfiguratorRouter.PageType.CONFIGURATION,
};
let slots$ = cold('-a-a', {
a: headerSlots,
});
const handledSlots$ = classUnderTest.handle(
slots$,
pageTemplateOther,
sectionHeader
);
expect(handledSlots$).toBeObservable(slots$);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Injectable } from '@angular/core';
import { PageLayoutHandler } from '@spartacus/storefront';
import {
ConfiguratorRouter,
ConfiguratorRouterExtractorService,
} from '@spartacus/product-configurator/common';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class CpqConfiguratorPageLayoutHandler implements PageLayoutHandler {
constructor(
protected configuratorRouterExtractorService: ConfiguratorRouterExtractorService
) {}
handle(
slots$: Observable<string[]>,
pageTemplate?: string,
section?: string
) {
if (pageTemplate === 'CpqConfigurationTemplate' && section === 'header') {
this.configuratorRouterExtractorService
.extractRouterData()
.pipe(take(1))
.subscribe((routerData) => {
if (
routerData.pageType === ConfiguratorRouter.PageType.CONFIGURATION
) {
slots$ = slots$.pipe(
map((slots) => {
const extendedSlots = ['PreHeader'];
extendedSlots.push(...slots);
return extendedSlots;
})
);
}
});
}

return slots$;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './cpq-configurator-interactive.module';
export * from './cpq-configurator-overview.module';
export * from './cpq-configurator-root.module';
export * from './cpq-configurator-layout.module';
export * from './interceptor/index';
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './variant-configurator-interactive.module';
export * from './variant-configurator-overview.module';
export * from './variant-configurator-interactive-layout.module';
export * from './variant-configurator-overview-layout.module';
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { NgModule } from '@angular/core';

import { provideDefaultConfig } from '@spartacus/core';
import { LayoutConfig } from '@spartacus/storefront';

/**
* Contains the layout configuration for the interactive configuration page. This configuration is
* optional as of version 4.2, and reduces the components that are rendered in the header section.
* It needs to be explicitly imported, otherwise the default configuration
* from VariantConfiguratorInteractiveModule is active
*/
@NgModule({
providers: [
provideDefaultConfig(<LayoutConfig>{
layoutSlots: {
VariantConfigurationTemplate: {
header: {
md: {
slots: ['PreHeader', 'SiteLogo', 'MiniCart'],
},
xs: {
slots: ['PreHeader', 'SiteLogo', 'MiniCart'],
},
},

navigation: {
lg: { slots: [] },
slots: ['VariantConfigMenu'],
},

lg: {
slots: [
'VariantConfigHeader',
'VariantConfigMenu',
'VariantConfigContent',
'VariantConfigBottombar',
],
},

slots: [
'VariantConfigHeader',
'VariantConfigContent',
'VariantConfigBottombar',
],
},
},
}),
],
})
export class VariantConfiguratorInteractiveLayoutModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { NgModule } from '@angular/core';
import { provideDefaultConfig } from '@spartacus/core';
import { LayoutConfig } from '@spartacus/storefront';

/**
* Contains the layout configuration for the overview configuration page. This configuration is
* optional as of version 4.2, and reduces the components that are rendered in the header section.
* It needs to be explicitly imported, otherwise the default configuration
* from VariantConfiguratorOverviewModule is active
*/
@NgModule({
providers: [
provideDefaultConfig(<LayoutConfig>{
layoutSlots: {
VariantConfigurationOverviewTemplate: {
header: {
md: {
slots: ['SiteLogo', 'MiniCart'],
},
xs: {
slots: ['SiteLogo', 'MiniCart'],
},
},
slots: [
'VariantConfigOverviewHeader',
'VariantConfigOverviewBanner',
'VariantConfigOverviewContent',
'VariantConfigOverviewBottombar',
],
},
},
}),
],
})
export class VariantConfiguratorOverviewLayoutModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@ import {
PRODUCT_CONFIGURATOR_RULEBASED_FEATURE,
RulebasedConfiguratorRootModule,
} from '@spartacus/product-configurator/rulebased/root';
import { VariantConfiguratorInteractiveLayoutModule } from '@spartacus/product-configurator/rulebased/root';
import { VariantConfiguratorOverviewLayoutModule } from '@spartacus/product-configurator/rulebased/root';
import { CpqConfiguratorLayoutModule } from '@spartacus/product-configurator/rulebased/root';

@NgModule({
imports: [RulebasedConfiguratorRootModule, CpqConfiguratorRootModule],
imports: [
RulebasedConfiguratorRootModule,
VariantConfiguratorInteractiveLayoutModule,
VariantConfiguratorOverviewLayoutModule,
CpqConfiguratorRootModule,
CpqConfiguratorLayoutModule,
],
providers: [
provideConfig({
featureModules: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import {
PRODUCT_CONFIGURATOR_RULEBASED_FEATURE,
RulebasedConfiguratorRootModule,
} from '@spartacus/product-configurator/rulebased/root';
import { VariantConfiguratorInteractiveLayoutModule } from '@spartacus/product-configurator/rulebased/root';
import { VariantConfiguratorOverviewLayoutModule } from '@spartacus/product-configurator/rulebased/root';

@NgModule({
imports: [RulebasedConfiguratorRootModule],
imports: [
RulebasedConfiguratorRootModule,
VariantConfiguratorInteractiveLayoutModule,
VariantConfiguratorOverviewLayoutModule,
],
providers: [
provideConfig({
featureModules: {
Expand Down

0 comments on commit bd79df1

Please sign in to comment.