From 628cd1efb11212e57b05ee067fde25ecde7c6847 Mon Sep 17 00:00:00 2001 From: Philipp Gfeller Date: Fri, 28 Jun 2024 16:32:42 +0200 Subject: [PATCH] feat(tooltip)!: add post-tooltip-trigger In order to get rid of mutation observers on attributes, a new element to wrap the trigger was added for the tooltip. This enables easier lifecycle management and more precise event listeners with less overhead. --- .changeset/empty-shoes-type.md | 5 + .changeset/unlucky-books-invite.md | 5 + packages/components/src/components.d.ts | 37 +++- .../post-popovercontainer.scss | 11 ++ .../post-popovercontainer.tsx | 27 ++- .../post-popovercontainer/readme.md | 1 + .../post-tooltip-trigger.tsx | 70 ++++++++ .../components/post-tooltip-trigger/readme.md | 17 ++ .../components/post-tooltip/post-tooltip.tsx | 162 +----------------- .../src/components/post-tooltip/readme.md | 15 +- packages/components/src/index.html | 17 ++ packages/components/src/utils/is-focusable.ts | 28 +++ .../components/tooltip/tooltip.stories.ts | 38 ++-- packages/styles/src/mixins/_animation.scss | 40 +++++ 14 files changed, 283 insertions(+), 190 deletions(-) create mode 100644 .changeset/empty-shoes-type.md create mode 100644 .changeset/unlucky-books-invite.md create mode 100644 packages/components/src/components/post-tooltip-trigger/post-tooltip-trigger.tsx create mode 100644 packages/components/src/components/post-tooltip-trigger/readme.md create mode 100644 packages/components/src/utils/is-focusable.ts diff --git a/.changeset/empty-shoes-type.md b/.changeset/empty-shoes-type.md new file mode 100644 index 0000000000..210b6d12ab --- /dev/null +++ b/.changeset/empty-shoes-type.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-components': major +--- + +Removed the arrow option for the `` element, the arrow is always shown for tooltips. diff --git a/.changeset/unlucky-books-invite.md b/.changeset/unlucky-books-invite.md new file mode 100644 index 0000000000..dd17513305 --- /dev/null +++ b/.changeset/unlucky-books-invite.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-components': major +--- + +Updated the trigger for the ``. Instead of using an attribute to link a trigger element with the tooltip, a new `` element is available for wrapping the element that should display the tooltip. diff --git a/packages/components/src/components.d.ts b/packages/components/src/components.d.ts index c4a14afe4f..a7d5b786d3 100644 --- a/packages/components/src/components.d.ts +++ b/packages/components/src/components.d.ts @@ -197,6 +197,10 @@ export namespace Components { "toggle": (target: HTMLElement, force?: boolean) => Promise; } interface PostPopovercontainer { + /** + * Animation style + */ + "animation"?: 'pop-in'; /** * Wheter or not to display a little pointer arrow */ @@ -277,9 +281,9 @@ export namespace Components { } interface PostTooltip { /** - * Wheter or not to display a little pointer arrow + * Choose a tooltip animation */ - "arrow"?: boolean; + "animation"?: 'pop-in' | null; /** * Programmatically hide this tooltip */ @@ -300,6 +304,12 @@ export namespace Components { */ "toggle": (target: HTMLElement, force?: boolean) => Promise; } + interface PostTooltipTrigger { + /** + * Link the trigger to a tooltip with this id + */ + "for": string; + } } export interface PostAlertCustomEvent extends CustomEvent { detail: T; @@ -484,6 +494,12 @@ declare global { prototype: HTMLPostTooltipElement; new (): HTMLPostTooltipElement; }; + interface HTMLPostTooltipTriggerElement extends Components.PostTooltipTrigger, HTMLStencilElement { + } + var HTMLPostTooltipTriggerElement: { + prototype: HTMLPostTooltipTriggerElement; + new (): HTMLPostTooltipTriggerElement; + }; interface HTMLElementTagNameMap { "post-accordion": HTMLPostAccordionElement; "post-accordion-item": HTMLPostAccordionItemElement; @@ -499,6 +515,7 @@ declare global { "post-tabs": HTMLPostTabsElement; "post-tag": HTMLPostTagElement; "post-tooltip": HTMLPostTooltipElement; + "post-tooltip-trigger": HTMLPostTooltipTriggerElement; } } declare namespace LocalJSX { @@ -656,6 +673,10 @@ declare namespace LocalJSX { "placement"?: Placement; } interface PostPopovercontainer { + /** + * Animation style + */ + "animation"?: 'pop-in'; /** * Wheter or not to display a little pointer arrow */ @@ -733,14 +754,20 @@ declare namespace LocalJSX { } interface PostTooltip { /** - * Wheter or not to display a little pointer arrow + * Choose a tooltip animation */ - "arrow"?: boolean; + "animation"?: 'pop-in' | null; /** * Defines the placement of the tooltip according to the floating-ui options available at https://floating-ui.com/docs/computePosition#placement. Tooltips are automatically flipped to the opposite side if there is not enough available space and are shifted towards the viewport if they would overlap edge boundaries. */ "placement"?: Placement; } + interface PostTooltipTrigger { + /** + * Link the trigger to a tooltip with this id + */ + "for"?: string; + } interface IntrinsicElements { "post-accordion": PostAccordion; "post-accordion-item": PostAccordionItem; @@ -756,6 +783,7 @@ declare namespace LocalJSX { "post-tabs": PostTabs; "post-tag": PostTag; "post-tooltip": PostTooltip; + "post-tooltip-trigger": PostTooltipTrigger; } } export { LocalJSX as JSX }; @@ -782,6 +810,7 @@ declare module "@stencil/core" { "post-tabs": LocalJSX.PostTabs & JSXBase.HTMLAttributes; "post-tag": LocalJSX.PostTag & JSXBase.HTMLAttributes; "post-tooltip": LocalJSX.PostTooltip & JSXBase.HTMLAttributes; + "post-tooltip-trigger": LocalJSX.PostTooltipTrigger & JSXBase.HTMLAttributes; } } } diff --git a/packages/components/src/components/post-popovercontainer/post-popovercontainer.scss b/packages/components/src/components/post-popovercontainer/post-popovercontainer.scss index 5a674a3a94..9e778ad972 100644 --- a/packages/components/src/components/post-popovercontainer/post-popovercontainer.scss +++ b/packages/components/src/components/post-popovercontainer/post-popovercontainer.scss @@ -4,6 +4,7 @@ @use '@swisspost/design-system-styles/variables/commons'; @use '@swisspost/design-system-styles/variables/spacing'; @use '@swisspost/design-system-styles/mixins/color' as color-mx; +@use '@swisspost/design-system-styles/mixins/animation' as animation-mx; @use '@swisspost/design-system-styles/mixins/elevation' as elevation-mx; @use '@swisspost/design-system-styles/functions/color' as color-fn; @@ -69,3 +70,13 @@ border-bottom: 2px solid transparent; } } + +.popover[data-animation='pop-in'] { + @include animation-mx.top-layer-pop-in(0.25s, ':popover-open'); + + @media (prefers-reduced-motion) { + & { + --transition-duration: 0s; + } + } +} diff --git a/packages/components/src/components/post-popovercontainer/post-popovercontainer.tsx b/packages/components/src/components/post-popovercontainer/post-popovercontainer.tsx index 497a1f58cb..bea13aae1f 100644 --- a/packages/components/src/components/post-popovercontainer/post-popovercontainer.tsx +++ b/packages/components/src/components/post-popovercontainer/post-popovercontainer.tsx @@ -61,6 +61,11 @@ export class PostPopovercontainer { */ @Prop() readonly placement?: Placement = 'top'; + /** + * Animation style + */ + @Prop() readonly animation?: 'pop-in' = null; + /** * Wheter or not to display a little pointer arrow */ @@ -98,10 +103,8 @@ export class PostPopovercontainer { */ @Method() async hide() { - if (!this.toggleTimeoutId) { - this.eventTarget = null; - this.popoverRef.hidePopover(); - } + this.eventTarget = null; + this.popoverRef.hidePopover(); } /** @@ -111,13 +114,9 @@ export class PostPopovercontainer { */ @Method() async toggle(target: HTMLElement, force?: boolean): Promise { - // Prevent instant double toggle - if (!this.toggleTimeoutId) { - this.eventTarget = target; - this.calculatePosition(); - this.popoverRef.togglePopover(force); - this.toggleTimeoutId = null; - } + this.eventTarget = target; + this.calculatePosition(); + this.popoverRef.togglePopover(force); return this.popoverRef.matches(':popover-open'); } @@ -128,7 +127,6 @@ export class PostPopovercontainer { * @param e ToggleEvent */ private handleToggle(e: ToggleEvent) { - this.toggleTimeoutId = window.setTimeout(() => (this.toggleTimeoutId = null), 10); const isOpen = e.newState === 'open'; if (isOpen) { this.startAutoupdates(); @@ -173,7 +171,7 @@ export class PostPopovercontainer { offset(this.arrow ? 12 : 8), // 4px outside of element to account for focus outline + ~arrow size ]; - if (this.arrow) { + if (this.arrow && this.arrowRef) { middleware.push(arrow({ element: this.arrowRef, padding: 8 })); } @@ -193,7 +191,7 @@ export class PostPopovercontainer { this.popoverRef.style.top = `${y}px`; // Arrow - if (this.arrow) { + if (this.arrow && this.arrowRef) { // Tutorial: https://codesandbox.io/s/mystifying-kare-ee3hmh?file=/src/index.js const side = currentPlacement.split('-')[0]; const { x: arrowX, y: arrowY } = middlewareData.arrow; @@ -217,6 +215,7 @@ export class PostPopovercontainer {
(this.popoverRef = el)} > diff --git a/packages/components/src/components/post-popovercontainer/readme.md b/packages/components/src/components/post-popovercontainer/readme.md index 1e966cc488..012e4a8dbd 100644 --- a/packages/components/src/components/post-popovercontainer/readme.md +++ b/packages/components/src/components/post-popovercontainer/readme.md @@ -9,6 +9,7 @@ | Property | Attribute | Description | Type | Default | | ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `animation` | `animation` | Animation style | `"pop-in"` | `null` | | `arrow` | `arrow` | Wheter or not to display a little pointer arrow | `boolean` | `false` | | `placement` | `placement` | Defines the placement of the tooltip according to the floating-ui options available at https://floating-ui.com/docs/computePosition#placement. Tooltips are automatically flipped to the opposite side if there is not enough available space and are shifted towards the viewport if they would overlap edge boundaries. | `"bottom" \| "bottom-end" \| "bottom-start" \| "left" \| "left-end" \| "left-start" \| "right" \| "right-end" \| "right-start" \| "top" \| "top-end" \| "top-start"` | `'top'` | diff --git a/packages/components/src/components/post-tooltip-trigger/post-tooltip-trigger.tsx b/packages/components/src/components/post-tooltip-trigger/post-tooltip-trigger.tsx new file mode 100644 index 0000000000..9eb757f1dc --- /dev/null +++ b/packages/components/src/components/post-tooltip-trigger/post-tooltip-trigger.tsx @@ -0,0 +1,70 @@ +import { Component, Element, Prop } from '@stencil/core'; +import { isFocusable } from '../../utils/is-focusable'; +import { version } from '@root/package.json'; +import 'long-press-event'; + +@Component({ + tag: 'post-tooltip-trigger', +}) +export class PostTooltipTrigger { + /** + * Link the trigger to a tooltip with this id + */ + @Prop() for: string; + + @Element() host: HTMLPostTooltipTriggerElement; + + private trigger: HTMLElement; + private localInterestHandler; + private localInterestLostHandler; + + constructor() { + this.localInterestHandler = this.interestHandler.bind(this); + this.localInterestLostHandler = this.interestLostHandler.bind(this); + } + + componentDidLoad() { + this.host.setAttribute('data-version', version); + + if (this.host?.children.length > 0 && this.host.children[0].nodeType === 1) { + this.trigger = this.host.children[0] as HTMLElement; + } else { + this.trigger = this.host; + } + + // Ensure trigger is focusable + if (!isFocusable(this.trigger)) { + this.trigger.setAttribute('tabindex', '0'); + } + + // Add tooltip to aria-describedby + const describedBy = this.trigger.getAttribute('aria-describedby'); + if (!describedBy?.includes(this.for)) { + const newDescribedBy = describedBy ? `${describedBy} ${this.for}` : this.for; + this.trigger.setAttribute('aria-describedby', newDescribedBy); + } + + this.host.addEventListener('pointerover', this.localInterestHandler); + this.host.addEventListener('pointerout', this.localInterestLostHandler); + this.host.addEventListener('focusin', this.localInterestHandler); + this.host.addEventListener('focusout', this.localInterestLostHandler); + this.host.addEventListener('long-press', this.localInterestHandler); + } + + private get tooltip(): HTMLPostTooltipElement | null { + const ref = document.getElementById(this.for); + if (ref && ref.tagName === 'POST-TOOLTIP') { + return ref as HTMLPostTooltipElement; + } + + return null; + } + + private interestHandler() { + this.tooltip?.show(this.trigger); + } + + private interestLostHandler() { + this.tooltip?.hide(); + } +} diff --git a/packages/components/src/components/post-tooltip-trigger/readme.md b/packages/components/src/components/post-tooltip-trigger/readme.md new file mode 100644 index 0000000000..cb151d4dd3 --- /dev/null +++ b/packages/components/src/components/post-tooltip-trigger/readme.md @@ -0,0 +1,17 @@ +# post-tooltip-trigger + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------- | --------- | ------------------------------------------ | -------- | ----------- | +| `for` | `for` | Link the trigger to a tooltip with this id | `string` | `undefined` | + + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/components/src/components/post-tooltip/post-tooltip.tsx b/packages/components/src/components/post-tooltip/post-tooltip.tsx index 68de40a449..7be586babd 100644 --- a/packages/components/src/components/post-tooltip/post-tooltip.tsx +++ b/packages/components/src/components/post-tooltip/post-tooltip.tsx @@ -1,93 +1,11 @@ import { Component, Element, h, Host, Method, Prop } from '@stencil/core'; import { Placement } from '@floating-ui/dom'; import { version } from '@root/package.json'; -import isFocusable from 'ally.js/is/focusable'; -import 'long-press-event'; -import { getAttributeObserver } from '@/utils/attribute-observer'; /** * @slot default - Slot for the content of the tooltip. */ -/** - * Track how many instances remain on the page. Used for removing global event listeners - */ -let tooltipInstances = 0; -let hideTooltipTimeout: number = null; -const tooltipTargetAttribute = 'data-tooltip-target'; -const tooltipTargetAttributeSelector = `[${tooltipTargetAttribute}]`; - -/** - * Global event listener to show tooltips. This is globalized so that triggers that are rendered - * async will still work without the need to set listeners on the element itself - * @param e Event - * @returns - */ -const globalInterestHandler = (e: PointerEvent | FocusEvent) => { - const targetElement = (e.target as HTMLElement).closest( - tooltipTargetAttributeSelector, - ) as HTMLElement; - if (!targetElement || !('getAttribute' in targetElement)) return; - const tooltipTarget = targetElement.getAttribute(tooltipTargetAttribute); - if (!tooltipTarget || tooltipTarget === '') return; - const tooltip = document.getElementById(tooltipTarget) as HTMLPostTooltipElement; - void tooltip?.show(targetElement); - if (hideTooltipTimeout) { - window.clearTimeout(hideTooltipTimeout); - hideTooltipTimeout = null; - } -}; - -/** - * Global event listener to hide tooltips. This is globalized so that triggers that are rendered - * async will still work without the need to set listeners on the element itself - * @param e Event - * @returns - */ -const globalInterestLostHandler = (e: PointerEvent | FocusEvent) => { - const targetElement = (e.target as HTMLElement).closest(tooltipTargetAttributeSelector); - if (!targetElement || !('getAttribute' in targetElement)) return; - const tooltipTarget = targetElement.getAttribute(tooltipTargetAttribute); - if (!tooltipTarget || tooltipTarget === '') return; - const tooltip = document.getElementById(tooltipTarget) as HTMLPostTooltipElement; - globalHideTooltip(tooltip); -}; - -/** - * Start the hiding process through a timeout to give other interest events a chance to - * intervene and cancel the hide event. - * @param {HTMLPostTooltipElement} tooltip - */ -const globalHideTooltip = (tooltip: HTMLPostTooltipElement | PostTooltip) => { - hideTooltipTimeout = window.setTimeout(() => { - tooltip.hide(); - hideTooltipTimeout = null; - }, 42); -}; - -/** - * Patch some accessibility features that are hard to remember or understand - * @param {HTMLElement} trigger - */ -const patchAccessibilityFeatures = (trigger: HTMLElement) => { - const describedBy = trigger.getAttribute('aria-describedby'); - const id = trigger.getAttribute(tooltipTargetAttribute); - - // Add tooltip to aria-describedby - if (!describedBy?.includes(id)) { - const newDescribedBy = describedBy ? `${describedBy} ${id}` : id; - trigger.setAttribute('aria-describedby', newDescribedBy); - } - - // Make element focusable - if (!isFocusable(trigger)) { - trigger.setAttribute('tabindex', '0'); - } -}; - -// Initialize a mutation observer for patching accessibility features -const triggerObserver = getAttributeObserver(tooltipTargetAttribute, patchAccessibilityFeatures); - @Component({ tag: 'post-tooltip', styleUrl: 'post-tooltip.scss', @@ -106,62 +24,19 @@ export class PostTooltip { @Prop() readonly placement?: Placement = 'top'; /** - * Wheter or not to display a little pointer arrow + * Choose a tooltip animation */ - @Prop() readonly arrow?: boolean = true; + @Prop() readonly animation?: 'pop-in' | null = 'pop-in'; componentDidLoad() { if (!this.host.id) { - throw new Error( + console.error( /*prettier-ignore*/ 'No id set: must have an id, linking it to it\'s target element using the data-tooltip-target attribute.', ); } } - /** - * Add interest event listeners, but only once, and start - * the accessibility patcher - */ - componentWillLoad() { - if (tooltipInstances === 0) { - // This is the first tooltip on the page, add event listeners - document.addEventListener('pointerover', globalInterestHandler); - document.addEventListener('pointerout', globalInterestLostHandler); - document.addEventListener('focusin', globalInterestHandler); - document.addEventListener('focusout', globalInterestLostHandler); - document.addEventListener('long-press', globalInterestHandler); - - // Initially run the accessibility patcher on all triggers - document.querySelectorAll('[data-tooltip-target]').forEach(patchAccessibilityFeatures); - - // Start watching for future triggers - triggerObserver.observe(document.body, { - subtree: true, - childList: true, - attributeFilter: [tooltipTargetAttribute], - }); - } - tooltipInstances++; - } - - /** - * Remove a bunch of event listeners if the tooltip gets removed from the DOM - * and disconnect the accessibility patcher - */ - disconnectedCallback() { - tooltipInstances--; - if (tooltipInstances <= 0) { - // The last tooltip has been removed, stop listening for these kind of events - document.removeEventListener('pointerover', globalInterestHandler); - document.removeEventListener('pointerout', globalInterestLostHandler); - document.removeEventListener('focusin', globalInterestHandler); - document.removeEventListener('focusout', globalInterestLostHandler); - document.removeEventListener('long-press', globalInterestHandler); - triggerObserver.disconnect(); - } - } - /** * Programmatically display the tooltip * @param target An element with [data-tooltip-target="id"] where the tooltip should be shown @@ -189,38 +64,13 @@ export class PostTooltip { this.popoverRef.toggle(target, force); } - /** - * Pointer or focus is on the tooltip, stop the tooltip from disappearing - */ - private handleInterest() { - if (hideTooltipTimeout) { - window.clearTimeout(hideTooltipTimeout); - hideTooltipTimeout = null; - } - } - - /** - * Pointer or focus left the tooltip, initiate the hiding process - */ - private handleInterestLost() { - globalHideTooltip(this); - } - render() { - const popoverClass = `${this.arrow ? ' has-arrow' : ''}`; return ( - + (this.popoverRef = el)} > diff --git a/packages/components/src/components/post-tooltip/readme.md b/packages/components/src/components/post-tooltip/readme.md index cb015f8702..6e98fd46b0 100644 --- a/packages/components/src/components/post-tooltip/readme.md +++ b/packages/components/src/components/post-tooltip/readme.md @@ -7,10 +7,10 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `arrow` | `arrow` | Wheter or not to display a little pointer arrow | `boolean` | `true` | -| `placement` | `placement` | Defines the placement of the tooltip according to the floating-ui options available at https://floating-ui.com/docs/computePosition#placement. Tooltips are automatically flipped to the opposite side if there is not enough available space and are shifted towards the viewport if they would overlap edge boundaries. | `"bottom" \| "bottom-end" \| "bottom-start" \| "left" \| "left-end" \| "left-start" \| "right" \| "right-end" \| "right-start" \| "top" \| "top-end" \| "top-start"` | `'top'` | +| Property | Attribute | Description | Type | Default | +| ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| `animation` | `animation` | Choose a tooltip animation | `"pop-in"` | `'pop-in'` | +| `placement` | `placement` | Defines the placement of the tooltip according to the floating-ui options available at https://floating-ui.com/docs/computePosition#placement. Tooltips are automatically flipped to the opposite side if there is not enough available space and are shifted towards the viewport if they would overlap edge boundaries. | `"bottom" \| "bottom-end" \| "bottom-start" \| "left" \| "left-end" \| "left-start" \| "right" \| "right-end" \| "right-start" \| "top" \| "top-end" \| "top-start"` | `'top'` | ## Methods @@ -59,6 +59,13 @@ Type: `Promise` +## Slots + +| Slot | Description | +| ----------- | ------------------------------------ | +| `"default"` | Slot for the content of the tooltip. | + + ## Dependencies ### Depends on diff --git a/packages/components/src/index.html b/packages/components/src/index.html index 0c95129ca8..664de9aa09 100644 --- a/packages/components/src/index.html +++ b/packages/components/src/index.html @@ -14,5 +14,22 @@
Add your component here and start developing!
+ + + + + + + no element inside + + + ma popover + with tooltip + tha other tooltip + + + tha other tooltip + I'm tha tooltip diff --git a/packages/components/src/utils/is-focusable.ts b/packages/components/src/utils/is-focusable.ts new file mode 100644 index 0000000000..66aa7d74dc --- /dev/null +++ b/packages/components/src/utils/is-focusable.ts @@ -0,0 +1,28 @@ +const focusableSelector = `:where(${[ + 'button', + 'input:not([type="hidden"])', + '[tabindex]', + 'select', + 'textarea', + '[contenteditable]', + 'a[href]', + 'iframe', + 'audio[controls]', + 'video[controls]', + 'area[href]', + 'details > summary:first-of-type', +].join(',')})`; + +const focusDisablingSelector = `:where(${[ + '[inert]', + '[inert] *', + ':disabled', + 'dialog:not([open]) *', + '[popover]:not(:popover-open) *', + 'details:not([open]) > *:not(details > summary:first-of-type)', + 'details:not([open]) > *:not(details > summary:first-of-type) *', +].join(',')})`; + +export const isFocusable = (element: Element) => { + return element?.matches(focusableSelector) && !element?.matches(focusDisablingSelector); +}; diff --git a/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts b/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts index a7a9c6b8a4..61c7fa5101 100644 --- a/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts +++ b/packages/documentation/src/stories/components/tooltip/tooltip.stories.ts @@ -62,11 +62,17 @@ const meta: MetaComponent = { placement: { name: 'Placement', }, - arrow: { - name: 'Arrow', + animation: { + name: 'Animation', + description: 'Defines what kind of animation to use when showing/hiding the tooltip.', control: { - type: 'boolean', + type: 'select', + labels: { + 'null': 'None', + 'pop-in': 'Pop in', + }, }, + options: ['null', 'pop-in'], }, }, }; @@ -82,12 +88,14 @@ function render(args: Args) { if (currentArgs.innerHTML !== innerHTML) updateArgs({ innerHTML }); return html` - + ${unsafeHTML(innerHTML)} @@ -103,7 +111,9 @@ export const NonFocusable: StoryObj = { }, render: (args: Args) => { return html` - This is a cite element with a tooltip on it. + + This is a cite element with a tooltip on it. + { return html` - - + + + + + +