1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-29 14:43:31 +00:00

[PM-28181] Open send dialog in drawer instead of popup in refreshed UI (#17666)

* [PM-28181] Open send dialog in drawer instead of popup in refreshed UI

* Fix types

* [PM-28181] Use drawer to edit sends with refreshed UI

* [PM-28181] Address bug where multiple Sends could not be navigated between
This commit is contained in:
Mike Amirault
2025-12-16 13:34:31 -05:00
committed by GitHub
parent 78784f509d
commit b63e1cb26c
5 changed files with 135 additions and 6 deletions

View File

@@ -0,0 +1,94 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
import { SendAddEditDialogComponent } from "@bitwarden/send-ui";
import { NewSendDropdownComponent } from "./new-send-dropdown.component";
describe("NewSendDropdownComponent", () => {
let component: NewSendDropdownComponent;
let fixture: ComponentFixture<NewSendDropdownComponent>;
const mockBillingAccountProfileStateService = mock<BillingAccountProfileStateService>();
const mockAccountService = mock<AccountService>();
const mockConfigService = mock<ConfigService>();
const mockI18nService = mock<I18nService>();
const mockPolicyService = mock<PolicyService>();
const mockSendService = mock<SendService>();
const mockPremiumUpgradePromptService = mock<PremiumUpgradePromptService>();
const mockSendApiService = mock<SendApiService>();
beforeAll(() => {
mockBillingAccountProfileStateService.hasPremiumFromAnySource$.mockImplementation(() =>
of(true),
);
mockAccountService.activeAccount$ = of({ id: "myTestAccount" } as Account);
mockPolicyService.policyAppliesToUser$.mockImplementation(() => of(false));
mockPremiumUpgradePromptService.promptForPremium.mockImplementation(async () => {});
});
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NewSendDropdownComponent],
declarations: [],
providers: [
{
provide: BillingAccountProfileStateService,
useValue: mockBillingAccountProfileStateService,
},
{ provide: AccountService, useValue: mockAccountService },
{ provide: ConfigService, useValue: mockConfigService },
{ provide: I18nService, useValue: mockI18nService },
{ provide: PolicyService, useValue: mockPolicyService },
{ provide: SendService, useValue: mockSendService },
{ provide: PremiumUpgradePromptService, useValue: mockPremiumUpgradePromptService },
{ provide: SendApiService, useValue: mockSendApiService },
],
}).compileComponents();
fixture = TestBed.createComponent(NewSendDropdownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
afterEach(() => {
jest.restoreAllMocks();
});
it("should create", () => {
expect(component).toBeTruthy();
});
it("should open send dialog in a popup without feature flag", async () => {
const openSpy = jest.spyOn(SendAddEditDialogComponent, "open");
const openDrawerSpy = jest.spyOn(SendAddEditDialogComponent, "openDrawer");
mockConfigService.getFeatureFlag.mockResolvedValue(false);
await component.createSend(SendType.Text);
expect(openSpy).toHaveBeenCalled();
expect(openDrawerSpy).not.toHaveBeenCalled();
});
it("should open send dialog in drawer with feature flag", async () => {
const openSpy = jest.spyOn(SendAddEditDialogComponent, "open");
const openDrawerSpy = jest.spyOn(SendAddEditDialogComponent, "openDrawer");
mockConfigService.getFeatureFlag.mockImplementation(async (key) =>
key === FeatureFlag.SendUIRefresh ? true : false,
);
await component.createSend(SendType.Text);
expect(openSpy).not.toHaveBeenCalled();
expect(openDrawerSpy).toHaveBeenCalled();
});
});

View File

@@ -6,6 +6,8 @@ import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/pre
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { ButtonModule, DialogService, MenuModule } from "@bitwarden/components";
import { DefaultSendFormConfigService, SendAddEditDialogComponent } from "@bitwarden/send-ui";
@@ -38,6 +40,7 @@ export class NewSendDropdownComponent {
private accountService: AccountService,
private dialogService: DialogService,
private addEditFormConfigService: DefaultSendFormConfigService,
private configService: ConfigService,
) {
this.canAccessPremium$ = this.accountService.activeAccount$.pipe(
switchMap((account) =>
@@ -60,6 +63,11 @@ export class NewSendDropdownComponent {
const formConfig = await this.addEditFormConfigService.buildConfig("add", undefined, type);
SendAddEditDialogComponent.open(this.dialogService, { formConfig });
const useRefresh = await this.configService.getFeatureFlag(FeatureFlag.SendUIRefresh);
if (useRefresh) {
SendAddEditDialogComponent.openDrawer(this.dialogService, { formConfig });
} else {
SendAddEditDialogComponent.open(this.dialogService, { formConfig });
}
}
}

View File

@@ -7,7 +7,9 @@ import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/sen
import { NoSendsIcon } from "@bitwarden/assets/svg";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -77,6 +79,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
toastService: ToastService,
private addEditFormConfigService: DefaultSendFormConfigService,
accountService: AccountService,
private configService: ConfigService,
) {
super(
sendService,
@@ -144,14 +147,21 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
* @param formConfig The form configuration.
* */
async openSendItemDialog(formConfig: SendFormConfig) {
// Prevent multiple dialogs from being opened.
if (this.sendItemDialogRef) {
const useRefresh = await this.configService.getFeatureFlag(FeatureFlag.SendUIRefresh);
// Prevent multiple dialogs from being opened but allow drawers since they will prevent multiple being open themselves
if (this.sendItemDialogRef && !useRefresh) {
return;
}
this.sendItemDialogRef = SendAddEditDialogComponent.open(this.dialogService, {
formConfig,
});
if (useRefresh) {
this.sendItemDialogRef = SendAddEditDialogComponent.openDrawer(this.dialogService, {
formConfig,
});
} else {
this.sendItemDialogRef = SendAddEditDialogComponent.open(this.dialogService, {
formConfig,
});
}
const result = await lastValueFrom(this.sendItemDialogRef.closed);
this.sendItemDialogRef = undefined;