@tiptap/react
Version:
React components for tiptap
122 lines (88 loc) • 3.54 kB
text/typescript
import { act, render } from '@testing-library/react'
import { Editor } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { TextSelection } from '@tiptap/pm/state'
import React from 'react'
import { afterEach, describe, expect, it } from 'vitest'
import { useEditorState } from './useEditorState.js'
const createTestEditor = () => {
return new Editor({
extensions: [Document, Paragraph, Text],
content: '<p>Hello</p>',
})
}
const EditableIndicator = ({ editor }: { editor: Editor }) => {
const isEditable = useEditorState({
editor,
selector: ctx => ctx.editor.isEditable,
})
return React.createElement('div', { 'data-testid': 'editable-state' }, String(isEditable))
}
const ContentIndicator = ({ editor }: { editor: Editor }) => {
const text = useEditorState({
editor,
selector: ctx => ctx.editor.getText(),
})
return React.createElement('div', { 'data-testid': 'content-state' }, text)
}
const TransactionCountIndicator = ({ editor }: { editor: Editor }) => {
const transactionNumber = useEditorState({
editor,
selector: ctx => ctx.transactionNumber,
})
return React.createElement('div', { 'data-testid': 'tx-number' }, String(transactionNumber))
}
describe('useEditorState', () => {
afterEach(() => {
document.body.innerHTML = ''
})
it('re-renders when editor.setEditable() toggles the editable state', () => {
const editor = createTestEditor()
const { getByTestId } = render(React.createElement(EditableIndicator, { editor }))
expect(getByTestId('editable-state').textContent).toBe('true')
act(() => {
editor.setEditable(false)
})
expect(getByTestId('editable-state').textContent).toBe('false')
act(() => {
editor.setEditable(true)
})
expect(getByTestId('editable-state').textContent).toBe('true')
editor.destroy()
})
it('still re-renders on a normal content-changing transaction (regression)', () => {
const editor = createTestEditor()
const { getByTestId } = render(React.createElement(ContentIndicator, { editor }))
expect(getByTestId('content-state').textContent).toBe('Hello')
act(() => {
editor.commands.setContent('<p>World</p>')
})
expect(getByTestId('content-state').textContent).toBe('World')
editor.destroy()
})
it('notifies once per document change (transaction + update are deduped)', () => {
const editor = createTestEditor()
const { getByTestId } = render(React.createElement(TransactionCountIndicator, { editor }))
const before = Number(getByTestId('tx-number').textContent)
act(() => {
// A single doc-changing transaction emits both `transaction` and `update`.
editor.view.dispatch(editor.state.tr.insertText('!'))
})
const after = Number(getByTestId('tx-number').textContent)
expect(after - before).toBe(1)
editor.destroy()
})
it('still notifies on selection-only transactions (which emit no update event)', () => {
const editor = createTestEditor()
const { getByTestId } = render(React.createElement(TransactionCountIndicator, { editor }))
const before = Number(getByTestId('tx-number').textContent)
act(() => {
editor.view.dispatch(editor.state.tr.setSelection(TextSelection.atStart(editor.state.doc)))
})
const after = Number(getByTestId('tx-number').textContent)
expect(after - before).toBe(1)
editor.destroy()
})
})