1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 17:53:39 +00:00

[PM-18520] - Update desktop cipher forms to use the same UI as web app and extension - (#13992)

* WIP - cipher form refactor

* cipher clone

* cipher clone

* finalize item view and form changes

* fix tests

* hide changes behind feature flag

* set flag to false

* create vault items v2. add button selector

* revert change to flag and vault items

* add attachments

* revert change to tsconfig

* move module

* fix modules

* cleanup

* fix import

* fix import

* fix import

* remove showForm

* update feature flag

* wip - cleanup

* fix up services

* cleanup

* fix type errors

* fix lint errors

* add dialog component

* revert changes to menu

* revert changes to menu

* fix vault-items-v2

* set feature flag to FALSE

* add missing i18n keys. fix collection state

* remove generator. update modules. bug fix

* fix restricted imports

* mark method as deprecated. add uri arg back

* fix shared.module

* fix shared.module

* fix shared.module

* add uri

* check and prompt for premium when opening attachments dialog

* move VaultItemDialogResult back

* fix import in spec file

* update copy functions

* fix MP reprompt issue
This commit is contained in:
Jordan Aasen
2025-04-23 11:13:44 -07:00
committed by GitHub
parent ef80c23707
commit b589951c90
36 changed files with 1569 additions and 89 deletions

View File

@@ -0,0 +1,19 @@
<bit-dialog dialogSize="default" background="alt">
<span bitDialogTitle>
{{ "attachments" | i18n }}
</span>
<ng-container bitDialogContent>
<app-cipher-attachments
*ngIf="cipherId"
[cipherId]="cipherId"
[submitBtn]="submitBtn"
(onUploadSuccess)="uploadSuccessful()"
(onRemoveSuccess)="removalSuccessful()"
></app-cipher-attachments>
</ng-container>
<ng-container bitDialogFooter>
<button bitButton type="submit" buttonType="primary" [attr.form]="attachmentFormId" #submitBtn>
{{ "upload" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -0,0 +1,65 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { mock } from "jest-mock-extended";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { CipherId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { DIALOG_DATA, DialogRef } from "@bitwarden/components";
import {
AttachmentsV2Component,
AttachmentDialogResult,
AttachmentsDialogParams,
} from "./attachments-v2.component";
describe("AttachmentsV2Component", () => {
let component: AttachmentsV2Component;
let fixture: ComponentFixture<AttachmentsV2Component>;
const mockCipherId: CipherId = "cipher-id" as CipherId;
const mockParams: AttachmentsDialogParams = {
cipherId: mockCipherId,
};
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AttachmentsV2Component, NoopAnimationsModule],
providers: [
{ provide: DIALOG_DATA, useValue: mockParams },
{ provide: DialogRef, useValue: mock<DialogRef>() },
{ provide: I18nService, useValue: mock<I18nService>() },
{ provide: CipherService, useValue: mock<CipherService>() },
{ provide: LogService, useValue: mock<LogService>() },
{ provide: AccountService, useValue: mock<AccountService>() },
],
}).compileComponents();
fixture = TestBed.createComponent(AttachmentsV2Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it("initializes without errors and with the correct cipherId", () => {
expect(component).toBeTruthy();
expect(component.cipherId).toBe(mockParams.cipherId);
});
it("closes the dialog with 'uploaded' result on uploadSuccessful", () => {
const dialogRefCloseSpy = jest.spyOn(component["dialogRef"], "close");
component.uploadSuccessful();
expect(dialogRefCloseSpy).toHaveBeenCalledWith({ action: AttachmentDialogResult.Uploaded });
});
it("closes the dialog with 'removed' result on removalSuccessful", () => {
const dialogRefCloseSpy = jest.spyOn(component["dialogRef"], "close");
component.removalSuccessful();
expect(dialogRefCloseSpy).toHaveBeenCalledWith({ action: AttachmentDialogResult.Removed });
});
});

View File

@@ -0,0 +1,94 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, Inject } from "@angular/core";
import { CipherId } from "@bitwarden/common/types/guid";
import {
ButtonModule,
DialogModule,
DialogService,
DIALOG_DATA,
DialogRef,
} from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
import { CipherAttachmentsComponent } from "../../cipher-form/components/attachments/cipher-attachments.component";
export interface AttachmentsDialogParams {
cipherId: CipherId;
}
/**
* Enum representing the possible results of the attachment dialog.
*/
export enum AttachmentDialogResult {
Uploaded = "uploaded",
Removed = "removed",
Closed = "closed",
}
export interface AttachmentDialogCloseResult {
action: AttachmentDialogResult;
}
/**
* Component for the attachments dialog.
*/
@Component({
selector: "app-vault-attachments-v2",
templateUrl: "attachments-v2.component.html",
standalone: true,
imports: [ButtonModule, CommonModule, DialogModule, I18nPipe, CipherAttachmentsComponent],
})
export class AttachmentsV2Component {
cipherId: CipherId;
attachmentFormId = CipherAttachmentsComponent.attachmentFormID;
/**
* Constructor for AttachmentsV2Component.
* @param dialogRef - Reference to the dialog.
* @param params - Parameters passed to the dialog.
*/
constructor(
private dialogRef: DialogRef<AttachmentDialogCloseResult>,
@Inject(DIALOG_DATA) public params: AttachmentsDialogParams,
) {
this.cipherId = params.cipherId;
}
/**
* Opens the attachments dialog.
* @param dialogService - The dialog service.
* @param params - The parameters for the dialog.
* @returns The dialog reference.
*/
static open(
dialogService: DialogService,
params: AttachmentsDialogParams,
): DialogRef<AttachmentDialogCloseResult> {
return dialogService.open(AttachmentsV2Component, {
data: params,
});
}
/**
* Called when an attachment is successfully uploaded.
* Closes the dialog with an 'uploaded' result.
*/
uploadSuccessful() {
this.dialogRef.close({
action: AttachmentDialogResult.Uploaded,
});
}
/**
* Called when an attachment is successfully removed.
* Closes the dialog with a 'removed' result.
*/
removalSuccessful() {
this.dialogRef.close({
action: AttachmentDialogResult.Removed,
});
}
}

View File

@@ -124,7 +124,8 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
}
const { username, password, totp, fido2Credentials } = this.cipher.login;
return username || password || totp || fido2Credentials;
return username || password || totp || fido2Credentials?.length > 0;
}
get hasAutofill() {

View File

@@ -1,2 +1,3 @@
export * from "./cipher-view.component";
export * from "./attachments/attachments-v2.component";
export { CipherAttachmentsComponent } from "../cipher-form/components/attachments/cipher-attachments.component";