mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-18 15:47:57 +01:00
Auth/PM-13318 - AnonLayoutWrapperData Refactor to add full Translation support (#11513)
* PM-13318 - AnonLayoutWrapperData refactor to support all possible string scenarios (untranslated string, translated string, and translated string with placeholders) * PM-13318 - Fix accidental check in * PM-13318 - Revert the correct change. * PM-13318 - Fix test failures
This commit is contained in:
parent
784dd3c9a0
commit
7297d0fccd
@ -9,7 +9,7 @@ import {
|
|||||||
AnonLayoutWrapperDataService,
|
AnonLayoutWrapperDataService,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { Icon, IconModule } from "@bitwarden/components";
|
import { Icon, IconModule, Translation } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||||
@ -90,11 +90,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (firstChildRouteData["pageTitle"] !== undefined) {
|
if (firstChildRouteData["pageTitle"] !== undefined) {
|
||||||
this.pageTitle = this.i18nService.t(firstChildRouteData["pageTitle"]);
|
this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstChildRouteData["pageSubtitle"] !== undefined) {
|
if (firstChildRouteData["pageSubtitle"] !== undefined) {
|
||||||
this.pageSubtitle = this.i18nService.t(firstChildRouteData["pageSubtitle"]);
|
this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstChildRouteData["pageIcon"] !== undefined) {
|
if (firstChildRouteData["pageIcon"] !== undefined) {
|
||||||
@ -132,19 +132,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.pageTitle) {
|
if (data.pageTitle) {
|
||||||
this.pageTitle = this.i18nService.t(data.pageTitle);
|
this.pageTitle = this.handleStringOrTranslation(data.pageTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.pageSubtitle) {
|
if (data.pageSubtitle) {
|
||||||
// If you pass just a string, we translate it by default
|
this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle);
|
||||||
if (typeof data.pageSubtitle === "string") {
|
|
||||||
this.pageSubtitle = this.i18nService.t(data.pageSubtitle);
|
|
||||||
} else {
|
|
||||||
// if you pass an object, you can specify if you want to translate it or not
|
|
||||||
this.pageSubtitle = data.pageSubtitle.translate
|
|
||||||
? this.i18nService.t(data.pageSubtitle.subtitle)
|
|
||||||
: data.pageSubtitle.subtitle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.pageIcon) {
|
if (data.pageIcon) {
|
||||||
@ -168,6 +160,16 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleStringOrTranslation(value: string | Translation): string {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
// If it's a string, return it as is
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a Translation object, translate it
|
||||||
|
return this.i18nService.t(value.key, ...(value.placeholders ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
private resetPageData() {
|
private resetPageData() {
|
||||||
this.pageTitle = null;
|
this.pageTitle = null;
|
||||||
this.pageSubtitle = null;
|
this.pageSubtitle = null;
|
||||||
|
@ -221,8 +221,12 @@ export const DefaultContentExample: Story = {
|
|||||||
|
|
||||||
// Dynamic Content Example
|
// Dynamic Content Example
|
||||||
const initialData: ExtensionAnonLayoutWrapperData = {
|
const initialData: ExtensionAnonLayoutWrapperData = {
|
||||||
pageTitle: "setAStrongPassword",
|
pageTitle: {
|
||||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
key: "setAStrongPassword",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishCreatingYourAccountBySettingAPassword",
|
||||||
|
},
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
showAcctSwitcher: true,
|
showAcctSwitcher: true,
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
@ -230,8 +234,12 @@ const initialData: ExtensionAnonLayoutWrapperData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const changedData: ExtensionAnonLayoutWrapperData = {
|
const changedData: ExtensionAnonLayoutWrapperData = {
|
||||||
pageTitle: "enterpriseSingleSignOn",
|
pageTitle: {
|
||||||
pageSubtitle: "checkYourEmail",
|
key: "enterpriseSingleSignOn",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "checkYourEmail",
|
||||||
|
},
|
||||||
pageIcon: RegistrationCheckEmailIcon,
|
pageIcon: RegistrationCheckEmailIcon,
|
||||||
showAcctSwitcher: false,
|
showAcctSwitcher: false,
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
|
@ -449,7 +449,9 @@ const routes: Routes = [
|
|||||||
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
|
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
|
||||||
data: {
|
data: {
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
pageTitle: "yourVaultIsLockedV2",
|
pageTitle: {
|
||||||
|
key: "yourVaultIsLockedV2",
|
||||||
|
},
|
||||||
showReadonlyHostname: true,
|
showReadonlyHostname: true,
|
||||||
showAcctSwitcher: true,
|
showAcctSwitcher: true,
|
||||||
} satisfies ExtensionAnonLayoutWrapperData,
|
} satisfies ExtensionAnonLayoutWrapperData,
|
||||||
@ -471,7 +473,9 @@ const routes: Routes = [
|
|||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
state: "signup",
|
state: "signup",
|
||||||
pageTitle: "createAccount",
|
pageTitle: {
|
||||||
|
key: "createAccount",
|
||||||
|
},
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@ -492,8 +496,12 @@ const routes: Routes = [
|
|||||||
path: "finish-signup",
|
path: "finish-signup",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "setAStrongPassword",
|
pageTitle: {
|
||||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
key: "setAStrongPassword",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishCreatingYourAccountBySettingAPassword",
|
||||||
|
},
|
||||||
state: "finish-signup",
|
state: "finish-signup",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -508,8 +516,12 @@ const routes: Routes = [
|
|||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
||||||
component: SetPasswordJitComponent,
|
component: SetPasswordJitComponent,
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "joinOrganization",
|
pageTitle: {
|
||||||
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
key: "joinOrganization",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
||||||
|
},
|
||||||
state: "set-password-jit",
|
state: "set-password-jit",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
|
@ -141,8 +141,12 @@ const routes: Routes = [
|
|||||||
path: "hint",
|
path: "hint",
|
||||||
canActivate: [unauthGuardFn()],
|
canActivate: [unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "requestPasswordHint",
|
pageTitle: {
|
||||||
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
|
key: "requestPasswordHint",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
|
||||||
|
},
|
||||||
pageIcon: UserLockIcon,
|
pageIcon: UserLockIcon,
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -164,7 +168,11 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: "signup",
|
path: "signup",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: { pageTitle: "createAccount" } satisfies AnonLayoutWrapperData,
|
data: {
|
||||||
|
pageTitle: {
|
||||||
|
key: "createAccount",
|
||||||
|
},
|
||||||
|
} satisfies AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
@ -184,8 +192,12 @@ const routes: Routes = [
|
|||||||
path: "finish-signup",
|
path: "finish-signup",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "setAStrongPassword",
|
pageTitle: {
|
||||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
key: "setAStrongPassword",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishCreatingYourAccountBySettingAPassword",
|
||||||
|
},
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@ -199,7 +211,9 @@ const routes: Routes = [
|
|||||||
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
|
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
|
||||||
data: {
|
data: {
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
pageTitle: "yourVaultIsLockedV2",
|
pageTitle: {
|
||||||
|
key: "yourVaultIsLockedV2",
|
||||||
|
},
|
||||||
showReadonlyHostname: true,
|
showReadonlyHostname: true,
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -214,8 +228,12 @@ const routes: Routes = [
|
|||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
||||||
component: SetPasswordJitComponent,
|
component: SetPasswordJitComponent,
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "joinOrganization",
|
pageTitle: {
|
||||||
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
key: "joinOrganization",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
||||||
|
},
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -14,22 +14,24 @@ describe("freeTrialTextResolver", () => {
|
|||||||
it("shows password manager text", () => {
|
it("shows password manager text", () => {
|
||||||
route.queryParams.product = `${ProductType.PasswordManager}`;
|
route.queryParams.product = `${ProductType.PasswordManager}`;
|
||||||
|
|
||||||
expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe(
|
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
|
||||||
"continueSettingUpFreeTrialPasswordManager",
|
key: "continueSettingUpFreeTrialPasswordManager",
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows secret manager text", () => {
|
it("shows secret manager text", () => {
|
||||||
route.queryParams.product = `${ProductType.SecretsManager}`;
|
route.queryParams.product = `${ProductType.SecretsManager}`;
|
||||||
|
|
||||||
expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe(
|
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
|
||||||
"continueSettingUpFreeTrialSecretsManager",
|
key: "continueSettingUpFreeTrialSecretsManager",
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows default text", () => {
|
it("shows default text", () => {
|
||||||
route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`;
|
route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`;
|
||||||
|
|
||||||
expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe("continueSettingUpFreeTrial");
|
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
|
||||||
|
key: "continueSettingUpFreeTrial",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { ActivatedRouteSnapshot, ResolveFn } from "@angular/router";
|
import { ActivatedRouteSnapshot, ResolveFn } from "@angular/router";
|
||||||
|
|
||||||
import { ProductType } from "@bitwarden/common/billing/enums";
|
import { ProductType } from "@bitwarden/common/billing/enums";
|
||||||
|
import { Translation } from "@bitwarden/components";
|
||||||
|
|
||||||
export const freeTrialTextResolver: ResolveFn<string | null> = (
|
export const freeTrialTextResolver: ResolveFn<Translation | null> = (
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
): string | null => {
|
): Translation | null => {
|
||||||
const { product } = route.queryParams;
|
const { product } = route.queryParams;
|
||||||
const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p));
|
const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p));
|
||||||
|
|
||||||
@ -13,10 +14,16 @@ export const freeTrialTextResolver: ResolveFn<string | null> = (
|
|||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case onlyPasswordManager:
|
case onlyPasswordManager:
|
||||||
return "continueSettingUpFreeTrialPasswordManager";
|
return {
|
||||||
|
key: "continueSettingUpFreeTrialPasswordManager",
|
||||||
|
};
|
||||||
case onlySecretsManager:
|
case onlySecretsManager:
|
||||||
return "continueSettingUpFreeTrialSecretsManager";
|
return {
|
||||||
|
key: "continueSettingUpFreeTrialSecretsManager",
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return "continueSettingUpFreeTrial";
|
return {
|
||||||
|
key: "continueSettingUpFreeTrial",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -228,7 +228,9 @@ const routes: Routes = [
|
|||||||
path: "signup",
|
path: "signup",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "createAccount",
|
pageTitle: {
|
||||||
|
key: "createAccount",
|
||||||
|
},
|
||||||
titleId: "createAccount",
|
titleId: "createAccount",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -250,8 +252,12 @@ const routes: Routes = [
|
|||||||
path: "finish-signup",
|
path: "finish-signup",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "setAStrongPassword",
|
pageTitle: {
|
||||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
key: "setAStrongPassword",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishCreatingYourAccountBySettingAPassword",
|
||||||
|
},
|
||||||
titleId: "setAStrongPassword",
|
titleId: "setAStrongPassword",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -264,7 +270,9 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: "send/:sendId/:key",
|
path: "send/:sendId/:key",
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "viewSend",
|
pageTitle: {
|
||||||
|
key: "viewSend",
|
||||||
|
},
|
||||||
showReadonlyHostname: true,
|
showReadonlyHostname: true,
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -284,15 +292,21 @@ const routes: Routes = [
|
|||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
||||||
component: SetPasswordJitComponent,
|
component: SetPasswordJitComponent,
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "joinOrganization",
|
pageTitle: {
|
||||||
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
key: "joinOrganization",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
||||||
|
},
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "signup-link-expired",
|
path: "signup-link-expired",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "expiredLink",
|
pageTitle: {
|
||||||
|
key: "expiredLink",
|
||||||
|
},
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@ -308,7 +322,9 @@ const routes: Routes = [
|
|||||||
path: "sso",
|
path: "sso",
|
||||||
canActivate: [unauthGuardFn()],
|
canActivate: [unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "enterpriseSingleSignOn",
|
pageTitle: {
|
||||||
|
key: "enterpriseSingleSignOn",
|
||||||
|
},
|
||||||
titleId: "enterpriseSingleSignOn",
|
titleId: "enterpriseSingleSignOn",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -338,7 +354,9 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "logIn",
|
pageTitle: {
|
||||||
|
key: "logIn",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...extensionRefreshSwap(
|
...extensionRefreshSwap(
|
||||||
@ -354,7 +372,9 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "yourVaultIsLockedV2",
|
pageTitle: {
|
||||||
|
key: "yourVaultIsLockedV2",
|
||||||
|
},
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
showReadonlyHostname: true,
|
showReadonlyHostname: true,
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
@ -369,7 +389,9 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "yourAccountIsLocked",
|
pageTitle: {
|
||||||
|
key: "yourAccountIsLocked",
|
||||||
|
},
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
showReadonlyHostname: true,
|
showReadonlyHostname: true,
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
@ -390,7 +412,9 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "verifyIdentity",
|
pageTitle: {
|
||||||
|
key: "verifyIdentity",
|
||||||
|
},
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -408,7 +432,9 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "recoverAccountTwoStep",
|
pageTitle: {
|
||||||
|
key: "recoverAccountTwoStep",
|
||||||
|
},
|
||||||
titleId: "recoverAccountTwoStep",
|
titleId: "recoverAccountTwoStep",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
@ -416,7 +442,9 @@ const routes: Routes = [
|
|||||||
path: "accept-emergency",
|
path: "accept-emergency",
|
||||||
canActivate: [deepLinkGuard()],
|
canActivate: [deepLinkGuard()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "emergencyAccess",
|
pageTitle: {
|
||||||
|
key: "emergencyAccess",
|
||||||
|
},
|
||||||
titleId: "acceptEmergency",
|
titleId: "acceptEmergency",
|
||||||
doNotSaveUrl: false,
|
doNotSaveUrl: false,
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
@ -434,7 +462,9 @@ const routes: Routes = [
|
|||||||
path: "recover-delete",
|
path: "recover-delete",
|
||||||
canActivate: [unauthGuardFn()],
|
canActivate: [unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "deleteAccount",
|
pageTitle: {
|
||||||
|
key: "deleteAccount",
|
||||||
|
},
|
||||||
titleId: "deleteAccount",
|
titleId: "deleteAccount",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -453,7 +483,9 @@ const routes: Routes = [
|
|||||||
path: "verify-recover-delete",
|
path: "verify-recover-delete",
|
||||||
canActivate: [unauthGuardFn()],
|
canActivate: [unauthGuardFn()],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "deleteAccount",
|
pageTitle: {
|
||||||
|
key: "deleteAccount",
|
||||||
|
},
|
||||||
titleId: "deleteAccount",
|
titleId: "deleteAccount",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
children: [
|
children: [
|
||||||
@ -468,7 +500,9 @@ const routes: Routes = [
|
|||||||
component: RemovePasswordComponent,
|
component: RemovePasswordComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: "removeMasterPassword",
|
pageTitle: {
|
||||||
|
key: "removeMasterPassword",
|
||||||
|
},
|
||||||
titleId: "removeMasterPassword",
|
titleId: "removeMasterPassword",
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
|
@ -157,8 +157,8 @@ export class AccessComponent implements OnInit {
|
|||||||
if (this.creatorIdentifier != null) {
|
if (this.creatorIdentifier != null) {
|
||||||
this.layoutWrapperDataService.setAnonLayoutWrapperData({
|
this.layoutWrapperDataService.setAnonLayoutWrapperData({
|
||||||
pageSubtitle: {
|
pageSubtitle: {
|
||||||
subtitle: this.i18nService.t("sendAccessCreatorIdentifier", this.creatorIdentifier),
|
key: "sendAccessCreatorIdentifier",
|
||||||
translate: false,
|
placeholders: [this.creatorIdentifier],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,34 @@ import { Subject, filter, switchMap, takeUntil, tap } from "rxjs";
|
|||||||
|
|
||||||
import { AnonLayoutComponent } from "@bitwarden/auth/angular";
|
import { AnonLayoutComponent } from "@bitwarden/auth/angular";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { Icon } from "@bitwarden/components";
|
import { Icon, Translation } from "@bitwarden/components";
|
||||||
|
|
||||||
import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service";
|
import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service";
|
||||||
|
|
||||||
export interface AnonLayoutWrapperData {
|
export interface AnonLayoutWrapperData {
|
||||||
pageTitle?: string;
|
/**
|
||||||
pageSubtitle?:
|
* The optional title of the page.
|
||||||
| string
|
* If a string is provided, it will be presented as is (ex: Organization name)
|
||||||
| {
|
* If a Translation object (supports placeholders) is provided, it will be translated
|
||||||
subtitle: string;
|
*/
|
||||||
translate: boolean;
|
pageTitle?: string | Translation;
|
||||||
};
|
/**
|
||||||
|
* The optional subtitle of the page.
|
||||||
|
* If a string is provided, it will be presented as is (ex: user's email)
|
||||||
|
* If a Translation object (supports placeholders) is provided, it will be translated
|
||||||
|
*/
|
||||||
|
pageSubtitle?: string | Translation;
|
||||||
|
/**
|
||||||
|
* The optional icon to display on the page.
|
||||||
|
*/
|
||||||
pageIcon?: Icon;
|
pageIcon?: Icon;
|
||||||
|
/**
|
||||||
|
* Optional flag to either show the optional environment selector (false) or just a readonly hostname (true).
|
||||||
|
*/
|
||||||
showReadonlyHostname?: boolean;
|
showReadonlyHostname?: boolean;
|
||||||
|
/**
|
||||||
|
* Optional flag to set the max-width of the page. Defaults to 'md' if not provided.
|
||||||
|
*/
|
||||||
maxWidth?: "md" | "3xl";
|
maxWidth?: "md" | "3xl";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +85,11 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (firstChildRouteData["pageTitle"] !== undefined) {
|
if (firstChildRouteData["pageTitle"] !== undefined) {
|
||||||
this.pageTitle = this.i18nService.t(firstChildRouteData["pageTitle"]);
|
this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstChildRouteData["pageSubtitle"] !== undefined) {
|
if (firstChildRouteData["pageSubtitle"] !== undefined) {
|
||||||
this.pageSubtitle = this.i18nService.t(firstChildRouteData["pageSubtitle"]);
|
this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstChildRouteData["pageIcon"] !== undefined) {
|
if (firstChildRouteData["pageIcon"] !== undefined) {
|
||||||
@ -101,19 +115,11 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.pageTitle) {
|
if (data.pageTitle) {
|
||||||
this.pageTitle = this.i18nService.t(data.pageTitle);
|
this.pageTitle = this.handleStringOrTranslation(data.pageTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.pageSubtitle) {
|
if (data.pageSubtitle) {
|
||||||
// If you pass just a string, we translate it by default
|
this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle);
|
||||||
if (typeof data.pageSubtitle === "string") {
|
|
||||||
this.pageSubtitle = this.i18nService.t(data.pageSubtitle);
|
|
||||||
} else {
|
|
||||||
// if you pass an object, you can specify if you want to translate it or not
|
|
||||||
this.pageSubtitle = data.pageSubtitle.translate
|
|
||||||
? this.i18nService.t(data.pageSubtitle.subtitle)
|
|
||||||
: data.pageSubtitle.subtitle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.pageIcon) {
|
if (data.pageIcon) {
|
||||||
@ -129,6 +135,16 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleStringOrTranslation(value: string | Translation): string {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
// If it's a string, return it as is
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a Translation object, translate it
|
||||||
|
return this.i18nService.t(value.key, ...(value.placeholders ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
private resetPageData() {
|
private resetPageData() {
|
||||||
this.pageTitle = null;
|
this.pageTitle = null;
|
||||||
this.pageSubtitle = null;
|
this.pageSubtitle = null;
|
||||||
|
@ -163,17 +163,20 @@ export const DefaultContentExample: Story = {
|
|||||||
|
|
||||||
// Dynamic Content Example
|
// Dynamic Content Example
|
||||||
const initialData: AnonLayoutWrapperData = {
|
const initialData: AnonLayoutWrapperData = {
|
||||||
pageTitle: "setAStrongPassword",
|
pageTitle: {
|
||||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
key: "setAStrongPassword",
|
||||||
|
},
|
||||||
|
pageSubtitle: {
|
||||||
|
key: "finishCreatingYourAccountBySettingAPassword",
|
||||||
|
},
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
const changedData: AnonLayoutWrapperData = {
|
const changedData: AnonLayoutWrapperData = {
|
||||||
pageTitle: "enterpriseSingleSignOn",
|
pageTitle: {
|
||||||
pageSubtitle: {
|
key: "enterpriseSingleSignOn",
|
||||||
subtitle: "user@email.com (non-translated)",
|
|
||||||
translate: false,
|
|
||||||
},
|
},
|
||||||
|
pageSubtitle: "user@email.com (non-translated)",
|
||||||
pageIcon: RegistrationCheckEmailIcon,
|
pageIcon: RegistrationCheckEmailIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,10 +233,7 @@ export class LockV2Component implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private setEmailAsPageSubtitle(email: string) {
|
private setEmailAsPageSubtitle(email: string) {
|
||||||
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
|
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
|
||||||
pageSubtitle: {
|
pageSubtitle: email,
|
||||||
subtitle: email,
|
|
||||||
translate: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user