From bd0a6f745760ce51c1011ea1f800f2e59bb84cec Mon Sep 17 00:00:00 2001 From: Andreas Steinmann Date: Thu, 5 Sep 2024 16:39:27 +0200 Subject: [PATCH] feat(accessibility): improve TAB key navigation in mini basket by using ngbDropdown (#1694) --- .../mini-basket/mini-basket.component.html | 8 ++--- .../mini-basket/mini-basket.component.spec.ts | 34 +++++++++++-------- .../mini-basket/mini-basket.component.ts | 13 ++++--- .../components/header/header-sticky.scss | 4 +++ .../components/header/language-switch.scss | 1 - src/styles/pages/checkout/quick-cart.scss | 13 ++++--- src/styles/themes/b2b/variables.scss | 2 +- src/styles/themes/b2c/variables.scss | 2 +- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/app/shell/header/mini-basket/mini-basket.component.html b/src/app/shell/header/mini-basket/mini-basket.component.html index 4615674a49..d8ba62e5f0 100644 --- a/src/app/shell/header/mini-basket/mini-basket.component.html +++ b/src/app/shell/header/mini-basket/mini-basket.component.html @@ -3,15 +3,15 @@ [ngClass]="{ 'd-none': view !== 'full', 'd-md-block': view !== 'small', 'mini-cart-active': !isCollapsed }" ishClickOutside (isClickedOutside)="collapse()" + ngbDropdown + #miniBasketDropdown="ngbDropdown" > -
+
diff --git a/src/app/shell/header/mini-basket/mini-basket.component.spec.ts b/src/app/shell/header/mini-basket/mini-basket.component.spec.ts index a194d15a08..842b5ad06e 100644 --- a/src/app/shell/header/mini-basket/mini-basket.component.spec.ts +++ b/src/app/shell/header/mini-basket/mini-basket.component.spec.ts @@ -1,5 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { MockComponent } from 'ng-mocks'; import { EMPTY, of } from 'rxjs'; @@ -34,7 +36,7 @@ describe('Mini Basket Component', () => { MockComponent(LazyMiniBasketContentComponent), PricePipe, ], - imports: [TranslateModule.forRoot()], + imports: [NgbDropdownModule, TranslateModule.forRoot()], providers: [ { provide: AccountFacade, useFactory: () => instance(accountFacade) }, { provide: AppFacade, useFactory: () => instance(appFacade) }, @@ -86,21 +88,23 @@ describe('Mini Basket Component', () => { expect(element.textContent.replace(/ /g, '')).toMatchInlineSnapshot(`"30items/$141,796.98"`); }); - it('should set isCollapsed to proper value if toggleCollapsed is called', () => { - component.isCollapsed = true; - component.toggleCollapse(); - expect(component.isCollapsed).toBeFalsy(); - component.toggleCollapse(); - expect(component.isCollapsed).toBeTruthy(); - }); + it('should toggle dropdown menu when clicked', () => { + const toggleButton = fixture.debugElement.query(By.css('button[ngbDropdownToggle]')).nativeElement; + toggleButton.click(); + fixture.detectChanges(); - it('should set isCollapsed to true if collapse() is called', () => { - component.collapse(); - expect(component.isCollapsed).toBeTruthy(); - }); + const dropdownMenu = fixture.debugElement.query(By.css('div[ngbDropdownMenu]')); + // menu should be present + expect(dropdownMenu).toBeTruthy(); - it('should set isCollapsed to false if open() is called', () => { - component.open(); - expect(component.isCollapsed).toBeFalsy(); + // check if menu is shown + expect(dropdownMenu.nativeElement.classList).toContain('show'); + + // click again to close + toggleButton.click(); + fixture.detectChanges(); + + // menu should be hidden + expect(dropdownMenu.nativeElement.classList).not.toContain('show'); }); }); diff --git a/src/app/shell/header/mini-basket/mini-basket.component.ts b/src/app/shell/header/mini-basket/mini-basket.component.ts index 866d2ff8b6..46a36b8e74 100644 --- a/src/app/shell/header/mini-basket/mini-basket.component.ts +++ b/src/app/shell/header/mini-basket/mini-basket.component.ts @@ -6,9 +6,11 @@ import { DestroyRef, Input, OnInit, + ViewChild, inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; import { Observable, concat, of, timer } from 'rxjs'; import { filter, map, switchMap } from 'rxjs/operators'; @@ -24,6 +26,8 @@ import { whenTruthy } from 'ish-core/utils/operators'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class MiniBasketComponent implements OnInit { + @ViewChild('miniBasketDropdown', { static: true }) miniBasketDropdown!: NgbDropdown; + basketAnimation$: Observable; itemTotal$: Observable; itemCount$: Observable; @@ -74,18 +78,12 @@ export class MiniBasketComponent implements OnInit { }); } - /** - * Toggle the collapse state of the mini basket programmatically. - */ - toggleCollapse() { - this.isCollapsed = !this.isCollapsed; - } - /** * Collapse the mini basket programmatically. */ collapse() { this.isCollapsed = true; + this.miniBasketDropdown.close(); this.cdRef.markForCheck(); } @@ -95,6 +93,7 @@ export class MiniBasketComponent implements OnInit { // visible-for-testing open() { this.isCollapsed = false; + this.miniBasketDropdown.open(); this.cdRef.markForCheck(); } } diff --git a/src/styles/components/header/header-sticky.scss b/src/styles/components/header/header-sticky.scss index 142c668150..9d153e69b4 100644 --- a/src/styles/components/header/header-sticky.scss +++ b/src/styles/components/header/header-sticky.scss @@ -145,6 +145,10 @@ } } } + + .mini-cart { + top: -2px !important; + } } } diff --git a/src/styles/components/header/language-switch.scss b/src/styles/components/header/language-switch.scss index 90c4a2d658..925358eca0 100644 --- a/src/styles/components/header/language-switch.scss +++ b/src/styles/components/header/language-switch.scss @@ -6,7 +6,6 @@ .dropdown-menu { position: absolute; min-width: 50px; - margin-top: 0; } .language-switch-button { diff --git a/src/styles/pages/checkout/quick-cart.scss b/src/styles/pages/checkout/quick-cart.scss index 67cac9b45b..574547b515 100644 --- a/src/styles/pages/checkout/quick-cart.scss +++ b/src/styles/pages/checkout/quick-cart.scss @@ -2,7 +2,7 @@ .quick-cart-link { position: relative; - padding: 0 $space-default; + padding: 0; margin-left: $space-default; background: $color-tertiary; @@ -26,10 +26,14 @@ font-size: 17px; color: $text-color-secondary; } + + &::after { + display: none; + } } > .btn-link { - padding: $space-default * 0.5; + padding: ($space-default * 0.5) $space-default; white-space: nowrap; } @@ -49,7 +53,8 @@ .mini-cart { position: absolute; - right: -1px; + top: -1px !important; + // left: 1px !important; z-index: 9999; width: 310px; padding: $space-default; @@ -91,7 +96,7 @@ @include clearfix(); - padding: $space-default 0; + padding: $space-default 2px $space-default 0; & ~ .product-row { border-top: $border-width-default solid $border-color-light; diff --git a/src/styles/themes/b2b/variables.scss b/src/styles/themes/b2b/variables.scss index 5a7357b73d..9362c64759 100644 --- a/src/styles/themes/b2b/variables.scss +++ b/src/styles/themes/b2b/variables.scss @@ -216,7 +216,7 @@ $input-accent-color: $CORPORATE-DARK; // Dropdown // Dropdown menu container and contents. -$dropdown-border-color: rgb(0 0 0 / 0.15); +$dropdown-border-color: $color-inverse; // Grid breakpoints // Define the minimum dimensions at which your layout will change, diff --git a/src/styles/themes/b2c/variables.scss b/src/styles/themes/b2c/variables.scss index 3b406bd6fd..b87335a731 100644 --- a/src/styles/themes/b2c/variables.scss +++ b/src/styles/themes/b2c/variables.scss @@ -220,7 +220,7 @@ $input-accent-color: $CORPORATE-DARK; // Dropdown // Dropdown menu container and contents. -$dropdown-border-color: rgb(0 0 0 / 0.15); +$dropdown-border-color: $color-inverse; // Grid breakpoints // Define the minimum dimensions at which your layout will change,