diff --git a/1st-gen/packages/tooltip/test/tooltip.test.ts b/1st-gen/packages/tooltip/test/tooltip.test.ts index 4d16ebbda50..932f54bfec9 100644 --- a/1st-gen/packages/tooltip/test/tooltip.test.ts +++ b/1st-gen/packages/tooltip/test/tooltip.test.ts @@ -271,7 +271,7 @@ describe('Tooltip', () => { consoleWarnStub.restore(); }); - it('warns when incorrectly using `self-managed`', async () => { + it('warns when using the deprecated `self-managed` attribute', async () => { const el = await fixture(html` Help text. `); @@ -281,8 +281,8 @@ describe('Tooltip', () => { expect(consoleWarnStub.called).to.be.true; const spyCall = consoleWarnStub.getCall(0); expect( - (spyCall.args[0] as string).includes('Self-managed'), - 'confirm dev warning message includes `Self-managed`' + (spyCall.args[0] as string).includes('self-managed'), + 'confirm dev warning message includes `self-managed`' ).to.be.true; expect( spyCall.args[spyCall.args.length - 1], @@ -291,7 +291,7 @@ describe('Tooltip', () => { data: { localName: 'sp-tooltip', type: 'api', - level: 'high', + level: 'deprecation', }, }); }); diff --git a/2nd-gen/packages/core/components/tooltip/Tooltip.base.ts b/2nd-gen/packages/core/components/tooltip/Tooltip.base.ts index 75c4d9a0165..3cfd9434819 100644 --- a/2nd-gen/packages/core/components/tooltip/Tooltip.base.ts +++ b/2nd-gen/packages/core/components/tooltip/Tooltip.base.ts @@ -147,9 +147,9 @@ export abstract class TooltipBase extends SpectrumElement { /** * When set, wires `ariaLabelledByElements` instead of `ariaDescribedByElements` on the trigger's - * inner interactive element. For icon-only triggers where the tooltip text is the sole accessible name. - * - * Additive/deferred: active in the additive phase. + * inner interactive element. Use for icon-only triggers where the tooltip text is the sole accessible + * name and adding an accessible label directly to the trigger host is not possible. + * Prefer `accessible-label` on the trigger when feasible — it works without this attribute. * * @default false */ @@ -195,11 +195,28 @@ export abstract class TooltipBase extends SpectrumElement { const target = (trigger.shadowRoot?.querySelector('button') ?? trigger) as Element & { ariaDescribedByElements: Element[] | null; + ariaLabelledByElements: Element[] | null; }; - const current = target.ariaDescribedByElements ?? []; - target.ariaDescribedByElements = this.open - ? [...current.filter((el) => el !== this), this] - : current.filter((el) => el !== this); + + if (this.labeling) { + // Remove any stale describedby reference (e.g. if labeling changed while open). + const described = target.ariaDescribedByElements ?? []; + target.ariaDescribedByElements = described.filter((el) => el !== this); + + const labelled = target.ariaLabelledByElements ?? []; + target.ariaLabelledByElements = this.open + ? [...labelled.filter((el) => el !== this), this] + : labelled.filter((el) => el !== this); + } else { + // Remove any stale labelledby reference (e.g. if labeling changed while open). + const labelled = target.ariaLabelledByElements ?? []; + target.ariaLabelledByElements = labelled.filter((el) => el !== this); + + const described = target.ariaDescribedByElements ?? []; + target.ariaDescribedByElements = this.open + ? [...described.filter((el) => el !== this), this] + : described.filter((el) => el !== this); + } } private dispatchAfterEvent(isOpen: boolean): void { @@ -234,7 +251,10 @@ export abstract class TooltipBase extends SpectrumElement { this.open = isOpen; } // When no CSS transition is active, dispatch after-* immediately since transitionend will not fire. - if (getComputedStyle(this).transitionDuration === '0s') { + // transitionDuration is comma-separated when multiple properties transition ("0s, 0s, …"), + // so check that every value in the list is zero rather than comparing the full string. + const durations = getComputedStyle(this).transitionDuration.split(','); + if (durations.every((d) => d.trim() === '0s')) { this.afterEventPending = false; this.dispatchAfterEvent(isOpen); } @@ -248,6 +268,13 @@ export abstract class TooltipBase extends SpectrumElement { this.dispatchAfterEvent(this.open); }; + // Allows Escape behavior to be testable, does not interfere with native popover dismissal + private readonly handleKeyDown = (event: KeyboardEvent): void => { + if (event.key === 'Escape' && this.open) { + this.open = false; + } + }; + protected override updated(changedProperties: PropertyValues): void { super.updated(changedProperties); if (changedProperties.has('open')) { @@ -258,6 +285,8 @@ export abstract class TooltipBase extends SpectrumElement { this.hidePopover(); } } + } + if (changedProperties.has('open') || changedProperties.has('labeling')) { this.syncAriaRelationship(); } } @@ -269,6 +298,7 @@ export abstract class TooltipBase extends SpectrumElement { this.addEventListener('beforetoggle', this.handleBeforeToggle); this.addEventListener('toggle', this.handleToggle); this.addEventListener('transitionend', this.handleTransitionEnd); + document.addEventListener('keydown', this.handleKeyDown); } public override disconnectedCallback(): void { @@ -276,5 +306,6 @@ export abstract class TooltipBase extends SpectrumElement { this.removeEventListener('beforetoggle', this.handleBeforeToggle); this.removeEventListener('toggle', this.handleToggle); this.removeEventListener('transitionend', this.handleTransitionEnd); + document.removeEventListener('keydown', this.handleKeyDown); } } diff --git a/2nd-gen/packages/core/global.d.ts b/2nd-gen/packages/core/global.d.ts index 6857cb7baea..60e16761795 100644 --- a/2nd-gen/packages/core/global.d.ts +++ b/2nd-gen/packages/core/global.d.ts @@ -20,6 +20,15 @@ type SWCWarningOptions = { }; type BrandedSWCWarningID = `${ElementLocalName}:${WarningType}:${WarningLevel}`; +/** + * ARIA element-reflection properties (IDRef-free associations). Not yet in the + * TypeScript DOM lib. + */ +interface ARIAMixin { + ariaDescribedByElements: readonly Element[] | null; + ariaLabelledByElements: readonly Element[] | null; +} + interface Window { __swc: { DEBUG: boolean; diff --git a/2nd-gen/packages/swc/.storybook/preview.ts b/2nd-gen/packages/swc/.storybook/preview.ts index 49abc731597..a52fb42a930 100644 --- a/2nd-gen/packages/swc/.storybook/preview.ts +++ b/2nd-gen/packages/swc/.storybook/preview.ts @@ -248,6 +248,7 @@ const preview = { 'Tools vs packages', 'Writing migration guides', 'Focus management', + 'Changelog strategy', ], 'Style guide', [ @@ -390,6 +391,11 @@ const preview = { ['Rendering and styling migration analysis'], 'Field label', ['Rendering and styling migration analysis'], + 'Grid', + [ + 'Accessibility migration analysis', + 'Rendering and styling migration analysis', + ], 'Help text', ['Rendering and styling migration analysis'], 'Illustrated message', @@ -436,6 +442,7 @@ const preview = { 'Popover', [ 'Accessibility migration analysis', + 'Migration plan', 'Rendering and styling migration analysis', ], 'Progress bar', diff --git a/2nd-gen/packages/swc/components/tooltip/Tooltip.ts b/2nd-gen/packages/swc/components/tooltip/Tooltip.ts index e9117f7c57d..9136027c9d1 100644 --- a/2nd-gen/packages/swc/components/tooltip/Tooltip.ts +++ b/2nd-gen/packages/swc/components/tooltip/Tooltip.ts @@ -28,6 +28,11 @@ import styles from './tooltip.css'; * @slot - Text label displayed in the tooltip. * * @cssprop --swc-tooltip-background-color - Background color of the tooltip bubble. Defaults to the neutral background color token. + * + * @fires swc-open - Dispatched when the tooltip begins to open, before the transition plays. + * @fires swc-close - Dispatched when the tooltip begins to close, before the transition plays. + * @fires swc-after-open - Dispatched after the tooltip finishes opening, once the transition completes. + * @fires swc-after-close - Dispatched after the tooltip finishes closing, once the transition completes. */ export class Tooltip extends TooltipBase { // ────────────────────────────── diff --git a/2nd-gen/packages/swc/components/tooltip/stories/tooltip.stories.ts b/2nd-gen/packages/swc/components/tooltip/stories/tooltip.stories.ts index ee33eb024e6..ef4da40b866 100644 --- a/2nd-gen/packages/swc/components/tooltip/stories/tooltip.stories.ts +++ b/2nd-gen/packages/swc/components/tooltip/stories/tooltip.stories.ts @@ -27,9 +27,9 @@ import { import '@adobe/spectrum-wc/components/button/swc-button.js'; import '@adobe/spectrum-wc/components/tooltip/swc-tooltip.js'; -// ──────────────────── -// METADATA SETUP -// ──────────────────── +// ──────────────── +// METADATA +// ──────────────── const { args, argTypes, template } = getStorybookHelpers('swc-tooltip'); @@ -126,7 +126,6 @@ const makeToggle = (id: string) => (event: MouseEvent) => { return; } - setupEventLogger(tooltip); tooltip.open = !tooltip.open; if (tooltip.open) { @@ -143,38 +142,42 @@ const makeToggle = (id: string) => (event: MouseEvent) => { } }; -// Temporary: logs tooltip lifecycle events to the console to verify event wiring. -// Replace with proper assertions in migration-testing (Phase 6). -// Storybook's Actions addon doesn't work well for this since the events are re-dispatched -// from the popover in the top layer, so we log directly from the component instance instead. -const loggedTooltips = new WeakSet(); -const setupEventLogger = (tooltip: Element): void => { - if (loggedTooltips.has(tooltip)) { - return; - } - loggedTooltips.add(tooltip); - for (const name of [ - 'swc-open', - 'swc-close', - 'swc-after-open', - 'swc-after-close', - ]) { - tooltip.addEventListener(name, () => { - console.log(`[swc-tooltip] ${name}`); - }); - } -}; - // Renders a button+tooltip pair linked via the `for` attribute. // Each pair needs a unique `id` so multiple instances can coexist in the same story. const triggered = ( tooltipArgs: Record, id: string, - buttonLabel: string -) => html` - ${buttonLabel} - ${template({ ...tooltipArgs, for: id })} -`; + buttonLabel?: string, + iconOnly: boolean = false +) => { + if (!iconOnly) { + return html` + ${buttonLabel} + ${template({ ...tooltipArgs, for: id })} + `; + } else { + return html` + + + + ${template({ ...tooltipArgs, for: id })} + `; + } +}; /** * Each story renders one or more buttons that trigger associated tooltips when clicked. @@ -184,6 +187,9 @@ const meta: Meta = { title: 'Tooltip', component: 'swc-tooltip', parameters: { + actions: { + handles: ['swc-open', 'swc-close', 'swc-after-open', 'swc-after-close'], + }, docs: { subtitle: `Brief contextual message that appears near a trigger element.`, }, @@ -344,6 +350,37 @@ export const Placements: Story = { // TODO: will complete in separate documentation pass of phase 7 +// ────────────────────────────── +// BEHAVIORS STORIES +// ────────────────────────────── + +/** + * When a trigger has no visible text label and the tooltip text is its sole accessible name, + * set the `labeling` attribute on the tooltip. This switches the ARIA wiring from + * `ariaDescribedByElements` (supplementary description) to `ariaLabelledByElements` (accessible name) + * on the trigger's inner interactive element. + */ +export const Labeling: Story = { + render: (args) => html` + ${triggered( + { + ...args, + labeling: true, + 'default-slot': 'Save changes', + }, + 'tooltip-labeling-trigger', + undefined, + true + )} + `, + args: { + placement: 'top', + variant: 'neutral', + }, + tags: ['behaviors'], + parameters: { 'section-order': 1 }, +}; + // ──────────────────────────────── // ACCESSIBILITY STORIES // ──────────────────────────────── diff --git a/2nd-gen/packages/swc/components/tooltip/test/.gitkeep b/2nd-gen/packages/swc/components/tooltip/test/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/2nd-gen/packages/swc/components/tooltip/test/tooltip.a11y.spec.ts b/2nd-gen/packages/swc/components/tooltip/test/tooltip.a11y.spec.ts new file mode 100644 index 00000000000..0eb6157de73 --- /dev/null +++ b/2nd-gen/packages/swc/components/tooltip/test/tooltip.a11y.spec.ts @@ -0,0 +1,158 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { expect, test } from '@playwright/test'; + +import { gotoStory } from '../../../utils/a11y-helpers.js'; + +/** + * Accessibility tests for Tooltip component (2nd Generation) + * + * ARIA snapshot tests validate the accessibility tree structure. + * aXe WCAG compliance and color contrast validation are run via + * test-storybook (see .storybook/test-runner.ts). Both are included + * in the `test:a11y` command. + */ + +test.describe('Tooltip - ARIA Snapshots', () => { + test('closed tooltip is hidden from accessibility tree', async ({ page }) => { + const root = await gotoStory( + page, + 'components-tooltip--overview', + 'swc-button' + ); + // The trigger button is accessible; the closed popover is hidden from the tree. + await expect(root).toMatchAriaSnapshot(` + - button "Open" + `); + }); + + test('open tooltip exposes role="tooltip" in accessibility tree', async ({ + page, + }) => { + await gotoStory(page, 'components-tooltip--overview', 'swc-button'); + + // Open the tooltip programmatically (HoverController not yet wired). + await page.evaluate(() => { + const tooltip = document.querySelector('swc-tooltip') as HTMLElement & { + open: boolean; + }; + if (tooltip) { + tooltip.open = true; + } + }); + + // Wait for the popover to appear in the top layer. + await page.waitForFunction(() => + document.querySelector('swc-tooltip')?.matches(':popover-open') + ); + + const root = page.locator('#storybook-root'); + await expect(root).toMatchAriaSnapshot(` + - button "Open" + - tooltip "Save your changes" + `); + }); + + test('tooltip is removed from accessibility tree when closed', async ({ + page, + }) => { + await gotoStory(page, 'components-tooltip--overview', 'swc-button'); + + // Open then close via the property. + await page.evaluate(() => { + const tooltip = document.querySelector('swc-tooltip') as HTMLElement & { + open: boolean; + }; + if (tooltip) { + tooltip.open = true; + } + }); + await page.waitForFunction(() => + document.querySelector('swc-tooltip')?.matches(':popover-open') + ); + + await page.evaluate(() => { + const tooltip = document.querySelector('swc-tooltip') as HTMLElement & { + open: boolean; + }; + if (tooltip) { + tooltip.open = false; + } + }); + await page.waitForFunction( + () => !document.querySelector('swc-tooltip')?.matches(':popover-open') + ); + + const root = page.locator('#storybook-root'); + await expect(root).toMatchAriaSnapshot(` + - button "Open" + `); + }); + + test('Escape closes an open tooltip', async ({ page }) => { + await gotoStory(page, 'components-tooltip--overview', 'swc-button'); + + await page.evaluate(() => { + const tooltip = document.querySelector('swc-tooltip') as HTMLElement & { + open: boolean; + }; + if (tooltip) { + tooltip.open = true; + } + }); + await page.waitForFunction(() => + document.querySelector('swc-tooltip')?.matches(':popover-open') + ); + + await page.keyboard.press('Escape'); + + await page.waitForFunction( + () => !document.querySelector('swc-tooltip')?.matches(':popover-open') + ); + + const open = await page.evaluate( + () => (document.querySelector('swc-tooltip') as { open?: boolean })?.open + ); + expect(open, 'tooltip.open is false after Escape').toBe(false); + }); + + test('all variant triggers are accessible', async ({ page }) => { + const root = await gotoStory( + page, + 'components-tooltip--variants', + 'swc-button' + ); + await expect(root).toMatchAriaSnapshot(` + - button "Save" + - button "Upload" + - button "Delete" + `); + }); + + test('all placement triggers are accessible', async ({ page }) => { + const root = await gotoStory( + page, + 'components-tooltip--placements', + 'swc-button' + ); + // Each placement renders a separate trigger button. + await expect(root).toMatchAriaSnapshot(` + - button "top" + - button "right" + - button "end" + - button "bottom" + - button "left" + - button "start" + `); + }); +}); diff --git a/2nd-gen/packages/swc/components/tooltip/test/tooltip.test.ts b/2nd-gen/packages/swc/components/tooltip/test/tooltip.test.ts new file mode 100644 index 00000000000..cbd69ce7959 --- /dev/null +++ b/2nd-gen/packages/swc/components/tooltip/test/tooltip.test.ts @@ -0,0 +1,631 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import { expect, userEvent } from '@storybook/test'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import type { Button } from '@adobe/spectrum-wc/button'; +import { Tooltip } from '@adobe/spectrum-wc/tooltip'; + +import '@adobe/spectrum-wc/components/button/swc-button.js'; +import '@adobe/spectrum-wc/components/tooltip/swc-tooltip.js'; + +import { + TOOLTIP_PLACEMENTS, + TOOLTIP_VARIANTS, +} from '../../../../core/components/tooltip/Tooltip.types.js'; +import { + getComponent, + getComponents, + withWarningSpy, +} from '../../../utils/test-utils.js'; +import meta, { + Overview, + Placements, + Variants, +} from '../stories/tooltip.stories.js'; + +// This file defines dev-only test stories that reuse the main story metadata. +export default { + ...meta, + title: 'Tooltip/Tests', + parameters: { + ...meta.parameters, + docs: { disable: true, page: null }, + }, + tags: ['!autodocs', 'dev'], +} as Meta; + +// Awaits a DOM event dispatched on the given element, resolving with the event object. +const waitForEvent = ( + el: EventTarget, + eventName: string +): Promise => + new Promise((resolve) => { + el.addEventListener(eventName, (event) => resolve(event as T), { + once: true, + }); + }); + +// ────────────────────────────────────────────────────────────── +// TEST: Defaults +// ────────────────────────────────────────────────────────────── + +export const OverviewTest: Story = { + ...Overview, + play: async ({ canvasElement, step }) => { + const tooltip = await getComponent(canvasElement, 'swc-tooltip'); + + await step('renders expected default property values', async () => { + expect(tooltip.variant, 'default variant is neutral').toBe('neutral'); + expect(tooltip.placement, 'default placement is top').toBe('top'); + expect(tooltip.open, 'default open is false').toBe(false); + expect(tooltip.manual, 'default manual is false').toBe(false); + expect(tooltip.delay, 'default delay is 1500').toBe(1500); + }); + + await step('sets role="tooltip" on the host element', async () => { + expect(tooltip.getAttribute('role'), 'host has role="tooltip"').toBe( + 'tooltip' + ); + }); + + await step('sets popover="auto" on the host element', async () => { + expect(tooltip.getAttribute('popover'), 'host has popover="auto"').toBe( + 'auto' + ); + }); + + await step('renders text content in default slot', async () => { + expect( + tooltip.textContent?.trim(), + 'default slot has text content' + ).toBeTruthy(); + }); + }, +}; + +// ────────────────────────────────────────────────────────────── +// TEST: Properties / Attributes +// ────────────────────────────────────────────────────────────── + +export const PropertyMutationTest: Story = { + ...Overview, + play: async ({ canvasElement, step }) => { + const tooltip = await getComponent(canvasElement, 'swc-tooltip'); + + await step('reflects variant attribute after mutation', async () => { + tooltip.variant = 'informative'; + await tooltip.updateComplete; + expect( + tooltip.getAttribute('variant'), + 'variant attribute is informative after mutation' + ).toBe('informative'); + + tooltip.variant = 'negative'; + await tooltip.updateComplete; + expect( + tooltip.getAttribute('variant'), + 'variant attribute is negative after second mutation' + ).toBe('negative'); + + tooltip.variant = 'neutral'; + await tooltip.updateComplete; + expect( + tooltip.getAttribute('variant'), + 'variant attribute is neutral after third mutation' + ).toBe('neutral'); + }); + + await step('reflects placement attribute after mutation', async () => { + tooltip.placement = 'bottom'; + await tooltip.updateComplete; + expect( + tooltip.getAttribute('placement'), + 'placement attribute is bottom after mutation' + ).toBe('bottom'); + + tooltip.placement = 'start'; + await tooltip.updateComplete; + expect( + tooltip.getAttribute('placement'), + 'placement attribute is start after mutation' + ).toBe('start'); + }); + }, +}; + +export const OpenCloseTest: Story = { + render: () => html` + Toggle + + Tooltip text + + `, + play: async ({ canvasElement, step }) => { + const tooltip = await getComponent(canvasElement, 'swc-tooltip'); + + await step( + 'reflects [open] attribute when open is set to true', + async () => { + tooltip.open = true; + await tooltip.updateComplete; + expect( + tooltip.hasAttribute('open'), + 'open attribute is present when open=true' + ).toBe(true); + } + ); + + await step( + 'removes [open] attribute when open is set to false', + async () => { + tooltip.open = false; + await tooltip.updateComplete; + expect( + tooltip.hasAttribute('open'), + 'open attribute is absent when open=false' + ).toBe(false); + } + ); + }, +}; + +export const LifecycleEventsTest: Story = { + render: () => html` + Open + + Tooltip content + + `, + play: async ({ canvasElement, step }) => { + const tooltip = await getComponent(canvasElement, 'swc-tooltip'); + + await step('fires swc-open when tooltip is opened', async () => { + const openPromise = waitForEvent(tooltip, 'swc-open'); + tooltip.open = true; + await openPromise; + tooltip.open = false; + await tooltip.updateComplete; + }); + + await step('fires swc-close when tooltip is closed', async () => { + tooltip.open = true; + await tooltip.updateComplete; + + const closePromise = waitForEvent(tooltip, 'swc-close'); + tooltip.open = false; + await closePromise; + }); + + await step( + 'fires swc-after-open after the tooltip transition ends', + async () => { + const afterOpenPromise = waitForEvent(tooltip, 'swc-after-open'); + tooltip.open = true; + await afterOpenPromise; + tooltip.open = false; + await tooltip.updateComplete; + } + ); + + await step( + 'fires swc-after-close after the tooltip transition ends', + async () => { + tooltip.open = true; + await tooltip.updateComplete; + + const afterClosePromise = waitForEvent(tooltip, 'swc-after-close'); + tooltip.open = false; + await afterClosePromise; + } + ); + + await step('dispatches events that bubble and are composed', async () => { + let openBubbled = false; + let closeBubbled = false; + canvasElement.addEventListener( + 'swc-open', + () => { + openBubbled = true; + }, + { once: true } + ); + canvasElement.addEventListener( + 'swc-close', + () => { + closeBubbled = true; + }, + { once: true } + ); + + tooltip.open = true; + await waitForEvent(tooltip, 'swc-open'); + expect(openBubbled, 'swc-open bubbled to canvas').toBe(true); + + tooltip.open = false; + await waitForEvent(tooltip, 'swc-close'); + expect(closeBubbled, 'swc-close bubbled to canvas').toBe(true); + + await tooltip.updateComplete; + }); + }, +}; + +export const AriaWiringNativeTest: Story = { + render: () => html` + + + Changes will be saved + + `, + play: async ({ canvasElement, step }) => { + const tooltip = await getComponent(canvasElement, 'swc-tooltip'); + const trigger = canvasElement.querySelector('#tt-native-trigger')!; + + await step( + 'sets ariaDescribedByElements on a native trigger when open is true', + async () => { + tooltip.open = true; + await tooltip.updateComplete; + + const elements = trigger.ariaDescribedByElements ?? []; + expect(elements, 'ariaDescribedByElements contains tooltip').toContain( + tooltip + ); + } + ); + + await step( + 'removes ariaDescribedByElements from a native trigger when open is false', + async () => { + tooltip.open = false; + await tooltip.updateComplete; + + const elements = trigger.ariaDescribedByElements ?? []; + expect( + elements, + 'ariaDescribedByElements no longer contains tooltip' + ).not.toContain(tooltip); + } + ); + }, +}; + +export const AriaWiringSwcTriggerTest: Story = { + render: () => html` + Save + + Changes will be saved + + `, + play: async ({ canvasElement, step }) => { + const swcButton = canvasElement.querySelector('#tt-swc-trigger') as Button; + await swcButton.updateComplete; + + const tooltip = await getComponent(canvasElement, 'swc-tooltip'); + const innerButton = swcButton.shadowRoot?.querySelector('button') ?? null; + + await step( + 'wires ariaDescribedByElements on the shadow