From 052f60a8d17815420d4f24884efd170514912b59 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Wed, 28 May 2025 15:29:25 -0700 Subject: [PATCH 1/3] add popover component --- frontend/src/components/ui/index.ts | 1 + frontend/src/components/ui/popover.ts | 61 +++++++++++++++++++ .../src/stories/components/Popover.stories.ts | 58 ++++++++++++++++++ frontend/src/stories/components/Popover.ts | 26 ++++++++ frontend/src/theme.stylesheet.css | 1 + 5 files changed, 147 insertions(+) create mode 100644 frontend/src/components/ui/popover.ts create mode 100644 frontend/src/stories/components/Popover.stories.ts create mode 100644 frontend/src/stories/components/Popover.ts diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts index 45160780be..d053ee38db 100644 --- a/frontend/src/components/ui/index.ts +++ b/frontend/src/components/ui/index.ts @@ -30,6 +30,7 @@ import("./numbered-list"); import("./overflow-dropdown"); import("./overflow-scroll"); import("./pagination"); +import("./popover"); import("./pw-strength-alert"); import("./relative-duration"); import("./search-combobox"); diff --git a/frontend/src/components/ui/popover.ts b/frontend/src/components/ui/popover.ts new file mode 100644 index 0000000000..ec61a74f63 --- /dev/null +++ b/frontend/src/components/ui/popover.ts @@ -0,0 +1,61 @@ +import { localized } from "@lit/localize"; +import SlTooltip from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.component.js"; +import slTooltipStyles from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.styles.js"; +import { css } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +/** + * @attr {String} content + * @attr {String} placement + * @attr {String} trigger + * @attr {Boolean} open + */ +@customElement("btrix-popover") +@localized() +export class Popover extends SlTooltip { + @property({ type: Boolean, reflect: true }) + hoist = true; + + @property({ type: String, reflect: true }) + placement: SlTooltip["placement"] = "bottom"; + + static styles = [ + slTooltipStyles, + css` + :host { + --btrix-border: 1px solid var(--sl-panel-border-color); + --sl-tooltip-background-color: var(--sl-color-neutral-50); + --sl-tooltip-color: var(--sl-color-neutral-700); + } + + ::part(body) { + border: var(--btrix-border); + box-shadow: var(--sl-shadow-small), var(--sl-shadow-medium); + } + + ::part(arrow) { + z-index: 1; + } + + [placement="bottom"]::part(arrow), + [placement="left"]::part(arrow) { + border-top: var(--btrix-border); + } + + [placement="bottom"]::part(arrow), + [placement="right"]::part(arrow) { + border-left: var(--btrix-border); + } + + [placement="top"]::part(arrow), + [placement="right"]::part(arrow) { + border-bottom: var(--btrix-border); + } + + [placement="top"]::part(arrow), + [placement="left"]::part(arrow) { + border-right: var(--btrix-border); + } + `, + ]; +} diff --git a/frontend/src/stories/components/Popover.stories.ts b/frontend/src/stories/components/Popover.stories.ts new file mode 100644 index 0000000000..b0daf1a6f0 --- /dev/null +++ b/frontend/src/stories/components/Popover.stories.ts @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +import { renderComponent, type RenderProps } from "./Popover"; + +const meta = { + title: "Components/Popover", + component: "btrix-popover", + tags: ["autodocs"], + decorators: (story) => + html`
${story()}
`, + render: renderComponent, + argTypes: { + anchor: { table: { disable: true } }, + }, + args: { + content: "Popover content", + anchor: html`Hover me`, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + args: {}, +}; + +export const Open: Story = { + args: { + open: true, + anchor: html`Always open`, + }, +}; + +export const TopPlacement: Story = { + args: { + open: true, + placement: "top", + anchor: html`Popover displays below`, + }, +}; + +export const LeftPlacement: Story = { + args: { + open: true, + placement: "left", + anchor: html`Popover displays left`, + }, +}; + +export const RightPlacement: Story = { + args: { + open: true, + placement: "right", + anchor: html`Popover displays right`, + }, +}; diff --git a/frontend/src/stories/components/Popover.ts b/frontend/src/stories/components/Popover.ts new file mode 100644 index 0000000000..2504baeb05 --- /dev/null +++ b/frontend/src/stories/components/Popover.ts @@ -0,0 +1,26 @@ +import { html, type TemplateResult } from "lit"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import type { Popover } from "@/components/ui/popover"; + +import "@/components/ui/popover"; + +export type RenderProps = Popover & { anchor: TemplateResult }; + +export const renderComponent = ({ + content, + placement, + open, + anchor: triggerContent, +}: Partial) => { + return html` + + ${triggerContent} + + `; +}; diff --git a/frontend/src/theme.stylesheet.css b/frontend/src/theme.stylesheet.css index 4a2321dd73..8684c0afce 100644 --- a/frontend/src/theme.stylesheet.css +++ b/frontend/src/theme.stylesheet.css @@ -258,6 +258,7 @@ } /* Style tooltip with white background */ + /* TODO Replace instances with `` */ sl-tooltip.invert-tooltip { --sl-tooltip-arrow-size: 0; --sl-tooltip-background-color: var(--sl-color-neutral-50); From f3a47b2fc7caae376e9022c3d40c5538b76f9fcd Mon Sep 17 00:00:00 2001 From: sua yoo Date: Wed, 28 May 2025 15:43:15 -0700 Subject: [PATCH 2/3] adjust styles --- frontend/src/components/ui/popover.ts | 23 +++++++++++-------- .../src/stories/components/Popover.stories.ts | 16 +++++++++++++ frontend/src/stories/components/Popover.ts | 15 ++++++++---- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/ui/popover.ts b/frontend/src/components/ui/popover.ts index ec61a74f63..dcd7d15c10 100644 --- a/frontend/src/components/ui/popover.ts +++ b/frontend/src/components/ui/popover.ts @@ -24,36 +24,39 @@ export class Popover extends SlTooltip { css` :host { --btrix-border: 1px solid var(--sl-panel-border-color); - --sl-tooltip-background-color: var(--sl-color-neutral-50); + --sl-tooltip-background-color: var(--sl-color-neutral-0); --sl-tooltip-color: var(--sl-color-neutral-700); + --sl-tooltip-font-size: var(--sl-font-size-x-small); + --sl-tooltip-padding: var(--sl-spacing-small); + --sl-tooltip-line-height: var(--sl-line-height-dense); } ::part(body) { border: var(--btrix-border); - box-shadow: var(--sl-shadow-small), var(--sl-shadow-medium); + box-shadow: var(--sl-shadow-medium); } ::part(arrow) { z-index: 1; } - [placement="bottom"]::part(arrow), - [placement="left"]::part(arrow) { + [placement^="bottom"]::part(arrow), + [placement^="left"]::part(arrow) { border-top: var(--btrix-border); } - [placement="bottom"]::part(arrow), - [placement="right"]::part(arrow) { + [placement^="bottom"]::part(arrow), + [placement^="right"]::part(arrow) { border-left: var(--btrix-border); } - [placement="top"]::part(arrow), - [placement="right"]::part(arrow) { + [placement^="top"]::part(arrow), + [placement^="right"]::part(arrow) { border-bottom: var(--btrix-border); } - [placement="top"]::part(arrow), - [placement="left"]::part(arrow) { + [placement^="top"]::part(arrow), + [placement^="left"]::part(arrow) { border-right: var(--btrix-border); } `, diff --git a/frontend/src/stories/components/Popover.stories.ts b/frontend/src/stories/components/Popover.stories.ts index b0daf1a6f0..b3a61b7ac6 100644 --- a/frontend/src/stories/components/Popover.stories.ts +++ b/frontend/src/stories/components/Popover.stories.ts @@ -12,6 +12,7 @@ const meta = { render: renderComponent, argTypes: { anchor: { table: { disable: true } }, + slottedContent: { table: { disable: true } }, }, args: { content: "Popover content", @@ -56,3 +57,18 @@ export const RightPlacement: Story = { anchor: html`Popover displays right`, }, }; + +export const HTMLContent: Story = { + args: { + open: true, + anchor: html`HTML Content`, + slottedContent: html` +
Popover Title
+
+

+ This popover has HTML content for displaying informative text or + additional details when the anchor is activated. +

+ `, + }, +}; diff --git a/frontend/src/stories/components/Popover.ts b/frontend/src/stories/components/Popover.ts index 2504baeb05..12cf427d79 100644 --- a/frontend/src/stories/components/Popover.ts +++ b/frontend/src/stories/components/Popover.ts @@ -1,17 +1,21 @@ -import { html, type TemplateResult } from "lit"; +import { html, nothing, type TemplateResult } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; import type { Popover } from "@/components/ui/popover"; import "@/components/ui/popover"; -export type RenderProps = Popover & { anchor: TemplateResult }; +export type RenderProps = Popover & { + anchor: TemplateResult; + slottedContent: TemplateResult; +}; export const renderComponent = ({ content, placement, open, - anchor: triggerContent, + anchor, + slottedContent, }: Partial) => { return html` - ${triggerContent} + ${anchor} + ${slottedContent + ? html`
${slottedContent}
` + : nothing}
`; }; From 780f589178dde61bcce520b33f4f4ecb59df758f Mon Sep 17 00:00:00 2001 From: sua yoo Date: Wed, 28 May 2025 15:48:50 -0700 Subject: [PATCH 3/3] add docs --- frontend/src/components/ui/popover.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/components/ui/popover.ts b/frontend/src/components/ui/popover.ts index dcd7d15c10..f4ea9f153b 100644 --- a/frontend/src/components/ui/popover.ts +++ b/frontend/src/components/ui/popover.ts @@ -5,6 +5,12 @@ import { css } from "lit"; import { customElement, property } from "lit/decorators.js"; /** + * Popovers are used to reveal supplementary information, like additional context or details. + * They're hidden until an anchor is activated, e.g. on hover. + * + * Popovers should be used to convey information in full sentences or complex HTML. + * To display titles, labels, and expand truncated text on hover, use ``. + * * @attr {String} content * @attr {String} placement * @attr {String} trigger