diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts index 28db10b35fa..9202a5a3839 100644 --- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts +++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts @@ -44,6 +44,40 @@ describe("OverlayNotificationsContentService", () => { expect(bodyAppendChildSpy).not.toHaveBeenCalled(); }); + it("applies correct styles when notificationRefreshFlag is true", async () => { + overlayNotificationsContentService["notificationRefreshFlag"] = true; + + sendMockExtensionMessage({ + command: "openNotificationBar", + data: { + type: "change", + typeData: mock(), + }, + }); + await flushPromises(); + + const barElement = overlayNotificationsContentService["notificationBarElement"]!; + expect(barElement.style.height).toBe("400px"); + expect(barElement.style.right).toBe("0px"); + }); + + it("applies correct styles when notificationRefreshFlag is false", async () => { + overlayNotificationsContentService["notificationRefreshFlag"] = false; + + sendMockExtensionMessage({ + command: "openNotificationBar", + data: { + type: "change", + typeData: mock(), + }, + }); + await flushPromises(); + + const barElement = overlayNotificationsContentService["notificationBarElement"]!; + expect(barElement.style.height).toBe("82px"); + expect(barElement.style.right).toBe("10px"); + }); + it("closes the notification bar if the notification bar type has changed", async () => { overlayNotificationsContentService["currentNotificationBarType"] = "add"; const closeNotificationBarSpy = jest.spyOn( diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts index ee005852a42..01f8237581d 100644 --- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts +++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts @@ -21,24 +21,32 @@ export class OverlayNotificationsContentService private notificationBarIframeElement: HTMLIFrameElement | null = null; private currentNotificationBarType: NotificationType | null = null; private notificationRefreshFlag: boolean = false; - private notificationBarElementStyles: Partial = { - height: "82px", - width: "430px", - maxWidth: "calc(100% - 20px)", - minHeight: "initial", - top: "10px", - right: "10px", - padding: "0", - position: "fixed", - zIndex: "2147483647", - visibility: "visible", - borderRadius: "4px", - border: "none", - backgroundColor: "transparent", - overflow: "hidden", - transition: "box-shadow 0.15s ease", - transitionDelay: "0.15s", - }; + private getNotificationBarStyles(): Partial { + const styles: Partial = { + height: "400px", + width: "430px", + maxWidth: "calc(100% - 20px)", + minHeight: "initial", + top: "10px", + right: "0px", + padding: "0", + position: "fixed", + zIndex: "2147483647", + visibility: "visible", + borderRadius: "4px", + border: "none", + backgroundColor: "transparent", + overflow: "hidden", + transition: "box-shadow 0.15s ease", + transitionDelay: "0.15s", + }; + + if (!this.notificationRefreshFlag) { + styles.height = "82px"; + styles.right = "10px"; + } + return styles; + } private notificationBarIframeElementStyles: Partial = { width: "100%", height: "100%", @@ -60,7 +68,6 @@ export class OverlayNotificationsContentService void sendExtensionMessage("checkNotificationQueue"); void sendExtensionMessage("notificationRefreshFlagValue").then((notificationRefreshFlag) => { this.notificationRefreshFlag = !!notificationRefreshFlag; - this.setNotificationRefreshBarHeight(); }); } @@ -233,32 +240,12 @@ export class OverlayNotificationsContentService this.notificationBarElement = globalThis.document.createElement("div"); this.notificationBarElement.id = "bit-notification-bar"; - setElementStyles(this.notificationBarElement, this.notificationBarElementStyles, true); - this.setNotificationRefreshBarHeight(); + setElementStyles(this.notificationBarElement, this.getNotificationBarStyles(), true); this.notificationBarElement.appendChild(this.notificationBarIframeElement); } } - /** - * Sets the height of the notification bar based on the value of `notificationRefreshFlag`. - * If the flag is `true`, the bar is expanded to 400px and aligned right. - * If the flag is `false`, `null`, or `undefined`, it defaults to height of 82px. - * Skips if the notification bar element has not yet been created. - * - */ - private setNotificationRefreshBarHeight() { - const isNotificationV3 = !!this.notificationRefreshFlag; - - if (!this.notificationBarElement) { - return; - } - - if (isNotificationV3) { - setElementStyles(this.notificationBarElement, { height: "400px", right: "0" }, true); - } - } - /** * Sets up the message listener for the initialization of the notification bar. * This will send the initialization data to the notification bar iframe. diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts index e60cc918fb8..dc1913a5336 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts @@ -27,12 +27,11 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { getById } from "@bitwarden/common/platform/misc"; -import { BannerModule, IconModule } from "@bitwarden/components"; +import { BannerModule, IconModule, AdminConsoleLogo } from "@bitwarden/components"; import { FreeFamiliesPolicyService } from "../../../billing/services/free-families-policy.service"; import { OrgSwitcherComponent } from "../../../layouts/org-switcher/org-switcher.component"; import { WebLayoutModule } from "../../../layouts/web-layout.module"; -import { AdminConsoleLogo } from "../../icons/admin-console-logo"; @Component({ selector: "app-organization-layout", diff --git a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html index 7a1ca2cd83d..98fe4032b55 100644 --- a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html +++ b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html @@ -48,7 +48,7 @@ { + return ( + (!trialPaymentOptional && !this.isSecretsManagerFree) || + (trialPaymentOptional && allowTrialLengthZero && this.trialLength === 0) + ); + }), + ); + /** Create an organization unless the trial is for secrets manager */ async conditionallyCreateOrganization(): Promise { if (!this.isSecretsManagerFree) { diff --git a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html index 08195d95bf9..f8ebfa60451 100644 --- a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +++ b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html @@ -16,43 +16,45 @@ > {{ "moreFromBitwarden" | i18n }} - - - -
- {{ more.otherProductOverrides?.name ?? more.name }} -
- {{ more.otherProductOverrides.supportingText }} + - - - - - diff --git a/apps/web/src/app/layouts/user-layout.component.ts b/apps/web/src/app/layouts/user-layout.component.ts index cd07d625281..db4c181cd0f 100644 --- a/apps/web/src/app/layouts/user-layout.component.ts +++ b/apps/web/src/app/layouts/user-layout.component.ts @@ -9,11 +9,10 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { SyncService } from "@bitwarden/common/platform/sync"; -import { IconModule } from "@bitwarden/components"; +import { IconModule, PasswordManagerLogo } from "@bitwarden/components"; import { BillingFreeFamiliesNavItemComponent } from "../billing/shared/billing-free-families-nav-item.component"; -import { PasswordManagerLogo } from "./password-manager-logo"; import { WebLayoutModule } from "./web-layout.module"; @Component({ diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts index 1eef528b639..24da000f213 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts @@ -22,7 +22,7 @@ import { import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { SecretsManagerLogo } from "@bitwarden/web-vault/app/layouts/secrets-manager-logo"; +import { SecretsManagerLogo } from "@bitwarden/components"; import { OrganizationCounts } from "../models/view/counts.view"; import { ProjectService } from "../projects/project.service"; diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index dd2aadc0009..68228b63bea 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -33,6 +33,7 @@ export enum FeatureFlag { PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships", PM19956_RequireProviderPaymentMethodDuringSetup = "pm-19956-require-provider-payment-method-during-setup", UseOrganizationWarningsService = "use-organization-warnings-service", + AllowTrialLengthZero = "pm-20322-allow-trial-length-0", /* Data Insights and Reporting */ EnableRiskInsightsNotifications = "enable-risk-insights-notifications", @@ -114,6 +115,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM17772_AdminInitiatedSponsorships]: FALSE, [FeatureFlag.PM19956_RequireProviderPaymentMethodDuringSetup]: FALSE, [FeatureFlag.UseOrganizationWarningsService]: FALSE, + [FeatureFlag.AllowTrialLengthZero]: FALSE, /* Key Management */ [FeatureFlag.PrivateKeyRegeneration]: FALSE, diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index b4f79b2467e..a1727fd7a1d 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -383,7 +383,7 @@ export class CipherService implements CipherServiceAbstraction { const decCiphers = await this.getDecryptedCiphers(userId); if (decCiphers != null && decCiphers.length !== 0) { await this.reindexCiphers(userId); - return await this.getDecryptedCiphers(userId); + return decCiphers; } const decrypted = await this.decryptCiphers(await this.getAll(userId), userId); diff --git a/libs/components/src/anon-layout/anon-layout.component.ts b/libs/components/src/anon-layout/anon-layout.component.ts index ee3a7ca7bee..45e7f3973a9 100644 --- a/libs/components/src/anon-layout/anon-layout.component.ts +++ b/libs/components/src/anon-layout/anon-layout.component.ts @@ -10,7 +10,8 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { IconModule, Icon } from "../icon"; -import { BitwardenLogo, BitwardenShield } from "../icon/icons"; +import { BitwardenLogo } from "../icon/icons"; +import { BitwardenShield } from "../icon/logos"; import { SharedModule } from "../shared"; import { TypographyModule } from "../typography"; diff --git a/libs/components/src/checkbox/checkbox.component.ts b/libs/components/src/checkbox/checkbox.component.ts index 079ede287cc..c420b3f3473 100644 --- a/libs/components/src/checkbox/checkbox.component.ts +++ b/libs/components/src/checkbox/checkbox.component.ts @@ -20,64 +20,75 @@ export class CheckboxComponent implements BitFormControlAbstraction { "tw-cursor-pointer", "tw-inline-block", "tw-align-sub", - "tw-rounded", - "tw-border", - "tw-border-solid", - "tw-border-secondary-500", - "tw-h-[1.12rem]", - "tw-w-[1.12rem]", - "tw-me-1.5", "tw-flex-none", // Flexbox fix for bit-form-control + "!tw-p-1", + "after:tw-inset-1", + // negative margin to negate the positioning added by the padding + "!-tw-mt-1", + "!-tw-mb-1", + "!-tw-ms-1", "before:tw-content-['']", "before:tw-block", - "before:tw-absolute", "before:tw-inset-0", + "before:tw-h-[1.12rem]", + "before:tw-w-[1.12rem]", + "before:tw-rounded", + "before:tw-border", + "before:tw-border-solid", + "before:tw-border-secondary-500", - "hover:tw-border-2", - "[&>label]:tw-border-2", + "after:tw-content-['']", + "after:tw-block", + "after:tw-absolute", + "after:tw-inset-0", + "after:tw-h-[1.12rem]", + "after:tw-w-[1.12rem]", + + "hover:before:tw-border-2", + "[&>label]:before:tw-border-2", // if it exists, the parent form control handles focus - "[&:not(bit-form-control_*)]:focus-visible:tw-ring-2", - "[&:not(bit-form-control_*)]:focus-visible:tw-ring-offset-2", - "[&:not(bit-form-control_*)]:focus-visible:tw-ring-primary-600", + "[&:not(bit-form-control_*)]:focus-visible:before:tw-ring-2", + "[&:not(bit-form-control_*)]:focus-visible:before:tw-ring-offset-2", + "[&:not(bit-form-control_*)]:focus-visible:before:tw-ring-primary-600", - "disabled:tw-cursor-auto", - "disabled:tw-border", - "disabled:hover:tw-border", - "disabled:tw-bg-secondary-100", - "disabled:hover:tw-bg-secondary-100", + "disabled:before:tw-cursor-auto", + "disabled:before:tw-border", + "disabled:before:hover:tw-border", + "disabled:before:tw-bg-secondary-100", + "disabled:hover:before:tw-bg-secondary-100", - "checked:tw-bg-primary-600", - "checked:tw-border-primary-600", - "checked:hover:tw-bg-primary-700", - "checked:hover:tw-border-primary-700", - "[&>label:hover]:checked:tw-bg-primary-700", - "[&>label:hover]:checked:tw-border-primary-700", - "checked:before:tw-bg-text-contrast", - "checked:before:tw-mask-position-[center]", - "checked:before:tw-mask-repeat-[no-repeat]", - "checked:disabled:tw-border-secondary-100", - "checked:disabled:hover:tw-border-secondary-100", - "checked:disabled:tw-bg-secondary-100", - "checked:disabled:before:tw-bg-text-muted", + "checked:before:tw-bg-primary-600", + "checked:before:tw-border-primary-600", + "checked:before:hover:tw-bg-primary-700", + "checked:before:hover:tw-border-primary-700", + "[&>label:hover]:checked:before:tw-bg-primary-700", + "[&>label:hover]:checked:before:tw-border-primary-700", + "checked:after:tw-bg-text-contrast", + "checked:after:tw-mask-position-[center]", + "checked:after:tw-mask-repeat-[no-repeat]", + "checked:disabled:before:tw-border-secondary-100", + "checked:disabled:hover:before:tw-border-secondary-100", + "checked:disabled:before:tw-bg-secondary-100", + "checked:disabled:after:tw-bg-text-muted", - "[&:not(:indeterminate)]:checked:before:tw-mask-image-[var(--mask-image)]", - "indeterminate:before:tw-mask-image-[var(--indeterminate-mask-image)]", + "[&:not(:indeterminate)]:checked:after:tw-mask-image-[var(--mask-image)]", + "indeterminate:after:tw-mask-image-[var(--indeterminate-mask-image)]", - "indeterminate:tw-bg-primary-600", - "indeterminate:tw-border-primary-600", - "indeterminate:hover:tw-bg-primary-700", - "indeterminate:hover:tw-border-primary-700", - "[&>label:hover]:indeterminate:tw-bg-primary-700", - "[&>label:hover]:indeterminate:tw-border-primary-700", - "indeterminate:before:tw-bg-text-contrast", - "indeterminate:before:tw-mask-position-[center]", - "indeterminate:before:tw-mask-repeat-[no-repeat]", - "indeterminate:before:tw-mask-image-[var(--indeterminate-mask-image)]", + "indeterminate:before:tw-bg-primary-600", + "indeterminate:before:tw-border-primary-600", + "indeterminate:hover:before:tw-bg-primary-700", + "indeterminate:hover:before:tw-border-primary-700", + "[&>label:hover]:indeterminate:before:tw-bg-primary-700", + "[&>label:hover]:indeterminate:before:tw-border-primary-700", + "indeterminate:after:tw-bg-text-contrast", + "indeterminate:after:tw-mask-position-[center]", + "indeterminate:after:tw-mask-repeat-[no-repeat]", + "indeterminate:after:tw-mask-image-[var(--indeterminate-mask-image)]", "indeterminate:disabled:tw-border-secondary-100", "indeterminate:disabled:tw-bg-secondary-100", - "indeterminate:disabled:before:tw-bg-text-muted", + "indeterminate:disabled:after:tw-bg-text-muted", ]; constructor(@Optional() @Self() private ngControl?: NgControl) {} diff --git a/libs/components/src/checkbox/checkbox.stories.ts b/libs/components/src/checkbox/checkbox.stories.ts index 123c6704ff4..9050d97cafc 100644 --- a/libs/components/src/checkbox/checkbox.stories.ts +++ b/libs/components/src/checkbox/checkbox.stories.ts @@ -13,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { BadgeModule } from "../badge"; import { FormControlModule } from "../form-control"; +import { FormFieldModule } from "../form-field"; import { TableModule } from "../table"; import { I18nMockService } from "../utils/i18n-mock.service"; @@ -30,6 +31,7 @@ const template = /*html*/ ` @Component({ selector: "app-example", template, + imports: [CheckboxModule, FormFieldModule, ReactiveFormsModule], }) class ExampleComponent { protected formObj = this.formBuilder.group({ @@ -55,8 +57,8 @@ export default { title: "Component Library/Form/Checkbox", decorators: [ moduleMetadata({ - declarations: [ExampleComponent], imports: [ + ExampleComponent, FormsModule, ReactiveFormsModule, FormControlModule, @@ -195,17 +197,17 @@ export const Custom: Story = { props: args, template: /*html*/ `
-
`, diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 7e2d8c62bb6..c7b8f0ae916 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -25,10 +25,13 @@ interface Animal { template: ` + `, - imports: [ButtonModule], + imports: [ButtonModule, LayoutComponent], }) class StoryDialogComponent { constructor(public dialogService: DialogService) {} @@ -41,6 +44,15 @@ class StoryDialogComponent { }); } + openDialogNonDismissable() { + this.dialogService.open(NonDismissableContent, { + data: { + animal: "panda", + }, + disableClose: true, + }); + } + openDrawer() { this.dialogService.openDrawer(StoryDialogContentComponent, { data: { @@ -79,13 +91,40 @@ class StoryDialogContentComponent { } } +@Component({ + template: ` + + + Dialog body text goes here. +
+ Animal: {{ animal }} +
+ + + +
+ `, + imports: [DialogModule, ButtonModule], +}) +class NonDismissableContent { + constructor( + public dialogRef: DialogRef, + @Inject(DIALOG_DATA) private data: Animal, + ) {} + + get animal() { + return this.data?.animal; + } +} + export default { title: "Component Library/Dialogs/Service", component: StoryDialogComponent, decorators: [ positionFixedWrapperDecorator(), moduleMetadata({ - declarations: [StoryDialogContentComponent], imports: [ SharedModule, ButtonModule, @@ -138,8 +177,7 @@ export const Default: Story = { }, }; -/** Drawers must be a descendant of `bit-layout`. */ -export const Drawer: Story = { +export const NonDismissable: Story = { play: async (context) => { const canvas = context.canvasElement; @@ -147,3 +185,13 @@ export const Drawer: Story = { await userEvent.click(button); }, }; + +/** Drawers must be a descendant of `bit-layout`. */ +export const Drawer: Story = { + play: async (context) => { + const canvas = context.canvasElement; + + const button = getAllByRole(canvas, "button")[2]; + await userEvent.click(button); + }, +}; diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index eaf7fc2beec..db08f88799b 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -30,15 +30,17 @@ } - + @if (!this.dialogRef?.disableClose) { + + }
Open Simple Dialog`, + template: ` + + + + `, imports: [ButtonModule], }) class StoryDialogComponent { constructor(public dialogService: DialogService) {} - openDialog() { - this.dialogService.open(StoryDialogContentComponent, { + openSimpleDialog() { + this.dialogService.open(SimpleDialogContent, { data: { animal: "panda", }, }); } + + openNonDismissableWithPrimaryButtonDialog() { + this.dialogService.open(NonDismissableWithPrimaryButtonContent, { + data: { + animal: "panda", + }, + disableClose: true, + }); + } + + openNonDismissableWithNoButtonsDialog() { + this.dialogService.open(NonDismissableWithNoButtonsContent, { + data: { + animal: "panda", + }, + disableClose: true, + }); + } } @Component({ @@ -49,7 +76,60 @@ class StoryDialogComponent { `, imports: [ButtonModule, DialogModule], }) -class StoryDialogContentComponent { +class SimpleDialogContent { + constructor( + public dialogRef: DialogRef, + @Inject(DIALOG_DATA) private data: Animal, + ) {} + + get animal() { + return this.data?.animal; + } +} + +@Component({ + template: ` + + Dialog Title + + Dialog body text goes here. +
+ Animal: {{ animal }} +
+ + + +
+ `, + imports: [ButtonModule, DialogModule], +}) +class NonDismissableWithPrimaryButtonContent { + constructor( + public dialogRef: DialogRef, + @Inject(DIALOG_DATA) private data: Animal, + ) {} + + get animal() { + return this.data?.animal; + } +} + +@Component({ + template: ` + + Dialog Title + + Dialog body text goes here. +
+ Animal: {{ animal }} +
+
+ `, + imports: [ButtonModule, DialogModule], +}) +class NonDismissableWithNoButtonsContent { constructor( public dialogRef: DialogRef, @Inject(DIALOG_DATA) private data: Animal, @@ -89,4 +169,29 @@ export default { type Story = StoryObj; -export const Default: Story = {}; +export const Default: Story = { + play: async (context) => { + const canvas = context.canvasElement; + + const button = getAllByRole(canvas, "button")[0]; + await userEvent.click(button); + }, +}; + +export const NonDismissableWithPrimaryButton: Story = { + play: async (context) => { + const canvas = context.canvasElement; + + const button = getAllByRole(canvas, "button")[1]; + await userEvent.click(button); + }, +}; + +export const NonDismissableWithNoButtons: Story = { + play: async (context) => { + const canvas = context.canvasElement; + + const button = getAllByRole(canvas, "button")[2]; + await userEvent.click(button); + }, +}; diff --git a/libs/components/src/form-control/form-control.component.html b/libs/components/src/form-control/form-control.component.html index 735e375a29a..15d422b01a1 100644 --- a/libs/components/src/form-control/form-control.component.html +++ b/libs/components/src/form-control/form-control.component.html @@ -1,5 +1,5 @@