diff --git a/apps/browser/src/platform/popup/popup-section-header/popup-section-header.component.html b/apps/browser/src/platform/popup/popup-section-header/popup-section-header.component.html deleted file mode 100644 index 4fdbb82312..0000000000 --- a/apps/browser/src/platform/popup/popup-section-header/popup-section-header.component.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
-

- {{ title }} -

- -
-
- -
-
diff --git a/apps/browser/src/platform/popup/popup-section-header/popup-section-header.component.ts b/apps/browser/src/platform/popup/popup-section-header/popup-section-header.component.ts deleted file mode 100644 index b33a2a0f33..0000000000 --- a/apps/browser/src/platform/popup/popup-section-header/popup-section-header.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component, Input } from "@angular/core"; - -import { TypographyModule } from "@bitwarden/components"; - -@Component({ - standalone: true, - selector: "popup-section-header", - templateUrl: "./popup-section-header.component.html", - imports: [TypographyModule], -}) -export class PopupSectionHeaderComponent { - @Input() title: string; -} diff --git a/apps/browser/src/platform/popup/popup-section-header/popup-section-header.stories.ts b/apps/browser/src/platform/popup/popup-section-header/popup-section-header.stories.ts deleted file mode 100644 index f5cb472a59..0000000000 --- a/apps/browser/src/platform/popup/popup-section-header/popup-section-header.stories.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; - -import { - CardComponent, - IconButtonModule, - SectionComponent, - TypographyModule, -} from "@bitwarden/components"; - -import { PopupSectionHeaderComponent } from "./popup-section-header.component"; - -export default { - title: "Browser/Popup Section Header", - component: PopupSectionHeaderComponent, - args: { - title: "Title", - }, - decorators: [ - moduleMetadata({ - imports: [SectionComponent, CardComponent, TypographyModule, IconButtonModule], - }), - ], -} as Meta; - -type Story = StoryObj; - -export const OnlyTitle: Story = { - render: (args) => ({ - props: args, - template: ` - - `, - }), - args: { - title: "Only Title", - }, -}; - -export const TrailingText: Story = { - render: (args) => ({ - props: args, - template: ` - - 13 - - `, - }), - args: { - title: "Trailing Text", - }, -}; - -export const TailingIcon: Story = { - render: (args) => ({ - props: args, - template: ` - - - - `, - }), - args: { - title: "Trailing Icon", - }, -}; - -export const TitleSuffix: Story = { - render: (args) => ({ - props: args, - template: ` - - - - `, - }), - args: { - title: "Title Suffix", - }, -}; - -export const WithSections: Story = { - render: () => ({ - template: ` -
- - - - - -

Card 1 Content

-
-
- - - - - -

Card 2 Content

-
-
-
- `, - }), -}; diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 86f4d9fa76..3c7f45e55f 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -46,7 +46,6 @@ import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.comp import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../platform/popup/layout/popup-page.component"; import { PopupTabNavigationComponent } from "../platform/popup/layout/popup-tab-navigation.component"; -import { PopupSectionHeaderComponent } from "../platform/popup/popup-section-header/popup-section-header.component"; import { FilePopoutCalloutComponent } from "../tools/popup/components/file-popout-callout.component"; import { GeneratorComponent } from "../tools/popup/generator/generator.component"; import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; @@ -119,7 +118,6 @@ import "../platform/popup/locales"; PopupFooterComponent, PopupHeaderComponent, UserVerificationDialogComponent, - PopupSectionHeaderComponent, CurrentAccountComponent, ], declarations: [ diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html index 83b07fc14c..e52018ab27 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html @@ -8,17 +8,19 @@ > - + +

+ {{ "autofillSuggestions" | i18n }} +

