@plone/volto 17.0.0-alpha.24 → 17.0.0-alpha.26

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 (130) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +92 -4
  3. package/CONTRIBUTING.md +5 -1
  4. package/README.md +9 -7
  5. package/cypress/support/commands.js +12 -9
  6. package/cypress.config.js +1 -0
  7. package/locales/ca/LC_MESSAGES/volto.po +41 -15
  8. package/locales/ca.json +1 -1
  9. package/locales/de/LC_MESSAGES/volto.po +41 -15
  10. package/locales/de.json +1 -1
  11. package/locales/en/LC_MESSAGES/volto.po +40 -14
  12. package/locales/en.json +1 -1
  13. package/locales/es/LC_MESSAGES/volto.po +69 -43
  14. package/locales/es.json +1 -1
  15. package/locales/eu/LC_MESSAGES/volto.po +40 -14
  16. package/locales/eu.json +1 -1
  17. package/locales/fi/LC_MESSAGES/volto.po +40 -14
  18. package/locales/fi.json +1 -1
  19. package/locales/fr/LC_MESSAGES/volto.po +41 -15
  20. package/locales/fr.json +1 -1
  21. package/locales/it/LC_MESSAGES/volto.po +40 -14
  22. package/locales/it.json +1 -1
  23. package/locales/ja/LC_MESSAGES/volto.po +40 -14
  24. package/locales/ja.json +1 -1
  25. package/locales/nl/LC_MESSAGES/volto.po +41 -15
  26. package/locales/nl.json +1 -1
  27. package/locales/pt/LC_MESSAGES/volto.po +41 -15
  28. package/locales/pt.json +1 -1
  29. package/locales/pt_BR/LC_MESSAGES/volto.po +40 -14
  30. package/locales/pt_BR.json +1 -1
  31. package/locales/ro/LC_MESSAGES/volto.po +41 -15
  32. package/locales/ro.json +1 -1
  33. package/locales/volto.pot +41 -15
  34. package/locales/zh_CN/LC_MESSAGES/volto.po +41 -15
  35. package/locales/zh_CN.json +1 -1
  36. package/package.json +4 -4
  37. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
  38. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
  39. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
  40. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +6 -0
  41. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
  42. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +10 -0
  43. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +30 -0
  44. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +10 -0
  45. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
  46. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +6 -0
  47. package/packages/volto-slate/package.json +1 -1
  48. package/packages/volto-slate/src/editor/render.jsx +2 -3
  49. package/src/actions/index.js +4 -0
  50. package/src/actions/navroot/navroot.js +16 -0
  51. package/src/actions/navroot/navroot.test.js +15 -0
  52. package/src/actions/relations/relations.js +17 -0
  53. package/src/actions/site/site.js +16 -0
  54. package/src/actions/site/site.test.js +15 -0
  55. package/src/actions/userSession/userSession.js +17 -1
  56. package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
  57. package/src/components/manage/Blocks/Block/Settings.test.jsx +90 -0
  58. package/src/components/manage/Blocks/Image/schema.js +5 -1
  59. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -11
  60. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +42 -25
  61. package/src/components/manage/Blocks/ToC/View.jsx +75 -13
  62. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +4 -13
  63. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
  64. package/src/components/manage/Contents/Contents.jsx +27 -0
  65. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
  66. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +11 -9
  67. package/src/components/manage/Controlpanels/Relations/Relations.jsx +3 -3
  68. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
  69. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +15 -9
  70. package/src/components/manage/Controlpanels/Rules/AddRule.jsx +1 -1
  71. package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
  72. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +95 -5
  73. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +127 -99
  74. package/src/components/manage/Diff/DiffField.jsx +25 -1
  75. package/src/components/manage/Form/BlockDataForm.jsx +3 -2
  76. package/src/components/manage/Form/BlockDataForm.test.jsx +34 -2
  77. package/src/components/manage/LinksToItem/LinksToItem.jsx +1 -1
  78. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +5 -2
  79. package/src/components/manage/Messages/Messages.jsx +32 -99
  80. package/src/components/manage/Messages/Messages.test.jsx +0 -1
  81. package/src/components/manage/Sharing/Sharing.jsx +50 -21
  82. package/src/components/manage/UniversalLink/UniversalLink.jsx +4 -6
  83. package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
  84. package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
  85. package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
  86. package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
  87. package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
  88. package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
  89. package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
  90. package/src/components/theme/Comments/Comment.stories.jsx +84 -0
  91. package/src/components/theme/Comments/Comments.jsx +273 -378
  92. package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +37 -3
  93. package/src/components/theme/Login/Login.jsx +159 -241
  94. package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
  95. package/src/components/theme/Logo/Logo.jsx +35 -29
  96. package/src/components/theme/Logo/Logo.test.jsx +135 -1
  97. package/src/components/theme/Logout/Logout.jsx +36 -83
  98. package/src/components/theme/Navigation/Navigation.jsx +86 -171
  99. package/src/components/theme/Search/SearchTags.jsx +30 -60
  100. package/src/components/theme/SearchWidget/SearchWidget.jsx +15 -3
  101. package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
  102. package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
  103. package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
  104. package/src/components/theme/View/View.jsx +2 -0
  105. package/src/config/ControlPanels.js +0 -1
  106. package/src/config/Widgets.jsx +2 -0
  107. package/src/config/index.js +15 -3
  108. package/src/constants/ActionTypes.js +4 -0
  109. package/src/express-middleware/images.js +1 -0
  110. package/src/helpers/MessageLabels/MessageLabels.js +26 -4
  111. package/src/helpers/Site/index.js +21 -0
  112. package/src/helpers/index.js +1 -0
  113. package/src/reducers/index.js +4 -0
  114. package/src/reducers/navroot/navroot.js +79 -0
  115. package/src/reducers/navroot/navroot.test.js +110 -0
  116. package/src/reducers/relations/relations.js +74 -46
  117. package/src/reducers/site/site.js +51 -0
  118. package/src/reducers/site/site.test.js +67 -0
  119. package/src/reducers/userSession/userSession.js +15 -1
  120. package/src/server.jsx +9 -0
  121. package/test-setup-config.js +1 -0
  122. package/theme/themes/pastanaga/collections/form.overrides +46 -0
  123. package/theme/themes/pastanaga/elements/input.overrides +10 -0
  124. package/theme/themes/pastanaga/elements/label.overrides +10 -0
  125. package/theme/themes/pastanaga/extras/login.less +3 -0
  126. package/webpack-plugins/webpack-less-plugin.js +19 -0
  127. package/.gitignore~ +0 -71
  128. package/news/4547.breaking~ +0 -1
  129. package/package.json~ +0 -444
  130. package/src/config/index.js~ +0 -223
