1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-27 10:03:23 +00:00

[PM-25627] convert policy dialogs to drawer (#18534)

* Enhance policy edit dialogs by updating dialog size to large for improved layout and switching to openDrawer method for better user experience.

* Enhance policy edit dialogs by adding policy status badges to indicate if a policy is enabled, improving user visibility and experience.

* Update dialog component styles to enhance drawer behavior by changing height to full screen and adding sticky footer for improved layout and user experience.

* Refactor policy display layout by wrapping buttons and badges in a flex container for improved alignment and spacing.

* Refactor password policy forms in admin console for improved layout

- Simplified the structure of form fields in `master-password.component.html` and `password-generator.component.html` by removing unnecessary div wrappers.
- Updated the label for the password type policy override in `messages.json` for clarity.

* Update dialog size in policy edit component for consistency

- Changed the dialog size from 'large' to 'default' in `policy-edit-dialog.component.html` to align with design standards.

* refactor(dialog): update dialog component styles for drawer layout

- Adjusted height class for drawer dialogs from 'tw-h-screen' to 'tw-h-full' for better layout management.
- Removed sticky positioning for footer in drawer mode to improve visual consistency.

* refactor(dialog): enhance form layout for policy edit dialogs

- Added classes for full height and flex column layout to the form elements in policy edit dialogs for improved visual consistency and usability.
This commit is contained in:
Jared
2026-02-09 13:50:20 -05:00
committed by GitHub
parent 3855332279
commit f22736bb64
10 changed files with 115 additions and 100 deletions

View File

@@ -19,12 +19,14 @@
@if (p.display$(organization, configService) | async) { @if (p.display$(organization, configService) | async) {
<tr bitRow> <tr bitRow>
<td bitCell ngPreserveWhitespaces> <td bitCell ngPreserveWhitespaces>
<div class="tw-flex tw-items-center tw-gap-2">
<button type="button" bitLink (click)="edit(p, organizationId)"> <button type="button" bitLink (click)="edit(p, organizationId)">
{{ p.name | i18n }} {{ p.name | i18n }}
</button> </button>
@if (policiesEnabledMap.get(p.type)) { @if (policiesEnabledMap.get(p.type)) {
<span bitBadge variant="success">{{ "on" | i18n }}</span> <span bitBadge variant="success">{{ "on" | i18n }}</span>
} }
</div>
<small class="tw-text-muted tw-block">{{ p.description | i18n }}</small> <small class="tw-text-muted tw-block">{{ p.description | i18n }}</small>
</td> </td>
</tr> </tr>

View File

@@ -13,18 +13,14 @@
<bit-label>{{ "enforceOnLoginDesc" | i18n }}</bit-label> <bit-label>{{ "enforceOnLoginDesc" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<div class="tw-flex tw-space-x-4"> <bit-form-field>
<bit-form-field class="tw-flex-auto">
<bit-label>{{ "minComplexityScore" | i18n }}</bit-label> <bit-label>{{ "minComplexityScore" | i18n }}</bit-label>
<bit-select formControlName="minComplexity" id="minComplexity"> <bit-select formControlName="minComplexity" id="minComplexity">
<bit-option <bit-option *ngFor="let o of passwordScores" [value]="o.value" [label]="o.name"></bit-option>
*ngFor="let o of passwordScores"
[value]="o.value"
[label]="o.name"
></bit-option>
</bit-select> </bit-select>
</bit-form-field> </bit-form-field>
<bit-form-field class="tw-flex-auto">
<bit-form-field>
<bit-label>{{ "minLength" | i18n }}</bit-label> <bit-label>{{ "minLength" | i18n }}</bit-label>
<input <input
bitInput bitInput
@@ -35,7 +31,6 @@
[max]="MaxPasswordLength" [max]="MaxPasswordLength"
/> />
</bit-form-field> </bit-form-field>
</div>
<bit-form-control class="!tw-mb-2"> <bit-form-control class="!tw-mb-2">
<input type="checkbox" bitCheckbox formControlName="requireUpper" id="requireUpper" /> <input type="checkbox" bitCheckbox formControlName="requireUpper" id="requireUpper" />

View File

@@ -4,9 +4,8 @@
<bit-label>{{ "turnOn" | i18n }}</bit-label> <bit-label>{{ "turnOn" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<div class="tw-grid tw-grid-cols-12 tw-gap-4"> <bit-form-field>
<bit-form-field class="tw-col-span-6 tw-mb-0"> <bit-label>{{ "passwordTypePolicyOverride" | i18n }}</bit-label>
<bit-label>{{ "overridePasswordTypePolicy" | i18n }}</bit-label>
<bit-select formControlName="overridePasswordType" id="overrideType"> <bit-select formControlName="overridePasswordType" id="overrideType">
<bit-option <bit-option
*ngFor="let o of overridePasswordTypeOptions" *ngFor="let o of overridePasswordTypeOptions"
@@ -15,13 +14,11 @@
></bit-option> ></bit-option>
</bit-select> </bit-select>
</bit-form-field> </bit-form-field>
</div>
<!-- password-specific policies --> <!-- password-specific policies -->
<div *ngIf="showPasswordPolicies$ | async"> <div *ngIf="showPasswordPolicies$ | async">
<h3 bitTypography="h3" class="tw-mt-4">{{ "password" | i18n }}</h3> <h3 bitTypography="h3" class="tw-mt-4">{{ "password" | i18n }}</h3>
<div class="tw-grid tw-grid-cols-12 tw-gap-4"> <bit-form-field>
<bit-form-field class="tw-col-span-6">
<bit-label>{{ "minLength" | i18n }}</bit-label> <bit-label>{{ "minLength" | i18n }}</bit-label>
<input <input
bitInput bitInput
@@ -31,9 +28,7 @@
formControlName="minLength" formControlName="minLength"
/> />
</bit-form-field> </bit-form-field>
</div> <bit-form-field>
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
<bit-form-field class="tw-col-span-6">
<bit-label>{{ "minNumbers" | i18n }}</bit-label> <bit-label>{{ "minNumbers" | i18n }}</bit-label>
<input <input
bitInput bitInput
@@ -43,7 +38,7 @@
formControlName="minNumbers" formControlName="minNumbers"
/> />
</bit-form-field> </bit-form-field>
<bit-form-field class="tw-col-span-6"> <bit-form-field>
<bit-label>{{ "minSpecial" | i18n }}</bit-label> <bit-label>{{ "minSpecial" | i18n }}</bit-label>
<input <input
bitInput bitInput
@@ -53,7 +48,6 @@
formControlName="minSpecial" formControlName="minSpecial"
/> />
</bit-form-field> </bit-form-field>
</div>
<bit-form-control> <bit-form-control>
<input type="checkbox" bitCheckbox formControlName="useUpper" id="useUpper" /> <input type="checkbox" bitCheckbox formControlName="useUpper" id="useUpper" />
<bit-label>{{ "uppercaseLabel" | i18n }}</bit-label> <bit-label>{{ "uppercaseLabel" | i18n }}</bit-label>
@@ -79,8 +73,7 @@
<!-- passphrase-specific policies --> <!-- passphrase-specific policies -->
<div *ngIf="showPassphrasePolicies$ | async"> <div *ngIf="showPassphrasePolicies$ | async">
<h3 bitTypography="h3" class="tw-mt-4">{{ "passphrase" | i18n }}</h3> <h3 bitTypography="h3" class="tw-mt-4">{{ "passphrase" | i18n }}</h3>
<div class="tw-grid tw-grid-cols-12 tw-gap-4"> <bit-form-field>
<bit-form-field class="tw-col-span-6">
<bit-label>{{ "minimumNumberOfWords" | i18n }}</bit-label> <bit-label>{{ "minimumNumberOfWords" | i18n }}</bit-label>
<input <input
bitInput bitInput
@@ -90,7 +83,6 @@
formControlName="minNumberWords" formControlName="minNumberWords"
/> />
</bit-form-field> </bit-form-field>
</div>
<bit-form-control> <bit-form-control>
<input type="checkbox" bitCheckbox formControlName="capitalize" id="capitalize" /> <input type="checkbox" bitCheckbox formControlName="capitalize" id="capitalize" />
<bit-label>{{ "capitalize" | i18n }}</bit-label> <bit-label>{{ "capitalize" | i18n }}</bit-label>

View File

@@ -1,5 +1,13 @@
<form [formGroup]="formGroup" [bitSubmit]="submit"> <form [formGroup]="formGroup" [bitSubmit]="submit" class="tw-h-full tw-flex tw-flex-col">
<bit-dialog [loading]="loading" [title]="'editPolicy' | i18n" [subtitle]="policy.name | i18n"> <bit-dialog dialogSize="default" [loading]="loading">
<ng-container bitDialogTitle>
<span class="tw-flex tw-items-center tw-gap-2">
{{ policy.name | i18n }}
@if (isPolicyEnabled) {
<span bitBadge variant="success">{{ "on" | i18n }}</span>
}
</span>
</ng-container>
<ng-container bitDialogContent> <ng-container bitDialogContent>
<div *ngIf="loading"> <div *ngIf="loading">
<i <i

View File

@@ -81,6 +81,10 @@ export class PolicyEditDialogComponent implements AfterViewInit {
return this.data.policy; return this.data.policy;
} }
get isPolicyEnabled(): boolean {
return this.policyComponent?.policyResponse?.enabled ?? false;
}
/** /**
* Type guard to check if the policy component has the buildVNextRequest method. * Type guard to check if the policy component has the buildVNextRequest method.
*/ */
@@ -196,6 +200,9 @@ export class PolicyEditDialogComponent implements AfterViewInit {
); );
} }
static open = (dialogService: DialogService, config: DialogConfig<PolicyEditDialogData>) => { static open = (dialogService: DialogService, config: DialogConfig<PolicyEditDialogData>) => {
return dialogService.open<PolicyEditDialogResult>(PolicyEditDialogComponent, config); return dialogService.openDrawer<PolicyEditDialogResult, PolicyEditDialogData>(
PolicyEditDialogComponent,
config,
);
}; };
} }

View File

@@ -1,5 +1,5 @@
<form [formGroup]="formGroup" [bitSubmit]="submit"> <form [formGroup]="formGroup" [bitSubmit]="submit" class="tw-h-full tw-flex tw-flex-col">
<bit-dialog [loading]="loading"> <bit-dialog dialogSize="large" [loading]="loading">
<ng-container bitDialogTitle> <ng-container bitDialogTitle>
@let title = (multiStepSubmit | async)[currentStep()]?.titleContent(); @let title = (multiStepSubmit | async)[currentStep()]?.titleContent();
@if (title) { @if (title) {
@@ -40,13 +40,16 @@
@if (showBadge) { @if (showBadge) {
<span bitBadge variant="info" class="tw-w-[99px] tw-my-2"> {{ "availableNow" | i18n }}</span> <span bitBadge variant="info" class="tw-w-[99px] tw-my-2"> {{ "availableNow" | i18n }}</span>
} }
<span> <span class="tw-flex tw-items-center tw-gap-2">
{{ (showBadge ? "autoConfirm" : "editPolicy") | i18n }} {{ (showBadge ? "autoConfirm" : "editPolicy") | i18n }}
@if (!showBadge) { @if (!showBadge) {
<span class="tw-text-muted tw-font-normal tw-text-sm"> <span class="tw-text-muted tw-font-normal tw-text-sm">
{{ policy.name | i18n }} {{ policy.name | i18n }}
</span> </span>
} }
@if (isPolicyEnabled) {
<span bitBadge variant="success">{{ "on" | i18n }}</span>
}
</span> </span>
</div> </div>
</ng-template> </ng-template>

View File

@@ -287,6 +287,9 @@ export class AutoConfirmPolicyDialogComponent
dialogService: DialogService, dialogService: DialogService,
config: DialogConfig<AutoConfirmPolicyDialogData>, config: DialogConfig<AutoConfirmPolicyDialogData>,
) => { ) => {
return dialogService.open<PolicyEditDialogResult>(AutoConfirmPolicyDialogComponent, config); return dialogService.openDrawer<PolicyEditDialogResult, AutoConfirmPolicyDialogData>(
AutoConfirmPolicyDialogComponent,
config,
);
}; };
} }

View File

@@ -1,5 +1,5 @@
<form [formGroup]="formGroup" [bitSubmit]="submit"> <form [formGroup]="formGroup" [bitSubmit]="submit" class="tw-h-full tw-flex tw-flex-col">
<bit-dialog [loading]="loading"> <bit-dialog dialogSize="large" [loading]="loading">
<ng-container bitDialogTitle> <ng-container bitDialogTitle>
@let title = multiStepSubmit()[currentStep()]?.titleContent(); @let title = multiStepSubmit()[currentStep()]?.titleContent();
@if (title) { @if (title) {
@@ -35,7 +35,12 @@
</form> </form>
<ng-template #step0Title> <ng-template #step0Title>
<span class="tw-flex tw-items-center tw-gap-2">
{{ policy.name | i18n }} {{ policy.name | i18n }}
@if (isPolicyEnabled) {
<span bitBadge variant="success">{{ "on" | i18n }}</span>
}
</span>
</ng-template> </ng-template>
<ng-template #step1Title> <ng-template #step1Title>

View File

@@ -216,7 +216,7 @@ export class OrganizationDataOwnershipPolicyDialogComponent
}; };
static open = (dialogService: DialogService, config: DialogConfig<PolicyEditDialogData>) => { static open = (dialogService: DialogService, config: DialogConfig<PolicyEditDialogData>) => {
return dialogService.open<PolicyEditDialogResult>( return dialogService.openDrawer<PolicyEditDialogResult, PolicyEditDialogData>(
OrganizationDataOwnershipPolicyDialogComponent, OrganizationDataOwnershipPolicyDialogComponent,
config, config,
); );

View File

@@ -5394,8 +5394,8 @@
"minimumNumberOfWords": { "minimumNumberOfWords": {
"message": "Minimum number of words" "message": "Minimum number of words"
}, },
"overridePasswordTypePolicy": { "passwordTypePolicyOverride": {
"message": "Password Type", "message": "Password type",
"description": "Name of the password generator policy that overrides the user's password/passphrase selection." "description": "Name of the password generator policy that overrides the user's password/passphrase selection."
}, },
"userPreference": { "userPreference": {