1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-30 16:23:53 +00:00

approach 1 of handling dialog close from menu item trigger

This commit is contained in:
Vicki League
2025-12-17 09:52:38 -05:00
parent e6062ec84e
commit 8ac1fb58db
9 changed files with 73 additions and 16 deletions

View File

@@ -49,6 +49,7 @@ import {
ItemModule,
ToastService,
CenterPositionStrategy,
DialogConfig,
} from "@bitwarden/components";
import {
AttachmentDialogCloseResult,
@@ -667,10 +668,15 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
* @param dialogService
* @param params
*/
static open(dialogService: DialogService, params: VaultItemDialogParams) {
static open(
dialogService: DialogService,
params: VaultItemDialogParams,
dialogConfig?: DialogConfig,
) {
return dialogService.open<VaultItemDialogResult, VaultItemDialogParams>(
VaultItemDialogComponent,
{
...dialogConfig,
data: params,
},
);

View File

@@ -1,5 +1,12 @@
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
Output,
viewChild,
} from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom, switchMap } from "rxjs";
@@ -56,6 +63,9 @@ export class VaultHeaderComponent {
protected CollectionDialogTabType = CollectionDialogTabType;
protected CipherType = CipherType;
/** Query for the NewCipherMenuComponent in the template */
readonly newCipherMenu = viewChild(NewCipherMenuComponent);
/**
* Boolean to determine the loading state of the header.
* Shows a loading spinner if set to true

View File

@@ -1,6 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
import {
ChangeDetectorRef,
Component,
computed,
NgZone,
OnDestroy,
OnInit,
viewChild,
ViewChild,
} from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import {
BehaviorSubject,
@@ -195,6 +204,12 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
// eslint-disable-next-line @angular-eslint/prefer-signals
@ViewChild("vaultItems", { static: false }) vaultItemsComponent: VaultItemsComponent<C>;
readonly vaultHeaderComponent = viewChild(VaultHeaderComponent);
readonly newButtonEl = computed(
() => this.vaultHeaderComponent()?.newCipherMenu()?.newCipherButton()?.el.nativeElement,
);
trashCleanupWarning: string = null;
kdfIterations: number;
activeFilter: VaultFilter = new VaultFilter();
@@ -861,7 +876,9 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
}
addFolder = (): void => {
AddEditFolderDialogComponent.open(this.dialogService);
AddEditFolderDialogComponent.open(this.dialogService, undefined, {
restoreFocus: this.newButtonEl(),
});
};
editFolder = async (folder: FolderFilter): Promise<void> => {
@@ -947,12 +964,18 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
formConfig: CipherFormConfig,
activeCollectionId?: CollectionId,
) {
this.vaultItemDialogRef = VaultItemDialogComponent.open(this.dialogService, {
mode,
formConfig,
activeCollectionId,
restore: this.restore,
});
this.vaultItemDialogRef = VaultItemDialogComponent.open(
this.dialogService,
{
mode,
formConfig,
activeCollectionId,
restore: this.restore,
},
{
restoreFocus: this.newButtonEl(),
},
);
const result = await lastValueFrom(this.vaultItemDialogRef.closed);
this.vaultItemDialogRef = undefined;
@@ -1098,6 +1121,7 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
showOrgSelector: true,
limitNestedCollections: true,
},
restoreFocus: this.newButtonEl(),
});
const result = await lastValueFrom(dialog.closed);
if (result.action === CollectionDialogAction.Saved) {
@@ -1121,6 +1145,7 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
initialTab: tab,
limitNestedCollections: true,
},
restoreFocus: this.newButtonEl(),
});
const result = await lastValueFrom(dialog.closed);

View File

@@ -148,7 +148,7 @@ export class ButtonComponent implements ButtonLikeAbstraction {
);
readonly disabled = model<boolean>(false);
private el = inject(ElementRef<HTMLButtonElement>);
readonly el = inject(ElementRef<HTMLButtonElement>);
constructor() {
ariaDisableElement(this.el.nativeElement, this.disabledAttr);

View File

@@ -62,7 +62,7 @@ export abstract class DialogRef<R = unknown, C = unknown> implements Pick<
export type DialogConfig<D = unknown, R = unknown> = Pick<
CdkDialogConfig<D, R>,
"data" | "disableClose" | "ariaModal" | "positionStrategy" | "height" | "width"
"data" | "disableClose" | "ariaModal" | "positionStrategy" | "height" | "width" | "restoreFocus"
>;
/**

View File

@@ -38,3 +38,8 @@ prior to being clicked and `aria-expanded="true"` after the user clicks the elem
User should be able to navigate the opened menu via the up and down arrow keys and close the menu
using the escape key or clicking out of the menu.
Are you using a menu item to open a dialog? Be sure to pass the `restoreFocus` config option to the
`dialog.open` method, specifying where the user's focus should return to upon dialog close. (The
menu closes when the dialog launches, so the built-in strategy of returning focus to the trigger
element is not possible, since the trigger element menu item is gone.)

View File

@@ -32,6 +32,7 @@ import {
FormFieldModule,
IconButtonModule,
ToastService,
DialogConfig,
} from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
@@ -175,10 +176,17 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
this.dialogRef.close(result);
}
static open(dialogService: DialogService, data?: AddEditFolderDialogData) {
static open(
dialogService: DialogService,
data?: AddEditFolderDialogData,
dialogConfig?: DialogConfig,
) {
return dialogService.open<AddEditFolderDialogResult, AddEditFolderDialogData>(
AddEditFolderDialogComponent,
{ data },
{
...dialogConfig,
data,
},
);
}
}

View File

@@ -7,6 +7,7 @@
[bitMenuTriggerFor]="addOptions"
id="newItemDropdown"
[appA11yTitle]="'new' | i18n"
#newItemDropdown
>
<i class="bwi bwi-plus" aria-hidden="true"></i>
{{ "new" | i18n }}

View File

@@ -1,12 +1,12 @@
import { CommonModule } from "@angular/common";
import { Component, input, output } from "@angular/core";
import { Component, input, output, viewChild } from "@angular/core";
import { map, shareReplay } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { CipherType } from "@bitwarden/common/vault/enums";
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items";
import { ButtonModule, MenuModule } from "@bitwarden/components";
import { ButtonComponent, ButtonModule, MenuModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -33,6 +33,8 @@ export class NewCipherMenuComponent {
collectionAdded = output();
cipherAdded = output<CipherType>();
readonly newCipherButton = viewChild<ButtonComponent>("newItemDropdown");
constructor(private restrictedItemTypesService: RestrictedItemTypesService) {}
/**