@@ -11,6 +11,7 @@ import {
11
11
  getControlpanel,
12
12
  updateUser,
13
13
  updateGroup,
14
+ getUserSchema,
14
15
  } from '@plone/volto/actions';
15
16
  import {
16
17
  Icon,
@@ -98,6 +99,7 @@ class UsersControlpanel extends Component {
98
99
  this.updateUserRole = this.updateUserRole.bind(this);
99
100
  this.state = {
100
101
  search: '',
102
+ isLoading: false,
101
103
  showAddUser: false,
102
104
  showAddUserErrorConfirm: false,
103
105
  addUserError: '',
@@ -107,6 +109,7 @@ class UsersControlpanel extends Component {
107
109
  isClient: false,
108
110
  currentPage: 0,
109
111
  pageSize: 10,
112
+ loginUsingEmail: false,
110
113
  };
111
114
  }
112
115
 
@@ -120,6 +123,15 @@ class UsersControlpanel extends Component {
120
123
  entries: this.props.users,
121
124
  });
122
125
  }
126
+ await this.props.getUserSchema();
127
+ };
128
+
129
+ // Because username field needs to be disabled if email login is enabled!
130
+ checkLoginUsingEmailStatus = async () => {
131
+ await this.props.getControlpanel('security');
132
+ this.setState({
133
+ loginUsingEmail: this.props.controlPanelData?.data.use_email_as_login,
134
+ });
123
135
  };
124
136
 
125
137
  /**
@@ -132,6 +144,7 @@ class UsersControlpanel extends Component {
132
144
  isClient: true,
133
145
  });
134
146
  this.fetchData();
147
+ this.checkLoginUsingEmailStatus();
135
148
  }
136
149
 
137
150
  UNSAFE_componentWillReceiveProps(nextProps) {
@@ -171,9 +184,19 @@ class UsersControlpanel extends Component {
171
184
  */
