From 898d514d5b5a0c93910ee28df71f6335f6d6363b Mon Sep 17 00:00:00 2001 From: Will Martin Date: Tue, 25 Nov 2025 13:13:07 -0500 Subject: [PATCH] [CL-854] feat: add bit-header component to component library (#17662) Add new bit-header component to libs/components with: - Header component with left, center, and right content projection - Storybook stories for documentation - Export from component library index --- .../src/header/header.component.html | 35 ++++ .../components/src/header/header.component.ts | 19 ++ libs/components/src/header/header.stories.ts | 189 ++++++++++++++++++ libs/components/src/header/index.ts | 1 + libs/components/src/index.ts | 1 + 5 files changed, 245 insertions(+) create mode 100644 libs/components/src/header/header.component.html create mode 100644 libs/components/src/header/header.component.ts create mode 100644 libs/components/src/header/header.stories.ts create mode 100644 libs/components/src/header/index.ts diff --git a/libs/components/src/header/header.component.html b/libs/components/src/header/header.component.html new file mode 100644 index 0000000000..992dcbea4a --- /dev/null +++ b/libs/components/src/header/header.component.html @@ -0,0 +1,35 @@ +
+
+
+ +

+
+ @if (icon()) { + + } + + {{ title() }} +
+
+

+
+
+
+ +
+
+ +
+
+
+
+ +
+
diff --git a/libs/components/src/header/header.component.ts b/libs/components/src/header/header.component.ts new file mode 100644 index 0000000000..08cd91ea20 --- /dev/null +++ b/libs/components/src/header/header.component.ts @@ -0,0 +1,19 @@ +import { ChangeDetectionStrategy, Component, input } from "@angular/core"; + +@Component({ + selector: "bit-header", + templateUrl: "./header.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, +}) +export class HeaderComponent { + /** + * The title of the page + */ + readonly title = input.required(); + + /** + * Icon to show before the title + */ + readonly icon = input(); +} diff --git a/libs/components/src/header/header.stories.ts b/libs/components/src/header/header.stories.ts new file mode 100644 index 0000000000..620f39a5dc --- /dev/null +++ b/libs/components/src/header/header.stories.ts @@ -0,0 +1,189 @@ +import { importProvidersFrom } from "@angular/core"; +import { RouterModule } from "@angular/router"; +import { + applicationConfig, + componentWrapperDecorator, + Meta, + moduleMetadata, + StoryObj, +} from "@storybook/angular"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { + AvatarModule, + BreadcrumbsModule, + ButtonModule, + IconButtonModule, + IconModule, + InputModule, + MenuModule, + NavigationModule, + TabsModule, + TypographyModule, +} from "@bitwarden/components"; + +import { I18nMockService } from "../utils"; + +import { HeaderComponent } from "./header.component"; + +export default { + title: "Component Library/Header", + component: HeaderComponent, + decorators: [ + componentWrapperDecorator( + (story) => `
${story}
`, + ), + moduleMetadata({ + imports: [ + HeaderComponent, + AvatarModule, + BreadcrumbsModule, + ButtonModule, + IconButtonModule, + IconModule, + InputModule, + MenuModule, + NavigationModule, + TabsModule, + TypographyModule, + ], + }), + applicationConfig({ + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + moreBreadcrumbs: "More breadcrumbs", + loading: "Loading", + }); + }, + }, + importProvidersFrom( + RouterModule.forRoot( + [ + { path: "", redirectTo: "foo", pathMatch: "full" }, + { path: "foo", component: HeaderComponent }, + { path: "bar", component: HeaderComponent }, + ], + { useHash: true }, + ), + ), + ], + }), + ], +} as Meta; + +type Story = StoryObj; + +export const KitchenSink: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + + Foo + Bar + + + + + + + + Foo + Bar + + + `, + }), +}; + +export const Basic: Story = { + render: (args: any) => ({ + props: args, + template: /*html*/ ` + + `, + }), +}; + +export const WithLongTitle: Story = { + render: (arg: any) => ({ + props: arg, + template: /*html*/ ` + + + + `, + }), +}; + +export const WithBreadcrumbs: Story = { + render: (args: any) => ({ + props: args, + template: /*html*/ ` + + + Foo + Bar + + + `, + }), +}; + +export const WithSearch: Story = { + render: (args: any) => ({ + props: args, + template: /*html*/ ` + + + + `, + }), +}; + +export const WithSecondaryContent: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + + + `, + }), +}; + +export const WithTabs: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + + Foo + Bar + + + `, + }), +}; + +export const WithTitleSuffixComponent: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + + + `, + }), +}; diff --git a/libs/components/src/header/index.ts b/libs/components/src/header/index.ts new file mode 100644 index 0000000000..c2d38d375e --- /dev/null +++ b/libs/components/src/header/index.ts @@ -0,0 +1 @@ +export * from "./header.component"; diff --git a/libs/components/src/index.ts b/libs/components/src/index.ts index 5346747d3b..410a96f1cb 100644 --- a/libs/components/src/index.ts +++ b/libs/components/src/index.ts @@ -19,6 +19,7 @@ export * from "./dialog"; export * from "./disclosure"; export * from "./drawer"; export * from "./form-field"; +export * from "./header"; export * from "./icon-button"; export * from "./icon"; export * from "./icon-tile";