[PF-1994] Migrate Popper to @base-ui/react + Tailwind#5001
Conversation
🦋 Changeset detectedLatest commit: 57765f5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 40 packages
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 |
|
📖 Storybook Preview 🚀 Your Storybook preview is ready: View Storybook 📍 Preview URL: This preview is updated automatically when you push changes to this PR. |
|
📖 Storybook Preview 🚀 Your Storybook preview is ready: View Storybook 📍 Preview URL: This preview is updated automatically when you push changes to this PR. |
1 similar comment
|
📖 Storybook Preview 🚀 Your Storybook preview is ready: View Storybook 📍 Preview URL: This preview is updated automatically when you push changes to this PR. |
🛑 Orchestrator escalation —
|
Triaged the Happo escalation. Authoritative gates are green: local gate composite PASS; Storybook Happo clean (its lone UserBadge diff is 0-pixel noise, unrelated to Popper). All 52 Cypress diffs are on Popper consumers, not Popper's own stories. ~30 are negligible (0px). The rest are expected floating-ui consequences: it positions/collision-resolves differently than popper.js — e.g. Slider Recommend accepting the new Cypress baselines in Happo rather than patching source. 👍 to confirm. (Minor: |
fe18ec8 to
3d5041f
Compare
Tier 2 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994
Tier 2 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 [ci-iter 3]
Tier 2 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 [ci-iter 4]
5e951ce to
57765f5
Compare
|
📖 Storybook Preview 🚀 Your Storybook preview is ready: View Storybook 📍 Preview URL: This preview is updated automatically when you push changes to this PR. |
Dropdown redefined the 12-member placement union locally, duplicating the identical type @toptal/picasso-popper already declares. Surface it from the Popper package entry and import it in Dropdown instead of the local copy. The type is byte-identical in the current popper.js Popper and in the @floating-ui/react migration (PR #5001), and #5001 leaves Popper/index.ts untouched, so this rebases cleanly. PopperOptions stays on popper.js for now; it has no native package export until #5001 and is repointed during that rebase. Also refresh the Dropdown changeset to match the landed implementation (shared ClickAwayListener, @starting-style open animation, imported PopperPlacementType). Refs: PF-1994 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dropdown redefined the 12-member placement union locally, duplicating the identical type @toptal/picasso-popper already declares. Surface it from the Popper package entry and import it in Dropdown instead of the local copy. The type is byte-identical in the current popper.js Popper and in the @floating-ui/react migration (PR #5001), and #5001 leaves Popper/index.ts untouched, so this rebases cleanly. PopperOptions stays on popper.js for now; it has no native package export until #5001 and is repointed then. Also refresh the Dropdown changeset to match the landed implementation (shared ClickAwayListener, @starting-style open animation, imported PopperPlacementType). Refs: PF-1994 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dropdown redefined the 12-member placement union locally, duplicating the identical type @toptal/picasso-popper already declares. Surface it from the Popper package entry and import it in Dropdown instead of the local copy. The type is byte-identical in the current popper.js Popper and in the @floating-ui/react migration (PR #5001), and #5001 leaves Popper/index.ts untouched, so this rebases cleanly. PopperOptions stays on popper.js for now; it has no native package export until #5001 and is repointed then. Also refresh the Dropdown changeset to match the landed implementation (shared ClickAwayListener, @starting-style open animation, imported PopperPlacementType). Refs: PF-1994 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [PF-1994] Migrate Dropdown to @base-ui/react + Tailwind Tier 3 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 * [PF-1994] Migrate Dropdown to @base-ui/react + Tailwind Tier 3 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 [ci-iter 3] * [PF-1994] Migrate Dropdown to @base-ui/react + Tailwind Tier 3 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 [ci-iter 5] * [PF-1994] Use shared ClickAwayListener in Dropdown The migration hand-rolled a local useClickAway hook (plus a wrapper <div> kept only for a since-resolved MUI bug) to replace MUI's ClickAwayListener. But @toptal/picasso-utils already exports a ClickAwayListener -- the MUI-free replacement that DatePicker and Menu use -- so swap to it for consistency with the rest of the library. The shared component is also more robust than the local hook: it adds a root-scrollbar click guard, portal / React-tree awareness, touch-move cancellation, and an activation delay so the opening interaction is not counted as a click-away. It clones its child, so no extra DOM node is introduced. Removes use-click-away.ts (the shared component carries its own tests). Render-neutral: verified via Jest (unchanged DOM snapshot) and a local Happo run (no new diffs). Refs: PF-1994 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [PF-1994] Restore Dropdown open animation via CSS @starting-style The MUI <Grow> replacement was a no-op: growIn flipped in a pre-paint layout effect (collapsed frame never painted) and scale sat on Tailwind v4's `scale` property, which transition-[opacity,transform] did not animate. Replace the growIn state machine with a CSS @starting-style entry transition (transition-[opacity,scale] + starting:scale-75 starting:opacity-0); the resting state is scale-100 so static captures stay stable. Drops the useIsomorphicLayoutEffect hack. Verified: Jest, lint, tsc, and a local Happo run (dropdown snapshots byte-identical to the settled baseline -- no new diffs). A TODO notes the @floating-ui/react Popper migration must trigger the entry animation only once positioned. Refs: PF-1994 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [PF-1994] Import PopperPlacementType from picasso-popper Dropdown redefined the 12-member placement union locally, duplicating the identical type @toptal/picasso-popper already declares. Surface it from the Popper package entry and import it in Dropdown instead of the local copy. The type is byte-identical in the current popper.js Popper and in the @floating-ui/react migration (PR #5001), and #5001 leaves Popper/index.ts untouched, so this rebases cleanly. PopperOptions stays on popper.js for now; it has no native package export until #5001 and is repointed then. Also refresh the Dropdown changeset to match the landed implementation (shared ClickAwayListener, @starting-style open animation, imported PopperPlacementType). Refs: PF-1994 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [PF-1994] Migrate Dropdown to @base-ui/react + Tailwind Tier 3 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 [review-iter 2] address review feedback * [PF-1994] Migrate Dropdown to @base-ui/react + Tailwind Tier 3 component. See PR description for prop-surface diff, import diff, and Happo summary. Refs: PF-1994 [cleanup] strip review-aid comments before merge --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

Popper — migrate MUI v4 Popper to @floating-ui/react
Summary
Re-implements
@toptal/picasso-popperon@floating-ui/react(decision LOCKED indocs/migration/decisions/popper-replacement.md—@base-ui/reactships no standalone Popper). The public Props surface is preserved verbatim;popperOptionsis re-typed as a Picasso-native, popper.js-v1-shaped structural subset so all five internal consumers (Dropdown, DatePicker, Autocomplete, Menu, Select) compile and behave unchanged without source edits. popper.js modifiers map to floating-ui middleware (flip→flip,preventOverflow→shift,offsetstring format parsed tocrossAxis/mainAxis,hide→hide+ the legacyx-out-of-boundariesattribute), and theonCreate/onUpdatelifecycle is replicated viaisPositioned.Decisions
PopperOptionstype instead of dropping the prop: consumers pass popper.js-typed objects (DatePickerPOPPER_OPTIONS, Dropdown/AutocompletepopperOptionsprops); the structural subset keeps them assignable understrictFunctionTypes(lifecycle callbacks typed(data: never) => voidbecause Dropdown's spread synthesizes property-style members, losing method bivariance). Verified by repotsc -bof all 90 packages.refexposes a minimalPopperHandle(popper,update,scheduleUpdate): DatePicker and Select's blur handling readpopperRef.current?.popper.contains(...); the handle is set only while the popper DOM is mounted, matching MUI v4 popperRef semantics.useRef<PopperJs>in consumers stays type-compatible (covariant RefObject).getPopperOptionsexport + parity attributes (role="tooltip",x-placement,x-out-of-boundaries): the component's own[&[x-out-of-boundaries]]:hiddenTailwind selector and existing tests rely on them; default hide/flip/preventOverflow enablement matches popper.js defaults pergetPopperOptions.FloatingPortalfor the portal path: SSR-safe deferred mounting like MUI's Portal; adds one unstyleddata-floating-ui-portalwrapper inside the container (consumer-visible DOM change, named in the changeset).versionBump: majortaken verbatim frommanifest.json.classeshandling: no-op perdocs/migration/components/Popper.md— Popperextends BaseProps, noclassesprop existed (source + audit §4 agree).Limitations / Out-of-scope
positionFixed,eventsEnabled, custom modifierfns,arrow,applyStyle) are accepted by older typings but ignored at runtime; repo-wide grep found no usage. External-consumer codemods belong to PF-1995.boundariesElement: 'scrollParent'maps to floating-ui's default clipping-ancestors detection (closest equivalent, not identical math).pnpm installwebpack graph referencing a node_modules path the install re-laid-out (ENOENT on@mui/base/node_modules/@floating-ui/core), watch ignores node_modules, and process restart is not permitted in this session. Runtime verification was done via Cypress instead (below). Operator: restartpnpm startbefore eyeballing stories.Verification
pnpm -F @toptal/picasso-popper build:package, fullpnpm build:package(90 projects), repopnpm typecheck,davinci-syntax lint code --check(0 errors/0 warnings), Popper jest suite (10 behavioral tests, rewritten off the old MUIPopper mock), consumer jest suites (82 suites / 379 tests; 2 Dropdown snapshots regenerated — FloatingPortal wrapper + floating-ui transform style).pnpm test:integration --spec Dropdown,Select,Autocomplete,DatePicker,Menu— 62/62 passing; exercises Dropdown focus-on-open (onCreate), Select blur-via-handle.popper.contains, Menu submenu offset parsing, DatePicker calendar popper.Mechanical diff evidence
Auto-generated by
bin/migration-diff.sh reportfrom pre/post snapshots. The agent's narrative is above; this section is the file-level facts.Popper migration diff
Generated: 2026-06-10 13:42:18 CEST
Package:
packages/base/PopperFiles
Imports
Removed:
Added:
MUI v4 / JSS residue check
@material-ui/*source imports@material-ui/corein package.jsonMigration is NOT complete until all three are 0.
package.json delta
Prop-surface diff
Click to expand .d.ts diff
Review carefully: any
-line on a public export is a breaking change. Seedocs/migration/rules/api-preservation.md.Happo
Happo log:
migration-runs/2026-06-10/Popper/happo.log(0? flagged lines).
Designer: review screen diffs >0.5% per
docs/migration/migration-plan.md§6.3.React 19 smoke
Stubbed (pending PF-1994). The real smoke wires up during PF-1994's first migration.