Search code examples
flexboxangular9sap-commerce-cloudspartacus-storefront

Custom Header Layout - Spartacus Storefront


Has anyone solved or knows how to solve the following situation given the implementation of the header in Spartacus?

I would like to show in the header, a layout on the right of two level blocks, and on the left of a single level block.

Objective Header Layout

I can't think of how to do it since I can't see how to wrap certain slots, given the Spartacus implementation for the header. Given the implementation of the header in Spartacus, inside the StorefrontComponent I cannot replace it using the ConfigModule.withConfig ({...}, as CmsConfig)

I understand and already tried that I can replace the header, implementing an Outlet (cxOutletRef = "header"), but this makes it impossible to edit it through SmartEdit, which is not acceptable to me.

Any suggestion? Or possible solution?

As a last option it occurs to me that I can create a component type from the back, and map it from Angular using "ConfigModule.withConfig ({...}, as CmsConfig)" implementing the "conflicting two-level" block from scratch or even the entire header.

Thank you !

////// CORRECTION 09/23/20 //////

Outlets do not prevent editing via SmartEdit. It's necessary to indicate the Slot to which the component corresponds, this is easy to implement using the PageSlotComponent.

✔ Example:

<ng-template cxOutletRef="cx-header">
      <header
        cxSkipLink="cx-header"
        [cxFocus]="{ disableMouseFocus: true }"
        [class.is-expanded]="isExpanded$ | async"
        (keydown.escape)="collapseMenu()"
        (click)="collapseMenuIfClickOutside($event)"
      >
        <cx-page-slot position="MiniCart"></cx-page-slot>
      </header>
      <cx-page-slot position="BottomHeaderSlot"> </cx-page-slot>
      <cx-global-message></cx-global-message>
    </ng-template>

In this way, SmartEdit does allow you to edit the MiniCart component, within its corresponding slot.

🚫 Wrong way:

<ng-template cxOutletRef="cx-header">
  <header
    cxSkipLink="cx-header"
    [cxFocus]="{ disableMouseFocus: true }"
    [class.is-expanded]="isExpanded$ | async"
    (keydown.escape)="collapseMenu()"
    (click)="collapseMenuIfClickOutside($event)"
  >
    <cx-mini-cart></cx-mini-cart>
  </header>
  <cx-page-slot position="BottomHeaderSlot"> </cx-page-slot>
  <cx-global-message></cx-global-message>
</ng-template>

Solution

  • you can indeed solve this with a custom layout configuration and additional CSS, but it's not necessary. I give you a few options to consider:

    Option 1: Change the generated DOM

    You can either provide a custom layout config as @pwavg suggests, or even introducing a custom storefront component. If you introduce a custom layout config, you're limited by the sections we use in the storefront component. If you insist on custom sections (ie. an element that wraps the searchbox, login, mincart and nav), you need to introduce a custom storefront component. The disadvantage here is that you'll deviating away from standard Spartacus component, which might result in missing features in the future.

    Option 2: Pure CSS

    A pure CSS solution is the easiest. You do not need to change any actual DOM, but apply some custom CSS rules to the standard DOM. Grid system is indeed designed for this. It's a bit complex to start with, but would do the job. You can actually achieve this with flexbox as well, but you'd need to move the logo slot out of the flexbox flow.

    Here's an actual quick and dirty code snippet to demonstrate changing by a few CSS rules only. It comes with a few assumptions/limitations, but for most cases it might be fine.

    
    header {
      cx-page-slot.SiteLogo {
        // we absolute position the logo, so it flows outside the flexbox system. this requires
        // an hard-coded top position, that might be fine, but I don't know the details. 
        position: absolute;
        top: 60px;
      }
    
      cx-page-slot.SearchBox {
        // align searchbox to the left, not sure if that's required, but looks better
        margin: 14px auto 14px 150px;
      }
    
      cx-page-slot.NavigationBar {
        margin-left: 150px;
        overflow: hidden;
      }
    
      // manipulate a very high logo to demonstrate this works
      cx-page-slot.SiteLogo img {
        height: 100px;
      }
    }
    
    

    Result (sorry for the logo ;) ) enter image description here

    Option 3: cx-header Outlet

    I would say you should be able to use outlets as well, as this will get you closer to option 1 (changing the actual DOM). I can't think of a reason why it would not work in SmartEdit - but happy to learn if it is the case. I'd recommend in this case to use the cx-header outletRef, so you would replace the entire header.