From 0d83c1b51c0d7e7b0d6506ffc211f2c1b898bf23 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 11 May 2026 20:27:07 +0300 Subject: [PATCH 1/3] feat(tooltip): add pf-v6-tooltip element Port tooltip to v6 design tokens and patterns. - FloatingDOMController with arrow element positioning - Cancellable TooltipShowEvent/TooltipHideEvent with reason enum - `trigger` attribute (IDREF) and property (Element ref) - `entry-delay`/`exit-delay` attributes (default 300ms) - `alignment` attribute (start/end/left/right) - `position`, `no-flip`, `flip-behavior` carried from v5 - `light-dark()` CSS fallbacks for inverse color scheme - `ariaDescribedByElements` wiring on show/hide - Escape key dismissal - 9 demos including playground, color-scheme, and element-ref trigger - 17 unit tests covering API, a11y, events, and cancellation Closes #3048 Assisted-By: Claude Opus 4.6 (1M context) --- elements/pf-v6-tooltip/demo/color-scheme.html | 44 +++ .../pf-v6-tooltip/demo/dynamic-content.html | 39 +++ elements/pf-v6-tooltip/demo/index.html | 20 ++ elements/pf-v6-tooltip/demo/left-aligned.html | 21 ++ elements/pf-v6-tooltip/demo/options.html | 101 ++++++ elements/pf-v6-tooltip/demo/placement.html | 61 ++++ .../pf-v6-tooltip/demo/slotted-content.html | 28 ++ .../pf-v6-tooltip/demo/trigger-element.html | 25 ++ elements/pf-v6-tooltip/demo/trigger-ref.html | 21 ++ elements/pf-v6-tooltip/pf-v6-tooltip.css | 71 ++++ elements/pf-v6-tooltip/pf-v6-tooltip.ts | 311 ++++++++++++++++++ .../pf-v6-tooltip/test/pf-v6-tooltip.spec.ts | 284 ++++++++++++++++ 12 files changed, 1026 insertions(+) create mode 100644 elements/pf-v6-tooltip/demo/color-scheme.html create mode 100644 elements/pf-v6-tooltip/demo/dynamic-content.html create mode 100644 elements/pf-v6-tooltip/demo/index.html create mode 100644 elements/pf-v6-tooltip/demo/left-aligned.html create mode 100644 elements/pf-v6-tooltip/demo/options.html create mode 100644 elements/pf-v6-tooltip/demo/placement.html create mode 100644 elements/pf-v6-tooltip/demo/slotted-content.html create mode 100644 elements/pf-v6-tooltip/demo/trigger-element.html create mode 100644 elements/pf-v6-tooltip/demo/trigger-ref.html create mode 100644 elements/pf-v6-tooltip/pf-v6-tooltip.css create mode 100644 elements/pf-v6-tooltip/pf-v6-tooltip.ts create mode 100644 elements/pf-v6-tooltip/test/pf-v6-tooltip.spec.ts diff --git a/elements/pf-v6-tooltip/demo/color-scheme.html b/elements/pf-v6-tooltip/demo/color-scheme.html new file mode 100644 index 0000000000..20b40f4b21 --- /dev/null +++ b/elements/pf-v6-tooltip/demo/color-scheme.html @@ -0,0 +1,44 @@ +--- +description: Tooltip inverts to contrast with the page color scheme. +--- +
+

Light mode

+ + Light page trigger + +
+ +
+

Dark mode

+ + Dark page trigger + +
+ + + + diff --git a/elements/pf-v6-tooltip/demo/dynamic-content.html b/elements/pf-v6-tooltip/demo/dynamic-content.html new file mode 100644 index 0000000000..d9c53f9886 --- /dev/null +++ b/elements/pf-v6-tooltip/demo/dynamic-content.html @@ -0,0 +1,39 @@ +--- +description: Tooltip content that updates dynamically. +--- +
+ + + +
+ + + + diff --git a/elements/pf-v6-tooltip/demo/index.html b/elements/pf-v6-tooltip/demo/index.html new file mode 100644 index 0000000000..6aa1df171a --- /dev/null +++ b/elements/pf-v6-tooltip/demo/index.html @@ -0,0 +1,20 @@ +--- +description: A tooltip wrapping a button, shown on hover or focus. +--- +
+ + I have a tooltip! + +
+ + + + diff --git a/elements/pf-v6-tooltip/demo/left-aligned.html b/elements/pf-v6-tooltip/demo/left-aligned.html new file mode 100644 index 0000000000..eeec46d9b8 --- /dev/null +++ b/elements/pf-v6-tooltip/demo/left-aligned.html @@ -0,0 +1,21 @@ +--- +description: Tooltip text can be left-aligned instead of centered. +--- +
+ + Left-aligned tooltip + +
+ + + + diff --git a/elements/pf-v6-tooltip/demo/options.html b/elements/pf-v6-tooltip/demo/options.html new file mode 100644 index 0000000000..fed5ef159f --- /dev/null +++ b/elements/pf-v6-tooltip/demo/options.html @@ -0,0 +1,101 @@ +--- +description: Interactive playground for tooltip options. +--- +
+
+ Position + +
+ +
+ Alignment + +
+ +
+ Delays + + +
+ +
+ Flip + +
+
+ +
+ + Tooltip + +
+ + + + diff --git a/elements/pf-v6-tooltip/demo/placement.html b/elements/pf-v6-tooltip/demo/placement.html new file mode 100644 index 0000000000..54e3fd3a1a --- /dev/null +++ b/elements/pf-v6-tooltip/demo/placement.html @@ -0,0 +1,61 @@ +--- +description: Tooltips can be positioned on any side of the trigger element. +--- +
+
+ + Top + + + Top Start + + + Top End + + + Bottom + + + Bottom Start + + + Bottom End + + + Left + + + Left Start + + + Left End + + + Right + + + Right Start + + + Right End + +
+
+ + + + diff --git a/elements/pf-v6-tooltip/demo/slotted-content.html b/elements/pf-v6-tooltip/demo/slotted-content.html new file mode 100644 index 0000000000..aaef1bf457 --- /dev/null +++ b/elements/pf-v6-tooltip/demo/slotted-content.html @@ -0,0 +1,28 @@ +--- +description: Tooltip content can include formatted HTML via the content slot. +--- +
+

A tooltip may contain + + HTML content + + Slotted content can be formatted, using HTML + tags like em, strong, or code. + + by using the content slot. +

