[PM-10934] Remove last form-field bottom border (#10751)

* match API of new CL FormField component

* remove readonly border for additional options component

* remove readonly border for last autofill option

* remove readonly border for last custom-field form field

* remove readonly border for when collection,org or folder is available

* add `ReadOnlyCipherCardComponent` to handle readonly border

* remove readonly border for the last identity form field

* remove readonly border for the last card form field

* remove readonly border for the last login form field

* remove unneeded true value
This commit is contained in:
Nick Krantz 2024-09-04 10:50:34 -05:00 committed by GitHub
parent 192fd885d5
commit 3e9fb2009e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 67 additions and 15 deletions

View File

@ -1,6 +1,7 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { import {
AfterContentChecked, AfterContentChecked,
booleanAttribute,
Component, Component,
ContentChild, ContentChild,
ContentChildren, ContentChildren,
@ -38,6 +39,13 @@ export class BitFormFieldComponent implements AfterContentChecked {
return this._disableMargin; return this._disableMargin;
} }
/**
* NOTE: Placeholder to match the API of the form-field component in the `ps/extension` branch,
* no functionality is implemented as of now.
*/
@Input({ transform: booleanAttribute })
disableReadOnlyBorder = false;
@HostBinding("class") @HostBinding("class")
get classList() { get classList() {
return ["tw-block"].concat(this.disableMargin ? [] : ["tw-mb-6"]); return ["tw-block"].concat(this.disableMargin ? [] : ["tw-mb-6"]);

View File

@ -3,7 +3,7 @@
<h2 bitTypography="h6">{{ "additionalOptions" | i18n }}</h2> <h2 bitTypography="h6">{{ "additionalOptions" | i18n }}</h2>
</bit-section-header> </bit-section-header>
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0"> <bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0">
<bit-form-field> <bit-form-field disableReadOnlyBorder>
<bit-label>{{ "note" | i18n }}</bit-label> <bit-label>{{ "note" | i18n }}</bit-label>
<textarea readonly bitInput aria-readonly="true">{{ notes }}</textarea> <textarea readonly bitInput aria-readonly="true">{{ notes }}</textarea>
<button <button

View File

@ -4,7 +4,11 @@
</bit-section-header> </bit-section-header>
<bit-card> <bit-card>
<ng-container *ngFor="let login of loginUris; let last = last"> <ng-container *ngFor="let login of loginUris; let last = last">
<bit-form-field [disableMargin]="last" data-testid="autofill-view-list"> <bit-form-field
[disableMargin]="last"
[disableReadOnlyBorder]="last"
data-testid="autofill-view-list"
>
<bit-label> <bit-label>
{{ "website" | i18n }} {{ "website" | i18n }}
</bit-label> </bit-label>

View File

@ -2,7 +2,7 @@
<bit-section-header> <bit-section-header>
<h2 bitTypography="h6">{{ setSectionTitle }}</h2> <h2 bitTypography="h6">{{ setSectionTitle }}</h2>
</bit-section-header> </bit-section-header>
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0"> <read-only-cipher-card>
<bit-form-field *ngIf="card.cardholderName"> <bit-form-field *ngIf="card.cardholderName">
<bit-label>{{ "cardholderName" | i18n }}</bit-label> <bit-label>{{ "cardholderName" | i18n }}</bit-label>
<input <input
@ -81,5 +81,5 @@
data-testid="copy-code" data-testid="copy-code"
></button> ></button>
</bit-form-field> </bit-form-field>
</bit-card> </read-only-cipher-card>
</bit-section> </bit-section>

View File

@ -13,6 +13,8 @@ import {
IconButtonModule, IconButtonModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-cipher-card.component";
@Component({ @Component({
selector: "app-card-details-view", selector: "app-card-details-view",
templateUrl: "card-details-view.component.html", templateUrl: "card-details-view.component.html",
@ -26,6 +28,7 @@ import {
TypographyModule, TypographyModule,
FormFieldModule, FormFieldModule,
IconButtonModule, IconButtonModule,
ReadOnlyCipherCardComponent,
], ],
}) })
export class CardDetailsComponent { export class CardDetailsComponent {

View File

@ -8,7 +8,7 @@
*ngFor="let field of fields; let last = last" *ngFor="let field of fields; let last = last"
[ngClass]="{ 'tw-mb-4': !last }" [ngClass]="{ 'tw-mb-4': !last }"
> >
<bit-form-field *ngIf="field.type === fieldType.Text"> <bit-form-field *ngIf="field.type === fieldType.Text" [disableReadOnlyBorder]="last">
<bit-label>{{ field.name }}</bit-label> <bit-label>{{ field.name }}</bit-label>
<input readonly bitInput type="text" [value]="field.value" aria-readonly="true" /> <input readonly bitInput type="text" [value]="field.value" aria-readonly="true" />
<button <button
@ -21,7 +21,7 @@
[appA11yTitle]="'copyValue' | i18n" [appA11yTitle]="'copyValue' | i18n"
></button> ></button>
</bit-form-field> </bit-form-field>
<bit-form-field *ngIf="field.type === fieldType.Hidden"> <bit-form-field *ngIf="field.type === fieldType.Hidden" [disableReadOnlyBorder]="last">
<bit-label>{{ field.name }}</bit-label> <bit-label>{{ field.name }}</bit-label>
<input readonly bitInput type="password" [value]="field.value" aria-readonly="true" /> <input readonly bitInput type="password" [value]="field.value" aria-readonly="true" />
<button bitSuffix type="button" bitIconButton bitPasswordInputToggle></button> <button bitSuffix type="button" bitIconButton bitPasswordInputToggle></button>
@ -45,7 +45,7 @@
/> />
<bit-label> {{ field.name }} </bit-label> <bit-label> {{ field.name }} </bit-label>
</bit-form-control> </bit-form-control>
<bit-form-field *ngIf="field.type === fieldType.Linked"> <bit-form-field *ngIf="field.type === fieldType.Linked" [disableReadOnlyBorder]="last">
<bit-label> {{ "linked" | i18n }}: {{ field.name }} </bit-label> <bit-label> {{ "linked" | i18n }}: {{ field.name }} </bit-label>
<input <input
readonly readonly

View File

@ -5,6 +5,9 @@
<bit-card> <bit-card>
<bit-form-field <bit-form-field
[disableMargin]="!cipher.collectionIds?.length && !cipher.organizationId && !cipher.folderId" [disableMargin]="!cipher.collectionIds?.length && !cipher.organizationId && !cipher.folderId"
[disableReadOnlyBorder]="
!cipher.collectionIds?.length && !cipher.organizationId && !cipher.folderId
"
> >
<bit-label> <bit-label>
{{ "itemName" | i18n }} {{ "itemName" | i18n }}

View File

@ -2,7 +2,7 @@
<bit-section-header> <bit-section-header>
<h2 bitTypography="h6">{{ "loginCredentials" | i18n }}</h2> <h2 bitTypography="h6">{{ "loginCredentials" | i18n }}</h2>
</bit-section-header> </bit-section-header>
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0"> <read-only-cipher-card>
<bit-form-field *ngIf="cipher.login.username"> <bit-form-field *ngIf="cipher.login.username">
<bit-label> <bit-label>
{{ "username" | i18n }} {{ "username" | i18n }}
@ -132,5 +132,5 @@
class="disabled:tw-cursor-default" class="disabled:tw-cursor-default"
></button> ></button>
</bit-form-field> </bit-form-field>
</bit-card> </read-only-cipher-card>
</bit-section> </bit-section>

View File

@ -19,6 +19,7 @@ import {
} from "@bitwarden/components"; } from "@bitwarden/components";
import { BitTotpCountdownComponent } from "../../components/totp-countdown/totp-countdown.component"; import { BitTotpCountdownComponent } from "../../components/totp-countdown/totp-countdown.component";
import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-cipher-card.component";
type TotpCodeValues = { type TotpCodeValues = {
totpCode: string; totpCode: string;
@ -41,6 +42,7 @@ type TotpCodeValues = {
BadgeModule, BadgeModule,
ColorPasswordModule, ColorPasswordModule,
BitTotpCountdownComponent, BitTotpCountdownComponent,
ReadOnlyCipherCardComponent,
], ],
}) })
export class LoginCredentialsViewComponent { export class LoginCredentialsViewComponent {

View File

@ -0,0 +1,3 @@
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0">
<ng-content></ng-content>
</bit-card>

View File

@ -0,0 +1,26 @@
import { AfterViewInit, Component, ContentChildren, QueryList } from "@angular/core";
import { CardComponent, BitFormFieldComponent } from "@bitwarden/components";
@Component({
selector: "read-only-cipher-card",
templateUrl: "./read-only-cipher-card.component.html",
standalone: true,
imports: [CardComponent],
})
/**
* A thin wrapper around the `bit-card` component that disables the bottom border for the last form field.
*/
export class ReadOnlyCipherCardComponent implements AfterViewInit {
@ContentChildren(BitFormFieldComponent) formFields: QueryList<BitFormFieldComponent>;
ngAfterViewInit(): void {
// Disable the bottom border for the last form field
if (this.formFields.last) {
// Delay model update until next change detection cycle
setTimeout(() => {
this.formFields.last.disableReadOnlyBorder = true;
});
}
}
}

View File

@ -3,7 +3,7 @@
<h2 bitTypography="h6">{{ "personalDetails" | i18n }}</h2> <h2 bitTypography="h6">{{ "personalDetails" | i18n }}</h2>
</bit-section-header> </bit-section-header>
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0"> <read-only-cipher-card>
<bit-form-field *ngIf="cipher.identity.fullName"> <bit-form-field *ngIf="cipher.identity.fullName">
<bit-label>{{ "name" | i18n }}</bit-label> <bit-label>{{ "name" | i18n }}</bit-label>
<input bitInput [value]="cipher.identity.fullName" readonly data-testid="name" /> <input bitInput [value]="cipher.identity.fullName" readonly data-testid="name" />
@ -43,7 +43,7 @@
[valueLabel]="'company' | i18n" [valueLabel]="'company' | i18n"
></button> ></button>
</bit-form-field> </bit-form-field>
</bit-card> </read-only-cipher-card>
</bit-section> </bit-section>
<bit-section *ngIf="showIdentificationDetails"> <bit-section *ngIf="showIdentificationDetails">
@ -51,7 +51,7 @@
<h2 bitTypography="h6">{{ "identification" | i18n }}</h2> <h2 bitTypography="h6">{{ "identification" | i18n }}</h2>
</bit-section-header> </bit-section-header>
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0"> <read-only-cipher-card>
<bit-form-field *ngIf="cipher.identity.ssn"> <bit-form-field *ngIf="cipher.identity.ssn">
<bit-label>{{ "ssn" | i18n }}</bit-label> <bit-label>{{ "ssn" | i18n }}</bit-label>
<input bitInput type="password" [value]="cipher.identity.ssn" readonly data-testid="ssn" /> <input bitInput type="password" [value]="cipher.identity.ssn" readonly data-testid="ssn" />
@ -111,7 +111,7 @@
[valueLabel]="'licenseNumber' | i18n" [valueLabel]="'licenseNumber' | i18n"
></button> ></button>
</bit-form-field> </bit-form-field>
</bit-card> </read-only-cipher-card>
</bit-section> </bit-section>
<bit-section *ngIf="showContactDetails"> <bit-section *ngIf="showContactDetails">
@ -119,7 +119,7 @@
<h2 bitTypography="h6">{{ "contactInfo" | i18n }}</h2> <h2 bitTypography="h6">{{ "contactInfo" | i18n }}</h2>
</bit-section-header> </bit-section-header>
<bit-card class="[&_bit-form-field:last-of-type]:tw-mb-0"> <read-only-cipher-card>
<bit-form-field *ngIf="cipher.identity.email"> <bit-form-field *ngIf="cipher.identity.email">
<bit-label>{{ "email" | i18n }}</bit-label> <bit-label>{{ "email" | i18n }}</bit-label>
<input bitInput [value]="cipher.identity.email" readonly data-testid="email" /> <input bitInput [value]="cipher.identity.email" readonly data-testid="email" />
@ -166,5 +166,5 @@
[valueLabel]="'address' | i18n" [valueLabel]="'address' | i18n"
></button> ></button>
</bit-form-field> </bit-form-field>
</bit-card> </read-only-cipher-card>
</bit-section> </bit-section>

View File

@ -12,6 +12,8 @@ import {
TypographyModule, TypographyModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-cipher-card.component";
@Component({ @Component({
standalone: true, standalone: true,
selector: "app-view-identity-sections", selector: "app-view-identity-sections",
@ -25,6 +27,7 @@ import {
TypographyModule, TypographyModule,
FormFieldModule, FormFieldModule,
IconButtonModule, IconButtonModule,
ReadOnlyCipherCardComponent,
], ],
}) })
export class ViewIdentitySectionsComponent implements OnInit { export class ViewIdentitySectionsComponent implements OnInit {