-
Notifications
You must be signed in to change notification settings - Fork 4
chore: #PEDAGO-3463, update package tiptap v2 to v3 and initialize in… #422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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
getReferenceClientRectfunction should not be part offloatingOptions. Based on the pattern used in BubbleMenuEditImage and BubbleMenuEditInformationPane, it should be extracted into a separatereferenceobject and passed via thegetReferencedVirtualElementprop:
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.
f3f748c to
6310b8c
Compare
There was a problem hiding this 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.
| 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; | ||
| }; |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| // 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); |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| // 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); |
| export const floatingOptions = { | ||
| placement: 'bottom' as const, | ||
| middleware: [ | ||
| { | ||
| name: 'offset', | ||
| options: { mainAxis: 10, crossAxis: 0 }, | ||
| }, |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| 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 }), |
| middleware: [ | ||
| { | ||
| name: 'offset', | ||
| options: { mainAxis: 0, crossAxis: 0 }, | ||
| }, | ||
| ], |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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 })].
| 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'; |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| @@ -1,15 +1,18 @@ | |||
| import { useEffect, useMemo, useState } from 'react'; | |||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |
| import { useEffect, useMemo, useState } from 'react'; |
| 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: () => { |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| middleware: [ | ||
| { | ||
| name: 'offset', | ||
| options: { mainAxis: 0, crossAxis: 0 }, | ||
| }, | ||
| ], |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
| middleware: [ | ||
| { | ||
| name: 'offset', | ||
| options: { mainAxis: 0, crossAxis: 0 }, | ||
| }, | ||
| ], |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
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.
b2c3329 to
ab43d2f
Compare
1e9dc29 to
43de3d0
Compare
… 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
Has the documentation changed?
Type of change
Please check options that are relevant.
Checklist: