@plone/volto 14.6.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 (35) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +4 -2
  3. package/src/components/index.js +2 -0
  4. package/src/components/manage/Contents/Contents.test.jsx +0 -7
  5. package/src/components/manage/Contents/ContentsItem.jsx +9 -14
  6. package/src/components/manage/Contents/ContentsUploadModal.jsx +2 -4
  7. package/src/components/manage/Controlpanels/ModerateComments.jsx +7 -5
  8. package/src/components/manage/Diff/Diff.jsx +13 -5
  9. package/src/components/manage/Diff/Diff.test.jsx +0 -6
  10. package/src/components/manage/Diff/DiffField.jsx +12 -3
  11. package/src/components/manage/Diff/DiffField.test.jsx +0 -6
  12. package/src/components/manage/History/History.jsx +6 -5
  13. package/src/components/manage/History/History.test.jsx +12 -7
  14. package/src/components/manage/Widgets/DatetimeWidget.jsx +10 -3
  15. package/src/components/manage/Widgets/DatetimeWidget.test.jsx +2 -2
  16. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +4 -3
  17. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +10 -3
  18. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +8 -4
  19. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +21 -13
  20. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.test.jsx +6 -0
  21. package/src/components/manage/Widgets/RecurrenceWidget/Utils.js +1 -2
  22. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +5 -3
  23. package/src/components/theme/Comments/Comments.jsx +3 -1
  24. package/src/components/theme/Comments/Comments.test.jsx +6 -0
  25. package/src/components/theme/FormattedDate/FormattedDate.jsx +42 -0
  26. package/src/components/theme/FormattedDate/FormattedDate.stories.jsx +91 -0
  27. package/src/components/theme/FormattedDate/FormattedRelativeDate.jsx +57 -0
  28. package/src/components/theme/FormattedDate/FormattedRelativeDate.stories.jsx +122 -0
  29. package/src/components/theme/View/EventDatesInfo.jsx +12 -6
  30. package/src/components/theme/View/EventDatesInfo.test.jsx +6 -0
  31. package/src/components/theme/View/EventView.test.jsx +6 -0
  32. package/src/helpers/Utils/Date.js +97 -0
  33. package/src/helpers/Utils/Date.test.js +197 -0
  34. package/src/helpers/Utils/Utils.js +1 -2
  35. package/src/helpers/Utils/Utils.test.js +25 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Change Log
2
2
 
3
+ ## 14.7.0 (2022-01-28)
4
+
5
+ ### Feature
6
+
7
+ - Add `<FormattedDate>` and `<FormattedRelativeDate>` components. Check their Storybook stories for details. This is part of ongoing work to minimize the use of 'deprecated' momentjs. @sneridagh @tiberiuichim
8
+
9
+ ### Internal
10
+
11
+ - Upgrade jest to latest release, 27 major. @tiberiuichim
12
+ - Lazyload momentjs. `parseDateTime` helper now requires passing the momentjs library @tiberiuichim
13
+
3
14
  ## 14.6.0 (2022-01-27)
4
15
 
5
16
  ### Feature
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "14.6.0",
12
+ "version": "14.7.0",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -35,7 +35,7 @@
35
35
  "build": "razzle build",
36
36
  "build-spa": "razzle build --type=spa",
37
37
  "test": "razzle test --env=jest-environment-jsdom-sixteen --maxWorkers=50%",
38
- "test:ci": "CI=true razzle test --env=jest-environment-jsdom-sixteen",
38
+ "test:ci": "CI=true NODE_ICU_DATA=node_modules/full-icu razzle test --env=jest-environment-jsdom-sixteen",
39
39
  "test:husky": "CI=true yarn test --bail --findRelatedTests",
40
40
  "test:debug": "node --inspect node_modules/.bin/jest --runInBand",
41
41
  "start:prod": "NODE_ENV=production node build/server.js",
