Skip to content

Commit

Permalink
Auth/PM-13318 - AnonLayoutWrapperData Refactor to add full Translatio…
Browse files Browse the repository at this point in the history
…n 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
  • Loading branch information
JaredSnider-Bitwarden authored Oct 11, 2024
1 parent 784dd3c commit 7297d0f
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
AnonLayoutWrapperDataService,
} from "@bitwarden/auth/angular";
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 { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
Expand Down Expand Up @@ -90,11 +90,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
}

if (firstChildRouteData["pageTitle"] !== undefined) {
this.pageTitle = this.i18nService.t(firstChildRouteData["pageTitle"]);
this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
}

if (firstChildRouteData["pageSubtitle"] !== undefined) {
this.pageSubtitle = this.i18nService.t(firstChildRouteData["pageSubtitle"]);
this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
}

if (firstChildRouteData["pageIcon"] !== undefined) {
Expand Down Expand Up @@ -132,19 +132,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
}

if (data.pageTitle) {
this.pageTitle = this.i18nService.t(data.pageTitle);
this.pageTitle = this.handleStringOrTranslation(data.pageTitle);
}

if (data.pageSubtitle) {
// If you pass just a string, we translate it by default
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;
}
this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle);
}

if (data.pageIcon) {
Expand All @@ -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() {
this.pageTitle = null;
this.pageSubtitle = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,25 @@ export const DefaultContentExample: Story = {

// Dynamic Content Example
const initialData: ExtensionAnonLayoutWrapperData = {
pageTitle: "setAStrongPassword",
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
pageTitle: {
key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
pageIcon: LockIcon,
showAcctSwitcher: true,
showBackButton: true,
showLogo: true,
};

const changedData: ExtensionAnonLayoutWrapperData = {
pageTitle: "enterpriseSingleSignOn",
pageSubtitle: "checkYourEmail",
pageTitle: {
key: "enterpriseSingleSignOn",
},
pageSubtitle: {
key: "checkYourEmail",
},
pageIcon: RegistrationCheckEmailIcon,
showAcctSwitcher: false,
showBackButton: false,
Expand Down
24 changes: 18 additions & 6 deletions apps/browser/src/popup/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,9 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
data: {
pageIcon: LockIcon,
pageTitle: "yourVaultIsLockedV2",
pageTitle: {
key: "yourVaultIsLockedV2",
},
showReadonlyHostname: true,
showAcctSwitcher: true,
} satisfies ExtensionAnonLayoutWrapperData,
Expand All @@ -471,7 +473,9 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
state: "signup",
pageTitle: "createAccount",
pageTitle: {
key: "createAccount",
},
} satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [
{
Expand All @@ -492,8 +496,12 @@ const routes: Routes = [
path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
pageTitle: "setAStrongPassword",
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
pageTitle: {
key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
state: "finish-signup",
} satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [
Expand All @@ -508,8 +516,12 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
component: SetPasswordJitComponent,
data: {
pageTitle: "joinOrganization",
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword",
pageTitle: {
key: "joinOrganization",
},
pageSubtitle: {
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
},
state: "set-password-jit",
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
Expand Down
34 changes: 26 additions & 8 deletions apps/desktop/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,12 @@ const routes: Routes = [
path: "hint",
canActivate: [unauthGuardFn()],
data: {
pageTitle: "requestPasswordHint",
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
pageTitle: {
key: "requestPasswordHint",
},
pageSubtitle: {
key: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
},
pageIcon: UserLockIcon,
} satisfies AnonLayoutWrapperData,
children: [
Expand All @@ -164,7 +168,11 @@ const routes: Routes = [
{
path: "signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { pageTitle: "createAccount" } satisfies AnonLayoutWrapperData,
data: {
pageTitle: {
key: "createAccount",
},
} satisfies AnonLayoutWrapperData,
children: [
{
path: "",
Expand All @@ -184,8 +192,12 @@ const routes: Routes = [
path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
pageTitle: "setAStrongPassword",
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
pageTitle: {
key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
} satisfies AnonLayoutWrapperData,
children: [
{
Expand All @@ -199,7 +211,9 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
data: {
pageIcon: LockIcon,
pageTitle: "yourVaultIsLockedV2",
pageTitle: {
key: "yourVaultIsLockedV2",
},
showReadonlyHostname: true,
} satisfies AnonLayoutWrapperData,
children: [
Expand All @@ -214,8 +228,12 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
component: SetPasswordJitComponent,
data: {
pageTitle: "joinOrganization",
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword",
pageTitle: {
key: "joinOrganization",
},
pageSubtitle: {
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
},
} satisfies AnonLayoutWrapperData,
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@ describe("freeTrialTextResolver", () => {
it("shows password manager text", () => {
route.queryParams.product = `${ProductType.PasswordManager}`;

expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe(
"continueSettingUpFreeTrialPasswordManager",
);
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpFreeTrialPasswordManager",
});
});

it("shows secret manager text", () => {
route.queryParams.product = `${ProductType.SecretsManager}`;

expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe(
"continueSettingUpFreeTrialSecretsManager",
);
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpFreeTrialSecretsManager",
});
});

it("shows default text", () => {
route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`;

expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe("continueSettingUpFreeTrial");
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpFreeTrial",
});
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ActivatedRouteSnapshot, ResolveFn } from "@angular/router";

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,
): string | null => {
): Translation | null => {
const { product } = route.queryParams;
const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p));

Expand All @@ -13,10 +14,16 @@ export const freeTrialTextResolver: ResolveFn<string | null> = (

switch (true) {
case onlyPasswordManager:
return "continueSettingUpFreeTrialPasswordManager";
return {
key: "continueSettingUpFreeTrialPasswordManager",
};
case onlySecretsManager:
return "continueSettingUpFreeTrialSecretsManager";
return {
key: "continueSettingUpFreeTrialSecretsManager",
};
default:
return "continueSettingUpFreeTrial";
return {
key: "continueSettingUpFreeTrial",
};
}
};
Loading

0 comments on commit 7297d0f

Please sign in to comment.