@pie-lib/editable-html-tip-tap 1.2.0-next.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/lib/components/CharacterPicker.js +1 -0
  3. package/lib/components/CharacterPicker.js.map +1 -1
  4. package/lib/components/EditableHtml.js +84 -43
  5. package/lib/components/EditableHtml.js.map +1 -1
  6. package/lib/components/MenuBar.js +74 -43
  7. package/lib/components/MenuBar.js.map +1 -1
  8. package/lib/components/TiptapContainer.js +9 -8
  9. package/lib/components/TiptapContainer.js.map +1 -1
  10. package/lib/components/icons/TextAlign.js +2 -2
  11. package/lib/components/icons/TextAlign.js.map +1 -1
  12. package/lib/components/image/InsertImageHandler.js +10 -13
  13. package/lib/components/image/InsertImageHandler.js.map +1 -1
  14. package/lib/components/media/MediaDialog.js.map +1 -1
  15. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +6 -1
  16. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js.map +1 -1
  17. package/lib/components/respArea/DragInTheBlank/choice.js +15 -7
  18. package/lib/components/respArea/DragInTheBlank/choice.js.map +1 -1
  19. package/lib/components/respArea/ExplicitConstructedResponse.js +29 -11
  20. package/lib/components/respArea/ExplicitConstructedResponse.js.map +1 -1
  21. package/lib/components/respArea/InlineDropdown.js +35 -6
  22. package/lib/components/respArea/InlineDropdown.js.map +1 -1
  23. package/lib/components/respArea/MathTemplated.js +130 -0
  24. package/lib/components/respArea/MathTemplated.js.map +1 -0
  25. package/lib/extensions/custom-toolbar-wrapper.js +3 -2
  26. package/lib/extensions/custom-toolbar-wrapper.js.map +1 -1
  27. package/lib/extensions/div-node.js +83 -0
  28. package/lib/extensions/div-node.js.map +1 -0
  29. package/lib/extensions/ensure-empty-root-div.js +48 -0
  30. package/lib/extensions/ensure-empty-root-div.js.map +1 -0
  31. package/lib/extensions/ensure-list-item-content-is-div.js +64 -0
  32. package/lib/extensions/ensure-list-item-content-is-div.js.map +1 -0
  33. package/lib/extensions/extended-list-item.js +15 -0
  34. package/lib/extensions/extended-list-item.js.map +1 -0
  35. package/lib/extensions/extended-table-cell.js +22 -0
  36. package/lib/extensions/extended-table-cell.js.map +1 -0
  37. package/lib/extensions/extended-table.js +50 -1
  38. package/lib/extensions/extended-table.js.map +1 -1
  39. package/lib/extensions/image-component.js +102 -51
  40. package/lib/extensions/image-component.js.map +1 -1
  41. package/lib/extensions/image.js +51 -2
  42. package/lib/extensions/image.js.map +1 -1
  43. package/lib/extensions/math.js +50 -9
  44. package/lib/extensions/math.js.map +1 -1
  45. package/lib/extensions/media.js +3 -1
  46. package/lib/extensions/media.js.map +1 -1
  47. package/lib/extensions/responseArea.js +22 -13
  48. package/lib/extensions/responseArea.js.map +1 -1
  49. package/lib/styles/editorContainerStyles.js +5 -4
  50. package/lib/styles/editorContainerStyles.js.map +1 -1
  51. package/lib/utils/helper.js +17 -0
  52. package/lib/utils/helper.js.map +1 -0
  53. package/package.json +8 -8
  54. package/src/__tests__/EditableHtml.test.jsx +93 -7
  55. package/src/__tests__/index.test.jsx +11 -3
  56. package/src/components/CharacterPicker.jsx +1 -0
  57. package/src/components/EditableHtml.jsx +93 -41
  58. package/src/components/MenuBar.jsx +57 -24
  59. package/src/components/TiptapContainer.jsx +10 -8
  60. package/src/components/__tests__/CharacterPicker.test.jsx +22 -0
  61. package/src/components/__tests__/ExplicitConstructedResponse.test.jsx +55 -12
  62. package/src/components/__tests__/InlineDropdown.test.jsx +203 -10
  63. package/src/components/__tests__/InsertImageHandler.test.js +28 -21
  64. package/src/components/__tests__/MenuBar.test.jsx +32 -0
  65. package/src/components/icons/TextAlign.jsx +1 -1
  66. package/src/components/image/InsertImageHandler.js +9 -13
  67. package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +6 -1
  68. package/src/components/respArea/DragInTheBlank/choice.jsx +32 -4
  69. package/src/components/respArea/ExplicitConstructedResponse.jsx +33 -10
  70. package/src/components/respArea/InlineDropdown.jsx +45 -10
  71. package/src/components/respArea/MathTemplated.jsx +124 -0
  72. package/src/components/respArea/__tests__/MathTemplated.test.jsx +210 -0
  73. package/src/extensions/__tests__/divNode.test.js +87 -0
  74. package/src/extensions/__tests__/ensure-empty-root-div.test.js +57 -0
  75. package/src/extensions/__tests__/ensure-list-item-content-is-div.test.js +44 -0
  76. package/src/extensions/__tests__/extended-list-item.test.js +13 -0
  77. package/src/extensions/__tests__/extended-table-cell.test.js +22 -0
  78. package/src/extensions/__tests__/extended-table.test.js +98 -1
  79. package/src/extensions/__tests__/image-component.test.jsx +105 -9
  80. package/src/extensions/__tests__/image.test.js +109 -8
  81. package/src/extensions/__tests__/math.test.js +348 -0
  82. package/src/extensions/__tests__/media-node-view.test.jsx +10 -8
  83. package/src/extensions/__tests__/responseArea.test.js +291 -0
  84. package/src/extensions/custom-toolbar-wrapper.jsx +2 -2
  85. package/src/extensions/div-node.js +86 -0
  86. package/src/extensions/ensure-empty-root-div.js +47 -0
  87. package/src/extensions/ensure-list-item-content-is-div.js +62 -0
  88. package/src/extensions/extended-list-item.js +10 -0
  89. package/src/extensions/extended-table-cell.js +19 -0
  90. package/src/extensions/extended-table.js +37 -1
  91. package/src/extensions/image-component.jsx +114 -69
  92. package/src/extensions/image.js +56 -1
  93. package/src/extensions/math.js +62 -10
  94. package/src/extensions/media.js +1 -1
  95. package/src/extensions/responseArea.js +15 -12
  96. package/src/styles/editorContainerStyles.js +5 -4
  97. package/src/utils/helper.js +17 -0
  98. /package/src/components/media/{MediaDialog.js → MediaDialog.jsx} +0 -0