@@ -414,7 +414,9 @@
414
414
  "@storybook/react": "^6.3.0",
415
415
  "babel-loader": "^8.1.0",
416
416
  "crypto-random-string": "3.2.0",
417
+ "full-icu": "1.4.0",
417
418
  "identity-obj-proxy": "3.0.0",
419
+ "jest": "27.4.5",
418
420
  "jest-environment-jsdom-sixteen": "1.0.3",
419
421
  "react-is": "^16.13.1",
420
422
  "tmp": "0.2.1",
@@ -198,5 +198,7 @@ export ToCSettingsSchema from '@plone/volto/components/manage/Blocks/ToC/Schema'
198
198
 
199
199
  export MaybeWrap from '@plone/volto/components/manage/MaybeWrap/MaybeWrap';
200
200
  export ContentMetadataTags from '@plone/volto/components/theme/ContentMetadataTags/ContentMetadataTags';
201
+ export FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
202
+ export FormattedRelativeDate from '@plone/volto/components/theme/FormattedDate/FormattedRelativeDate';
201
203
 
202
204
  export App from '@plone/volto/components/theme/App/App';
@@ -24,13 +24,6 @@ jest.mock('./ContentsUploadModal', () =>
24
24
  jest.fn(() => <div className="UploadModal" />),
25
25
  );
26
26
 
