@plone/volto 17.0.0-alpha.0 → 17.0.0-alpha.10

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 (190) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +451 -19
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +12 -15
  5. package/addon-registry.js +34 -0
  6. package/create-theme-addons-loader.js +79 -0
  7. package/cypress/support/commands.js +25 -0
  8. package/locales/ca/LC_MESSAGES/volto.po +187 -6
  9. package/locales/ca.json +1 -1
  10. package/locales/de/LC_MESSAGES/volto.po +206 -25
  11. package/locales/de.json +1 -1
  12. package/locales/en/LC_MESSAGES/volto.po +186 -5
  13. package/locales/en.json +1 -1
  14. package/locales/es/LC_MESSAGES/volto.po +187 -6
  15. package/locales/es.json +1 -1
  16. package/locales/eu/LC_MESSAGES/volto.po +187 -6
  17. package/locales/eu.json +1 -1
  18. package/locales/fi/LC_MESSAGES/volto.po +4792 -0
  19. package/locales/fi.json +1 -1
  20. package/locales/fr/LC_MESSAGES/volto.po +187 -6
  21. package/locales/fr.json +1 -1
  22. package/locales/it/LC_MESSAGES/volto.po +187 -6
  23. package/locales/it.json +1 -1
  24. package/locales/ja/LC_MESSAGES/volto.po +187 -6
  25. package/locales/ja.json +1 -1
  26. package/locales/nl/LC_MESSAGES/volto.po +842 -649
  27. package/locales/nl.json +1 -1
  28. package/locales/pt/LC_MESSAGES/volto.po +187 -6
  29. package/locales/pt.json +1 -1
  30. package/locales/pt_BR/LC_MESSAGES/volto.po +195 -14
  31. package/locales/pt_BR.json +1 -1
  32. package/locales/ro/LC_MESSAGES/volto.po +187 -6
  33. package/locales/ro.json +1 -1
  34. package/locales/volto.pot +187 -6
  35. package/locales/zh_CN/LC_MESSAGES/volto.po +187 -6
  36. package/locales/zh_CN.json +1 -1
  37. package/package-why.json +0 -1
  38. package/package.json +9 -8
  39. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
  40. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
  41. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
  42. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
  43. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
  44. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
  45. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
  46. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
  47. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
  48. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
  49. package/packages/volto-slate/package.json +1 -1
  50. package/packages/volto-slate/src/blocks/Table/TableBlockView.jsx +4 -4
  51. package/packages/volto-slate/src/blocks/Table/index.js +2 -0
  52. package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
  53. package/packages/volto-slate/src/editor/deserialize.js +0 -1
  54. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  55. package/razzle.config.js +28 -0
  56. package/src/actions/index.js +6 -0
  57. package/src/actions/language/language.js +9 -8
  58. package/src/actions/querystringsearch/querystringsearch.js +20 -14
  59. package/src/actions/relations/rebuild.js +25 -0
  60. package/src/actions/relations/relations.js +86 -0
  61. package/src/actions/relations/relations.test.js +15 -0
  62. package/src/components/index.js +1 -0
  63. package/src/components/manage/Add/Add.jsx +2 -2
  64. package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
  65. package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
  66. package/src/components/manage/BlockChooser/BlockChooserButton.jsx +63 -29
  67. package/src/components/manage/BlockChooser/BlockChooserSearch.jsx +0 -1
  68. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -19
  69. package/src/components/manage/Blocks/Listing/ListingBody.jsx +77 -61
  70. package/src/components/manage/Blocks/Listing/View.jsx +0 -4
  71. package/src/components/manage/Blocks/Listing/getAsyncData.js +10 -2
  72. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -13
  73. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
  74. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
  75. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  76. package/src/components/manage/Blocks/Search/components/Facets.jsx +58 -2
  77. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +24 -11
  78. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  79. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  80. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  81. package/src/components/manage/Blocks/Search/schema.js +16 -1
  82. package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
  83. package/src/components/manage/Contents/Contents.jsx +69 -33
  84. package/src/components/manage/Contents/ContentsItem.jsx +6 -0
  85. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
  86. package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
  87. package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
  88. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
  89. package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
  90. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
  91. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
  92. package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
  93. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
  94. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
  95. package/src/components/manage/DragDropList/DragDropList.jsx +63 -42
  96. package/src/components/manage/Form/BlocksToolbar.jsx +5 -1
  97. package/src/components/manage/Form/Form.jsx +11 -5
  98. package/src/components/manage/Form/InlineForm.jsx +39 -9
  99. package/src/components/manage/Form/InlineFormState.js +8 -0
  100. package/src/components/manage/History/History.jsx +35 -18
  101. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  102. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  103. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  104. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  105. package/src/components/manage/Toast/Toast.jsx +1 -1
  106. package/src/components/manage/Toolbar/Types.jsx +2 -2
  107. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  108. package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
  109. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  110. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  111. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  112. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +7 -2
  113. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  114. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  115. package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
  116. package/src/components/theme/Footer/Footer.jsx +2 -13
  117. package/src/components/theme/Header/Header.jsx +37 -63
  118. package/src/components/theme/Header/Header.test.jsx +18 -0
  119. package/src/components/theme/Icon/Icon.jsx +2 -2
  120. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  121. package/src/components/theme/Login/Login.jsx +1 -0
  122. package/src/components/theme/Logo/Logo.jsx +2 -1
  123. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  124. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  125. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  126. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
  127. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
  128. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  129. package/src/components/theme/View/DefaultView.jsx +1 -1
  130. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  131. package/src/components/theme/View/EventView.jsx +1 -1
  132. package/src/components/theme/View/NewsItemView.jsx +1 -1
  133. package/src/components/theme/View/RenderBlocks.jsx +7 -1
  134. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  135. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  136. package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
  137. package/src/config/ControlPanels.js +2 -0
  138. package/src/config/Widgets.jsx +1 -0
  139. package/src/config/index.js +3 -0
  140. package/src/config/server.js +19 -0
  141. package/src/constants/ActionTypes.js +4 -0
  142. package/src/constants/Languages.js +8 -4
  143. package/src/express-middleware/devproxy.js +4 -2
  144. package/src/express-middleware/sitemap.js +36 -4
  145. package/src/express-middleware/static.js +32 -0
  146. package/src/helpers/Api/Api.js +1 -1
  147. package/src/helpers/FormValidation/FormValidation.js +11 -2
  148. package/src/helpers/FormValidation/FormValidation.test.js +73 -0
  149. package/src/helpers/Html/Html.jsx +3 -1
  150. package/src/helpers/Html/Html.test.jsx +5 -0
  151. package/src/helpers/MessageLabels/MessageLabels.js +72 -0
  152. package/src/helpers/Robots/Robots.js +24 -6
  153. package/src/helpers/Sitemap/Sitemap.js +44 -2
  154. package/src/helpers/Url/Url.js +27 -6
  155. package/src/helpers/Url/Url.test.js +26 -0
  156. package/src/helpers/Utils/Utils.js +38 -13
  157. package/src/helpers/Utils/Utils.test.js +4 -4
  158. package/src/helpers/index.js +7 -2
  159. package/src/hooks/userSession/useToken.js +5 -0
  160. package/src/middleware/Api.test.js +54 -0
  161. package/src/middleware/api.js +8 -4
  162. package/src/reducers/actions/actions.js +1 -1
  163. package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
  164. package/src/reducers/index.js +2 -0
  165. package/src/reducers/navigation/navigation.js +1 -1
  166. package/src/reducers/relations/relations.js +173 -0
  167. package/src/reducers/types/types.js +1 -1
  168. package/src/routes.js +5 -0
  169. package/src/server.jsx +29 -30
  170. package/src/start-server.js +4 -2
  171. package/test-setup-config.js +1 -0
  172. package/theme/themes/pastanaga/extras/blocks.less +0 -9
  173. package/theme/themes/pastanaga/extras/contents.less +1 -0
  174. package/theme/themes/pastanaga/extras/main.less +80 -1
  175. package/theme/themes/pastanaga/extras/search.less +6 -0
  176. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  177. package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
  178. package/.changelog.draft +0 -22
  179. package/.editorconfig +0 -36
  180. package/.storybook/main.js +0 -127
  181. package/.storybook/manager.js +0 -15
  182. package/.storybook/preview.js +0 -21
  183. package/.storybook/static/previewImage.svg +0 -48
  184. package/.yarnrc.yml +0 -5
  185. package/jsdoc.json +0 -16
  186. package/netlify.toml +0 -5
  187. package/pyvenv.cfg +0 -3
  188. package/share/man/man1/ttx.1 +0 -225
  189. package/src/components/theme/Header/Header.md +0 -27
  190. package/towncrier.toml +0 -33
