@plone/volto 18.28.2 → 18.29.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 (160) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/cypress/support/commands.js +5 -6
  3. package/cypress.config.js +1 -0
  4. package/locales/af/LC_MESSAGES/volto.po +5 -0
  5. package/locales/af.json +1 -1
  6. package/locales/ar/LC_MESSAGES/volto.po +5 -0
  7. package/locales/ar.json +1 -1
  8. package/locales/bg/LC_MESSAGES/volto.po +5 -0
  9. package/locales/bg.json +1 -1
  10. package/locales/bn/LC_MESSAGES/volto.po +5 -0
  11. package/locales/bn.json +1 -1
  12. package/locales/ca/LC_MESSAGES/volto.po +5 -0
  13. package/locales/ca.json +1 -1
  14. package/locales/cs/LC_MESSAGES/volto.po +5 -0
  15. package/locales/cs.json +1 -1
  16. package/locales/cy/LC_MESSAGES/volto.po +5 -0
  17. package/locales/cy.json +1 -1
  18. package/locales/da/LC_MESSAGES/volto.po +5 -0
  19. package/locales/da.json +1 -1
  20. package/locales/de/LC_MESSAGES/volto.po +5 -0
  21. package/locales/de.json +1 -1
  22. package/locales/el/LC_MESSAGES/volto.po +5 -0
  23. package/locales/el.json +1 -1
  24. package/locales/en/LC_MESSAGES/volto.po +5 -0
  25. package/locales/en.json +1 -1
  26. package/locales/en_AU/LC_MESSAGES/volto.po +5 -0
  27. package/locales/en_AU.json +1 -1
  28. package/locales/en_GB/LC_MESSAGES/volto.po +5 -0
  29. package/locales/en_GB.json +1 -1
  30. package/locales/eo/LC_MESSAGES/volto.po +5 -0
  31. package/locales/eo.json +1 -1
  32. package/locales/es/LC_MESSAGES/volto.po +18 -13
  33. package/locales/es.json +1 -1
  34. package/locales/et/LC_MESSAGES/volto.po +5 -0
  35. package/locales/et.json +1 -1
  36. package/locales/eu/LC_MESSAGES/volto.po +7 -2
  37. package/locales/eu.json +1 -1
  38. package/locales/fa/LC_MESSAGES/volto.po +5 -0
  39. package/locales/fa.json +1 -1
  40. package/locales/fi/LC_MESSAGES/volto.po +5 -0
  41. package/locales/fi.json +1 -1
  42. package/locales/fr/LC_MESSAGES/volto.po +5 -0
  43. package/locales/fr.json +1 -1
  44. package/locales/fu/LC_MESSAGES/volto.po +5 -0
  45. package/locales/fu.json +1 -1
  46. package/locales/gl/LC_MESSAGES/volto.po +5 -0
  47. package/locales/gl.json +1 -1
  48. package/locales/he/LC_MESSAGES/volto.po +5 -0
  49. package/locales/he.json +1 -1
  50. package/locales/hi/LC_MESSAGES/volto.po +5 -0
  51. package/locales/hi.json +1 -1
  52. package/locales/hr/LC_MESSAGES/volto.po +5 -0
  53. package/locales/hr.json +1 -1
  54. package/locales/hu/LC_MESSAGES/volto.po +5 -0
  55. package/locales/hu.json +1 -1
  56. package/locales/hy/LC_MESSAGES/volto.po +5 -0
  57. package/locales/hy.json +1 -1
  58. package/locales/id/LC_MESSAGES/volto.po +5 -0
  59. package/locales/id.json +1 -1
  60. package/locales/it/LC_MESSAGES/volto.po +5 -0
  61. package/locales/it.json +1 -1
  62. package/locales/ja/LC_MESSAGES/volto.po +49 -44
  63. package/locales/ja.json +1 -1
  64. package/locales/ka/LC_MESSAGES/volto.po +5 -0
  65. package/locales/ka.json +1 -1
  66. package/locales/kn/LC_MESSAGES/volto.po +5 -0
  67. package/locales/kn.json +1 -1
  68. package/locales/ko/LC_MESSAGES/volto.po +5 -0
  69. package/locales/ko.json +1 -1
  70. package/locales/lt/LC_MESSAGES/volto.po +5 -0
  71. package/locales/lt.json +1 -1
  72. package/locales/lv/LC_MESSAGES/volto.po +5 -0
  73. package/locales/lv.json +1 -1
  74. package/locales/mi/LC_MESSAGES/volto.po +5 -0
  75. package/locales/mi.json +1 -1
  76. package/locales/mk/LC_MESSAGES/volto.po +5 -0
  77. package/locales/mk.json +1 -1
  78. package/locales/my/LC_MESSAGES/volto.po +5 -0
  79. package/locales/my.json +1 -1
  80. package/locales/nb_NO/LC_MESSAGES/volto.po +5 -0
  81. package/locales/nb_NO.json +1 -1
  82. package/locales/nl/LC_MESSAGES/volto.po +7 -2
  83. package/locales/nl.json +1 -1
  84. package/locales/nn/LC_MESSAGES/volto.po +5 -0
  85. package/locales/nn.json +1 -1
  86. package/locales/pl/LC_MESSAGES/volto.po +5 -0
  87. package/locales/pl.json +1 -1
  88. package/locales/pt/LC_MESSAGES/volto.po +5 -0
  89. package/locales/pt.json +1 -1
  90. package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
  91. package/locales/pt_BR.json +1 -1
  92. package/locales/rm/LC_MESSAGES/volto.po +5 -0
  93. package/locales/rm.json +1 -1
  94. package/locales/ro/LC_MESSAGES/volto.po +5 -0
  95. package/locales/ro.json +1 -1
  96. package/locales/ru/LC_MESSAGES/volto.po +5 -0
  97. package/locales/ru.json +1 -1
  98. package/locales/sk/LC_MESSAGES/volto.po +5 -0
  99. package/locales/sk.json +1 -1
  100. package/locales/sl/LC_MESSAGES/volto.po +5 -0
  101. package/locales/sl.json +1 -1
  102. package/locales/sm/LC_MESSAGES/volto.po +5 -0
  103. package/locales/sm.json +1 -1
  104. package/locales/sq/LC_MESSAGES/volto.po +5 -0
  105. package/locales/sq.json +1 -1
  106. package/locales/sr/LC_MESSAGES/volto.po +5 -0
  107. package/locales/sr.json +1 -1
  108. package/locales/sr@cyrl/LC_MESSAGES/volto.po +5 -0
  109. package/locales/sr@cyrl.json +1 -1
  110. package/locales/sr@latn/LC_MESSAGES/volto.po +5 -0
  111. package/locales/sr@latn.json +1 -1
  112. package/locales/sv/LC_MESSAGES/volto.po +5 -0
  113. package/locales/sv.json +1 -1
  114. package/locales/ta/LC_MESSAGES/volto.po +58 -52
  115. package/locales/ta.json +1 -1
  116. package/locales/te/LC_MESSAGES/volto.po +5 -0
  117. package/locales/te.json +1 -1
  118. package/locales/th/LC_MESSAGES/volto.po +5 -0
  119. package/locales/th.json +1 -1
  120. package/locales/to/LC_MESSAGES/volto.po +5 -0
  121. package/locales/to.json +1 -1
  122. package/locales/tr/LC_MESSAGES/volto.po +5 -0
  123. package/locales/tr.json +1 -1
  124. package/locales/uk/LC_MESSAGES/volto.po +5 -0
  125. package/locales/uk.json +1 -1
  126. package/locales/vi/LC_MESSAGES/volto.po +5 -0
  127. package/locales/vi.json +1 -1
  128. package/locales/volto.pot +6 -1
  129. package/locales/zh_CN/LC_MESSAGES/volto.po +5 -0
  130. package/locales/zh_CN.json +1 -1
  131. package/locales/zh_Hant/LC_MESSAGES/volto.po +5 -0
  132. package/locales/zh_Hant.json +1 -1
  133. package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +5 -0
  134. package/locales/zh_Hant_HK.json +1 -1
  135. package/news/7428.feat +1 -0
  136. package/package.json +14 -12
  137. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +3 -2
  138. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +575 -630
  139. package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +4 -3
  140. package/src/components/manage/Form/InlineForm.jsx +2 -2
  141. package/src/components/manage/Multilingual/CreateTranslation.jsx +8 -8
  142. package/src/components/manage/Widgets/ImageWidget.jsx +11 -4
  143. package/src/components/theme/LanguageSelector/{LanguageSelector.jsx → LanguageSelector.tsx} +36 -27
  144. package/src/components/theme/View/View.jsx +1 -1
  145. package/src/helpers/Content/Content.js +23 -0
  146. package/src/helpers/Content/Content.test.js +39 -0
  147. package/src/helpers/MessageLabels/MessageLabels.js +5 -0
  148. package/src/hooks/user/useUser.js +1 -1
  149. package/src/reducers/content/content.js +3 -18
  150. package/src/reducers/diff/diff.js +5 -1
  151. package/src/reducers/diff/diff.test.js +60 -4
  152. package/theme/themes/pastanaga/extras/main.less +4 -0
  153. package/tsconfig.json +3 -4
  154. package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +6 -2
  155. package/types/components/manage/Controlpanels/index.d.ts +1 -1
  156. package/types/components/theme/LanguageSelector/LanguageSelector.d.ts +3 -10
  157. package/types/helpers/Content/Content.d.ts +7 -0
  158. package/types/helpers/MessageLabels/MessageLabels.d.ts +68 -62
  159. package/types/reducers/index.d.ts +1 -0
  160. /package/src/components/theme/LanguageSelector/{LanguageSelector.test.jsx → LanguageSelector.test.tsx} +0 -0
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { render, act } from '@testing-library/react';
3
3
  import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
+ import { MemoryRouter } from 'react-router-dom';
5
6
  import jwt from 'jsonwebtoken';
6
7
 
7
8
  import UsersControlpanel from './UsersControlpanel';
@@ -41,10 +42,10 @@ describe('UsersControlpanel', () => {
41
42
  const { container } = await act(async () => {
42
43
  return render(
43
44
  <Provider store={store}>
44
- <>
45
- <UsersControlpanel location={{ pathname: '/blog' }} />
45
+ <MemoryRouter initialEntries={['/controlpanel/users']}>
46
+ <UsersControlpanel />
46
47
  <div id="toolbar"></div>
47
- </>
48
+ </MemoryRouter>
48
49
  </Provider>,
49
50
  );
50
51
  });
@@ -158,7 +158,7 @@ const InlineForm = (props) => {
158
158
  onChangeField(id, value, itemInfo);
159
159
  }}
160
160
  key={field}
161
- error={errors?.[block]?.[field] || {}}
161
+ error={errors?.[block]?.[field] || []}
162
162
  block={block}
163
163
  />
164
164
  ))}
@@ -199,7 +199,7 @@ const InlineForm = (props) => {
199
199
  onChangeField(id, value);
200
200
  }}
201
201
  key={field}
202
- error={errors?.[block]?.[field] || {}}
202
+ error={errors?.[block]?.[field] || []}
203
203
  block={block}
