@plone/volto 17.0.0-alpha.9 → 17.0.1

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 +774 -5
  3. package/CONTRIBUTING.md +5 -1
  4. package/README.md +12 -9
  5. package/RELEASING.md +5 -5
  6. package/addon-registry.js +10 -1
  7. package/create-addons-loader.js +1 -1
  8. package/cypress/support/commands.js +70 -14
  9. package/cypress/support/e2e.js +1 -2
  10. package/cypress/support/volto-slate.js +4 -5
  11. package/cypress.config.js +1 -0
  12. package/docker-compose.yml +1 -1
  13. package/locales/ca/LC_MESSAGES/volto.po +281 -53
  14. package/locales/ca.json +1 -1
  15. package/locales/de/LC_MESSAGES/volto.po +289 -61
  16. package/locales/de.json +1 -1
  17. package/locales/en/LC_MESSAGES/volto.po +279 -51
  18. package/locales/en.json +1 -1
  19. package/locales/es/LC_MESSAGES/volto.po +318 -90
  20. package/locales/es.json +1 -1
  21. package/locales/eu/LC_MESSAGES/volto.po +280 -52
  22. package/locales/eu.json +1 -1
  23. package/locales/fi/LC_MESSAGES/volto.po +280 -52
  24. package/locales/fi.json +1 -1
  25. package/locales/fr/LC_MESSAGES/volto.po +281 -53
  26. package/locales/fr.json +1 -1
  27. package/locales/it/LC_MESSAGES/volto.po +474 -246
  28. package/locales/it.json +1 -1
  29. package/locales/ja/LC_MESSAGES/volto.po +280 -52
  30. package/locales/ja.json +1 -1
  31. package/locales/nl/LC_MESSAGES/volto.po +281 -53
  32. package/locales/nl.json +1 -1
  33. package/locales/pt/LC_MESSAGES/volto.po +281 -53
  34. package/locales/pt.json +1 -1
  35. package/locales/pt_BR/LC_MESSAGES/volto.po +314 -86
  36. package/locales/pt_BR.json +1 -1
  37. package/locales/ro/LC_MESSAGES/volto.po +281 -53
  38. package/locales/ro.json +1 -1
  39. package/locales/volto.pot +284 -52
  40. package/locales/zh_CN/LC_MESSAGES/volto.po +281 -53
  41. package/locales/zh_CN.json +1 -1
  42. package/package.json +44 -34
  43. package/packages/volto-slate/package.json +1 -1
  44. package/packages/volto-slate/src/actions/index.js +1 -1
  45. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  46. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  47. package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +8 -3
  48. package/packages/volto-slate/src/blocks/Text/TextBlockView.jsx +21 -16
  49. package/packages/volto-slate/src/blocks/Text/extensions/withDeserializers.js +3 -1
  50. package/packages/volto-slate/src/blocks/Text/index.js +10 -7
  51. package/packages/volto-slate/src/editor/config.jsx +5 -4
  52. package/packages/volto-slate/src/editor/index.js +4 -4
  53. package/packages/volto-slate/src/editor/less/slate.less +28 -0
  54. package/packages/volto-slate/src/editor/plugins/Link/render.jsx +5 -6
  55. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  56. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  57. package/packages/volto-slate/src/editor/render.jsx +77 -8
  58. package/packages/volto-slate/src/editor/ui/SlateContextToolbar.jsx +2 -2
  59. package/packages/volto-slate/src/editor/ui/index.js +15 -15
  60. package/packages/volto-slate/src/index.js +2 -2
  61. package/packages/volto-slate/src/utils/blocks.js +7 -0
  62. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  63. package/razzle.config.js +4 -6
  64. package/src/actions/index.js +4 -0
  65. package/src/actions/navroot/navroot.js +16 -0
  66. package/src/actions/navroot/navroot.test.js +15 -0
  67. package/src/actions/relations/rebuild.js +7 -7
  68. package/src/actions/relations/relations.js +17 -0
  69. package/src/actions/site/site.js +16 -0
  70. package/src/actions/site/site.test.js +15 -0
  71. package/src/actions/userSession/userSession.js +17 -1
  72. package/src/components/index.js +194 -192
  73. package/src/components/manage/Actions/Actions.jsx +133 -243
  74. package/src/components/manage/Add/Add.jsx +7 -8
  75. package/src/components/manage/AnchorPlugin/index.jsx +2 -2
  76. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +3 -1
  77. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  78. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  79. package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
  80. package/src/components/manage/Blocks/Block/Settings.test.jsx +92 -0
  81. package/src/components/manage/Blocks/Block/Style.jsx +2 -2
  82. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  83. package/src/components/manage/Blocks/Container/Edit.jsx +177 -0
  84. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +121 -0
  85. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  86. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  87. package/src/components/manage/Blocks/Grid/Edit.jsx +47 -0
  88. package/src/components/manage/Blocks/Grid/View.jsx +43 -0
  89. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  90. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  91. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  92. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  93. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  94. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  95. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  96. package/src/components/manage/Blocks/HTML/Edit.jsx +8 -8
  97. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +36 -26
  98. package/src/components/manage/Blocks/Image/Edit.jsx +51 -12
  99. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  100. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +66 -16
  101. package/src/components/manage/Blocks/Image/View.jsx +26 -5
  102. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  103. package/src/components/manage/Blocks/Image/schema.js +17 -10
  104. package/src/components/manage/Blocks/Image/utils.js +14 -0
  105. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  106. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  107. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  108. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  109. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  110. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  111. package/src/components/manage/Blocks/Listing/DefaultTemplate.jsx +18 -3
  112. package/src/components/manage/Blocks/Listing/ListingBody.jsx +32 -8
  113. package/src/components/manage/Blocks/Listing/ListingBody.test.jsx +20 -0
  114. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  115. package/src/components/manage/Blocks/Listing/getAsyncData.js +3 -5
  116. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +27 -17
  117. package/src/components/manage/Blocks/Maps/Edit.jsx +135 -209
  118. package/src/components/manage/Blocks/Maps/Edit.test.jsx +1 -2
  119. package/src/components/manage/Blocks/Maps/View.test.jsx +1 -2
  120. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +3 -2
  121. package/src/components/manage/Blocks/Search/components/Facets.jsx +66 -7
  122. package/src/components/manage/Blocks/Search/components/FilterList.jsx +4 -6
  123. package/src/components/manage/Blocks/Search/components/SearchInput.jsx +9 -2
  124. package/src/components/manage/Blocks/Search/components/SelectFacet.jsx +2 -9
  125. package/src/components/manage/Blocks/Search/components/index.js +13 -13
  126. package/src/components/manage/Blocks/Search/hocs/index.js +2 -2
  127. package/src/components/manage/Blocks/Search/hocs/withQueryString.jsx +5 -2
  128. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +70 -36
  129. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  130. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  131. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  132. package/src/components/manage/Blocks/Search/schema.js +29 -14
  133. package/src/components/manage/Blocks/Table/Cell.jsx +2 -3
  134. package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
  135. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +5 -10
  136. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  137. package/src/components/manage/Blocks/Text/Edit.jsx +2 -3
  138. package/src/components/manage/Blocks/Title/View.jsx +0 -23
  139. package/src/components/manage/Blocks/Title/View.test.jsx +16 -1
  140. package/src/components/manage/Blocks/ToC/Schema.jsx +40 -7
  141. package/src/components/manage/Blocks/ToC/View.jsx +84 -14
  142. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +8 -3
  143. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
  144. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +149 -10
  145. package/src/components/manage/Blocks/ToC/variations/index.js +3 -1
  146. package/src/components/manage/Blocks/Video/View.test.jsx +1 -1
  147. package/src/components/manage/Contents/Contents.jsx +285 -114
  148. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +90 -166
  149. package/src/components/manage/Contents/ContentsRenameModal.jsx +88 -139
  150. package/src/components/manage/Contents/ContentsRenameModal.stories.jsx +61 -0
  151. package/src/components/manage/Contents/ContentsTagsModal.jsx +83 -130
  152. package/src/components/manage/Contents/ContentsTagsModal.stories.jsx +68 -0
  153. package/src/components/manage/Contents/ContentsUploadModal.jsx +11 -7
  154. package/src/components/manage/Contents/ContentsWorkflowModal.jsx +87 -154
  155. package/src/components/manage/Controlpanels/Aliases.jsx +4 -12
  156. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
  157. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
  158. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +38 -13
  159. package/src/components/manage/Controlpanels/Relations/Relations.jsx +5 -5
  160. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
  161. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +68 -68
  162. package/src/components/manage/Controlpanels/Rules/AddRule.jsx +3 -10
  163. package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
  164. package/src/components/manage/Controlpanels/UndoControlpanel.jsx +6 -9
  165. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +97 -7
  166. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +127 -99
  167. package/src/components/manage/Delete/Delete.jsx +96 -171
  168. package/src/components/manage/Diff/DiffField.jsx +25 -1
  169. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  170. package/src/components/manage/Form/BlockDataForm.jsx +3 -2
  171. package/src/components/manage/Form/BlockDataForm.test.jsx +51 -17
  172. package/src/components/manage/Form/Form.jsx +7 -6
  173. package/src/components/manage/Form/InlineForm.test.jsx +16 -14
  174. package/src/components/manage/History/History.jsx +11 -1
  175. package/src/components/manage/LinksToItem/LinksToItem.jsx +209 -0
  176. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +100 -0
  177. package/src/components/manage/LockingToastsFactory/LockingToastsFactory.jsx +1 -2
  178. package/src/components/manage/Messages/Messages.jsx +32 -99
  179. package/src/components/manage/Messages/Messages.test.jsx +0 -1
  180. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  181. package/src/components/manage/Sharing/Sharing.jsx +80 -22
  182. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  183. package/src/components/manage/Sidebar/Sidebar.jsx +139 -220
  184. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  185. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  186. package/src/components/manage/TemplateChooser/template.svg +10 -0
  187. package/src/components/manage/Toast/Toast.jsx +1 -1
  188. package/src/components/manage/Toolbar/More.jsx +17 -2
  189. package/src/components/manage/Toolbar/PersonalTools.jsx +97 -155
  190. package/src/components/manage/Toolbar/Toolbar.jsx +2 -2
  191. package/src/components/manage/UniversalLink/UniversalLink.jsx +6 -12
  192. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +37 -0
  193. package/src/components/manage/Widgets/AlignWidget.jsx +2 -4
  194. package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
  195. package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
  196. package/src/components/manage/Widgets/ColorPickerWidget.jsx +6 -1
  197. package/src/components/manage/Widgets/ColorPickerWidget.test.jsx +9 -7
  198. package/src/components/manage/Widgets/DatetimeWidget.jsx +2 -8
  199. package/src/components/manage/Widgets/FileWidget.jsx +2 -1
  200. package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
  201. package/src/components/manage/Widgets/IdWidget.jsx +1 -2
  202. package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +2 -9
  203. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +3 -10
  204. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthIndexField.jsx +4 -4
  205. package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
  206. package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
  207. package/src/components/manage/Widgets/SchemaWidget.jsx +6 -9
  208. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  209. package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
  210. package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
  211. package/src/components/manage/Widgets/WysiwygWidget.jsx +2 -9
  212. package/src/components/manage/Workflow/Workflow.jsx +75 -184
  213. package/src/components/theme/Anontools/Anontools.jsx +44 -72
  214. package/src/components/theme/Anontools/Anontools.stories.jsx +16 -6
  215. package/src/components/theme/Anontools/Anontools.test.jsx +16 -2
  216. package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
  217. package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
  218. package/src/components/theme/Comments/Comment.stories.jsx +84 -0
  219. package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
  220. package/src/components/theme/Comments/Comments.jsx +268 -380
  221. package/src/components/theme/Component/Component.jsx +1 -1
  222. package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
  223. package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
  224. package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
  225. package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +41 -3
  226. package/src/components/theme/Error/ServerError.jsx +29 -0
  227. package/src/components/theme/Header/Header.jsx +37 -63
  228. package/src/components/theme/Header/Header.test.jsx +18 -0
  229. package/src/components/theme/Image/Image.jsx +96 -0
  230. package/src/components/theme/Image/Image.test.jsx +125 -0
  231. package/src/components/theme/Login/Login.jsx +160 -243
  232. package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
  233. package/src/components/theme/Logo/Logo.jsx +35 -27
  234. package/src/components/theme/Logo/Logo.test.jsx +135 -1
  235. package/src/components/theme/Logout/Logout.jsx +36 -83
  236. package/src/components/theme/Navigation/Navigation.jsx +86 -171
  237. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -5
  238. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +95 -170
  239. package/src/components/theme/PreviewImage/PreviewImage.jsx +31 -15
  240. package/src/components/theme/PreviewImage/PreviewImage.test.js +53 -13
  241. package/src/components/theme/Register/Register.jsx +2 -4
  242. package/src/components/theme/Search/SearchTags.jsx +30 -60
  243. package/src/components/theme/SearchWidget/SearchWidget.jsx +49 -97
  244. package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
  245. package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
  246. package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
  247. package/src/components/theme/TsTest/TsTest.test.tsx +11 -0
  248. package/src/components/theme/TsTest/TsTest.tsx +15 -0
  249. package/src/components/theme/View/AlbumView.jsx +21 -16
  250. package/src/components/theme/View/EventView.jsx +36 -25
  251. package/src/components/theme/View/FileView.jsx +23 -18
  252. package/src/components/theme/View/ImageView.jsx +40 -32
  253. package/src/components/theme/View/ImageView.test.jsx +4 -0
  254. package/src/components/theme/View/LinkView.jsx +53 -78
  255. package/src/components/theme/View/ListingView.jsx +36 -28
  256. package/src/components/theme/View/NewsItemView.jsx +16 -17
  257. package/src/components/theme/View/RenderBlocks.jsx +56 -27
  258. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  259. package/src/components/theme/View/SummaryView.jsx +49 -39
  260. package/src/components/theme/View/TabularView.jsx +59 -53
  261. package/src/components/theme/View/View.jsx +2 -0
  262. package/src/components/theme/Widgets/ImageWidget.stories.jsx +1 -2
  263. package/src/config/Blocks.jsx +46 -0
  264. package/src/config/Components.jsx +3 -1
  265. package/src/config/ControlPanels.js +0 -1
  266. package/src/config/Loadables.jsx +1 -1
  267. package/src/config/NonContentRoutes.jsx +1 -0
  268. package/src/config/RichTextEditor/Blocks.jsx +4 -5
  269. package/src/config/RichTextEditor/FromHTML.jsx +2 -2
  270. package/src/config/RichTextEditor/Plugins.jsx +2 -3
  271. package/src/config/RichTextEditor/Styles.jsx +1 -1
  272. package/src/config/RichTextEditor/ToHTML.jsx +12 -10
  273. package/src/config/RichTextEditor/index.js +2 -3
  274. package/src/config/Views.jsx +6 -4
  275. package/src/config/Widgets.jsx +3 -0
  276. package/src/config/index.js +36 -2
  277. package/src/config/server.js +2 -0
  278. package/src/constants/ActionTypes.js +4 -0
  279. package/src/constants/Indexes.js +3 -1
  280. package/src/express-middleware/devproxy.js +1 -1
  281. package/src/express-middleware/files.js +11 -9
  282. package/src/express-middleware/images.js +12 -5
  283. package/src/express-middleware/ok.js +16 -0
  284. package/src/express-middleware/robotstxt.js +1 -1
  285. package/src/express-middleware/sitemap.js +1 -1
  286. package/src/express-middleware/static.js +3 -3
  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 +6 -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
