Skip to content

Conversation

@Headan
Copy link
Contributor

@Headan Headan commented Nov 6, 2025

… files

Description

Monté de version de tiptap v2 a v3.

Liens utiles :
https://tiptap.dev/docs/guides/upgrade-tiptap-v2

Which Package changed?

Please check the name of the package you changed

  • Components
  • Core
  • Icons
  • Hooks

Has the documentation changed?

  • Storybook

Type of change

Please check options that are relevant.

  • Chore (PATCH)
  • Doc (PATCH)
  • Bug fix (PATCH)
  • New feature (MINOR)
  • Breaking change (MAJOR)

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

@Headan Headan requested a review from Copilot November 6, 2025 17:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR upgrades TipTap from version 2.11.0 to 3.10.2, a major version upgrade that introduces breaking changes to import paths and API patterns for floating menus and bubble menus.

  • Updates TipTap core and all extension packages from 2.11.0 to 3.10.2
  • Refactors FloatingMenu and BubbleMenu components to use new API patterns with separate reference objects and updated positioning configuration
  • Consolidates extension imports from individual packages to grouped exports (e.g., @tiptap/extensions, @tiptap/extension-table)

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/react/package.json Updates all TipTap dependencies from 2.11.0 to 3.10.2 and adds @floating-ui/dom
packages/extensions/package.json Updates TipTap dependencies to 3.10.2
packages/react/src/modules/editor/hooks/useTipTapEditor.ts Consolidates extension imports and replaces TextStyle/Underline with TextStyleKit
packages/react/src/modules/editor/hooks/useCommentEditor.ts Updates CharacterCount import to use @tiptap/extensions package
packages/react/src/modules/editor/components/Toolbar/TableToolbar.tsx Updates FloatingMenu import path and refactors tippyOptions to options with new middleware format
packages/react/src/modules/editor/components/Toolbar/LinkToolbar.tsx Updates FloatingMenu import and references renamed FloatingOptions file
packages/react/src/modules/editor/components/Toolbar/LinkToolbar.TippyOptions.ts Removed file, replaced by LinkToolbar.FloatingOptions.ts
packages/react/src/modules/editor/components/Toolbar/LinkToolbar.FloatingOptions.ts New file with refactored floating menu configuration using middleware pattern
packages/react/src/modules/editor/components/BubbleMenuEditInformationPane/BubbleMenuEditInformationPane.tsx Updates BubbleMenu to use new API with separate reference object and getReferencedVirtualElement prop
packages/react/src/modules/editor/components/BubbleMenuEditImage/BubbleMenuEditImage.tsx Updates BubbleMenu API and changes setAttributes to updateAttributes for custom-image
packages/extensions/src/table-cell/table-cell.ts Updates TableCell import from @tiptap/extension-table-cell to @tiptap/extension-table
Comments suppressed due to low confidence (1)

packages/react/src/modules/editor/components/Toolbar/TableToolbar.tsx:79

  • The getReferenceClientRect function should not be part of floatingOptions. Based on the pattern used in BubbleMenuEditImage and BubbleMenuEditInformationPane, it should be extracted into a separate reference object and passed via the getReferencedVirtualElement prop:
const reference = useMemo(() => {
  // Adjust a DOMRect to make it visible at a correct place.
  function adjustRect(rect: DOMRect) {
    let yOffset = 0;
    if (window.visualViewport) {
      const bottomScreen =
        window.innerHeight || document.documentElement.clientHeight;
      if (rect.bottom >= bottomScreen) {
        yOffset += rect.bottom - bottomScreen - rect.height;
      }
    }
    return new DOMRect(rect.x, rect.y - yOffset, rect.width, rect.height);
  }
  
  return {
    getBoundingClientRect: () => {
      const parentDiv = editor?.isActive('table')
        ? findParentNodeClosestToPos(
            editor.state.selection.$anchor,
            (node) => node.type.name === 'table',
          )
        : null;

      if (parentDiv) {
        const parentDomNode = editor?.view.nodeDOM(parentDiv.pos) as
          | HTMLElement
          | undefined;

        const tableDomNode =
          parentDomNode?.querySelector('table') || parentDomNode;
        if (tableDomNode) {
          return adjustRect(tableDomNode.getBoundingClientRect());
        }
      }

      return new DOMRect(0, 0, 100, 100);
    },
  };
}, [editor]);

const floatingOptions = useMemo(() => {
  return {
    placement: 'bottom' as const,
    middleware: [
      {
        name: 'offset',
        options: { mainAxis: 0, crossAxis: 0 },
      },
    ],
    strategy: 'fixed' as const,
  };
}, []);

Then update the FloatingMenu component at line 105 to include:

getReferencedVirtualElement={() => reference}
    return {
      placement: 'bottom' as const,
      middleware: [
        {
          name: 'offset',
          options: { mainAxis: 0, crossAxis: 0 },
        },
      ],
      strategy: 'fixed' as const,
      // popperOptions: {modifiers: [ /*see popper v2 modifiers*/ ]},
      // Try to get the bounding rect of the table.
      getReferenceClientRect: () => {
        const parentDiv = editor?.isActive('table')
          ? findParentNodeClosestToPos(
              editor.state.selection.$anchor,
              (node) => node.type.name === 'table',
            )
          : null;

        // Try to retrieve the <div class="tableWrapper"> that wraps the <table>
        if (parentDiv) {
          const parentDomNode = editor?.view.nodeDOM(parentDiv.pos) as
            | HTMLElement
            | undefined;

          const tableDomNode =
            parentDomNode?.querySelector('table') || parentDomNode;
          if (tableDomNode) {
            return adjustRect(tableDomNode.getBoundingClientRect());
          }
        }

        // This should never happen... but it keeps the transpiler happy.
        return new DOMRect(0, 0, 100, 100);
      },
    };

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 9 comments.

