-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PM-7713] Refresh Appearance Settings (#10458)
* add v2 of appearance component * swap in new appearance component based on refresh flag * update default theme verbiage
- Loading branch information
1 parent
4556e59
commit d212bb1
Showing
5 changed files
with
261 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
apps/browser/src/vault/popup/settings/appearance-v2.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<popup-page> | ||
<popup-header slot="header" [pageTitle]="'appearance' | i18n" showBackButton> | ||
<ng-container slot="end"> | ||
<app-pop-out></app-pop-out> | ||
</ng-container> | ||
</popup-header> | ||
|
||
<form [formGroup]="appearanceForm"> | ||
<bit-card> | ||
<bit-form-field> | ||
<bit-label>{{ "theme" | i18n }}</bit-label> | ||
<bit-select formControlName="theme"> | ||
<bit-option | ||
*ngFor="let o of themeOptions" | ||
[value]="o.value" | ||
[label]="o.name" | ||
></bit-option> | ||
</bit-select> | ||
</bit-form-field> | ||
|
||
<bit-form-control> | ||
<input bitCheckbox formControlName="enableBadgeCounter" type="checkbox" /> | ||
<bit-label>{{ "showNumberOfAutofillSuggestions" | i18n }}</bit-label> | ||
</bit-form-control> | ||
|
||
<bit-form-control disableMargin> | ||
<input bitCheckbox formControlName="enableFavicon" type="checkbox" /> | ||
<bit-label>{{ "enableFavicon" | i18n }}</bit-label> | ||
</bit-form-control> | ||
</bit-card> | ||
</form> | ||
</popup-page> |
110 changes: 110 additions & 0 deletions
110
apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { Component, Input } from "@angular/core"; | ||
import { ComponentFixture, TestBed } from "@angular/core/testing"; | ||
import { mock } from "jest-mock-extended"; | ||
import { BehaviorSubject } from "rxjs"; | ||
|
||
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service"; | ||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; | ||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; | ||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; | ||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; | ||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; | ||
import { ThemeType } from "@bitwarden/common/platform/enums"; | ||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; | ||
|
||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; | ||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; | ||
|
||
import { AppearanceV2Component } from "./appearance-v2.component"; | ||
|
||
@Component({ | ||
standalone: true, | ||
selector: "popup-header", | ||
template: `<ng-content></ng-content>`, | ||
}) | ||
class MockPopupHeaderComponent { | ||
@Input() pageTitle: string; | ||
@Input() backAction: () => void; | ||
} | ||
|
||
@Component({ | ||
standalone: true, | ||
selector: "popup-page", | ||
template: `<ng-content></ng-content>`, | ||
}) | ||
class MockPopupPageComponent {} | ||
|
||
describe("AppearanceV2Component", () => { | ||
let component: AppearanceV2Component; | ||
let fixture: ComponentFixture<AppearanceV2Component>; | ||
|
||
const showFavicons$ = new BehaviorSubject<boolean>(true); | ||
const enableBadgeCounter$ = new BehaviorSubject<boolean>(true); | ||
const selectedTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Nord); | ||
const setSelectedTheme = jest.fn().mockResolvedValue(undefined); | ||
const setShowFavicons = jest.fn().mockResolvedValue(undefined); | ||
const setEnableBadgeCounter = jest.fn().mockResolvedValue(undefined); | ||
|
||
beforeEach(async () => { | ||
setSelectedTheme.mockClear(); | ||
setShowFavicons.mockClear(); | ||
setEnableBadgeCounter.mockClear(); | ||
|
||
await TestBed.configureTestingModule({ | ||
imports: [AppearanceV2Component], | ||
providers: [ | ||
{ provide: ConfigService, useValue: mock<ConfigService>() }, | ||
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() }, | ||
{ provide: MessagingService, useValue: mock<MessagingService>() }, | ||
{ provide: I18nService, useValue: { t: (key: string) => key } }, | ||
{ provide: DomainSettingsService, useValue: { showFavicons$, setShowFavicons } }, | ||
{ | ||
provide: BadgeSettingsServiceAbstraction, | ||
useValue: { enableBadgeCounter$, setEnableBadgeCounter }, | ||
}, | ||
{ provide: ThemeStateService, useValue: { selectedTheme$, setSelectedTheme } }, | ||
], | ||
}) | ||
.overrideComponent(AppearanceV2Component, { | ||
remove: { | ||
imports: [PopupHeaderComponent, PopupPageComponent], | ||
}, | ||
add: { | ||
imports: [MockPopupHeaderComponent, MockPopupPageComponent], | ||
}, | ||
}) | ||
.compileComponents(); | ||
|
||
fixture = TestBed.createComponent(AppearanceV2Component); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it("populates the form with the user's current settings", () => { | ||
expect(component.appearanceForm.value).toEqual({ | ||
enableFavicon: true, | ||
enableBadgeCounter: true, | ||
theme: ThemeType.Nord, | ||
}); | ||
}); | ||
|
||
describe("form changes", () => { | ||
it("updates the users theme", () => { | ||
component.appearanceForm.controls.theme.setValue(ThemeType.Light); | ||
|
||
expect(setSelectedTheme).toHaveBeenCalledWith(ThemeType.Light); | ||
}); | ||
|
||
it("updates the users favicon setting", () => { | ||
component.appearanceForm.controls.enableFavicon.setValue(false); | ||
|
||
expect(setShowFavicons).toHaveBeenCalledWith(false); | ||
}); | ||
|
||
it("updates the users badge counter setting", () => { | ||
component.appearanceForm.controls.enableBadgeCounter.setValue(false); | ||
|
||
expect(setEnableBadgeCounter).toHaveBeenCalledWith(false); | ||
}); | ||
}); | ||
}); |
110 changes: 110 additions & 0 deletions
110
apps/browser/src/vault/popup/settings/appearance-v2.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { CommonModule } from "@angular/common"; | ||
import { Component, DestroyRef, OnInit } from "@angular/core"; | ||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; | ||
import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; | ||
import { firstValueFrom } from "rxjs"; | ||
|
||
import { JslibModule } from "@bitwarden/angular/jslib.module"; | ||
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service"; | ||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; | ||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; | ||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; | ||
import { ThemeType } from "@bitwarden/common/platform/enums"; | ||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; | ||
import { CheckboxModule } from "@bitwarden/components"; | ||
|
||
import { CardComponent } from "../../../../../../libs/components/src/card/card.component"; | ||
import { FormFieldModule } from "../../../../../../libs/components/src/form-field/form-field.module"; | ||
import { SelectModule } from "../../../../../../libs/components/src/select/select.module"; | ||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; | ||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; | ||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; | ||
|
||
@Component({ | ||
standalone: true, | ||
templateUrl: "./appearance-v2.component.html", | ||
imports: [ | ||
CommonModule, | ||
JslibModule, | ||
PopupPageComponent, | ||
PopupHeaderComponent, | ||
PopOutComponent, | ||
CardComponent, | ||
FormFieldModule, | ||
SelectModule, | ||
ReactiveFormsModule, | ||
CheckboxModule, | ||
], | ||
}) | ||
export class AppearanceV2Component implements OnInit { | ||
appearanceForm = this.formBuilder.group({ | ||
enableFavicon: false, | ||
enableBadgeCounter: true, | ||
theme: ThemeType.System, | ||
}); | ||
|
||
/** Available theme options */ | ||
themeOptions: { name: string; value: ThemeType }[]; | ||
|
||
constructor( | ||
private messagingService: MessagingService, | ||
private domainSettingsService: DomainSettingsService, | ||
private badgeSettingsService: BadgeSettingsServiceAbstraction, | ||
private themeStateService: ThemeStateService, | ||
private formBuilder: FormBuilder, | ||
private destroyRef: DestroyRef, | ||
i18nService: I18nService, | ||
) { | ||
this.themeOptions = [ | ||
{ name: i18nService.t("systemDefault"), value: ThemeType.System }, | ||
{ name: i18nService.t("light"), value: ThemeType.Light }, | ||
{ name: i18nService.t("dark"), value: ThemeType.Dark }, | ||
{ name: "Nord", value: ThemeType.Nord }, | ||
{ name: i18nService.t("solarizedDark"), value: ThemeType.SolarizedDark }, | ||
]; | ||
} | ||
|
||
async ngOnInit() { | ||
const enableFavicon = await firstValueFrom(this.domainSettingsService.showFavicons$); | ||
const enableBadgeCounter = await firstValueFrom(this.badgeSettingsService.enableBadgeCounter$); | ||
const theme = await firstValueFrom(this.themeStateService.selectedTheme$); | ||
|
||
// Set initial values for the form | ||
this.appearanceForm.setValue({ | ||
enableFavicon, | ||
enableBadgeCounter, | ||
theme, | ||
}); | ||
|
||
this.appearanceForm.controls.theme.valueChanges | ||
.pipe(takeUntilDestroyed(this.destroyRef)) | ||
.subscribe((newTheme) => { | ||
void this.saveTheme(newTheme); | ||
}); | ||
|
||
this.appearanceForm.controls.enableFavicon.valueChanges | ||
.pipe(takeUntilDestroyed(this.destroyRef)) | ||
.subscribe((enableFavicon) => { | ||
void this.updateFavicon(enableFavicon); | ||
}); | ||
|
||
this.appearanceForm.controls.enableBadgeCounter.valueChanges | ||
.pipe(takeUntilDestroyed(this.destroyRef)) | ||
.subscribe((enableBadgeCounter) => { | ||
void this.updateBadgeCounter(enableBadgeCounter); | ||
}); | ||
} | ||
|
||
async updateFavicon(enableFavicon: boolean) { | ||
await this.domainSettingsService.setShowFavicons(enableFavicon); | ||
} | ||
|
||
async updateBadgeCounter(enableBadgeCounter: boolean) { | ||
await this.badgeSettingsService.setEnableBadgeCounter(enableBadgeCounter); | ||
this.messagingService.send("bgUpdateContextMenu"); | ||
} | ||
|
||
async saveTheme(newTheme: ThemeType) { | ||
await this.themeStateService.setSelectedTheme(newTheme); | ||
} | ||
} |