@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,49 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
3
+
4
+ export type CheckboxProps<TChecked, TUnchecked> = {
5
+ label: string;
6
+ onChange: (value: TChecked | TUnchecked) => void;
7
+ defaultChecked?: boolean;
8
+ unchecked: TUnchecked;
9
+ checked: TChecked;
10
+ };
11
+
12
+ export const Checkbox = <TChecked, TUnchecked>({
13
+ label,
14
+ onChange,
15
+ defaultChecked = false,
16
+ unchecked,
17
+ checked,
18
+ }: CheckboxProps<TChecked, TUnchecked>) => {
19
+ const [toggled, setToggled] = useState<boolean>(defaultChecked);
20
+
21
+ useEffect(() => {
22
+ if (toggled) {
23
+ onChange(checked);
24
+ } else {
25
+ onChange(unchecked);
26
+ }
27
+ }, [toggled]);
28
+
29
+ const toggleCheckbox = () => setToggled(!toggled);
30
+
31
+ return (
32
+ <div className="squiz-fte-checkbox">
33
+ <button
34
+ type="button"
35
+ role="checkbox"
36
+ aria-label={label}
37
+ aria-checked={toggled}
38
+ className="checkbox"
39
+ onClick={toggleCheckbox}
40
+ >
41
+ {toggled && <CheckRoundedIcon />}
42
+ </button>
43
+ {/* Checkbox label as a button, acts as a secondary way to toggle */}
44
+ <button type="button" className="label" onClick={toggleCheckbox} tabIndex={-1}>
45
+ {label}
46
+ </button>
47
+ </div>
48
+ );
49
+ };
@@ -0,0 +1,26 @@
1
+ .squiz-fte-checkbox {
2
+ @apply text-gray-800;
3
+ font-size: 14px;
4
+
5
+ display: flex;
6
+ align-items: center;
7
+ margin-top: 0.75rem;
8
+ gap: 0.75rem;
9
+
10
+ .checkbox {
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+
15
+ width: 1.25rem;
16
+ height: 1.25rem;
17
+ background-color: #fff;
18
+
19
+ border: 2px solid #e0e0e0;
20
+ border-radius: 4px;
21
+
22
+ svg {
23
+ width: 100%;
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,49 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import React from 'react';
4
+ import { Input } from './Input';
5
+
6
+ describe('Input', () => {
7
+ const mockOnChange = jest.fn();
8
+
9
+ const InputComponent = () => {
10
+ return <Input name="text-input" defaultValue="Water" label="Text input" onChange={mockOnChange} required />;
11
+ };
12
+
13
+ it('Renders the label', () => {
14
+ render(<InputComponent />);
15
+ // Check that the supplied label renders
16
+ const inputLabel = screen.getByLabelText('Text input');
17
+ expect(inputLabel).toBeInTheDocument();
18
+ });
19
+
20
+ it('Renders the default value', () => {
21
+ render(<InputComponent />);
22
+ // Check that default value supplied renders
23
+ expect(screen.getByDisplayValue('Water')).toBeInTheDocument();
24
+ });
25
+
26
+ it('Changes the value when new value entered', () => {
27
+ render(<InputComponent />);
28
+ const input = screen.getByLabelText('Text input') as HTMLInputElement;
29
+ // Check that default value supplied renders
30
+ expect(input.value).toBe('Water');
31
+ fireEvent.change(input, { target: { value: 'Wine' } });
32
+ expect(input.value).toBe('Wine');
33
+ });
34
+
35
+ it('Fires the change function when new value entered', () => {
36
+ render(<InputComponent />);
37
+ const input = screen.getByLabelText('Text input') as HTMLInputElement;
38
+ // Check that default value supplied renders
39
+ expect(input.value).toBe('Water');
40
+ fireEvent.change(input, { target: { value: 'Wine' } });
41
+ expect(mockOnChange).toHaveBeenCalled();
42
+ });
43
+
44
+ it('If the field is declared required, it is marked as such', () => {
45
+ render(<InputComponent />);
46
+ const requiredMarker = screen.getByLabelText('Required field');
47
+ expect(requiredMarker).toBeInTheDocument();
48
+ });
49
+ });
@@ -0,0 +1,25 @@
1
+ import React, { ForwardedRef, forwardRef, InputHTMLAttributes } from 'react';
2
+ import { InputContainer, InputContainerProps } from '../InputContainer/InputContainer';
3
+
4
+ type InputProps = InputHTMLAttributes<HTMLInputElement> & Omit<InputContainerProps, 'children'>;
5
+
6
+ const InputInternal = (
7
+ { name, label, type = 'text', error, required, ...rest }: InputProps,
8
+ ref: ForwardedRef<HTMLInputElement>,
9
+ ) => {
10
+ return (
11
+ <InputContainer name={name} label={label} error={error} required={required}>
12
+ <input
13
+ ref={ref}
14
+ id={name}
15
+ name={name}
16
+ type={type}
17
+ aria-invalid={!!error}
18
+ className="squiz-fte-form-control"
19
+ {...rest}
20
+ />
21
+ </InputContainer>
22
+ );
23
+ };
24
+
25
+ export const Input = forwardRef(InputInternal);
@@ -0,0 +1,18 @@
1
+ import '@testing-library/jest-dom';
2
+ import React from 'react';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { InputContainer } from './InputContainer';
5
+
6
+ describe('InputContainer', () => {
7
+ it('Renders with expected content', () => {
8
+ render(
9
+ <InputContainer name="my-input" label="My input" error="Input is invalid" required={true}>
10
+ input element
11
+ </InputContainer>,
12
+ );
13
+
14
+ expect(screen.getByText('My input')).toHaveClass('squiz-fte-form-label');
15
+ expect(screen.getByText('input element')).toBeInTheDocument();
16
+ expect(screen.getByText('Input is invalid')).toHaveClass('squiz-fte-form-error');
17
+ });
18
+ });
@@ -0,0 +1,29 @@
1
+ import React, { ReactNode } from 'react';
2
+ import clsx from 'clsx';
3
+
4
+ export type InputContainerProps = {
5
+ name?: string;
6
+ label?: string;
7
+ error?: string;
8
+ required?: boolean;
9
+ children: ReactNode;
10
+ };
11
+
12
+ export const InputContainer = ({ name, label, error, required, children }: InputContainerProps) => {
13
+ return (
14
+ <div className={clsx(error && 'squiz-fte-invalid-form-field')}>
15
+ {label && (
16
+ <label htmlFor={name} className="squiz-fte-form-label">
17
+ {label}
18
+ </label>
19
+ )}
20
+ {label && required && (
21
+ <span className="text-gray-600" aria-label="Required field">
22
+ *
23
+ </span>
24
+ )}
25
+ {children}
26
+ {error && <div className="squiz-fte-form-error">{error}</div>}
27
+ </div>
28
+ );
29
+ };
@@ -0,0 +1,121 @@
1
+ import '@testing-library/jest-dom';
2
+ import React from 'react';
3
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
4
+ import { MatrixAsset } from './MatrixAsset';
5
+ import { mockResourceBrowserContext } from '../../../../tests';
6
+
7
+ describe('MatrixAsset', () => {
8
+ it('Renders empty state when no value is provided', async () => {
9
+ const { MockResourceBrowserContext } = mockResourceBrowserContext({
10
+ sources: [],
11
+ resources: [],
12
+ });
13
+ render(
14
+ <MockResourceBrowserContext>
15
+ <MatrixAsset modalTitle="Insert asset" onChange={jest.fn()} />
16
+ </MockResourceBrowserContext>,
17
+ );
18
+
19
+ await waitFor(() => {
20
+ expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
21
+ });
22
+ });
23
+
24
+ it('Renders a selected state when a value is provided', async () => {
25
+ const { MockResourceBrowserContext } = mockResourceBrowserContext({
26
+ sources: [{ id: 'my-source-id' }],
27
+ resources: [{ id: 'my-resource-id', name: 'My resource' }],
28
+ });
29
+
30
+ render(
31
+ <MockResourceBrowserContext>
32
+ <MatrixAsset
33
+ modalTitle="Insert asset"
34
+ value={{
35
+ matrixIdentifier: 'matrix-api-identifier',
36
+ matrixAssetId: 'my-resource-id',
37
+ addional: 'addditional data',
38
+ }}
39
+ onChange={jest.fn()}
40
+ />
41
+ </MockResourceBrowserContext>,
42
+ );
43
+ await waitFor(() => {
44
+ expect(screen.getByText('My resource')).toBeInTheDocument();
45
+ });
46
+ });
47
+
48
+ it('Calls onChange with expected value when resources is selected', async () => {
49
+ const handleChange = jest.fn();
50
+ const { MockResourceBrowserContext, selectResource } = mockResourceBrowserContext({
51
+ sources: [{ id: 'my-source-id' }],
52
+ resources: [{ id: 'my-resource-id', name: 'My resource' }],
53
+ });
54
+
55
+ render(
56
+ <MockResourceBrowserContext>
57
+ <MatrixAsset
58
+ modalTitle="Insert asset"
59
+ value={{ matrixIdentifier: undefined, matrixAssetId: undefined, additional: 'additional data' }}
60
+ onChange={handleChange}
61
+ />
62
+ </MockResourceBrowserContext>,
63
+ );
64
+
65
+ await waitFor(() => {
66
+ expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
67
+ });
68
+ await selectResource(screen.getByRole('button', { name: 'Choose asset' }), 'My resource');
69
+
70
+ expect(handleChange).toHaveBeenCalledWith({
71
+ target: {
72
+ value: {
73
+ additional: 'additional data',
74
+ matrixAssetId: 'my-resource-id',
75
+ matrixIdentifier: 'matrix-api-identifier',
76
+ url: 'https://default-resource/',
77
+ },
78
+ },
79
+ });
80
+ });
81
+
82
+ it('Calls onChange with expected value when resources is cleared', async () => {
83
+ const handleChange = jest.fn();
84
+ const { MockResourceBrowserContext } = mockResourceBrowserContext({
85
+ sources: [{ id: 'my-source-id' }],
86
+ resources: [
87
+ { id: 'my-resource-id', name: 'My resource' },
88
+ { id: 'updated-resource-id', name: 'Updated resource' },
89
+ ],
90
+ });
91
+
92
+ render(
93
+ <MockResourceBrowserContext>
94
+ <MatrixAsset
95
+ modalTitle="Insert asset"
96
+ value={{
97
+ matrixIdentifier: 'my-source-id',
98
+ matrixAssetId: 'my-resource-id',
99
+ additional: 'additional data',
100
+ }}
101
+ onChange={handleChange}
102
+ />
103
+ </MockResourceBrowserContext>,
104
+ );
105
+
106
+ await waitFor(() => {
107
+ expect(screen.getByRole('button', { name: 'Remove selection' })).toBeInTheDocument();
108
+ });
109
+ fireEvent.click(screen.getByRole('button', { name: 'Remove selection' }));
110
+
111
+ expect(handleChange).toHaveBeenCalledWith({
112
+ target: {
113
+ value: {
114
+ additional: 'additional data',
115
+ matrixAssetId: undefined,
116
+ matrixIdentifier: undefined,
117
+ },
118
+ },
119
+ });
120
+ });
121
+ });
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import { ResourceBrowserResource, ResourceBrowser } from '@squiz/resource-browser';
3
+ import { InputContainer, InputContainerProps } from '../InputContainer/InputContainer';
4
+
5
+ type MatrixAssetValue = {
6
+ matrixIdentifier?: string;
7
+ matrixAssetId?: string;
8
+ url?: string;
9
+ };
10
+
11
+ export type MatrixAssetProps<T extends MatrixAssetValue> = Omit<InputContainerProps, 'children'> & {
12
+ modalTitle: string;
13
+ allowedTypes?: string[];
14
+ value?: T | null;
15
+ // LinkForm contains a "target" property.
16
+ // react-hook-form treats the presence of this property as an "Event" object being passed through, see:
17
+ // https://github.com/react-hook-form/react-hook-form/blob/master/src/logic/getEventValue.ts
18
+ // Nest the value under a "target" object to work around the behaviour.
19
+ onChange: (value: { target: { value: T } }) => void;
20
+ };
21
+
22
+ export const MatrixAsset = <T extends MatrixAssetValue>({
23
+ modalTitle,
24
+ allowedTypes,
25
+ value,
26
+ onChange,
27
+ ...props
28
+ }: MatrixAssetProps<T>) => {
29
+ return (
30
+ <InputContainer {...props}>
31
+ <ResourceBrowser
32
+ modalTitle={modalTitle}
33
+ allowedTypes={allowedTypes}
34
+ value={
35
+ value && value.matrixIdentifier && value.matrixAssetId
36
+ ? {
37
+ sourceId: value.matrixIdentifier,
38
+ resourceId: value.matrixAssetId,
39
+ }
40
+ : null
41
+ }
42
+ onChange={(resource: ResourceBrowserResource | null) => {
43
+ onChange({
44
+ target: {
45
+ value: {
46
+ ...value,
47
+ matrixIdentifier: resource?.source?.id,
48
+ matrixAssetId: resource?.id,
49
+ url: resource?.url,
50
+ } as T,
51
+ },
52
+ });
53
+ }}
54
+ />
55
+ </InputContainer>
56
+ );
57
+ };
@@ -0,0 +1,30 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import React from 'react';
4
+ import { Select } from './Select';
5
+
6
+ describe('Select field', () => {
7
+ const selectOptions = {
8
+ chicken: { label: 'chicken' },
9
+ egg: { label: 'Egg' },
10
+ };
11
+
12
+ it('Renders the label', () => {
13
+ render(<Select name="select-field" label="Which came first?" value="Chicken" options={selectOptions} />);
14
+ // Check that the supplied label renders
15
+ const inputLabel = screen.getByLabelText('Which came first?');
16
+ expect(inputLabel).toBeInTheDocument();
17
+ });
18
+
19
+ it('Renders the default value', () => {
20
+ render(<Select name="select-field" label="Which came first?" value="Chicken" options={selectOptions} />);
21
+ // Check that default value supplied renders
22
+ expect(screen.getByDisplayValue('Chicken')).toBeInTheDocument();
23
+ });
24
+
25
+ it('Renders an empty state when no value is provided', () => {
26
+ render(<Select name="select-field" label="Which came first?" options={selectOptions} />);
27
+ // nbsp should be rendered so input maintains a consistent size when nothing is selected.
28
+ expect(screen.getByLabelText('Which came first?')).toContainHTML('&nbsp;');
29
+ });
30
+ });
@@ -0,0 +1,66 @@
1
+ import { Listbox } from '@headlessui/react';
2
+ import React, { useState } from 'react';
3
+ import clsx from 'clsx';
4
+ import ExpandMoreRoundedIcon from '@mui/icons-material/ExpandMoreRounded';
5
+ import ExpandLessRoundedIcon from '@mui/icons-material/ExpandLessRounded';
6
+
7
+ export type SelectOptions = Record<string, SelectOption>;
8
+ export type SelectOption = {
9
+ label: string;
10
+ };
11
+
12
+ export type SelectProps = {
13
+ name: string;
14
+ label?: string;
15
+ value?: string;
16
+ options: SelectOptions;
17
+ onChange?: (value: string) => void;
18
+ };
19
+
20
+ export const Select = ({ name, label, value, onChange, options }: SelectProps) => {
21
+ const [selectedOption, setSelectedOptions] = useState(value ? options[value] : null);
22
+ const handleChange = (value: string): void => {
23
+ setSelectedOptions(options[value]);
24
+ onChange?.(value);
25
+ };
26
+
27
+ return (
28
+ <Listbox value={value} name={name} aria-labelledby={name} onChange={handleChange}>
29
+ {({ open }) => (
30
+ <>
31
+ {label && <Listbox.Label className="squiz-fte-form-label">{label}</Listbox.Label>}
32
+ <div className="relative text-md text-gray-800">
33
+ <Listbox.Button className="w-full cursor-default rounded border-2 border-gray-300 bg-white py-2 pl-3 pr-10 focus:border-blue-300 focus:outline-none">
34
+ <span className="flex items-center">
35
+ <span className="block truncate">{selectedOption?.label}&nbsp;</span>
36
+ </span>
37
+ <span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
38
+ {!open ? (
39
+ <ExpandMoreRoundedIcon className="text-gray-500" aria-hidden="true" />
40
+ ) : (
41
+ <ExpandLessRoundedIcon className="text-gray-500" aria-hidden="true" />
42
+ )}
43
+ </span>
44
+ </Listbox.Button>
45
+ <Listbox.Options className="absolute z-10 mt-1 w-full overflow-auto rounded border-2 border-gray-300 bg-white">
46
+ {Object.entries(options).map(([key, option]) => (
47
+ <Listbox.Option
48
+ key={key}
49
+ value={key}
50
+ className={({ active }) =>
51
+ clsx(
52
+ active ? 'bg-gray-100' : 'bg-white',
53
+ 'relative cursor-default select-none flex hover:bg-gray-100 py-2 px-3',
54
+ )
55
+ }
56
+ >
57
+ <span className="block truncate">{option.label}</span>
58
+ </Listbox.Option>
59
+ ))}
60
+ </Listbox.Options>
61
+ </div>
62
+ </>
63
+ )}
64
+ </Listbox>
65
+ );
66
+ };
@@ -0,0 +1,21 @@
1
+ import '@testing-library/jest-dom';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import InsertLinkRoundedIcon from '@mui/icons-material/InsertLinkRounded';
4
+ import React from 'react';
5
+ import FormModal from './FormModal';
6
+
7
+ describe('FormModal', () => {
8
+ it('Triggers form submit handler when modal is submitted', () => {
9
+ const handleSubmit = jest.fn();
10
+
11
+ render(
12
+ <FormModal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={jest.fn()}>
13
+ <form onSubmit={handleSubmit}></form>
14
+ </FormModal>,
15
+ );
16
+
17
+ fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
18
+
19
+ expect(handleSubmit).toHaveBeenCalled();
20
+ });
21
+ });
@@ -0,0 +1,17 @@
1
+ import React, { createRef, ReactElement } from 'react';
2
+ import Modal, { ModalProps } from './Modal';
3
+
4
+ type FormModalProps = Omit<ModalProps, 'onSubmit'>;
5
+
6
+ const FormModal = (props: FormModalProps): ReactElement => {
7
+ const ref = createRef<HTMLDivElement>();
8
+ const handleSubmit = () => {
9
+ const form = ref.current?.querySelector('form');
10
+
11
+ form?.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
12
+ };
13
+
14
+ return <Modal ref={ref} {...props} onSubmit={handleSubmit} />;
15
+ };
16
+
17
+ export default FormModal;
@@ -0,0 +1,136 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import InsertLinkRoundedIcon from '@mui/icons-material/InsertLinkRounded';
4
+ import React from 'react';
5
+ import Modal from './Modal';
6
+ import { Select } from '../Fields/Select/Select';
7
+ import { Input } from '../Fields/Input/Input';
8
+
9
+ describe('Modal', () => {
10
+ const mockOnCancel = jest.fn();
11
+ const mockOnSubmit = jest.fn();
12
+
13
+ const ModalComponent = () => {
14
+ return (
15
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel}>
16
+ <div>I am a child in the modal</div>
17
+ </Modal>
18
+ );
19
+ };
20
+
21
+ it('Renders the modal title', () => {
22
+ render(<ModalComponent />);
23
+ // Check that the modal heading displays
24
+ const modalHeading = screen.getByRole('heading', {
25
+ name: /Modal title/i,
26
+ });
27
+ expect(modalHeading).toBeInTheDocument();
28
+ });
29
+
30
+ it('Renders the modal image', () => {
31
+ render(<ModalComponent />);
32
+ // Check that the modal image displays
33
+ const modalImage = screen.getByTestId('InsertLinkRoundedIcon');
34
+ expect(modalImage).toBeInTheDocument();
35
+ });
36
+
37
+ it('Renders the child', () => {
38
+ render(<ModalComponent />);
39
+ // Check that the modal heading displays
40
+ const modalChild = screen.getByText(/I am a child in the modal/i);
41
+ expect(modalChild).toBeInTheDocument();
42
+ });
43
+
44
+ it('Renders the cancel button', () => {
45
+ render(<ModalComponent />);
46
+ // Check that the cancel button renders
47
+ const cancelButton = screen.getByRole('button', { name: 'Cancel' });
48
+ expect(cancelButton).toBeInTheDocument();
49
+ // check that firing the cancel button fires the cancel function
50
+ fireEvent.click(cancelButton);
51
+ expect(mockOnCancel).toHaveBeenCalled();
52
+ });
53
+
54
+ it('Does not render the submit button if there is no submit function supplied', () => {
55
+ render(<ModalComponent />);
56
+ // Check that the submit button does not render
57
+ const submitButton = screen.queryByRole('button', { name: 'Apply' });
58
+ expect(submitButton).not.toBeInTheDocument();
59
+ });
60
+
61
+ it('Renders the submit button if there is a submit function supplied', () => {
62
+ render(
63
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel} onSubmit={mockOnSubmit}>
64
+ <div>I am a child in the modal</div>
65
+ </Modal>,
66
+ );
67
+ // Check that the submit button renders
68
+ const submitButton = screen.getByRole('button', { name: 'Apply' });
69
+ expect(submitButton).toBeInTheDocument();
70
+ });
71
+
72
+ it('Checks that the submit function fires if you click on the submit button', () => {
73
+ render(
74
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel} onSubmit={mockOnSubmit}>
75
+ <div>I am a child in the modal</div>
76
+ </Modal>,
77
+ );
78
+ // Check that the submit button renders
79
+ const submitButton = screen.getByRole('button', { name: 'Apply' });
80
+ expect(submitButton).toBeInTheDocument();
81
+ // check that firing the submit button fires the submit function
82
+ fireEvent.click(submitButton);
83
+ expect(mockOnSubmit).toHaveBeenCalled();
84
+ });
85
+
86
+ it('Calls the onSubmit handler when the enter key is pressed', () => {
87
+ render(
88
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel} onSubmit={mockOnSubmit}>
89
+ <div>Modal content</div>
90
+ </Modal>,
91
+ );
92
+
93
+ fireEvent.keyDown(screen.getByText('Modal content'), { key: 'Enter' });
94
+
95
+ expect(mockOnSubmit).toHaveBeenCalled();
96
+ });
97
+
98
+ it('Calls the onCancel handler when the escape key is pressed', () => {
99
+ render(
100
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel} onSubmit={mockOnSubmit}>
101
+ <div>Modal content</div>
102
+ </Modal>,
103
+ );
104
+
105
+ fireEvent.keyDown(screen.getByText('Modal content'), { key: 'Escape' });
106
+
107
+ expect(mockOnCancel).toHaveBeenCalled();
108
+ });
109
+
110
+ it('Auto-focuses on the first non-hidden input on mount', () => {
111
+ render(
112
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel} onSubmit={mockOnSubmit}>
113
+ <>
114
+ <input id="hidden-input" type="hidden" />
115
+ <label htmlFor="my-input">My input</label>
116
+ <input id="my-input" type="text" />
117
+ </>
118
+ </Modal>,
119
+ );
120
+
121
+ expect(screen.getByLabelText('My input')).toHaveFocus();
122
+ });
123
+
124
+ it('Auto-focuses on the first select field on mount', () => {
125
+ render(
126
+ <Modal title="Modal title" icon={<InsertLinkRoundedIcon />} onCancel={mockOnCancel} onSubmit={mockOnSubmit}>
127
+ <>
128
+ <Select label="Dropdown" name="select" options={{}} />
129
+ <Input label="Input" name="input" />
130
+ </>
131
+ </Modal>,
132
+ );
133
+
134
+ expect(screen.getByLabelText('Dropdown')).toHaveFocus();
135
+ });
136
+ });