feat: add configurable display options for categories and search bar in store header
This commit is contained in:
@@ -19,46 +19,50 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-12 px-0 d-md-none order-3">
|
||||
<hr class="store-layout__mobile-separator">
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md order-4 order-md-2">
|
||||
<div class="store-layout__search-wrapper ms-md-auto">
|
||||
<div class="input-group store-layout__search" role="search">
|
||||
<label class="visually-hidden" for="store-search-input">Buscar productos</label>
|
||||
<input
|
||||
id="store-search-input"
|
||||
type="search"
|
||||
class="form-control store-layout__search-input border-end-0 rounded-start"
|
||||
placeholder="Buscar productos"
|
||||
autocomplete="off"
|
||||
[attr.aria-describedby]="showSearchError() ? 'store-search-error' : null"
|
||||
[attr.aria-invalid]="showSearchError()"
|
||||
[minlength]="minSearchLength"
|
||||
[formControl]="searchControl"
|
||||
(input)="clearSearchError()"
|
||||
(keydown.enter)="submitSearch($event)"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn store-layout__search-button border-start-0 rounded-end px-3"
|
||||
aria-label="Buscar"
|
||||
(click)="submitSearch()"
|
||||
>
|
||||
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (showSearchError()) {
|
||||
<p id="store-search-error" class="store-layout__search-error mb-0" role="alert">
|
||||
Ingresá al menos {{ minSearchLength }} caracteres para buscar.
|
||||
</p>
|
||||
}
|
||||
@if (displaySeachBar()) {
|
||||
<div class="col-12 px-0 d-md-none order-3">
|
||||
<hr class="store-layout__mobile-separator" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="store-layout__actions-column col-auto col-md-auto order-2 order-md-3 ms-auto d-flex flex-row align-items-center justify-content-end gap-2 gap-md-3">
|
||||
<div class="col-12 col-md order-4 order-md-2">
|
||||
<div class="store-layout__search-wrapper ms-md-auto">
|
||||
<div class="input-group store-layout__search" role="search">
|
||||
<label class="visually-hidden" for="store-search-input">Buscar productos</label>
|
||||
<input
|
||||
id="store-search-input"
|
||||
type="search"
|
||||
class="form-control store-layout__search-input border-end-0 rounded-start"
|
||||
placeholder="Buscar productos"
|
||||
autocomplete="off"
|
||||
[attr.aria-describedby]="showSearchError() ? 'store-search-error' : null"
|
||||
[attr.aria-invalid]="showSearchError()"
|
||||
[minlength]="minSearchLength"
|
||||
[formControl]="searchControl"
|
||||
(input)="clearSearchError()"
|
||||
(keydown.enter)="submitSearch($event)"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn store-layout__search-button border-start-0 rounded-end px-3"
|
||||
aria-label="Buscar"
|
||||
(click)="submitSearch()"
|
||||
>
|
||||
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (showSearchError()) {
|
||||
<p id="store-search-error" class="store-layout__search-error mb-0" role="alert">
|
||||
Ingresá al menos {{ minSearchLength }} caracteres para buscar.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div
|
||||
class="store-layout__actions-column col-auto col-md-auto order-2 order-md-3 ms-auto d-flex flex-row align-items-center justify-content-end gap-2 gap-md-3"
|
||||
>
|
||||
@if (ticketsMenu(); as menu) {
|
||||
<app-button
|
||||
variant="primary"
|
||||
@@ -108,24 +112,29 @@
|
||||
/>
|
||||
|
||||
@if (isMobileMenuOpen()) {
|
||||
<div id="store-mobile-menu" class="position-absolute end-0 bg-white rounded shadow mt-2 py-2 store-layout__mobile-dropdown-menu">
|
||||
<a
|
||||
class="dropdown-item d-flex align-items-center gap-2 px-3 py-2 text-decoration-none store-layout__mobile-dropdown-item"
|
||||
<div
|
||||
id="store-mobile-menu"
|
||||
class="position-absolute end-0 bg-white rounded shadow mt-2 py-2 store-layout__mobile-dropdown-menu"
|
||||
>
|
||||
<a
|
||||
class="dropdown-item d-flex align-items-center gap-2 px-3 py-2 text-decoration-none store-layout__mobile-dropdown-item"
|
||||
(click)="onMobileAccountClick()"
|
||||
>
|
||||
<i class="fa-regular fa-circle-user fa-lg"></i>
|
||||
<span class="fw-medium">Mi cuenta</span>
|
||||
</a>
|
||||
|
||||
<hr class="my-1 border-secondary opacity-25">
|
||||
@if (displayCategories()) {
|
||||
<hr class="my-1 border-secondary opacity-25" />
|
||||
|
||||
@for (category of categories(); track category.id) {
|
||||
<a
|
||||
class="dropdown-item px-3 py-2 text-decoration-none store-layout__mobile-dropdown-item"
|
||||
(click)="onCategorySelect(category)"
|
||||
>
|
||||
<span class="fw-medium">{{ category.nombre }}</span>
|
||||
</a>
|
||||
@for (category of categories(); track category.id) {
|
||||
<a
|
||||
class="dropdown-item px-3 py-2 text-decoration-none store-layout__mobile-dropdown-item"
|
||||
(click)="onCategorySelect(category)"
|
||||
>
|
||||
<span class="fw-medium">{{ category.nombre }}</span>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -135,28 +144,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="store-layout__categories d-none d-md-block">
|
||||
<div class="container-xl px-3 px-md-4">
|
||||
<div class="store-layout__category-menu">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-link d-inline-flex align-items-center gap-2 px-0 py-2 text-uppercase fw-bold text-decoration-none store-layout__category-trigger"
|
||||
aria-label="Categorias"
|
||||
aria-controls="store-category-dropdown"
|
||||
[attr.aria-expanded]="isCategoryDropdownOpen()"
|
||||
(click)="toggleCategoryDropdown()"
|
||||
>
|
||||
<span>Categorias</span>
|
||||
<i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
@if (displayCategories()) {
|
||||
<div class="store-layout__categories d-none d-md-block">
|
||||
<div class="container-xl px-3 px-md-4">
|
||||
<div class="store-layout__category-menu">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-link d-inline-flex align-items-center gap-2 px-0 py-2 text-uppercase fw-bold text-decoration-none store-layout__category-trigger"
|
||||
aria-label="Categorias"
|
||||
aria-controls="store-category-dropdown"
|
||||
[attr.aria-expanded]="isCategoryDropdownOpen()"
|
||||
(click)="toggleCategoryDropdown()"
|
||||
>
|
||||
<span>Categorias</span>
|
||||
<i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<app-category-dropdown
|
||||
id="store-category-dropdown"
|
||||
[categories]="categories()"
|
||||
[open]="isCategoryDropdownOpen()"
|
||||
(categorySelect)="onCategorySelect($event)"
|
||||
/>
|
||||
<app-category-dropdown
|
||||
id="store-category-dropdown"
|
||||
[categories]="categories()"
|
||||
[open]="isCategoryDropdownOpen()"
|
||||
(categorySelect)="onCategorySelect($event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</header>
|
||||
|
||||
@@ -35,6 +35,8 @@ export class StoreHeaderComponent {
|
||||
readonly accountNavigationMenus = input<readonly Menu[]>([]);
|
||||
readonly ticketsMenu = input<Menu | null>(null);
|
||||
readonly categories = input<readonly Category[]>([]);
|
||||
readonly displayCategories = input(true);
|
||||
readonly displaySeachBar = input(true);
|
||||
readonly cartClick = output<void>();
|
||||
readonly ticketsClick = output<void>();
|
||||
readonly loginClick = output<void>();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
[accountNavigationMenus]="accountNavigationMenus()"
|
||||
[ticketsMenu]="ticketsMenu() ?? null"
|
||||
[categories]="tenant()?.categories ?? []"
|
||||
[displayCategories]="tenant()?.display_categories ?? true"
|
||||
[displaySeachBar]="tenant()?.display_seach_bar ?? true"
|
||||
(cartClick)="isCartOpen.set(!isCartOpen())"
|
||||
(ticketsClick)="onTicketsClick()"
|
||||
(loginClick)="onLoginClick()"
|
||||
@@ -16,10 +18,7 @@
|
||||
/>
|
||||
|
||||
@if (isCartOpen()) {
|
||||
<div
|
||||
class="store-layout__cart-overlay"
|
||||
(click)="isCartOpen.set(false)"
|
||||
></div>
|
||||
<div class="store-layout__cart-overlay" (click)="isCartOpen.set(false)"></div>
|
||||
<div class="store-layout__cart-dropdown card border-0 shadow-lg">
|
||||
<app-cart
|
||||
[showClose]="true"
|
||||
@@ -30,10 +29,7 @@
|
||||
[backgroundColor]="'#ffffff'"
|
||||
(closed)="isCartOpen.set(false)"
|
||||
>
|
||||
<app-button
|
||||
variant="secondary"
|
||||
class="flex-grow-1"
|
||||
(click)="isCartOpen.set(false)"
|
||||
<app-button variant="secondary" class="flex-grow-1" (click)="isCartOpen.set(false)"
|
||||
>Seguir comprando</app-button
|
||||
>
|
||||
<app-button
|
||||
|
||||
@@ -204,6 +204,29 @@ describe('StoreLayoutComponent', () => {
|
||||
expect(compiled.querySelector('app-store-footer .store-layout__footer')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('hides the configured header elements when the tenant disables them', () => {
|
||||
tenantState.set({
|
||||
...tenant,
|
||||
categories: [{ id: 1, nombre: 'Remeras', subcategories: [] }],
|
||||
display_categories: false,
|
||||
display_seach_bar: false,
|
||||
});
|
||||
|
||||
const fixture = TestBed.createComponent(StoreLayoutComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
|
||||
expect(compiled.querySelector('#store-search-input')).toBeNull();
|
||||
expect(compiled.querySelector('.store-layout__category-trigger')).toBeNull();
|
||||
|
||||
const header = fixture.debugElement.query(By.directive(StoreHeaderComponent));
|
||||
(header.componentInstance as any).toggleMobileMenu();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(compiled.querySelectorAll('.store-layout__mobile-dropdown-item')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders the tenant branding and action icons', () => {
|
||||
const fixture = TestBed.createComponent(StoreLayoutComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -114,6 +114,8 @@ export interface Tenant {
|
||||
search_product_layout?: 'row' | 'column_with_image' | 'column_with_cart';
|
||||
search_group_layout?: 'paginated' | 'simple' | 'simple_vertical' | 'carousel';
|
||||
search_items_per_page?: number;
|
||||
display_categories?: boolean;
|
||||
display_seach_bar?: boolean;
|
||||
social_media?: SocialMedia[];
|
||||
menues?: Menu[];
|
||||
categories: Category[];
|
||||
|
||||
@@ -222,10 +222,7 @@
|
||||
}
|
||||
|
||||
.hero-action ::ng-deep .btn {
|
||||
min-height: 1.875rem;
|
||||
padding: 0.4rem 1.35rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.625rem;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.event-card-container {
|
||||
|
||||
Reference in New Issue
Block a user