@plone/volto 17.0.0-alpha.8 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/.eslintrc +26 -3
  2. package/CHANGELOG.md +488 -5
  3. package/CONTRIBUTING.md +5 -1
  4. package/README.md +12 -9
  5. package/addon-registry.js +10 -1
  6. package/create-addons-loader.js +1 -1
  7. package/cypress/support/commands.js +70 -14
  8. package/cypress/support/e2e.js +1 -2
  9. package/cypress/support/volto-slate.js +4 -5
  10. package/cypress.config.js +1 -0
  11. package/docker-compose.yml +1 -1
  12. package/locales/ca/LC_MESSAGES/volto.po +276 -53
  13. package/locales/ca.json +1 -1
  14. package/locales/de/LC_MESSAGES/volto.po +284 -61
  15. package/locales/de.json +1 -1
  16. package/locales/en/LC_MESSAGES/volto.po +274 -51
  17. package/locales/en.json +1 -1
  18. package/locales/es/LC_MESSAGES/volto.po +313 -90
  19. package/locales/es.json +1 -1
  20. package/locales/eu/LC_MESSAGES/volto.po +275 -52
  21. package/locales/eu.json +1 -1
  22. package/locales/fi/LC_MESSAGES/volto.po +275 -52
  23. package/locales/fi.json +1 -1
  24. package/locales/fr/LC_MESSAGES/volto.po +276 -53
  25. package/locales/fr.json +1 -1
  26. package/locales/it/LC_MESSAGES/volto.po +469 -246
  27. package/locales/it.json +1 -1
  28. package/locales/ja/LC_MESSAGES/volto.po +275 -52
  29. package/locales/ja.json +1 -1
  30. package/locales/nl/LC_MESSAGES/volto.po +276 -53
  31. package/locales/nl.json +1 -1
  32. package/locales/pt/LC_MESSAGES/volto.po +276 -53
  33. package/locales/pt.json +1 -1
  34. package/locales/pt_BR/LC_MESSAGES/volto.po +309 -86
  35. package/locales/pt_BR.json +1 -1
  36. package/locales/ro/LC_MESSAGES/volto.po +276 -53
  37. package/locales/ro.json +1 -1
  38. package/locales/volto.pot +279 -52
  39. package/locales/zh_CN/LC_MESSAGES/volto.po +276 -53
  40. package/locales/zh_CN.json +1 -1
  41. package/package.json +44 -34
  42. package/packages/volto-slate/package.json +1 -1
  43. package/packages/volto-slate/src/actions/index.js +1 -1
  44. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  45. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  46. package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +8 -3
  47. package/packages/volto-slate/src/blocks/Text/TextBlockView.jsx +21 -16
  48. package/packages/volto-slate/src/blocks/Text/extensions/withDeserializers.js +3 -1
  49. package/packages/volto-slate/src/blocks/Text/index.js +10 -7
  50. package/packages/volto-slate/src/editor/config.jsx +5 -4
  51. package/packages/volto-slate/src/editor/index.js +4 -4
  52. package/packages/volto-slate/src/editor/less/slate.less +28 -0
  53. package/packages/volto-slate/src/editor/plugins/Link/render.jsx +5 -6
  54. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  55. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  56. package/packages/volto-slate/src/editor/render.jsx +77 -8
  57. package/packages/volto-slate/src/editor/ui/SlateContextToolbar.jsx +2 -2
  58. package/packages/volto-slate/src/editor/ui/index.js +15 -15
  59. package/packages/volto-slate/src/index.js +2 -2
  60. package/packages/volto-slate/src/utils/blocks.js +7 -0
  61. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  62. package/razzle.config.js +4 -6
  63. package/src/actions/index.js +4 -0
  64. package/src/actions/navroot/navroot.js +16 -0
  65. package/src/actions/navroot/navroot.test.js +15 -0
  66. package/src/actions/relations/rebuild.js +7 -7
  67. package/src/actions/relations/relations.js +17 -0
  68. package/src/actions/site/site.js +16 -0
  69. package/src/actions/site/site.test.js +15 -0
  70. package/src/actions/userSession/userSession.js +17 -1
  71. package/src/components/index.js +194 -192
  72. package/src/components/manage/Actions/Actions.jsx +133 -243
  73. package/src/components/manage/Add/Add.jsx +7 -8
  74. package/src/components/manage/AnchorPlugin/index.jsx +2 -2
  75. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +3 -1
  76. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  77. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  78. package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
  79. package/src/components/manage/Blocks/Block/Settings.test.jsx +92 -0
  80. package/src/components/manage/Blocks/Block/Style.jsx +2 -2
  81. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  82. package/src/components/manage/Blocks/Container/Edit.jsx +177 -0
  83. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +121 -0
  84. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  85. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  86. package/src/components/manage/Blocks/Grid/Edit.jsx +47 -0
  87. package/src/components/manage/Blocks/Grid/View.jsx +43 -0
  88. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  89. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  90. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  91. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  92. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  93. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  94. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  95. package/src/components/manage/Blocks/HTML/Edit.jsx +8 -8
  96. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +36 -26
  97. package/src/components/manage/Blocks/Image/Edit.jsx +51 -12
  98. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  99. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +66 -16
  100. package/src/components/manage/Blocks/Image/View.jsx +26 -5
  101. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  102. package/src/components/manage/Blocks/Image/schema.js +17 -10
  103. package/src/components/manage/Blocks/Image/utils.js +14 -0
  104. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  105. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  106. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  107. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  108. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  109. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  110. package/src/components/manage/Blocks/Listing/DefaultTemplate.jsx +18 -3
  111. package/src/components/manage/Blocks/Listing/ListingBody.jsx +32 -8
  112. package/src/components/manage/Blocks/Listing/ListingBody.test.jsx +20 -0
  113. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  114. package/src/components/manage/Blocks/Listing/getAsyncData.js +3 -5
  115. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +27 -17
  116. package/src/components/manage/Blocks/Maps/Edit.jsx +135 -209
  117. package/src/components/manage/Blocks/Maps/Edit.test.jsx +1 -2
  118. package/src/components/manage/Blocks/Maps/View.test.jsx +1 -2
  119. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +3 -2
  120. package/src/components/manage/Blocks/Search/components/Facets.jsx +66 -7
  121. package/src/components/manage/Blocks/Search/components/FilterList.jsx +4 -6
  122. package/src/components/manage/Blocks/Search/components/SearchInput.jsx +9 -2
  123. package/src/components/manage/Blocks/Search/components/SelectFacet.jsx +2 -9
  124. package/src/components/manage/Blocks/Search/components/index.js +13 -13
  125. package/src/components/manage/Blocks/Search/hocs/index.js +2 -2
  126. package/src/components/manage/Blocks/Search/hocs/withQueryString.jsx +5 -2
  127. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +70 -36
  128. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  129. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  130. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  131. package/src/components/manage/Blocks/Search/schema.js +29 -14
  132. package/src/components/manage/Blocks/Table/Cell.jsx +2 -3
  133. package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
  134. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +5 -10
  135. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  136. package/src/components/manage/Blocks/Text/Edit.jsx +2 -3
  137. package/src/components/manage/Blocks/Title/View.jsx +0 -23
  138. package/src/components/manage/Blocks/Title/View.test.jsx +16 -1
  139. package/src/components/manage/Blocks/ToC/Schema.jsx +40 -7
  140. package/src/components/manage/Blocks/ToC/View.jsx +84 -14
  141. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +8 -3
  142. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
  143. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +149 -10
  144. package/src/components/manage/Blocks/ToC/variations/index.js +3 -1
  145. package/src/components/manage/Contents/Contents.jsx +285 -114
  146. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +90 -166
  147. package/src/components/manage/Contents/ContentsRenameModal.jsx +88 -139
  148. package/src/components/manage/Contents/ContentsRenameModal.stories.jsx +61 -0
  149. package/src/components/manage/Contents/ContentsTagsModal.jsx +83 -130
  150. package/src/components/manage/Contents/ContentsTagsModal.stories.jsx +68 -0
  151. package/src/components/manage/Contents/ContentsUploadModal.jsx +11 -7
  152. package/src/components/manage/Contents/ContentsWorkflowModal.jsx +87 -154
  153. package/src/components/manage/Controlpanels/Aliases.jsx +4 -12
  154. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
  155. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
  156. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +38 -13
  157. package/src/components/manage/Controlpanels/Relations/Relations.jsx +5 -5
  158. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
  159. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +68 -68
  160. package/src/components/manage/Controlpanels/Rules/AddRule.jsx +3 -10
  161. package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
  162. package/src/components/manage/Controlpanels/UndoControlpanel.jsx +6 -9
  163. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +97 -7
  164. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +127 -99
  165. package/src/components/manage/Delete/Delete.jsx +96 -171
  166. package/src/components/manage/Diff/DiffField.jsx +25 -1
  167. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  168. package/src/components/manage/Form/BlockDataForm.jsx +3 -2
  169. package/src/components/manage/Form/BlockDataForm.test.jsx +51 -17
  170. package/src/components/manage/Form/Form.jsx +7 -6
  171. package/src/components/manage/Form/InlineForm.test.jsx +16 -14
  172. package/src/components/manage/History/History.jsx +11 -1
  173. package/src/components/manage/LinksToItem/LinksToItem.jsx +209 -0
  174. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +100 -0
  175. package/src/components/manage/LockingToastsFactory/LockingToastsFactory.jsx +1 -2
  176. package/src/components/manage/Messages/Messages.jsx +32 -99
  177. package/src/components/manage/Messages/Messages.test.jsx +0 -1
  178. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  179. package/src/components/manage/Sharing/Sharing.jsx +62 -22
  180. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  181. package/src/components/manage/Sidebar/Sidebar.jsx +139 -220
  182. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  183. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  184. package/src/components/manage/TemplateChooser/template.svg +10 -0
  185. package/src/components/manage/Toast/Toast.jsx +1 -1
  186. package/src/components/manage/Toolbar/More.jsx +17 -2
  187. package/src/components/manage/Toolbar/PersonalTools.jsx +97 -155
  188. package/src/components/manage/Toolbar/Toolbar.jsx +2 -2
  189. package/src/components/manage/UniversalLink/UniversalLink.jsx +6 -12
  190. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +37 -0
  191. package/src/components/manage/Widgets/AlignWidget.jsx +2 -4
  192. package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
  193. package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
  194. package/src/components/manage/Widgets/ColorPickerWidget.jsx +6 -1
  195. package/src/components/manage/Widgets/ColorPickerWidget.test.jsx +9 -7
  196. package/src/components/manage/Widgets/DatetimeWidget.jsx +2 -8
  197. package/src/components/manage/Widgets/FileWidget.jsx +2 -1
  198. package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
  199. package/src/components/manage/Widgets/IdWidget.jsx +1 -2
  200. package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +2 -9
  201. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +3 -10
  202. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthIndexField.jsx +4 -4
  203. package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
  204. package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
  205. package/src/components/manage/Widgets/SchemaWidget.jsx +6 -9
  206. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  207. package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
  208. package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
  209. package/src/components/manage/Widgets/WysiwygWidget.jsx +2 -9
  210. package/src/components/manage/Workflow/Workflow.jsx +75 -184
  211. package/src/components/theme/Anontools/Anontools.jsx +44 -72
  212. package/src/components/theme/Anontools/Anontools.stories.jsx +16 -6
  213. package/src/components/theme/Anontools/Anontools.test.jsx +16 -2
  214. package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
  215. package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
  216. package/src/components/theme/Comments/Comment.stories.jsx +84 -0
  217. package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
  218. package/src/components/theme/Comments/Comments.jsx +268 -380
  219. package/src/components/theme/Component/Component.jsx +1 -1
  220. package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
  221. package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
  222. package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
  223. package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +41 -3
  224. package/src/components/theme/Error/ServerError.jsx +29 -0
  225. package/src/components/theme/Header/Header.jsx +37 -63
  226. package/src/components/theme/Header/Header.test.jsx +18 -0
  227. package/src/components/theme/Image/Image.jsx +96 -0
  228. package/src/components/theme/Image/Image.test.jsx +125 -0
  229. package/src/components/theme/Login/Login.jsx +160 -243
  230. package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
  231. package/src/components/theme/Logo/Logo.jsx +35 -27
  232. package/src/components/theme/Logo/Logo.test.jsx +135 -1
  233. package/src/components/theme/Logout/Logout.jsx +36 -83
  234. package/src/components/theme/Navigation/Navigation.jsx +86 -171
  235. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  236. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -5
  237. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +95 -170
  238. package/src/components/theme/PreviewImage/PreviewImage.jsx +31 -15
  239. package/src/components/theme/PreviewImage/PreviewImage.test.js +53 -13
  240. package/src/components/theme/Register/Register.jsx +2 -4
  241. package/src/components/theme/Search/SearchTags.jsx +30 -60
  242. package/src/components/theme/SearchWidget/SearchWidget.jsx +49 -97
  243. package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
  244. package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
  245. package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
  246. package/src/components/theme/TsTest/TsTest.test.tsx +11 -0
  247. package/src/components/theme/TsTest/TsTest.tsx +15 -0
  248. package/src/components/theme/View/AlbumView.jsx +21 -16
  249. package/src/components/theme/View/EventView.jsx +36 -25
  250. package/src/components/theme/View/FileView.jsx +23 -18
  251. package/src/components/theme/View/ImageView.jsx +40 -32
  252. package/src/components/theme/View/ImageView.test.jsx +4 -0
  253. package/src/components/theme/View/LinkView.jsx +53 -78
  254. package/src/components/theme/View/ListingView.jsx +36 -28
  255. package/src/components/theme/View/NewsItemView.jsx +16 -17
  256. package/src/components/theme/View/RenderBlocks.jsx +56 -21
  257. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  258. package/src/components/theme/View/SummaryView.jsx +49 -39
  259. package/src/components/theme/View/TabularView.jsx +59 -53
  260. package/src/components/theme/View/View.jsx +2 -0
  261. package/src/components/theme/Widgets/ImageWidget.stories.jsx +1 -2
  262. package/src/config/Blocks.jsx +46 -0
  263. package/src/config/Components.jsx +3 -1
  264. package/src/config/ControlPanels.js +0 -1
  265. package/src/config/Loadables.jsx +1 -1
  266. package/src/config/NonContentRoutes.jsx +1 -0
  267. package/src/config/RichTextEditor/Blocks.jsx +4 -5
  268. package/src/config/RichTextEditor/FromHTML.jsx +2 -2
  269. package/src/config/RichTextEditor/Plugins.jsx +2 -3
  270. package/src/config/RichTextEditor/Styles.jsx +1 -1
  271. package/src/config/RichTextEditor/ToHTML.jsx +12 -10
  272. package/src/config/RichTextEditor/index.js +2 -3
  273. package/src/config/Views.jsx +6 -4
  274. package/src/config/Widgets.jsx +3 -0
  275. package/src/config/index.js +36 -2
  276. package/src/config/server.js +2 -0
  277. package/src/constants/ActionTypes.js +4 -0
  278. package/src/constants/Indexes.js +3 -1
  279. package/src/express-middleware/devproxy.js +1 -1
  280. package/src/express-middleware/files.js +11 -9
  281. package/src/express-middleware/images.js +12 -5
  282. package/src/express-middleware/ok.js +16 -0
  283. package/src/express-middleware/robotstxt.js +1 -1
  284. package/src/express-middleware/sitemap.js +1 -1
  285. package/src/express-middleware/static.js +3 -3
  286. package/src/helpers/Api/Api.js +1 -1
  287. package/src/helpers/Blocks/Blocks.js +52 -6
  288. package/src/helpers/Blocks/Blocks.test.js +92 -13
  289. package/src/helpers/Extensions/index.js +2 -1
  290. package/src/helpers/Extensions/withBlockSchemaEnhancer.js +63 -61
  291. package/src/helpers/Extensions/withBlockSchemaEnhancer.test.js +145 -0
  292. package/src/helpers/FormValidation/FormValidation.js +37 -7
  293. package/src/helpers/FormValidation/FormValidation.test.js +32 -0
  294. package/src/helpers/Html/Html.jsx +2 -8
  295. package/src/helpers/Loadable/__mocks__/Loadable.js +18 -18
  296. package/src/helpers/MessageLabels/MessageLabels.js +39 -4
  297. package/src/helpers/ScrollToTop/ScrollToTop.jsx +5 -3
  298. package/src/helpers/Site/index.js +21 -0
  299. package/src/helpers/Url/Url.js +22 -1
  300. package/src/helpers/Url/Url.test.js +41 -0
  301. package/src/helpers/Utils/UseDetectClickOutside.stories.jsx +190 -0
  302. package/src/helpers/Utils/Utils.js +35 -0
  303. package/src/helpers/Utils/Utils.test.js +13 -0
  304. package/src/helpers/Utils/usePagination.js +67 -14
  305. package/src/helpers/Utils/usePagination.test.js +115 -0
  306. package/src/helpers/index.js +15 -8
  307. package/src/hooks/client/useClient.js +11 -0
  308. package/src/hooks/clipboard/useClipboard.js +26 -0
  309. package/src/hooks/index.js +2 -0
  310. package/src/icons/grid-block.svg +11 -0
  311. package/src/middleware/api.js +203 -173
  312. package/src/middleware/blacklistRoutes.js +25 -22
  313. package/src/middleware/index.js +2 -2
  314. package/src/middleware/storeProtectLoadUtils.js +61 -62
  315. package/src/middleware/storeProtectLoadUtils.test.js +47 -43
  316. package/src/reducers/actions/actions.js +7 -5
  317. package/src/reducers/actions/actions.test.js +70 -0
  318. package/src/reducers/content/content.test.js +4 -4
  319. package/src/reducers/index.js +4 -0
  320. package/src/reducers/navigation/navigation.js +5 -5
  321. package/src/reducers/navigation/navigation.test.js +30 -0
  322. package/src/reducers/navroot/navroot.js +79 -0
  323. package/src/reducers/navroot/navroot.test.js +110 -0
  324. package/src/reducers/relations/relations.js +74 -46
  325. package/src/reducers/site/site.js +51 -0
  326. package/src/reducers/site/site.test.js +67 -0
  327. package/src/reducers/userSession/userSession.js +15 -1
  328. package/src/registry.js +2 -2
  329. package/src/routes.js +9 -0
  330. package/src/server.jsx +9 -0
  331. package/src/start-server.js +2 -2
  332. package/src/storybook.jsx +24 -38
  333. package/test-setup-config.js +11 -1
  334. package/theme/themes/pastanaga/collections/form.overrides +46 -0
  335. package/theme/themes/pastanaga/collections/menu.overrides +3 -2
  336. package/theme/themes/pastanaga/elements/container.overrides +5 -2
  337. package/theme/themes/pastanaga/elements/input.overrides +11 -1
  338. package/theme/themes/pastanaga/elements/label.overrides +10 -0
  339. package/theme/themes/pastanaga/elements/step.overrides +2 -1
  340. package/theme/themes/pastanaga/extras/blocks.less +25 -15
  341. package/theme/themes/pastanaga/extras/color-picker-widget.less +1 -1
  342. package/theme/themes/pastanaga/extras/contents.less +6 -1
  343. package/theme/themes/pastanaga/extras/draftjs.less +4 -4
  344. package/theme/themes/pastanaga/extras/grid.less +427 -0
  345. package/theme/themes/pastanaga/extras/login.less +3 -0
  346. package/theme/themes/pastanaga/extras/main.less +14 -7
  347. package/theme/themes/pastanaga/extras/react-dates-overrides.less +4 -2
  348. package/theme/themes/pastanaga/extras/search.less +7 -1
  349. package/theme/themes/pastanaga/extras/sidebar.less +5 -4
  350. package/theme/themes/pastanaga/extras/time-picker-overrides.less +5 -3
  351. package/theme/themes/pastanaga/extras/toc.less +29 -0
  352. package/theme/themes/pastanaga/extras/toolbar.less +6 -2
  353. package/theme/themes/pastanaga/extras/userscontrolpanel.less +17 -9
  354. package/theme/themes/pastanaga/extras/widgets.less +1 -1
  355. package/theme/themes/pastanaga/modules/rating.overrides +2 -1
  356. package/theme/themes/pastanaga-cms-ui/elements/container.overrides +2 -1
  357. package/theme/themes/pastanaga-cms-ui/extras/cms-ui.elements.container.less +6 -2
  358. package/theme/themes/pastanaga-cms-ui/extras/cms-ui.site.less +2 -2
  359. package/tsconfig.json +33 -0
  360. package/webpack-plugins/webpack-less-plugin.js +19 -0
  361. package/.yarn/install-state.gz +0 -0
  362. package/.yarn/releases/yarn-3.2.3.cjs +0 -783
  363. package/src/components/manage/Blocks/Teaser/utils.js +0 -44
  364. package/src/components/manage/Blocks/Teaser/utils.test.jsx +0 -229
  365. package/src/components/theme/Header/Header.md +0 -27
@@ -1,5 +1,8 @@
1
1
  import { map, uniq, keys, intersection, isEmpty } from 'lodash';
2
2
  import { messages } from '../MessageLabels/MessageLabels';
3
+ import config from '@plone/volto/registry';
4
+ import { toast } from 'react-toastify';
5
+ import Toast from '@plone/volto/components/manage/Toast/Toast';
3
6
 
4
7
  /**
5
8
  * Will return the intl message if invalid
@@ -44,7 +47,8 @@ const widgetValidation = {
44
47
  isValidEmail: (emailValue, emailObj, intlFunc) => {
45
48
  // Email Regex taken from from WHATWG living standard:
46
49
  // https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type=email)
47
- const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
50
+ const emailRegex =
51
+ /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
48
52
  const isValid = emailRegex.test(emailValue);
49
53
  return !isValid ? intlFunc(messages.isValidEmail) : null;
50
54
  },
@@ -67,11 +71,11 @@ const widgetValidation = {
67
71
  isValidURL: (urlValue, urlObj, intlFunc) => {
68
72
  var urlRegex = new RegExp(
69
73
  '^(https?:\\/\\/)?' + // validate protocol
70
- '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
71
- '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address
72
- '(localhost)' + // validate OR localhost address
73
- '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
74
- '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
74
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
75
+ '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address
76
+ '(localhost)' + // validate OR localhost address
77
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
78
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
75
79
  '(\\#[-a-z\\d_]*)?$', // validate fragment locator
76
80
  'i',
77
81
  );
@@ -203,7 +207,7 @@ const validateRequiredFields = (
203
207
  const type = schema.properties[requiredField]?.type;
204
208
  const widget = schema.properties[requiredField]?.widget;
205
209
 
206
- let isEmpty = !formData[requiredField];
210
+ let isEmpty = !formData[requiredField] && formData[requiredField] !== 0;
207
211
  if (!isEmpty) {
208
212
  if (type === 'array') {
209
213
  isEmpty = formData[requiredField]
@@ -369,3 +373,29 @@ class FormValidation {
369
373
  }
370
374
 
371
375
  export default FormValidation;
376
+
377
+ /**
378
+ * Check if a file upload is within the maximum size limit.
379
+ * @param {File} file
380
+ * @param {Function} intlFunc
381
+ * @returns {Boolean}
382
+ */
383
+ export const validateFileUploadSize = (file, intlFunc) => {
384
+ const isValid =
385
+ !config.settings.maxFileUploadSize ||
386
+ file.size <= config.settings.maxFileUploadSize;
387
+ if (!isValid) {
388
+ toast.error(
389
+ <Toast
390
+ error
391
+ title={intlFunc(messages.error)}
392
+ content={intlFunc(messages.fileTooLarge, {
393
+ limit: `${Math.floor(
394
+ config.settings.maxFileUploadSize / 1024 / 1024,
395
+ )}MB`,
396
+ })}
397
+ />,
398
+ );
399
+ }
400
+ return isValid;
401
+ };
@@ -66,6 +66,38 @@ describe('FormValidation', () => {
66
66
  });
67
67
  });
68
68
 
69
+ it('do not treat 0 as missing required value', () => {
70
+ let newSchema = {
71
+ ...schema,
72
+ properties: {
73
+ ...schema.properties,
74
+ age: {
75
+ title: 'age',
76
+ type: 'integer',
77
+ widget: 'number',
78
+ description: '',
79
+ },
80
+ },
81
+ required: ['age'],
82
+ };
83
+ expect(
84
+ FormValidation.validateFieldsPerFieldset({
85
+ schema: newSchema,
86
+ formData: { username: 'test username', age: null },
87
+ formatMessage,
88
+ }),
89
+ ).toEqual({
90
+ age: [messages.required.defaultMessage],
91
+ });
92
+ expect(
93
+ FormValidation.validateFieldsPerFieldset({
94
+ schema: newSchema,
95
+ formData: { username: 'test username', age: 0 },
96
+ formatMessage,
97
+ }),
98
+ ).toEqual({});
99
+ });
100
+
69
101
  it('validates incorrect email', () => {
70
102
  expect(
71
103
  FormValidation.validateFieldsPerFieldset({
@@ -87,14 +87,8 @@ class Html extends Component {
87
87
  * @returns {string} Markup for the component.
88
88
  */
89
89
  render() {
90
- const {
91
- extractor,
92
- markup,
93
- store,
94
- criticalCss,
95
- apiPath,
96
- publicURL,
97
- } = this.props;
90
+ const { extractor, markup, store, criticalCss, apiPath, publicURL } =
91
+ this.props;
98
92
  const head = Helmet.rewind();
99
93
  const bodyClass = join(BodyClass.rewind(), ' ');
100
94
  const htmlAttributes = head.htmlAttributes.toComponent();
@@ -18,22 +18,22 @@ export const __setLoadables = async () => {
18
18
  };
19
19
 
20
20
  // TODO: filter mockAllLoadables
21
- export const injectLazyLibs = jest
22
- .fn()
23
- .mockImplementation(function ([libraries]) {
24
- return jest.fn((WrappedComponent) =>
25
- jest.fn((props) => {
26
- return <WrappedComponent {...props} {...mockAllLoadables} />;
27
- }),
28
- );
29
- });
21
+ export const injectLazyLibs = jest.fn().mockImplementation(function ([
22
+ libraries,
23
+ ]) {
24
+ return jest.fn((WrappedComponent) =>
25
+ jest.fn((props) => {
26
+ return <WrappedComponent {...props} {...mockAllLoadables} />;
27
+ }),
28
+ );
29
+ });
30
30
 
31
- export const preloadLazyLibs = jest
32
- .fn()
33
- .mockImplementation(function ([libraries]) {
34
- return jest.fn((WrappedComponent) =>
35
- jest.fn((props) => {
36
- return <WrappedComponent {...props} />;
37
- }),
38
- );
39
- });
31
+ export const preloadLazyLibs = jest.fn().mockImplementation(function ([
32
+ libraries,
33
+ ]) {
34
+ return jest.fn((WrappedComponent) =>
35
+ jest.fn((props) => {
36
+ return <WrappedComponent {...props} />;
37
+ }),
38
+ );
39
+ });
@@ -172,6 +172,27 @@ export const messages = defineMessages({
172
172
  id: 'Roles',
173
173
  defaultMessage: 'Roles',
174
174
  },
175
+ addUserFormPasswordAndSendPasswordTogetherNotAllowed: {
176
+ id: 'It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them.',
177
+ defaultMessage:
178
+ 'It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them.',
179
+ },
180
+ userSearchNoResults: {
181
+ id: 'There are no users with the searched criteria',
182
+ defaultMessage: 'There are no users with the searched criteria',
183
+ },
184
+ groupSearchNoResults: {
185
+ id: 'There are no groups with the searched criteria',
186
+ defaultMessage: 'There are no groups with the searched criteria',
187
+ },
188
+ updateUserFormTitle: {
189
+ id: 'Update User',
190
+ defaultMessage: 'Update User',
191
+ },
192
+ updateUserSuccess: {
193
+ id: 'User updated successfuly',
194
+ defaultMessage: 'User updated successfuly',
195
+ },
175
196
  updateRoles: {
176
197
  id: 'User roles updated',
177
198
  defaultMessage: 'User roles updated',
@@ -234,19 +255,19 @@ export const messages = defineMessages({
234
255
  },
235
256
  copyBlocks: {
236
257
  id: 'Copy blocks',
237
- defaultMesages: 'Copy blocks',
258
+ defaultMessage: 'Copy blocks',
238
259
  },
239
260
  cutBlocks: {
240
261
  id: 'Cut blocks',
241
- defaultMesages: 'Cut blocks',
262
+ defaultMessage: 'Cut blocks',
242
263
  },
243
264
  pasteBlocks: {
244
265
  id: 'Paste blocks',
245
- defaultMesages: 'Paste blocks',
266
+ defaultMessage: 'Paste blocks',
246
267
  },
247
268
  deleteBlocks: {
248
269
  id: 'Delete blocks',
249
- defaultMesages: 'Delete blocks',
270
+ defaultMessage: 'Delete blocks',
250
271
  },
251
272
  showAllUserButton: {
252
273
  id: 'Show All',
@@ -260,6 +281,10 @@ export const messages = defineMessages({
260
281
  id: 'Show groups of users below',
261
282
  defaultMessage: 'Show groups of users below',
262
283
  },
284
+ urlClipboardCopy: {
285
+ id: 'Link copied to clipboard',
286
+ defaultMessage: 'Anchor link copied to the clipboard',
287
+ },
263
288
  inspectRelations: {
264
289
  id: 'Inspect relations',
265
290
  defaultMessage: 'Inspect relations',
@@ -308,6 +333,12 @@ export const messages = defineMessages({
308
333
  id: 'flush intIds and rebuild relations',
309
334
  defaultMessage: 'flush intIds and rebuild relations',
310
335
  },
336
+ flushAndRebuildRelationsHints: {
337
+ id: 'flushAndRebuildRelationsHints',
338
+ defaultMessage:
339
+ '<ul><li>Regenerate intIds (tokens of relations in relation catalog)</li><li>Rebuild relations</li></ul><p>Check the log for details!</p><p><b>Warning</b>: If you have add-ons relying on intIds, you should not flush them.</p>',
340
+ },
341
+
311
342
  addPotentialTargetsPath: {
312
343
  id: 'target path',
313
344
  defaultMessage: 'target path',
@@ -332,4 +363,8 @@ export const messages = defineMessages({
332
363
  id: 'Filter',
333
364
  defaultMessage: 'Filter',
334
365
  },
366
+ fileTooLarge: {
367
+ id: 'fileTooLarge',
368
+ defaultMessage: 'This website does not accept files larger than {limit}',
369
+ },
335
370
  });
@@ -28,15 +28,17 @@ class ScrollToTop extends React.Component {
28
28
  * @memberof ScrollToTop
29
29
  */
30
30
  componentDidUpdate(prevProps) {
31
+ const { location } = this.props;
31
32
  const noInitialBlocksFocus = // Do not scroll on /edit
32
33
  config.blocks?.initialBlocksFocus === null
33
34
  ? this.props.location?.pathname.slice(-5) !== '/edit'
34
35
  : true;
36
+
37
+ const isHash = location?.hash || location?.pathname.hash;
35
38
  if (
36
- !this.props.location?.hash &&
37
- !this.props.location?.pathname.hash &&
39
+ !isHash &&
38
40
  noInitialBlocksFocus &&
39
- this.props.location?.pathname !== prevProps.location?.pathname
41
+ location?.pathname !== prevProps.location?.pathname
40
42
  ) {
41
43
  window.scrollTo(0, 0);
42
44
  }
@@ -0,0 +1,21 @@
1
+ import { GET_SITE } from '@plone/volto/constants/ActionTypes';
2
+ import { getSite } from '@plone/volto/actions';
3
+
4
+ const getSiteAsyncPropExtender = {
5
+ path: '/',
6
+ extend: (dispatchActions) => {
7
+ if (
8
+ dispatchActions.filter((asyncAction) => asyncAction.key === GET_SITE)
9
+ .length === 0
10
+ ) {
11
+ dispatchActions.push({
12
+ key: GET_SITE,
13
+ promise: ({ location, store: { dispatch } }) =>
14
+ __SERVER__ && dispatch(getSite()),
15
+ });
16
+ }
17
+ return dispatchActions;
18
+ },
19
+ };
20
+
21
+ export { getSiteAsyncPropExtender };
@@ -3,7 +3,7 @@
3
3
  * @module helpers/Url
4
4
  */
5
5
 
6
- import { last, memoize } from 'lodash';
6
+ import { last, memoize, isArray, isObject, isString } from 'lodash';
7
7
  import { urlRegex, telRegex, mailRegex } from './urlRegex';
8
8
  import prependHttp from 'prepend-http';
9
9
  import config from '@plone/volto/registry';
@@ -251,6 +251,27 @@ export function isUrl(url) {
251
251
  return urlRegex().test(url);
252
252
  }
253
253
 
254
+ /**
255
+ * Get field url
256
+ * @method getFieldURL
257
+ * @param {object} data
258
+ * @returns {string | any} URL string value if field is of url type or any.
259
+ */
260
+ export const getFieldURL = (data) => {
261
+ let url = data;
262
+ const _isObject = data && isObject(data) && !isArray(data);
263
+ if (_isObject && data['@type'] === 'URL') {
264
+ url = data['value'] ?? data['url'] ?? data['href'] ?? data;
265
+ } else if (_isObject) {
266
+ url = data['@id'] ?? data['url'] ?? data['href'] ?? data;
267
+ }
268
+ if (isArray(data)) {
269
+ url = data.map((item) => getFieldURL(item));
270
+ }
271
+ if (isString(url) && isInternalURL(url)) return flattenToAppURL(url);
272
+ return url;
273
+ };
274
+
254
275
  /**
255
276
  * Normalize URL, adds protocol (if required eg. user has not entered the protocol)
256
277
  * @method normalizeUrl
@@ -10,6 +10,7 @@ import {
10
10
  isCmsUi,
11
11
  isInternalURL,
12
12
  isUrl,
13
+ getFieldURL,
13
14
  normalizeUrl,
14
15
  removeProtocol,
15
16
  addAppURL,
@@ -260,6 +261,46 @@ describe('Url', () => {
260
261
  expect(isUrl(href)).toBe(false);
261
262
  });
262
263
  });
264
+ describe('getFieldURL', () => {
265
+ it('returns app URL if the field is a string', () => {
266
+ const field = `${settings.apiPath}/foo/bar`;
267
+ expect(getFieldURL(field)).toBe('/foo/bar');
268
+ });
269
+ it('returns app URL if the field is an object with "@id"', () => {
270
+ const field = { '@id': '/foo/bar' };
271
+ expect(getFieldURL(field)).toBe('/foo/bar');
272
+ });
273
+ it('returns app URL if the field is an object with type URL', () => {
274
+ const field = { '@type': 'URL', value: '/foo/bar' };
275
+ expect(getFieldURL(field)).toBe('/foo/bar');
276
+ });
277
+ it('returns app URL if the field is an object with url or href properties', () => {
278
+ const fieldUrl = { url: '/foo/bar' };
279
+ const fieldHref = { href: '/foo/bar' };
280
+ expect(getFieldURL(fieldUrl)).toBe('/foo/bar');
281
+ expect(getFieldURL(fieldHref)).toBe('/foo/bar');
282
+ });
283
+ it('returns array of app URL if the field is an array of strings', () => {
284
+ const field = [
285
+ `${settings.apiPath}/foo/bar/1`,
286
+ `${settings.apiPath}/foo/bar/2`,
287
+ ];
288
+ expect(getFieldURL(field)).toStrictEqual(['/foo/bar/1', '/foo/bar/2']);
289
+ });
290
+ it('returns array of app URL if the field is an array of objects', () => {
291
+ const field = [
292
+ {
293
+ '@type': 'URL',
294
+ value: `${settings.apiPath}/foo/bar/1`,
295
+ },
296
+ {
297
+ '@type': 'URL',
298
+ value: `${settings.apiPath}/foo/bar/2`,
299
+ },
300
+ ];
301
+ expect(getFieldURL(field)).toStrictEqual(['/foo/bar/1', '/foo/bar/2']);
302
+ });
303
+ });
263
304
  describe('normalizeUrl', () => {
264
305
  it('normalizeUrl test', () => {
265
306
  const href = `www.example.com`;
@@ -0,0 +1,190 @@
1
+ import React from 'react';
2
+ import { useDetectClickOutside } from './useDetectClickOutside';
3
+ import { Portal } from 'react-portal';
4
+ import { usePopper } from 'react-popper';
5
+ import { BlockChooser } from '@plone/volto/components';
6
+
7
+ function OpenedChooser(props) {
8
+ const blockChooserRef = useDetectClickOutside({
9
+ onTriggered: () => props.setOpenMenu(false),
10
+ triggerKeys: ['Escape'],
11
+ });
12
+
13
+ return (
14
+ <div ref={blockChooserRef} style={{ marginLeft: '20px' }}>
15
+ Hello
16
+ </div>
17
+ );
18
+ }
19
+
20
+ function TestComponent(props) {
21
+ const [isOpenMenu, setOpenMenu] = React.useState(false);
22
+
23
+ return (
24
+ <div style={{ display: 'flex', marginBottom: '20px' }}>
25
+ <button onClick={() => setOpenMenu(true)}>Click me</button>
26
+ {isOpenMenu && <OpenedChooser setOpenMenu={setOpenMenu} />}
27
+ </div>
28
+ );
29
+ }
30
+
31
+ function StoryComponent(args) {
32
+ return (
33
+ <>
34
+ <TestComponent />
35
+ <TestComponent />
36
+ <TestComponent />
37
+ </>
38
+ );
39
+ }
40
+
41
+ function OpenedChooserWithPortal(props) {
42
+ const blockChooserRef = useDetectClickOutside({
43
+ onTriggered: () => props.setOpenMenu(false),
44
+ triggerKeys: ['Escape'],
45
+ });
46
+
47
+ return (
48
+ <Portal node={document.getElementById('body')}>
49
+ <div ref={blockChooserRef}>{`Hello ${props.id}`}</div>
50
+ </Portal>
51
+ );
52
+ }
53
+
54
+ function TestComponentWithPortal(props) {
55
+ const [isOpenMenu, setOpenMenu] = React.useState(false);
56
+
57
+ return (
58
+ <div style={{ display: 'flex', marginBottom: '20px' }}>
59
+ <button
60
+ onClick={() => setOpenMenu(true)}
61
+ >{`Click me ${props.id}`}</button>
62
+ {isOpenMenu && (
63
+ <OpenedChooserWithPortal {...props} setOpenMenu={setOpenMenu} />
64
+ )}
65
+ </div>
66
+ );
67
+ }
68
+
69
+ function StoryComponentWithPortal(args) {
70
+ return (
71
+ <>
72
+ <TestComponentWithPortal id={1} />
73
+ <TestComponentWithPortal id={2} />
74
+ <TestComponentWithPortal id={3} />
75
+ </>
76
+ );
77
+ }
78
+
79
+ function OpenedChooserWithPortalAndPopper(props) {
80
+ const { showBlockChooser } = props;
81
+
82
+ const blockChooserRef = useDetectClickOutside({
83
+ onTriggered: () => props.setOpenMenu(false),
84
+ triggerKeys: ['Escape'],
85
+ });
86
+
87
+ return showBlockChooser ? (
88
+ <BlockChooser
89
+ // onMutateBlock={onMutateBlock}
90
+ // currentBlock={block}
91
+ showRestricted
92
+ // blocksConfig={blocksConfig}
93
+ ref={blockChooserRef}
94
+ />
95
+ ) : (
96
+ <div ref={blockChooserRef}>{`Hello ${props.id}`}</div>
97
+ );
98
+ }
99
+
100
+ function TestComponentWithPortalAndPopper(props) {
101
+ const [isOpenMenu, setOpenMenu] = React.useState(false);
102
+ const [referenceElement, setReferenceElement] = React.useState(null);
103
+ const [popperElement, setPopperElement] = React.useState(null);
104
+ const { styles, attributes } = usePopper(referenceElement, popperElement, {
105
+ placement: 'right',
106
+ modifiers: [
107
+ {
108
+ name: 'offset',
109
+ options: {
110
+ offset: [-10, 10],
111
+ },
112
+ },
113
+ {
114
+ name: 'flip',
115
+ options: {
116
+ fallbackPlacements: ['top-start'],
117
+ },
118
+ },
119
+ ],
120
+ });
121
+ return (
122
+ <div style={{ display: 'flex', marginBottom: '20px' }}>
123
+ <button
124
+ ref={setReferenceElement}
125
+ onClick={() => setOpenMenu(true)}
126
+ >{`Click me ${props.id}`}</button>
127
+ <Portal node={document.getElementById('body')}>
128
+ <div
129
+ ref={setPopperElement}
130
+ style={styles.popper}
131
+ {...attributes.popper}
132
+ >
133
+ {isOpenMenu && (
134
+ <OpenedChooserWithPortalAndPopper
135
+ {...props}
136
+ setOpenMenu={setOpenMenu}
137
+ />
138
+ )}
139
+ </div>
140
+ </Portal>
141
+ </div>
142
+ );
143
+ }
144
+
145
+ function StoryComponentWithPortalAndPopper(args) {
146
+ const { showBlockChooser } = args;
147
+ return (
148
+ <>
149
+ <TestComponentWithPortalAndPopper
150
+ id={1}
151
+ showBlockChooser={showBlockChooser}
152
+ />
153
+ <TestComponentWithPortalAndPopper
154
+ id={2}
155
+ showBlockChooser={showBlockChooser}
156
+ />
157
+ <TestComponentWithPortalAndPopper
158
+ id={3}
159
+ showBlockChooser={showBlockChooser}
160
+ />
161
+ </>
162
+ );
163
+ }
164
+
165
+ export const Default = StoryComponent.bind({});
166
+ Default.args = {};
167
+
168
+ export const WithPortal = StoryComponentWithPortal.bind({});
169
+ WithPortal.args = {};
170
+
171
+ export const WithPortalAndPopper = StoryComponentWithPortalAndPopper.bind({});
172
+ WithPortalAndPopper.args = {};
173
+
174
+ export const WithPortalAndPopperUsingBlockChooser =
175
+ StoryComponentWithPortalAndPopper.bind({});
176
+ WithPortalAndPopperUsingBlockChooser.args = {
177
+ showBlockChooser: true,
178
+ };
179
+
180
+ export default {
181
+ title: 'Internal Components/useDetectClickOutside',
182
+ component: TestComponent,
183
+ decorators: [
184
+ (Story) => (
185
+ <div style={{ width: '600px' }}>
186
+ <Story />
187
+ </div>
188
+ ),
189
+ ],
190
+ };
@@ -284,6 +284,16 @@ export const reorderArray = (array, origin, target) => {
284
284
  return result;
285
285
  };
286
286
 
287
+ /**
288
+ * Normalize (unicode) string to a normalized plain ascii string
289
+ * @method normalizeString
290
+ * @param {string} str The string to be normalized
291
+ * @returns {string} Normalized plain ascii string
292
+ */
293
+ export function normalizeString(str) {
294
+ return str.normalize('NFD').replace(/\p{Diacritic}/gu, '');
295
+ }
296
+
287
297
  /**
288
298
  * Slugify a string: remove whitespaces, special chars and replace with _
289
299
  * @param {string} string String to be slugified
@@ -324,3 +334,28 @@ export const arrayRange = (start, stop, step) =>
324
334
  { length: (stop - start) / step + 1 },
325
335
  (value, index) => start + index * step,
326
336
  );
337
+
338
+ /**
339
+ * Given an event target element returns if it's an interactive element
340
+ * of the one in the list.
341
+ * @param {node} element event.target element type
342
+ * @returns {boolean} If it's an interactive element of the list
343
+ */
344
+ export function isInteractiveElement(
345
+ element,
346
+ interactiveElements = [
347
+ 'button',
348
+ 'input',
349
+ 'textarea',
350
+ 'select',
351
+ 'option',
352
+ 'svg',
353
+ 'path',
354
+ ],
355
+ ) {
356
+ if (interactiveElements.includes(element.tagName.toLowerCase())) {
357
+ return true;
358
+ }
359
+
360
+ return false;
361
+ }
@@ -14,6 +14,7 @@ import {
14
14
  safeWrapper,
15
15
  slugify,
16
16
  cloneDeepSchema,
17
+ normalizeString,
17
18
  } from './Utils';
18
19
  import moment from 'moment';
19
20
  import deepFreeze from 'deep-freeze';
@@ -386,6 +387,18 @@ describe('Utils tests', () => {
386
387
  });
387
388
  });
388
389
 
390
+ describe('normalizeString', () => {
391
+ it('normalizeString no diacritics', () => {
392
+ const str = `my string without diacritics`;
393
+ expect(normalizeString(str)).toBe('my string without diacritics');
394
+ });
395
+
396
+ it('normalizeString with diacritics', () => {
397
+ const str = `my Ü Ú é à ñ string with diacritics`;
398
+ expect(normalizeString(str)).toBe('my U U e a n string with diacritics');
399
+ });
400
+ });
401
+
389
402
  describe('slugify', () => {
390
403
  it('slugifies a standard string', () => {
391
404
  expect(slugify('Content Type')).toBe('content_type');