Skip to content

Notes: Add full emoji picker behind a + button (stacked on #76767)#78176

Open
adamsilverstein wants to merge 10 commits into
add-notes-emoji-reactions-try-addditional-comment-typefrom
add-notes-emoji-reactions-full-picker
Open

Notes: Add full emoji picker behind a + button (stacked on #76767)#78176
adamsilverstein wants to merge 10 commits into
add-notes-emoji-reactions-try-addditional-comment-typefrom
add-notes-emoji-reactions-full-picker

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented May 11, 2026

Copy link
Copy Markdown
Member

Summary

Stacked on top of #76767 — review that PR first (curated 5-emoji reactions baseline). This PR adds the full searchable emoji picker behind a + button next to the smiley trigger.

Splitting this off per @t-hamano's request: library selection and bundling strategy still need wider discussion, so it shouldn't block the basic-reactions foundation.

Two-trigger picker UX

Each note now has two adjacent triggers:

Both buttons are independent siblings; the curated picker doesn't nest the full picker, so popovers never conflict.

Native picker built from WordPress primitives

Earlier iterations used Frimousse, an MIT-licensed React picker. @t-hamano flagged that Frimousse's UI strings come from Emojibase's .po files — not on GlotPress, no coverage for many WordPress locales. Replaced with a native picker built from SearchControl over a Composite grid grouped by Emojibase category, with sticky headers and content-visibility: auto per row so all ~1949 emojis render quickly. All chrome strings (Search emoji, Loading…, No emoji found., skin tone label) route through __() and translate via the WordPress translation pipeline.

packages/editor/src/components/collab-sidebar/emoji-picker.js:

  • resolveEmojibaseLocale() maps a BCP-47 / WordPress locale (pt-BR, fr_FR, zh-TW) to the closest of Emojibase's 28 locales, falling back to en.
  • useEmojibaseData(baseUrl, locale) fetches data.json + messages.json once per locale, with module-level cache and abort-on-unmount.
  • <EmojiPicker> renders the grid with full keyboard nav via Composite (same primitives WPDS uses).

Storage normalization

Picks from the full picker are stored as a lowercase hex-codepoint sequence joined by -, e.g. 1f44d for 👍 or 1f468-200d-1f4bb for 👨‍💻. Variation selector U+FE0F is stripped so visually-equivalent presentations collapse — 2764-fe0f (❤️) folds into the curated heart slug. The hex helpers (emojiToHexKey, hexKeyToEmoji, emojiToStorageKey) extend reaction-emoji-picker.js and remain compatible with the curated slug storage from the base PR.

Bundle-size strategy: lazy load + same-origin data

Two layers keep the editor bundle lean:

  1. The full picker is lazy-imported via React.lazy in reaction-display.js. The picker code only ships when the user first opens the + popover in a session — for editors who never use the full picker, the picker module never loads at all.
  2. Emojibase JSON data is served same-origin from the plugin, not embedded in the bundle. A build step (bin/copy-emojibase-data.mjs) copies per-locale data.json + messages.json from node_modules/emojibase-data/ into build/emojibase-data/<locale>/ after the main build runs (28 locales, ~7 MB on disk). PHP exposes the directory URL to JS via window.gutenbergEmojibaseUrl (set with wp_add_inline_script). Per-session network cost stays at one locale (~85 KB gzipped), only fetched when the user opens the + picker for the first time.

For npm consumers of @wordpress/editor outside the plugin: emojibase-data is a Gutenberg root devDependency, not a runtime dep of the package. The + button hides itself when window.gutenbergEmojibaseUrl is unset, so npm consumers must opt in by self-hosting the data and setting the global — there's no fallthrough to a third-party CDN.

Per-locale label overrides

gutenberg_emoji_picker_label_overrides PHP filter lets sites override Emojibase emoji labels for any locale (e.g. when curated labels in the base PR differ from Emojibase's choice for the same code point). The overrides are exposed to JS via an inline script.

Screencast

react.with.emoji.picker.mp4

Testing

Test in Playground: https://playground.wordpress.net/?gutenberg-pr=78176

For multi-user testing, try https://wordpress.org/plugins/user-switching/

  • Verify the base PR (Notes: Add emoji reactions - stored as custom comment type #76767) is checked out alongside this one
  • Create a note on a block
  • Click 😊 — pick from the curated 5-emoji row still works
  • Click ➕ — picker lazy-loads on first open; search and pick anything
  • Pick the same emoji twice (e.g. ❤️) via the curated row and via the full picker — verify both fold into the same heart reaction
  • Pick a non-curated emoji (e.g. 👍) — verify it renders correctly in the reaction pill
  • Press Escape with the full picker open — verify it closes
  • Search for zzzznoresults — verify empty-state message
  • Switch site language to French / German / Japanese — verify emoji labels, category headers, and chrome strings all translate
  • Run E2E tests: npm run test:e2e -- test/e2e/specs/editor/various/block-notes.spec.js (full-picker-specific tests)
  • Run JS unit tests for the picker: npm run test:unit packages/editor/src/components/collab-sidebar/test/emoji-picker.js
  • Run PHP tests: npm run test:unit:php:base -- --filter=Emoji_Picker_Data

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org>
Co-authored-by: manzoorwanijk <manzoorwanijk@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@adamsilverstein adamsilverstein self-assigned this May 11, 2026
@adamsilverstein adamsilverstein added [Type] Feature New feature to highlight in changelogs. [Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting labels May 11, 2026
@adamsilverstein adamsilverstein requested review from Mamaduka and t-hamano and removed request for ajitbohra, ntwb and spacedmonkey May 11, 2026 16:19
@adamsilverstein adamsilverstein added the Needs Design Needs design efforts. label May 11, 2026
Adds the "+" More-emojis trigger as a sibling of the curated smiley
trigger. Tapping it opens a lazy-loaded native picker built from
SearchControl + Composite over Emojibase data served same-origin from
the plugin (28 locales, fetched per-session on first open).

Picks fold into the curated slug when they match (e.g. ❤ → `heart`)
and store as a normalized hex-codepoint key otherwise (e.g. `1f44d` for
👍), so visually-equivalent presentations don't fragment the
reaction_summary aggregation. The hex encode/decode pair and the
HEX_KEY_RE fallback in getEmojiBySlug/getLabelBySlug come along for the
ride, since they only ever fire when this picker is enabled.

Stacked on the basic-reactions baseline so picker library and bundling
choices can be reviewed independently, per t-hamano in #76767.
@adamsilverstein adamsilverstein force-pushed the add-notes-emoji-reactions-full-picker branch from e05a3b0 to 6aa1aec Compare May 11, 2026 16:27
@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown

Size Change: +4.89 kB (+0.07%)

Total Size: 7.51 MB

📦 View Changed
Filename Size Change
build/scripts/editor/index.min.js 479 kB +2.66 kB (+0.56%)
build/styles/editor/style-rtl.css 31.4 kB +561 B (+1.82%)
build/styles/editor/style-rtl.min.css 26.8 kB +550 B (+2.09%)
build/styles/editor/style.css 31.5 kB +563 B (+1.82%)
build/styles/editor/style.min.css 26.8 kB +555 B (+2.11%)

compressed-size-action

@adamsilverstein

adamsilverstein commented May 11, 2026

Copy link
Copy Markdown
Member Author

Carrying over from @swissspidy's review comment on the original PR, which now lives in this stacked PR:

IIUC emojibase-data could also be served from a CDN, which probably should be considered prior to release given the size of this per locale. […] we already serve Twemoji from the dotorg CDN

Worth deciding before merge. Current setup bundles ~7 MB across 28 locales into the plugin's build/emojibase-data/ and serves it same-origin via gutenbergEmojibaseUrl. Per-session fetch is ~85 KB gzipped (one locale, lazy on first + open), so the user-facing cost is fine — but the plugin tarball cost and the 28-locale build/copy step are both real.

A few options:

  1. Bundle as today — predictable, no external dependency, no CORS, but plugin grows ~7 MB on disk and we maintain bin/copy-emojibase-data.mjs.
  2. Serve from s.w.org (like Twemoji) — drops the build step and tarball weight, gains a CDN, but needs ops sign-off to host the dataset and a versioning strategy (data.json shape can change between emojibase major versions). Would also need a fallback URL for self-hosted/airgapped installs.
  3. Hybrid — ship bundled, allow window.gutenbergEmojibaseUrl to be filtered/overridden so sites can opt into a CDN. Simple to land but defers the storage decision.

@swissspidy what do you think?

… into add-notes-emoji-reactions-full-picker

Resolve conflicts from the base branch's reaction picker refactors:

- reaction-emoji-picker.js: adopt the base branch's memoized
  buildEmojiBySlugMap for curated lookups while keeping the full
  picker's hex-codepoint helpers (emojiToHexKey/hexKeyToEmoji/
  emojiToStorageKey). Drop the now-unused getEmojiBySlug/getLabelBySlug.
- reaction-display.js: combine both sides' imports (inline smiley icon
  + plus icon, useMemo + lazy/Suspense) and decode hex-key slugs in the
  display fallback so full-picker reactions render as emoji rather than
  raw codepoints.
- block-notes.spec.js: keep the horizontal-listbox keyboard-navigation
  comment that matches the merged Composite orientation.
… into add-notes-emoji-reactions-full-picker

Resolve conflicts:
- package.json / package-lock.json: adopt trunk's slimmed root
  devDependencies (build tooling migrated to workspaces) while keeping
  the feature dependency emojibase-data.
- build.mjs: the build script moved from bin/ to tools/build-scripts/;
  preserve the emojibase data copy step (copy-emojibase-data.mjs still
  lives in bin/ and is invoked via a repo-root-relative path).
Comment thread tools/build-scripts/build.mjs Outdated
Co-authored-by: Manzoor Wani <manzoorwani.jk@gmail.com>
@swissspidy

Copy link
Copy Markdown
Member

Carrying over from @swissspidy's review comment on the original PR, which now lives in this stacked PR:

IIUC emojibase-data could also be served from a CDN, which probably should be considered prior to release given the size of this per locale. […] we already serve Twemoji from the dotorg CDN

Worth deciding before merge. Current setup bundles ~7 MB across 28 locales into the plugin's build/emojibase-data/ and serves it same-origin via gutenbergEmojibaseUrl. Per-session fetch is ~85 KB gzipped (one locale, lazy on first + open), so the user-facing cost is fine — but the plugin tarball cost and the 28-locale build/copy step are both real.

A few options:

  1. Bundle as today — predictable, no external dependency, no CORS, but plugin grows ~7 MB on disk and we maintain bin/copy-emojibase-data.mjs.
  2. Serve from s.w.org (like Twemoji) — drops the build step and tarball weight, gains a CDN, but needs ops sign-off to host the dataset and a versioning strategy (data.json shape can change between emojibase major versions). Would also need a fallback URL for self-hosted/airgapped installs.
  3. Hybrid — ship bundled, allow window.gutenbergEmojibaseUrl to be filtered/overridden so sites can opt into a CDN. Simple to land but defers the storage decision.

@swissspidy what do you think?

Apologies, I missed this. For the plugin, bundling is probably fine, but before a core merge this could be reconsidered and discussed in a larger group. I'm not familiar with the details/requirements regarding the CDN.

@manzoorwanijk manzoorwanijk left a comment

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.

Can we please move the script from bin/ to tools/ for isolated dependencies?

Comment thread tools/build-scripts/copy-emojibase-data.mjs
Comment thread package.json Outdated
Address review feedback from manzoorwanijk on PR #78176:

- Move bin/copy-emojibase-data.mjs into the tools/build-scripts
  workspace, following the workspace development guidelines instead of
  adding tooling to bin/.
- Relocate the emojibase-data dependency from the root package.json to
  the @wordpress/build-scripts workspace to keep the root clean (#75041).
- Resolve emojibase-data via node module resolution so it works whether
  hoisted or nested in the workspace.
- Drop the stale 'Step 0:' comment numbering in build.mjs.
- Update references to the script path in the picker, drift test, and
  PHP compat docblock.
The prop is now the default behavior, so the lint rule flags it as no
longer needed. This was failing the Lint JavaScript CI check.

@manzoorwanijk manzoorwanijk left a comment

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.

This looks fine to me from a build-tooling perspective. I would still prefer other folks to have a look at this.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Thanks for the helpful feedback @manzoorwanijk

…dditional-comment-type' into add-notes-emoji-reactions-full-picker

# Conflicts:
#	package-lock.json
…dditional-comment-type' into add-notes-emoji-reactions-full-picker
@adamsilverstein

Copy link
Copy Markdown
Member Author

ping @jasmussen for any feedback

The design system renamed several color token families:
`--wpds-color-bg-*` to `--wpds-color-background-*`,
`--wpds-color-fg-*` to `--wpds-color-foreground-*`, and dropped the
`-brand` suffix from `--wpds-color-stroke-focus-brand`.

The collab sidebar styles still referenced the old names, which fails the
design-token validation during the asset build and stylelint, cascading
into every downstream CI job. Update all affected references.
…dditional-comment-type' into add-notes-emoji-reactions-full-picker

# Conflicts:
#	packages/editor/src/components/collab-sidebar/reaction-display.js
@github-actions

Copy link
Copy Markdown

Flaky tests detected in 5fa099e.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/28251843690
📝 Reported issues:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting Needs Design Needs design efforts. [Package] Editor /packages/editor [Type] Feature New feature to highlight in changelogs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants