mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
PM-13808 - Use new <bit-form-field> component in Excluded domains settings. (#13111)
* Use new <bit-form-field> component in Excluded domains settings. Changing to new <bit-form-field> component necessitates modifying form to use ReactiveForms conventions. To handle this change, the deletable/uneditable domain state and new form control state are managed separately, including clearing the form state on save. Fields which compute vaules from the former state should now compute both values (see domain count); [formGroup], formArrayName, formControlName all necessary directives/properties on form and children. * Disables margin; Removes OnInit in favor of protected member (fixes lint, and type checks). * Ensure excludedDomain-based IDs are offset by the fieldsEditThreshold for form controls existing outside of stored values. * Remove unnecessary chaining.
This commit is contained in:
@@ -14,46 +14,52 @@
|
||||
<bit-section *ngIf="!isLoading">
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h6">{{ "domainsTitle" | i18n }}</h2>
|
||||
<span bitTypography="body2" slot="end">{{ excludedDomainsState?.length || 0 }}</span>
|
||||
<span bitTypography="body2" slot="end">{{
|
||||
excludedDomainsState.length + domainForms.value.length
|
||||
}}</span>
|
||||
</bit-section-header>
|
||||
|
||||
<ng-container *ngIf="excludedDomainsState">
|
||||
<bit-item
|
||||
*ngFor="let domain of excludedDomainsState; let i = index; trackBy: trackByFunction"
|
||||
<bit-item
|
||||
*ngFor="let domain of excludedDomainsState; let i = index; trackBy: trackByFunction"
|
||||
>
|
||||
<bit-item-content *ngIf="i < fieldsEditThreshold">
|
||||
<div id="excludedDomain{{ i }}">{{ domain }}</div>
|
||||
</bit-item-content>
|
||||
<button
|
||||
*ngIf="i < fieldsEditThreshold"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
bitIconButton="bwi-minus-circle"
|
||||
buttonType="danger"
|
||||
size="small"
|
||||
slot="end"
|
||||
type="button"
|
||||
(click)="removeDomain(i)"
|
||||
></button>
|
||||
</bit-item>
|
||||
<form [formGroup]="domainListForm">
|
||||
<bit-card
|
||||
formArrayName="domains"
|
||||
*ngFor="let domain of domainForms.controls; let i = index"
|
||||
>
|
||||
<bit-item-content>
|
||||
<bit-label *ngIf="i >= fieldsEditThreshold">{{
|
||||
"websiteItemLabel" | i18n: i + 1
|
||||
}}</bit-label>
|
||||
<bit-form-field disableMargin>
|
||||
<bit-label>{{ "websiteItemLabel" | i18n: i + fieldsEditThreshold + 1 }}</bit-label>
|
||||
<input
|
||||
*ngIf="i >= fieldsEditThreshold"
|
||||
bitInput
|
||||
#uriInput
|
||||
appInputVerbatim
|
||||
bitInput
|
||||
id="excludedDomain{{ i }}"
|
||||
id="excludedDomain{{ i + fieldsEditThreshold }}"
|
||||
inputmode="url"
|
||||
name="excludedDomain{{ i }}"
|
||||
name="excludedDomain{{ i + fieldsEditThreshold }}"
|
||||
type="text"
|
||||
(change)="fieldChange()"
|
||||
[(ngModel)]="excludedDomainsState[i]"
|
||||
formControlName="{{ i }}"
|
||||
/>
|
||||
<div id="excludedDomain{{ i }}" *ngIf="i < fieldsEditThreshold">{{ domain }}</div>
|
||||
</bit-item-content>
|
||||
<button
|
||||
*ngIf="i < fieldsEditThreshold"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
bitIconButton="bwi-minus-circle"
|
||||
buttonType="danger"
|
||||
size="small"
|
||||
slot="end"
|
||||
type="button"
|
||||
(click)="removeDomain(i)"
|
||||
></button>
|
||||
</bit-item>
|
||||
</ng-container>
|
||||
<button bitLink class="tw-pt-2" type="button" linkType="primary" (click)="addNewDomain()">
|
||||
<i class="bwi bwi-plus-circle bwi-fw" aria-hidden="true"></i> {{ "addDomain" | i18n }}
|
||||
</button>
|
||||
</bit-form-field>
|
||||
</bit-card>
|
||||
<button bitLink class="tw-pt-2" type="button" linkType="primary" (click)="addNewDomain()">
|
||||
<i class="bwi bwi-plus-circle bwi-fw" aria-hidden="true"></i> {{ "addDomain" | i18n }}
|
||||
</button>
|
||||
</form>
|
||||
</bit-section>
|
||||
</div>
|
||||
<popup-footer slot="footer">
|
||||
|
||||
@@ -7,7 +7,13 @@ import {
|
||||
AfterViewInit,
|
||||
ViewChildren,
|
||||
} from "@angular/core";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import {
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
FormBuilder,
|
||||
FormGroup,
|
||||
FormArray,
|
||||
} from "@angular/forms";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
@@ -45,6 +51,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
|
||||
CommonModule,
|
||||
FormFieldModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
IconButtonModule,
|
||||
ItemModule,
|
||||
JslibModule,
|
||||
@@ -68,6 +75,11 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
isLoading = false;
|
||||
excludedDomainsState: string[] = [];
|
||||
storedExcludedDomains: string[] = [];
|
||||
|
||||
protected domainListForm = new FormGroup({
|
||||
domains: this.formBuilder.array([]),
|
||||
});
|
||||
|
||||
// How many fields should be non-editable before editable fields
|
||||
fieldsEditThreshold: number = 0;
|
||||
|
||||
@@ -77,10 +89,15 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private i18nService: I18nService,
|
||||
private toastService: ToastService,
|
||||
private formBuilder: FormBuilder,
|
||||
) {
|
||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
||||
}
|
||||
|
||||
get domainForms() {
|
||||
return this.domainListForm.get("domains") as FormArray;
|
||||
}
|
||||
|
||||
async ngAfterViewInit() {
|
||||
this.domainSettingsService.neverDomains$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
@@ -117,8 +134,7 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
async addNewDomain() {
|
||||
// add empty field to the Domains list interface
|
||||
this.excludedDomainsState.push("");
|
||||
this.domainForms.push(this.formBuilder.control(null));
|
||||
|
||||
await this.fieldChange();
|
||||
}
|
||||
@@ -148,7 +164,11 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
this.isLoading = true;
|
||||
|
||||
const newExcludedDomainsSaveState: NeverDomains = {};
|
||||
const uniqueExcludedDomains = new Set(this.excludedDomainsState);
|
||||
|
||||
const uniqueExcludedDomains = new Set([
|
||||
...this.excludedDomainsState,
|
||||
...this.domainForms.value,
|
||||
]);
|
||||
|
||||
for (const uri of uniqueExcludedDomains) {
|
||||
if (uri && uri !== "") {
|
||||
@@ -194,13 +214,14 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||
title: "",
|
||||
variant: "success",
|
||||
});
|
||||
|
||||
this.domainForms.clear();
|
||||
} catch {
|
||||
this.toastService.showToast({
|
||||
message: this.i18nService.t("unexpectedError"),
|
||||
title: "",
|
||||
variant: "error",
|
||||
});
|
||||
|
||||
// Don't reset via `handleStateUpdate` to preserve input values
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user