Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/emoji-mart-react-19-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@toptal/picasso-rich-text-editor': patch
---

### RichTextEditor

- drop the `@emoji-mart/react` dependency and render emoji-mart's `Picker` through a local component instead. `@emoji-mart/react@1.1.1` is the latest release, was last published in January 2023, and declares a `react` peer range of `^16.8 || ^17 || ^18` that excludes React 19 — so no upgrade could unblock React 19 support. The wrapper was around 20 lines and used no API React 19 removes, so owning it removes the constraint without changing behavior. `emoji-mart` and `@emoji-mart/data` were already direct dependencies and are unchanged, so nothing is added to the dependency tree
- the local component pushes prop updates into the picker from an effect. The upstream wrapper did this during render, which is a render-phase side effect
1 change: 0 additions & 1 deletion packages/picasso-rich-text-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
},
"dependencies": {
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@lexical/html": "0.11.3",
"@lexical/react": "0.11.3",
"@toptal/picasso-container": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import data from '@emoji-mart/data'
import { render, waitFor } from '@toptal/picasso-test-utils'

import EmojiMartPicker from './EmojiMartPicker'

const onEmojiSelect = jest.fn()

const renderEmojiMartPicker = () =>
render(<EmojiMartPicker data={data} onEmojiSelect={onEmojiSelect} />)

describe('EmojiMartPicker', () => {
it('mounts the emoji-mart picker element', async () => {
const { container } = renderEmojiMartPicker()

await waitFor(() =>
expect(container.querySelector('em-emoji-picker')).toBeInTheDocument()
)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { useEffect, useRef } from 'react'
import { Picker } from 'emoji-mart'

import type { CustomEmojiGroup, Emoji } from '../plugins/EmojiPlugin'

interface Props {
/** Emoji dataset to render, as exported by `@emoji-mart/data` */
data: unknown
/** Additional groups of custom emojis appended to the picker */
custom?: CustomEmojiGroup[]
/** Called with the picked emoji when a selection is made */
onEmojiSelect: (emoji: Emoji) => void
/** Called when a click lands outside the picker */
onClickOutside?: () => void
}

/**
* Renders emoji-mart's `Picker` custom element.
*
* `Picker` appends itself to the element passed as `ref` and is fed prop
* changes through `update`, so it is constructed after the first commit and
* updated on every commit after that.
*/
const EmojiMartPicker = (props: Props) => {
const containerRef = useRef<HTMLDivElement>(null)
const pickerRef = useRef<Picker | null>(null)

useEffect(() => {
if (!pickerRef.current) {
pickerRef.current = new Picker({ ...props, ref: containerRef })

return
}

pickerRef.current.update(props)
})

useEffect(
() => () => {
pickerRef.current = null
},
[]
)

return <div ref={containerRef} />
}

EmojiMartPicker.displayName = 'EmojiMartPicker'

export default EmojiMartPicker
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable no-inline-styles/no-inline-styles */
import React, { useEffect } from 'react'
import data from '@emoji-mart/data'
import Picker from '@emoji-mart/react'
import cx from 'classnames'
import { Container } from '@toptal/picasso-container'

import RichTextEditorButton from '../RichTextEditorButton'
import EmojiMartPicker from './EmojiMartPicker'
import type { CustomEmojiGroup, Emoji } from '../plugins/EmojiPlugin'

interface Props {
Expand Down Expand Up @@ -82,11 +82,11 @@ export const RichTextEditorEmojiPicker = ({
showEmojiPicker && classes.activePointers
)}
>
<Picker
<EmojiMartPicker
data={data}
custom={customEmojis}
onEmojiSelect={handleEmojiInsert}
onClickOutside={showEmojiPicker && closePicker}
onClickOutside={showEmojiPicker ? closePicker : undefined}
/>
</Container>
</Container>
Expand Down
14 changes: 0 additions & 14 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading