Skip to content

Add Tooltip stories for Links and Rich Text Content#2995

Open
marcysutton wants to merge 5 commits intomainfrom
tooltip-link-story
Open

Add Tooltip stories for Links and Rich Text Content#2995
marcysutton wants to merge 5 commits intomainfrom
tooltip-link-story

Conversation

@marcysutton
Copy link
Copy Markdown
Member

Summary:

We got a question in Slack about whether the Tooltip component supports HTML content, which it does. I noticed there wasn't a story showing this working, so I added one. The apparent consumer code also had incorrect structuring with a Link, and I noticed there wasn't a Link example either. https://khanacademy.slack.com/archives/C8Z9DSKC7/p1775096784762589

Issue: WB-XXXX

Test plan:

  1. Review new stories:
    • /?path=/story/packages-tooltip-tooltip--with-link-anchor
    • /?path=/story/packages-tooltip-tooltip--with-rich-text-content
    • /?path=/story/packages-tooltip-tooltipcontent--rich-text-content

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 2, 2026

🦋 Changeset detected

Latest commit: 0fccf04

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@khan-actions-bot khan-actions-bot requested a review from a team April 2, 2026 17:37
@khan-actions-bot
Copy link
Copy Markdown
Contributor

khan-actions-bot commented Apr 2, 2026

Gerald

Required Reviewers
  • @Khan/wonder-blocks for changes to .changeset/eight-hairs-crash.md, __docs__/wonder-blocks-tooltip/tooltip-content.stories.tsx, __docs__/wonder-blocks-tooltip/tooltip.argtypes.ts, __docs__/wonder-blocks-tooltip/tooltip.stories.tsx, packages/wonder-blocks-tooltip/src/components/tooltip.tsx

Don't want to be involved in this pull request? Comment #removeme and we won't notify you of further changes.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

npm Snapshot: NOT Published

🤕 Oh noes!! We couldn't find any changesets in this PR (98cf24c). As a result, we did not publish an npm snapshot for you.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Size Change: 0 B

Total Size: 121 kB

ℹ️ View Unchanged
Filename Size
packages/wonder-blocks-accordion/dist/es/index.js 3 kB
packages/wonder-blocks-announcer/dist/es/index.js 2.43 kB
packages/wonder-blocks-badge/dist/es/index.js 2.02 kB
packages/wonder-blocks-banner/dist/es/index.js 2.01 kB
packages/wonder-blocks-birthday-picker/dist/es/index.js 1.91 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 755 B
packages/wonder-blocks-button/dist/es/index.js 4.28 kB
packages/wonder-blocks-card/dist/es/index.js 1.08 kB
packages/wonder-blocks-cell/dist/es/index.js 2.18 kB
packages/wonder-blocks-clickable/dist/es/index.js 2.6 kB
packages/wonder-blocks-core/dist/es/index.js 2.59 kB
packages/wonder-blocks-data/dist/es/index.js 5.48 kB
packages/wonder-blocks-date-picker/dist/es/index.js 8.06 kB
packages/wonder-blocks-dropdown/dist/es/index.js 19.7 kB
packages/wonder-blocks-form/dist/es/index.js 6.3 kB
packages/wonder-blocks-grid/dist/es/index.js 1.24 kB
packages/wonder-blocks-icon-button/dist/es/index.js 4.01 kB
packages/wonder-blocks-icon/dist/es/index.js 1.91 kB
packages/wonder-blocks-labeled-field/dist/es/index.js 3.47 kB
packages/wonder-blocks-layout/dist/es/index.js 1.63 kB
packages/wonder-blocks-link/dist/es/index.js 1.53 kB
packages/wonder-blocks-modal/dist/es/index.js 7.36 kB
packages/wonder-blocks-pill/dist/es/index.js 1.31 kB
packages/wonder-blocks-popover/dist/es/index.js 4.32 kB
packages/wonder-blocks-progress-spinner/dist/es/index.js 1.48 kB
packages/wonder-blocks-search-field/dist/es/index.js 1.1 kB
packages/wonder-blocks-styles/dist/es/index.js 464 B
packages/wonder-blocks-switch/dist/es/index.js 1.55 kB
packages/wonder-blocks-tabs/dist/es/index.js 5.57 kB
packages/wonder-blocks-testing-core/dist/es/index.js 3.25 kB
packages/wonder-blocks-testing/dist/es/index.js 978 B
packages/wonder-blocks-theming/dist/es/index.js 384 B
packages/wonder-blocks-timing/dist/es/index.js 1.37 kB
packages/wonder-blocks-tokens/dist/es/index.js 5.18 kB
packages/wonder-blocks-toolbar/dist/es/index.js 906 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.02 kB
packages/wonder-blocks-typography/dist/es/index.js 1.57 kB

compressed-size-action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

A new build was pushed to Chromatic! 🚀

https://5e1bf4b385e3fb0020b7073c-doeeyvhjpy.chromatic.com/

Chromatic results:

Metric Total
Captured snapshots 31
Tests with visual changes 0
Total stories 834
Inherited (not captured) snapshots [TurboSnap] 430
Tests on the build 461

@jandrade
Copy link
Copy Markdown
Member

jandrade commented Apr 2, 2026

@claude review once

Copy link
Copy Markdown
Member

@beaesguerra beaesguerra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the docs, great idea :)

I left some questions! One thing I wanted to verify is if TooltipContent should be in the example!

