@squiz/formatted-text-editor 0.0.0-rbv2-20240530041851

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 (457) hide show
  1. package/.eslintrc.json +41 -0
  2. package/CHANGELOG.md +155 -0
  3. package/README.md +74 -0
  4. package/build.js +21 -0
  5. package/coverage/clover.xml +1544 -0
  6. package/coverage/coverage-final.json +74 -0
  7. package/coverage/lcov-report/base.css +224 -0
  8. package/coverage/lcov-report/block-navigation.js +87 -0
  9. package/coverage/lcov-report/favicon.png +0 -0
  10. package/coverage/lcov-report/index.html +911 -0
  11. package/coverage/lcov-report/prettify.css +1 -0
  12. package/coverage/lcov-report/prettify.js +2 -0
  13. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  14. package/coverage/lcov-report/sorter.js +196 -0
  15. package/coverage/lcov-report/src/Editor/Editor.tsx.html +436 -0
  16. package/coverage/lcov-report/src/Editor/EditorContext.ts.html +145 -0
  17. package/coverage/lcov-report/src/Editor/index.html +131 -0
  18. package/coverage/lcov-report/src/EditorToolbar/FloatingToolbar.tsx.html +271 -0
  19. package/coverage/lcov-report/src/EditorToolbar/Toolbar.tsx.html +265 -0
  20. package/coverage/lcov-report/src/EditorToolbar/Tools/Bold/BoldButton.tsx.html +178 -0
  21. package/coverage/lcov-report/src/EditorToolbar/Tools/Bold/index.html +116 -0
  22. package/coverage/lcov-report/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.tsx.html +229 -0
  23. package/coverage/lcov-report/src/EditorToolbar/Tools/ClearFormatting/index.html +116 -0
  24. package/coverage/lcov-report/src/EditorToolbar/Tools/HorizontalLine/HorizontalLineButton.tsx.html +187 -0
  25. package/coverage/lcov-report/src/EditorToolbar/Tools/HorizontalLine/index.html +116 -0
  26. package/coverage/lcov-report/src/EditorToolbar/Tools/Image/Form/ImageForm.tsx.html +688 -0
  27. package/coverage/lcov-report/src/EditorToolbar/Tools/Image/Form/index.html +116 -0
  28. package/coverage/lcov-report/src/EditorToolbar/Tools/Image/ImageButton.tsx.html +301 -0
  29. package/coverage/lcov-report/src/EditorToolbar/Tools/Image/ImageModal.tsx.html +181 -0
  30. package/coverage/lcov-report/src/EditorToolbar/Tools/Image/index.html +131 -0
  31. package/coverage/lcov-report/src/EditorToolbar/Tools/Italic/ItalicButton.tsx.html +178 -0
  32. package/coverage/lcov-report/src/EditorToolbar/Tools/Italic/index.html +116 -0
  33. package/coverage/lcov-report/src/EditorToolbar/Tools/Link/Form/LinkForm.tsx.html +508 -0
  34. package/coverage/lcov-report/src/EditorToolbar/Tools/Link/Form/index.html +116 -0
  35. package/coverage/lcov-report/src/EditorToolbar/Tools/Link/LinkButton.tsx.html +286 -0
  36. package/coverage/lcov-report/src/EditorToolbar/Tools/Link/LinkModal.tsx.html +196 -0
  37. package/coverage/lcov-report/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx.html +211 -0
  38. package/coverage/lcov-report/src/EditorToolbar/Tools/Link/index.html +146 -0
  39. package/coverage/lcov-report/src/EditorToolbar/Tools/Lists/ListButtons.tsx.html +127 -0
  40. package/coverage/lcov-report/src/EditorToolbar/Tools/Lists/OrderedList/OrderedListButton.tsx.html +175 -0
  41. package/coverage/lcov-report/src/EditorToolbar/Tools/Lists/OrderedList/index.html +116 -0
  42. package/coverage/lcov-report/src/EditorToolbar/Tools/Lists/UnorderedList/UnorderedListButton.tsx.html +175 -0
  43. package/coverage/lcov-report/src/EditorToolbar/Tools/Lists/UnorderedList/index.html +116 -0
  44. package/coverage/lcov-report/src/EditorToolbar/Tools/Lists/index.html +116 -0
  45. package/coverage/lcov-report/src/EditorToolbar/Tools/Redo/RedoButton.tsx.html +178 -0
  46. package/coverage/lcov-report/src/EditorToolbar/Tools/Redo/index.html +116 -0
  47. package/coverage/lcov-report/src/EditorToolbar/Tools/Table/TableButton.tsx.html +181 -0
  48. package/coverage/lcov-report/src/EditorToolbar/Tools/Table/index.html +116 -0
  49. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.tsx.html +178 -0
  50. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/CenterAlign/index.html +116 -0
  51. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.tsx.html +178 -0
  52. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/JustifyAlign/index.html +116 -0
  53. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.tsx.html +178 -0
  54. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/LeftAlign/index.html +116 -0
  55. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.tsx.html +178 -0
  56. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/RightAlign/index.html +116 -0
  57. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/TextAlignButtons.tsx.html +148 -0
  58. package/coverage/lcov-report/src/EditorToolbar/Tools/TextAlign/index.html +116 -0
  59. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.tsx.html +181 -0
  60. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/CodeBlock/index.html +116 -0
  61. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/Heading/HeadingButton.tsx.html +241 -0
  62. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/Heading/index.html +116 -0
  63. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.tsx.html +160 -0
  64. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/Paragraph/index.html +116 -0
  65. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.tsx.html +181 -0
  66. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/Preformatted/index.html +116 -0
  67. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/TextTypeDropdown.tsx.html +247 -0
  68. package/coverage/lcov-report/src/EditorToolbar/Tools/TextType/index.html +116 -0
  69. package/coverage/lcov-report/src/EditorToolbar/Tools/Underline/UnderlineButton.tsx.html +178 -0
  70. package/coverage/lcov-report/src/EditorToolbar/Tools/Underline/index.html +116 -0
  71. package/coverage/lcov-report/src/EditorToolbar/Tools/Undo/UndoButton.tsx.html +178 -0
  72. package/coverage/lcov-report/src/EditorToolbar/Tools/Undo/index.html +116 -0
  73. package/coverage/lcov-report/src/EditorToolbar/index.html +146 -0
  74. package/coverage/lcov-report/src/EditorToolbar/index.ts.html +91 -0
  75. package/coverage/lcov-report/src/Extensions/ClearFormattingExtension/ClearFormattingExtension.ts.html +256 -0
  76. package/coverage/lcov-report/src/Extensions/ClearFormattingExtension/index.html +116 -0
  77. package/coverage/lcov-report/src/Extensions/CodeBlockExtension/CodeBlockExtension.ts.html +187 -0
  78. package/coverage/lcov-report/src/Extensions/CodeBlockExtension/index.html +116 -0
  79. package/coverage/lcov-report/src/Extensions/CommandsExtension/CommandsExtension.ts.html +247 -0
  80. package/coverage/lcov-report/src/Extensions/CommandsExtension/index.html +116 -0
  81. package/coverage/lcov-report/src/Extensions/Extensions.ts.html +349 -0
  82. package/coverage/lcov-report/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts.html +289 -0
  83. package/coverage/lcov-report/src/Extensions/FetchUrlExtension/index.html +116 -0
  84. package/coverage/lcov-report/src/Extensions/ImageExtension/AssetImageExtension.ts.html +412 -0
  85. package/coverage/lcov-report/src/Extensions/ImageExtension/ImageExtension.ts.html +142 -0
  86. package/coverage/lcov-report/src/Extensions/ImageExtension/index.html +131 -0
  87. package/coverage/lcov-report/src/Extensions/LinkExtension/AssetLinkExtension.ts.html +466 -0
  88. package/coverage/lcov-report/src/Extensions/LinkExtension/LinkExtension.ts.html +400 -0
  89. package/coverage/lcov-report/src/Extensions/LinkExtension/common.ts.html +115 -0
  90. package/coverage/lcov-report/src/Extensions/LinkExtension/index.html +146 -0
  91. package/coverage/lcov-report/src/Extensions/PreformattedExtension/PreformattedExtension.ts.html +340 -0
  92. package/coverage/lcov-report/src/Extensions/PreformattedExtension/index.html +116 -0
  93. package/coverage/lcov-report/src/Extensions/UnsuportedExtension/UnsupportedNodeExtension.tsx.html +325 -0
  94. package/coverage/lcov-report/src/Extensions/UnsuportedExtension/index.html +116 -0
  95. package/coverage/lcov-report/src/Extensions/index.html +116 -0
  96. package/coverage/lcov-report/src/hooks/index.html +161 -0
  97. package/coverage/lcov-report/src/hooks/index.ts.html +91 -0
  98. package/coverage/lcov-report/src/hooks/useExpandedSelection.ts.html +217 -0
  99. package/coverage/lcov-report/src/hooks/useExtensionNames.ts.html +130 -0
  100. package/coverage/lcov-report/src/hooks/useFocus.ts.html +268 -0
  101. package/coverage/lcov-report/src/index.html +116 -0
  102. package/coverage/lcov-report/src/index.ts.html +112 -0
  103. package/coverage/lcov-report/src/ui/Button/Button.tsx.html +190 -0
  104. package/coverage/lcov-report/src/ui/Button/index.html +116 -0
  105. package/coverage/lcov-report/src/ui/CollapseBox/CollapseBox.tsx.html +193 -0
  106. package/coverage/lcov-report/src/ui/CollapseBox/index.html +116 -0
  107. package/coverage/lcov-report/src/ui/Fields/Checkbox/Checkbox.tsx.html +232 -0
  108. package/coverage/lcov-report/src/ui/Fields/Checkbox/index.html +116 -0
  109. package/coverage/lcov-report/src/ui/Fields/Input/Input.tsx.html +160 -0
  110. package/coverage/lcov-report/src/ui/Fields/Input/index.html +116 -0
  111. package/coverage/lcov-report/src/ui/Fields/InputContainer/InputContainer.tsx.html +172 -0
  112. package/coverage/lcov-report/src/ui/Fields/InputContainer/index.html +116 -0
  113. package/coverage/lcov-report/src/ui/Fields/MatrixAsset/MatrixAsset.tsx.html +256 -0
  114. package/coverage/lcov-report/src/ui/Fields/MatrixAsset/index.html +116 -0
  115. package/coverage/lcov-report/src/ui/Fields/Select/Select.tsx.html +283 -0
  116. package/coverage/lcov-report/src/ui/Fields/Select/index.html +116 -0
  117. package/coverage/lcov-report/src/ui/Modal/FormModal.tsx.html +136 -0
  118. package/coverage/lcov-report/src/ui/Modal/Modal.tsx.html +397 -0
  119. package/coverage/lcov-report/src/ui/Modal/index.html +131 -0
  120. package/coverage/lcov-report/src/ui/Tabs/Tabs.tsx.html +208 -0
  121. package/coverage/lcov-report/src/ui/Tabs/index.html +116 -0
  122. package/coverage/lcov-report/src/ui/ToolbarDropdown/ToolbarDropdown.tsx.html +211 -0
  123. package/coverage/lcov-report/src/ui/ToolbarDropdown/index.html +116 -0
  124. package/coverage/lcov-report/src/ui/ToolbarDropdownButton/ToolbarDropdownButton.tsx.html +184 -0
  125. package/coverage/lcov-report/src/ui/ToolbarDropdownButton/index.html +116 -0
  126. package/coverage/lcov-report/src/utils/converters/htmlToSquizNode/htmlToSquizNode.ts.html +166 -0
  127. package/coverage/lcov-report/src/utils/converters/htmlToSquizNode/index.html +116 -0
  128. package/coverage/lcov-report/src/utils/converters/mocks/index.html +116 -0
  129. package/coverage/lcov-report/src/utils/converters/mocks/squizNodeJson.mock.ts.html +895 -0
  130. package/coverage/lcov-report/src/utils/converters/remirrorNodeToSquizNode/index.html +116 -0
  131. package/coverage/lcov-report/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts.html +976 -0
  132. package/coverage/lcov-report/src/utils/converters/squizNodeToRemirrorNode/index.html +116 -0
  133. package/coverage/lcov-report/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts.html +748 -0
  134. package/coverage/lcov-report/src/utils/createToolbarPositioner.ts.html +469 -0
  135. package/coverage/lcov-report/src/utils/getCursorRect.ts.html +100 -0
  136. package/coverage/lcov-report/src/utils/getMarkNamesByGroup.ts.html +106 -0
  137. package/coverage/lcov-report/src/utils/getNodeNamesByGroup.ts.html +106 -0
  138. package/coverage/lcov-report/src/utils/getShortcutSymbol.ts.html +97 -0
  139. package/coverage/lcov-report/src/utils/index.html +206 -0
  140. package/coverage/lcov-report/src/utils/undefinedIfEmpty.ts.html +94 -0
  141. package/coverage/lcov-report/src/utils/validation.ts.html +133 -0
  142. package/coverage/lcov.info +2737 -0
  143. package/cypress/e2e/bold.spec.cy.ts +18 -0
  144. package/cypress/global.d.ts +9 -0
  145. package/cypress/support/commands.ts +130 -0
  146. package/cypress/support/e2e.ts +20 -0
  147. package/cypress/tsconfig.json +8 -0
  148. package/cypress.config.ts +7 -0
  149. package/demo/App.tsx +107 -0
  150. package/demo/AppContext.tsx +70 -0
  151. package/demo/index.html +13 -0
  152. package/demo/index.scss +33 -0
  153. package/demo/main.tsx +12 -0
  154. package/demo/public/favicon-dxp.svg +3 -0
  155. package/demo/resources.json +204 -0
  156. package/demo/sources.json +27 -0
  157. package/demo/vite-env.d.ts +1 -0
  158. package/file-transformer.js +1 -0
  159. package/jest.bootstrap.ts +3 -0
  160. package/jest.config.ts +27 -0
  161. package/lib/Editor/Editor.d.ts +16 -0
  162. package/lib/Editor/Editor.js +85 -0
  163. package/lib/Editor/EditorContext.d.ts +10 -0
  164. package/lib/Editor/EditorContext.js +16 -0
  165. package/lib/EditorToolbar/FloatingToolbar.d.ts +2 -0
  166. package/lib/EditorToolbar/FloatingToolbar.js +76 -0
  167. package/lib/EditorToolbar/Toolbar.d.ts +7 -0
  168. package/lib/EditorToolbar/Toolbar.js +47 -0
  169. package/lib/EditorToolbar/Tools/Bold/BoldButton.d.ts +3 -0
  170. package/lib/EditorToolbar/Tools/Bold/BoldButton.js +23 -0
  171. package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.d.ts +3 -0
  172. package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.js +57 -0
  173. package/lib/EditorToolbar/Tools/HorizontalLine/HorizontalLineButton.d.ts +3 -0
  174. package/lib/EditorToolbar/Tools/HorizontalLine/HorizontalLineButton.js +25 -0
  175. package/lib/EditorToolbar/Tools/Image/Form/ImageForm.d.ts +18 -0
  176. package/lib/EditorToolbar/Tools/Image/Form/ImageForm.js +143 -0
  177. package/lib/EditorToolbar/Tools/Image/ImageButton.d.ts +6 -0
  178. package/lib/EditorToolbar/Tools/Image/ImageButton.js +76 -0
  179. package/lib/EditorToolbar/Tools/Image/ImageModal.d.ts +9 -0
  180. package/lib/EditorToolbar/Tools/Image/ImageModal.js +24 -0
  181. package/lib/EditorToolbar/Tools/Italic/ItalicButton.d.ts +3 -0
  182. package/lib/EditorToolbar/Tools/Italic/ItalicButton.js +23 -0
  183. package/lib/EditorToolbar/Tools/Link/Form/LinkForm.d.ts +19 -0
  184. package/lib/EditorToolbar/Tools/Link/Form/LinkForm.js +63 -0
  185. package/lib/EditorToolbar/Tools/Link/LinkButton.d.ts +6 -0
  186. package/lib/EditorToolbar/Tools/Link/LinkButton.js +71 -0
  187. package/lib/EditorToolbar/Tools/Link/LinkModal.d.ts +9 -0
  188. package/lib/EditorToolbar/Tools/Link/LinkModal.js +27 -0
  189. package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.d.ts +4 -0
  190. package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.js +54 -0
  191. package/lib/EditorToolbar/Tools/Lists/ListButtons.d.ts +3 -0
  192. package/lib/EditorToolbar/Tools/Lists/ListButtons.js +14 -0
  193. package/lib/EditorToolbar/Tools/Lists/OrderedList/OrderedListButton.d.ts +3 -0
  194. package/lib/EditorToolbar/Tools/Lists/OrderedList/OrderedListButton.js +22 -0
  195. package/lib/EditorToolbar/Tools/Lists/UnorderedList/UnorderedListButton.d.ts +3 -0
  196. package/lib/EditorToolbar/Tools/Lists/UnorderedList/UnorderedListButton.js +22 -0
  197. package/lib/EditorToolbar/Tools/Redo/RedoButton.d.ts +3 -0
  198. package/lib/EditorToolbar/Tools/Redo/RedoButton.js +22 -0
  199. package/lib/EditorToolbar/Tools/Table/TableButton.d.ts +3 -0
  200. package/lib/EditorToolbar/Tools/Table/TableButton.js +22 -0
  201. package/lib/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.d.ts +3 -0
  202. package/lib/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.js +22 -0
  203. package/lib/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.d.ts +3 -0
  204. package/lib/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.js +22 -0
  205. package/lib/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.d.ts +3 -0
  206. package/lib/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.js +22 -0
  207. package/lib/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.d.ts +3 -0
  208. package/lib/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.js +22 -0
  209. package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.d.ts +3 -0
  210. package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.js +21 -0
  211. package/lib/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.d.ts +3 -0
  212. package/lib/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.js +22 -0
  213. package/lib/EditorToolbar/Tools/TextType/Heading/HeadingButton.d.ts +6 -0
  214. package/lib/EditorToolbar/Tools/TextType/Heading/HeadingButton.js +37 -0
  215. package/lib/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.d.ts +3 -0
  216. package/lib/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.js +21 -0
  217. package/lib/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.d.ts +3 -0
  218. package/lib/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.js +22 -0
  219. package/lib/EditorToolbar/Tools/TextType/TextTypeDropdown.d.ts +3 -0
  220. package/lib/EditorToolbar/Tools/TextType/TextTypeDropdown.js +46 -0
  221. package/lib/EditorToolbar/Tools/Underline/UnderlineButton.d.ts +3 -0
  222. package/lib/EditorToolbar/Tools/Underline/UnderlineButton.js +23 -0
  223. package/lib/EditorToolbar/Tools/Undo/UndoButton.d.ts +3 -0
  224. package/lib/EditorToolbar/Tools/Undo/UndoButton.js +22 -0
  225. package/lib/EditorToolbar/index.d.ts +2 -0
  226. package/lib/EditorToolbar/index.js +18 -0
  227. package/lib/Extensions/ClearFormattingExtension/ClearFormattingExtension.d.ts +5 -0
  228. package/lib/Extensions/ClearFormattingExtension/ClearFormattingExtension.js +63 -0
  229. package/lib/Extensions/CodeBlockExtension/CodeBlockExtension.d.ts +5 -0
  230. package/lib/Extensions/CodeBlockExtension/CodeBlockExtension.js +30 -0
  231. package/lib/Extensions/CommandsExtension/CommandsExtension.d.ts +20 -0
  232. package/lib/Extensions/CommandsExtension/CommandsExtension.js +52 -0
  233. package/lib/Extensions/Extensions.d.ts +17 -0
  234. package/lib/Extensions/Extensions.js +73 -0
  235. package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.d.ts +12 -0
  236. package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.js +63 -0
  237. package/lib/Extensions/ImageExtension/AssetImageExtension.d.ts +17 -0
  238. package/lib/Extensions/ImageExtension/AssetImageExtension.js +91 -0
  239. package/lib/Extensions/ImageExtension/ImageExtension.d.ts +7 -0
  240. package/lib/Extensions/ImageExtension/ImageExtension.js +18 -0
  241. package/lib/Extensions/LinkExtension/AssetLinkExtension.d.ts +27 -0
  242. package/lib/Extensions/LinkExtension/AssetLinkExtension.js +101 -0
  243. package/lib/Extensions/LinkExtension/LinkExtension.d.ts +23 -0
  244. package/lib/Extensions/LinkExtension/LinkExtension.js +87 -0
  245. package/lib/Extensions/LinkExtension/common.d.ts +7 -0
  246. package/lib/Extensions/LinkExtension/common.js +14 -0
  247. package/lib/Extensions/PreformattedExtension/PreformattedExtension.d.ts +12 -0
  248. package/lib/Extensions/PreformattedExtension/PreformattedExtension.js +76 -0
  249. package/lib/Extensions/UnsuportedExtension/UnsupportedNodeExtension.d.ts +10 -0
  250. package/lib/Extensions/UnsuportedExtension/UnsupportedNodeExtension.js +76 -0
  251. package/lib/hooks/index.d.ts +2 -0
  252. package/lib/hooks/index.js +18 -0
  253. package/lib/hooks/useExpandedSelection.d.ts +23 -0
  254. package/lib/hooks/useExpandedSelection.js +37 -0
  255. package/lib/hooks/useExtensionNames.d.ts +1 -0
  256. package/lib/hooks/useExtensionNames.js +16 -0
  257. package/lib/hooks/useFocus.d.ts +8 -0
  258. package/lib/hooks/useFocus.js +43 -0
  259. package/lib/index.css +1525 -0
  260. package/lib/index.d.ts +8 -0
  261. package/lib/index.js +16 -0
  262. package/lib/types.d.ts +7 -0
  263. package/lib/types.js +2 -0
  264. package/lib/ui/Button/Button.d.ts +12 -0
  265. package/lib/ui/Button/Button.js +13 -0
  266. package/lib/ui/CollapseBox/CollapseBox.d.ts +7 -0
  267. package/lib/ui/CollapseBox/CollapseBox.js +48 -0
  268. package/lib/ui/Fields/Checkbox/Checkbox.d.ts +9 -0
  269. package/lib/ui/Fields/Checkbox/Checkbox.js +47 -0
  270. package/lib/ui/Fields/Input/Input.d.ts +3 -0
  271. package/lib/ui/Fields/Input/Input.js +33 -0
  272. package/lib/ui/Fields/InputContainer/InputContainer.d.ts +9 -0
  273. package/lib/ui/Fields/InputContainer/InputContainer.js +16 -0
  274. package/lib/ui/Fields/MatrixAsset/MatrixAsset.d.ts +19 -0
  275. package/lib/ui/Fields/MatrixAsset/MatrixAsset.js +30 -0
  276. package/lib/ui/Modal/FormModal.d.ts +5 -0
  277. package/lib/ui/Modal/FormModal.js +39 -0
  278. package/lib/ui/Modal/Modal.d.ts +11 -0
  279. package/lib/ui/Modal/Modal.js +79 -0
  280. package/lib/ui/Tabs/Tabs.d.ts +11 -0
  281. package/lib/ui/Tabs/Tabs.js +46 -0
  282. package/lib/ui/ToolbarDropdown/ToolbarDropdown.d.ts +7 -0
  283. package/lib/ui/ToolbarDropdown/ToolbarDropdown.js +48 -0
  284. package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.d.ts +11 -0
  285. package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.js +15 -0
  286. package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.d.ts +5 -0
  287. package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.js +23 -0
  288. package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.props.d.ts +3 -0
  289. package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.props.js +2 -0
  290. package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.d.ts +11 -0
  291. package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +229 -0
  292. package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.d.ts +9 -0
  293. package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +205 -0
  294. package/lib/utils/createToolbarPositioner.d.ts +18 -0
  295. package/lib/utils/createToolbarPositioner.js +96 -0
  296. package/lib/utils/getCursorRect.d.ts +2 -0
  297. package/lib/utils/getCursorRect.js +7 -0
  298. package/lib/utils/getMarkNamesByGroup.d.ts +2 -0
  299. package/lib/utils/getMarkNamesByGroup.js +9 -0
  300. package/lib/utils/getNodeNamesByGroup.d.ts +2 -0
  301. package/lib/utils/getNodeNamesByGroup.js +9 -0
  302. package/lib/utils/getShortcutSymbol.d.ts +1 -0
  303. package/lib/utils/getShortcutSymbol.js +8 -0
  304. package/lib/utils/undefinedIfEmpty.d.ts +1 -0
  305. package/lib/utils/undefinedIfEmpty.js +7 -0
  306. package/lib/utils/validation.d.ts +3 -0
  307. package/lib/utils/validation.js +16 -0
  308. package/package.json +83 -0
  309. package/postcss.config.js +12 -0
  310. package/src/Editor/Editor.spec.tsx +465 -0
  311. package/src/Editor/Editor.tsx +117 -0
  312. package/src/Editor/EditorContext.spec.tsx +25 -0
  313. package/src/Editor/EditorContext.ts +20 -0
  314. package/src/Editor/_editor.scss +430 -0
  315. package/src/EditorToolbar/FloatingToolbar.spec.tsx +49 -0
  316. package/src/EditorToolbar/FloatingToolbar.tsx +62 -0
  317. package/src/EditorToolbar/Toolbar.tsx +60 -0
  318. package/src/EditorToolbar/Tools/Bold/BoldButton.spec.tsx +19 -0
  319. package/src/EditorToolbar/Tools/Bold/BoldButton.tsx +31 -0
  320. package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.spec.tsx +34 -0
  321. package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.tsx +48 -0
  322. package/src/EditorToolbar/Tools/HorizontalLine/HorizontalLineButton.spec.tsx +23 -0
  323. package/src/EditorToolbar/Tools/HorizontalLine/HorizontalLineButton.tsx +34 -0
  324. package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +117 -0
  325. package/src/EditorToolbar/Tools/Image/Form/ImageForm.tsx +201 -0
  326. package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +385 -0
  327. package/src/EditorToolbar/Tools/Image/ImageButton.tsx +72 -0
  328. package/src/EditorToolbar/Tools/Image/ImageModal.spec.tsx +123 -0
  329. package/src/EditorToolbar/Tools/Image/ImageModal.tsx +32 -0
  330. package/src/EditorToolbar/Tools/Italic/ItalicButton.spec.tsx +19 -0
  331. package/src/EditorToolbar/Tools/Italic/ItalicButton.tsx +31 -0
  332. package/src/EditorToolbar/Tools/Link/Form/LinkForm.spec.tsx +79 -0
  333. package/src/EditorToolbar/Tools/Link/Form/LinkForm.tsx +141 -0
  334. package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +402 -0
  335. package/src/EditorToolbar/Tools/Link/LinkButton.tsx +67 -0
  336. package/src/EditorToolbar/Tools/Link/LinkModal.tsx +37 -0
  337. package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +143 -0
  338. package/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx +42 -0
  339. package/src/EditorToolbar/Tools/Lists/ListButtons.tsx +14 -0
  340. package/src/EditorToolbar/Tools/Lists/OrderedList/OrderListButton.spec.tsx +39 -0
  341. package/src/EditorToolbar/Tools/Lists/OrderedList/OrderedListButton.tsx +30 -0
  342. package/src/EditorToolbar/Tools/Lists/UnorderedList/UnorderedList.spec.tsx +19 -0
  343. package/src/EditorToolbar/Tools/Lists/UnorderedList/UnorderedListButton.tsx +30 -0
  344. package/src/EditorToolbar/Tools/Redo/RedoButton.spec.tsx +59 -0
  345. package/src/EditorToolbar/Tools/Redo/RedoButton.tsx +31 -0
  346. package/src/EditorToolbar/Tools/Table/TableButton.spec.tsx +25 -0
  347. package/src/EditorToolbar/Tools/Table/TableButton.tsx +32 -0
  348. package/src/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.spec.tsx +39 -0
  349. package/src/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.tsx +31 -0
  350. package/src/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.spec.tsx +39 -0
  351. package/src/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.tsx +31 -0
  352. package/src/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.spec.tsx +39 -0
  353. package/src/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.tsx +31 -0
  354. package/src/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.spec.tsx +39 -0
  355. package/src/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.tsx +31 -0
  356. package/src/EditorToolbar/Tools/TextAlign/TextAlignButtons.tsx +21 -0
  357. package/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.spec.tsx +47 -0
  358. package/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.tsx +32 -0
  359. package/src/EditorToolbar/Tools/TextType/Heading/HeadingButton.spec.tsx +56 -0
  360. package/src/EditorToolbar/Tools/TextType/Heading/HeadingButton.tsx +52 -0
  361. package/src/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.spec.tsx +30 -0
  362. package/src/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.tsx +25 -0
  363. package/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.spec.tsx +47 -0
  364. package/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.tsx +32 -0
  365. package/src/EditorToolbar/Tools/TextType/TextTypeDropdown.spec.tsx +51 -0
  366. package/src/EditorToolbar/Tools/TextType/TextTypeDropdown.tsx +54 -0
  367. package/src/EditorToolbar/Tools/Underline/Underline.spec.tsx +19 -0
  368. package/src/EditorToolbar/Tools/Underline/UnderlineButton.tsx +31 -0
  369. package/src/EditorToolbar/Tools/Undo/UndoButton.spec.tsx +70 -0
  370. package/src/EditorToolbar/Tools/Undo/UndoButton.tsx +31 -0
  371. package/src/EditorToolbar/_floating-toolbar.scss +9 -0
  372. package/src/EditorToolbar/_toolbar.scss +37 -0
  373. package/src/EditorToolbar/index.ts +2 -0
  374. package/src/Extensions/ClearFormattingExtension/ClearFormattingExtension.ts +57 -0
  375. package/src/Extensions/CodeBlockExtension/CodeBlockExtension.ts +34 -0
  376. package/src/Extensions/CommandsExtension/CommandsExtension.ts +54 -0
  377. package/src/Extensions/Extensions.ts +88 -0
  378. package/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts +68 -0
  379. package/src/Extensions/ImageExtension/AssetImageExtension.spec.ts +77 -0
  380. package/src/Extensions/ImageExtension/AssetImageExtension.ts +109 -0
  381. package/src/Extensions/ImageExtension/ImageExtension.ts +19 -0
  382. package/src/Extensions/LinkExtension/AssetLinkExtension.spec.ts +106 -0
  383. package/src/Extensions/LinkExtension/AssetLinkExtension.ts +127 -0
  384. package/src/Extensions/LinkExtension/LinkExtension.spec.ts +68 -0
  385. package/src/Extensions/LinkExtension/LinkExtension.ts +105 -0
  386. package/src/Extensions/LinkExtension/common.ts +10 -0
  387. package/src/Extensions/PreformattedExtension/PreformattedExtension.spec.ts +43 -0
  388. package/src/Extensions/PreformattedExtension/PreformattedExtension.ts +85 -0
  389. package/src/Extensions/UnsuportedExtension/UnsupportedNodeExtension.spec.ts +137 -0
  390. package/src/Extensions/UnsuportedExtension/UnsupportedNodeExtension.tsx +80 -0
  391. package/src/hooks/index.ts +2 -0
  392. package/src/hooks/useExpandedSelection.ts +44 -0
  393. package/src/hooks/useExtensionNames.ts +15 -0
  394. package/src/hooks/useFocus.ts +61 -0
  395. package/src/index.scss +24 -0
  396. package/src/index.ts +9 -0
  397. package/src/types.ts +10 -0
  398. package/src/ui/Button/Button.spec.tsx +44 -0
  399. package/src/ui/Button/Button.tsx +35 -0
  400. package/src/ui/Button/_button.scss +37 -0
  401. package/src/ui/CollapseBox/CollapseBox.spec.tsx +49 -0
  402. package/src/ui/CollapseBox/CollapseBox.tsx +36 -0
  403. package/src/ui/CollapseBox/_collapseBox.scss +23 -0
  404. package/src/ui/Fields/Checkbox/Checkbox.spec.tsx +50 -0
  405. package/src/ui/Fields/Checkbox/Checkbox.tsx +49 -0
  406. package/src/ui/Fields/Checkbox/_checkbox.scss +26 -0
  407. package/src/ui/Fields/Input/Input.spec.tsx +49 -0
  408. package/src/ui/Fields/Input/Input.tsx +25 -0
  409. package/src/ui/Fields/InputContainer/InputContainer.spec.tsx +18 -0
  410. package/src/ui/Fields/InputContainer/InputContainer.tsx +29 -0
  411. package/src/ui/Fields/MatrixAsset/MatrixAsset.spec.tsx +121 -0
  412. package/src/ui/Fields/MatrixAsset/MatrixAsset.tsx +57 -0
  413. package/src/ui/Fields/Select/Select.spec.tsx +30 -0
  414. package/src/ui/Fields/Select/Select.tsx +66 -0
  415. package/src/ui/Modal/FormModal.spec.tsx +21 -0
  416. package/src/ui/Modal/FormModal.tsx +17 -0
  417. package/src/ui/Modal/Modal.spec.tsx +136 -0
  418. package/src/ui/Modal/Modal.tsx +104 -0
  419. package/src/ui/Modal/_modal.scss +24 -0
  420. package/src/ui/Tabs/Tabs.spec.tsx +44 -0
  421. package/src/ui/Tabs/Tabs.tsx +41 -0
  422. package/src/ui/ToolbarDropdown/ToolbarDropdown.spec.tsx +80 -0
  423. package/src/ui/ToolbarDropdown/ToolbarDropdown.tsx +42 -0
  424. package/src/ui/ToolbarDropdown/_toolbar-dropdown.scss +32 -0
  425. package/src/ui/ToolbarDropdownButton/ToolbarDropdownButton.spec.tsx +48 -0
  426. package/src/ui/ToolbarDropdownButton/ToolbarDropdownButton.tsx +33 -0
  427. package/src/ui/ToolbarDropdownButton/_toolbar-dropdown-button.scss +25 -0
  428. package/src/ui/_forms.scss +32 -0
  429. package/src/ui/_typography.scss +95 -0
  430. package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.props.ts +3 -0
  431. package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.spec.ts +179 -0
  432. package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.ts +27 -0
  433. package/src/utils/converters/mocks/squizNodeJson.mock.ts +270 -0
  434. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +1058 -0
  435. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +297 -0
  436. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +878 -0
  437. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +221 -0
  438. package/src/utils/createToolbarPositioner.ts +128 -0
  439. package/src/utils/getCursorRect.ts +5 -0
  440. package/src/utils/getMarkNamesByGroup.spec.ts +20 -0
  441. package/src/utils/getMarkNamesByGroup.ts +7 -0
  442. package/src/utils/getNodeNamesByGroup.spec.ts +37 -0
  443. package/src/utils/getNodeNamesByGroup.ts +7 -0
  444. package/src/utils/getShortcutSymbol.spec.ts +27 -0
  445. package/src/utils/getShortcutSymbol.ts +4 -0
  446. package/src/utils/undefinedIfEmpty.spec.ts +12 -0
  447. package/src/utils/undefinedIfEmpty.ts +3 -0
  448. package/src/utils/validation.spec.ts +22 -0
  449. package/src/utils/validation.ts +16 -0
  450. package/tailwind.config.cjs +87 -0
  451. package/tests/index.ts +4 -0
  452. package/tests/mockResourceBrowserContext.tsx +99 -0
  453. package/tests/renderWithContext.tsx +75 -0
  454. package/tests/renderWithEditor.tsx +123 -0
  455. package/tests/select.ts +16 -0
  456. package/tsconfig.json +22 -0
  457. package/vite.config.ts +27 -0