27
- jest.mock('moment', () =>
28
- jest.fn(() => ({
29
- format: jest.fn(() => 'Sunday, April 23, 2017 3:38 AM'),
30
- fromNow: jest.fn(() => 'a few seconds ago'),
31
- })),
32
- );
33
-
34
27
  describe('Contents', () => {
35
28
  it('renders a folder contents view component', () => {
36
29
  const store = mockStore({
@@ -8,9 +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
11
  import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
13
- import { Icon, Circle } from '@plone/volto/components';
12
+ import { Circle, FormattedDate, Icon } from '@plone/volto/components';
14
13
  import { getContentIcon } from '@plone/volto/helpers';
15
14
  import moreSVG from '@plone/volto/icons/more.svg';
16
15
  import checkboxUncheckedSVG from '@plone/volto/icons/checkbox-unchecked.svg';
@@ -48,6 +47,10 @@ const messages = defineMessages({
48
47
  id: 'no workflow state',
49
48
  defaultMessage: 'No workflow state',
50
49
  },
50
+ none: {
51
+ id: 'None',
52
+ defaultMessage: 'None',
53
+ },
51
54
  });
52
55
 
53
56
  function getColor(string) {
@@ -187,21 +190,13 @@ export const ContentsItemComponent = ({
187
190
  </div>
188
191
  )}
189
192
  {index.type === 'date' && (
190
- <span
191
- title={
192
- item[index.id] !== 'None' ? (
193
- moment(item[index.id]).format('LLLL')
194
- ) : (
195
- <FormattedMessage id="None" defaultMessage="None" />
196
- )
197
- }
198
- >
193
+ <>
199
194
  {item[index.id] !== 'None' ? (
200
- moment(item[index.id]).format('L')
195
+ <FormattedDate date={item[index.id]} />
201
196
  ) : (
202
- <FormattedMessage id="None" defaultMessage="None" />
197
+ intl.formatMessage(messages.none)
203
198
  )}
204
- </span>
199
+ </>
205
200
  )}
206
201
  {index.type === 'array' && (
207
202
  <span>{item[index.id]?.join(', ')}</span>
@@ -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 () =>
@@ -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
  );
@@ -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);
@@ -6,12 +6,12 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { defineMessages, injectIntl } from 'react-intl';
9
- import moment from 'moment';
10
9
  import cx from 'classnames';
11
10
  import { List, Button, Header, Label } from 'semantic-ui-react';
12
11
  import { Icon } from '@plone/volto/components';
13
12
  import addSVG from '@plone/volto/icons/circle-plus.svg';
14
13
  import trashSVG from '@plone/volto/icons/delete.svg';
14
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
15
15
 
16
16
  import { toISOString } from './Utils';
17
17
 
@@ -46,7 +46,7 @@ const messages = defineMessages({
46
46
  },
47
47
  });
48
48
 
49
- const formatDate = (d) => {
49
+ const formatDate = (d, moment) => {
50
50
  const m = moment(d);
51
51
  return m.format('dddd') + ', ' + m.format('LL');
52
52
  };
@@ -56,14 +56,16 @@ const formatDate = (d) => {
56
56
  * @function Occurences
57
57
  * @returns {string} Markup of the component.
58
58
  */
59
- const Occurences = ({
59
+ const Occurences_ = ({
60
60
  rruleSet,
61
61
  exclude,
62
62
  undoExclude,
63
63
  intl,
64
64
  showTitle,
65
65
  editOccurences,
66
+ moment: momentlib,
66
67
  }) => {
68
+ const moment = momentlib.default;
67
69
  moment.locale(intl.locale);
68
70
  let all = [];
69
71
  const isExcluded = (date) => {
@@ -154,7 +156,7 @@ const Occurences = ({
154
156
  </List.Content>
155
157
  )}
156
158
  <List.Content className={cx({ excluded: excluded })}>
157
- {formatDate(date)}
159
+ {formatDate(date, moment)}
158
160
  {isAdditional(date) && (
159
161
  <Label
160
162
  pointing="left"
@@ -182,6 +184,8 @@ const Occurences = ({
182
184
  );
183
185
  };
184
186
 
187
+ export const Occurences = injectLazyLibs(['moment'])(Occurences_);
188
+
185
189
  /**
186
190
  * Property types.
187
191
  * @property {Object} propTypes Property types.
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { Component } from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import moment from 'moment';
8
+ import { compose } from 'redux';
9
9
  import { RRule, RRuleSet, rrulestr } from 'rrule';
10
10
 
11
11
  import cx from 'classnames';
@@ -22,6 +22,7 @@ import {
22
22
  } from 'semantic-ui-react';
23
23
 
24
24
  import { SelectWidget, Icon, DatetimeWidget } from '@plone/volto/components';
25
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
25
26
 
26
27
  import saveSVG from '@plone/volto/icons/save.svg';
27
28
  import editingSVG from '@plone/volto/icons/editing.svg';
@@ -179,7 +180,8 @@ class RecurrenceWidget extends Component {
179
180
  constructor(props, intl) {
180
181
  super(props);
181
182
 
182
- moment.locale(this.props.intl.locale);
183
+ this.moment = this.props.moment.default;
184
+ this.moment.locale(this.props.intl.locale);
183
185
 
184
186
  let rruleSet = this.props.value
185
187
  ? rrulestr(props.value, {
@@ -197,7 +199,7 @@ class RecurrenceWidget extends Component {
197
199
  open: false,
198
200
  rruleSet: rruleSet,
199
201
  formValues: this.getFormValues(rruleSet),
200
- RRULE_LANGUAGE: rrulei18n(this.props.intl),
202
+ RRULE_LANGUAGE: rrulei18n(this.props.intl, this.moment),
201
203
  };
202
204
  }
203
205
 
@@ -257,8 +259,8 @@ class RecurrenceWidget extends Component {
257
259
 
258
260
  getUTCDate = (date) => {
259
261
  return date.match(/T(.)*(-|\+|Z)/g)
260
- ? moment(date).utc()
261
- : moment(`${date}Z`).utc();
262
+ ? this.moment(date).utc()
263
+ : this.moment(`${date}Z`).utc();
262
264
  };
263
265
 
264
266
  show = (dimmer) => () => {
@@ -412,7 +414,7 @@ class RecurrenceWidget extends Component {
412
414
  }
413
415
  break;
414
416
  case 'until':
415
- value = value ? moment(new Date(value)).utc().toDate() : null;
417
+ value = value ? this.moment(new Date(value)).utc().toDate() : null;
416
418
  break;
417
419
  default:
418
420
  break;
@@ -437,7 +439,7 @@ class RecurrenceWidget extends Component {
437
439
  ? value
438
440
  : rruleSet.dtstart()
439
441
  ? rruleSet.dtstart()
440
- : moment().utc().toDate();
442
+ : this.moment().utc().toDate();
441
443
  var exdates =
442
444
  field === 'exdates' ? value : Object.assign([], rruleSet.exdates());
443
445
 
@@ -457,6 +459,7 @@ class RecurrenceWidget extends Component {
457
459
  };
458
460
 
459
461
  getDefaultUntil = (freq) => {
462
+ const moment = this.moment;
460
463
  var end = this.props.formData?.end
461
464
  ? toISOString(this.getUTCDate(this.props.formData.end).toDate())
462
465
  : null;
@@ -495,18 +498,19 @@ class RecurrenceWidget extends Component {
495
498
  changeField = (formValues, field, value) => {
496
499
  // git p.log('field', field, 'value', value);
497
500
  //get weekday from state.
498
- var byweekday =
501
+ const moment = this.moment;
502
+ const byweekday =
499
503
  this.state?.rruleSet?.rrules().length > 0
500
504
  ? this.state.rruleSet.rrules()[0].origOptions.byweekday
501
505
  : null;
502
- var currWeekday = this.getWeekday(moment().day() - 1);
503
- var currMonth = moment().month() + 1;
506
+ const currWeekday = this.getWeekday(moment().day() - 1);
507
+ const currMonth = moment().month() + 1;
504
508
 
505
- var startMonth = this.props.formData?.start
509
+ const startMonth = this.props.formData?.start
506
510
  ? moment(this.props.formData.start).month() + 1
507
511
  : currMonth;
508
512
 
509
- var startWeekday = this.props.formData?.start
513
+ const startWeekday = this.props.formData?.start
510
514
  ? this.getWeekday(moment(this.props.formData.start).day() - 1)
511
515
  : currWeekday;
512
516
  formValues[field] = value;
@@ -687,6 +691,7 @@ class RecurrenceWidget extends Component {
687
691
  };
688
692
 
689
693
  addDate = (date) => {
694
+ const moment = this.moment;
690
695
  let all = concat(this.state.rruleSet.all(), this.state.rruleSet.exdates());
691
696
 
692
697
  var simpleDate = moment(new Date(date)).startOf('day').toDate().getTime();
@@ -957,4 +962,7 @@ class RecurrenceWidget extends Component {
957
962
  }
958
963
  }
959
964
 
960
- export default injectIntl(RecurrenceWidget);
965
+ export default compose(
966
+ injectLazyLibs(['moment']),
967
+ injectIntl,
968
+ )(RecurrenceWidget);
@@ -6,6 +6,12 @@ import { waitFor } from '@testing-library/react';
6
6
 
7
7
  import RecurrenceWidget from './RecurrenceWidget';
8
8
 
9
+ jest.mock('@plone/volto/helpers/Loadable/Loadable');
10
+ beforeAll(
11
+ async () =>
12
+ await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
13
+ );
14
+
9
15
  const mockStore = configureStore();
10
16
 
11
17
  test('renders a recurrence widget component', async () => {
@@ -1,5 +1,4 @@
1
1
  import { RRule } from 'rrule';
2
- import moment from 'moment';
3
2
  import { defineMessages } from 'react-intl';
4
3
 
5
4
  export const Days = {
@@ -43,7 +42,7 @@ export const toISOString = (date) => {
43
42
  return date.toISOString().split('T')[0];
44
43
  };
45
44
 
46
- export const rrulei18n = (intl) => {
45
+ export const rrulei18n = (intl, moment) => {
47
46
  moment.locale(intl.locale);
48
47
 
49
48
  const messages = defineMessages({