diff --git a/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-interactive.module.ts b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-interactive.module.ts index dc46749cee2..9b39b46290b 100644 --- a/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-interactive.module.ts +++ b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-interactive.module.ts @@ -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'; @@ -30,7 +31,7 @@ import { defaultCpqInteractiveRoutingConfig } from './default-cpq-interactive-ro ], providers: [ provideDefaultConfig(defaultCpqInteractiveRoutingConfig), - provideDefaultConfig({ + provideDefaultConfig({ layoutSlots: { CpqConfigurationTemplate: { header: { diff --git a/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-layout.module.ts b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-layout.module.ts new file mode 100644 index 00000000000..b6da93d7183 --- /dev/null +++ b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-layout.module.ts @@ -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({ + 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 {} diff --git a/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-page-layout-handler.spec.ts b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-page-layout-handler.spec.ts new file mode 100644 index 00000000000..d091ca1e562 --- /dev/null +++ b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-page-layout-handler.spec.ts @@ -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 { + 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 + ); + }); + + 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$); + }); +}); diff --git a/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-page-layout-handler.ts b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-page-layout-handler.ts new file mode 100644 index 00000000000..fff7c9a01d6 --- /dev/null +++ b/feature-libs/product-configurator/rulebased/root/cpq/cpq-configurator-page-layout-handler.ts @@ -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, + 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$; + } +} diff --git a/feature-libs/product-configurator/rulebased/root/cpq/index.ts b/feature-libs/product-configurator/rulebased/root/cpq/index.ts index 1fda03488cf..e64f3e1bd0b 100644 --- a/feature-libs/product-configurator/rulebased/root/cpq/index.ts +++ b/feature-libs/product-configurator/rulebased/root/cpq/index.ts @@ -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'; diff --git a/feature-libs/product-configurator/rulebased/root/variant/index.ts b/feature-libs/product-configurator/rulebased/root/variant/index.ts index 36bb77f4758..9140c1f65e5 100644 --- a/feature-libs/product-configurator/rulebased/root/variant/index.ts +++ b/feature-libs/product-configurator/rulebased/root/variant/index.ts @@ -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'; diff --git a/feature-libs/product-configurator/rulebased/root/variant/variant-configurator-interactive-layout.module.ts b/feature-libs/product-configurator/rulebased/root/variant/variant-configurator-interactive-layout.module.ts new file mode 100644 index 00000000000..4cec1c64276 --- /dev/null +++ b/feature-libs/product-configurator/rulebased/root/variant/variant-configurator-interactive-layout.module.ts @@ -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({ + 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 {} diff --git a/feature-libs/product-configurator/rulebased/root/variant/variant-configurator-overview-layout.module.ts b/feature-libs/product-configurator/rulebased/root/variant/variant-configurator-overview-layout.module.ts new file mode 100644 index 00000000000..4bd093d12b6 --- /dev/null +++ b/feature-libs/product-configurator/rulebased/root/variant/variant-configurator-overview-layout.module.ts @@ -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({ + layoutSlots: { + VariantConfigurationOverviewTemplate: { + header: { + md: { + slots: ['SiteLogo', 'MiniCart'], + }, + xs: { + slots: ['SiteLogo', 'MiniCart'], + }, + }, + slots: [ + 'VariantConfigOverviewHeader', + 'VariantConfigOverviewBanner', + 'VariantConfigOverviewContent', + 'VariantConfigOverviewBottombar', + ], + }, + }, + }), + ], +}) +export class VariantConfiguratorOverviewLayoutModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts index ed150247643..9ed55e3e5f2 100644 --- a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts @@ -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: { diff --git a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts index 50497ecbe64..e25c1a61f89 100644 --- a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts @@ -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: {