Skip to content

[PF-2292] Unlock picass-rich-text-editor React v19 - #5072

Open
azebich wants to merge 1 commit into
feature/pf-2262-lift-the-react-19-peer-depfrom
pf-2292-upgrade-rich-text-editor-package-to-allow-react-v19-support
Open

[PF-2292] Unlock picass-rich-text-editor React v19#5072
azebich wants to merge 1 commit into
feature/pf-2262-lift-the-react-19-peer-depfrom
pf-2292-upgrade-rich-text-editor-package-to-allow-react-v19-support

Conversation

@azebich

@azebich azebich commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

PF-2292

Description

Remove the @emoji-mart/react dependency from @toptal/picasso-rich-text-editor
and render emoji-mart's Picker through a small local component instead, so the
package no longer carries a react peer range that excludes React 19. This
unblocks the peer-cap lift on the PF-2262 branch; Picasso's own react peer is
untouched here.

Why not just upgrade. @emoji-mart/react@1.1.1 is the latest release — last
published January 2023 — and declares react: ^16.8 || ^17 || ^18. No version
exists that supports React 19, so there is nothing to bump to. Relaxing the peer
locally (pnpm patch or peerDependencyRules) would fix only this repo's install:
patches aren't published, so consumers on React 19 would still resolve
@emoji-mart/react@1.1.1 and hit a peer warning or a failed install.

Owning the wrapper is cheap and permanent. It was ~20 lines, exactly one file
imported it, and emoji-mart plus @emoji-mart/data were already direct
dependencies — so nothing is added to the dependency tree, only removed.

The new component (EmojiMartPicker) constructs the Picker on the first
commit and feeds later prop changes through picker.update(props) — both from
effects. The upstream wrapper called update during render, which is a
render-phase side effect; owning the code let us fix that. It also nulls its
instance on unmount.

It is fully typed with no escape hatches: Picker imports from emoji-mart as
both a value and a type, so there is no any and no boundary cast at all. data
is deliberately typed unknown@emoji-mart/data's .d.ts exports only
interfaces with no default export, so typing it as EmojiMartData would break the
call site.

One call-site change: onClickOutside={showEmojiPicker && closePicker} passed
literal false when closed and now passes undefined. Both are falsy to
emoji-mart, so this is parity rather than a behaviour change.

Not addressed here, deliberately:

  • No story that opens the picker. It renders behind opacity-0 until toggled,
    so Happo almost certainly never captures it open. Adding one would genuinely
    improve coverage but moves the visual baseline, so it deserves its own decision.
  • SSR is unchanged. emoji-mart calls customElements.define(...) and probes
    canvas at module scope, so importing it needs a DOM. That is pre-existing — the
    old import chain reached the same module — and this change neither improves nor
    worsens it.
  • emoji-mart 5.5.2 → 5.6.0 (which would enable the Emoji 15 additions) is
    tracked as a separate follow-up ticket, since it is a user-visible content
    change and not a React 19 blocker.

How to test

  • Temploy
  • Open the RichText → Default story and use the emoji button in the toolbar:
    the picker opens and closes, standard emojis insert into the editor, and the
    custom emojis from that story's customEmojis still appear and insert.
  • Confirm Escape closes the picker, and that clicking outside it closes it.
  • Confirm no console errors on open/close/insert.
  • Check the Happo report: no visual change is expected.

Note the new unit test proves the picker mounts, but cannot exercise its UI — the
picker's contents live in a shadow DOM that testing-library cannot query. The
manual pass above is the real verification.

Verified locally:

  • @emoji-mart/react is gone from the manifest, pnpm-lock.yaml (0 occurrences)
    and node_modules; no reference remains anywhere in the repo
  • emoji-mart@5.5.2 and @emoji-mart/data@1.2.1 unchanged — nothing added
  • tsc -b --force exit 0 for picasso-rich-text-editor and picasso-forms
    (its only dependent)
  • Jest: picasso-rich-text-editor 22 suites / 91 tests pass, run three times with
    identical results; picasso-forms 17 suites / 66 tests / 11 snapshots pass
  • ESLint (which runs with --fix) reported nothing and rewrote nothing; Prettier
    clean on all touched files
  • The new test is non-vacuous: em-emoji-picker is a custom element only
    emoji-mart creates, so it fails if the picker never mounts

Reviewer note: pnpm typecheck is a no-op in this repo — the root
tsconfig.json is "files": [] plus project references, and tsc --noEmit does
not follow references. Use tsc -b as above.

Screenshots

No visual change is intended — the picker renders the same emoji-mart element as
before, just constructed by our own component. The Happo report is the evidence;
see How to test.

Before. After.
n/a — no visual change expected n/a — no visual change expected

Development checks

  • Add changeset according to guidelines (if needed) — patch for @toptal/picasso-rich-text-editor: internal implementation change, no public API change
  • Double check if picasso-tailwind-merge requires major update (check its README.md) — n/a, no Tailwind or styling changes
  • Read CONTRIBUTING.md and Component API principles
  • Make sure that additions and changes on the design follow Toptal's BASE design, and it's been already discussed with designers at #-base-core — n/a, no design change
  • Annotate all props in component with documentation — the new internal component's props are JSDoc'd; no public props added or changed
  • Create examples for component — n/a, no new public component; the picker is already covered by the RichText story
  • Ensure that deployed demo has expected results and good examples
  • Ensure the changed/created components have not caused accessibility issues. How to use accessibility plugin in storybook.
  • Self reviewed
  • Covered with tests (visual tests included) — added a mount test for the new component; existing 22 RTE suites and picasso-forms suites pass unchanged; visual coverage via Happo

Breaking change

Not a breaking change — no public API change and no codemod needed. The only
theoretical impact is on a consumer that imported @emoji-mart/react without
declaring it and relied on Picasso hoisting it; such a consumer would now need to
declare it themselves.

  • codemod is created and showcased in the changeset
  • test alpha package of Picasso in StaffPortal

@azebich azebich self-assigned this Jul 28, 2026
@azebich
azebich requested a review from a team as a code owner July 28, 2026 15:55
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d77af1e

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

This PR includes changesets to release 2 packages
Name Type
@toptal/picasso-rich-text-editor Patch
@toptal/picasso-forms Patch

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
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/5072/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant