@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.
- package/CHANGELOG.md +11 -0
- package/package.json +4 -2
- package/src/components/index.js +2 -0
- package/src/components/manage/Contents/Contents.test.jsx +0 -7
- package/src/components/manage/Contents/ContentsItem.jsx +9 -14
- package/src/components/manage/Contents/ContentsUploadModal.jsx +2 -4
- package/src/components/manage/Controlpanels/ModerateComments.jsx +7 -5
- package/src/components/manage/Diff/Diff.jsx +13 -5
- package/src/components/manage/Diff/Diff.test.jsx +0 -6
- package/src/components/manage/Diff/DiffField.jsx +12 -3
- package/src/components/manage/Diff/DiffField.test.jsx +0 -6
- package/src/components/manage/History/History.jsx +6 -5
- package/src/components/manage/History/History.test.jsx +12 -7
- package/src/components/manage/Widgets/DatetimeWidget.jsx +10 -3
- package/src/components/manage/Widgets/DatetimeWidget.test.jsx +2 -2
- package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +4 -3
- package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +10 -3
- package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +8 -4
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +21 -13
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.test.jsx +6 -0
- package/src/components/manage/Widgets/RecurrenceWidget/Utils.js +1 -2
- package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +5 -3
- package/src/components/theme/Comments/Comments.jsx +3 -1
- package/src/components/theme/Comments/Comments.test.jsx +6 -0
- package/src/components/theme/FormattedDate/FormattedDate.jsx +42 -0
- package/src/components/theme/FormattedDate/FormattedDate.stories.jsx +91 -0
- package/src/components/theme/FormattedDate/FormattedRelativeDate.jsx +57 -0
- package/src/components/theme/FormattedDate/FormattedRelativeDate.stories.jsx +122 -0
- package/src/components/theme/View/EventDatesInfo.jsx +12 -6
- package/src/components/theme/View/EventDatesInfo.test.jsx +6 -0
- package/src/components/theme/View/EventView.test.jsx +6 -0
- package/src/helpers/Utils/Date.js +97 -0
- package/src/helpers/Utils/Date.test.js +197 -0
- package/src/helpers/Utils/Utils.js +1 -2
- 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.
|
|
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",
|
package/src/components/index.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
195
|
+
<FormattedDate date={item[index.id]} />
|
|
201
196
|
) : (
|
|
202
|
-
|
|
197
|
+
intl.formatMessage(messages.none)
|
|
203
198
|
)}
|
|
204
|
-
|
|
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
|
-
{
|
|
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 {
|
|
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
|
-
<
|
|
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 {
|
|
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:
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
text: (
|
|
197
|
+
<>
|
|
198
|
+
{index === 0 ? 'Current' : entry.version} (
|
|
199
|
+
<FormattedDate date={entry.time} long className="text" />,
|
|
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
|
-
|
|
60
|
-
|
|
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 {
|
|
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
|
-
<
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
undefined,
|
|
106
|
+
this.moment,
|
|
107
|
+
)?.toISOString() === this.moment().utc().toISOString(),
|
|
106
108
|
};
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
getInternalValue() {
|
|
110
|
-
return parseDateTime(
|
|
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={
|
|
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 = ({
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
503
|
-
|
|
506
|
+
const currWeekday = this.getWeekday(moment().day() - 1);
|
|
507
|
+
const currMonth = moment().month() + 1;
|
|
504
508
|
|
|
505
|
-
|
|
509
|
+
const startMonth = this.props.formData?.start
|
|
506
510
|
? moment(this.props.formData.start).month() + 1
|
|
507
511
|
: currMonth;
|
|
508
512
|
|
|
509
|
-
|
|
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
|
|
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({
|