From b3e4ecc568abaf2196ffc60749603acf9bfca4e7 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 6 Mar 2024 09:32:50 -0800 Subject: [PATCH] [PM-3388] AnonLayout Component (#6424) * setup anon-layout component * add story with content * simplify stories and add title input * add responsiveness * adjust border styling * add logo * add logo * mock PlatformUtilsService * more responsivness * add secondary content * add stories and clarifying comments * add more to responsiveness * Update libs/components/tailwind.config.js Co-authored-by: Oscar Hinton * Update libs/components/tailwind.config.base.js Co-authored-by: Oscar Hinton * Update libs/auth/src/components/anon-layout.stories.ts Co-authored-by: Oscar Hinton * refactor: use bit-icon instead of css file, add auth- prefix, adjust tailwind settings * account for longer content * allow for adding an icon above logo * simplify stories by removing unnecessary styling * delete duplicate logo and minify logo and icon * remove componentWrapperDecorator * change subTitle to subtitle * use bitTypography * add accessibility title and use tw class for fill color * add element to SVG * typography update and minor styling updates for stories * match breakpoint for logo and h1 * reduce spacing between sections * move to new folder * add closing tag * make fields protected * use svg directly * refactor icons * revert to allow for additional icons in the future * decouple icon from component --------- Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com> --- .../anon-layout/anon-layout.component.html | 26 +++++ .../anon-layout/anon-layout.component.ts | 31 +++++ .../anon-layout/anon-layout.stories.ts | 107 ++++++++++++++++++ libs/auth/src/icons/bitwarden-logo.ts | 9 ++ libs/auth/src/icons/icon-lock.ts | 7 ++ libs/components/tailwind.config.base.js | 6 +- 6 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 libs/auth/src/angular/anon-layout/anon-layout.component.html create mode 100644 libs/auth/src/angular/anon-layout/anon-layout.component.ts create mode 100644 libs/auth/src/angular/anon-layout/anon-layout.stories.ts create mode 100644 libs/auth/src/icons/bitwarden-logo.ts create mode 100644 libs/auth/src/icons/icon-lock.ts diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html new file mode 100644 index 0000000000..1f583edf20 --- /dev/null +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html @@ -0,0 +1,26 @@ +<main + class="tw-flex tw-min-h-screen tw-max-w-xl tw-w-full tw-mx-auto tw-flex-col tw-gap-9 tw-px-4 tw-pb-4 tw-pt-14 tw-text-main" +> + <div class="tw-text-center"> + <div class="tw-px-8"> + <div *ngIf="icon" class="tw-mb-8"> + <bit-icon [icon]="icon"></bit-icon> + </div> + <bit-icon [icon]="logo" class="tw-mx-auto tw-block tw-max-w-72 sm:tw-max-w-xs"></bit-icon> + </div> + <h1 *ngIf="title" bitTypography="h3" class="tw-mt-8 sm:tw-text-2xl"> + {{ title }} + </h1> + <p *ngIf="subtitle" bitTypography="body1">{{ subtitle }}</p> + </div> + <div class="tw-mb-auto tw-mx-auto tw-max-w-md tw-grid tw-gap-9"> + <div class="tw-rounded-xl sm:tw-border sm:tw-border-solid sm:tw-border-secondary-300 sm:tw-p-8"> + <ng-content></ng-content> + </div> + <ng-content select="[slot=secondary]"></ng-content> + </div> + <footer class="tw-text-center"> + <div>© {{ year }} Bitwarden Inc.</div> + <div>{{ version }}</div> + </footer> +</main> diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.ts b/libs/auth/src/angular/anon-layout/anon-layout.component.ts new file mode 100644 index 0000000000..d247a010bf --- /dev/null +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.ts @@ -0,0 +1,31 @@ +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; + +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { IconModule, Icon } from "../../../../components/src/icon"; +import { TypographyModule } from "../../../../components/src/typography"; +import { BitwardenLogo } from "../../icons/bitwarden-logo"; + +@Component({ + standalone: true, + selector: "auth-anon-layout", + templateUrl: "./anon-layout.component.html", + imports: [IconModule, CommonModule, TypographyModule], +}) +export class AnonLayoutComponent { + @Input() title: string; + @Input() subtitle: string; + @Input() icon: Icon; + + protected logo = BitwardenLogo; + protected version: string; + protected year = "2024"; + + constructor(private platformUtilsService: PlatformUtilsService) {} + + async ngOnInit() { + this.year = new Date().getFullYear().toString(); + this.version = await this.platformUtilsService.getApplicationVersion(); + } +} diff --git a/libs/auth/src/angular/anon-layout/anon-layout.stories.ts b/libs/auth/src/angular/anon-layout/anon-layout.stories.ts new file mode 100644 index 0000000000..daba5b5e53 --- /dev/null +++ b/libs/auth/src/angular/anon-layout/anon-layout.stories.ts @@ -0,0 +1,107 @@ +import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; + +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { ButtonModule } from "../../../../components/src/button"; +import { IconLock } from "../../icons/icon-lock"; + +import { AnonLayoutComponent } from "./anon-layout.component"; + +class MockPlatformUtilsService implements Partial<PlatformUtilsService> { + getApplicationVersion = () => Promise.resolve("Version 2023.1.1"); +} + +export default { + title: "Auth/Anon Layout", + component: AnonLayoutComponent, + decorators: [ + moduleMetadata({ + imports: [ButtonModule], + providers: [ + { + provide: PlatformUtilsService, + useClass: MockPlatformUtilsService, + }, + ], + }), + ], + args: { + title: "The Page Title", + subtitle: "The subtitle (optional)", + icon: IconLock, + }, +} as Meta; + +type Story = StoryObj<AnonLayoutComponent>; + +export const WithPrimaryContent: Story = { + render: (args) => ({ + props: args, + template: + /** + * The projected content (i.e. the <div> ) and styling below is just a + * sample and could be replaced with any content and styling + */ + ` + <auth-anon-layout [title]="title" [subtitle]="subtitle"> + <div class="tw-font-bold">Primary Projected Content Area (customizable)</div> + <div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?</div> + </auth-anon-layout> + `, + }), +}; + +export const WithSecondaryContent: Story = { + render: (args) => ({ + props: args, + template: + // Notice that slot="secondary" is requred to project any secondary content: + ` + <auth-anon-layout [title]="title" [subtitle]="subtitle"> + <div> + <div class="tw-font-bold">Primary Projected Content Area (customizable)</div> + <div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?</div> + </div> + + <div slot="secondary" class="text-center"> + <div class="tw-font-bold tw-mb-2">Secondary Projected Content (optional)</div> + <button bitButton>Perform Action</button> + </div> + </auth-anon-layout> + `, + }), +}; + +export const WithLongContent: Story = { + render: (args) => ({ + props: args, + template: ` + <auth-anon-layout title="Page Title lorem ipsum dolor consectetur sit amet expedita quod est" subtitle="Subtitle here Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita, quod est?"> + <div> + <div class="tw-font-bold">Primary Projected Content Area (customizable)</div> + <div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam? Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit.</div> + </div> + + <div slot="secondary" class="text-center"> + <div class="tw-font-bold tw-mb-2">Secondary Projected Content (optional)</div> + <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias laborum nostrum natus. Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias laborum nostrum natus. Expedita, quod est? </p> + <button bitButton>Perform Action</button> + </div> + </auth-anon-layout> + `, + }), +}; + +export const WithIcon: Story = { + render: (args) => ({ + props: args, + template: ` + <auth-anon-layout [title]="title" [subtitle]="subtitle" [icon]="icon"> + <div> + <div class="tw-font-bold">Primary Projected Content Area (customizable)</div> + <div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?</div> + </div> + </auth-anon-layout> + `, + }), +}; diff --git a/libs/auth/src/icons/bitwarden-logo.ts b/libs/auth/src/icons/bitwarden-logo.ts new file mode 100644 index 0000000000..90591e0fe7 --- /dev/null +++ b/libs/auth/src/icons/bitwarden-logo.ts @@ -0,0 +1,9 @@ +import { svgIcon } from "@bitwarden/components"; + +export const BitwardenLogo = svgIcon` + <svg viewBox="0 0 290 45" fill="none" xmlns="http://www.w3.org/2000/svg"> + <title>Bitwarden + + + +`; diff --git a/libs/auth/src/icons/icon-lock.ts b/libs/auth/src/icons/icon-lock.ts new file mode 100644 index 0000000000..b56c1ea36d --- /dev/null +++ b/libs/auth/src/icons/icon-lock.ts @@ -0,0 +1,7 @@ +import { svgIcon } from "@bitwarden/components"; + +export const IconLock = svgIcon` + + + +`; diff --git a/libs/components/tailwind.config.base.js b/libs/components/tailwind.config.base.js index 9d22588fb4..5f49c6fc26 100644 --- a/libs/components/tailwind.config.base.js +++ b/libs/components/tailwind.config.base.js @@ -8,7 +8,11 @@ function rgba(color) { module.exports = { prefix: "tw-", - content: ["./src/**/*.{html,ts}", "../../libs/components/src/**/*.{html,ts}"], + content: [ + "./src/**/*.{html,ts}", + "../../libs/components/src/**/*.{html,ts}", + "../../libs/auth/src/**/*.{html,ts}", + ], safelist: [], corePlugins: { preflight: false }, theme: {