204
204
  />
205
205
  ))}
@@ -12,9 +12,12 @@ const CreateTranslation = (props) => {
12
12
  const dispatch = useDispatch();
13
13
  const { language, translationOf } = props.location.state;
14
14
  const [translationLocation, setTranslationLocation] = React.useState(null);
15
- const [translationObject, setTranslationObject] = React.useState(null);
16
15
  const languageFrom = useSelector((state) => state.intl.locale);
17
16
 
17
+ const translationObject = useSelector(
18
+ (state) => state.content.subrequests['translationObject'],
19
+ );
20
+
18
21
  React.useEffect(() => {
19
22
  // Only on mount, we dispatch the locator query
20
23
  dispatch(getTranslationLocator(translationOf, language)).then((resp) => {
@@ -22,11 +25,7 @@ const CreateTranslation = (props) => {
22
25
  });
23
26
 
24
27
  //and we load the translationObject
25
- dispatch(getContent(translationOf, null, 'translationObject')).then(
26
- (resp) => {
27
- setTranslationObject(resp);
28
- },
29
- );
28
+ dispatch(getContent(translationOf, null, 'translationObject'));
30
29
 
31
30
  // On unmount we dispatch the language change
32
31
  return () => {
@@ -46,7 +45,8 @@ const CreateTranslation = (props) => {
46
45
 
47
46
  return (
48
47
  translationLocation &&
49
- translationObject && (
48
+ translationObject.data &&
49
+ translationObject.loaded > 0 && (
50
50
  <Redirect
51
51
  to={{
52
52
  pathname: `${flattenToAppURL(translationLocation)}/add`,
@@ -54,7 +54,7 @@ const CreateTranslation = (props) => {
54
54
  state: {
55
55
  translationOf: props.location.state.translationOf,
56
56
  language: props.location.state.language,
57
- translationObject: translationObject,
57
+ translationObject: translationObject.data,
58
58
  languageFrom,
59
59
  },
60
60
  }}
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import { Button, Dimmer, Loader, Message } from 'semantic-ui-react';
3
3
  import { useIntl, defineMessages } from 'react-intl';
4
- import { useDispatch } from 'react-redux';
4
+ import { useDispatch, useSelector } from 'react-redux';
5
5
  import { useLocation } from 'react-router-dom';
6
6
  import loadable from '@loadable/component';
7
7
  import { connect } from 'react-redux';
@@ -14,6 +14,7 @@ import config from '@plone/volto/registry';
14
14
  import {
15
15
  flattenToAppURL,
16
16
  getBaseUrl,
17
+ getParentUrl,
17
18
  isInternalURL,
18
19
  normalizeUrl,
19
20
  removeProtocol,
@@ -109,6 +110,9 @@ const UnconnectedImageInput = (props) => {
109
110
  const linkEditor = useLinkEditor();
110
111
  const location = useLocation();
111
112
  const dispatch = useDispatch();
113
+ const isFolderish = useSelector(
114
+ (state) => state?.content?.data?.is_folderish,
115
+ );
112
116
  const contextUrl = location.pathname;
113
117
 
114
118
  const [uploading, setUploading] = React.useState(false);
@@ -156,6 +160,8 @@ const UnconnectedImageInput = (props) => {
156
160
 
157
161
  const handleUpload = React.useCallback(
158
162
  (eventOrFile) => {
163
+ let uploadUrl = getBaseUrl(contextUrl);
164
+ if (!isFolderish) uploadUrl = getParentUrl(uploadUrl);
159
165
  if (restrictFileUpload === true) return;
160
166
  eventOrFile.target && eventOrFile.stopPropagation();
161
167
 
@@ -171,7 +177,7 @@ const UnconnectedImageInput = (props) => {
171
177
  const fields = fileData.match(/^data:(.*);(.*),(.*)$/);
172
178
  dispatch(
173
179
  createContent(
174
- getBaseUrl(contextUrl),
180
+ uploadUrl,
175
181
  {
176
182
  '@type': 'Image',
177
183
  title: file.name,
@@ -188,11 +194,12 @@ const UnconnectedImageInput = (props) => {
188
194
  });
189
195
  },
190
196
  [
197
+ contextUrl,
198
+ isFolderish,
191
199
  restrictFileUpload,
192
200
  intl.formatMessage,
193
201
  dispatch,
194
- props,
195
- contextUrl,
202
+ props.block,
196
203
  requestId,
197
204
  ],
198
205
  );
@@ -1,15 +1,7 @@
1
- /**
2
- * Language selector component.
3
- * @module components/LanguageSelector/LanguageSelector
4
- */
5
-
6
- import React from 'react';
7
- import PropTypes from 'prop-types';
8
1
  import { Link } from 'react-router-dom';
9
2
 
10
3
  import { useSelector } from 'react-redux';
11
4
  import cx from 'classnames';
12
- import find from 'lodash/find';
13
5
  import map from 'lodash/map';
14
6
 
15
7
  import Helmet from '@plone/volto/helpers/Helmet/Helmet';
@@ -19,7 +11,12 @@ import { toReactIntlLang } from '@plone/volto/helpers/Utils/Utils';
19
11
 
20
12
  import config from '@plone/volto/registry';
21
13
 
22
- import { defineMessages, useIntl } from 'react-intl';
14
+ import { defineMessages, useIntl, type IntlShape } from 'react-intl';
15
+ import type {
16
+ Content,
17
+ GetSiteResponse,
18
+ GetTranslationResponse,
19
+ } from '@plone/types';
23
20
 
24
21
  const messages = defineMessages({
25
22
  switchLanguageTo: {
@@ -28,33 +25,53 @@ const messages = defineMessages({
28
25
  },
29
26
  });
30
27
 
31
- const LanguageSelector = (props) => {
28
+ type FormState = {
29
+ content: {
30
+ data: Content;
31
+ };
32
+ intl: IntlShape;
33
+ site: {
34
+ data: GetSiteResponse;
35
+ };
36
+ };
37
+
38
+ const LanguageSelector = ({
39
+ onClickAction = () => {},
40
+ }: {
41
+ onClickAction?: () => void;
42
+ }) => {
32
43
  const intl = useIntl();
33
- const currentLang = useSelector((state) => state.intl.locale);
34
- const translations = useSelector(
35
- (state) => state.content.data?.['@components']?.translations?.items,
44
+ const currentLang = useSelector<FormState, IntlShape['locale']>(
45
+ (state) => state.intl.locale,
36
46
  );
47
+ const translations = useSelector<
48
+ FormState,
49
+ GetTranslationResponse['items'] | undefined
50
+ >((state) => state.content.data?.['@components']?.translations?.items);
37
51
 
38
52
  const { settings } = config;
39
53
 
40
54
  return settings.isMultilingual ? (
41
55
  <div className="language-selector">
42
- {map(settings.supportedLanguages, (lang) => {
43
- const translation = find(translations, { language: lang });
56
+ {map(settings.supportedLanguages || [], (lang) => {
57
+ const langKey = lang as keyof typeof langmap;
58
+ const translation = translations?.find(
59
+ (t: { language: string }) => t.language === lang,
60
+ );
44
61
  return (
45
62
  <Link
46
63
  aria-label={`${intl.formatMessage(
47
64
  messages.switchLanguageTo,
48
- )} ${langmap[lang].nativeName.toLowerCase()}`}
65
+ )} ${langmap[langKey].nativeName.toLowerCase()}`}
49
66
  className={cx({ selected: toReactIntlLang(lang) === currentLang })}
50
67
  to={translation ? flattenToAppURL(translation['@id']) : `/${lang}`}
51
- title={langmap[lang].nativeName}
68
+ title={langmap[langKey].nativeName}
52
69
  onClick={() => {
53
- props.onClickAction();
70
+ onClickAction();
54
71
  }}
55
72
  key={`language-selector-${lang}`}
56
73
  >
57
- {langmap[lang].nativeName}
74
+ {langmap[langKey].nativeName}
58
75
  </Link>
59
76
  );
60
77
  })}
@@ -66,12 +83,4 @@ const LanguageSelector = (props) => {
66
83
  );
67
84
  };
68
85
 
69
- LanguageSelector.propTypes = {
70
- onClickAction: PropTypes.func,
71
- };
72
-
73
- LanguageSelector.defaultProps = {
74
- onClickAction: () => {},
75
- };
76
-
77
86
  export default LanguageSelector;
@@ -246,7 +246,7 @@ class View extends Component {
246
246
  />
247
247
  <SlotRenderer name="aboveContent" content={this.props.content} />
248
248
  <RenderedView
249
- key={this.props.content['@id']}
249
+ key={flattenToAppURL(this.props.content['@id'])}
250
250
  content={this.props.content}
251
251
  location={this.props.location}
252
252
  token={this.props.token}
@@ -11,6 +11,7 @@ import keys from 'lodash/keys';
11
11
  import endsWith from 'lodash/endsWith';
12
12
  import find from 'lodash/find';
13
13
  import config from '@plone/volto/registry';
14
+ import omit from 'lodash/omit';
14
15
 
15
16
  /**
16
17
  * Nest content.
@@ -85,3 +86,25 @@ export function getLanguageIndependentFields(schema) {
85
86
  properties[field]['multilingual_options']?.['language_independent'],
86
87
  );
87
88
  }
89
+
90
+ /**
91
+ * Flattens static behaviors into the parent object with dot-notation keys.
92
+ * @function flattenStaticBehaviors
93
+ * @param {Object} result The result object containing static behaviors.
94
+ * @returns {Object} Result object with flattened static behaviors.
95
+ */
96
+ export function flattenStaticBehaviors(result) {
97
+ if (!result['@static_behaviors']) {
98
+ return result;
99
+ }
100
+
101
+ let flattened = Object.assign({}, result);
102
+ map(result['@static_behaviors'], (behavior) => {
103
+ flattened = {
104
+ ...omit(flattened, behavior),
105
+ ...mapKeys(flattened[behavior], (value, key) => `${behavior}.${key}`),
106
+ };
107
+ });
108
+
109
+ return flattened;
110
+ }
@@ -2,6 +2,7 @@ import {
2
2
  nestContent,
3
3
  getContentIcon,
4
4
  getLanguageIndependentFields,
5
+ flattenStaticBehaviors,
5
6
  } from './Content';
6
7
  import contentExistingSVG from '@plone/volto/icons/content-existing.svg';
7
8
  import linkSVG from '@plone/volto/icons/link.svg';
@@ -96,4 +97,42 @@ describe('Content', () => {
96
97
  expect(getLanguageIndependentFields(schema)).toStrictEqual(['lif']);
97
98
  });
98
99
  });
100
+
101
+ describe('flattenStaticBehaviors', () => {
102
+ it('returns object unchanged when no @static_behaviors', () => {
103
+ const input = {
104
+ title: 'Example',
105
+ creator: 'admin',
106
+ };
107
+ expect(flattenStaticBehaviors(input)).toEqual(input);
108
+ });
109
+
110
+ it('flattens static behaviors into dot-notation keys', () => {
111
+ const input = {
112
+ title: 'Example',
113
+ '@static_behaviors': [
114
+ 'guillotina_cms.interfaces.blocks.IBlocks',
115
+ 'guillotina_cms.interfaces.dublin_core.IDublinCore',
116
+ ],
117
+ 'guillotina_cms.interfaces.blocks.IBlocks': {
118
+ blocks: 'blocks',
119
+ blocks_layout: 'blocks_layout',
120
+ },
121
+ 'guillotina_cms.interfaces.dublin_core.IDublinCore': {
122
+ creator: 'creator',
123
+ },
124
+ };
125
+ expect(flattenStaticBehaviors(input)).toEqual({
126
+ title: 'Example',
127
+ '@static_behaviors': [
128
+ 'guillotina_cms.interfaces.blocks.IBlocks',
129
+ 'guillotina_cms.interfaces.dublin_core.IDublinCore',
130
+ ],
131
+ 'guillotina_cms.interfaces.blocks.IBlocks.blocks': 'blocks',
132
+ 'guillotina_cms.interfaces.blocks.IBlocks.blocks_layout':
133
+ 'blocks_layout',
134
+ 'guillotina_cms.interfaces.dublin_core.IDublinCore.creator': 'creator',
135
+ });
136
+ });
137
+ });
99
138
  });
@@ -152,6 +152,11 @@ export const messages = defineMessages({
152
152
  id: 'Groupname',
153
153
  defaultMessage: 'Groupname',
154
154
  },
155
+ addGroupsFormGroupNameDescription: {
156
+ id: 'A unique identifier for the group. Cannot be changed after creation. No spaces allowed.',
157
+ defaultMessage:
158
+ 'A unique identifier for the group. Cannot be changed after creation. No spaces allowed.',
159
+ },
155
160
  addGroupsFormDescriptionTitle: {
156
161
  id: 'Description',
157
162
  defaultMessage: 'Description',
@@ -12,7 +12,7 @@ const useUser = () => {
12
12
  const dispatch = useDispatch();
13
13
 
14
14
  useEffect(() => {
15
- if (!user?.id && users?.get.loading === false) {
15
+ if (userId && !user?.id && users?.get.loading === false) {
16
16
  dispatch(getUser(userId));
17
17
  }
18
18
  }, [dispatch, userId, user, users?.get.loading]);
@@ -3,11 +3,10 @@
3
3
  * @module reducers/content/content
4
4
  */
5
5
 
6
- import map from 'lodash/map';
7
- import mapKeys from 'lodash/mapKeys';
8
6
  import omit from 'lodash/omit';
9
7
 
10
8
  import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
9
+ import { flattenStaticBehaviors } from '@plone/volto/helpers/Content/Content';
11
10
 
12
11
  import {
13
12
  CREATE_CONTENT,
@@ -131,14 +130,7 @@ export default function content(state = initialState, action = {}) {
131
130
  },
132
131
  };
133
132
  case `${CREATE_CONTENT}_SUCCESS`:
134
- if (result['@static_behaviors']) {
135
- map(result['@static_behaviors'], (behavior) => {
136
- result = {
137
- ...omit(result, behavior),
138
- ...mapKeys(result[behavior], (value, key) => `${behavior}.${key}`),
139
- };
140
- });
141
- }
133
+ result = flattenStaticBehaviors(result);
142
134
  const data = action.subrequest
143
135
  ? Array.isArray(result)
144
136
  ? result.map((item) => ({
@@ -188,14 +180,7 @@ export default function content(state = initialState, action = {}) {
188
180
  },
189
181
  };
190
182
  case `${GET_CONTENT}_SUCCESS`:
191
- if (result['@static_behaviors']) {
192
- map(result['@static_behaviors'], (behavior) => {
193
- result = {
194
- ...omit(result, behavior),
195
- ...mapKeys(result[behavior], (value, key) => `${behavior}.${key}`),
196
- };
197
- });
198
- }
183
+ result = flattenStaticBehaviors(result);
199
184
 
200
185
  const transforms = config.getUtilities({
201
186
  type: 'transform',
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { GET_DIFF } from '@plone/volto/constants/ActionTypes';
7
+ import { flattenStaticBehaviors } from '@plone/volto/helpers/Content/Content';
7
8
 
8
9
  const initialState = {
9
10
  error: null,
@@ -29,10 +30,13 @@ export default function diff(state = initialState, action = {}) {
29
30
  loading: true,
30
31
  };
31
32
  case `${GET_DIFF}_SUCCESS`:
33
+ const first_result = flattenStaticBehaviors(action.result[0]);
34
+ const second_result = flattenStaticBehaviors(action.result[1]);
35
+
32
36
  return {
33
37
  ...state,
34
38
  error: null,
35
- data: action.result,
39
+ data: [first_result, second_result],
36
40
  loaded: true,
37
41
  loading: false,
38
42
  };
@@ -1,5 +1,5 @@
1
- import diff from './diff';
2
1
  import { GET_DIFF } from '@plone/volto/constants/ActionTypes';
2
+ import diff from './diff';
3
3
 
4
4
  describe('Diff reducer', () => {
5
5
  it('should return the initial state', () => {
@@ -24,15 +24,71 @@ describe('Diff reducer', () => {
24
24
  });
25
25
  });
26
26
 
27
- it('should handle GET_DIFF_SUCCESS', () => {
27
+ it('should handle GET_DIFF_SUCCESS with array result with static behaviors', () => {
28
+ expect(
29
+ diff(undefined, {
30
+ type: `${GET_DIFF}_SUCCESS`,
31
+ result: [
32
+ {
33
+ '@static_behaviors': [
34
+ 'guillotina.behaviors.dublincore.IDublinCore',
35
+ ],
36
+ 'guillotina.behaviors.dublincore.IDublinCore': {
37
+ title: 'test page',
38
+ },
39
+ },
40
+ {
41
+ '@static_behaviors': [
42
+ 'guillotina.behaviors.dublincore.IDublinCore',
43
+ ],
44
+ 'guillotina.behaviors.dublincore.IDublinCore': {
45
+ title: 'test second page',
46
+ },
47
+ },
48
+ ],
49
+ }),
50
+ ).toEqual({
51
+ error: null,
52
+ data: [
53
+ {
54
+ '@static_behaviors': ['guillotina.behaviors.dublincore.IDublinCore'],
55
+
56
+ 'guillotina.behaviors.dublincore.IDublinCore.title': 'test page',
57
+ },
58
+ {
59
+ '@static_behaviors': ['guillotina.behaviors.dublincore.IDublinCore'],
60
+ 'guillotina.behaviors.dublincore.IDublinCore.title':
61
+ 'test second page',
62
+ },
63
+ ],
64
+ loaded: true,
65
+ loading: false,
66
+ });
67
+ });
68
+
69
+ it('should handle GET_DIFF_SUCCESS with array result without static behaviors', () => {
70
+ const data = [
71
+ {
72
+ title: 'test page',
73
+ 'guillotina.behaviors.dublincore.IDublinCore': {
74
+ title: 'test page',
75
+ },
76
+ },
77
+ {
78
+ title: 'test second page',
79
+ 'guillotina.behaviors.dublincore.IDublinCore': {
80
+ title: 'test second page',
81
+ },
82
+ },
83
+ ];
28
84
  expect(
29
85
  diff(undefined, {
30
86
  type: `${GET_DIFF}_SUCCESS`,
31
- result: 'result',
87
+ result: data,
32
88
  }),
33
89
  ).toEqual({
34
90
  error: null,
35
- data: 'result',
91
+ data: data,
36
92
  loaded: true,
37
93
  loading: false,
38
94
  });
@@ -440,6 +440,10 @@ fieldset.invisible {
440
440
  padding: 0;
441
441
  border: none;
442
442
  margin: 0;
443
+ // Reset visibility, this 'invisible' is not meant to hide the fieldset
444
+ // This prevents issues when used along other CSS framworks that might
445
+ // have this utility class defined (e.g. TailwindCSS)
446
+ visibility: initial;
443
447
  }
444
448
 
445
449
  .vertical-form {
package/tsconfig.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ESNext",
4
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
3
+ "target": "es2022",
4
+ "lib": ["es2022", "dom", "dom.iterable"],
5
5
  "types": ["vitest/globals", "jest"],
6
- "module": "commonjs",
6
+ "module": "preserve",
7
7
  "allowJs": true,
8
8
  "skipLibCheck": true,
9
9
  "esModuleInterop": true,
@@ -11,7 +11,6 @@
11
11
  "strict": true,
12
12
  "forceConsistentCasingInFileNames": true,
13
13
  "strictPropertyInitialization": false,
14
- "moduleResolution": "Node",
15
14
  "resolveJsonModule": true,
16
15
  "isolatedModules": true,
17
16
  "noEmit": true,
@@ -1,2 +1,6 @@
1
- declare const _default: any;
2
- export default _default;
1
+ export default UsersControlpanel;
2
+ /**
3
+ * UsersControlpanel functional component.
4
+ * @function UsersControlpanel
5
+ */
6
+ declare function UsersControlpanel(): import("react/jsx-runtime").JSX.Element;
@@ -4,7 +4,7 @@ export declare const RulesControlpanel: import("@loadable/component").LoadableCl
4
4
  export declare const AddRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
5
5
  export declare const EditRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
6
6
  export declare const ConfigureRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
7
- export declare const UsersControlpanel: import("@loadable/component").LoadableClassComponent<any>;
7
+ export declare const UsersControlpanel: import("@loadable/component").LoadableComponent<unknown>;
8
8
  export declare const RenderUsers: import("@loadable/component").LoadableComponent<any>;
9
9
  export declare const UserGroupMembershipControlPanel: import("@loadable/component").LoadableComponent<unknown>;
10
10
  export declare const GroupsControlpanel: import("@loadable/component").LoadableClassComponent<any>;
@@ -1,11 +1,4 @@
1
+ declare const LanguageSelector: ({ onClickAction, }: {
2
+ onClickAction?: () => void;
3
+ }) => import("react/jsx-runtime").JSX.Element;
1
4
  export default LanguageSelector;
2
- declare function LanguageSelector(props: any): import("react/jsx-runtime").JSX.Element;
3
- declare namespace LanguageSelector {
4
- namespace propTypes {
5
- let onClickAction: any;
6
- }
7
- namespace defaultProps {
8
- export function onClickAction_1(): void;
9
- export { onClickAction_1 as onClickAction };
10
- }
11
- }
@@ -29,3 +29,10 @@ export function getContentIcon(type: string, isFolderish: boolean): any;
29
29
  * @returns {array} List of language independent fields
30
30
  */
31
31
  export function getLanguageIndependentFields(schema: string): any[];
32
+ /**
33
+ * Flattens static behaviors into the parent object with dot-notation keys.
34
+ * @function flattenStaticBehaviors
35
+ * @param {Object} result The result object containing static behaviors.
36
+ * @returns {Object} Result object with flattened static behaviors.
37
+ */
38
+ export function flattenStaticBehaviors(result: any): any;