@@ -0,0 +1,385 @@
1
+ import '@testing-library/jest-dom';
2
+ import { screen, fireEvent, waitForElementToBeRemoved, act, waitFor } from '@testing-library/react';
3
+ import { NodeSelection } from 'prosemirror-state';
4
+ import React from 'react';
5
+ import { renderWithEditor, mockResourceBrowserContext } from '../../../../tests';
6
+ import ImageButton from './ImageButton';
7
+ import { getImageSize } from 'react-image-size';
8
+
9
+ jest.mock('react-image-size');
10
+
11
+ describe('ImageButton', () => {
12
+ const openModal = async () => {
13
+ fireEvent.click(screen.getByRole('button', { name: 'Image (Ctrl+L)' }));
14
+ await screen.findByRole('button', { name: 'Apply' });
15
+ fireEvent.click(screen.getByRole('button', { name: 'From URL' }));
16
+ };
17
+
18
+ beforeEach(() => {
19
+ (getImageSize as jest.Mock).mockResolvedValue({ width: 2, height: 2 });
20
+ });
21
+
22
+ it('Opens the modal when clicking on the image button', async () => {
23
+ await renderWithEditor(<ImageButton />);
24
+
25
+ // open the modal and assert it is visible.
26
+ await openModal();
27
+ const modalHeading = screen.getByRole('heading', { name: 'Image' });
28
+ expect(modalHeading).toBeInTheDocument();
29
+ });
30
+
31
+ it('Opens the modal when clicking the keyboard shortcut', async () => {
32
+ const { elements } = await renderWithEditor(<ImageButton />);
33
+
34
+ // press the keyboard shortcut.
35
+ fireEvent.keyDown(elements.editor, { key: 'l', ctrlKey: true });
36
+
37
+ // verify the modal opens
38
+ expect(await screen.findByRole('button', { name: 'Choose image' })).toBeInTheDocument();
39
+ });
40
+
41
+ it('Adds a new image', async () => {
42
+ const { getJsonContent } = await renderWithEditor(<ImageButton />, { content: 'Some nonsense content here' });
43
+
44
+ // open the modal and add an image.
45
+ await openModal();
46
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: 'https://httpcats.com/529.jpg' } });
47
+ fireEvent.change(screen.getByLabelText('Alternative description'), { target: { value: 'Many cats' } });
48
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
49
+
50
+ await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Apply' }));
51
+
52
+ expect(getJsonContent()).toEqual({
53
+ type: 'paragraph',
54
+ attrs: expect.any(Object),
55
+ content: [
56
+ {
57
+ type: 'image',
58
+ attrs: {
59
+ alt: 'Many cats',
60
+ crop: null,
61
+ height: '',
62
+ width: '',
63
+ rotate: null,
64
+ src: 'https://httpcats.com/529.jpg',
65
+ title: '',
66
+ fileName: null,
67
+ resizable: false,
68
+ },
69
+ },
70
+ { type: 'text', text: 'Some nonsense content here' },
71
+ ],
72
+ });
73
+ });
74
+
75
+ it('Updates the attributes of an existing image', async () => {
76
+ const { editor, getJsonContent } = await renderWithEditor(<ImageButton />, {
77
+ content: 'Some <img src="https://httpcats.com/529.jpg" alt="hi" /> nonsense',
78
+ });
79
+
80
+ await act(() => editor.selectText(new NodeSelection(editor.state.doc.resolve(6))));
81
+
82
+ await openModal();
83
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: 'https://httpcats.com/303.jpg' } });
84
+ fireEvent.change(screen.getByLabelText('Alternative description'), { target: { value: 'Updated cats!' } });
85
+
86
+ // wait for the new image dimensions to be calculated
87
+ await waitFor(() => expect(screen.getByLabelText('Height')).toHaveValue(2));
88
+
89
+ // verify the content matches what was initially set prior to applying
90
+ expect(getJsonContent()).toEqual({
91
+ type: 'paragraph',
92
+ attrs: expect.any(Object),
93
+ content: [
94
+ {
95
+ text: 'Some ',
96
+ type: 'text',
97
+ },
98
+ {
99
+ type: 'image',
100
+ attrs: {
101
+ alt: 'hi',
102
+ crop: null,
103
+ height: null,
104
+ width: null,
105
+ rotate: null,
106
+ src: 'https://httpcats.com/529.jpg',
107
+ title: '',
108
+ fileName: null,
109
+ resizable: false,
110
+ },
111
+ },
112
+ { type: 'text', text: ' nonsense' },
113
+ ],
114
+ });
115
+
116
+ // apply the changes
117
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
118
+
119
+ await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Apply' }));
120
+
121
+ // asset the content has been updated
122
+ expect(getJsonContent()).toEqual({
123
+ type: 'paragraph',
124
+ attrs: expect.any(Object),
125
+ content: [
126
+ {
127
+ text: 'Some ',
128
+ type: 'text',
129
+ },
130
+ {
131
+ type: 'image',
132
+ attrs: {
133
+ alt: 'Updated cats!',
134
+ crop: null,
135
+ height: 2,
136
+ width: 2,
137
+ rotate: null,
138
+ src: 'https://httpcats.com/303.jpg',
139
+ title: '',
140
+ fileName: null,
141
+ resizable: false,
142
+ },
143
+ },
144
+ { type: 'text', text: ' nonsense' },
145
+ ],
146
+ });
147
+ });
148
+
149
+ it('Removes the image when content is cleared (backspaced)', async () => {
150
+ const { editor, getJsonContent } = await renderWithEditor(<ImageButton />, {
151
+ content: '<img src="https://media2.giphy.com/media/3o6ozsIxg5legYvggo/giphy.gif"/>',
152
+ });
153
+
154
+ await act(() => editor.selectText(10));
155
+ await act(() => editor.backspace(1));
156
+
157
+ expect(getJsonContent()).toEqual({
158
+ type: 'paragraph',
159
+ attrs: expect.any(Object),
160
+ });
161
+ });
162
+
163
+ it('Closes the modal when clicking on the cancel button', async () => {
164
+ await renderWithEditor(<ImageButton />);
165
+
166
+ // open the modal and assert it is visible.
167
+ await openModal();
168
+ const modalHeading = screen.getByRole('heading', { name: 'Image' });
169
+ expect(modalHeading).toBeInTheDocument();
170
+
171
+ // close the modal and assert it has disappeared.
172
+ fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
173
+ expect(modalHeading).not.toBeInTheDocument();
174
+ });
175
+
176
+ it('Adds a new image with no source field', async () => {
177
+ await renderWithEditor(<ImageButton />, { content: 'Some nonsense content here' });
178
+
179
+ // open the modal and add an image.
180
+ await openModal();
181
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: '' } });
182
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
183
+ expect(await screen.findByText('Source is required')).toBeInTheDocument();
184
+ });
185
+
186
+ it('Adds a new image with data URI in source field', async () => {
187
+ await renderWithEditor(<ImageButton />);
188
+
189
+ const dataUri = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
190
+
191
+ // open the modal and add an image.
192
+ await openModal();
193
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: dataUri } });
194
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
195
+ expect(await screen.findByText('Must not be a data URI')).toBeInTheDocument();
196
+ });
197
+
198
+ it('Adds a new image with a non-image URL in source field', async () => {
199
+ (getImageSize as jest.Mock).mockRejectedValue('error: not an image');
200
+
201
+ await renderWithEditor(<ImageButton />);
202
+
203
+ // open the modal and add an image.
204
+ await openModal();
205
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: 'https://not-an-image.com/not-an-image' } });
206
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
207
+ expect(await screen.findByText('Must be a valid image URL')).toBeInTheDocument();
208
+ });
209
+
210
+ it('Adds a new image with no alt text', async () => {
211
+ await renderWithEditor(<ImageButton />, { content: 'Some tacos here' });
212
+
213
+ // open the modal and add an image.
214
+ await openModal();
215
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: 'https://httpcats.com/529.jpg' } });
216
+ fireEvent.change(screen.getByLabelText('Alternative description'), { target: { value: '' } });
217
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
218
+
219
+ await waitFor(() => {
220
+ expect(document.querySelector('img[src="https://httpcats.com/529.jpg"]')).toBeInTheDocument();
221
+ });
222
+ });
223
+
224
+ it('Adds a new image with no width or height text', async () => {
225
+ await renderWithEditor(<ImageButton />, { content: 'Some beautiful content here' });
226
+
227
+ // open the modal and add an image.
228
+ await openModal();
229
+ fireEvent.change(screen.getByLabelText('Width'), { target: { value: '' } });
230
+ fireEvent.change(screen.getByLabelText('Height'), { target: { value: '' } });
231
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
232
+ expect(await screen.findByText('Width is required')).toBeInTheDocument();
233
+ expect(await screen.findByText('Height is required')).toBeInTheDocument();
234
+ });
235
+
236
+ it('Adds a new asset image', async () => {
237
+ const matrixIdentifier = 'matrix-api-identifier';
238
+ const matrixDomain = 'https://my-matrix.squiz.net';
239
+ const { MockResourceBrowserContext, selectResource } = mockResourceBrowserContext({
240
+ sources: [{ id: matrixIdentifier }],
241
+ resources: [
242
+ {
243
+ id: 'image-resource-id',
244
+ name: 'My image resource',
245
+ type: {
246
+ code: 'image',
247
+ name: 'Image',
248
+ },
249
+ },
250
+ ],
251
+ });
252
+
253
+ const { getJsonContent } = await renderWithEditor(
254
+ <MockResourceBrowserContext>
255
+ <ImageButton />
256
+ </MockResourceBrowserContext>,
257
+ {
258
+ content: 'Some nonsense content here',
259
+ context: {
260
+ editor: {
261
+ matrix: {
262
+ matrixDomain,
263
+ },
264
+ },
265
+ },
266
+ },
267
+ );
268
+
269
+ // open the modal and add an image.
270
+ await openModal();
271
+ fireEvent.click(screen.getByRole('button', { name: 'From source' }));
272
+ await waitFor(() => {
273
+ screen.getByRole('button', { name: 'Choose image' });
274
+ });
275
+ await selectResource(screen.getByRole('button', { name: 'Choose image' }), 'My image resource');
276
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
277
+
278
+ await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Apply' }));
279
+
280
+ expect(getJsonContent()).toEqual({
281
+ type: 'paragraph',
282
+ attrs: expect.any(Object),
283
+ content: [
284
+ {
285
+ type: 'assetImage',
286
+ attrs: {
287
+ matrixAssetId: 'image-resource-id',
288
+ matrixIdentifier,
289
+ matrixDomain,
290
+ src: 'https://default-resource/',
291
+ },
292
+ },
293
+ { type: 'text', text: 'Some nonsense content here' },
294
+ ],
295
+ });
296
+ });
297
+
298
+ it('Updates the attributes of an existing asset image', async () => {
299
+ const matrixIdentifier = 'matrix-api-identifier';
300
+ const matrixDomain = 'https://my-matrix.squiz.net';
301
+ const { MockResourceBrowserContext, selectResource } = mockResourceBrowserContext({
302
+ sources: [{ id: matrixIdentifier }],
303
+ resources: [
304
+ {
305
+ id: 'image-resource-id',
306
+ name: 'My image resource',
307
+ type: {
308
+ code: 'image',
309
+ name: 'Image',
310
+ },
311
+ },
312
+ ],
313
+ });
314
+
315
+ const { editor, getJsonContent } = await renderWithEditor(
316
+ <MockResourceBrowserContext>
317
+ <ImageButton />
318
+ </MockResourceBrowserContext>,
319
+ {
320
+ content: 'Some <img src="https://httpcats.com/529.jpg" alt="hi" /> nonsense',
321
+ context: {
322
+ editor: {
323
+ matrix: {
324
+ matrixDomain,
325
+ },
326
+ },
327
+ },
328
+ },
329
+ );
330
+
331
+ await act(() => editor.selectText(new NodeSelection(editor.state.doc.resolve(6))));
332
+
333
+ await openModal();
334
+ fireEvent.click(screen.getByRole('button', { name: 'From source' }));
335
+ await waitFor(() => {
336
+ screen.getByRole('button', { name: 'Choose image' });
337
+ });
338
+ await selectResource(screen.getByRole('button', { name: 'Choose image' }), 'My image resource');
339
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
340
+
341
+ await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Apply' }));
342
+
343
+ expect(getJsonContent()).toEqual({
344
+ type: 'paragraph',
345
+ attrs: expect.any(Object),
346
+ content: [
347
+ {
348
+ text: 'Some ',
349
+ type: 'text',
350
+ },
351
+ {
352
+ type: 'assetImage',
353
+ attrs: {
354
+ matrixAssetId: 'image-resource-id',
355
+ matrixIdentifier,
356
+ matrixDomain,
357
+ src: 'https://default-resource/',
358
+ },
359
+ },
360
+ { type: 'text', text: ' nonsense' },
361
+ ],
362
+ });
363
+ });
364
+
365
+ it('Shows an error if a resource is not selected', async () => {
366
+ await renderWithEditor(<ImageButton />);
367
+ await openModal();
368
+ fireEvent.click(screen.getByRole('button', { name: 'From source' }));
369
+ await act(() => fireEvent.click(screen.getByRole('button', { name: 'Apply' })));
370
+
371
+ expect(screen.getByText('An image must be selected')).toBeInTheDocument();
372
+ });
373
+
374
+ it('Shows an error if the field value is just an empty space', async () => {
375
+ await renderWithEditor(<ImageButton />);
376
+
377
+ await openModal();
378
+ await act(async () => {
379
+ fireEvent.change(screen.getByLabelText('Source'), { target: { value: ' ' } });
380
+ });
381
+ await act(() => fireEvent.click(screen.getByRole('button', { name: 'Apply' })));
382
+
383
+ expect(screen.getAllByText('Empty space is not allowed')).toHaveLength(1);
384
+ });
385
+ });
@@ -0,0 +1,72 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { useActive, useCommands, useCurrentSelection, useKeymap } from '@remirror/react';
3
+ import ImageRoundedIcon from '@mui/icons-material/ImageRounded';
4
+ import ImageModal from './ImageModal';
5
+ import { ImageFormData } from './Form/ImageForm';
6
+ import Button from '../../../ui/Button/Button';
7
+ import { ImageExtension } from '../../../Extensions/ImageExtension/ImageExtension';
8
+ import { NodeName } from '../../../Extensions/Extensions';
9
+ import { AssetImageExtension } from '../../../Extensions/ImageExtension/AssetImageExtension';
10
+ import { CodeBlockExtension } from 'remirror/dist-types/extensions';
11
+ import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
12
+
13
+ type ImageButtonProps = {
14
+ inPopover?: boolean;
15
+ };
16
+
17
+ const ImageButton = ({ inPopover = false }: ImageButtonProps) => {
18
+ const [showModal, setShowModal] = useState(false);
19
+ const { insertImage, insertAssetImage } = useCommands<ImageExtension | AssetImageExtension>();
20
+ const active = useActive<ImageExtension | AssetImageExtension | CodeBlockExtension>();
21
+ const selection = useCurrentSelection();
22
+ // if the active selection is not an image, disable the button as it means it will be text
23
+ const disabled = (!selection.empty && !active.image() && !active.assetImage()) || active.codeBlock();
24
+
25
+ const handleClick = () => {
26
+ if (!showModal) {
27
+ setShowModal(true);
28
+ }
29
+ };
30
+
31
+ const insertImageFromData = (data: ImageFormData) => {
32
+ const { imageType, image, assetImage } = data;
33
+ if (imageType === NodeName.Image) {
34
+ insertImage(image);
35
+ } else {
36
+ insertAssetImage(assetImage);
37
+ }
38
+ };
39
+
40
+ const handleSubmit = async (data: ImageFormData) => {
41
+ insertImageFromData(data);
42
+ setShowModal(false);
43
+ };
44
+
45
+ const handleShortcut = useCallback(() => {
46
+ handleClick();
47
+ // Prevent other key handlers being run
48
+ return true;
49
+ }, []);
50
+
51
+ // when Ctrl+l is pressed show the modal, only registered in the toolbar button instance to avoid the key press
52
+ // being double handled.
53
+ if (!inPopover) {
54
+ // disable the shortcut if the button is disabled
55
+ useKeymap('Mod-l', disabled ? () => false : handleShortcut);
56
+ }
57
+
58
+ return (
59
+ <>
60
+ <Button
61
+ handleOnClick={handleClick}
62
+ isActive={active.image() || active.assetImage()}
63
+ icon={<ImageRoundedIcon />}
64
+ label={`Image (${getShortcutSymbol()}+L)`}
65
+ isDisabled={disabled}
66
+ />
67
+ {showModal && <ImageModal onCancel={() => setShowModal(false)} onSubmit={handleSubmit} />}
68
+ </>
69
+ );
70
+ };
71
+
72
+ export default ImageButton;
@@ -0,0 +1,123 @@
1
+ import '@testing-library/jest-dom';
2
+ import { screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import React from 'react';
4
+ import { renderWithEditor } from '../../../../tests';
5
+ import ImageModal from './ImageModal';
6
+ import { getImageSize } from 'react-image-size';
7
+
8
+ jest.mock('react-image-size');
9
+ beforeEach(() => {
10
+ (getImageSize as jest.Mock).mockResolvedValue({ width: 0, height: 0 });
11
+ });
12
+ const mockSubmitFunction = jest.fn();
13
+ const mockCancelFunction = jest.fn();
14
+ const setup = async () => {
15
+ const utils = await renderWithEditor(<ImageModal onCancel={mockCancelFunction} onSubmit={mockSubmitFunction} />);
16
+ fireEvent.click(screen.getByRole('button', { name: 'From URL' }));
17
+ const sourceInput = screen.getByRole('textbox', { name: /source/i }) as HTMLInputElement;
18
+ const altInput = screen.getByRole('textbox', { name: /alt/i }) as HTMLInputElement;
19
+ const widthInput = screen.getByRole('spinbutton', { name: /Width/i }) as HTMLInputElement;
20
+ const heightInput = screen.getByRole('spinbutton', { name: /Height/i }) as HTMLInputElement;
21
+ return {
22
+ sourceInput,
23
+ altInput,
24
+ widthInput,
25
+ heightInput,
26
+ ...utils,
27
+ };
28
+ };
29
+
30
+ describe('ImageModal', () => {
31
+ it('Renders the modal with title', async () => {
32
+ await renderWithEditor(<ImageModal onCancel={mockCancelFunction} onSubmit={mockSubmitFunction} />);
33
+ const modalHeading = screen.getByRole('heading', { name: 'Image' });
34
+ expect(modalHeading).toBeInTheDocument();
35
+ });
36
+
37
+ it('Populates the source field when a source is supplied', async () => {
38
+ const { sourceInput } = await setup();
39
+ fireEvent.change(sourceInput, { target: { value: 'https://httpcats.com/302.jpg' } });
40
+ await waitFor(() => {
41
+ expect(sourceInput.value).toBe('https://httpcats.com/302.jpg');
42
+ });
43
+ });
44
+
45
+ it('Renders empty width and height fields if the image source is empty', async () => {
46
+ const { sourceInput, widthInput, heightInput } = await setup();
47
+ fireEvent.change(sourceInput, { target: { value: '' } });
48
+ await waitFor(() => {
49
+ expect(widthInput.value).toBe('');
50
+ expect(heightInput.value).toBe('');
51
+ });
52
+ });
53
+
54
+ it('Updates the height field with aspect ratio based value from width', async () => {
55
+ const { widthInput, heightInput } = await setup();
56
+ fireEvent.change(widthInput, { target: { value: '300' } });
57
+ await waitFor(() => {
58
+ expect(widthInput.value).toBe('300');
59
+ expect(heightInput.value).toBe('168.75');
60
+ });
61
+ });
62
+
63
+ it('Updates the width field with aspect ratio based value from height', async () => {
64
+ const { widthInput, heightInput } = await setup();
65
+ fireEvent.change(heightInput, { target: { value: '100' } });
66
+ await waitFor(() => {
67
+ expect(heightInput.value).toBe('100');
68
+ expect(widthInput.value).toBe('177.78');
69
+ });
70
+ });
71
+
72
+ it('Does not change the width when height is changed and aspect ratio link is off', async () => {
73
+ const { widthInput, heightInput } = await setup();
74
+ fireEvent.change(heightInput, { target: { value: '100' } });
75
+ expect(heightInput.value).toBe('100');
76
+ expect(widthInput.value).toBe('177.78');
77
+ fireEvent.click(screen.getByRole('button', { name: /constrain properties/i }));
78
+ fireEvent.change(heightInput, { target: { value: '200' } });
79
+ expect(heightInput.value).toBe('200');
80
+ expect(widthInput.value).toBe('177.78');
81
+ });
82
+
83
+ it('Does not change the height when width is changed and aspect ratio link is off', async () => {
84
+ const { widthInput, heightInput } = await setup();
85
+ fireEvent.change(widthInput, { target: { value: '450' } });
86
+ expect(widthInput.value).toBe('450');
87
+ expect(heightInput.value).toBe('253.13');
88
+ fireEvent.click(screen.getByRole('button', { name: /constrain properties/i }));
89
+ fireEvent.change(widthInput, { target: { value: '600' } });
90
+ expect(widthInput.value).toBe('600');
91
+ expect(heightInput.value).toBe('253.13');
92
+ });
93
+
94
+ it('Changes the icon when aspect ratio button is toggled', async () => {
95
+ await setup();
96
+ expect(screen.getByTestId('InsertLinkRoundedIcon')).toBeInTheDocument();
97
+ fireEvent.click(screen.getByRole('button', { name: /constrain properties/i }));
98
+ expect(screen.queryByTestId('InsertLinkRoundedIcon')).not.toBeInTheDocument();
99
+ expect(screen.getByTestId('LinkOffIcon')).toBeInTheDocument();
100
+ });
101
+
102
+ it('Returns relevant error message if the width is not higher than 0', async () => {
103
+ const { widthInput } = await setup();
104
+
105
+ fireEvent.change(widthInput, { target: { value: '0' } });
106
+ fireEvent.click(screen.getByRole('button', { name: /Apply/i }));
107
+ await waitFor(() => {
108
+ expect(widthInput.value).toBe('0');
109
+ });
110
+ expect(await screen.findByText('Must be higher than 0')).toBeInTheDocument();
111
+ });
112
+
113
+ it('Returns relevant error message if the height is not higher than 0', async () => {
114
+ const { heightInput } = await setup();
115
+
116
+ fireEvent.change(heightInput, { target: { value: '0' } });
117
+ fireEvent.click(screen.getByRole('button', { name: /Apply/i }));
118
+ await waitFor(() => {
119
+ expect(heightInput.value).toBe('0');
120
+ });
121
+ expect(await screen.findByText('Must be higher than 0')).toBeInTheDocument();
122
+ });
123
+ });
@@ -0,0 +1,32 @@
1
+ import ImageForm, { ImageFormData } from './Form/ImageForm';
2
+ import React from 'react';
3
+ import { useCurrentSelection } from '@remirror/react';
4
+ import ImageRoundedIcon from '@mui/icons-material/ImageRounded';
5
+ import FormModal from '../../../ui/Modal/FormModal';
6
+ import { SubmitHandler } from 'react-hook-form';
7
+ import { NodeSelection } from 'prosemirror-state';
8
+ import { NodeName } from '../../../Extensions/Extensions';
9
+
10
+ type ImageModalProps = {
11
+ onCancel: () => void;
12
+ onSubmit: SubmitHandler<ImageFormData>;
13
+ };
14
+
15
+ const ImageModal = ({ onCancel, onSubmit }: ImageModalProps) => {
16
+ const selection = useCurrentSelection() as NodeSelection;
17
+ const currentImage = selection?.node;
18
+ const currentImageAttrs = { ...currentImage?.attrs };
19
+ const formData = {
20
+ imageType: currentImage?.type.name === NodeName.Image ? NodeName.Image : NodeName.AssetImage,
21
+ image: currentImage?.type?.name === NodeName.Image ? currentImageAttrs : {},
22
+ assetImage: currentImage?.type?.name === NodeName.AssetImage ? currentImageAttrs : {},
23
+ };
24
+
25
+ return (
26
+ <FormModal title="Image" icon={<ImageRoundedIcon />} onCancel={onCancel}>
27
+ <ImageForm data={formData} onSubmit={onSubmit} />
28
+ </FormModal>
29
+ );
30
+ };
31
+
32
+ export default ImageModal;
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import '@testing-library/jest-dom';
3
+ import { render, screen, fireEvent } from '@testing-library/react';
4
+ import Editor from '../../../Editor/Editor';
5
+
6
+ describe('Italic button', () => {
7
+ it('Renders the italic button', () => {
8
+ render(<Editor />);
9
+ expect(screen.getByRole('button', { name: 'Italic (Ctrl+I)' })).toBeInTheDocument();
10
+ });
11
+
12
+ it('Activates the button if clicked', () => {
13
+ render(<Editor />);
14
+ expect(screen.getByRole('button', { name: 'Italic (Ctrl+I)' }).classList.contains('squiz-fte-btn')).toBeTruthy();
15
+ const italic = screen.getByRole('button', { name: 'Italic (Ctrl+I)' });
16
+ fireEvent.click(italic);
17
+ expect(italic.classList.contains('squiz-fte-btn--is-active')).toBeTruthy();
18
+ });
19
+ });
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { useCommands, useActive, useChainedCommands } from '@remirror/react';
3
+ import { ItalicExtension } from '@remirror/extension-italic';
4
+ import Button from '../../../ui/Button/Button';
5
+ import FormatItalicRoundedIcon from '@mui/icons-material/FormatItalicRounded';
6
+ import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
7
+
8
+ const ItalicButton = () => {
9
+ const { toggleItalic } = useCommands();
10
+ const chain = useChainedCommands();
11
+
12
+ const active = useActive<ItalicExtension>();
13
+ const enabled = toggleItalic.enabled();
14
+ const handleSelect = () => {
15
+ if (toggleItalic.enabled()) {
16
+ chain.toggleItalic().focus().run();
17
+ }
18
+ };
19
+
20
+ return (
21
+ <Button
22
+ handleOnClick={handleSelect}
23
+ isDisabled={!enabled}
24
+ isActive={active.italic()}
25
+ icon={<FormatItalicRoundedIcon />}
26
+ label={`Italic (${getShortcutSymbol()}+I)`}
27
+ />
28
+ );
29
+ };
30
+
31
+ export default ItalicButton;