@@ -63,7 +63,7 @@ var styles = function styles(theme) {
63
63
  background: 'var(--black)',
64
64
  borderRadius: '0.5rem',
65
65
  color: 'var(--white)',
66
- fontFamily: "'JetBrainsMono', monospace",
66
+ fontFamily: '\'JetBrainsMono\', monospace',
67
67
  margin: '1.5rem 0',
68
68
  padding: '0.75rem 1rem',
69
69
  '& code': {
@@ -74,9 +74,10 @@ var styles = function styles(theme) {
74
74
  }
75
75
  },
76
76
  '& blockquote': {
77
- borderLeft: '3px solid var(--gray-3)',
78
- margin: '1.5rem 0',
79
- paddingLeft: '1rem'
77
+ background: '#f9f9f9',
78
+ borderLeft: '5px solid #ccc',
79
+ margin: '1.5em 10px',
80
+ padding: '.5em 10px'
80
81
  },
81
82
  '& hr': {
82
83
  border: 'none',
@@ -1 +1 @@
1
- {"version":3,"file":"editorContainerStyles.js","names":["_renderUi","require","styles","theme","root","position","padding","border","borderRadius","cursor","wordBreak","overflow","maxHeight","marginTop","margin","marginBottom","lineHeight","textWrap","fontSize","backgroundColor","color","background","fontFamily","borderLeft","paddingLeft","borderTop","tableLayout","width","borderCollapse","text","textAlign","children","editorHolder","overflowY","disabledScrollbar","display","scrollbarWidth","error","concat","palette","main","noBorder","noPadding","toolbarOnTop","_default","exports"],"sources":["../../src/styles/editorContainerStyles.js"],"sourcesContent":["import { color } from '@pie-lib/render-ui';\n\nconst styles = (theme) => ({\n root: {\n position: 'relative',\n padding: '0px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n cursor: 'text',\n '& [data-slate-editor=\"true\"]': {\n wordBreak: 'break-word',\n overflow: 'visible',\n maxHeight: '500px',\n // needed in order to be able to put the focus before a void element when it is the first one in the editor\n padding: '5px',\n },\n\n '&:first-child': {\n marginTop: 0,\n },\n\n '& ul, & ol': {\n padding: '0 1rem',\n margin: '1.25rem 1rem 1.25rem 0.4rem',\n },\n\n '& ul li p, & ol li p': {\n marginTop: '0.25em',\n marginBottom: '0.25em',\n },\n\n '& h1, & h2, & h3, & h4, & h5, & h6': {\n lineHeight: 1.1,\n marginTop: '2.5rem',\n textWrap: 'pretty',\n },\n\n '& h1, & h2': {\n marginTop: '3.5rem',\n marginBottom: '1.5rem',\n },\n\n '& h1': {\n fontSize: '1.4rem',\n },\n\n '& h2': {\n fontSize: '1.2rem',\n },\n\n '& h3': {\n fontSize: '1.1rem',\n },\n\n '& h4, & h5, & h6': {\n fontSize: '1rem',\n },\n\n '& code': {\n backgroundColor: 'var(--purple-light)',\n borderRadius: '0.4rem',\n color: 'var(--black)',\n fontSize: '0.85rem',\n padding: '0.25em 0.3em',\n },\n\n '& pre': {\n background: 'var(--black)',\n borderRadius: '0.5rem',\n color: 'var(--white)',\n fontFamily: \"'JetBrainsMono', monospace\",\n margin: '1.5rem 0',\n padding: '0.75rem 1rem',\n\n '& code': {\n background: 'none',\n color: 'inherit',\n fontSize: '0.8rem',\n padding: 0,\n },\n },\n\n '& blockquote': {\n borderLeft: '3px solid var(--gray-3)',\n margin: '1.5rem 0',\n paddingLeft: '1rem',\n },\n\n '& hr': {\n border: 'none',\n borderTop: '1px solid var(--gray-2)',\n margin: '2rem 0',\n },\n\n '& table': {\n tableLayout: 'fixed',\n width: '100%',\n borderCollapse: 'collapse',\n color: color.text(),\n backgroundColor: color.background(),\n },\n '& table:not([border=\"1\"]) tr': {\n borderTop: '1px solid #dfe2e5',\n },\n '& td, th': {\n padding: '.6em 1em',\n textAlign: 'center',\n },\n '& table:not([border=\"1\"]) td, th': {\n border: '1px solid #dfe2e5',\n },\n },\n children: {\n padding: '10px 16px',\n },\n editorHolder: {\n position: 'relative',\n padding: '0px',\n overflowY: 'auto',\n color: color.text(),\n backgroundColor: color.background(),\n },\n disabledScrollbar: {\n '&::-webkit-scrollbar': {\n display: 'none',\n },\n scrollbarWidth: 'none',\n '-ms-overflow-style': 'none',\n },\n error: {\n border: `2px solid ${theme.palette.error.main} !important`,\n },\n noBorder: {\n border: 'none',\n },\n noPadding: {\n padding: 0,\n },\n toolbarOnTop: {\n marginTop: '45px',\n },\n});\n\nexport default styles;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"editorContainerStyles.js","names":["_renderUi","require","styles","theme","root","position","padding","border","borderRadius","cursor","wordBreak","overflow","maxHeight","marginTop","margin","marginBottom","lineHeight","textWrap","fontSize","backgroundColor","color","background","fontFamily","borderLeft","borderTop","tableLayout","width","borderCollapse","text","textAlign","children","editorHolder","overflowY","disabledScrollbar","display","scrollbarWidth","error","concat","palette","main","noBorder","noPadding","toolbarOnTop","_default","exports"],"sources":["../../src/styles/editorContainerStyles.js"],"sourcesContent":["import { color } from '@pie-lib/render-ui';\n\nconst styles = (theme) => ({\n root: {\n position: 'relative',\n padding: '0px',\n border: '1px solid #ccc',\n borderRadius: '4px',\n cursor: 'text',\n '& [data-slate-editor=\"true\"]': {\n wordBreak: 'break-word',\n overflow: 'visible',\n maxHeight: '500px',\n // needed in order to be able to put the focus before a void element when it is the first one in the editor\n padding: '5px',\n },\n\n '&:first-child': {\n marginTop: 0,\n },\n\n '& ul, & ol': {\n padding: '0 1rem',\n margin: '1.25rem 1rem 1.25rem 0.4rem',\n },\n\n '& ul li p, & ol li p': {\n marginTop: '0.25em',\n marginBottom: '0.25em',\n },\n\n '& h1, & h2, & h3, & h4, & h5, & h6': {\n lineHeight: 1.1,\n marginTop: '2.5rem',\n textWrap: 'pretty',\n },\n\n '& h1, & h2': {\n marginTop: '3.5rem',\n marginBottom: '1.5rem',\n },\n\n '& h1': {\n fontSize: '1.4rem',\n },\n\n '& h2': {\n fontSize: '1.2rem',\n },\n\n '& h3': {\n fontSize: '1.1rem',\n },\n\n '& h4, & h5, & h6': {\n fontSize: '1rem',\n },\n\n '& code': {\n backgroundColor: 'var(--purple-light)',\n borderRadius: '0.4rem',\n color: 'var(--black)',\n fontSize: '0.85rem',\n padding: '0.25em 0.3em',\n },\n\n '& pre': {\n background: 'var(--black)',\n borderRadius: '0.5rem',\n color: 'var(--white)',\n fontFamily: '\\'JetBrainsMono\\', monospace',\n margin: '1.5rem 0',\n padding: '0.75rem 1rem',\n\n '& code': {\n background: 'none',\n color: 'inherit',\n fontSize: '0.8rem',\n padding: 0,\n },\n },\n\n '& blockquote': {\n background: '#f9f9f9',\n borderLeft: '5px solid #ccc',\n margin: '1.5em 10px',\n padding: '.5em 10px',\n },\n\n '& hr': {\n border: 'none',\n borderTop: '1px solid var(--gray-2)',\n margin: '2rem 0',\n },\n\n '& table': {\n tableLayout: 'fixed',\n width: '100%',\n borderCollapse: 'collapse',\n color: color.text(),\n backgroundColor: color.background(),\n },\n '& table:not([border=\"1\"]) tr': {\n borderTop: '1px solid #dfe2e5',\n },\n '& td, th': {\n padding: '.6em 1em',\n textAlign: 'center',\n },\n '& table:not([border=\"1\"]) td, th': {\n border: '1px solid #dfe2e5',\n },\n },\n children: {\n padding: '10px 16px',\n },\n editorHolder: {\n position: 'relative',\n padding: '0px',\n overflowY: 'auto',\n color: color.text(),\n backgroundColor: color.background(),\n },\n disabledScrollbar: {\n '&::-webkit-scrollbar': {\n display: 'none',\n },\n scrollbarWidth: 'none',\n '-ms-overflow-style': 'none',\n },\n error: {\n border: `2px solid ${theme.palette.error.main} !important`,\n },\n noBorder: {\n border: 'none',\n },\n noPadding: {\n padding: 0,\n },\n toolbarOnTop: {\n marginTop: '45px',\n },\n});\n\nexport default styles;\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.normalizeInitialMarkup = void 0;
7
+ var escapeHtml = function escapeHtml(str) {
8
+ return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
9
+ };
10
+ var normalizeInitialMarkup = exports.normalizeInitialMarkup = function normalizeInitialMarkup(markup) {
11
+ var trimmed = String(markup !== null && markup !== void 0 ? markup : '').trim();
12
+ if (!trimmed) return '<div></div>';
13
+ var looksLikeHtml = /<[^>]+>/.test(trimmed);
14
+ if (looksLikeHtml) return trimmed;
15
+ return "<div>".concat(escapeHtml(trimmed), "</div>");
16
+ };
17
+ //# sourceMappingURL=helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.js","names":["escapeHtml","str","String","replace","normalizeInitialMarkup","exports","markup","trimmed","trim","looksLikeHtml","test","concat"],"sources":["../../src/utils/helper.js"],"sourcesContent":["const escapeHtml = (str) =>\n String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n\nexport const normalizeInitialMarkup = (markup) => {\n const trimmed = String(markup ?? '').trim();\n if (!trimmed) return '<div></div>';\n\n const looksLikeHtml = /<[^>]+>/.test(trimmed);\n if (looksLikeHtml) return trimmed;\n\n return `<div>${escapeHtml(trimmed)}</div>`;\n};\n"],"mappings":";;;;;;AAAA,IAAMA,UAAU,GAAG,SAAbA,UAAUA,CAAIC,GAAG;EAAA,OACrBC,MAAM,CAACD,GAAG,CAAC,CACRE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CACtBA,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CACrBA,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CACrBA,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CACvBA,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;AAAA;AAEpB,IAAMC,sBAAsB,GAAAC,OAAA,CAAAD,sBAAA,GAAG,SAAzBA,sBAAsBA,CAAIE,MAAM,EAAK;EAChD,IAAMC,OAAO,GAAGL,MAAM,CAACI,MAAM,aAANA,MAAM,cAANA,MAAM,GAAI,EAAE,CAAC,CAACE,IAAI,CAAC,CAAC;EAC3C,IAAI,CAACD,OAAO,EAAE,OAAO,aAAa;EAElC,IAAME,aAAa,GAAG,SAAS,CAACC,IAAI,CAACH,OAAO,CAAC;EAC7C,IAAIE,aAAa,EAAE,OAAOF,OAAO;EAEjC,eAAAI,MAAA,CAAeX,UAAU,CAACO,OAAO,CAAC;AACpC,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.0-next.8",
6
+ "version": "2.0.0",
7
7
  "description": "",
8
8
  "license": "ISC",
9
9
  "main": "lib/index.js",
@@ -16,11 +16,11 @@
16
16
  "@dnd-kit/utilities": "3.2.2",
17
17
  "@mui/icons-material": "^7.3.4",
18
18
  "@mui/material": "^7.3.4",
19
- "@pie-lib/drag": "^3.2.0-next.2",
20
- "@pie-lib/math-input": "^7.2.0-next.2",
21
- "@pie-lib/math-rendering": "^4.2.0-next.1",
22
- "@pie-lib/math-toolbar": "^2.2.0-next.3",
23
- "@pie-lib/render-ui": "^5.2.0-next.2",
19
+ "@pie-lib/drag": "^4.0.0",
20
+ "@pie-lib/math-input": "^8.0.0",
21
+ "@pie-lib/math-rendering": "^5.0.0",
22
+ "@pie-lib/math-toolbar": "^3.0.0",
23
+ "@pie-lib/render-ui": "^6.0.0",
24
24
  "@tiptap/core": "3.0.9",
25
25
  "@tiptap/extension-character-count": "3.0.9",
26
26
  "@tiptap/extension-color": "3.0.9",
@@ -52,13 +52,13 @@
52
52
  "to-style": "^1.3.3"
53
53
  },
54
54
  "devDependencies": {
55
- "@pie-framework/mathquill": "^1.1.3",
55
+ "@pie-framework/mathquill": "1.2.1-beta.1",
56
56
  "react": "^18.2.0",
57
57
  "react-dom": "^18.2.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "react": "^18.2.0"
61
61
  },
62
- "gitHead": "01fa85c5f94bfdef2cd95f49bd465ec0b702fe6d",
62
+ "gitHead": "bf0904ba8bdd2d6f88fb8ad99060e45e40180348",
63
63
  "scripts": {}
64
64
  }
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { render, waitFor } from '@testing-library/react';
3
+ import { useEditor } from '@tiptap/react';
3
4
  import { EditableHtml } from '../components/EditableHtml';
4
5
 
5
6
  // Mock TipTap dependencies
@@ -24,7 +25,9 @@ jest.mock('@tiptap/react', () => ({
24
25
 
25
26
  jest.mock('@tiptap/starter-kit', () => ({
26
27
  __esModule: true,
27
- default: {},
28
+ default: {
29
+ configure: jest.fn(() => ({})),
30
+ },
28
31
  }));
29
32
 
30
33
  jest.mock('@tiptap/extension-text-style', () => ({
@@ -68,12 +71,9 @@ jest.mock('@tiptap/extension-table-row', () => ({
68
71
  TableRow: {},
69
72
  }));
70
73
 
71
- jest.mock('@tiptap/extension-table-cell', () => ({
72
- TableCell: {},
73
- }));
74
-
75
- jest.mock('@tiptap/extension-table-header', () => ({
76
- TableHeader: {},
74
+ jest.mock('../extensions/extended-table-cell', () => ({
75
+ ExtendedTableCell: {},
76
+ ExtendedTableHeader: {},
77
77
  }));
78
78
 
79
79
  jest.mock('../extensions/extended-table', () => ({
@@ -81,6 +81,18 @@ jest.mock('../extensions/extended-table', () => ({
81
81
  default: {},
82
82
  }));
83
83
 
84
+ jest.mock('../extensions/ensure-empty-root-div', () => ({
85
+ EnsureEmptyRootIsDiv: {},
86
+ }));
87
+
88
+ jest.mock('../extensions/extended-list-item', () => ({
89
+ ExtendedListItem: {},
90
+ }));
91
+
92
+ jest.mock('../extensions/ensure-list-item-content-is-div', () => ({
93
+ EnsureListItemContentIsDiv: {},
94
+ }));
95
+
84
96
  jest.mock('../extensions/responseArea', () => ({
85
97
  ExplicitConstructedResponseNode: {
86
98
  configure: jest.fn(() => ({})),
@@ -91,6 +103,9 @@ jest.mock('../extensions/responseArea', () => ({
91
103
  InlineDropdownNode: {
92
104
  configure: jest.fn(() => ({})),
93
105
  },
106
+ MathTemplatedNode: {
107
+ configure: jest.fn(() => ({})),
108
+ },
94
109
  ResponseAreaExtension: {
95
110
  configure: jest.fn(() => ({})),
96
111
  },
@@ -263,4 +278,75 @@ describe('EditableHtml', () => {
263
278
  const { container } = render(<EditableHtml {...defaultProps} disableImageAlignmentButtons={true} />);
264
279
  expect(container).toBeInTheDocument();
265
280
  });
281
+
282
+ it('calls editorRef callback when editor is initialized', async () => {
283
+ const editorRef = jest.fn();
284
+ render(<EditableHtml {...defaultProps} editorRef={editorRef} />);
285
+
286
+ await waitFor(() => {
287
+ expect(editorRef).toHaveBeenCalled();
288
+ });
289
+ });
290
+
291
+ it('calls editorRef with the editor instance', async () => {
292
+ const editorRef = jest.fn();
293
+ render(<EditableHtml {...defaultProps} editorRef={editorRef} />);
294
+
295
+ await waitFor(() => {
296
+ expect(editorRef).toHaveBeenCalled();
297
+ // Verify it was called with an object that has editor-like properties
298
+ const callArg = editorRef.mock.calls[0][0];
299
+ expect(callArg).toHaveProperty('getHTML');
300
+ expect(callArg).toHaveProperty('commands');
301
+ });
302
+ });
303
+
304
+ it('handles editorRef being undefined', () => {
305
+ const { container } = render(<EditableHtml {...defaultProps} editorRef={undefined} />);
306
+ expect(container).toBeInTheDocument();
307
+ });
308
+
309
+ it('applies flex display to StyledEditorContent', async () => {
310
+ const { getByTestId } = render(<EditableHtml {...defaultProps} />);
311
+ await waitFor(() => {
312
+ const editorContent = getByTestId('editor-content');
313
+ expect(editorContent).toBeInTheDocument();
314
+ });
315
+ });
316
+
317
+ it('does not run blur onChange/onDone while an image insert flow is active', async () => {
318
+ jest.useFakeTimers();
319
+ const onChange = jest.fn();
320
+ const onDone = jest.fn();
321
+
322
+ render(
323
+ <EditableHtml
324
+ {...defaultProps}
325
+ markup="<p>Hello World</p>"
326
+ onChange={onChange}
327
+ onDone={onDone}
328
+ toolbarOpts={{ ...defaultProps.toolbarOpts, doneOn: 'blur' }}
329
+ />,
330
+ );
331
+
332
+ await waitFor(() => {
333
+ expect(useEditor).toHaveBeenCalled();
334
+ });
335
+
336
+ const editorConfig = useEditor.mock.calls[useEditor.mock.calls.length - 1][0];
337
+ const blurEditor = {
338
+ getHTML: jest.fn(() => '<p>changed</p>'),
339
+ _insertingImage: true,
340
+ _toolbarOpened: false,
341
+ isActive: jest.fn(() => false),
342
+ };
343
+
344
+ editorConfig.onBlur({ editor: blurEditor });
345
+ jest.advanceTimersByTime(200);
346
+
347
+ expect(onChange).not.toHaveBeenCalled();
348
+ expect(onDone).not.toHaveBeenCalled();
349
+
350
+ jest.useRealTimers();
351
+ });
266
352
  });
@@ -13,7 +13,10 @@ jest.mock('@tiptap/react', () => ({
13
13
  useEditorState: jest.fn(() => ({ isFocused: false })),
14
14
  }));
15
15
 
16
- jest.mock('@tiptap/starter-kit', () => ({ __esModule: true, default: {} }));
16
+ jest.mock('@tiptap/starter-kit', () => ({
17
+ __esModule: true,
18
+ default: { configure: jest.fn(() => ({})) },
19
+ }));
17
20
  jest.mock('@tiptap/extension-text-style', () => ({ TextStyleKit: {} }));
18
21
  jest.mock('@tiptap/extension-character-count', () => ({
19
22
  CharacterCount: { configure: jest.fn(() => ({})) },
@@ -27,9 +30,14 @@ jest.mock('@tiptap/extension-text-align', () => ({
27
30
  jest.mock('@tiptap/extension-image', () => ({ __esModule: true, default: {} }));
28
31
  jest.mock('@tiptap/extension-table', () => ({ __esModule: true, default: {} }));
29
32
  jest.mock('@tiptap/extension-table-row', () => ({ TableRow: {} }));
30
- jest.mock('@tiptap/extension-table-cell', () => ({ TableCell: {} }));
31
- jest.mock('@tiptap/extension-table-header', () => ({ TableHeader: {} }));
33
+ jest.mock('../extensions/extended-table-cell', () => ({
34
+ ExtendedTableCell: {},
35
+ ExtendedTableHeader: {},
36
+ }));
32
37
  jest.mock('../extensions/extended-table', () => ({ __esModule: true, default: {} }));
38
+ jest.mock('../extensions/ensure-empty-root-div', () => ({ EnsureEmptyRootIsDiv: {} }));
39
+ jest.mock('../extensions/extended-list-item', () => ({ ExtendedListItem: {} }));
40
+ jest.mock('../extensions/ensure-list-item-content-is-div', () => ({ EnsureListItemContentIsDiv: {} }));
33
41
  jest.mock('../extensions/responseArea', () => ({
34
42
  ExplicitConstructedResponseNode: { configure: jest.fn(() => ({})) },
35
43
  DragInTheBlankNode: { configure: jest.fn(() => ({})) },
@@ -121,6 +121,7 @@ export function CharacterPicker({ editor, opts, onClose }) {
121
121
  <div
122
122
  ref={containerRef}
123
123
  className="insert-character-dialog"
124
+ data-toolbar-for={editor.instanceId}
124
125
  style={{
125
126
  visibility: position.top === 0 && position.left === 0 ? 'hidden' : 'initial',
126
127
  position: 'absolute',
@@ -1,5 +1,7 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import debounce from 'lodash-es/debounce';
2
3
  import { EditorContent, useEditor, useEditorState } from '@tiptap/react';
4
+ import { styled } from '@mui/material/styles';
3
5
  import StarterKit from '@tiptap/starter-kit';
4
6
  import { TextStyleKit } from '@tiptap/extension-text-style';
5
7
  import { CharacterCount } from '@tiptap/extension-character-count';
@@ -8,23 +10,26 @@ import SubScript from '@tiptap/extension-subscript';
8
10
  import TextAlign from '@tiptap/extension-text-align';
9
11
  import Image from '@tiptap/extension-image';
10
12
  import Placeholder from '@tiptap/extension-placeholder';
11
- import { styled } from '@mui/material/styles';
12
- import debounce from 'lodash-es/debounce';
13
+ import { normalizeInitialMarkup } from '../utils/helper';
13
14
 
14
15
  import ExtendedTable from '../extensions/extended-table';
16
+ import { ExtendedTableCell, ExtendedTableHeader } from '../extensions/extended-table-cell';
17
+ import { DivNode } from '../extensions/div-node';
18
+ import { EnsureEmptyRootIsDiv } from '../extensions/ensure-empty-root-div';
19
+ import { EnsureListItemContentIsDiv } from '../extensions/ensure-list-item-content-is-div';
15
20
  import { TableRow } from '@tiptap/extension-table-row';
16
- import { TableCell } from '@tiptap/extension-table-cell';
17
- import { TableHeader } from '@tiptap/extension-table-header';
18
21
  import {
19
22
  DragInTheBlankNode,
20
23
  ExplicitConstructedResponseNode,
21
24
  InlineDropdownNode,
25
+ MathTemplatedNode,
22
26
  ResponseAreaExtension,
23
27
  } from '../extensions/responseArea';
24
28
  import { MathNode } from '../extensions/math';
25
29
  import { ImageUploadNode } from '../extensions/image';
26
30
  import { Media } from '../extensions/media';
27
31
  import { CSSMark } from '../extensions/css';
32
+ import { ExtendedListItem } from '../extensions/extended-list-item';
28
33
 
29
34
  import EditorContainer from './TiptapContainer';
30
35
  import { valueToSize } from '../utils/size';
@@ -96,6 +101,19 @@ export const EditableHtml = (props) => {
96
101
  const [scheduled, setScheduled] = useState(false);
97
102
  const { toolbarOpts } = props;
98
103
 
104
+ const removePendingImage = useCallback(
105
+ (imagePos) => {
106
+ setPendingImages((prev) => {
107
+ const next = prev.filter((img) => img.pos !== imagePos);
108
+ if (next.length === 0) {
109
+ setScheduled(false);
110
+ }
111
+ return next;
112
+ });
113
+ },
114
+ [setPendingImages],
115
+ );
116
+
99
117
  const toolbarOptsToUse = {
100
118
  ...defaultToolbarOpts,
101
119
  ...toolbarOpts,
@@ -134,11 +152,24 @@ export const EditableHtml = (props) => {
134
152
  }, [props]);
135
153
 
136
154
  const extensions = [
155
+ TextAlign.configure({
156
+ types: ['heading', 'paragraph', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'th'],
157
+ alignments: ['left', 'right', 'center', 'justify'],
158
+ }),
137
159
  TextStyleKit,
138
160
  CharacterCount.configure({
139
161
  limit: props.charactersLimit || 1000000,
140
162
  }),
141
- StarterKit,
163
+ StarterKit.configure({
164
+ trailingNode: {
165
+ node: 'paragraph',
166
+ notAfter: ['paragraph', 'div'],
167
+ },
168
+ }),
169
+ ExtendedListItem,
170
+ DivNode,
171
+ EnsureEmptyRootIsDiv,
172
+ EnsureListItemContentIsDiv,
142
173
  Placeholder.configure({
143
174
  placeholder: props.placeholder,
144
175
  // show placeholder even when editor is focused
@@ -148,27 +179,25 @@ export const EditableHtml = (props) => {
148
179
  }),
149
180
  ExtendedTable,
150
181
  TableRow,
151
- TableHeader,
152
- TableCell,
182
+ ExtendedTableHeader,
183
+ ExtendedTableCell,
153
184
  ResponseAreaExtension.configure(props.responseAreaProps),
154
185
  ExplicitConstructedResponseNode.configure(props.responseAreaProps),
155
186
  DragInTheBlankNode.configure(props.responseAreaProps),
156
187
  InlineDropdownNode.configure(props.responseAreaProps),
188
+ MathTemplatedNode.configure(props.responseAreaProps),
157
189
  MathNode.configure({
158
190
  toolbarOpts: toolbarOptsToUse,
191
+ math: props.pluginProps?.math || {},
159
192
  }),
160
193
  SubScript,
161
194
  SuperScript,
162
- TextAlign.configure({
163
- types: ['heading', 'paragraph'],
164
- alignments: ['left', 'right', 'center'],
165
- }),
166
195
  Image,
167
196
  ImageUploadNode.configure({
168
197
  toolbarOpts: toolbarOptsToUse,
169
198
  imageHandling: {
170
199
  disableImageAlignmentButtons: props.disableImageAlignmentButtons,
171
- onDone: () => props.onDone?.(editor.getHTML()),
200
+ onDone: (editor) => props.onDone?.(editor.getHTML()),
172
201
  onDelete:
173
202
  props.imageSupport &&
174
203
  props.imageSupport.delete &&
@@ -176,19 +205,14 @@ export const EditableHtml = (props) => {
176
205
  const { src } = node.attrs;
177
206
 
178
207
  props.imageSupport.delete(src, (e) => {
179
- const newPendingImages = pendingImages.filter((img) => img.key !== node.key);
180
- const newState = {
181
- pendingImages: newPendingImages,
182
- scheduled: scheduled && newPendingImages.length === 0 ? false : scheduled,
183
- };
184
-
185
- setPendingImages(newState.pendingImages);
186
- setScheduled(newState.scheduled);
208
+ removePendingImage(node.pos);
187
209
  });
188
210
  }),
189
211
  insertImageRequested:
190
212
  props.imageSupport &&
191
- ((addedImage, getHandler) => {
213
+ ((editor, imageInfo, getHandler) => {
214
+ const [addedImage, pos] = imageInfo;
215
+
192
216
  const onFinish = (result) => {
193
217
  let cb;
194
218
 
@@ -197,29 +221,39 @@ export const EditableHtml = (props) => {
197
221
  cb = props.onChange;
198
222
  }
199
223
 
200
- const newPendingImages = pendingImages.filter((img) => img.key !== addedImage.key);
201
- const newState = {
202
- pendingImages: newPendingImages,
203
- };
204
-
205
- if (newPendingImages.length === 0) {
206
- newState.scheduled = false;
207
- }
208
-
209
- setPendingImages(newState.pendingImages);
210
- setScheduled(newState.scheduled);
224
+ removePendingImage(pos);
211
225
  cb?.(editor.getHTML());
212
226
  };
227
+
213
228
  const callback = () => {
214
229
  /**
215
230
  * The handler is the object through which the outer context
216
231
  * communicates file upload events like: fileChosen, cancel, progress
217
232
  */
218
233
  const handler = getHandler(onFinish);
234
+
235
+ // If the user closes the file picker without choosing a file, the window regains
236
+ // focus while _insertingImage is still true — drop the stale pending entry.
237
+ const focusHandler = debounce(() => {
238
+ const detach = () => window.removeEventListener('focus', focusHandler);
239
+
240
+ if (!editor._insertingImage) {
241
+ detach();
242
+ return;
243
+ }
244
+
245
+ removePendingImage(pos);
246
+ editor._insertingImage = false;
247
+ detach();
248
+ }, 500);
249
+
250
+ window.addEventListener('focus', focusHandler);
251
+
219
252
  props.imageSupport.add(handler);
220
253
  };
221
254
 
222
- setPendingImages([...pendingImages, addedImage]);
255
+ editor._insertingImage = true;
256
+ setPendingImages((prev) => [...prev, addedImage]);
223
257
  callback();
224
258
  }),
225
259
  maxImageWidth: props.maxImageWidth,
@@ -250,7 +284,7 @@ export const EditableHtml = (props) => {
250
284
  },
251
285
  },
252
286
  editable: !props.disabled,
253
- content: props.markup,
287
+ content: normalizeInitialMarkup(props.markup),
254
288
  onUpdate: ({ editor, transaction }) => {
255
289
  if (transaction.isDone) {
256
290
  props.onChange?.(editor.getHTML());
@@ -258,6 +292,7 @@ export const EditableHtml = (props) => {
258
292
  },
259
293
  onBlur: debounce(({ editor }) => {
260
294
  const otherToolbarOpened =
295
+ editor._insertingImage ||
261
296
  editor._toolbarOpened ||
262
297
  editor.isActive('inline_dropdown') ||
263
298
  editor.isActive('explicit_constructed_response');
@@ -278,6 +313,12 @@ export const EditableHtml = (props) => {
278
313
  [props.charactersLimit],
279
314
  );
280
315
 
316
+ useEffect(() => {
317
+ if (props.editorRef) {
318
+ props.editorRef(editor);
319
+ }
320
+ }, [props.editorRef, editor]);
321
+
281
322
  useEffect(() => {
282
323
  editor?.setEditable(!props.disabled);
283
324
  }, [props.disabled, editor]);
@@ -286,9 +327,10 @@ export const EditableHtml = (props) => {
286
327
  if (!editor) {
287
328
  return;
288
329
  }
330
+ const nextMarkup = normalizeInitialMarkup(props.markup);
289
331
 
290
- if (props.markup !== editor.getHTML()) {
291
- editor.commands.setContent(props.markup, false); // false = don’t emit update
332
+ if (nextMarkup !== editor.getHTML()) {
333
+ editor.commands.setContent(nextMarkup, false);
292
334
  }
293
335
  }, [props.markup, editor]);
294
336
 
@@ -347,26 +389,36 @@ export const EditableHtml = (props) => {
347
389
  const StyledEditorContent = styled(EditorContent, {
348
390
  shouldForwardProp: (prop) => !['showParagraph', 'separateParagraph'].includes(prop),
349
391
  })(({ showParagraph, separateParagraph }) => ({
392
+ display: 'flex',
350
393
  outline: 'none !important',
351
394
  '& .ProseMirror': {
395
+ flex: 1,
352
396
  padding: '5px',
353
397
  maxHeight: '500px',
354
398
  outline: 'none !important',
355
399
  position: 'initial',
356
- '& > p': {
400
+
401
+ // reset default margins for all block paragraphs/divs in the editor
402
+ '& > p, & > div': {
357
403
  margin: '0',
358
404
  },
359
405
 
360
- '& p.is-editor-empty:first-child::before': {
406
+ // Out of flow so the caret stays at the start of the block; in-flow ::before pushes the caret after the hint text.
407
+ '& p.is-editor-empty, & div.is-editor-empty': {
408
+ position: 'relative',
409
+ },
410
+ '& p.is-editor-empty::before, & div.is-editor-empty::before': {
361
411
  content: 'attr(data-placeholder)',
362
- display: 'block',
412
+ position: 'absolute',
413
+ left: 0,
414
+ top: 0,
363
415
  color: '#9CA3AF',
364
416
  pointerEvents: 'none',
365
417
  whiteSpace: 'pre-wrap',
366
418
  },
367
419
 
368
420
  ...(showParagraph && {
369
- '& > p:has(+ p)::after': {
421
+ '& > p:has(+ p)::after, & > div:has(+ div)::after': {
370
422
  display: 'block',
371
423
  content: '"¶"',
372
424
  fontSize: '1em',