@@ -0,0 +1,209 @@
1
+ /**
2
+ * LinksToItem component
3
+ * @module components/manage/LinksToItem/LinksToItem
4
+ */
5
+ import { useEffect } from 'react';
6
+ import { find } from 'lodash';
7
+ import { Helmet } from '@plone/volto/helpers';
8
+ import { Link } from 'react-router-dom';
9
+ import { Portal } from 'react-portal';
10
+ import { Container, Segment, Table } from 'semantic-ui-react';
11
+ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
12
+ import { useDispatch, useSelector } from 'react-redux';
13
+ import { getContent, queryRelations } from '@plone/volto/actions';
14
+ import {
15
+ Icon as IconNext,
16
+ Toolbar,
17
+ UniversalLink,
18
+ } from '@plone/volto/components';
19
+
20
+ import { getBaseUrl } from '@plone/volto/helpers';
21
+ import backSVG from '@plone/volto/icons/back.svg';
22
+ import settingsSVG from '@plone/volto/icons/settings.svg';
23
+
24
+ const messages = defineMessages({
25
+ back: {
26
+ id: 'Back',
27
+ defaultMessage: 'Back',
28
+ },
29
+ linkstoitem: {
30
+ id: 'Links and references',
31
+ defaultMessage: 'Links and references',
32
+ },
33
+ helpLinkRelationsControlPanel: {
34
+ id: 'Overview of relations of all content items',
35
+ defaultMessage: 'Overview of relations of all content items',
36
+ },
37
+ });
38
+
39
+ const LinksToItem = (props) => {
40
+ const intl = useIntl();
41
+ const dispatch = useDispatch();
42
+ const pathname = props.location.pathname;
43
+ const itempath = getBaseUrl(pathname);
44
+
45
+ const title = useSelector((state) => state.content.data?.title || '');
46
+ const myrelations = useSelector(
47
+ (state) => state.relations.subrequests[itempath]?.data,
48
+ );
49
+ const actions = useSelector((state) => state.actions?.actions ?? {});
50
+ const ploneSetupAction = find(actions.user, {
51
+ id: 'plone_setup',
52
+ });
53
+
54
+ useEffect(() => {
55
+ dispatch(queryRelations(null, false, itempath, null, [itempath]));
56
+ }, [dispatch, itempath]);
57
+
58
+ useEffect(() => {
59
+ if (!title) dispatch(getContent(itempath));
60
+ }, [dispatch, itempath, title]);
61
+
62
+ let links = {};
63
+ if (myrelations) {
64
+ Object.keys(myrelations).forEach((relationtype) => {
65
+ links[relationtype] = {};
66
+ myrelations[relationtype].items.forEach((item) => {
67
+ links[relationtype][item.source.UID] = item.source;
68
+ });
69
+ });
70
+ }
71
+
72
+ let links_ordered = {};
73
+ Object.keys(links).forEach((relationtype) => {
74
+ links_ordered[relationtype] = Object.values(links[relationtype]).sort(
75
+ (link) => link['@id'],
76
+ );
77
+ });
78
+
79
+ const relations_found = Object.keys(links_ordered).length > 0;
80
+ return (
81
+ <Container id="linkstoitem">
82
+ <Helmet title={intl.formatMessage(messages.linkstoitem)} />
83
+ <Segment.Group raised>
84
+ <Segment className="primary">
85
+ <FormattedMessage
86
+ id="Content that links to or references {title}"
87
+ defaultMessage="Content that links to or references {title}"
88
+ values={{ title: <q>{title}</q> }}
89
+ />
90
+ </Segment>
91
+ {relations_found ? (
92
+ <Table selectable compact singleLine attached>
93
+ {
94
+ <Table.Body>
95
+ {Object.keys(links_ordered).map((relationtype) => {
96
+ return [].concat(
97
+ [
98
+ <Table.Row>
99
+ <Table.HeaderCell>
100
+ {relationtype === 'isReferencing' ? (
101
+ <FormattedMessage
102
+ id="Linking this item with hyperlink in text"
103
+ defaultMessage="Linking this item with hyperlink in text"
104
+ />
105
+ ) : relationtype === 'relatedItems' ? (
106
+ <FormattedMessage
107
+ id="Referencing this item as related item"
108
+ defaultMessage="Referencing this item as related item"
109
+ />
110
+ ) : (
111
+ <>
112
+ <FormattedMessage
113
+ id="Referencing this item with {relationship}"
114
+ defaultMessage="Referencing this item with {relationship}"
115
+ values={{ relationship: <q>{relationtype}</q> }}
116
+ />
117
+ </>
118
+ )}
119
+ </Table.HeaderCell>
120
+ <Table.HeaderCell>
121
+ <FormattedMessage
122
+ id="Review state"
123
+ defaultMessage="Review state"
124
+ />
125
+ </Table.HeaderCell>
126
+ <Table.HeaderCell>
127
+ <FormattedMessage id="Type" defaultMessage="Type" />
128
+ </Table.HeaderCell>
129
+ </Table.Row>,
130
+ ],
131
+ links_ordered[relationtype].map((link) => {
132
+ return (
133
+ <Table.Row key={link['@id']}>
134
+ <Table.Cell>
135
+ <UniversalLink
136
+ href={link['@id']}
137
+ className={`source ${link.review_state}`}
138
+ >
139
+ <span className="label" title={link.type_title}>
140
+ {link.title}
141
+ </span>
142
+ </UniversalLink>
143
+ </Table.Cell>
144
+ <Table.Cell>
145
+ <span>{link.review_state}</span>
146
+ </Table.Cell>
147
+ <Table.Cell>
148
+ <span>{link.type_title || ''}</span>
149
+ </Table.Cell>
150
+ </Table.Row>
151
+ );
152
+ }),
153
+ );
154
+ })}
155
+ </Table.Body>
156
+ }
157
+ </Table>
158
+ ) : (
159
+ <Segment secondary>
160
+ <FormattedMessage
161
+ id="No links to this item found."
162
+ defaultMessage="No links to this item found."
163
+ />
164
+ </Segment>
165
+ )}
166
+ </Segment.Group>
167
+ {__CLIENT__ && (
168
+ <Portal node={document.getElementById('toolbar')}>
169
+ <Toolbar
170
+ pathname={pathname}
171
+ hideDefaultViewButtons
172
+ inner={
173
+ <>
174
+ <Link to={itempath} className="item">
175
+ <IconNext
176
+ name={backSVG}
177
+ className="contents circled"
178
+ size="30px"
179
+ title={intl.formatMessage(messages.back)}
180
+ />
181
+ </Link>
182
+
183
+ <>
184
+ {ploneSetupAction ? (
185
+ <Link to="/controlpanel/relations" className="relations">
186
+ <IconNext
187
+ name={settingsSVG}
188
+ className="circled"
189
+ aria-label={intl.formatMessage(
190
+ messages.helpLinkRelationsControlPanel,
191
+ )}
192
+ size="30px"
193
+ title={intl.formatMessage(
194
+ messages.helpLinkRelationsControlPanel,
195
+ )}
196
+ />
197
+ </Link>
198
+ ) : null}
199
+ </>
200
+ </>
201
+ }
202
+ />
203
+ </Portal>
204
+ )}
205
+ </Container>
206
+ );
207
+ };
208
+
209
+ export default LinksToItem;
@@ -0,0 +1,100 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import { Provider } from 'react-intl-redux';
4
+ import configureMockStore from 'redux-mock-store';
5
+ import thunk from 'redux-thunk';
6
+ import { MemoryRouter } from 'react-router-dom';
7
+
8
+ import LinksToItem from './LinksToItem';
9
+
10
+ const middlewares = [thunk];
11
+ const mockStore = configureMockStore(middlewares);
12
+
13
+ jest.mock('react-portal', () => ({
14
+ Portal: jest.fn(() => <div id="Portal" />),
15
+ }));
16
+ jest.mock('../Toolbar/More', () => jest.fn(() => <div className="More" />));
17
+
18
+ describe('LinksToItem', () => {
19
+ it('renders "links and references" view', () => {
20
+ const store = mockStore({
21
+ relations: {
22
+ subrequests: {
23
+ '/page-1': {
24
+ data: {
25
+ isReferencing: {
26
+ items: [
27
+ {
28
+ source: {
29
+ '@id': 'http://localhost:3000/page-basil',
30
+ '@type': 'Document',
31
+ UID: 'SOMEUID008',
32
+ description: '',
33
+ review_state: 'published',
34
+ title: 'Basil',
35
+ type_title: 'Document',
36
+ },
37
+ target: {
38
+ '@id': 'http://localhost:3000/page-tomato',
39
+ '@type': 'Document',
40
+ UID: 'SOMEUID007',
41
+ description: '',
42
+ review_state: 'published',
43
+ title: 'Tomato',
44
+ type_title: 'Document',
45
+ },
46
+ },
47
+ ],
48
+ items_total: 1,
49
+ },
50
+ relatedItems: {
51
+ items: [
52
+ {
53
+ source: {
54
+ '@id': 'http://localhost:3000/page-cucumber',
55
+ '@type': 'Document',
56
+ UID: 'SOMEUID008',
57
+ description: '',
58
+ review_state: 'published',
59
+ title: 'Cucumber',
60
+ type_title: 'Document',
61
+ },
62
+ target: {
63
+ '@id': 'http://localhost:3000/page-tomato',
64
+ '@type': 'Document',
65
+ UID: 'SOMEUID007',
66
+ description: '',
67
+ review_state: 'published',
68
+ title: 'Tomato',
69
+ type_title: 'Document',
70
+ },
71
+ },
72
+ ],
73
+ items_total: 1,
74
+ },
75
+ },
76
+ },
77
+ },
78
+ },
79
+ content: {
80
+ data: {
81
+ UID: 'SOMEUID007',
82
+ title: 'page #1',
83
+ },
84
+ },
85
+ intl: {
86
+ locale: 'en',
87
+ messages: {},
88
+ },
89
+ });
90
+ const component = renderer.create(
91
+ <Provider store={store}>
92
+ <MemoryRouter>
93
+ <LinksToItem location={{ pathname: '/page-1/links-to-item' }} />
94
+ </MemoryRouter>
95
+ </Provider>,
96
+ );
97
+ const json = component.toJSON();
98
+ expect(json).toMatchSnapshot();
99
+ });
100
+ });
@@ -12,8 +12,7 @@ const messages = defineMessages({
12
12
  defaultMessage: 'This item was locked by {creator} on {date}',
13
13
  },
14
14
  unlockItem: {
15
- id:
16
- 'If you are certain this user has abandoned the object, you may unlock the object. You will then be able to edit it.',
15
+ id: 'If you are certain this user has abandoned the object, you may unlock the object. You will then be able to edit it.',
17
16
  defaultMessage:
18
17
  'If you are certain this user has abandoned the object, you may unlock the object. You will then be able to edit it.',
19
18
  },
@@ -1,107 +1,40 @@
1
- /**
2
- * Messages component.
3
- * @module components/manage/Messages/Messages
4
- */
5
-
6
- import React, { Component } from 'react';
7
- import PropTypes from 'prop-types';
8
- import { connect } from 'react-redux';
1
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
9
2
  import { Message, Container } from 'semantic-ui-react';
10
3
  import { map } from 'lodash';
11
4
 
12
5
  import { removeMessage } from '@plone/volto/actions';
13
6
 
14
- /**
15
- * Messages container class.
16
- * @class Messages
17
- * @extends Component
18
- */
19
- class Messages extends Component {
20
- /**
21
- * Property types.
22
- * @property {Object} propTypes Property types.
23
- * @static
24
- */
25
- static propTypes = {
26
- removeMessage: PropTypes.func.isRequired,
27
- messages: PropTypes.arrayOf(
28
- PropTypes.shape({
29
- title: PropTypes.string,
30
- body: PropTypes.string,
31
- level: PropTypes.string,
32
- }),
33
- ).isRequired,
34
- };
35
-
36
- /**
37
- * Constructor
38
- * @method constructor
39
- * @param {Object} props Component properties
40
- * @constructs Messages
41
- */
42
- constructor(props) {
43
- super(props);
44
- this.onDismiss = this.onDismiss.bind(this);
45
- }
7
+ const Messages = () => {
8
+ const dispatch = useDispatch();
46
9
 
47
- // /**
48
- // * Component will receive props
49
- // * @method componentWillReceiveProps
50
- // * @param {Object} nextProps Next properties
51
- // * @returns {undefined}
52
- // */
53
- // componentWillReceiveProps(nextProps) {
54
- // if (nextProps.messages.length > this.props.messages.length) {
55
- // window.setTimeout(() => {
56
- // if (this.props.messages.length > 0) {
57
- // this.props.removeMessage(-1);
58
- // }
59
- // }, 6000);
60
- // }
61
- // }
10
+ const messages = useSelector(
11
+ (state) => state.messages.messages,
12
+ shallowEqual,
13
+ );
62
14
 
63
- /**
64
- * On dismiss
65
- * @method onDismiss
66
- * @param {Object} event Event object
67
- * @param {number} value Index of message
68
- * @returns {undefined}
69
- */
70
- onDismiss(event, { value }) {
71
- this.props.removeMessage(value);
72
- }
73
-
74
- /**
75
- * Render method.
76
- * @method render
77
- * @returns {string} Markup for the component.
78
- */
79
- render() {
80
- return (
81
- this.props.messages && (
82
- <Container className="messages">
83
- {map(this.props.messages, (message, index) => (
84
- <Message
85
- key={message.id}
86
- value={index}
87
- onDismiss={this.onDismiss}
88
- error={message.level === 'error'}
89
- success={message.level === 'success'}
90
- warning={message.level === 'warning'}
91
- info={message.level === 'info'}
92
- header={message.title}
93
- content={message.body}
94
- />
95
- ))}
96
- </Container>
97
- )
98
- );
99
- }
100
- }
15
+ const onDismiss = (event, { value }) => {
16
+ dispatch(removeMessage(value));
17
+ };
101
18
 
102
- export default connect(
103
- (state) => ({
104
- messages: state.messages.messages,
105
- }),
106
- { removeMessage },
107
- )(Messages);
19
+ return (
20
+ messages && (
21
+ <Container className="messages">
22
+ {map(messages, (message, index) => (
23
+ <Message
24
+ key={message.id}
25
+ value={index}
26
+ onDismiss={onDismiss}
27
+ error={message.level === 'error'}
28
+ success={message.level === 'success'}
29
+ warning={message.level === 'warning'}
30
+ info={message.level === 'info'}
31
+ header={message.title}
32
+ content={message.body}
33
+ />
34
+ ))}
35
+ </Container>
36
+ )
37
+ );
38
+ };
39
+
40
+ export default Messages;
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import renderer from 'react-test-renderer';
3
2
  import configureStore from 'redux-mock-store';
4
3
  import { Provider } from 'react-redux';
@@ -43,8 +43,8 @@ const messages = defineMessages({
43
43
  defaultMessage: 'New password',
44
44
  },
45
45
  newPasswordDescription: {
46
- id: 'Enter your new password. Minimum 5 characters.',
47
- defaultMessage: 'Enter your new password. Minimum 5 characters.',
46
+ id: 'Enter your new password. Minimum 8 characters.',
47
+ defaultMessage: 'Enter your new password. Minimum 8 characters.',
48
48
  },
49
49
  newPasswordRepeatTitle: {
50
50
  id: 'Confirm password',
@@ -14,7 +14,7 @@ import { Portal } from 'react-portal';
14
14
  import {
15
15
  Button,
16
16
  Checkbox,
17
- Container,
17
+ Container as SemanticContainer,
18
18
  Form,
19
19
  Icon as IconOld,
20
20
  Input,
@@ -28,6 +28,7 @@ import { updateSharing, getSharing } from '@plone/volto/actions';
28
28
  import { getBaseUrl } from '@plone/volto/helpers';
29
29
  import { Icon, Toolbar, Toast } from '@plone/volto/components';
30
30
  import { toast } from 'react-toastify';
31
+ import config from '@plone/volto/registry';
31
32
 
32
33
  import aheadSVG from '@plone/volto/icons/ahead.svg';
33
34
  import clearSVG from '@plone/volto/icons/clear.svg';
@@ -38,6 +39,10 @@ const messages = defineMessages({
38
39
  id: 'Search for user or group',
39
40
  defaultMessage: 'Search for user or group',
40
41
  },
42
+ search: {
43
+ id: 'Search',
44
+ defaultMessage: 'Search',
45
+ },
41
46
  inherit: {
42
47
  id: 'Inherit permissions from higher levels',
43
48
  defaultMessage: 'Inherit permissions from higher levels',
@@ -82,6 +87,10 @@ const messages = defineMessages({
82
87
  id: 'Permissions have been updated successfully',
83
88
  defaultMessage: 'Permissions have been updated successfully',
84
89
  },
90
+ assignNewRoles: {
91
+ id: 'Assign the {role} role to {entry}',
92
+ defaultMessage: 'Assign the {role} role to {entry}',
93
+ },
85
94
  });
86
95
 
87
96
  /**
@@ -144,6 +153,7 @@ class SharingComponent extends Component {
144
153
  this.onToggleInherit = this.onToggleInherit.bind(this);
145
154
  this.state = {
146
155
  search: '',
156
+ isLoading: false,
147
157
  inherit: props.inherit,
148
158
  entries: props.entries,
149
159
  isClient: false,
@@ -224,7 +234,17 @@ class SharingComponent extends Component {
224
234
  */
225
235
  onSearch(event) {
226
236
  event.preventDefault();
227
- this.props.getSharing(getBaseUrl(this.props.pathname), this.state.search);
237
+ this.setState({ isLoading: true });
238
+ this.props
239
+ .getSharing(getBaseUrl(this.props.pathname), this.state.search)
240
+ .then(() => {
241
+ this.setState({ isLoading: false });
242
+ })
243
+ .catch((error) => {
244
+ this.setState({ isLoading: false });
245
+ // eslint-disable-next-line no-console
246
+ console.error('Error searching users or groups', error);
247
+ });
228
248
  }
229
249
 
230
250
  /**
@@ -245,9 +265,9 @@ class SharingComponent extends Component {
245
265
  * @returns {undefined}
246
266
  */
247
267
  onToggleInherit() {
248
- this.setState({
249
- inherit: !this.state.inherit,
250
- });
268
+ this.setState((state) => ({
269
+ inherit: !state.inherit,
270
+ }));
251
271
  }
252
272
 
253
273
  /**
@@ -288,11 +308,17 @@ class SharingComponent extends Component {
288
308
  * @returns {string} Markup for the component.
289
309
  */
290
310
  render() {
311
+ const Container =
312
+ config.getComponent({ name: 'Container' }).component || SemanticContainer;
313
+
291
314
  return (
292
315
  <Container id="page-sharing">
293
316
  <Helmet title={this.props.intl.formatMessage(messages.sharing)} />
294
317
  <Segment.Group raised>
295
- <Pluggable name="sharing-component" />
318
+ <Pluggable
319
+ name="sharing-component"
320
+ params={{ isLoading: this.state.isLoading }}
321
+ />
296
322
  <Plug pluggable="sharing-component" id="sharing-component-title">
297
323
  <Segment className="primary">
298
324
  <FormattedMessage
@@ -314,20 +340,32 @@ class SharingComponent extends Component {
314
340
  </Segment>
315
341
  </Plug>
316
342
  <Plug pluggable="sharing-component" id="sharing-component-search">
317
- <Segment>
318
- <Form onSubmit={this.onSearch}>
319
- <Form.Field>
320
- <Input
321
- name="SearchableText"
322
- action={{ icon: 'search' }}
323
- placeholder={this.props.intl.formatMessage(
324
- messages.searchForUserOrGroup,
325
- )}
326
- onChange={this.onChangeSearch}
327
- />
328
- </Form.Field>
329
- </Form>
330
- </Segment>
343
+ {({ isLoading }) => {
344
+ return (
345
+ <Segment>
346
+ <Form onSubmit={this.onSearch}>
347
+ <Form.Field>
348
+ <Input
349
+ name="SearchableText"
350
+ action={{
351
+ icon: 'search',
352
+ loading: isLoading,
353
+ disabled: isLoading,
354
+ 'aria-label': this.props.intl.formatMessage(
355
+ messages.search,
356
+ ),
357
+ }}
358
+ placeholder={this.props.intl.formatMessage(
359
+ messages.searchForUserOrGroup,
360
+ )}
361
+ onChange={this.onChangeSearch}
362
+ id="sharing-component-search"
363
+ />
364
+ </Form.Field>
365
+ </Form>
366
+ </Segment>
367
+ );
368
+ }}
331
369
  </Plug>
332
370
  <Plug
333
371
  pluggable="sharing-component"
@@ -385,6 +423,20 @@ class SharingComponent extends Component {
385
423
  )}
386
424
  {typeof entry.roles[role.id] === 'boolean' && (
387
425
  <Checkbox
426
+ name={this.props.intl.formatMessage(
427
+ messages.assignNewRoles,
428
+ {
429
+ entry: entry.title,
430
+ role: role.id,
431
+ },
432
+ )}
433
+ aria-label={this.props.intl.formatMessage(
434
+ messages.assignNewRoles,
435
+ {
436
+ entry: entry.title,
437
+ role: role.id,
438
+ },
439
+ )}
388
440
  onChange={this.onChange}
389
441
  value={`${entry.id}:${role.id}`}
390
442
  checked={entry.roles[role.id]}
@@ -400,9 +452,15 @@ class SharingComponent extends Component {
400
452
  <Segment attached>
401
453
  <Form.Field>
402
454
  <Checkbox
403
- checked={this.state.inherit}
455
+ id="inherit-permissions-checkbox"
456
+ name="inherit-permissions-checkbox"
457
+ defaultChecked={this.state.inherit}
404
458
  onChange={this.onToggleInherit}
405
- label={this.props.intl.formatMessage(messages.inherit)}
459
+ label={
460
+ <label htmlFor="inherit-permissions-checkbox">
461
+ {this.props.intl.formatMessage(messages.inherit)}
462
+ </label>
463
+ }
406
464
  />
407
465
  </Form.Field>
408
466
  <p className="help">
@@ -57,7 +57,7 @@ const AlignBlock = ({
57
57
  return (
58
58
  <div className="align-buttons">
59
59
  {actions.map((action) => (
60
- <Button.Group>
60
+ <Button.Group key={action}>
61
61
  <Button
62
62
  icon
63
63
  basic