feat(store-header): implement immersive categories and update header properties
This commit is contained in:
@@ -1,16 +1,11 @@
|
|||||||
<header
|
<header
|
||||||
class="immersive-header"
|
class="immersive-header"
|
||||||
[class.immersive-header--compact]="!showHero()"
|
[class.immersive-header--compact]="!showHero()"
|
||||||
[class.immersive-header--has-image]="showHero() && !!backgroundImageUrl()"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="immersive-header__backdrop"
|
class="immersive-header__backdrop"
|
||||||
[style.background-image]="backgroundImageUrl() ? 'url(' + backgroundImageUrl() + ')' : null"
|
[style.background-image]="backgroundImageUrl() ? 'url(' + backgroundImageUrl() + ')' : null"
|
||||||
>
|
></div>
|
||||||
@if (showHero() && backgroundImageUrl(); as imageUrl) {
|
|
||||||
<img class="immersive-header__backdrop-size" [src]="imageUrl" alt="" aria-hidden="true" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="immersive-header__content">
|
<div class="immersive-header__content">
|
||||||
<nav class="immersive-header__nav" aria-label="Navegación de la tienda">
|
<nav class="immersive-header__nav" aria-label="Navegación de la tienda">
|
||||||
|
|||||||
@@ -5,20 +5,18 @@
|
|||||||
.immersive-header {
|
.immersive-header {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
min-height: 26rem;
|
min-height: 640px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.immersive-header--has-image {
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.immersive-header__content {
|
.immersive-header__content {
|
||||||
grid-area: 1 / 1;
|
grid-area: 1 / 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
min-width: 0;
|
||||||
padding-top: clamp(1.75rem, 4vw, 2.5rem);
|
padding-top: clamp(1.75rem, 4vw, 2.5rem);
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -32,13 +30,6 @@
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.immersive-header__backdrop-size {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.immersive-header__backdrop::after {
|
.immersive-header__backdrop::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -47,7 +38,7 @@
|
|||||||
180deg,
|
180deg,
|
||||||
rgba(0, 0, 0, 0.48) 0%,
|
rgba(0, 0, 0, 0.48) 0%,
|
||||||
rgba(0, 0, 0, 0.4) 47%,
|
rgba(0, 0, 0, 0.4) 47%,
|
||||||
rgba(245, 245, 245, 0.88) 100%
|
#f5f5f5 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,3 +50,19 @@
|
|||||||
(categorySelect)="categorySelect.emit($event)"
|
(categorySelect)="categorySelect.emit($event)"
|
||||||
/>
|
/>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@if (headerType() === 'immersive' && displayCategories() && categories().length) {
|
||||||
|
<nav class="immersive-categories" aria-label="Categorías">
|
||||||
|
<div class="immersive-categories__list">
|
||||||
|
@for (category of categories(); track category.id) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="immersive-categories__item"
|
||||||
|
(click)="categorySelect.emit(category)"
|
||||||
|
>
|
||||||
|
{{ category.nombre | uppercase }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.immersive-categories {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.immersive-categories__list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: clamp(1rem, 3vw, 2.5rem);
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 3.5rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.immersive-categories__item {
|
||||||
|
flex: none;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.immersive-categories__item:hover,
|
||||||
|
.immersive-categories__item:focus-visible {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet, UpperCasePipe } from '@angular/common';
|
||||||
import { Component, input, output } from '@angular/core';
|
import { Component, input, output } from '@angular/core';
|
||||||
import { AuthUser } from '../../../services/auth/auth.interfaces';
|
import { AuthUser } from '../../../services/auth/auth.interfaces';
|
||||||
import { Category, Menu, WebsiteExtras } from '../../../services/tenant.interface';
|
import { Category, Menu, WebsiteExtras } from '../../../services/tenant.interface';
|
||||||
@@ -9,8 +9,9 @@ export type StoreHeaderType = 'standard' | 'immersive';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-store-header',
|
selector: 'app-store-header',
|
||||||
imports: [NgTemplateOutlet, StandardStoreHeaderComponent, ImmersiveStoreHeaderComponent],
|
imports: [NgTemplateOutlet, UpperCasePipe, StandardStoreHeaderComponent, ImmersiveStoreHeaderComponent],
|
||||||
templateUrl: './store-header.component.html',
|
templateUrl: './store-header.component.html',
|
||||||
|
styleUrl: './store-header.component.scss',
|
||||||
})
|
})
|
||||||
export class StoreHeaderComponent {
|
export class StoreHeaderComponent {
|
||||||
readonly headerType = input<StoreHeaderType>('standard');
|
readonly headerType = input<StoreHeaderType>('standard');
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
ngSkipHydration
|
ngSkipHydration
|
||||||
[headerType]="tenant()?.header_type ?? 'standard'"
|
[headerType]="tenant()?.header_type ?? 'standard'"
|
||||||
[heroContent]="tenant()?.extras?.immersiveHero"
|
[heroContent]="tenant()?.extras?.immersiveHero"
|
||||||
[carouselImages]="tenant()?.extras?.heroCarousel ?? []"
|
[carouselImages]="tenant()?.extras?.immersiveHeroCarousel ?? []"
|
||||||
[showHero]="isHomeRoute()"
|
[showHero]="isHomeRoute()"
|
||||||
[cartQuantity]="cartQuantity()"
|
[cartQuantity]="cartQuantity()"
|
||||||
[logoUrl]="tenant()?.header_logo ?? null"
|
[logoUrl]="tenant()?.header_logo ?? null"
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ describe('StoreLayoutComponent', () => {
|
|||||||
title: 'experiencia',
|
title: 'experiencia',
|
||||||
description: 'Reservá tu entrada',
|
description: 'Reservá tu entrada',
|
||||||
},
|
},
|
||||||
heroCarousel: ['/first.jpg', '/second.jpg'],
|
immersiveHeroCarousel: ['/first.jpg', '/second.jpg'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -288,6 +288,41 @@ describe('StoreLayoutComponent', () => {
|
|||||||
expect(element.querySelector('app-cart-icon')).not.toBeNull();
|
expect(element.querySelector('app-cart-icon')).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows immersive categories below the header and navigates on selection', () => {
|
||||||
|
tenantState.set({
|
||||||
|
...tenant,
|
||||||
|
header_type: 'immersive',
|
||||||
|
display_categories: true,
|
||||||
|
categories: [{ id: 11, nombre: 'Música', subcategories: [] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(StoreLayoutComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
|
const header = element.querySelector('app-immersive-store-header header');
|
||||||
|
const categories = element.querySelector('app-store-header .immersive-categories');
|
||||||
|
const categoryButton = categories?.querySelector<HTMLButtonElement>('button');
|
||||||
|
|
||||||
|
expect(header).not.toBeNull();
|
||||||
|
expect(categories).not.toBeNull();
|
||||||
|
expect(header?.contains(categories)).toBe(false);
|
||||||
|
expect(categoryButton?.textContent?.trim()).toBe('MÚSICA');
|
||||||
|
|
||||||
|
const navigate = vi.spyOn(TestBed.inject(Router), 'navigate');
|
||||||
|
categoryButton?.click();
|
||||||
|
expect(navigate).toHaveBeenCalledWith(['/categoria', 11], { queryParams: { page: 1 } });
|
||||||
|
|
||||||
|
tenantState.set({
|
||||||
|
...tenant,
|
||||||
|
header_type: 'immersive',
|
||||||
|
display_categories: false,
|
||||||
|
categories: [{ id: 11, nombre: 'Música', subcategories: [] }],
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('.immersive-categories')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it('opens the cart when requested through the openCart query parameter', () => {
|
it('opens the cart when requested through the openCart query parameter', () => {
|
||||||
const fixture = TestBed.createComponent(StoreLayoutComponent);
|
const fixture = TestBed.createComponent(StoreLayoutComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export interface TenantEvent {
|
|||||||
|
|
||||||
export interface WebsiteExtras {
|
export interface WebsiteExtras {
|
||||||
carousel?: string[];
|
carousel?: string[];
|
||||||
heroCarousel?: string[];
|
immersiveHeroCarousel?: string[];
|
||||||
immersiveHero?: {
|
immersiveHero?: {
|
||||||
eyebrow: string;
|
eyebrow: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user