172
185
  onSearch(event) {
173
186
  event.preventDefault();
174
- this.props.listUsers({
175
- search: this.state.search,
176
- });
187
+ this.setState({ isLoading: true });
188
+ this.props
189
+ .listUsers({
190
+ search: this.state.search,
191
+ })
192
+ .then(() => {
193
+ this.setState({ isLoading: false });
194
+ })
195
+ .catch((error) => {
196
+ this.setState({ isLoading: false });
197
+ // eslint-disable-next-line no-console
198
+ console.error('Error searching users', error);
199
+ });
177
200
  }
178
201
 
179
202
  /**
@@ -255,12 +278,28 @@ class UsersControlpanel extends Component {
255
278
  * @returns {undefined}
256
279
  */
257
280
  onAddUserSubmit(data, callback) {
258
- const { groups, sendPasswordReset } = data;
259
- if (groups && groups.length > 0) this.addUserToGroup(data);
260
- this.props.createUser(data, sendPasswordReset);
261
- this.setState({
262
- addUserSetFormDataCallback: callback,
263
- });
281
+ const { groups, sendPasswordReset, password } = data;
282
+ if (
283
+ sendPasswordReset !== undefined &&
284
+ sendPasswordReset === true &&
285
+ password !== undefined
286
+ ) {
287
+ toast.error(
288
+ <Toast
289
+ error
290
+ title={this.props.intl.formatMessage(messages.error)}
291
+ content={this.props.intl.formatMessage(
292
+ messages.addUserFormPasswordAndSendPasswordTogetherNotAllowed,
293
+ )}
294
+ />,
295
+ );
296
+ } else {
297
+ if (groups && groups.length > 0) this.addUserToGroup(data);
298
+ this.props.createUser(data, sendPasswordReset);
299
+ this.setState({
300
+ addUserSetFormDataCallback: callback,
301
+ });
302
+ }
264
303
  }
265
304
 
266
305
  /**
@@ -382,6 +421,65 @@ class UsersControlpanel extends Component {
382
421
  let usernameToDelete = this.state.userToDelete
383
422
  ? this.state.userToDelete.username
384
423
  : '';
424
+ // Copy the userschema using JSON serialization/deserialization
425
+ // this is really ugly, but if we don't do this the original value
426
+ // of the userschema is changed and it is used like that through
427
+ // the lifecycle of the application
428
+ let adduserschema = {};
429
+ if (this.props?.userschema?.loaded) {
430
+ adduserschema = JSON.parse(
431
+ JSON.stringify(this.props?.userschema?.userschema),
432
+ );
433
+ adduserschema.properties['username'] = {
434
+ title: this.props.intl.formatMessage(messages.addUserFormUsernameTitle),
435
+ type: 'string',
436
+ description: this.props.intl.formatMessage(
437
+ messages.addUserFormUsernameDescription,
438
+ ),
439
+ };
440
+ adduserschema.properties['password'] = {
441
+ title: this.props.intl.formatMessage(messages.addUserFormPasswordTitle),
442
+ type: 'password',
443
+ description: this.props.intl.formatMessage(
444
+ messages.addUserFormPasswordDescription,
445
+ ),
446
+ widget: 'password',
447
+ };
448
+ adduserschema.properties['sendPasswordReset'] = {
449
+ title: this.props.intl.formatMessage(
450
+ messages.addUserFormSendPasswordResetTitle,
451
+ ),
452
+ type: 'boolean',
453
+ };
454
+ adduserschema.properties['roles'] = {
455
+ title: this.props.intl.formatMessage(messages.addUserFormRolesTitle),
456
+ type: 'array',
457
+ choices: this.props.roles.map((role) => [role.id, role.title]),
458
+ noValueOption: false,
459
+ };
460
+ adduserschema.properties['groups'] = {
461
+ title: this.props.intl.formatMessage(messages.addUserGroupNameTitle),
462
+ type: 'array',
463
+ choices: this.props.groups.map((group) => [group.id, group.id]),
464
+ noValueOption: false,
465
+ };
466
+ if (
467
+ adduserschema.fieldsets &&
468
+ adduserschema.fieldsets.length > 0 &&
469
+ !adduserschema.fieldsets[0]['fields'].includes('username')
470
+ ) {
471
+ adduserschema.fieldsets[0]['fields'] = adduserschema.fieldsets[0][
472
+ 'fields'
473
+ ].concat([
474
+ 'username',
475
+ 'password',
476
+ 'sendPasswordReset',
477
+ 'roles',
478
+ 'groups',
479
+ ]);
480
+ }
481
+ }
482
+
385
483
  return (
386
484
  <Container className="users-control-panel">
387
485
  <Helmet title={this.props.intl.formatMessage(messages.users)} />
@@ -408,7 +506,7 @@ class UsersControlpanel extends Component {
408
506
  onConfirm={this.onDeleteOk}
409
507
  size={null}
410
508
  />
411
- {this.state.showAddUser ? (
509
+ {this.props?.userschema?.loaded && this.state.showAddUser ? (
412
510
  <ModalForm
413
511
  open={this.state.showAddUser}
414
512
  className="modal"
@@ -419,92 +517,7 @@ class UsersControlpanel extends Component {
419
517
  }
420
518
  title={this.props.intl.formatMessage(messages.addUserFormTitle)}
421
519
  loading={this.props.createRequest.loading}
422
- schema={{
423
- fieldsets: [
424
- {
425
- id: 'default',
426
- title: 'FIXME: User Data',
427
- fields: [
428
- 'username',
429
- 'fullname',
430
- 'email',
431
- 'password',
432
- 'sendPasswordReset',
433
- 'roles',
434
- 'groups',
435
- ],
436
- },
437
- ],
438
- properties: {
439
- username: {
440
- title: this.props.intl.formatMessage(
441
- messages.addUserFormUsernameTitle,
442
- ),
443
- type: 'string',
444
- description: this.props.intl.formatMessage(
445
- messages.addUserFormUsernameDescription,
446
- ),
447
- },
448
- fullname: {
449
- title: this.props.intl.formatMessage(
450
- messages.addUserFormFullnameTitle,
451
- ),
452
- type: 'string',
453
- description: this.props.intl.formatMessage(
454
- messages.addUserFormFullnameDescription,
455
- ),
456
- },
457
- email: {
458
- title: this.props.intl.formatMessage(
459
- messages.addUserFormEmailTitle,
460
- ),
461
- type: 'string',
462
- description: this.props.intl.formatMessage(
463
- messages.addUserFormEmailDescription,
464
- ),
465
- widget: 'email',
466
- },
467
- password: {
468
- title: this.props.intl.formatMessage(
469
- messages.addUserFormPasswordTitle,
470
- ),
471
- type: 'password',
472
- description: this.props.intl.formatMessage(
473
- messages.addUserFormPasswordDescription,
474
- ),
475
- widget: 'password',
476
- },
477
- sendPasswordReset: {
478
- title: this.props.intl.formatMessage(
479
- messages.addUserFormSendPasswordResetTitle,
480
- ),
481
- type: 'boolean',
482
- },
483
- roles: {
484
- title: this.props.intl.formatMessage(
485
- messages.addUserFormRolesTitle,
486
- ),
487
- type: 'array',
488
- choices: this.props.roles.map((role) => [
489
- role.id,
490
- role.title,
491
- ]),
492
- noValueOption: false,
493
- },
494
- groups: {
495
- title: this.props.intl.formatMessage(
496
- messages.addUserGroupNameTitle,
497
- ),
498
- type: 'array',
499
- choices: this.props.groups.map((group) => [
500
- group.id,
501
- group.id,
502
- ]),
503
- noValueOption: false,
504
- },
505
- },
506
- required: ['username', 'email'],
507
- }}
520
+ schema={adduserschema}
508
521
  />
509
522
  ) : null}
510
523
  </div>
@@ -533,7 +546,11 @@ class UsersControlpanel extends Component {
533
546
  <Form.Field>
534
547
  <Input
535
548
  name="SearchableText"
536
- action={{ icon: 'search' }}
549
+ action={{
550
+ icon: 'search',
551
+ loading: this.state.isLoading,
552
+ disabled: this.state.isLoading,
553
+ }}
537
554
  placeholder={this.props.intl.formatMessage(
538
555
  messages.searchUsers,
539
556
  )}
@@ -544,7 +561,8 @@ class UsersControlpanel extends Component {
544
561
  </Form>
545
562
  </Segment>
546
563
  <Form>
547
- <div className="table">
564
+ {((this.props.many_users && this.state.entries.length > 0) ||
565
+ !this.props.many_users) && (
548
566
  <Table padded striped attached unstackable>
549
567
  <Table.Header>
550
568
  <Table.Row>
@@ -578,11 +596,18 @@ class UsersControlpanel extends Component {
578
596
  user={user}
579
597
  updateUser={this.updateUserRole}
580
598
  inheritedRole={this.props.inheritedRole}
599
+ userschema={this.props.userschema}
600
+ listUsers={this.props.listUsers}
581
601
  />
582
602
  ))}
583
603
  </Table.Body>
584
604
  </Table>
585
- </div>
605
+ )}
606
+ {this.state.entries.length === 0 && this.state.search && (
607
+ <Segment>
608
+ {this.props.intl.formatMessage(messages.userSearchNoResults)}
609
+ </Segment>
610
+ )}
586
611
  <div className="contents-pagination">
587
612
  <Pagination
588
613
  current={this.state.currentPage}
@@ -670,6 +695,8 @@ export default compose(
670
695
  createRequest: state.users.create,
671
696
  loadRolesRequest: state.roles,
672
697
  inheritedRole: state.authRole.authenticatedRole,
698
+ userschema: state.userschema,
699
+ controlPanelData: state.controlpanels?.controlpanel,
673
700
  }),
674
701
  (dispatch) =>
675
702
  bindActionCreators(
@@ -682,6 +709,7 @@ export default compose(
682
709
  createUser,
683
710
  updateUser,
684
711
  updateGroup,
712
+ getUserSchema,
685
713
  },
686
714
  dispatch,
687
715
  ),
@@ -17,6 +17,7 @@ import { useSelector } from 'react-redux';
17
17
  import { Api } from '@plone/volto/helpers';
18
18
  import configureStore from '@plone/volto/store';
19
19
  import { DefaultView } from '@plone/volto/components/';
20
+ import { serializeNodes } from '@plone/volto-slate/editor/render';
20
21
 
21
22
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
22
23
 
@@ -69,7 +70,7 @@ const DiffField = ({
69
70
  .replace('\u202F', ' '),
70
71
  );
71
72
  break;
72
- case 'json':
73
+ case 'json': {
73
74
  const api = new Api();
74
75
  const history = createBrowserHistory();
75
76
  const store = configureStore(window.__data, history, api);
@@ -90,6 +91,29 @@ const DiffField = ({
90
91
  ),
91
92
  );
92
93
  break;
94
+ }
95
+ case 'slate': {
96
+ const api = new Api();
97
+ const history = createBrowserHistory();
98
+ const store = configureStore(window.__data, history, api);
99
+ parts = diffWords(
100
+ ReactDOMServer.renderToStaticMarkup(
101
+ <Provider store={store}>
102
+ <ConnectedRouter history={history}>
103
+ {serializeNodes(one)}
104
+ </ConnectedRouter>
105
+ </Provider>,
106
+ ),
107
+ ReactDOMServer.renderToStaticMarkup(
108
+ <Provider store={store}>
109
+ <ConnectedRouter history={history}>
110
+ {serializeNodes(two)}
111
+ </ConnectedRouter>
112
+ </Provider>,
113
+ ),
114
+ );
115
+ break;
116
+ }
93
117
  case 'textarea':
94
118
  default:
95
119
  parts = diffWords(one, two);
@@ -5,7 +5,7 @@ import { withVariationSchemaEnhancer } from '@plone/volto/helpers';
5
5
  const EnhancedBlockDataForm = withVariationSchemaEnhancer(InlineForm);
6
6
 
7
7
  export default function BlockDataForm(props) {
8
- const { onChangeBlock, block } = props;
8
+ const { onChangeBlock, block, applySchemaEnhancers = true } = props;
9
9
 
10
10
  if (!onChangeBlock) {
11
11
  // eslint-disable-next-line no-console
@@ -19,8 +19,9 @@ export default function BlockDataForm(props) {
19
19
  [block, onChangeBlock],
20
20
  );
21
21
 
22
+ const Form = applySchemaEnhancers ? EnhancedBlockDataForm : InlineForm;
22
23
  return (
23
- <EnhancedBlockDataForm
24
+ <Form
24
25
  {...props}
25
26
  onChangeFormData={onChangeBlock ? onChangeFormData : undefined}
26
27
  />
@@ -72,7 +72,7 @@ beforeAll(() => {
72
72
  });
73
73
 
74
74
  describe('BlockDataForm', () => {
75
- it('should does not add variations to schema when unneeded', () => {
75
+ it('should not add variations to schema when unneeded', () => {
76
76
  const WrappedBlockDataForm = withStateManagement(BlockDataForm);
77
77
  const store = mockStore({
78
78
  intl: {
@@ -103,7 +103,7 @@ describe('BlockDataForm', () => {
103
103
  expect(testSchema.fieldsets[0].fields).toStrictEqual([]);
104
104
  });
105
105
 
106
- it('should does not add variations when only one variation', () => {
106
+ it('should not add variations when only one variation', () => {
107
107
  const WrappedBlockDataForm = withStateManagement(BlockDataForm);
108
108
  const store = mockStore({
109
109
  intl: {
@@ -164,4 +164,36 @@ describe('BlockDataForm', () => {
164
164
  // schema is cloned, not mutated in place
165
165
  expect(testSchema.fieldsets[0].fields).toStrictEqual([]);
166
166
  });
167
+
168
+ it('should not add variations to schema when explicitly disabled', () => {
169
+ const WrappedBlockDataForm = withStateManagement(BlockDataForm);
170
+ const store = mockStore({
171
+ intl: {
172
+ locale: 'en',
173
+ messages: {},
174
+ },
175
+ });
176
+ const testSchema = {
177
+ fieldsets: [{ title: 'Default', id: 'default', fields: [] }],
178
+ properties: {},
179
+ required: [],
180
+ };
181
+ const formData = {
182
+ '@type': 'testBlock',
183
+ };
184
+ const { container } = render(
185
+ <Provider store={store}>
186
+ <WrappedBlockDataForm
187
+ formData={formData}
188
+ schema={testSchema}
189
+ onChangeField={(id, value) => {}}
190
+ applySchemaEnhancers={false}
191
+ />
192
+ </Provider>,
193
+ );
194
+ expect(container).toMatchSnapshot();
195
+
196
+ // schema is cloned, not mutated in place
197
+ expect(testSchema.fieldsets[0].fields).toStrictEqual([]);
198
+ });
167
199
  });
@@ -44,7 +44,7 @@ const LinksToItem = (props) => {
44
44
 
45
45
  const title = useSelector((state) => state.content.data?.title || '');
46
46
  const myrelations = useSelector(
47
- (state) => state.relations.subrequests[itempath]?.relations,
47
+ (state) => state.relations.subrequests[itempath]?.data,
48
48
  );
49
49
  const actions = useSelector((state) => state.actions?.actions ?? {});
50
50
  const ploneSetupAction = find(actions.user, {
@@ -3,6 +3,7 @@ import renderer from 'react-test-renderer';
3
3
  import { Provider } from 'react-intl-redux';
4
4
  import configureMockStore from 'redux-mock-store';
5
5
  import thunk from 'redux-thunk';
6
+ import { MemoryRouter } from 'react-router-dom';
6
7
 
7
8
  import LinksToItem from './LinksToItem';
8
9
 
@@ -20,7 +21,7 @@ describe('LinksToItem', () => {
20
21
  relations: {
21
22
  subrequests: {
22
23
  '/page-1': {
23
- relations: {
24
+ data: {
24
25
  isReferencing: {
25
26
  items: [
26
27
  {
@@ -88,7 +89,9 @@ describe('LinksToItem', () => {
88
89
  });
89
90
  const component = renderer.create(
90
91
  <Provider store={store}>
91
- <LinksToItem location={{ pathname: '/page-1/links-to-item' }} />
92
+ <MemoryRouter>
93
+ <LinksToItem location={{ pathname: '/page-1/links-to-item' }} />
94
+ </MemoryRouter>
92
95
  </Provider>,
93
96
  );
94
97
  const json = component.toJSON();
@@ -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';