Comments suppressed due to low confidence (1)

packages/react/src/modules/editor/components/Toolbar/LinkToolbar.tsx:1

  • Unused import useCallback.
import { useCallback, useEffect, useMemo, useState } from 'react';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +36
const getSelectedNode = () => {
const { $anchor } = editor.view.state.selection;
for (let depth = $anchor.depth; depth >= 0; depth--) {
const node = $anchor.node(depth);
if (node.type.name === 'information-pane') {
return node;
}
}
return null;
};
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getSelectedNode function is defined inside the component body and will be recreated on every render. This causes a React Hooks ESLint warning in the useEffect at line 38-41 since the function is used but not included in the dependency array. Consider wrapping this function with useCallback and adding it to the useEffect dependencies, or moving the logic directly into the useEffect to avoid potential stale closures.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +39
// Listen to both selection changes and content updates
editor.on('selectionUpdate', handleUpdate);
editor.on('transaction', handleUpdate);

return () => {
editor.off('selectionUpdate', handleUpdate);
editor.off('transaction', handleUpdate);
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Listening to both 'selectionUpdate' and 'transaction' events may cause redundant state updates since a transaction event typically includes selection updates. This could trigger unnecessary re-renders in components using this hook. Consider using only the 'update' event which fires after any editor state change, or evaluate if both listeners are truly necessary for the migration use case.

Suggested change
// Listen to both selection changes and content updates
editor.on('selectionUpdate', handleUpdate);
editor.on('transaction', handleUpdate);
return () => {
editor.off('selectionUpdate', handleUpdate);
editor.off('transaction', handleUpdate);
// Listen to all editor state changes (content and selection)
editor.on('update', handleUpdate);
return () => {
editor.off('update', handleUpdate);

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +7
export const floatingOptions = {
placement: 'bottom' as const,
middleware: [
{
name: 'offset',
options: { mainAxis: 10, crossAxis: 0 },
},
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The middleware configuration is incorrect for TipTap v3 which uses floating-ui. The middleware array should contain actual middleware functions from '@floating-ui/dom', not plain objects with name and options properties. Import the offset middleware from '@floating-ui/dom' and use it like: middleware: [offset({ mainAxis: 10, crossAxis: 0 })]. The same issue exists in other floating options configurations throughout this PR.

Suggested change
export const floatingOptions = {
placement: 'bottom' as const,
middleware: [
{
name: 'offset',
options: { mainAxis: 10, crossAxis: 0 },
},
import { offset } from '@floating-ui/dom';
export const floatingOptions = {
placement: 'bottom' as const,
middleware: [
offset({ mainAxis: 10, crossAxis: 0 }),

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +48
middleware: [
{
name: 'offset',
options: { mainAxis: 0, crossAxis: 0 },
},
],
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The middleware configuration is incorrect for TipTap v3 which uses floating-ui. The middleware array should contain actual middleware functions from '@floating-ui/dom', not plain objects. Import the offset middleware from '@floating-ui/dom' and use it like: middleware: [offset({ mainAxis: 0, crossAxis: 0 })].

Copilot uses AI. Check for mistakes.
import Focus from '@tiptap/extension-focus';
import FontFamily from '@tiptap/extension-font-family';
import Placeholder from '@tiptap/extension-placeholder';
import { TextStyleKit } from '@tiptap/extension-text-style';
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import TextStyleKit does not exist in TipTap v3. The correct import should be import TextStyle from '@tiptap/extension-text-style' (default export). Line 86 should also use TextStyle instead of TextStyleKit.

Copilot uses AI. Check for mistakes.
@@ -1,15 +1,18 @@
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useCallback import is unused and should be removed. No callback functions in this component are wrapped with useCallback.

Suggested change
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';

Copilot uses AI. Check for mistakes.
Comment on lines 41 to 52
return {
placement: 'bottom',
offset: [0, 0],
zIndex: 999,
placement: 'bottom' as const,
middleware: [
{
name: 'offset',
options: { mainAxis: 0, crossAxis: 0 },
},
],
strategy: 'fixed' as const,
// popperOptions: {modifiers: [ /*see popper v2 modifiers*/ ]},
// Try to get the bounding rect of the table.
getReferenceClientRect: () => {
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TipTap v3, the getReferenceClientRect function should not be part of the floatingOptions object. Looking at the updated patterns in BubbleMenuEditImage and BubbleMenuEditInformationPane, the correct approach is to: 1) Create a separate reference object with a getBoundingClientRect method, 2) Pass only placement, middleware, and strategy in the options prop, and 3) Pass the reference object to a getReferencedVirtualElement prop on the FloatingMenu component.

Copilot uses AI. Check for mistakes.
Comment on lines +191 to +196
middleware: [
{
name: 'offset',
options: { mainAxis: 0, crossAxis: 0 },
},
],
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The middleware configuration is incorrect for TipTap v3 which uses floating-ui. Import the offset middleware from '@floating-ui/dom' and use it like: middleware: [offset({ mainAxis: 0, crossAxis: 0 })] instead of plain objects with name and options properties.

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +198
middleware: [
{
name: 'offset',
options: { mainAxis: 0, crossAxis: 0 },
},
],
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The middleware configuration is incorrect for TipTap v3 which uses floating-ui. Import the offset middleware from '@floating-ui/dom' and use it like: middleware: [offset({ mainAxis: 0, crossAxis: 0 })] instead of plain objects with name and options properties.

Copilot uses AI. Check for mistakes.
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.

3 participants