@plone/volto 14.4.0 → 14.5.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 +19 -0
- package/package.json +1 -1
- package/src/components/manage/Add/Add.jsx +0 -3
- package/src/components/manage/Blocks/Block/BlocksForm.test.jsx +6 -0
- package/src/components/manage/Blocks/Listing/ListingBody.jsx +6 -0
- package/src/components/manage/Contents/Contents.jsx +15 -6
- package/src/components/manage/Contents/ContentsIndexHeader.jsx +46 -34
- package/src/components/manage/Contents/ContentsItem.jsx +60 -47
- package/src/components/manage/DragDropList/DragDropList.jsx +4 -2
- package/src/components/manage/Form/Field.jsx +12 -2
- package/src/components/manage/Widgets/ObjectListWidget.test.js +6 -0
- package/src/components/manage/Widgets/SchemaWidget.jsx +4 -2
- package/src/components/manage/Widgets/SchemaWidget.test.jsx +6 -0
- package/src/components/manage/Widgets/SchemaWidgetFieldset.jsx +8 -4
- package/src/components/manage/Widgets/SchemaWidgetFieldset.test.jsx +7 -1
- package/src/components/manage/Widgets/VocabularyTermsWidget.jsx +41 -7
- package/src/components/manage/Widgets/VocabularyTermsWidget.stories.js +6 -21
- package/src/components/manage/Widgets/VocabularyTermsWidget.test.jsx +6 -0
- package/src/components/theme/Navigation/Navigation.jsx +1 -1
- package/src/config/Loadables.jsx +3 -0
- package/src/config/index.js +1 -0
- package/theme/themes/pastanaga/extras/widgets.less +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 14.5.0 (2022-01-26)
|
|
4
|
+
|
|
5
|
+
### Feature
|
|
6
|
+
|
|
7
|
+
- VocabularyTermsWidget: Token is now on creation of term editable, but stays ineditable afterwards. @ksuess
|
|
8
|
+
|
|
9
|
+
### Bugfix
|
|
10
|
+
|
|
11
|
+
- Fix A11Y violations in Navigation @iRohitSingh
|
|
12
|
+
- Fix `language-independent-field` CSS class styling @sneridagh
|
|
13
|
+
|
|
14
|
+
### Internal
|
|
15
|
+
|
|
16
|
+
- Lazyload react-beautiful-dnd @tiberiuichim
|
|
17
|
+
- Lazyload react-dnd @tiberiuichim
|
|
18
|
+
- Improve docs on environment variables, add recipes @sneridagh
|
|
19
|
+
- Update p.restapi to 8.20.0 and plone.volto to 4.0.0a1 and plone.rest to 2.0.0a2 @sneridagh
|
|
20
|
+
|
|
3
21
|
## 14.4.0 (2022-01-21)
|
|
4
22
|
|
|
5
23
|
### Feature
|
|
@@ -16,6 +34,7 @@
|
|
|
16
34
|
|
|
17
35
|
### Bugfix
|
|
18
36
|
|
|
37
|
+
- Fix ListingBlock to add "No results" message when there are no messages @erral
|
|
19
38
|
- Fix overflow table in Content view @giuliaghisini
|
|
20
39
|
- Fixed url validation in FormValidation to admit ip addresses. @giuliaghisini
|
|
21
40
|
- Upgrade to plone.restapi 8.19.0 (to support the language independent fields serialization) @sneridagh
|
package/package.json
CHANGED
|
@@ -12,8 +12,6 @@ import { keys, isEmpty } from 'lodash';
|
|
|
12
12
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
13
13
|
import { Button, Grid, Menu } from 'semantic-ui-react';
|
|
14
14
|
import { Portal } from 'react-portal';
|
|
15
|
-
import { DragDropContext } from 'react-dnd';
|
|
16
|
-
import HTML5Backend from 'react-dnd-html5-backend';
|
|
17
15
|
import { v4 as uuid } from 'uuid';
|
|
18
16
|
import qs from 'query-string';
|
|
19
17
|
import { toast } from 'react-toastify';
|
|
@@ -446,7 +444,6 @@ class Add extends Component {
|
|
|
446
444
|
}
|
|
447
445
|
|
|
448
446
|
export default compose(
|
|
449
|
-
DragDropContext(HTML5Backend),
|
|
450
447
|
injectIntl,
|
|
451
448
|
connect(
|
|
452
449
|
(state, props) => ({
|
|
@@ -6,6 +6,12 @@ import { render } from '@testing-library/react';
|
|
|
6
6
|
|
|
7
7
|
import config from '@plone/volto/registry';
|
|
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
|
let mockSerial = 0;
|
|
10
16
|
|
|
11
17
|
jest.mock('uuid', () => {
|
|
@@ -97,6 +97,12 @@ const ListingBody = withQuerystringResults((props) => {
|
|
|
97
97
|
</div>
|
|
98
98
|
) : (
|
|
99
99
|
<div>
|
|
100
|
+
{hasLoaded && (
|
|
101
|
+
<FormattedMessage
|
|
102
|
+
id="No results found."
|
|
103
|
+
defaultMessage="No results found."
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
100
106
|
<Dimmer active={!hasLoaded} inverted>
|
|
101
107
|
<Loader indeterminate size="small">
|
|
102
108
|
<FormattedMessage id="loading" defaultMessage="Loading" />
|
|
@@ -33,8 +33,6 @@ import {
|
|
|
33
33
|
pull,
|
|
34
34
|
} from 'lodash';
|
|
35
35
|
import move from 'lodash-move';
|
|
36
|
-
import { DragDropContext } from 'react-dnd';
|
|
37
|
-
import HTML5Backend from 'react-dnd-html5-backend';
|
|
38
36
|
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|
39
37
|
import { asyncConnect } from '@plone/volto/helpers';
|
|
40
38
|
|
|
@@ -1780,9 +1778,21 @@ class Contents extends Component {
|
|
|
1780
1778
|
}
|
|
1781
1779
|
}
|
|
1782
1780
|
|
|
1781
|
+
const DragDropConnector = (props) => {
|
|
1782
|
+
const { DragDropContext } = props.reactDnd;
|
|
1783
|
+
const HTML5Backend = props.reactDndHtml5Backend.default;
|
|
1784
|
+
|
|
1785
|
+
const DndConnectedContents = React.useMemo(
|
|
1786
|
+
() => DragDropContext(HTML5Backend)(Contents),
|
|
1787
|
+
[DragDropContext, HTML5Backend],
|
|
1788
|
+
);
|
|
1789
|
+
|
|
1790
|
+
return <DndConnectedContents {...props} />;
|
|
1791
|
+
};
|
|
1792
|
+
|
|
1783
1793
|
export const __test__ = compose(
|
|
1784
1794
|
injectIntl,
|
|
1785
|
-
injectLazyLibs(['toastify']),
|
|
1795
|
+
injectLazyLibs(['toastify', 'reactDnd']),
|
|
1786
1796
|
connect(
|
|
1787
1797
|
(store, props) => {
|
|
1788
1798
|
return {
|
|
@@ -1822,7 +1832,6 @@ export const __test__ = compose(
|
|
|
1822
1832
|
)(Contents);
|
|
1823
1833
|
|
|
1824
1834
|
export default compose(
|
|
1825
|
-
DragDropContext(HTML5Backend),
|
|
1826
1835
|
injectIntl,
|
|
1827
1836
|
connect(
|
|
1828
1837
|
(store, props) => {
|
|
@@ -1869,5 +1878,5 @@ export default compose(
|
|
|
1869
1878
|
await dispatch(listActions(getBaseUrl(location.pathname))),
|
|
1870
1879
|
},
|
|
1871
1880
|
]),
|
|
1872
|
-
injectLazyLibs(['toastify']),
|
|
1873
|
-
)(
|
|
1881
|
+
injectLazyLibs(['toastify', 'reactDnd', 'reactDndHtml5Backend']),
|
|
1882
|
+
)(DragDropConnector);
|
|
@@ -5,8 +5,8 @@
|
|
|
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';
|
|
9
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
10
10
|
|
|
11
11
|
const widthValues = [
|
|
12
12
|
'one',
|
|
@@ -69,39 +69,51 @@ ContentsIndexHeaderComponent.propTypes = {
|
|
|
69
69
|
onOrderIndex: PropTypes.func.isRequired,
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
hover(props, monitor) {
|
|
76
|
-
const dragOrder = monitor.getItem().order;
|
|
77
|
-
const hoverOrder = props.order;
|
|
72
|
+
const DragDropConnector = (props) => {
|
|
73
|
+
const { DropTarget, DragSource } = props.reactDnd;
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
const DndConnectedContentsIndexHeader = React.useMemo(
|
|
76
|
+
() =>
|
|
77
|
+
DropTarget(
|
|
78
|
+
'index',
|
|
79
|
+
{
|
|
80
|
+
hover(props, monitor) {
|
|
81
|
+
const dragOrder = monitor.getItem().order;
|
|
82
|
+
const hoverOrder = props.order;
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
if (dragOrder === hoverOrder) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
88
|
+
props.onOrderIndex(dragOrder, hoverOrder - dragOrder);
|
|
89
|
+
|
|
90
|
+
monitor.getItem().order = hoverOrder;
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
(connect) => ({
|
|
94
|
+
connectDropTarget: connect.dropTarget(),
|
|
95
|
+
}),
|
|
96
|
+
)(
|
|
97
|
+
DragSource(
|
|
98
|
+
'index',
|
|
99
|
+
{
|
|
100
|
+
beginDrag(props) {
|
|
101
|
+
return {
|
|
102
|
+
id: props.label,
|
|
103
|
+
order: props.order,
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
(connect, monitor) => ({
|
|
108
|
+
connectDragSource: connect.dragSource(),
|
|
109
|
+
isDragging: monitor.isDragging(),
|
|
110
|
+
}),
|
|
111
|
+
)(injectIntl(ContentsIndexHeaderComponent)),
|
|
112
|
+
),
|
|
113
|
+
[DragSource, DropTarget],
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
return <DndConnectedContentsIndexHeader {...props} />;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default injectLazyLibs('reactDnd')(DragDropConnector);
|
|
@@ -9,7 +9,6 @@ import { Link } from 'react-router-dom';
|
|
|
9
9
|
import PropTypes from 'prop-types';
|
|
10
10
|
import { map } from 'lodash';
|
|
11
11
|
import moment from 'moment';
|
|
12
|
-
import { DragSource, DropTarget } from 'react-dnd';
|
|
13
12
|
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|
14
13
|
import { Icon, Circle } from '@plone/volto/components';
|
|
15
14
|
import { getContentIcon } from '@plone/volto/helpers';
|
|
@@ -26,6 +25,8 @@ import editingSVG from '@plone/volto/icons/editing.svg';
|
|
|
26
25
|
import dragSVG from '@plone/volto/icons/drag.svg';
|
|
27
26
|
import cx from 'classnames';
|
|
28
27
|
|
|
28
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
29
|
+
|
|
29
30
|
const messages = defineMessages({
|
|
30
31
|
private: {
|
|
31
32
|
id: 'private',
|
|
@@ -313,55 +314,67 @@ ContentsItemComponent.propTypes = {
|
|
|
313
314
|
onOrderItem: PropTypes.func.isRequired,
|
|
314
315
|
};
|
|
315
316
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
hover(props, monitor) {
|
|
320
|
-
const id = monitor.getItem().id;
|
|
321
|
-
const dragOrder = monitor.getItem().order;
|
|
322
|
-
const hoverOrder = props.order;
|
|
317
|
+
const DragDropConnector = (props) => {
|
|
318
|
+
const { DropTarget, DragSource } = props.reactDnd;
|
|
323
319
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
320
|
+
const DndConnectedContentsItem = React.useMemo(
|
|
321
|
+
() =>
|
|
322
|
+
DropTarget(
|
|
323
|
+
'item',
|
|
324
|
+
{
|
|
325
|
+
hover(props, monitor) {
|
|
326
|
+
const id = monitor.getItem().id;
|
|
327
|
+
const dragOrder = monitor.getItem().order;
|
|
328
|
+
const hoverOrder = props.order;
|
|
327
329
|
|
|
328
|
-
|
|
330
|
+
if (dragOrder === hoverOrder) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
329
333
|
|
|
330
|
-
|
|
331
|
-
},
|
|
332
|
-
drop(props, monitor) {
|
|
333
|
-
const id = monitor.getItem().id;
|
|
334
|
-
const dragOrder = monitor.getItem().startOrder;
|
|
335
|
-
const dropOrder = props.order;
|
|
334
|
+
props.onOrderItem(id, dragOrder, hoverOrder - dragOrder, false);
|
|
336
335
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
336
|
+
monitor.getItem().order = hoverOrder;
|
|
337
|
+
},
|
|
338
|
+
drop(props, monitor) {
|
|
339
|
+
const id = monitor.getItem().id;
|
|
340
|
+
const dragOrder = monitor.getItem().startOrder;
|
|
341
|
+
const dropOrder = props.order;
|
|
340
342
|
|
|
341
|
-
|
|
343
|
+
if (dragOrder === dropOrder) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
342
346
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
)
|
|
347
|
+
props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true);
|
|
348
|
+
|
|
349
|
+
monitor.getItem().order = dropOrder;
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
(connect) => ({
|
|
353
|
+
connectDropTarget: connect.dropTarget(),
|
|
354
|
+
}),
|
|
355
|
+
)(
|
|
356
|
+
DragSource(
|
|
357
|
+
'item',
|
|
358
|
+
{
|
|
359
|
+
beginDrag(props) {
|
|
360
|
+
return {
|
|
361
|
+
id: props.item['@id'],
|
|
362
|
+
order: props.order,
|
|
363
|
+
startOrder: props.order,
|
|
364
|
+
};
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
(connect, monitor) => ({
|
|
368
|
+
connectDragSource: connect.dragSource(),
|
|
369
|
+
connectDragPreview: connect.dragPreview(),
|
|
370
|
+
isDragging: monitor.isDragging(),
|
|
371
|
+
}),
|
|
372
|
+
)(ContentsItemComponent),
|
|
373
|
+
),
|
|
374
|
+
[DragSource, DropTarget],
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
return <DndConnectedContentsItem {...props} />;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
export default injectLazyLibs('reactDnd')(DragDropConnector);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useRef } from 'react';
|
|
2
2
|
import { isEmpty } from 'lodash';
|
|
3
|
-
import {
|
|
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
|
|
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.
|
|
@@ -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
|
|
|
@@ -11,7 +11,7 @@ import { concat, findIndex, isString, map, omit, slice, without } from 'lodash';
|
|
|
11
11
|
import move from 'lodash-move';
|
|
12
12
|
import { Confirm, Form, Grid, Icon, Message, Segment } from 'semantic-ui-react';
|
|
13
13
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
14
|
-
import {
|
|
14
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
Field,
|
|
@@ -1078,7 +1078,8 @@ class SchemaWidget extends Component {
|
|
|
1078
1078
|
* @returns {string} Markup for the component.
|
|
1079
1079
|
*/
|
|
1080
1080
|
render() {
|
|
1081
|
-
const { error } = this.props;
|
|
1081
|
+
const { error, reactBeautifulDnd } = this.props;
|
|
1082
|
+
const { Draggable, DragDropContext, Droppable } = reactBeautifulDnd;
|
|
1082
1083
|
if (!this.props.value) {
|
|
1083
1084
|
return '';
|
|
1084
1085
|
}
|
|
@@ -1427,6 +1428,7 @@ class SchemaWidget extends Component {
|
|
|
1427
1428
|
|
|
1428
1429
|
export default compose(
|
|
1429
1430
|
injectIntl,
|
|
1431
|
+
injectLazyLibs(['reactBeautifulDnd']),
|
|
1430
1432
|
connect(
|
|
1431
1433
|
(state, props) => ({
|
|
1432
1434
|
value: isString(props.value) ? JSON.parse(props.value) : props.value,
|
|
@@ -5,6 +5,12 @@ import { Provider } from 'react-intl-redux';
|
|
|
5
5
|
|
|
6
6
|
import SchemaWidget from './SchemaWidget';
|
|
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
|
const mockStore = configureStore();
|
|
9
15
|
|
|
10
16
|
test('renders a schema widget component', () => {
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import PropTypes from 'prop-types';
|
|
9
9
|
import React from 'react';
|
|
10
|
-
import { Draggable } from 'react-beautiful-dnd';
|
|
11
10
|
import { Icon } from 'semantic-ui-react';
|
|
12
11
|
|
|
12
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
13
|
+
|
|
13
14
|
/**
|
|
14
15
|
* Schema widget fieldset component.
|
|
15
16
|
* @function SchemaWidgetFieldset
|
|
@@ -26,8 +27,9 @@ export const SchemaWidgetFieldsetComponent = ({
|
|
|
26
27
|
getItemStyle,
|
|
27
28
|
isDraggable,
|
|
28
29
|
isDisabled,
|
|
30
|
+
reactBeautifulDnd,
|
|
29
31
|
}) => (
|
|
30
|
-
<Draggable draggableId={title} index={order} key={title}>
|
|
32
|
+
<reactBeautifulDnd.Draggable draggableId={title} index={order} key={title}>
|
|
31
33
|
{(provided, snapshot) => (
|
|
32
34
|
<div
|
|
33
35
|
className={`item${active ? ' active' : ''}`}
|
|
@@ -71,7 +73,7 @@ export const SchemaWidgetFieldsetComponent = ({
|
|
|
71
73
|
)}
|
|
72
74
|
</div>
|
|
73
75
|
)}
|
|
74
|
-
</Draggable>
|
|
76
|
+
</reactBeautifulDnd.Draggable>
|
|
75
77
|
);
|
|
76
78
|
|
|
77
79
|
/**
|
|
@@ -91,4 +93,6 @@ SchemaWidgetFieldsetComponent.propTypes = {
|
|
|
91
93
|
isDisabled: PropTypes.bool,
|
|
92
94
|
};
|
|
93
95
|
|
|
94
|
-
export default
|
|
96
|
+
export default injectLazyLibs(['reactBeautifulDnd'])(
|
|
97
|
+
SchemaWidgetFieldsetComponent,
|
|
98
|
+
);
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
|
3
3
|
import renderer from 'react-test-renderer';
|
|
4
|
-
import
|
|
4
|
+
import SchemaWidgetFieldsetComponent from './SchemaWidgetFieldset';
|
|
5
|
+
|
|
6
|
+
jest.mock('@plone/volto/helpers/Loadable/Loadable');
|
|
7
|
+
beforeAll(
|
|
8
|
+
async () =>
|
|
9
|
+
await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
|
|
10
|
+
);
|
|
5
11
|
|
|
6
12
|
test('renders a contents item component', () => {
|
|
7
13
|
const component = renderer.create(
|
|
@@ -84,11 +84,11 @@ def TalkTypesVocabulary(context):
|
|
|
84
84
|
|
|
85
85
|
import React from 'react';
|
|
86
86
|
import { useDispatch } from 'react-redux';
|
|
87
|
-
import { findIndex, remove } from 'lodash';
|
|
87
|
+
import { find, findIndex, remove } from 'lodash';
|
|
88
88
|
import { defineMessages, useIntl } from 'react-intl';
|
|
89
89
|
import { v4 as uuid } from 'uuid';
|
|
90
90
|
|
|
91
|
-
import { Button
|
|
91
|
+
import { Button } from 'semantic-ui-react';
|
|
92
92
|
|
|
93
93
|
import {
|
|
94
94
|
DragDropList,
|
|
@@ -134,6 +134,7 @@ const VocabularyTermsWidget = (props) => {
|
|
|
134
134
|
var widgetvalue = value;
|
|
135
135
|
const dispatch = useDispatch();
|
|
136
136
|
const [toFocusId, setToFocusId] = React.useState('');
|
|
137
|
+
const [editableToken, setEditableToken] = React.useState('');
|
|
137
138
|
const intl = useIntl();
|
|
138
139
|
|
|
139
140
|
React.useEffect(() => {
|
|
@@ -206,6 +207,7 @@ const VocabularyTermsWidget = (props) => {
|
|
|
206
207
|
items: newitems,
|
|
207
208
|
});
|
|
208
209
|
setToFocusId(`field-${supportedLanguages[0]}-0-${id}-${newtoken}`);
|
|
210
|
+
setEditableToken(newtoken);
|
|
209
211
|
}
|
|
210
212
|
|
|
211
213
|
function swap(arr, from, to) {
|
|
@@ -217,10 +219,7 @@ const VocabularyTermsWidget = (props) => {
|
|
|
217
219
|
});
|
|
218
220
|
|
|
219
221
|
return (
|
|
220
|
-
<FormFieldWrapper {...props} className="dictwidget">
|
|
221
|
-
<Segment basic>
|
|
222
|
-
<h3>{props.title}</h3>
|
|
223
|
-
</Segment>
|
|
222
|
+
<FormFieldWrapper {...props} className="vocabularytermswidget dictwidget">
|
|
224
223
|
<div className="add-item-button-wrapper">
|
|
225
224
|
<Button
|
|
226
225
|
aria-label={intl.formatMessage(messages.termtitle)}
|
|
@@ -268,7 +267,7 @@ const VocabularyTermsWidget = (props) => {
|
|
|
268
267
|
schema={TermSchema}
|
|
269
268
|
title="Translation of term"
|
|
270
269
|
/>,
|
|
271
|
-
termProps,
|
|
270
|
+
{ editableToken, setEditableToken, ...termProps },
|
|
272
271
|
);
|
|
273
272
|
}}
|
|
274
273
|
</DragDropList>
|
|
@@ -287,6 +286,33 @@ const TermsWrapper = (props) => {
|
|
|
287
286
|
const { termProps, draginfo, children } = props;
|
|
288
287
|
const { id, vocabularyterms, vterm, onChange } = termProps;
|
|
289
288
|
|
|
289
|
+
const _updateTermsWithNewToken = (term, newtoken) => {
|
|
290
|
+
let newitems = termProps.vocabularyterms;
|
|
291
|
+
let index = findIndex(newitems, { token: term.token });
|
|
292
|
+
newitems.splice(index, 1, {
|
|
293
|
+
token: newtoken,
|
|
294
|
+
titles: newitems[index].titles,
|
|
295
|
+
});
|
|
296
|
+
onChange(id, {
|
|
297
|
+
items: newitems,
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
function onChangeTokenHandler(event) {
|
|
302
|
+
let value = event.target.value;
|
|
303
|
+
// required token length: 3
|
|
304
|
+
if (value.length > 2) {
|
|
305
|
+
// check if value is different from already used tokens
|
|
306
|
+
if (find(termProps.vocabularyterms, (el) => el.token === value)) {
|
|
307
|
+
// token already token. Stay with uuid.
|
|
308
|
+
} else {
|
|
309
|
+
// `token '${value}' is OK`
|
|
310
|
+
_updateTermsWithNewToken(vterm, value);
|
|
311
|
+
termProps.setEditableToken('');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
290
316
|
return (
|
|
291
317
|
<div
|
|
292
318
|
ref={draginfo.innerRef}
|
|
@@ -299,6 +325,14 @@ const TermsWrapper = (props) => {
|
|
|
299
325
|
</div>
|
|
300
326
|
<div className="ui drag block inner">{children}</div>
|
|
301
327
|
<div>
|
|
328
|
+
{vterm.token === termProps.editableToken ? (
|
|
329
|
+
<input
|
|
330
|
+
id={`token-${vterm.token}`}
|
|
331
|
+
title="Token"
|
|
332
|
+
placeholder="token"
|
|
333
|
+
onBlur={onChangeTokenHandler}
|
|
334
|
+
/>
|
|
335
|
+
) : null}
|
|
302
336
|
<Button
|
|
303
337
|
icon
|
|
304
338
|
basic
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import VocabularyTermsWidget from './VocabularyTermsWidget';
|
|
2
1
|
import React from 'react';
|
|
3
|
-
|
|
2
|
+
import VocabularyTermsWidget from './VocabularyTermsWidget';
|
|
4
3
|
import WidgetStory from './story';
|
|
5
4
|
|
|
6
|
-
export const
|
|
7
|
-
props: { id: '
|
|
5
|
+
export const VocabularyTerms = WidgetStory.bind({
|
|
6
|
+
props: { id: 'vocabularyterms', title: 'Vocabulary terms', block: 'block' },
|
|
8
7
|
widget: VocabularyTermsWidget,
|
|
9
8
|
customStore: {
|
|
10
9
|
userSession: { token: '1234' },
|
|
@@ -34,26 +33,12 @@ export const JSONField = WidgetStory.bind({
|
|
|
34
33
|
],
|
|
35
34
|
},
|
|
36
35
|
});
|
|
37
|
-
|
|
38
|
-
export const Simple = WidgetStory.bind({
|
|
39
|
-
props: { id: 'simplevocabulary', title: 'Vocabulary terms' },
|
|
40
|
-
widget: VocabularyTermsWidget,
|
|
41
|
-
customStore: {
|
|
42
|
-
userSession: { token: '1234' },
|
|
43
|
-
intl: {
|
|
44
|
-
locale: 'en',
|
|
45
|
-
messages: {},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
initialValue: {
|
|
49
|
-
'001': 'manual',
|
|
50
|
-
'002': 'questions & answers',
|
|
51
|
-
},
|
|
52
|
-
});
|
|
36
|
+
VocabularyTerms.args = {};
|
|
53
37
|
|
|
54
38
|
export default {
|
|
55
|
-
title: 'Widgets/
|
|
39
|
+
title: 'Widgets/VocabularyTerms',
|
|
56
40
|
component: VocabularyTermsWidget,
|
|
41
|
+
argTypes: {},
|
|
57
42
|
decorators: [
|
|
58
43
|
(Story) => (
|
|
59
44
|
<div className="ui segment form attached" style={{ width: '600px' }}>
|
|
@@ -5,6 +5,12 @@ import { Provider } from 'react-intl-redux';
|
|
|
5
5
|
|
|
6
6
|
import VocabularyTermsWidget from './VocabularyTermsWidget';
|
|
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
|
|
|
@@ -124,7 +124,7 @@ class Navigation extends Component {
|
|
|
124
124
|
*/
|
|
125
125
|
render() {
|
|
126
126
|
return (
|
|
127
|
-
<nav className="navigation" id="navigation">
|
|
127
|
+
<nav className="navigation" id="navigation" aria-label="navigation">
|
|
128
128
|
<div className="hamburger-wrapper mobile tablet only">
|
|
129
129
|
<button
|
|
130
130
|
className={cx('hamburger hamburger--spin', {
|
package/src/config/Loadables.jsx
CHANGED
|
@@ -29,4 +29,7 @@ export const loadables = {
|
|
|
29
29
|
diffLib: loadable.lib(() => import('diff')),
|
|
30
30
|
moment: loadable.lib(() => import('moment')),
|
|
31
31
|
reactDates: loadable.lib(() => import('react-dates')),
|
|
32
|
+
reactDnd: loadable.lib(() => import('react-dnd')),
|
|
33
|
+
reactDndHtml5Backend: loadable.lib(() => import('react-dnd-html5-backend')),
|
|
34
|
+
reactBeautifulDnd: loadable.lib(() => import('react-beautiful-dnd')),
|
|
32
35
|
};
|
package/src/config/index.js
CHANGED