+
+ + + + diff --git a/elements/pf-v6-tooltip/demo/trigger-element.html b/elements/pf-v6-tooltip/demo/trigger-element.html new file mode 100644 index 0000000000..7aafa98ebc --- /dev/null +++ b/elements/pf-v6-tooltip/demo/trigger-element.html @@ -0,0 +1,25 @@ +--- +description: Setting the trigger property to an Element reference directly. +--- +
+ Tooltip via element reference + + +
+ + + + diff --git a/elements/pf-v6-tooltip/demo/trigger-ref.html b/elements/pf-v6-tooltip/demo/trigger-ref.html new file mode 100644 index 0000000000..1ca0b91e65 --- /dev/null +++ b/elements/pf-v6-tooltip/demo/trigger-ref.html @@ -0,0 +1,21 @@ +--- +description: A tooltip referencing an external element as its trigger. +--- +
+ Tooltip attached via trigger ref + + +
+ + + + diff --git a/elements/pf-v6-tooltip/pf-v6-tooltip.css b/elements/pf-v6-tooltip/pf-v6-tooltip.css new file mode 100644 index 0000000000..538469fd82 --- /dev/null +++ b/elements/pf-v6-tooltip/pf-v6-tooltip.css @@ -0,0 +1,71 @@ +:host { + display: inline; + color-scheme: light dark; +} + +* { + box-sizing: border-box; +} + +#tooltip { + position: absolute; + user-select: none; + opacity: 0; + pointer-events: none; + z-index: 10000; + transition: opacity 300ms cubic-bezier(0.54, 1.5, 0.38, 1.11); + translate: var(--_floating-content-translate); + max-width: var(--pf-v6-c-tooltip--MaxWidth, 18.75rem); + box-shadow: var(--pf-v6-c-tooltip--BoxShadow, + var(--pf-t--global--box-shadow--md, + 0 0.25rem 0.5625rem 0 rgba(0, 0, 0, 0.5))); + width: max-content; + inset-block-start: 0; + inset-inline-start: 0; + will-change: opacity; + + &.open { + opacity: 1; + user-select: initial; + pointer-events: auto; + } +} + +#content { + position: relative; + text-align: center; + word-break: break-word; + line-height: 1.5; + padding-block-start: var(--pf-v6-c-tooltip__content--PaddingBlockStart, + var(--pf-t--global--spacer--sm, 0.5rem)); + padding-inline-end: var(--pf-v6-c-tooltip__content--PaddingInlineEnd, + var(--pf-t--global--spacer--md, 1rem)); + padding-block-end: var(--pf-v6-c-tooltip__content--PaddingBlockEnd, + var(--pf-t--global--spacer--sm, 0.5rem)); + padding-inline-start: var(--pf-v6-c-tooltip__content--PaddingInlineStart, + var(--pf-t--global--spacer--md, 1rem)); + font-size: var(--pf-v6-c-tooltip__content--FontSize, + var(--pf-t--global--font--size--body--sm, 0.75rem)); + color: var(--pf-v6-c-tooltip__content--Color, + var(--pf-t--global--text--color--inverse, + light-dark(#fff, #1f1f1f))); + background-color: var(--pf-v6-c-tooltip__content--BackgroundColor, + var(--pf-t--global--background--color--inverse--default, + light-dark(#1b1d21, #f2f2f2))); + border-radius: var(--pf-v6-c-tooltip__content--BorderRadius, + var(--pf-t--global--border--radius--small, 6px)); +} + +#arrow { + position: absolute; + width: var(--pf-v6-c-tooltip__arrow--Width, 0.9375rem); + height: var(--pf-v6-c-tooltip__arrow--Height, 0.9375rem); + pointer-events: none; + background-color: var(--pf-v6-c-tooltip__arrow--BackgroundColor, + var(--pf-t--global--background--color--inverse--default, + light-dark(#1b1d21, #f2f2f2))); + box-shadow: var(--pf-v6-c-tooltip__arrow--BoxShadow, + var(--pf-t--global--box-shadow--md, + 0 0.25rem 0.5625rem 0 rgba(0, 0, 0, 0.5))); + rotate: 45deg; +} diff --git a/elements/pf-v6-tooltip/pf-v6-tooltip.ts b/elements/pf-v6-tooltip/pf-v6-tooltip.ts new file mode 100644 index 0000000000..cf0423936e --- /dev/null +++ b/elements/pf-v6-tooltip/pf-v6-tooltip.ts @@ -0,0 +1,311 @@ +/* eslint-disable lit-a11y/accessible-name -- tooltip content text IS the accessible name */ +import type { PropertyValues, TemplateResult } from 'lit'; +import { LitElement, html, isServer } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; +import { styleMap } from 'lit/directives/style-map.js'; +import { classMap } from 'lit/directives/class-map.js'; + +import { + FloatingDOMController, + type Placement, +} from '@patternfly/pfe-core/controllers/floating-dom-controller.js'; + +import { StringListConverter } from '@patternfly/pfe-core'; + +import styles from './pf-v6-tooltip.css'; + +export type { Placement }; + +export type TooltipAlignment = 'start' | 'end' | 'left' | 'right'; + +export type TooltipTriggerReason = + | 'mouseenter' + | 'focusin' + | 'mouseleave' + | 'focusout'; + +export class TooltipShowEvent extends Event { + constructor(public reason: TooltipTriggerReason) { + super('show', { bubbles: true, cancelable: true }); + } +} + +export class TooltipHideEvent extends Event { + constructor(public reason: TooltipTriggerReason) { + super('hide', { bubbles: true, cancelable: true }); + } +} + +const ENTRY_EVENTS: readonly string[] = ['focusin', 'mouseenter']; +const EXIT_EVENTS: readonly string[] = ['focusout', 'mouseleave']; + +/** + * A tooltip is in-app messaging used to identify elements on a page with + * short, clarifying text. + * @summary Supplementary text popup on hover or focus. + * @slot - Trigger element that invokes the tooltip on hover or focus. + * @slot content - Rich tooltip content. Overrides the `content` attribute. + * @cssprop {} [--pf-v6-c-tooltip--MaxWidth=18.75rem] - Maximum width of the tooltip. + * @cssprop {} [--pf-v6-c-tooltip__content--Color] - Tooltip text color. + * @cssprop {} [--pf-v6-c-tooltip__content--BackgroundColor] - Tooltip background color. + * @cssprop {} [--pf-v6-c-tooltip__content--FontSize] - Tooltip font size. + * @cssprop {} [--pf-v6-c-tooltip__content--BorderRadius] - Tooltip border radius. + * @cssprop {} [--pf-v6-c-tooltip__content--PaddingBlockStart] - Block start padding. + * @cssprop {} [--pf-v6-c-tooltip__content--PaddingBlockEnd] - Block end padding. + * @cssprop {} [--pf-v6-c-tooltip__content--PaddingInlineStart] - Inline start padding. + * @cssprop {} [--pf-v6-c-tooltip__content--PaddingInlineEnd] - Inline end padding. + * @cssprop [--pf-v6-c-tooltip--BoxShadow] - Tooltip box shadow. + * @cssprop {} [--pf-v6-c-tooltip__arrow--Width=0.9375rem] - Arrow width. + * @cssprop {} [--pf-v6-c-tooltip__arrow--Height=0.9375rem] - Arrow height. + * @cssprop {} [--pf-v6-c-tooltip__arrow--BackgroundColor] - Arrow background color. + * @fires {TooltipShowEvent} show - Cancelable event fired before the tooltip shows. The `reason` field indicates what triggered it. + * @fires {TooltipHideEvent} hide - Cancelable event fired before the tooltip hides. The `reason` field indicates what triggered it. + */ +@customElement('pf-v6-tooltip') +export class PfV6Tooltip extends LitElement { + static readonly styles: CSSStyleSheet[] = [styles]; + + /** Position of the tooltip relative to the trigger element */ + @property() position: Placement = 'top'; + + /** Tooltip content text. Overridden by the content slot. */ + @property() content?: string; + + /** Disables automatic repositioning when the tooltip would overflow */ + @property({ type: Boolean, attribute: 'no-flip' }) noFlip = false; + + /** + * Fallback positions when flip is enabled and the initial position + * is not possible. Comma-separated list of placements. + */ + @property({ + attribute: 'flip-behavior', + converter: StringListConverter, + }) flipBehavior?: Placement[]; + + /** + * External trigger element. As an attribute, accepts the ID of an element + * in the same root. As a property, also accepts an Element reference directly. + */ + @property() trigger?: string | Element; + + /** Delay in ms before the tooltip appears */ + @property({ type: Number, attribute: 'entry-delay' }) entryDelay = 300; + + /** Delay in ms before the tooltip disappears */ + @property({ type: Number, attribute: 'exit-delay' }) exitDelay = 300; + + /** Text alignment within the tooltip content */ + @property() alignment?: TooltipAlignment; + + #entryTimeout?: ReturnType; + #exitTimeout?: ReturnType; + #triggerElement?: HTMLElement | null; + + get #invoker(): HTMLSlotElement | null { + return this.shadowRoot?.querySelector('#invoker') ?? null; + } + + get #invokerElement(): HTMLElement | null { + if (this.#triggerElement) { + return this.#triggerElement; + } + const slot = this.#invoker; + if (slot instanceof HTMLSlotElement) { + return slot.assignedElements()[0] as HTMLElement ?? null; + } + return null; + } + + get #tooltipEl(): HTMLElement | null { + return this.shadowRoot?.querySelector('#tooltip') ?? null; + } + + get #arrowEl(): HTMLElement | null { + return this.shadowRoot?.querySelector('#arrow') ?? null; + } + + #float = new FloatingDOMController(this, { + content: (): HTMLElement | null | undefined => this.#tooltipEl, + invoker: (): HTMLElement | null | undefined => { + if (this.#triggerElement) { + return this.#triggerElement; + } + const slot = this.#invoker; + if (slot instanceof HTMLSlotElement + && slot.assignedElements().length > 0) { + return slot.assignedElements()[0] as HTMLElement; + } + return slot; + }, + arrow: (): HTMLElement | null | undefined => this.#arrowEl, + }); + + override connectedCallback(): void { + super.connectedCallback(); + if (!isServer) { + this.#updateTriggerListeners(); + this.addEventListener('keydown', this.#onKeydown); + } + } + + override disconnectedCallback(): void { + super.disconnectedCallback(); + this.#clearTimers(); + this.#removeTriggerListeners(); + this.removeEventListener('keydown', this.#onKeydown); + } + + override willUpdate(changed: PropertyValues): void { + if (changed.has('trigger')) { + this.#updateTriggerListeners(); + } + } + + override render(): TemplateResult { + const { alignment: floatAlignment, anchor, open, styles: floatStyles } = this.#float; + return html` + +
+
+ +
+ `; + } + + /** Show the tooltip programmatically */ + async show(): Promise { + await this.updateComplete; + const placement = this.position; + const offset = + !placement?.match(/top|bottom/) ? 15 + : { mainAxis: 15, alignmentAxis: -4 }; + await this.#float.show({ + offset, + placement, + flip: !this.noFlip, + fallbackPlacements: this.flipBehavior, + }); + this.#setAriaDescribedBy(true); + } + + /** Hide the tooltip programmatically */ + async hide(): Promise { + this.#clearTimers(); + this.#setAriaDescribedBy(false); + await this.#float.hide(); + } + + #setAriaDescribedBy(add: boolean): void { + const trigger = this.#invokerElement; + const content = this.shadowRoot?.querySelector('#content') ?? null; + if (!trigger || !content) { + return; + } + if ('ariaDescribedByElements' in trigger) { + (trigger as unknown as { ariaDescribedByElements: Element[] }) + .ariaDescribedByElements = add ? [content] : []; + } + } + + #clearTimers(): void { + clearTimeout(this.#entryTimeout); + clearTimeout(this.#exitTimeout); + } + + #onSlotChange(): void { + if (!isServer) { + this.#updateTriggerListeners(); + } + this.requestUpdate(); + } + + #getTriggerElement(): HTMLElement | null { + if (!this.trigger) { + return null; + } + if (typeof this.trigger !== 'string') { + return this.trigger instanceof HTMLElement ? this.trigger : null; + } + return (this.getRootNode() as Document | ShadowRoot) + .getElementById(this.trigger); + } + + #updateTriggerListeners(): void { + if (isServer) { + return; + } + this.#removeTriggerListeners(); + this.#triggerElement = this.#getTriggerElement(); + const target = this.#triggerElement ?? this; + for (const evt of ENTRY_EVENTS) { + target.addEventListener(evt, this.#onEntry); + } + for (const evt of EXIT_EVENTS) { + target.addEventListener(evt, this.#onExit); + } + if (this.#triggerElement) { + this.addEventListener('mouseenter', this.#onEntry); + this.addEventListener('mouseleave', this.#onExit); + } + } + + #removeTriggerListeners(): void { + const target = this.#triggerElement ?? this; + for (const evt of ENTRY_EVENTS) { + target.removeEventListener(evt, this.#onEntry); + } + for (const evt of EXIT_EVENTS) { + target.removeEventListener(evt, this.#onExit); + } + if (this.#triggerElement) { + this.removeEventListener('mouseenter', this.#onEntry); + this.removeEventListener('mouseleave', this.#onExit); + } + } + + #onEntry = (event: Event): void => { + const reason = event.type as TooltipTriggerReason; + if (!this.dispatchEvent(new TooltipShowEvent(reason))) { + return; + } + this.#clearTimers(); + this.#entryTimeout = setTimeout(() => this.show(), this.entryDelay); + }; + + #onExit = (event: Event): void => { + const reason = event.type as TooltipTriggerReason; + if (!this.dispatchEvent(new TooltipHideEvent(reason))) { + return; + } + this.#clearTimers(); + this.#exitTimeout = setTimeout(() => this.hide(), this.exitDelay); + }; + + #onKeydown = (event: KeyboardEvent): void => { + if (event.key === 'Escape' && this.#float.open) { + this.hide(); + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-v6-tooltip': PfV6Tooltip; + } +} diff --git a/elements/pf-v6-tooltip/test/pf-v6-tooltip.spec.ts b/elements/pf-v6-tooltip/test/pf-v6-tooltip.spec.ts new file mode 100644 index 0000000000..96e1002d55 --- /dev/null +++ b/elements/pf-v6-tooltip/test/pf-v6-tooltip.spec.ts @@ -0,0 +1,284 @@ +import { expect, html, fixture } from '@open-wc/testing'; + +import { PfV6Tooltip, TooltipShowEvent, TooltipHideEvent } from '../pf-v6-tooltip.js'; +import { setViewport, sendMouse } from '@web/test-runner-commands'; +import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js'; + +describe('', function() { + let element: PfV6Tooltip; + + beforeEach(async function() { + await setViewport({ width: 1000, height: 1000 }); + }); + + it('imperatively instantiates', function() { + expect(document.createElement('pf-v6-tooltip')).to.be.an.instanceof(PfV6Tooltip); + }); + + it('should upgrade', async function() { + element = await fixture(html``); + const klass = customElements.get('pf-v6-tooltip'); + expect(element) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(PfV6Tooltip); + }); + + describe('with content attribute', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + }); + + it('should be accessible', async function() { + await expect(element).shadowDom.to.be.accessible(); + }); + + it('should hide tooltip content from assistive technology when closed', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.axContainName('Trigger'); + expect(snapshot).to.not.axContainName('Tooltip text'); + }); + + describe('after calling show()', function() { + beforeEach(async function() { + await element.show(); + await element.updateComplete; + }); + + it('should show tooltip content to assistive technology', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.axContainName('Trigger'); + expect(snapshot).to.axContainName('Tooltip text'); + }); + }); + + describe('after calling hide()', function() { + beforeEach(async function() { + await element.show(); + await element.updateComplete; + await element.hide(); + await element.updateComplete; + }); + + it('should hide tooltip content from assistive technology', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.axContainName('Trigger'); + expect(snapshot).to.not.axContainName('Tooltip text'); + }); + }); + + describe('hovering the element', function() { + beforeEach(async function() { + const { x, y } = element.getBoundingClientRect(); + await sendMouse({ position: [x + 5, y + 5], type: 'move' }); + await new Promise(r => setTimeout(r, 400)); + await element.updateComplete; + }); + + it('should show tooltip content to assistive technology', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.axContainName('Trigger'); + expect(snapshot).to.axContainName('Tooltip text'); + }); + }); + }); + + describe('with content slot', function() { + beforeEach(async function() { + element = await fixture(html` + + + Rich tooltip content + + `); + }); + + describe('after calling show()', function() { + beforeEach(async function() { + await element.show(); + await element.updateComplete; + }); + + it('should show slotted content', async function() { + const snapshot = await a11ySnapshot(); + const text = JSON.stringify(snapshot); + expect(text).to.include('Rich'); + expect(text).to.include('tooltip'); + }); + }); + }); + + describe('with position attribute', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + }); + + it('should accept position attribute', function() { + expect(element.position).to.equal('bottom'); + }); + }); + + describe('with no-flip attribute', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + }); + + it('should accept no-flip attribute', function() { + expect(element.noFlip).to.be.true; + }); + }); + + describe('with entry-delay and exit-delay', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + }); + + it('should accept delay attributes', function() { + expect(element.entryDelay).to.equal(100); + expect(element.exitDelay).to.equal(50); + }); + }); + + describe('with alignment attribute', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + }); + + it('should accept alignment attribute', function() { + expect(element.alignment).to.equal('start'); + }); + }); + + describe('with trigger attribute', function() { + let triggerButton: HTMLButtonElement; + + beforeEach(async function() { + const container = await fixture(html` +
+ + +
+ `); + triggerButton = container.querySelector('#ext-trigger')!; + element = container.querySelector('pf-v6-tooltip')!; + await element.updateComplete; + }); + + it('should accept trigger attribute', function() { + expect(element.trigger).to.equal('ext-trigger'); + }); + + describe('after calling show()', function() { + beforeEach(async function() { + await element.show(); + await element.updateComplete; + }); + + it('should show tooltip content', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.axContainName('External tooltip'); + }); + }); + }); + + describe('show event', function() { + let showEvent: TooltipShowEvent | null; + + beforeEach(async function() { + showEvent = null; + element = await fixture(html` + + + + `); + element.addEventListener('show', function(e) { + showEvent = e as TooltipShowEvent; + }); + }); + + describe('hovering the element', function() { + beforeEach(async function() { + const { x, y } = element.getBoundingClientRect(); + await sendMouse({ position: [x + 5, y + 5], type: 'move' }); + await new Promise(r => setTimeout(r, 50)); + }); + + it('should fire show event with reason', function() { + expect(showEvent).to.be.an.instanceOf(TooltipShowEvent); + expect(showEvent!.reason).to.equal('mouseenter'); + }); + }); + }); + + describe('cancelling show event', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + element.addEventListener('show', function(e) { + e.preventDefault(); + }); + }); + + describe('hovering the element', function() { + beforeEach(async function() { + const { x, y } = element.getBoundingClientRect(); + await sendMouse({ position: [x + 5, y + 5], type: 'move' }); + await new Promise(r => setTimeout(r, 100)); + await element.updateComplete; + }); + + it('should not show tooltip', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.not.axContainName('Prevented'); + }); + }); + }); + + describe('Escape key', function() { + beforeEach(async function() { + element = await fixture(html` + + + + `); + await element.show(); + await element.updateComplete; + }); + + describe('pressing Escape', function() { + beforeEach(async function() { + element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })); + await element.updateComplete; + await new Promise(r => setTimeout(r, 50)); + }); + + it('should hide tooltip', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot).to.not.axContainName('Escapable'); + }); + }); + }); +}); From 2bd04494bcff3b182c70d89261dc75dcacbc0d5e Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Tue, 12 May 2026 10:23:34 +0300 Subject: [PATCH 2/3] feat(tooltip): delete pf-v5-tooltip Replaced by pf-v6-tooltip. Assisted-By: Claude Opus 4.6 (1M context) --- elements/pf-v5-tooltip/README.md | 64 ------ .../pf-v5-tooltip/demo/block-triggers.html | 60 ------ .../pf-v5-tooltip/demo/custom-styles.html | 60 ------ elements/pf-v5-tooltip/demo/flip.html | 95 --------- elements/pf-v5-tooltip/demo/index.html | 37 ---- elements/pf-v5-tooltip/demo/performance.html | 63 ------ elements/pf-v5-tooltip/demo/placement.html | 70 ------- .../pf-v5-tooltip/demo/slotted-content.html | 42 ---- elements/pf-v5-tooltip/demo/trigger.html | 15 -- elements/pf-v5-tooltip/docs/CHANGELOG.old.md | 125 ------------ elements/pf-v5-tooltip/docs/pf-v5-tooltip.md | 106 ---------- elements/pf-v5-tooltip/docs/screenshot.png | Bin 66066 -> 0 bytes elements/pf-v5-tooltip/pf-v5-tooltip.css | 108 ---------- elements/pf-v5-tooltip/pf-v5-tooltip.ts | 184 ------------------ elements/pf-v5-tooltip/test/pf-tooltip.e2e.ts | 25 --- .../pf-v5-tooltip/test/pf-tooltip.spec.ts | 57 ------ 16 files changed, 1111 deletions(-) delete mode 100644 elements/pf-v5-tooltip/README.md delete mode 100644 elements/pf-v5-tooltip/demo/block-triggers.html delete mode 100644 elements/pf-v5-tooltip/demo/custom-styles.html delete mode 100644 elements/pf-v5-tooltip/demo/flip.html delete mode 100644 elements/pf-v5-tooltip/demo/index.html delete mode 100644 elements/pf-v5-tooltip/demo/performance.html delete mode 100644 elements/pf-v5-tooltip/demo/placement.html delete mode 100644 elements/pf-v5-tooltip/demo/slotted-content.html delete mode 100644 elements/pf-v5-tooltip/demo/trigger.html delete mode 100644 elements/pf-v5-tooltip/docs/CHANGELOG.old.md delete mode 100644 elements/pf-v5-tooltip/docs/pf-v5-tooltip.md delete mode 100644 elements/pf-v5-tooltip/docs/screenshot.png delete mode 100644 elements/pf-v5-tooltip/pf-v5-tooltip.css delete mode 100644 elements/pf-v5-tooltip/pf-v5-tooltip.ts delete mode 100644 elements/pf-v5-tooltip/test/pf-tooltip.e2e.ts delete mode 100644 elements/pf-v5-tooltip/test/pf-tooltip.spec.ts diff --git a/elements/pf-v5-tooltip/README.md b/elements/pf-v5-tooltip/README.md deleted file mode 100644 index 92131d606d..0000000000 --- a/elements/pf-v5-tooltip/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Tooltip -A tooltip is a floating text area triggered by a user that provides helpful or contextual information. - -## Usage -A tooltip is used by wrapping an html element in the pf-v5-tooltip element along with contextual information to be displayed alongside of the element. - -Read more about tooltips in the [PatternFly Elements Tooltip documentation](https://patternflyelements.org/components/tooltip) - -## Installation - -Load `` via CDN: - -```html - -``` - -Or, if you are using [NPM](https://npm.im), install it - -```bash -npm install @patternfly/elements -``` - -Then once installed, import it to your application: - -```js -import '@patternfly/elements/pf-v5-tooltip/pf-v5-tooltip.js'; -``` - -## Usage - - -### Basic Tooltip -```html - This is An Element -
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et - ultrices. -
-
-``` - -### Tooltip With Left Positioning (also available: top, right, bottom) -```html - This is An Element -
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et - ultrices. -
-
-``` - -### Tooltip With Left Positioning And Offset -```html - -
- This is An Element -
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices. -
-
-``` diff --git a/elements/pf-v5-tooltip/demo/block-triggers.html b/elements/pf-v5-tooltip/demo/block-triggers.html deleted file mode 100644 index 1a728b26b2..0000000000 --- a/elements/pf-v5-tooltip/demo/block-triggers.html +++ /dev/null @@ -1,60 +0,0 @@ -
-

Toggle Container Width

- - - -
- -
-

Block Triggers

-

Tooltips can wrap block-level elements. - Tooltips which wrap focusable content must not have a tabindex greater than -1. -

-
    -
  • Left Tooltip
  • -
  • Left Start Tooltip
  • -
  • Left End Tooltip
  • -
  • Right Tooltip
  • -
  • Right Start Tooltip
  • -
  • Right End Tooltip
  • -
  • Top Tooltip
  • -
  • Top Start Tooltip
  • -
  • Top End Tooltip
  • -
  • Bottom Tooltip
  • -
  • Bottom Start Tooltip
  • -
  • Bottom End Tooltip
  • -
-
- - - - diff --git a/elements/pf-v5-tooltip/demo/custom-styles.html b/elements/pf-v5-tooltip/demo/custom-styles.html deleted file mode 100644 index 390c36cc39..0000000000 --- a/elements/pf-v5-tooltip/demo/custom-styles.html +++ /dev/null @@ -1,60 +0,0 @@ -
-

Toggle Container Width

- - - -
- -
-

Tooltips can be styled using CSS custom properties. For example, a tooltip may have - - custom text color - , - - a custom shadow - , or - - custom content padding - . -

-
- - - - diff --git a/elements/pf-v5-tooltip/demo/flip.html b/elements/pf-v5-tooltip/demo/flip.html deleted file mode 100644 index 7350a12512..0000000000 --- a/elements/pf-v5-tooltip/demo/flip.html +++ /dev/null @@ -1,95 +0,0 @@ -
- - Tooltip! - - - -
Flip fallback
-
- -
- - Tooltip! - -
No flip
-
- - - - - diff --git a/elements/pf-v5-tooltip/demo/index.html b/elements/pf-v5-tooltip/demo/index.html deleted file mode 100644 index 7b8c713c8b..0000000000 --- a/elements/pf-v5-tooltip/demo/index.html +++ /dev/null @@ -1,37 +0,0 @@ -
-

Toggle Container Width

- - - -
- -
-

- A Tooltip - is a piece of flow content with an associated secondary piece of hidden flow content. - Tooltips which wrap non-interactive content must have a tabindex of 0. -

-
- - - - diff --git a/elements/pf-v5-tooltip/demo/performance.html b/elements/pf-v5-tooltip/demo/performance.html deleted file mode 100644 index 40d8734e1d..0000000000 --- a/elements/pf-v5-tooltip/demo/performance.html +++ /dev/null @@ -1,63 +0,0 @@ -
-

Performance

-

This demo measures the time needed to render 10000 tooltip elements then toggle the first tooltip.

-
- Render - Reset - -
-
-
- - - - diff --git a/elements/pf-v5-tooltip/demo/placement.html b/elements/pf-v5-tooltip/demo/placement.html deleted file mode 100644 index 9b6e6dabaf..0000000000 --- a/elements/pf-v5-tooltip/demo/placement.html +++ /dev/null @@ -1,70 +0,0 @@ -
-

Toggle Container Width

- - - -
- -
-

- Tooltips can be anchored to the left: - Left, - Left Start, - Left End. - They can be anchored to the right: - Right, - Right Start, - Right End. - They can be anchored to the top: - Top, - Top Start, - Top End. - They can be anchored to the bottom: - Bottom, - Bottom Start, - Bottom End. -

-

- Tooltips can have content which wraps lines, but this is discouraged, - because tooltips are meant to contain as little content as possible. - When tooltips have lengthy content anchored to the left, it looks like this: - Left, - Left Start, - Left End. - When tooltips have lengthy content anchored to the right, it looks like this: - Right, - Right Start, - Right End. - When tooltips have lengthy content anchored to the top, it looks like this: - Top, - Top Start, - Top End. - When tooltips have lengthy content anchored to the bottom, it looks like this: - Bottom, - Bottom Start, - Bottom End. -

-
- - - - diff --git a/elements/pf-v5-tooltip/demo/slotted-content.html b/elements/pf-v5-tooltip/demo/slotted-content.html deleted file mode 100644 index 220cfce0af..0000000000 --- a/elements/pf-v5-tooltip/demo/slotted-content.html +++ /dev/null @@ -1,42 +0,0 @@ -
-

Toggle Container Width

- - - -
- -
-

A tooltip may contain - - HTML content - - Slotted content can be formatted, using HTML - tags like em, strong, or code. - - by using the content slot. - Slotted content must be phrasing content, so no <p>s. -

-
- - - - diff --git a/elements/pf-v5-tooltip/demo/trigger.html b/elements/pf-v5-tooltip/demo/trigger.html deleted file mode 100644 index c5ae928272..0000000000 --- a/elements/pf-v5-tooltip/demo/trigger.html +++ /dev/null @@ -1,15 +0,0 @@ -
- - Button -
- - - - diff --git a/elements/pf-v5-tooltip/docs/CHANGELOG.old.md b/elements/pf-v5-tooltip/docs/CHANGELOG.old.md deleted file mode 100644 index 845280705c..0000000000 --- a/elements/pf-v5-tooltip/docs/CHANGELOG.old.md +++ /dev/null @@ -1,125 +0,0 @@ -# @patternfly/pfe-tooltip - -## 2.0.0-next.9 - -### Patch Changes - -- 457eaa9d0: `pfe-tools`: Set typescript compilerOptions `composite: true` - - `pfe-tooltip`: Added return type for anonymous function for content in constructor - -- Updated dependencies [82da44c11] - - @patternfly/pfe-core@2.0.0-next.14 - -## 2.0.0-next.8 - -### Major Changes - -- b841afe40: ``: - - - updated `FloatingDOMController` - - removed `offset` attribute - - added `content` attribute - - simplified DOM and CSS - -### Patch Changes - -- Updated dependencies [b841afe40] -- Updated dependencies [0fe6c52db] -- Updated dependencies [0fe6c52db] - - @patternfly/pfe-core@2.0.0-next.13 - -## 1.1.0-next.7 - -### Patch Changes - -- b51b551f: Use `NumberListConverter` from pfe-core -- Updated dependencies [6b6e2617] - - @patternfly/pfe-core@2.0.0-next.12 - -## 1.1.0-next.6 - -### Minor Changes - -- daba8a53: Adds styles to slotted timestamps. - - ```html - - - Last updated on - - - - - ``` - -## 1.1.0-next.5 - -### Patch Changes - -- 07ad1d3d: Updates use of `` -- Updated dependencies [07ad1d3d] - - @patternfly/pfe-core@2.0.0-next.10 - -## 1.1.0-next.4 - -### Minor Changes - -- 166ecee1: Improves performance of floating DOM (tooltip) by lazily initializing - -### Patch Changes - -- 0b90b899: Null check in tooltip's `show()` method -- Updated dependencies [166ecee1] - - @patternfly/pfe-core@2.0.0-next.9 - -## 1.1.0-next.3 - -### Patch Changes - -- bfad8b4b: Updates dependencies -- Updated dependencies [bfad8b4b] - - @patternfly/pfe-core@2.0.0-next.8 - -## 1.1.0-next.2 - -### Patch Changes - -- f25258e9: Updating the README.md, fixing an scss variable found in pfe-tooltip for the background color. - -## 1.1.0-next.1 - -### Patch Changes - -- b5fe1d3e: Use popper version from pfe-core - -## 1.1.0-next.0 - -### Minor Changes - -- 7c9b85cc: Adding `pfe-tooltip` - - ```html - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris - pharetra et ultrices. - - ``` - -### Patch Changes - -- Updated dependencies [7c9b85cc] - - @patternfly/pfe-core@2.0.0-next.7 diff --git a/elements/pf-v5-tooltip/docs/pf-v5-tooltip.md b/elements/pf-v5-tooltip/docs/pf-v5-tooltip.md deleted file mode 100644 index 2688f24a4b..0000000000 --- a/elements/pf-v5-tooltip/docs/pf-v5-tooltip.md +++ /dev/null @@ -1,106 +0,0 @@ -{% renderInstallation %} {% endrenderInstallation %} - -{% renderOverview %} - ### Default - - Tooltip - -{% endrenderOverview %} - -{% band header="Usage" %} - ### Left Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Left-Start Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Left-End Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Right Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Right-Start Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Right-End Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Top Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Top-Start Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Top-End Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Bottom Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Bottom-Start Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} - - ### Bottom-End Tooltip - {% htmlexample %} - - Tooltip - - {% endhtmlexample %} -{% endband %} - -{% renderSlots %}{% endrenderSlots %} - -{% renderAttributes %}{% endrenderAttributes %} - -{% renderMethods %}{% endrenderMethods %} - -{% renderEvents %}{% endrenderEvents %} - -{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} - -{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/pf-v5-tooltip/docs/screenshot.png b/elements/pf-v5-tooltip/docs/screenshot.png deleted file mode 100644 index 54986c4291e16f818dcc8768b808e47262f8c5e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66066 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 diff --git a/elements/pf-v5-tooltip/pf-v5-tooltip.css b/elements/pf-v5-tooltip/pf-v5-tooltip.css deleted file mode 100644 index 7fe60ddfdc..0000000000 --- a/elements/pf-v5-tooltip/pf-v5-tooltip.css +++ /dev/null @@ -1,108 +0,0 @@ -:host { - --_timestamp-text-decoration: underline dashed 1px; - --_timestamp-text-underline-offset: 4px; - display: inline; -} - -* { box-sizing: border-box; } - -#container { - display: inline-flex; - position: relative; - max-width: 100%; - /** Tooltip arrow width */ - --_floating-arrow-size: var(--pf-v5-c-tooltip__arrow--Width, 0.5rem); -} - -#tooltip, -#tooltip::after { - position: absolute; -} - -#tooltip { - --_timestamp-text-decoration: none; - --_timestamp-text-underline-offset: initial; - user-select: none; - display: block; - opacity: 0; - pointer-events: none; - z-index: 10000; - transition: opacity 300ms cubic-bezier(0.54, 1.5, 0.38, 1.11) 0s; - text-align: center; - word-break: break-word; - translate: var(--_floating-content-translate); - max-width: calc(100vw - 10px); - width: max-content; - top: 0; - left: 0; - will-change: opacity; - /** Sets the font color for the tooltip content */ - line-height: var(--pf-v5-c-tooltip--line-height, 1.5); - /** Maximum width for the tooltip */ - max-width: var(--pf-v5-c-tooltip--MaxWidth, 18.75rem); - /** Box shadow for the tooltip */ - box-shadow: var(--pf-v5-c-tooltip--BoxShadow, - var(--pf-global--BoxShadow--md, - 0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), - 0 0 0.25rem 0 rgba(3, 3, 3, 0.06))); - /** Tooltip padding (top, right, bottom, left) */ - padding: - var(--pf-v5-c-tooltip__content--PaddingTop, - var(--pf-global--spacer--sm, 0.5rem)) - var(--pf-v5-c-tooltip__content--PaddingRight, - var(--pf-global--spacer--sm, 0.5rem)) - var(--pf-v5-c-tooltip__content--PaddingBottom, - var(--pf-global--spacer--sm, 0.5rem)) - var(--pf-v5-c-tooltip__content--PaddingLeft, - var(--pf-global--spacer--sm, 0.5rem)); - /** Font size for the tooltip content */ - font-size: var(--pf-v5-c-tooltip__content--FontSize, - var(--pf-global--FontSize--sm, 0.875rem)); - /** Sets the font color for the tooltip content */ - color: var(--pf-v5-c-tooltip__content--Color, - var(--pf-global--Color--light-100, #ffffff)); - /** Sets the background color for the tooltip content */ - background-color: var(--pf-v5-c-tooltip__content--BackgroundColor, - var(--pf-global--BackgroundColor--dark-100, #151515)); -} - -#tooltip::after { - display: block; - content: ''; - rotate: 45deg; - width: var(--_floating-arrow-size); - height: var(--_floating-arrow-size); - will-change: left top right bottom; - background-color: var(--pf-v5-c-tooltip__content--BackgroundColor, - var(--pf-global--BackgroundColor--dark-100, #151515)); -} - -.open #tooltip { - opacity: 1; - user-select: initial; -} - -/* LEFT */ -.left #tooltip::after { right: calc(-0.5 * var(--_floating-arrow-size)); } -.left.center #tooltip::after { top: calc(50% - 0.5 * var(--_floating-arrow-size)); } -.left.start #tooltip::after { top: var(--_floating-arrow-size); } -.left.end #tooltip::after { bottom: var(--_floating-arrow-size); } - -/* TOP */ -.top #tooltip::after { top: calc(100% - 0.5 * var(--_floating-arrow-size)); } -.top.center #tooltip::after { right: calc(50% - 0.5 * var(--_floating-arrow-size)); } -.top.start #tooltip::after { left: var(--_floating-arrow-size); } -.top.end #tooltip::after { right: var(--_floating-arrow-size); } - -/* RIGHT */ -.right #tooltip::after { right: calc(100% - 0.5 * var(--_floating-arrow-size)); } -.right.center #tooltip::after { top: calc(50% - 0.5 * var(--_floating-arrow-size)); } -.right.start #tooltip::after { top: var(--_floating-arrow-size); } -.right.end #tooltip::after { bottom: var(--_floating-arrow-size); } - -/* BOTTOM */ -.bottom #tooltip::after { bottom: calc(100% - 0.5 * var(--_floating-arrow-size)); } -.bottom.center #tooltip::after { right: calc(50% - 0.5 * var(--_floating-arrow-size)); } -.bottom.start #tooltip::after { left: var(--_floating-arrow-size); } -.bottom.end #tooltip::after { right: var(--_floating-arrow-size); } - diff --git a/elements/pf-v5-tooltip/pf-v5-tooltip.ts b/elements/pf-v5-tooltip/pf-v5-tooltip.ts deleted file mode 100644 index 12f1682dba..0000000000 --- a/elements/pf-v5-tooltip/pf-v5-tooltip.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { PropertyValues, TemplateResult } from 'lit'; -import { LitElement, html, isServer } from 'lit'; -import { customElement } from 'lit/decorators/custom-element.js'; -import { property } from 'lit/decorators/property.js'; -import { styleMap } from 'lit/directives/style-map.js'; -import { classMap } from 'lit/directives/class-map.js'; - -import { - FloatingDOMController, - type Placement, -} from '@patternfly/pfe-core/controllers/floating-dom-controller.js'; - -import { bound } from '@patternfly/pfe-core/decorators/bound.js'; - -import { StringListConverter } from '@patternfly/pfe-core'; - -import styles from './pf-v5-tooltip.css'; - -const EnterEvents = ['focusin', 'tap', 'click', 'mouseenter']; -const ExitEvents = ['focusout', 'blur', 'mouseleave']; - -/** - * A **tooltip** is in-app messaging used to identify elements on a page with short, - * clarifying text. - * @summary Toggle the visibility of helpful or contextual information. - * @alias Tooltip - */ -@customElement('pf-v5-tooltip') -export class PfV5Tooltip extends LitElement { - static readonly styles: CSSStyleSheet[] = [styles]; - - /** The position of the tooltip, relative to the invoking content */ - @property() position: Placement = 'top'; - - /** Tooltip content. Overridden by the content slot */ - @property() content?: string; - - /** If false, prevents the tooltip from trying to remain in view by flipping itself when necessary */ - @property({ type: Boolean, attribute: 'no-flip' }) noFlip = false; - - @property() trigger?: string | Element; - - /** - * The flip order when flip is enabled and the initial position is not possible. - * There are 12 options: `top`, `bottom`, `left`, `right`, `top-start`, `top-end`, - * `bottom-start`, `bottom-end`, `left-start`, `left-end`,`right-start`, `right-end`. - * The default is [oppositePlacement], where only the opposite placement is tried. - */ - @property({ - attribute: 'flip-behavior', - converter: StringListConverter, - }) flipBehavior?: Placement[]; - - get #invoker(): HTMLSlotElement | null { - return this.shadowRoot?.querySelector?.('#invoker') ?? null; - } - - get #content(): HTMLElement | null { - return this.shadowRoot?.querySelector?.('#tooltip') ?? null; - } - - #referenceTrigger?: HTMLElement | null; - - #float = new FloatingDOMController(this, { - content: (): HTMLElement | null | undefined => this.#content, - invoker: (): HTMLElement | null | undefined => { - if (this.#referenceTrigger) { - return this.#referenceTrigger; - } else if (this.#invoker instanceof HTMLSlotElement - && this.#invoker.assignedElements().length > 0) { - return this.#invoker.assignedElements().at(0) as HTMLElement; - } else { - return this.#invoker; - } - }, - }); - - override connectedCallback(): void { - super.connectedCallback(); - this.#invokerChanged(); - this.#updateTrigger(); - } - - /** - * Removes event listeners from the old trigger element and attaches - * them to the new trigger element. - * @param changed changed properties - */ - override willUpdate(changed: PropertyValues): void { - if (changed.has('trigger')) { - this.#updateTrigger(); - } - } - - override render(): TemplateResult<1> { - const { alignment, anchor, open, styles } = this.#float; - - const blockInvoker = - this.#invoker?.assignedElements().length === 0 - && this.#invoker?.assignedNodes().length > 0; - const display = blockInvoker ? 'block' : 'contents'; - - return html` -
-
- - -
-
- - ${this.content} -
-
- `; - } - - /** the invoker slot should render at block level if it only has text nodes */ - #invokerChanged() { - this.requestUpdate(); - } - - #getReferenceTrigger() { - return (this.getRootNode() as Document | ShadowRoot) - .getElementById(this.trigger?.normalize() ?? ''); - } - - #updateTrigger() { - if (!isServer) { - const oldReferenceTrigger = this.#referenceTrigger; - this.#referenceTrigger = - this.trigger instanceof HTMLElement ? this.trigger - : typeof this.trigger === 'string' ? this.#getReferenceTrigger() - : null; - for (const evt of EnterEvents) { - if (this.#referenceTrigger) { - this.removeEventListener(evt, this.show); - this.#referenceTrigger.addEventListener(evt, this.show); - } else { - oldReferenceTrigger?.removeEventListener(evt, this.show); - this.addEventListener(evt, this.show); - } - } - for (const evt of ExitEvents) { - if (this.#referenceTrigger) { - this.removeEventListener(evt, this.hide); - this.#referenceTrigger.addEventListener(evt, this.hide); - } else { - oldReferenceTrigger?.removeEventListener(evt, this.hide); - this.addEventListener(evt, this.hide); - } - } - } - } - - @bound async show(): Promise { - await this.updateComplete; - const placement = this.position; - const offset = - !placement?.match(/top|bottom/) ? 15 - : { mainAxis: 15, alignmentAxis: -4 }; - await this.#float.show({ - offset, - placement, - flip: !this.noFlip, - fallbackPlacements: this.flipBehavior, - }); - } - - @bound async hide(): Promise { - await this.#float.hide(); - } -} - -declare global { - interface HTMLElementTagNameMap { - 'pf-v5-tooltip': PfV5Tooltip; - } -} diff --git a/elements/pf-v5-tooltip/test/pf-tooltip.e2e.ts b/elements/pf-v5-tooltip/test/pf-tooltip.e2e.ts deleted file mode 100644 index e51a80a7fb..0000000000 --- a/elements/pf-v5-tooltip/test/pf-tooltip.e2e.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { test } from '@playwright/test'; -import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; -import { SSRPage } from '@patternfly/pfe-tools/test/playwright/SSRPage.js'; - -const tagName = 'pf-v5-tooltip'; - -test.describe(tagName, () => { - test('snapshot', async ({ page }) => { - const componentPage = new PfeDemoPage(page, tagName); - await componentPage.navigate(); - await componentPage.snapshot(); - }); - - test('ssr', async ({ browser }) => { - const fixture = new SSRPage({ - tagName, - browser, - demoDir: new URL('../demo/', import.meta.url), - importSpecifiers: [ - `@patternfly/elements/${tagName}/${tagName}.js`, - ], - }); - await fixture.snapshots(); - }); -}); diff --git a/elements/pf-v5-tooltip/test/pf-tooltip.spec.ts b/elements/pf-v5-tooltip/test/pf-tooltip.spec.ts deleted file mode 100644 index 48482ce25b..0000000000 --- a/elements/pf-v5-tooltip/test/pf-tooltip.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { expect, html, fixture } from '@open-wc/testing'; - -import { PfV5Tooltip } from '../pf-v5-tooltip.js'; -import { setViewport, sendMouse } from '@web/test-runner-commands'; -import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js'; - -describe('', function() { - let element: PfV5Tooltip; - - beforeEach(async function() { - await setViewport({ width: 1000, height: 1000 }); - }); - - it('imperatively instantiates', function() { - expect(document.createElement('pf-v5-tooltip')).to.be.an.instanceof(PfV5Tooltip); - }); - - it('should upgrade', async function() { - element = await fixture(html``); - const klass = customElements.get('pf-v5-tooltip'); - expect(element) - .to.be.an.instanceOf(klass) - .and - .to.be.an.instanceOf(PfV5Tooltip); - }); - - describe('typical usage', function() { - beforeEach(async function() { - element = await fixture(html` - Tooltip - `); - }); - - it('should be accessible', async function() { - await expect(element).shadowDom.to.be.accessible(); - }); - - it('should hide tooltip content from assistive technology', async function() { - const snapshot = await a11ySnapshot(); - expect(snapshot).to.axContainName('Tooltip'); - expect(snapshot).to.not.axContainName('Content'); - }); - - describe('hovering the element', function() { - beforeEach(async function() { - const { x, y } = element.getBoundingClientRect(); - await sendMouse({ position: [x + 5, y + 5], type: 'move' }); - await element.updateComplete; - }); - it('should show tooltip content to assistive technology', async function() { - const snapshot = await a11ySnapshot(); - expect(snapshot).to.axContainName('Tooltip'); - expect(snapshot).to.axContainName('Content'); - }); - }); - }); -}); From 683110fac616017cb25a7e6f3e4e36bcb3780227 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Tue, 12 May 2026 10:26:32 +0300 Subject: [PATCH 3/3] fix: update v5 tooltip importers to use pf-v6-tooltip Assisted-By: Claude Opus 4.6 (1M context) --- elements/pf-v5-chip/demo/long-chip-with-tooltip.html | 6 +++--- elements/pf-v5-clipboard-copy/pf-v5-clipboard-copy.ts | 6 +++--- elements/pf-v5-icon/demo/sets.html | 2 +- elements/pf-v5-progress/pf-v5-progress.ts | 6 +++--- elements/pf-v5-timestamp/demo/custom-tooltip.html | 10 +++++----- .../demo/relative-format-with-tooltip.html | 10 +++++----- elements/pf-v5-timestamp/demo/tooltip.html | 10 +++++----- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/elements/pf-v5-chip/demo/long-chip-with-tooltip.html b/elements/pf-v5-chip/demo/long-chip-with-tooltip.html index 03ebabe000..9c1c49331e 100644 --- a/elements/pf-v5-chip/demo/long-chip-with-tooltip.html +++ b/elements/pf-v5-chip/demo/long-chip-with-tooltip.html @@ -1,7 +1,7 @@ - + Really long chip that goes on and on Really long chip that goes on and on - +