Skip to content

Conversation

@jandrade
Copy link
Member

@jandrade jandrade commented Dec 1, 2025

Summary:

Adds right-to-left support to Floating component.

To support this, I've created a custom middleware to be able to mirror the
floating placement in RTL contexts.

Issue: https://khanacademy.atlassian.net/browse/WB-2162

Test plan:

Navigate to any floating stories, select the RTL mode, and verify that the
floating element flips when the placement is left* or right*.

@jandrade jandrade self-assigned this Dec 1, 2025
@changeset-bot
Copy link

changeset-bot bot commented Dec 1, 2025

🦋 Changeset detected

Latest commit: 8ece8c8

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

This PR includes changesets to release 1 package
Name Type
@khanacademy/wonder-blocks-floating Minor

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

@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 2025

Size Change: +154 B (+0.14%)

Total Size: 112 kB

Filename Size Change
packages/wonder-blocks-floating/dist/es/index.js 2.78 kB +154 B (+5.87%) 🔍
ℹ️ View Unchanged
Filename Size
packages/wonder-blocks-accordion/dist/es/index.js 3 kB
packages/wonder-blocks-announcer/dist/es/index.js 1.74 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.92 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 755 B
packages/wonder-blocks-button/dist/es/index.js 4.25 kB
packages/wonder-blocks-card/dist/es/index.js 1.06 kB
packages/wonder-blocks-cell/dist/es/index.js 2.19 kB
packages/wonder-blocks-clickable/dist/es/index.js 2.66 kB
packages/wonder-blocks-core/dist/es/index.js 2.48 kB
packages/wonder-blocks-data/dist/es/index.js 5.48 kB
packages/wonder-blocks-dropdown/dist/es/index.js 19.4 kB
packages/wonder-blocks-form/dist/es/index.js 6.2 kB
packages/wonder-blocks-grid/dist/es/index.js 1.24 kB
packages/wonder-blocks-icon-button/dist/es/index.js 3.16 kB
packages/wonder-blocks-icon/dist/es/index.js 1.91 kB
packages/wonder-blocks-labeled-field/dist/es/index.js 3.48 kB
packages/wonder-blocks-layout/dist/es/index.js 1.63 kB
packages/wonder-blocks-link/dist/es/index.js 1.52 kB
packages/wonder-blocks-modal/dist/es/index.js 7.06 kB
packages/wonder-blocks-pill/dist/es/index.js 1.31 kB
packages/wonder-blocks-popover/dist/es/index.js 4.3 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 3.71 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.01 kB
packages/wonder-blocks-toolbar/dist/es/index.js 906 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.4 kB
packages/wonder-blocks-typography/dist/es/index.js 1.57 kB

compressed-size-action

@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 2025

A new build was pushed to Chromatic! 🚀

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

Chromatic results:

Metric Total
Captured snapshots 5
Tests with visual changes 0
Total stories 753
Inherited (not captured) snapshots [TurboSnap] 441
Tests on the build 446

@jandrade jandrade marked this pull request as ready for review December 1, 2025 18:59
@khan-actions-bot khan-actions-bot requested a review from a team December 1, 2025 19:00
@khan-actions-bot
Copy link
Contributor

khan-actions-bot commented Dec 1, 2025

Gerald

Required Reviewers
  • @Khan/wonder-blocks for changes to .changeset/mighty-penguins-kick.md, __docs__/wonder-blocks-floating/floating-testing-snapshots.stories.tsx, __docs__/wonder-blocks-floating/floating.argtypes.ts, __docs__/wonder-blocks-floating/floating.stories.tsx, __docs__/wonder-blocks-icon/phosphor-icon.argtypes.ts, packages/wonder-blocks-floating/src/components/floating.tsx, packages/wonder-blocks-floating/src/util/rtl-mirror-middleware.ts, packages/wonder-blocks-floating/src/util/__tests__/rtl-mirror-middleware.test.ts

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

Copy link
Member

@marcysutton marcysutton left a comment

Choose a reason for hiding this comment

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

This looks good to me! Just left a few questions for my own understanding.

},
render: function Render(args, {globals}) {
const isRTL = globals.direction === "rtl";
const placements: Array<{name: Placement; icon: PhosphorRegular}> = [
Copy link
Member

Choose a reason for hiding this comment

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

praise: this is a slick way to set up these variants!

gridTemplateColumns: "repeat(3, 1fr)",
gridTemplateRows: "repeat(4, 150px)",
placeItems: "center",
width: "100%",
Copy link
Member

Choose a reason for hiding this comment

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

question: Should we move to inlineSize and blockSize instead of width and height?


describe("rtlMirror", () => {
// Helper function to create a reference element with optional RTL container
const createReferenceElement = (isRTL: boolean) => {
Copy link
Member

Choose a reason for hiding this comment

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

question: Is it possible this would be a useful helper function for other tests? Would it make sense to put it in a more general location?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is only used here for now. I'll move it if we start needing it for other test files.

}

return {
reset: {
Copy link
Member

Choose a reason for hiding this comment

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

question: Is reset a @floating-ui/react thing? What does it represent?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question, I'll add a comment + link to the docs :)

Copy link
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.

Looks good to me! Left some non-blocking questions 😄

placement,
elements: {reference},
} = state;
const isRtl = !!(reference as Element).closest("[dir='rtl']");
Copy link
Member

Choose a reason for hiding this comment

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

Since we cast the reference as an Element, Is it possible for reference to be null or not yet defined at runtime? I'm wondering if we need to check it before calling closest!

Comment on lines +44 to +47
if (placement.startsWith("left")) {
nextPlacement = placement.replace("left", "right") as Placement;
} else {
nextPlacement = placement.replace("right", "left") as Placement;
Copy link
Member

Choose a reason for hiding this comment

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

Do you think there would ever be a use case where the floating component shouldn't get mirrored based on the writing direction? I can't think of an example right now, but I was wondering if consumers would want to opt out of this middleware (we can also add an option to support that later on if needed)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think (and have seen) that most of the cases use either top or bottom (which are not affected by this custom logic), so we should be fine keeping this middleware by default. Also checked in other RTL-friendly websites and have seen that tooltips are mirrored under similar conditions (e.g. booking.com). We can add the option to support that if it is needed :)

Copy link
Member

Choose a reason for hiding this comment

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

While testing the Default story, I found that the args in the query params weren't working when I'd refresh. I think it's unrelated to the RTL work and is only a storybook thing, but thought I'd mention it!

Screen.Recording.2025-12-01.at.3.47.34.PM.mov

Copy link
Member Author

Choose a reason for hiding this comment

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

Good finding! I've fixed it :)


type Placement = PropsFor<typeof Floating>["placement"];

export const Placements: StoryComponentType = {
Copy link
Member

Choose a reason for hiding this comment

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

Should we add documentation somewhere so consumers know that the floating direction will be handled for them? Maybe for this story, or in the props docs for the placement prop!

@jandrade jandrade merged commit f3c5eed into feature/floating-ui Dec 2, 2025
15 checks passed
@jandrade jandrade deleted the wb-floating-rtl branch December 2, 2025 16:01
@codecov
Copy link

codecov bot commented Dec 2, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (4a6e7fb) to head (8ece8c8).
⚠️ Report is 1 commits behind head on feature/floating-ui.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##   feature/floating-ui   #2881   +/-   ##
===========================================
===========================================

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4a6e7fb...8ece8c8. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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