@plone/volto 14.3.0 → 14.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/addon-registry.js +15 -2
  3. package/locales/ca/LC_MESSAGES/volto.po +5 -0
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +5 -0
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +5 -0
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +5 -0
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +5 -0
  12. package/locales/eu.json +1 -1
  13. package/locales/fr/LC_MESSAGES/volto.po +5 -0
  14. package/locales/fr.json +1 -1
  15. package/locales/it/LC_MESSAGES/volto.po +10 -5
  16. package/locales/it.json +1 -1
  17. package/locales/ja/LC_MESSAGES/volto.po +5 -0
  18. package/locales/ja.json +1 -1
  19. package/locales/nl/LC_MESSAGES/volto.po +5 -0
  20. package/locales/nl.json +1 -1
  21. package/locales/pt/LC_MESSAGES/volto.po +5 -0
  22. package/locales/pt.json +1 -1
  23. package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
  24. package/locales/pt_BR.json +1 -1
  25. package/locales/ro/LC_MESSAGES/volto.po +5 -0
  26. package/locales/ro.json +1 -1
  27. package/locales/volto.pot +6 -1
  28. package/package.json +4 -2
  29. package/razzle.config.js +1 -1
  30. package/src/components/index.js +2 -0
  31. package/src/components/manage/Add/Add.jsx +14 -3
  32. package/src/components/manage/Blocks/Block/BlocksForm.test.jsx +6 -0
  33. package/src/components/manage/Blocks/Listing/ListingBody.jsx +6 -0
  34. package/src/components/manage/Contents/Contents.jsx +15 -6
  35. package/src/components/manage/Contents/Contents.test.jsx +0 -7
  36. package/src/components/manage/Contents/ContentsIndexHeader.jsx +46 -34
  37. package/src/components/manage/Contents/ContentsItem.jsx +69 -61
  38. package/src/components/manage/Contents/ContentsUploadModal.jsx +2 -4
  39. package/src/components/manage/Controlpanels/ModerateComments.jsx +7 -5
  40. package/src/components/manage/Diff/Diff.jsx +13 -5
  41. package/src/components/manage/Diff/Diff.test.jsx +0 -6
  42. package/src/components/manage/Diff/DiffField.jsx +12 -3
  43. package/src/components/manage/Diff/DiffField.test.jsx +0 -6
  44. package/src/components/manage/DragDropList/DragDropList.jsx +4 -2
  45. package/src/components/manage/Form/Field.jsx +12 -2
  46. package/src/components/manage/History/History.jsx +6 -5
  47. package/src/components/manage/History/History.test.jsx +12 -7
  48. package/src/components/manage/Widgets/DatetimeWidget.jsx +10 -3
  49. package/src/components/manage/Widgets/DatetimeWidget.test.jsx +2 -2
  50. package/src/components/manage/Widgets/FormFieldWrapper.jsx +14 -1
  51. package/src/components/manage/Widgets/ObjectListWidget.stories.js +3 -3
  52. package/src/components/manage/Widgets/ObjectListWidget.test.js +6 -0
  53. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +4 -3
  54. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +10 -3
  55. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +8 -4
  56. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +21 -13
  57. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.test.jsx +6 -0
  58. package/src/components/manage/Widgets/RecurrenceWidget/Utils.js +1 -2
  59. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +5 -3
  60. package/src/components/manage/Widgets/SchemaWidget.jsx +4 -2
  61. package/src/components/manage/Widgets/SchemaWidget.test.jsx +6 -0
  62. package/src/components/manage/Widgets/SchemaWidgetFieldset.jsx +8 -4
  63. package/src/components/manage/Widgets/SchemaWidgetFieldset.test.jsx +7 -1
  64. package/src/components/manage/Widgets/VocabularyTermsWidget.jsx +41 -7
  65. package/src/components/manage/Widgets/VocabularyTermsWidget.stories.js +6 -21
  66. package/src/components/manage/Widgets/VocabularyTermsWidget.test.jsx +6 -0
  67. package/src/components/theme/Comments/Comments.jsx +3 -1
  68. package/src/components/theme/Comments/Comments.test.jsx +6 -0
  69. package/src/components/theme/FormattedDate/FormattedDate.jsx +42 -0
  70. package/src/components/theme/FormattedDate/FormattedDate.stories.jsx +91 -0
  71. package/src/components/theme/FormattedDate/FormattedRelativeDate.jsx +57 -0
  72. package/src/components/theme/FormattedDate/FormattedRelativeDate.stories.jsx +122 -0
  73. package/src/components/theme/Navigation/Navigation.jsx +1 -1
  74. package/src/components/theme/View/EventDatesInfo.jsx +12 -6
  75. package/src/components/theme/View/EventDatesInfo.test.jsx +6 -0
  76. package/src/components/theme/View/EventView.test.jsx +6 -0
  77. package/src/config/Loadables.jsx +3 -0
  78. package/src/config/index.js +1 -0
  79. package/src/helpers/Content/Content.js +16 -0
  80. package/src/helpers/Content/Content.test.js +20 -1
  81. package/src/helpers/FormValidation/FormValidation.js +1 -1
  82. package/src/helpers/Utils/Date.js +97 -0
  83. package/src/helpers/Utils/Date.test.js +197 -0
  84. package/src/helpers/Utils/Utils.js +1 -2
  85. package/src/helpers/Utils/Utils.test.js +25 -0
  86. package/src/helpers/index.js +1 -0
  87. package/theme/themes/pastanaga/extras/widgets.less +12 -0
  88. package/volto.config.js +3 -3
@@ -8,10 +8,8 @@ import { Button, Dropdown, Table } from 'semantic-ui-react';
8
8
  import { Link } from 'react-router-dom';
9
9
  import PropTypes from 'prop-types';
10
10
  import { map } from 'lodash';
11
- import moment from 'moment';
12
- import { DragSource, DropTarget } from 'react-dnd';
13
11
  import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
14
- import { Icon, Circle } from '@plone/volto/components';
12
+ import { Circle, FormattedDate, Icon } from '@plone/volto/components';
15
13
  import { getContentIcon } from '@plone/volto/helpers';
16
14
  import moreSVG from '@plone/volto/icons/more.svg';
17
15
  import checkboxUncheckedSVG from '@plone/volto/icons/checkbox-unchecked.svg';
@@ -26,6 +24,8 @@ import editingSVG from '@plone/volto/icons/editing.svg';
26
24
  import dragSVG from '@plone/volto/icons/drag.svg';
27
25
  import cx from 'classnames';
28
26
 
27
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
28
+
29
29
  const messages = defineMessages({
30
30
  private: {
31
31
  id: 'private',
@@ -47,6 +47,10 @@ const messages = defineMessages({
47
47
  id: 'no workflow state',
48
48
  defaultMessage: 'No workflow state',
49
49
  },
50
+ none: {
51
+ id: 'None',
52
+ defaultMessage: 'None',
53
+ },
50
54
  });
51
55
 
52
56
  function getColor(string) {
@@ -186,21 +190,13 @@ export const ContentsItemComponent = ({
186
190
  </div>
187
191
  )}
188
192
  {index.type === 'date' && (
189
- <span
190
- title={
191
- item[index.id] !== 'None' ? (
192
- moment(item[index.id]).format('LLLL')
193
- ) : (
194
- <FormattedMessage id="None" defaultMessage="None" />
195
- )
196
- }
197
- >
193
+ <>
198
194
  {item[index.id] !== 'None' ? (
199
- moment(item[index.id]).format('L')
195
+ <FormattedDate date={item[index.id]} />
200
196
  ) : (
201
- <FormattedMessage id="None" defaultMessage="None" />
197
+ intl.formatMessage(messages.none)
202
198
  )}
203
- </span>
199
+ </>
204
200
  )}
205
201
  {index.type === 'array' && (
206
202
  <span>{item[index.id]?.join(', ')}</span>
@@ -313,55 +309,67 @@ ContentsItemComponent.propTypes = {
313
309
  onOrderItem: PropTypes.func.isRequired,
314
310
  };
315
311
 
316
- export default DropTarget(
317
- 'item',
318
- {
319
- hover(props, monitor) {
320
- const id = monitor.getItem().id;
321
- const dragOrder = monitor.getItem().order;
322
- const hoverOrder = props.order;
312
+ const DragDropConnector = (props) => {
313
+ const { DropTarget, DragSource } = props.reactDnd;
323
314
 
324
- if (dragOrder === hoverOrder) {
325
- return;
326
- }
315
+ const DndConnectedContentsItem = React.useMemo(
316
+ () =>
317
+ DropTarget(
318
+ 'item',
319
+ {
320
+ hover(props, monitor) {
321
+ const id = monitor.getItem().id;
322
+ const dragOrder = monitor.getItem().order;
323
+ const hoverOrder = props.order;
327
324
 
328
- props.onOrderItem(id, dragOrder, hoverOrder - dragOrder, false);
325
+ if (dragOrder === hoverOrder) {
326
+ return;
327
+ }
329
328
 
330
- monitor.getItem().order = hoverOrder;
331
- },
332
- drop(props, monitor) {
333
- const id = monitor.getItem().id;
334
- const dragOrder = monitor.getItem().startOrder;
335
- const dropOrder = props.order;
329
+ props.onOrderItem(id, dragOrder, hoverOrder - dragOrder, false);
336
330
 
337
- if (dragOrder === dropOrder) {
338
- return;
339
- }
331
+ monitor.getItem().order = hoverOrder;
332
+ },
333
+ drop(props, monitor) {
334
+ const id = monitor.getItem().id;
335
+ const dragOrder = monitor.getItem().startOrder;
336
+ const dropOrder = props.order;
340
337
 
341
- props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true);
338
+ if (dragOrder === dropOrder) {
339
+ return;
340
+ }
342
341
 
343
- monitor.getItem().order = dropOrder;
344
- },
345
- },
346
- (connect) => ({
347
- connectDropTarget: connect.dropTarget(),
348
- }),
349
- )(
350
- DragSource(
351
- 'item',
352
- {
353
- beginDrag(props) {
354
- return {
355
- id: props.item['@id'],
356
- order: props.order,
357
- startOrder: props.order,
358
- };
359
- },
360
- },
361
- (connect, monitor) => ({
362
- connectDragSource: connect.dragSource(),
363
- connectDragPreview: connect.dragPreview(),
364
- isDragging: monitor.isDragging(),
365
- }),
366
- )(ContentsItemComponent),
367
- );
342
+ props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true);
343
+
344
+ monitor.getItem().order = dropOrder;
345
+ },
346
+ },
347
+ (connect) => ({
348
+ connectDropTarget: connect.dropTarget(),
349
+ }),
350
+ )(
351
+ DragSource(
352
+ 'item',
353
+ {
354
+ beginDrag(props) {
355
+ return {
356
+ id: props.item['@id'],
357
+ order: props.order,
358
+ startOrder: props.order,
359
+ };
360
+ },
361
+ },
362
+ (connect, monitor) => ({
363
+ connectDragSource: connect.dragSource(),
364
+ connectDragPreview: connect.dragPreview(),
365
+ isDragging: monitor.isDragging(),
366
+ }),
367
+ )(ContentsItemComponent),
368
+ ),
369
+ [DragSource, DropTarget],
370
+ );
371
+
372
+ return <DndConnectedContentsItem {...props} />;
373
+ };
374
+
375
+ export default injectLazyLibs('reactDnd')(DragDropConnector);
@@ -20,10 +20,10 @@ import {
20
20
  } from 'semantic-ui-react';
21
21
  import loadable from '@loadable/component';
22
22
  import { concat, filter, map } from 'lodash';
23
- import moment from 'moment';
24
23
  import filesize from 'filesize';
25
24
  import { readAsDataURL } from 'promise-file-reader';
26
25
  import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
26
+ import { FormattedRelativeDate } from '@plone/volto/components';
27
27
  import { createContent } from '@plone/volto/actions';
28
28
 
29
29
  const Dropzone = loadable(() => import('react-dropzone'));
@@ -180,8 +180,6 @@ class ContentsUploadModal extends Component {
180
180
  * @returns {string} Markup for the component.
181
181
  */
182
182
  render() {
183
- moment.locale(this.props.intl.locale);
184
-
185
183
  return (
186
184
  this.props.open && (
187
185
  <Modal open={this.props.open}>
@@ -269,7 +267,7 @@ class ContentsUploadModal extends Component {
269
267
  <Table.Row className="upload-row" key={file.name}>
270
268
  <Table.Cell>{file.name}</Table.Cell>
271
269
  <Table.Cell>
272
- {moment(file.lastModifiedDate).fromNow()}
270
+ <FormattedRelativeDate date={file.lastModifiedDate} />
273
271
  </Table.Cell>
274
272
  <Table.Cell>
275
273
  {filesize(file.size, { round: 0 })}
@@ -11,11 +11,15 @@ import { Link } from 'react-router-dom';
11
11
  import { getParentUrl, Helmet } from '@plone/volto/helpers';
12
12
  import { Portal } from 'react-portal';
13
13
  import { Container, Button, Table } from 'semantic-ui-react';
14
- import moment from 'moment';
15
14
  import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
16
15
 
17
16
  import { deleteComment, searchContent } from '@plone/volto/actions';
18
- import { CommentEditModal, Icon, Toolbar } from '@plone/volto/components';
17
+ import {
18
+ CommentEditModal,
19
+ FormattedRelativeDate,
20
+ Icon,
21
+ Toolbar,
22
+ } from '@plone/volto/components';
19
23
 
20
24
  import backSVG from '@plone/volto/icons/back.svg';
21
25
 
@@ -230,9 +234,7 @@ class ModerateComments extends Component {
230
234
  <Table.Row key={item['@id']}>
231
235
  <Table.Cell>{item.author_name}</Table.Cell>
232
236
  <Table.Cell>
233
- <span title={moment(item.creation_date).format('LLLL')}>
234
- {moment(item.creation_date).fromNow()}
235
- </span>
237
+ <FormattedRelativeDate date={item.creation_date} />
236
238
  </Table.Cell>
237
239
  <Table.Cell>{item.text.data}</Table.Cell>
238
240
  <Table.Cell>
@@ -12,7 +12,6 @@ import { filter, isEqual, map } from 'lodash';
12
12
  import { Container, Button, Dropdown, Grid, Table } from 'semantic-ui-react';
13
13
  import { Link, withRouter } from 'react-router-dom';
14
14
  import { Portal } from 'react-portal';
15
- import moment from 'moment';
16
15
  import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
17
16
  import qs from 'query-string';
18
17
 
@@ -23,7 +22,12 @@ import {
23
22
  getBlocksLayoutFieldname,
24
23
  hasBlocksData,
25
24
  } from '@plone/volto/helpers';
26
- import { DiffField, Icon, Toolbar } from '@plone/volto/components';
25
+ import {
26
+ DiffField,
27
+ FormattedDate,
28
+ Icon,
29
+ Toolbar,
30
+ } from '@plone/volto/components';
27
31
 
28
32
  import backSVG from '@plone/volto/icons/back.svg';
29
33
 
@@ -189,9 +193,13 @@ class Diff extends Component {
189
193
  const versions = map(
190
194
  filter(this.props.historyEntries, (entry) => 'version' in entry),
191
195
  (entry, index) => ({
192
- text: `${index === 0 ? 'Current' : entry.version} (${moment(
193
- entry.time,
194
- ).format('LLLL')}, ${entry.actor.fullname})`,
196
+ text: (
197
+ <>
198
+ {index === 0 ? 'Current' : entry.version}&nbsp;(
199
+ <FormattedDate date={entry.time} long className="text" />, &nbsp;
200
+ {entry.actor.fullname})
201
+ </>
202
+ ),
195
203
  value: `${entry.version}`,
196
204
  key: `${entry.version}`,
197
205
  }),
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- // import renderer from 'react-test-renderer';
3
2
  import configureStore from 'redux-mock-store';
4
3
  import { Provider } from 'react-intl-redux';
5
4
  import { MemoryRouter } from 'react-router-dom';
@@ -12,11 +11,6 @@ const mockStore = configureStore();
12
11
  jest.mock('react-portal', () => ({
13
12
  Portal: jest.fn(() => <div id="Portal" />),
14
13
  }));
15
- jest.mock('moment', () =>
16
- jest.fn(() => ({
17
- format: jest.fn(() => 'Sunday, April 23, 2017 3:38 AM'),
18
- })),
19
- );
20
14
 
21
15
  jest.mock('@plone/volto/helpers/Loadable/Loadable');
22
16
  beforeAll(
@@ -8,11 +8,11 @@ import React from 'react';
8
8
  import { join, map } from 'lodash';
9
9
  import PropTypes from 'prop-types';
10
10
  import { Table } from 'semantic-ui-react';
11
- import moment from 'moment';
12
11
  import ReactDOMServer from 'react-dom/server';
13
12
  import { Provider } from 'react-intl-redux';
14
13
  import { createBrowserHistory } from 'history';
15
14
  import { ConnectedRouter } from 'connected-react-router';
15
+ import { useSelector } from 'react-redux';
16
16
 
17
17
  import { Api } from '@plone/volto/helpers';
18
18
  import configureStore from '@plone/volto/store';
@@ -44,6 +44,11 @@ const DiffField = ({
44
44
  schema,
45
45
  diffLib,
46
46
  }) => {
47
+ const language = useSelector((state) => state.intl.locale);
48
+ const readable_date_format = {
49
+ dateStyle: 'full',
50
+ timeStyle: 'short',
51
+ };
47
52
  const diffWords = (oneStr, twoStr) => {
48
53
  return diffLib.diffWords(String(oneStr), String(twoStr));
49
54
  };
@@ -56,8 +61,12 @@ const DiffField = ({
56
61
  break;
57
62
  case 'datetime':
58
63
  parts = diffWords(
59
- moment(one).format('LLLL'),
60
- moment(two).format('LLLL'),
64
+ new Intl.DateTimeFormat(language, readable_date_format).format(
65
+ new Date(one),
66
+ ),
67
+ new Intl.DateTimeFormat(language, readable_date_format).format(
68
+ new Date(two),
69
+ ),
61
70
  );
62
71
  break;
63
72
  case 'json':
@@ -5,12 +5,6 @@ import { waitFor, render, screen } from '@testing-library/react';
5
5
 
6
6
  import DiffField from './DiffField';
7
7
 
8
- jest.mock('moment', () =>
9
- jest.fn(() => ({
10
- format: jest.fn(() => 'Sunday, April 23, 2017 3:38 AM'),
11
- })),
12
- );
13
-
14
8
  jest.mock('@plone/volto/helpers/Loadable/Loadable');
15
9
  beforeAll(
16
10
  async () =>
@@ -1,6 +1,6 @@
1
1
  import React, { useRef } from 'react';
2
2
  import { isEmpty } from 'lodash';
3
- import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
3
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
4
4
  import { v4 as uuid } from 'uuid';
5
5
 
6
6
  const getPlaceholder = (draggedDOM, sourceIndex, destinationIndex) => {
@@ -55,7 +55,9 @@ const DragDropList = (props) => {
55
55
  as = 'div',
56
56
  style,
57
57
  forwardedAriaLabelledBy,
58
+ reactBeautifulDnd,
58
59
  } = props; //renderChild
60
+ const { DragDropContext, Draggable, Droppable } = reactBeautifulDnd;
59
61
  const [placeholderProps, setPlaceholderProps] = React.useState({});
60
62
  const [uid] = React.useState(uuid());
61
63
  // queueing timed action
@@ -157,4 +159,4 @@ const DragDropList = (props) => {
157
159
  );
158
160
  };
159
161
 
160
- export default DragDropList;
162
+ export default injectLazyLibs(['reactBeautifulDnd'])(DragDropList);
@@ -5,9 +5,9 @@
5
5
 
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { DragSource, DropTarget } from 'react-dnd';
9
8
  import { injectIntl } from 'react-intl';
10
9
  import config from '@plone/volto/registry';
10
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
11
11
 
12
12
  const MODE_HIDDEN = 'hidden'; //hidden mode. If mode is hidden, field is not rendered
13
13
  /**
@@ -150,7 +150,7 @@ const getWidgetByType = (type) => config.widgets.type[type] || null;
150
150
  * @param {Object} props Properties.
151
151
  * @returns {string} Markup of the component.
152
152
  */
153
- const Field = (props, { intl }) => {
153
+ const UnconnectedField = (props, { intl }) => {
154
154
  const Widget =
155
155
  getWidgetByFieldId(props.id) ||
156
156
  getWidgetFromTaggedValues(props.widgetOptions) ||
@@ -173,6 +173,7 @@ const Field = (props, { intl }) => {
173
173
  };
174
174
 
175
175
  if (props.onOrder) {
176
+ const { DropTarget, DragSource } = props.reactDnd;
176
177
  const WrappedWidget = DropTarget(
177
178
  'field',
178
179
  {
@@ -230,6 +231,15 @@ const Field = (props, { intl }) => {
230
231
  return <Widget {...widgetProps} />;
231
232
  };
232
233
 
234
+ const DndConnectedField = injectLazyLibs(['reactDnd'])(UnconnectedField);
235
+
236
+ const Field = (props) =>
237
+ props.onOrder ? (
238
+ <DndConnectedField {...props} />
239
+ ) : (
240
+ <UnconnectedField {...props} />
241
+ );
242
+
233
243
  /**
234
244
  * Property types.
235
245
  * @property {Object} propTypes Property types.
@@ -12,10 +12,13 @@ import { compose } from 'redux';
12
12
  import { Container, Dropdown, Icon, Segment, Table } from 'semantic-ui-react';
13
13
  import { concat, map, reverse } from 'lodash';
14
14
  import { Portal } from 'react-portal';
15
- import moment from 'moment';
16
15
  import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
17
16
 
18
- import { Icon as IconNext, Toolbar } from '@plone/volto/components';
17
+ import {
18
+ FormattedRelativeDate,
19
+ Icon as IconNext,
20
+ Toolbar,
21
+ } from '@plone/volto/components';
19
22
  import { getHistory, revertHistory } from '@plone/volto/actions';
20
23
  import { getBaseUrl } from '@plone/volto/helpers';
21
24
 
@@ -206,9 +209,7 @@ class History extends Component {
206
209
  </Table.Cell>
207
210
  <Table.Cell>{entry.actor.fullname}</Table.Cell>
208
211
  <Table.Cell>
209
- <span title={moment(entry.time).format('LLLL')}>
210
- {moment(entry.time).fromNow()}
211
- </span>
212
+ <FormattedRelativeDate date={entry.time} />
212
213
  </Table.Cell>
213
214
  <Table.Cell>{entry.comments}</Table.Cell>
214
215
  <Table.Cell>
@@ -3,21 +3,26 @@ import renderer from 'react-test-renderer';
3
3
  import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
5
 
6
+ import FakeTimers from '@sinonjs/fake-timers';
7
+
6
8
  import History from './History';
7
9
 
8
10
  const mockStore = configureStore();
9
-
10
11
  jest.mock('react-portal', () => ({
11
12
  Portal: jest.fn(() => <div id="Portal" />),
12
13
  }));
13
- jest.mock('moment', () =>
14
- jest.fn(() => ({
15
- format: jest.fn(() => 'Sunday, April 23, 2017 3:38 AM'),
16
- fromNow: jest.fn(() => 'a few seconds ago'),
17
- })),
18
- );
14
+
15
+ const FIXED_SYSTEM_TIME = '2017-04-23T15:38:00.000Z';
19
16
 
20
17
  describe('History', () => {
18
+ let clock;
19
+ beforeEach(() => {
20
+ clock = FakeTimers.install({ now: Date.parse(FIXED_SYSTEM_TIME) });
21
+ });
22
+ afterEach(() => {
23
+ clock.uninstall();
24
+ });
25
+
21
26
  it('renders a history component', () => {
22
27
  const store = mockStore({
23
28
  history: {
@@ -93,7 +93,7 @@ export class DatetimeWidgetComponent extends Component {
93
93
  */
94
94
  constructor(props) {
95
95
  super(props);
96
- const moment = props.moment.default;
96
+ this.moment = props.moment.default;
97
97
 
98
98
  this.state = {
99
99
  focused: false,
@@ -102,12 +102,19 @@ export class DatetimeWidgetComponent extends Component {
102
102
  parseDateTime(
103
103
  this.props.intl.locale,
104
104
  this.props.value,
105
- )?.toISOString() === moment().utc().toISOString(),
105
+ undefined,
106
+ this.moment,
107
+ )?.toISOString() === this.moment().utc().toISOString(),
106
108
  };
107
109
  }
108
110
 
109
111
  getInternalValue() {
110
- return parseDateTime(this.props.intl.locale, this.props.value);
112
+ return parseDateTime(
113
+ this.props.intl.locale,
114
+ this.props.value,
115
+ undefined,
116
+ this.moment,
117
+ );
111
118
  }
112
119
 
113
120
  getDateOnly() {
@@ -1,4 +1,3 @@
1
- import moment from 'moment';
2
1
  import React from 'react';
3
2
  import { Provider } from 'react-intl-redux';
4
3
  import configureStore from 'redux-mock-store';
@@ -20,6 +19,7 @@ test('renders a datetime widget component', async () => {
20
19
  messages: {},
21
20
  },
22
21
  });
22
+ const isoDate = new Date('2019-10-21').toISOString();
23
23
  const { container } = render(
24
24
  <Provider store={store}>
25
25
  <DatetimeWidget
@@ -27,7 +27,7 @@ test('renders a datetime widget component', async () => {
27
27
  title="My field"
28
28
  fieldSet="default"
29
29
  onChange={() => {}}
30
- value={moment('2019-10-21').toISOString()}
30
+ value={isoDate}
31
31
  />
32
32
  </Provider>,
33
33
  );
@@ -18,6 +18,10 @@ const messages = defineMessages({
18
18
  id: 'Delete',
19
19
  defaultMessage: 'Delete',
20
20
  },
21
+ language_independent: {
22
+ id: 'Language independent field.',
23
+ defaultMessage: 'Language independent field.',
24
+ },
21
25
  });
22
26
  /**
23
27
  * FormFieldWrapper component class.
@@ -85,6 +89,7 @@ class FormFieldWrapper extends Component {
85
89
  onDelete,
86
90
  intl,
87
91
  noForInFieldLabel,
92
+ multilingual_options,
88
93
  } = this.props;
89
94
  const wdg = (
90
95
  <>
@@ -107,6 +112,9 @@ class FormFieldWrapper extends Component {
107
112
  description ? 'help' : '',
108
113
  className,
109
114
  `field-wrapper-${id}`,
115
+ multilingual_options?.language_independent
116
+ ? 'language-independent-field'
117
+ : null,
110
118
  )}
111
119
  >
112
120
  <Grid>
@@ -160,7 +168,12 @@ class FormFieldWrapper extends Component {
160
168
  {description && (
161
169
  <Grid.Row stretched>
162
170
  <Grid.Column stretched width="12">
163
- <p className="help">{description}</p>
171
+ <p className="help">
172
+ {this.props.multilingual_options
173
+ ? `${intl.formatMessage(messages.language_independent)} `
174
+ : null}
175
+ {description}
176
+ </p>
164
177
  </Grid.Column>
165
178
  </Grid.Row>
166
179
  )}
@@ -1,5 +1,5 @@
1
1
  import ObjectListWidgetDefault from './ObjectListWidget';
2
- import Wrapper, { FormUndoWrapper } from '@plone/volto/storybook';
2
+ import { RealStoreWrapper, FormUndoWrapper } from '@plone/volto/storybook';
3
3
  import React from 'react';
4
4
  import { searchResults } from './ObjectBrowserWidget.stories';
5
5
 
@@ -109,7 +109,7 @@ const ObjectListWidgetComponent = ({
109
109
  ...args
110
110
  }) => {
111
111
  return (
112
- <Wrapper
112
+ <RealStoreWrapper
113
113
  location={{ pathname: '/folder2/folder21/doc212' }}
114
114
  customStore={customStore}
115
115
  >
@@ -157,7 +157,7 @@ const ObjectListWidgetComponent = ({
157
157
  </div>
158
158
  )}
159
159
  </FormUndoWrapper>
160
- </Wrapper>
160
+ </RealStoreWrapper>
161
161
  );
162
162
  };
163
163
 
@@ -5,6 +5,12 @@ import configureStore from 'redux-mock-store';
5
5
  import '@testing-library/jest-dom/extend-expect';
6
6
  import ObjectListWidget from './ObjectListWidget';
7
7
 
8
+ jest.mock('@plone/volto/helpers/Loadable/Loadable');
9
+ beforeAll(
10
+ async () =>
11
+ await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
12
+ );
13
+
8
14
  let mockSerial = 0;
9
15
  const mockStore = configureStore();
10
16
 
@@ -6,17 +6,18 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { Form, Grid, Button } from 'semantic-ui-react';
9
- import moment from 'moment';
10
9
  import { Days } from './Utils';
11
10
  import { useIntl } from 'react-intl';
11
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
12
12
 
13
13
  /**
14
14
  * ByDayField component class.
15
15
  * @function ByDayField
16
16
  * @returns {string} Markup of the component.
17
17
  */
18
- const ByDayField = ({ label, value, onChange }) => {
18
+ const ByDayField = ({ label, value, onChange, moment: momentlib }) => {
19
19
  const intl = useIntl();
20
+ const moment = momentlib.default;
20
21
  moment.locale(intl.locale);
21
22
 
22
23
  const toggleWeekDay = (dayName) => {
@@ -84,4 +85,4 @@ ByDayField.defaultProps = {
84
85
  onChange: null,
85
86
  };
86
87
 
87
- export default ByDayField;
88
+ export default injectLazyLibs(['moment'])(ByDayField);
@@ -6,17 +6,24 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { map } from 'lodash';
9
- import moment from 'moment';
10
9
  import { useIntl } from 'react-intl';
11
10
  import { Form } from 'semantic-ui-react';
12
11
  import SelectInput from './SelectInput';
12
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
13
13
 
14
14
  /**
15
15
  * MonthOfTheYearField component class.
16
16
  * @function MonthOfTheYearField
17
17
  * @returns {string} Markup of the component.
18
18
  */
19
- const MonthOfTheYearField = ({ value, disabled, inline, onChange }) => {
19
+ const MonthOfTheYearField = ({
20
+ value,
21
+ disabled,
22
+ inline,
23
+ onChange,
24
+ moment: momentlib,
25
+ }) => {
26
+ const moment = momentlib.default;
20
27
  const intl = useIntl();
21
28
  moment.locale(intl.locale);
22
29
  const monthList = [
@@ -63,4 +70,4 @@ MonthOfTheYearField.defaultProps = {
63
70
  onChange: null,
64
71
  };
65
72
 
66
- export default MonthOfTheYearField;
73
+ export default injectLazyLibs(['moment'])(MonthOfTheYearField);