1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-05 11:13:44 +00:00

focus on dialog header when switching modes

This commit is contained in:
Nick Krantz
2026-01-23 13:41:21 -06:00
parent d394991044
commit 6b823d402d
2 changed files with 80 additions and 1 deletions

View File

@@ -104,7 +104,7 @@ describe("VaultItemDialogComponent", () => {
getFeatureFlag$: () => of(false),
},
},
{ provide: Router, useValue: {} },
{ provide: Router, useValue: { navigate: jest.fn() } },
{ provide: ActivatedRoute, useValue: {} },
{
provide: BillingAccountProfileStateService,
@@ -337,4 +337,76 @@ describe("VaultItemDialogComponent", () => {
});
});
});
describe("changeMode", () => {
beforeEach(() => {
component.setTestCipher({ type: CipherType.Login, id: "cipher-id" });
});
it("refocuses the dialog header", async () => {
const focusOnHeaderSpy = jest.spyOn(component["dialogComponent"](), "focusOnHeader");
await component["changeMode"]("view");
expect(focusOnHeaderSpy).toHaveBeenCalled();
});
describe("to view", () => {
beforeEach(() => {
component.setTestParams({ mode: "form" });
fixture.detectChanges();
});
it("sets mode to view", async () => {
await component["changeMode"]("view");
expect(component["params"].mode).toBe("view");
});
it("updates the url", async () => {
const router = TestBed.inject(Router);
await component["changeMode"]("view");
expect(router.navigate).toHaveBeenCalledWith([], {
queryParams: { action: "view", itemId: "cipher-id" },
queryParamsHandling: "merge",
replaceUrl: true,
});
});
});
describe("to form", () => {
const waitForFormReady = async () => {
const changeModePromise = component["changeMode"]("form");
expect(component["loadForm"]).toBe(true);
component["onFormReady"]();
await changeModePromise;
};
beforeEach(() => {
component.setTestParams({ mode: "view" });
fixture.detectChanges();
});
it("waits for form to be ready when switching to form mode", async () => {
await waitForFormReady();
expect(component["params"].mode).toBe("form");
});
it("updates the url", async () => {
const router = TestBed.inject(Router);
await waitForFormReady();
expect(router.navigate).toHaveBeenCalledWith([], {
queryParams: { action: "edit", itemId: "cipher-id" },
queryParamsHandling: "merge",
replaceUrl: true,
});
});
});
});
});

View File

@@ -8,6 +8,7 @@ import {
Inject,
OnDestroy,
OnInit,
viewChild,
ViewChild,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
@@ -50,6 +51,7 @@ import {
ItemModule,
ToastService,
CenterPositionStrategy,
DialogComponent,
} from "@bitwarden/components";
import {
AttachmentDialogCloseResult,
@@ -172,6 +174,8 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
// eslint-disable-next-line @angular-eslint/prefer-signals
@ViewChild(CipherFormComponent) cipherFormComponent!: CipherFormComponent;
private readonly dialogComponent = viewChild(DialogComponent);
/**
* Tracks if the cipher was ever modified while the dialog was open. Used to ensure the dialog emits the correct result
* in case of closing with the X button or ESC key.
@@ -693,6 +697,9 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
// Scroll to the top of the dialog content when switching modes.
this.dialogContent.nativeElement.parentElement.scrollTop = 0;
// Refocus on title element, the built-in focus management of the dialog only works for the initial open.
this.dialogComponent().focusOnHeader();
// Update the URL query params to reflect the new mode.
await this.router.navigate([], {
queryParams: {