-
+ {{ "autofillSuggestionsTip" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts index 9a4670bb4c..1b9876759f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts @@ -3,10 +3,14 @@ import { Component } from "@angular/core"; import { combineLatest, map, Observable } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { IconButtonModule, SectionComponent, TypographyModule } from "@bitwarden/components"; +import { + IconButtonModule, + SectionComponent, + SectionHeaderComponent, + TypographyModule, +} from "@bitwarden/components"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; -import { PopupSectionHeaderComponent } from "../../../../../platform/popup/popup-section-header/popup-section-header.component"; import { VaultPopupItemsService } from "../../../services/vault-popup-items.service"; import { PopupCipherView } from "../../../views/popup-cipher.view"; import { VaultListItemsContainerComponent } from "../vault-list-items-container/vault-list-items-container.component"; @@ -19,7 +23,7 @@ import { VaultListItemsContainerComponent } from "../vault-list-items-container/ TypographyModule, VaultListItemsContainerComponent, JslibModule, - PopupSectionHeaderComponent, + SectionHeaderComponent, IconButtonModule, ], selector: "app-autofill-vault-list-items", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html index 74ee1af1ff..c2c345fd75 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html @@ -1,16 +1,18 @@ - - {{ ciphers.length }} + +

+ {{ title }} +

-
+ {{ ciphers.length }} + +
+ +
+
+ +
+ diff --git a/libs/components/src/section/section-header.component.ts b/libs/components/src/section/section-header.component.ts new file mode 100644 index 0000000000..9f7b1a21f1 --- /dev/null +++ b/libs/components/src/section/section-header.component.ts @@ -0,0 +1,16 @@ +import { Component } from "@angular/core"; + +import { TypographyModule } from "../typography"; + +@Component({ + standalone: true, + selector: "bit-section-header", + templateUrl: "./section-header.component.html", + imports: [TypographyModule], + host: { + class: + // apply bottom and x padding when a `bit-card` or `bit-item` is the immediate sibling, or nested in the immediate sibling + "tw-block has-[+_*_bit-card]:tw-pb-1 has-[+_bit-card]:tw-pb-1 has-[+_*_bit-item]:tw-pb-1 has-[+_bit-item]:tw-pb-1 has-[+_*_bit-card]:tw-px-1 has-[+_bit-card]:tw-px-1 has-[+_*_bit-item]:tw-px-1 has-[+_bit-item]:tw-px-1", + }, +}) +export class SectionHeaderComponent {} diff --git a/libs/components/src/section/section.mdx b/libs/components/src/section/section.mdx new file mode 100644 index 0000000000..33e640e4f0 --- /dev/null +++ b/libs/components/src/section/section.mdx @@ -0,0 +1,89 @@ +import { Meta, Story, Primary, Controls, Canvas } from "@storybook/addon-docs"; + +import * as stories from "./section.stories"; + + + +```ts +import { SectionComponent, SectionHeaderComponent } from "@bitwarden/components"; +``` + +# Section + +Sections are simple containers that apply a responsive bottom margin and utilize the semantic +`section` HTML element. + + + + + +## Section Header + +Sections often contain a heading. Use `bit-section-header` inside of the `bit-section`. + +```html + + +

I'm a section header

+
+
Section content here!
+
+``` + +### Section Header Padding + +When placed inside of a section with a `bit-card` or `bit-item` as the immediate next sibling (or +nested in the immediate next sibling), the section header will automatically apply bottom and x-axis +padding to align the header with the border radius of the card/item. + +```html + + +

I'm a section header

+ +
+ +

I'm card content

+
+
+``` + + + + + +If placed inside of a section without a `bit-card` or `bit-item`, or with a `bit-card`/`bit-item` +that is not a descendant of the immediate next sibling, the padding is not applied. + + + + + +### Section Header Content Slots + +`bit-section-header` contains the following slots to help position the content: + +| Slot | Description | +| ------------ | ------------------------------- | +| default | title text of the header | +| `slot="end"` | placed at the end of the header | + +#### Default slot + +Anything passed to the default slot will display as part of the title. The title should be a +`bitTypography` element, usually an `h2` styled as an `h6`. + +Title suffixes (typically an icon or icon button) can be added as well. A gap is automatically +applied between the children of the default slot. + + + + + +#### End slot + +The `end` slot will typically be used for text or an icon button. + + + + diff --git a/libs/components/src/section/section.stories.ts b/libs/components/src/section/section.stories.ts index 65b6a67d47..0f720d1dba 100644 --- a/libs/components/src/section/section.stories.ts +++ b/libs/components/src/section/section.stories.ts @@ -1,15 +1,24 @@ import { Meta, StoryObj, componentWrapperDecorator, moduleMetadata } from "@storybook/angular"; +import { CardComponent } from "../card"; +import { IconButtonModule } from "../icon-button"; +import { ItemModule } from "../item"; import { TypographyModule } from "../typography"; -import { SectionComponent } from "./section.component"; +import { SectionComponent, SectionHeaderComponent } from "./"; export default { title: "Component Library/Section", component: SectionComponent, decorators: [ moduleMetadata({ - imports: [TypographyModule], + imports: [ + TypographyModule, + SectionHeaderComponent, + CardComponent, + IconButtonModule, + ItemModule, + ], }), componentWrapperDecorator((story) => `
${story}
`), ], @@ -17,19 +26,149 @@ export default { type Story = StoryObj; -/** Sections are simple containers that apply a responsive bottom margin. They often contain a heading. */ export const Default: Story = { - render: (args) => ({ - props: args, - template: ` + render: () => ({ + template: /*html*/ ` +

Foo

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex.

+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex.

+

Bar

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex.

+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex.

`, }), }; + +export const HeaderVariants: Story = { + render: () => ({ + template: /*html*/ ` + +

+ Title only +

+
+ +

+ Title with icon button suffix +

+ +
+ `, + }), +}; + +export const HeaderEndSlotVariants: Story = { + render: () => ({ + template: /*html*/ ` + +

+ Title with end slot text +

+ 13 +
+ +

+ Title with end slot icon button +

+ +
+ `, + }), +}; + +export const HeaderWithPadding: Story = { + render: () => ({ + template: /*html*/ ` +
+ + +

+ Card as immediate sibling +

+ +
+ +

bit-section-header has padding

+
+
+ + +

+ Card nested in immediate sibling +

+ +
+
+ +

bit-section-header has padding

+
+
+
+ + +

+ Item as immediate sibling +

+ +
+ + bit-section-header has padding + +
+ + +

+ Item nested in immediate sibling +

+ +
+ + + bit-section-header has padding + + +
+
+ `, + }), +}; + +export const HeaderWithoutPadding: Story = { + render: () => ({ + template: /*html*/ ` +
+ + +

+ No card or item used +

+ +
+
+

just a div, so bit-section-header has no padding

+
+
+ + +

+ Card nested in non-immediate sibling +

+ +
+
+ a div here +
+ +

bit-section-header has no padding

+
+
+
+ `, + }), +};