@@ -1,5 +1,9 @@
1
1
  import { updateIntl } from 'react-intl-redux';
2
- import { normalizeLanguageName, getCookieOptions } from '@plone/volto/helpers';
2
+ import {
3
+ toGettextLang,
4
+ toReactIntlLang,
5
+ getCookieOptions,
6
+ } from '@plone/volto/helpers';
3
7
  import Cookies from 'universal-cookie';
4
8
 
5
9
  export function changeLanguageCookies(language, req) {
@@ -7,18 +11,15 @@ export function changeLanguageCookies(language, req) {
7
11
 
8
12
  const cookieOptions = getCookieOptions({
9
13
  secure: req?.protocol?.startsWith('https') ? true : false,
14
+ sameSite: 'strict',
10
15
  });
11
16
 
12
17
  if (!req) {
13
- cookies.set(
14
- 'I18N_LANGUAGE',
15
- normalizeLanguageName(language) || '',
16
- cookieOptions,
17
- );
18
+ cookies.set('I18N_LANGUAGE', toGettextLang(language) || '', cookieOptions);
18
19
  } else {
19
20
  req.universalCookies.set(
20
21
  'I18N_LANGUAGE',
21
- normalizeLanguageName(language) || '',
22
+ toGettextLang(language) || '',
22
23
  cookieOptions,
23
24
  );
24
25
  }
@@ -35,7 +36,7 @@ export function changeLanguage(language, locale, req) {
35
36
  changeLanguageCookies(language, req);
36
37
 
37
38
  return updateIntl({
38
- locale: language,
39
+ locale: toReactIntlLang(language),
39
40
  messages: locale,
40
41
  });
41
42
  }
@@ -31,24 +31,30 @@ export function getQueryStringResults(path, data, subrequest, page) {
31
31
  }
32
32
  }
33
33
 
34
+ const query = {
35
+ ...requestData,
36
+ ...(!requestData.b_size && {
37
+ b_size: settings.defaultPageSize,
38
+ }),
39
+ ...(page && {
40
+ b_start: requestData.b_size
41
+ ? data.b_size * (page - 1)
42
+ : settings.defaultPageSize * (page - 1),
43
+ }),
44
+ query: requestData?.query,
45
+ };
46
+
34
47
  return {
35
48
  type: GET_QUERYSTRING_RESULTS,
36
49
  subrequest,
37
50
  request: {
38
- op: 'post',
39
- path: `${path}/@querystring-search`,
40
- data: {
41
- ...requestData,
42
- ...(!requestData.b_size && {
43
- b_size: settings.defaultPageSize,
44
- }),
45
- ...(page && {
46
- b_start: requestData.b_size
47
- ? data.b_size * (page - 1)
48
- : settings.defaultPageSize * (page - 1),
49
- }),
50
- query: requestData?.query,
51
- },
51
+ op: settings.querystringSearchGet ? 'get' : 'post',
52
+ path: `${path}/@querystring-search${
53
+ settings.querystringSearchGet
54
+ ? `?query=${encodeURIComponent(JSON.stringify(query))}`
55
+ : ''
56
+ }`,
57
+ data: settings.querystringSearchGet ? null : query,
52
58
  },
53
59
  };
54
60
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Rebuild relations action.
3
+ * @module actions/relations/rebuild
4
+ */
5
+
6
+ import { REBUILD_RELATIONS } from '@plone/volto/constants/ActionTypes';
7
+
8
+ /**
9
+ * Rebuild relation function.
10
+ * @function rebuildRelations
11
+ * @param {Boolean} flush Flush intids
12
+ * @returns {Object} Rebuild relation action.
13
+ */
14
+ export function rebuildRelations(flush = false) {
15
+ let path = '/@relations';
16
+ var searchParams = new URLSearchParams();
17
+ searchParams.append('rebuild', '1');
18
+ flush && searchParams.append('flush', '1');
19
+ const searchParamsToString = searchParams.toString();
20
+ path += `?${searchParamsToString}`;
21
+ return {
22
+ type: REBUILD_RELATIONS,
23
+ request: { op: 'get', path: path },
24
+ };
25
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Relations actions.
3
+ * @module actions/relations/relations
4
+ */
5
+
6
+ import {
7
+ CREATE_RELATIONS,
8
+ DELETE_RELATIONS,
9
+ LIST_RELATIONS,
10
+ } from '@plone/volto/constants/ActionTypes';
11
+
12
+ /**
13
+ * Create relation function.
14
+ * @function createRelations
15
+ * @param {Object|Array} content Relation data.
16
+ * @returns {Object} Create relation action.
17
+ */
18
+ export function createRelations(content) {
19
+ return {
20
+ type: CREATE_RELATIONS,
21
+ request: {
22
+ op: 'post',
23
+ path: '/@relations',
24
+ data: {
25
+ items: content,
26
+ },
27
+ },
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Delete relation function.
33
+ * @function deleteRelations
34
+ * @param {string} id Relation id
35
+ * @returns {Object} Delete relation action.
36
+ */
37
+ export function deleteRelations(content) {
38
+ return {
39
+ type: DELETE_RELATIONS,
40
+ request: {
41
+ op: 'del',
42
+ path: `/@relations`,
43
+ data: {
44
+ items: content,
45
+ },
46
+ },
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Query relations
52
+ * @function queryRelations
53
+ * @param {string} relation Name of relation
54
+ * @param {boolean} onlyBroken
55
+ * @returns {Object} List relations action
56
+ */
57
+ export function queryRelations(
58
+ relation = null,
59
+ onlyBroken = false,
60
+ subrequest = null,
61
+ source = null,
62
+ target = null,
63
+ query_source = null,
64
+ query_target = null,
65
+ ) {
66
+ let path = '/@relations';
67
+ var searchParams = new URLSearchParams();
68
+ relation && searchParams.append('relation', relation);
69
+ onlyBroken && searchParams.append('onlyBroken', onlyBroken);
70
+ source && searchParams.append('source', source);
71
+ target && searchParams.append('target', target);
72
+ query_source && searchParams.append('query_source', query_source);
73
+ query_target && searchParams.append('query_target', query_target);
74
+ const searchParamsToString = searchParams.toString();
75
+ if (searchParamsToString) {
76
+ path += `?${searchParamsToString}`;
77
+ }
78
+ return {
79
+ type: LIST_RELATIONS,
80
+ subrequest,
81
+ request: {
82
+ op: 'get',
83
+ path: path,
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,15 @@
1
+ import { queryRelations } from './relations';
2
+ import { LIST_RELATIONS } from '@plone/volto/constants/ActionTypes';
3
+
4
+ describe('Relations action', () => {
5
+ describe('queryRelations', () => {
6
+ it('should create an action to get relations of type "relatedItems"', () => {
7
+ const relation = 'relatedItems';
8
+ const action = queryRelations(relation);
9
+
10
+ expect(action.type).toEqual(LIST_RELATIONS);
11
+ expect(action.request.op).toEqual('get');
12
+ expect(action.request.path).toEqual(`/@relations?relation=${relation}`);
13
+ });
14
+ });
15
+ });
@@ -89,6 +89,7 @@ export ContentTypeSchema from '@plone/volto/components/manage/Controlpanels/Cont
89
89
  export ContentTypesActions from '@plone/volto/components/manage/Controlpanels/ContentTypesActions';
90
90
  export UsersControlpanel from '@plone/volto/components/manage/Controlpanels/Users/UsersControlpanel';
91
91
  export UserGroupMembershipControlPanel from '@plone/volto/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel';
92
+ export Relations from '@plone/volto/components/manage/Controlpanels/Relations/Relations';
92
93
  export GroupsControlpanel from '@plone/volto/components/manage/Controlpanels/Groups/GroupsControlpanel';
93
94
  export RulesControlpanel from '@plone/volto/components/manage/Controlpanels/Rules/Rules';
94
95
  export AddRuleControlpanel from '@plone/volto/components/manage/Controlpanels/Rules/AddRule';
@@ -33,7 +33,7 @@ import {
33
33
  getBlocksLayoutFieldname,
34
34
  getLanguageIndependentFields,
35
35
  langmap,
36
- normalizeLanguageName,
36
+ toGettextLang,
37
37
  } from '@plone/volto/helpers';
38
38
 
39
39
  import { preloadLazyLibs } from '@plone/volto/helpers/Loadable';
@@ -219,7 +219,7 @@ class Add extends Component {
219
219
  onCancel() {
220
220
  if (this.props.location?.state?.translationOf) {
221
221
  const language = this.props.location.state.languageFrom;
222
- const langFileName = normalizeLanguageName(language);
222
+ const langFileName = toGettextLang(language);
223
223
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
224
224
  this.props.changeLanguage(language, locale.default);
225
225
  });
@@ -33,17 +33,22 @@ const BlockChooser = ({
33
33
  properties = {},
34
34
  }) => {
35
35
  const intl = useIntl();
36
- const useAllowedBlocks = !isEmpty(allowedBlocks);
36
+ const hasAllowedBlocks = !isEmpty(allowedBlocks);
37
37
 
38
38
  const filteredBlocksConfig = filter(blocksConfig, (item) => {
39
+ // Check if the block is well formed (has at least id and title)
40
+ const blockIsWellFormed = Boolean(item.title && item.id);
41
+ if (!blockIsWellFormed) {
42
+ return false;
43
+ }
39
44
  if (showRestricted) {
40
- if (useAllowedBlocks) {
45
+ if (hasAllowedBlocks) {
41
46
  return allowedBlocks.includes(item.id);
42
47
  } else {
43
48
  return true;
44
49
  }
45
50
  } else {
46
- if (useAllowedBlocks) {
51
+ if (hasAllowedBlocks) {
47
52
  return allowedBlocks.includes(item.id);
48
53
  } else {
49
54
  // Overload restricted as a function, so we can decide the availability of a block
@@ -89,14 +94,18 @@ const BlockChooser = ({
89
94
  function blocksAvailableFilter(blocks) {
90
95
  return blocks.filter(
91
96
  (block) =>
92
- getFormatMessage(block.title).toLowerCase().includes(filterValue) ||
97
+ getFormatMessage(block.title)
98
+ .toLowerCase()
99
+ .includes(filterValue.toLowerCase()) ||
93
100
  filterVariations(block)?.length,
94
101
  );
95
102
  }
96
103
  function filterVariations(block) {
97
104
  return block.variations?.filter(
98
105
  (variation) =>
99
- getFormatMessage(variation.title).toLowerCase().includes(filterValue) &&
106
+ getFormatMessage(variation.title)
107
+ .toLowerCase()
108
+ .includes(filterValue.toLowerCase()) &&
100
109
  !variation.title.toLowerCase().includes('default'),
101
110
  );
102
111
  }
@@ -108,6 +108,11 @@ config.blocks.blocksConfig = {
108
108
  restricted: false,
109
109
  mostUsed: false,
110
110
  },
111
+ malformedBlock: {
112
+ icon: blockSVG,
113
+ group: 'common',
114
+ restricted: false,
115
+ },
111
116
  };
112
117
 
113
118
  const mockStore = configureStore();
@@ -4,8 +4,10 @@ import addSVG from '@plone/volto/icons/circle-plus.svg';
4
4
  import { blockHasValue } from '@plone/volto/helpers';
5
5
  import { Icon, BlockChooser } from '@plone/volto/components';
6
6
  import config from '@plone/volto/registry';
7
- import { Button } from 'semantic-ui-react';
7
+ import { Button, Ref } from 'semantic-ui-react';
8
8
  import { defineMessages, useIntl } from 'react-intl';
9
+ import { usePopper } from 'react-popper';
10
+ import { Portal } from 'react-portal';
9
11
 
10
12
  const messages = defineMessages({
11
13
  addBlock: {
@@ -76,41 +78,73 @@ const BlockChooserButton = (props) => {
76
78
  };
77
79
  }, [handleClickOutside]);
78
80
 
81
+ const [referenceElement, setReferenceElement] = React.useState(null);
82
+ const [popperElement, setPopperElement] = React.useState(null);
83
+ const { styles, attributes } = usePopper(referenceElement, popperElement, {
84
+ placement: config.experimental.addBlockButton.enabled
85
+ ? 'bottom'
86
+ : 'right-start',
87
+ modifiers: [
88
+ {
89
+ name: 'offset',
90
+ options: {
91
+ offset: [-10, 5],
92
+ },
93
+ },
94
+ {
95
+ name: 'flip',
96
+ options: {
97
+ fallbackPlacements: ['right-end', 'top-start'],
98
+ },
99
+ },
100
+ ],
101
+ });
102
+
79
103
  return (
80
104
  <>
81
105
  {!disableNewBlocks &&
82
106
  (config.experimental.addBlockButton.enabled ||
83
107
  !blockHasValue(data)) && (
84
- <Component
85
- {...props}
86
- onShowBlockChooser={() => setAddNewBlockOpened(true)}
87
- />
108
+ <Ref innerRef={setReferenceElement}>
109
+ <Component
110
+ {...props}
111
+ onShowBlockChooser={() => setAddNewBlockOpened(true)}
112
+ />
113
+ </Ref>
88
114
  )}
89
115
  {addNewBlockOpened && (
90
- <BlockChooser
91
- onMutateBlock={
92
- onMutateBlock
93
- ? (id, value) => {
94
- setAddNewBlockOpened(false);
95
- onMutateBlock(id, value);
96
- }
97
- : null
98
- }
99
- onInsertBlock={
100
- onInsertBlock
101
- ? (id, value) => {
102
- setAddNewBlockOpened(false);
103
- onInsertBlock(id, value);
104
- }
105
- : null
106
- }
107
- currentBlock={block}
108
- allowedBlocks={allowedBlocks}
109
- blocksConfig={blocksConfig}
110
- properties={properties}
111
- showRestricted={showRestricted}
112
- ref={blockChooserRef}
113
- />
116
+ <Portal node={document.getElementById('body')}>
117
+ <div
118
+ ref={setPopperElement}
119
+ style={styles.popper}
120
+ {...attributes.popper}
121
+ >
122
+ <BlockChooser
123
+ onMutateBlock={
124
+ onMutateBlock
125
+ ? (id, value) => {
126
+ setAddNewBlockOpened(false);
127
+ onMutateBlock(id, value);
128
+ }
129
+ : null
130
+ }
131
+ onInsertBlock={
132
+ onInsertBlock
133
+ ? (id, value) => {
134
+ setAddNewBlockOpened(false);
135
+ onInsertBlock(id, value);
136
+ }
137
+ : null
138
+ }
139
+ currentBlock={block}
140
+ allowedBlocks={allowedBlocks}
141
+ blocksConfig={blocksConfig}
142
+ properties={properties}
143
+ showRestricted={showRestricted}
144
+ ref={blockChooserRef}
145
+ />
146
+ </div>
147
+ </Portal>
114
148
  )}
115
149
  </>
116
150
  );
@@ -31,7 +31,6 @@ const BlockChooserSearch = ({ onChange, searchValue }) => {
31
31
  autoComplete="off"
32
32
  placeholder={intl.formatMessage(messages.search)}
33
33
  title={intl.formatMessage(messages.search)}
34
- autoFocus
35
34
  ref={searchInput}
36
35
  />
37
36
  {searchValue && (
@@ -26,35 +26,16 @@ const messages = defineMessages({
26
26
  const Edit = React.memo(
27
27
  (props) => {
28
28
  const { data, onChangeBlock, block, selected, pathname } = props;
29
-
30
29
  const intl = useIntl();
31
-
32
- // componentDidMount
33
- React.useEffect(() => {
34
- if (!data.query) {
35
- onChangeBlock(block, {
36
- ...data,
37
- query: [],
38
- block,
39
- });
40
- }
41
- /* eslint-disable react-hooks/exhaustive-deps */
42
- }, []);
43
-
44
30
  const placeholder =
45
31
  data.placeholder ||
46
32
  (data?.querystring?.query?.length
47
33
  ? intl.formatMessage(messages.results)
48
34
  : intl.formatMessage(messages.items));
49
35
 
50
- const HeadlineTag = data.headlineTag || 'h2';
51
-
52
36
  return (
53
37
  <>
54
38
  <p className="items-preview">{placeholder}</p>
55
- {data.headline && (
56
- <HeadlineTag className="headline">{data.headline}</HeadlineTag>
57
- )}
58
39
  <ListingBody {...props} path={getBaseUrl(pathname)} isEditMode />
59
40
  <SidebarPortal selected={selected}>
60
41
  <ListingData
@@ -1,5 +1,6 @@
1
1
  import React, { createRef } from 'react';
2
2
  import { FormattedMessage, injectIntl } from 'react-intl';
3
+ import cx from 'classnames';
3
4
  import { Pagination, Dimmer, Loader } from 'semantic-ui-react';
4
5
  import { Icon } from '@plone/volto/components';
5
6
  import config from '@plone/volto/registry';
@@ -44,70 +45,85 @@ const ListingBody = withQuerystringResults((props) => {
44
45
  ? variation.noResultsComponent
45
46
  : config.blocks?.blocksConfig['listing'].noResultsComponent;
46
47
 
47
- return listingItems?.length > 0 ? (
48
- <div ref={listingRef}>
49
- <ListingBodyTemplate
50
- items={listingItems}
51
- isEditMode={isEditMode}
52
- {...data}
53
- {...variation}
54
- />
55
- {totalPages > 1 && (
56
- <div className="pagination-wrapper">
57
- <Pagination
58
- activePage={currentPage}
59
- totalPages={totalPages}
60
- onPageChange={(e, { activePage }) => {
61
- !isEditMode &&
62
- listingRef.current.scrollIntoView({ behavior: 'smooth' });
63
- onPaginationChange(e, { activePage });
64
- }}
65
- firstItem={null}
66
- lastItem={null}
67
- prevItem={{
68
- content: <Icon name={paginationLeftSVG} size="18px" />,
69
- icon: true,
70
- 'aria-disabled': !prevBatch,
71
- className: !prevBatch ? 'disabled' : null,
72
- }}
73
- nextItem={{
74
- content: <Icon name={paginationRightSVG} size="18px" />,
75
- icon: true,
76
- 'aria-disabled': !nextBatch,
77
- className: !nextBatch ? 'disabled' : null,
78
- }}
48
+ const HeadlineTag = data.headlineTag || 'h2';
49
+
50
+ return (
51
+ <>
52
+ {data.headline && (
53
+ <HeadlineTag
54
+ className={cx('headline', {
55
+ emptyListing: !listingItems?.length > 0,
56
+ })}
57
+ >
58
+ {data.headline}
59
+ </HeadlineTag>
60
+ )}
61
+ {listingItems?.length > 0 ? (
62
+ <div ref={listingRef}>
63
+ <ListingBodyTemplate
64
+ items={listingItems}
65
+ isEditMode={isEditMode}
66
+ {...data}
67
+ {...variation}
79
68
  />
69
+ {totalPages > 1 && (
70
+ <div className="pagination-wrapper">
71
+ <Pagination
72
+ activePage={currentPage}
73
+ totalPages={totalPages}
74
+ onPageChange={(e, { activePage }) => {
75
+ !isEditMode &&
76
+ listingRef.current.scrollIntoView({ behavior: 'smooth' });
77
+ onPaginationChange(e, { activePage });
78
+ }}
79
+ firstItem={null}
80
+ lastItem={null}
81
+ prevItem={{
82
+ content: <Icon name={paginationLeftSVG} size="18px" />,
83
+ icon: true,
84
+ 'aria-disabled': !prevBatch,
85
+ className: !prevBatch ? 'disabled' : null,
86
+ }}
87
+ nextItem={{
88
+ content: <Icon name={paginationRightSVG} size="18px" />,
89
+ icon: true,
90
+ 'aria-disabled': !nextBatch,
91
+ className: !nextBatch ? 'disabled' : null,
92
+ }}
93
+ />
94
+ </div>
95
+ )}
96
+ </div>
97
+ ) : isEditMode ? (
98
+ <div className="listing message" ref={listingRef}>
99
+ {isFolderContentsListing && (
100
+ <FormattedMessage
101
+ id="No items found in this container."
102
+ defaultMessage="No items found in this container."
103
+ />
104
+ )}
105
+ {hasLoaded && NoResults && (
106
+ <NoResults isEditMode={isEditMode} {...data} />
107
+ )}
108
+ <Dimmer active={!hasLoaded} inverted>
109
+ <Loader indeterminate size="small">
110
+ <FormattedMessage id="loading" defaultMessage="Loading" />
111
+ </Loader>
112
+ </Dimmer>
113
+ </div>
114
+ ) : (
115
+ <div className="emptyListing">
116
+ {hasLoaded && NoResults && (
117
+ <NoResults isEditMode={isEditMode} {...data} />
118
+ )}
119
+ <Dimmer active={!hasLoaded} inverted>
120
+ <Loader indeterminate size="small">
121
+ <FormattedMessage id="loading" defaultMessage="Loading" />
122
+ </Loader>
123
+ </Dimmer>
80
124
  </div>
81
125
  )}
82
- </div>
83
- ) : isEditMode ? (
84
- <div className="listing message" ref={listingRef}>
85
- {isFolderContentsListing && (
86
- <FormattedMessage
87
- id="No items found in this container."
88
- defaultMessage="No items found in this container."
89
- />
90
- )}
91
- {hasLoaded && NoResults && (
92
- <NoResults isEditMode={isEditMode} {...data} />
93
- )}
94
- <Dimmer active={!hasLoaded} inverted>
95
- <Loader indeterminate size="small">
96
- <FormattedMessage id="loading" defaultMessage="Loading" />
97
- </Loader>
98
- </Dimmer>
99
- </div>
100
- ) : (
101
- <div>
102
- {hasLoaded && NoResults && (
103
- <NoResults isEditMode={isEditMode} {...data} />
104
- )}
105
- <Dimmer active={!hasLoaded} inverted>
106
- <Loader indeterminate size="small">
107
- <FormattedMessage id="loading" defaultMessage="Loading" />
108
- </Loader>
109
- </Dimmer>
110
- </div>
126
+ </>
111
127
  );
112
128
  });
113
129
 
@@ -7,15 +7,11 @@ import { ListingBlockBody as ListingBody } from '@plone/volto/components';
7
7
 
8
8
  const View = (props) => {
9
9
  const { data, path, pathname, className } = props;
10
- const HeadlineTag = data.headlineTag || 'h2';
11
10
 
12
11
  return (
13
12
  <div
14
13
  className={cx('block listing', data.variation || 'default', className)}
15
14
  >
16
- {data.headline && (
17
- <HeadlineTag className="headline">{data.headline}</HeadlineTag>
18
- )}
19
15
  <ListingBody {...props} path={path ?? pathname} />
20
16
  </div>
21
17
  );
@@ -1,7 +1,13 @@
1
1
  import { getQueryStringResults } from '@plone/volto/actions';
2
2
  import { resolveBlockExtensions } from '@plone/volto/helpers';
3
3
 
4
- export default ({ dispatch, data, path, blocksConfig }) => {
4
+ const getListingBlockAsyncData = ({
5
+ dispatch,
6
+ id,
7
+ data,
8
+ path,
9
+ blocksConfig,
10
+ }) => {
5
11
  const { resolvedExtensions } = resolveBlockExtensions(data, blocksConfig);
6
12
 
7
13
  return [
@@ -14,8 +20,10 @@ export default ({ dispatch, data, path, blocksConfig }) => {
14
20
  ? { fullobjects: 1 }
15
21
  : { metadata_fields: '_all' }),
16
22
  },
17
- data.block,
23
+ id,
18
24
  ),
19
25
  ),
20
26
  ];
21
27
  };
28
+
29
+ export default getListingBlockAsyncData;