ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
27.75k stars 2.31k forks source link

[Bug]: The React Installation Example in https://tiptap.dev/installation/react seems to be broken in multiple places, especially with vite and vitest #4618

Open peterkmurphy opened 1 year ago

peterkmurphy commented 1 year ago

Which packages did you experience the bug in?

core, react

What Tiptap version are you using?

2.1.12

What’s the bug you are facing?

My project team has an existing React/TypeScript/Vite project, and we are looking for a WYSIWYG editor to add for the next stage, so I decided to investigate TipTap. There is an installation example for React that I'd expect to "work out of the box" at https://tiptap.dev/installation/react - 'The complete setup (optional)", so I decided to copy and paste the example into existing code.

However, I have identified two issues where TypeScript typing is broken, and two issues where it has broken the vitest testing framework, which we use. So there are four issues. I have replicated this with a bare bones React/TypeScript/Vite project, which I will put on GitHub. I will start with the TypeScript issue.

Issue 1 - you code example:

<EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>

Give me this error:

Property 'children' is missing in type '{ slotBefore: Element; extensions: (Extension<ColorOptions, any> | Mark<TextStyleOptions, any> | Extension<StarterKitOptions, any>)[]; content: string; }' but required in type '{ children: ReactNode; slotBefore?: ReactNode; slotAfter?: ReactNode; }'.ts(2741)
Context.d.ts(11, 5): 'children' is declared here.
(alias) const EditorProvider: ({ children, slotAfter, slotBefore, ...editorOptions }: EditorProviderProps) => React.JSX.Element | null
import EditorProvider

To fix it, I need to put in a dummy element {' '}. (It doesn't do anything, but it really shouldn't be necessary.)

    <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}>{' '}</EditorProvider>

Issue 2 - this code example:

const extensions = [
  Color.configure({ types: [TextStyle.name, ListItem.name] }),
  TextStyle.configure({ types: [ListItem.name] }),

Gives me this error:

Argument of type '{ types: string[]; }' is not assignable to parameter of type 'Partial<TextStyleOptions>'.
  Object literal may only specify known properties, and 'types' does not exist in type 'Partial<TextStyleOptions>'.ts(2345)
(property) types: string[]

I can of course change the line to make the errors go away, but it really shouldn't be necessary:

  TextStyle.configure({ types: [ListItem.name] } as Partial<TextStyleOptions>),

Issue 3 is more serious. At the moment, I have an App that contains this code:

import './App.css'
import TipTapEditor from './TipTapEditor'

function App() {

  return (
    <>
      <h1>Vite + React</h1>
      <TipTapEditor />
    </>
  )
}

export default App

So I have a simple test in App.test.tsx:

import { render, screen } from '@testing-library/react';

import App from './App';

describe('App', () => {
  it('renders headline', () => {
    render(<App />);
    expect(screen.getByText('Vite + React')).toBeTruthy()

    // check if App components renders headline
  });
});

Now in the TipTapEditor class there is a contents constant, with the following piece inside it

<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>

When I run the Vitest test, I get:

 FAIL  src/App.test.tsx > App > renders headline
TypeError: ((intermediate value)(intermediate value)(intermediate value) || []) is not iterable
 ❯ Object.parseHTML node_modules/@tiptap/extension-code-block/src/code-block.ts:72:71
 ❯ node_modules/@tiptap/core/src/helpers/injectExtensionAttributesToParseRule.ts:31:28
 ❯ Object.getAttrs node_modules/@tiptap/core/src/helpers/injectExtensionAttributesToParseRule.ts:29:49
 ❯ DOMParser.matchTag node_modules/prosemirror-model/dist/index.js:2545:39
 ❯ ParseContext.addElement node_modules/prosemirror-model/dist/index.js:2812:35
 ❯ ParseContext.addDOM node_modules/prosemirror-model/dist/index.js:2751:18
 ❯ ParseContext.addAll node_modules/prosemirror-model/dist/index.js:2943:18
 ❯ node_modules/prosemirror-model/dist/index.js:2839:53
 ❯ ParseContext.withStyleRules node_modules/prosemirror-model/dist/index.js:2756:20

When I take out the <pre> element and its contents, that error goes away, and the test works. Note: there is no problem with the app at all, but vitest has a problem.

The fourth error is from the code example page (3. Create a new component). Let's assume that I don't have that dodgy <pre> element instead, but I have this code:

    <EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}>
      <FloatingMenu>This is the floating menu</FloatingMenu>
      <BubbleMenu>This is the bubble menu</BubbleMenu>
    </EditorProvider>

I get:

 FAIL  src/App.test.tsx > App > renders headline
DOMException: Failed to remove node. Node is not child of parent.
 ❯ Function.removeChild node_modules/happy-dom/src/nodes/node/NodeUtility.ts:103:10
 ❯ Function.removeChild node_modules/happy-dom/src/nodes/element/ElementUtility.ts:106:15
 ❯ HTMLElement.removeChild node_modules/happy-dom/src/nodes/element/Element.ts:400:25
 ❯ removeChildFromContainer node_modules/react-dom/cjs/react-dom.development.js:11105:15
 ❯ commitDeletionEffectsOnFiber node_modules/react-dom/cjs/react-dom.development.js:24026:15
 ❯ recursivelyTraverseDeletionEffects node_modules/react-dom/cjs/react-dom.development.js:23989:5
 ❯ commitDeletionEffectsOnFiber node_modules/react-dom/cjs/react-dom.development.js:24118:9
 ❯ recursivelyTraverseDeletionEffects node_modules/react-dom/cjs/react-dom.development.js:23989:5
 ❯ commitDeletionEffectsOnFiber node_modules/react-dom/cjs/react-dom.development.js:24170:9
 ❯ recursivelyTraverseDeletionEffects node_modules/react-dom/cjs/react-dom.development.js:23989:5

Again - this is a situation where the app works, but vitest doesn't.

I have created a repository that replicates the third error (the ((intermediate value)(intermediate value)(intermediate value) || []) is not iterable). I wanted a repository, as it replicates the existing conditions that our project team are using (Vite, Vitest, use of HappyDom) without extraneous stuff. To repliacate the other errors, please edit as necessary.

What browser are you using?

Firefox

Code example

https://github.com/peterkmurphy/vite-with-tiptap

What did you expect to happen?

Here's a nice code example that I copy and paste into my existing code without any dramas. There are no problems in TypeScript or testing, and we decide to choose TipTap as the rich text editor for our project.

Anything to add? (optional)

Thank you very much to the team for looking into it.

Did you update your dependencies?

Are you sponsoring us?

lkim-hyungsuk commented 11 months ago

+1

Goodosky commented 10 months ago

+1

galdeguer-ut commented 10 months ago

+1

pitops commented 10 months ago

Got here because of the typing of TextStyle.configure({ types: [ListItem.name] }.

danilo-89 commented 10 months ago

+1

Alan-eMartin commented 9 months ago

+1

SupLano commented 9 months ago

+1

josh-gc commented 8 months ago

+1 Thank you @peterkmurphy this was super helpful!

Aryan3212 commented 7 months ago

+1

hjoelh commented 7 months ago

experiencing the same with EditorProvider, I'm happy to open a PR making children optional? @bdbch

draft here