diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index bb2483daf3b..3c46085c3d7 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5588,6 +5588,9 @@ "showLess": { "message": "Show less" }, + "next": { + "message": "Next" + }, "moreBreadcrumbs": { "message": "More breadcrumbs", "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index c805096189b..1094a8be26f 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -4080,5 +4080,8 @@ "moreBreadcrumbs": { "message": "More breadcrumbs", "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." + }, + "next": { + "message": "Next" } } diff --git a/libs/vault/src/components/carousel/carousel.component.html b/libs/vault/src/components/carousel/carousel.component.html index 04c6e559056..778b70e15e2 100644 --- a/libs/vault/src/components/carousel/carousel.component.html +++ b/libs/vault/src/components/carousel/carousel.component.html @@ -6,18 +6,40 @@ #container > -
- +
+ +
+ +
+
diff --git a/libs/vault/src/components/carousel/carousel.component.spec.ts b/libs/vault/src/components/carousel/carousel.component.spec.ts index 1409aea0cb2..ebb38576813 100644 --- a/libs/vault/src/components/carousel/carousel.component.spec.ts +++ b/libs/vault/src/components/carousel/carousel.component.spec.ts @@ -2,6 +2,8 @@ import { Component } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { VaultCarouselSlideComponent } from "./carousel-slide/carousel-slide.component"; import { VaultCarouselComponent } from "./carousel.component"; @@ -33,6 +35,7 @@ describe("VaultCarouselComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [VaultCarouselComponent, VaultCarouselSlideComponent], + providers: [{ provide: I18nService, useValue: { t: (key: string) => key } }], }).compileComponents(); }); @@ -48,7 +51,7 @@ describe("VaultCarouselComponent", () => { it("shows the active slides content", () => { // Set the second slide as active - fixture.debugElement.queryAll(By.css("button"))[1].nativeElement.click(); + fixture.debugElement.queryAll(By.css("button"))[2].nativeElement.click(); fixture.detectChanges(); const heading = fixture.debugElement.query(By.css("h1")).nativeElement; @@ -63,10 +66,37 @@ describe("VaultCarouselComponent", () => { it('emits "slideChange" event when slide changes', () => { jest.spyOn(component.slideChange, "emit"); - const thirdSlideButton = fixture.debugElement.queryAll(By.css("button"))[2]; + const thirdSlideButton = fixture.debugElement.queryAll(By.css("button"))[3]; thirdSlideButton.nativeElement.click(); expect(component.slideChange.emit).toHaveBeenCalledWith(2); }); + + it('advances to the next slide when the "next" button is pressed', () => { + const middleSlideButton = fixture.debugElement.queryAll(By.css("button"))[2]; + const nextButton = fixture.debugElement.queryAll(By.css("button"))[4]; + + middleSlideButton.nativeElement.click(); + + jest.spyOn(component.slideChange, "emit"); + + nextButton.nativeElement.click(); + + expect(component.slideChange.emit).toHaveBeenCalledWith(2); + }); + + it('advances to the previous slide when the "back" button is pressed', async () => { + const middleSlideButton = fixture.debugElement.queryAll(By.css("button"))[2]; + const backButton = fixture.debugElement.queryAll(By.css("button"))[0]; + + middleSlideButton.nativeElement.click(); + await new Promise((r) => setTimeout(r, 100)); // Give time for the DOM to update. + + jest.spyOn(component.slideChange, "emit"); + + backButton.nativeElement.click(); + + expect(component.slideChange.emit).toHaveBeenCalledWith(0); + }); }); diff --git a/libs/vault/src/components/carousel/carousel.component.ts b/libs/vault/src/components/carousel/carousel.component.ts index f2d211697df..fdebbebc33b 100644 --- a/libs/vault/src/components/carousel/carousel.component.ts +++ b/libs/vault/src/components/carousel/carousel.component.ts @@ -20,7 +20,9 @@ import { import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { take } from "rxjs"; -import { ButtonModule } from "@bitwarden/components"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, IconButtonModule } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { VaultCarouselButtonComponent } from "./carousel-button/carousel-button.component"; import { VaultCarouselContentComponent } from "./carousel-content/carousel-content.component"; @@ -32,9 +34,12 @@ import { VaultCarouselSlideComponent } from "./carousel-slide/carousel-slide.com imports: [ CdkPortalOutlet, CommonModule, + JslibModule, + IconButtonModule, ButtonModule, VaultCarouselContentComponent, VaultCarouselButtonComponent, + I18nPipe, ], }) export class VaultCarouselComponent implements AfterViewInit { @@ -97,6 +102,18 @@ export class VaultCarouselComponent implements AfterViewInit { this.slideChange.emit(index); } + protected nextSlide() { + if (this.selectedIndex < this.slides.length - 1) { + this.selectSlide(this.selectedIndex + 1); + } + } + + protected prevSlide() { + if (this.selectedIndex > 0) { + this.selectSlide(this.selectedIndex - 1); + } + } + async ngAfterViewInit() { this.keyManager = new FocusKeyManager(this.carouselButtons) .withHorizontalOrientation("ltr") diff --git a/libs/vault/src/components/carousel/carousel.stories.ts b/libs/vault/src/components/carousel/carousel.stories.ts index 521a561a19f..1e393779a6a 100644 --- a/libs/vault/src/components/carousel/carousel.stories.ts +++ b/libs/vault/src/components/carousel/carousel.stories.ts @@ -1,5 +1,6 @@ import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ButtonComponent, TypographyModule } from "@bitwarden/components"; import { VaultCarouselSlideComponent } from "./carousel-slide/carousel-slide.component"; @@ -11,6 +12,7 @@ export default { decorators: [ moduleMetadata({ imports: [VaultCarouselSlideComponent, TypographyModule, ButtonComponent], + providers: [{ provide: I18nService, useValue: { t: (key: string) => key } }], }), ], } as Meta;