/**
* To render rich text in tooltip content, pass a React element as `children`
* instead of a plain string. When a string is passed, it is wrapped in
* `LabelMedium` and rendered as plain text — HTML tags in a string will appear
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment can be updated!

Suggested change
* `LabelMedium` and rendered as plain text HTML tags in a string will appear
* `BodyText` and rendered as plain text HTML tags in a string will appear


/**
* Tooltips can be used with links as anchors.
* When a `Link` is the anchor element, set `forceAnchorFocusivity={false}`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL about forceAnchorFocusivity!

I noticed that the docs for the prop isn't showing up in the SB docs! Could you update that as well?

Image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Focusivity isn't exactly a word but it works! (I thought it would be "forceAnchorFocusability" when trying to write from memory)

Updated the argtype.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I invented that term a long time ago. Focusability would probably have been a better choice - I can't tell you where my brain was, naming things is hard.

render: function Render() {
return (
<Tooltip
content={
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if content needs to be wrapped with TooltipContent when passing in jsx! I see the type definition for the prop includes TooltipContent:

content:
| string
| React.ReactElement<React.ComponentProps<typeof TooltipContent>>;

It wasn't immediately clear to me in the docs how TooltipContent should be used, so I am glad we're adding more examples :)

cc: @jandrade in case you have context on this!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it this way to mimic the production code, so we could validate it works with the setup in frontend. It seems to work, so maybe the type definition isn't quite right?

Copy link
Copy Markdown
Member

@jandrade jandrade Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh hmmm I think this is another case where the Flow to TS migration couldn't add full type safety support for these cases with TooltipContent. I mean, probably we were expecting to wrap components with TooltipContent, but TS is not able to fully enforce that.

<View style={styles.scrollbox}>
<View style={styles.hostbox}>
<Body>
<BodyText>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating typography components! :)

Comment on lines 169 to +190
},
};

/**
* Tooltips can be used with links as anchors.
* When a `Link` is the anchor element, set `forceAnchorFocusivity={false}`
* since the link is already keyboard focusable. The tooltip will appear on
* hover or focus and the `aria-describedby` attribute is automatically applied
* to the `Link` element.
*/
export const WithLinkAnchor: StoryComponentType = {
render: function Render() {
return (
<Tooltip
content="This link navigates to the Khan Academy homepage."
placement="top"
forceAnchorFocusivity={false}
>
<Link href="https://www.khanacademy.org">Khan Academy</Link>
</Tooltip>
);
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The WithLinkAnchor story (lines 169-190) will capture only the link anchor in Chromatic snapshots — the tooltip bubble is never visible because there is no opened={true}, no play function, and no chromatic: { disableSnapshot: true }. This wastes a monthly snapshot and does not demonstrate the tooltip behavior the story is meant to showcase. Fix by adding opened={true} (like WithRichTextContent does in this same PR), a play function to trigger hover (like Default does), or parameters: { chromatic: { disableSnapshot: true } } if visual regression is not needed.

Extended reasoning...

What the bug is and how it manifests

The WithLinkAnchor story renders a Tooltip around a Link component with no mechanism to make the tooltip visible at snapshot time. Chromatic captures a static screenshot of the story in its initial state. Since tooltips only appear on hover or focus, and neither is simulated here, Chromatic will record only the plain "Khan Academy" link — not the tooltip bubble.

The specific code path that triggers it

In tooltip.stories.tsx, the WithLinkAnchor story (around line 169) renders:

<Tooltip
    content="This link navigates to the Khan Academy homepage."
    placement="top"
    forceAnchorFocusivity={false}
>
    <Link href="https://www.khanacademy.org">Khan Academy</Link>
</Tooltip>

There is no opened={true} prop, no play function, and no parameters.chromatic.disableSnapshot. Chromatic captures this story in its default resting state.

Why existing code doesn't prevent it

Every other story in this file that needs the tooltip bubble visible uses one of three strategies: (1) opened={true} — used by WithRichTextContent, WithStyle, Controlled, and AutoUpdate; (2) a play function that simulates hover — used by Default and ComplexAnchorAndTitle; or (3) chromatic: { disableSnapshot: true } — used by AnchorInScrollableParent, SideBySide, TooltipInModal, TooltipOnButtons, InTopCorner, and InCorners. WithLinkAnchor falls into none of these categories. Critically, WithRichTextContent — the companion story added in this very same PR — correctly uses opened={true}, making the inconsistency visible within the diff itself.

Impact

CLAUDE.md (line 244) explicitly states: "Disable Chromatic for stories that don't need visual regression tests (limited monthly snapshots)." A snapshot that captures only a bare link element with no tooltip visible provides no visual regression value and burns a monthly snapshot. The story's own JSDoc says it demonstrates "Tooltips can be used with links as anchors", but the captured snapshot shows no tooltip at all.

Step-by-step proof

  1. Chromatic loads the WithLinkAnchor story in a headless browser.
  2. The story renders: a Tooltip (hidden by default) wrapping a Link ("Khan Academy").
  3. No hover or focus event is fired; no opened prop forces the tooltip open.
  4. Chromatic captures the screenshot — only the "Khan Academy" link is visible.
  5. The tooltip bubble with "This link navigates to the Khan Academy homepage." never appears in the snapshot.
  6. The snapshot is stored, consuming a monthly quota, with no useful tooltip regression coverage.

How to fix it

The simplest fix matching this PR's own pattern is to add opened={true} to WithLinkAnchor, mirroring WithRichTextContent. Alternatively, add a play function that hovers the link element (mirroring Default), or add parameters: { chromatic: { disableSnapshot: true } } if tooltip appearance on links does not need visual regression coverage.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! It caught something I noticed from checking the snapshots visually https://www.chromatic.com/test?appId=5e1bf4b385e3fb0020b7073c&id=69cea98f30ca01a1a71c9dd9

The type for the `Tooltip` `content` prop was a little misleading in that it suggested it could only be used with `TooltipContent`. In reality, any React element could be used--and that's what we saw in `frontend`.

This PR widens the type to match runtime behavior.
@khan-actions-bot khan-actions-bot requested a review from a team April 3, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants