@plone/volto 15.0.0-alpha.1 → 15.0.0-alpha.5
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/.github/workflows/{core-sandbox.yml → coresandbox.yml} +1 -1
- package/.storybook/main.js +3 -9
- package/CHANGELOG.md +55 -0
- package/README.md +1 -0
- package/addon-registry.js +16 -11
- package/docker-compose.yml +15 -10
- package/locales/volto.pot +1 -1
- package/package.json +9 -9
- package/pyvenv.cfg +3 -0
- package/razzle.config.js +13 -13
- package/src/actions/language/language.js +7 -2
- package/src/components/manage/AnchorPlugin/components/LinkButton/AddLinkForm.jsx +1 -2
- package/src/components/manage/AnchorPlugin/components/LinkButton/index.jsx +15 -6
- package/src/components/manage/AnchorPlugin/index.jsx +12 -5
- package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +8 -7
- package/src/components/manage/Blocks/Description/Edit.jsx +33 -19
- package/src/components/manage/Blocks/Description/Edit.test.jsx +6 -0
- package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +37 -26
- package/src/components/manage/Blocks/HeroImageLeft/Edit.test.jsx +6 -0
- package/src/components/manage/Blocks/Listing/View.jsx +1 -1
- package/src/components/manage/Blocks/Table/Cell.jsx +40 -14
- package/src/components/manage/Blocks/Table/Cell.test.jsx +7 -1
- package/src/components/manage/Blocks/Table/Edit.jsx +7 -3
- package/src/components/manage/Blocks/Table/Edit.test.jsx +6 -0
- package/src/components/manage/Blocks/Table/View.jsx +2 -2
- package/src/components/manage/Blocks/Text/Edit.jsx +56 -14
- package/src/components/manage/Blocks/Text/Edit.test.jsx +7 -1
- package/src/components/manage/Blocks/Text/View.jsx +2 -2
- package/src/components/manage/Blocks/Title/Edit.jsx +25 -13
- package/src/components/manage/Blocks/Title/Edit.test.jsx +6 -0
- package/src/components/manage/Contents/ContentsUploadModal.jsx +3 -1
- package/src/components/manage/Sidebar/Sidebar.jsx +5 -1
- package/src/components/manage/Toolbar/Toolbar.jsx +3 -1
- package/src/components/manage/UniversalLink/UniversalLink.jsx +5 -2
- package/src/components/manage/Widgets/UrlWidget.jsx +1 -1
- package/src/components/manage/Widgets/WysiwygWidget.jsx +45 -19
- package/src/components/manage/Widgets/WysiwygWidget.test.jsx +7 -1
- package/src/components/theme/ContactForm/ContactForm.jsx +1 -1
- package/src/components/theme/LanguageSelector/LanguageSelector.js +2 -18
- package/src/components/theme/Login/Login.jsx +3 -1
- package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +17 -2
- package/src/components/theme/View/LinkView.jsx +3 -3
- package/src/config/Loadables.jsx +22 -0
- package/src/config/RichTextEditor/Blocks.jsx +25 -20
- package/src/config/RichTextEditor/Plugins.jsx +38 -31
- package/src/config/RichTextEditor/Styles.jsx +48 -44
- package/src/config/RichTextEditor/index.js +25 -0
- package/src/config/index.js +14 -18
- package/src/helpers/Url/Url.js +32 -1
- package/src/helpers/Url/urlRegex.js +7 -1
- package/src/helpers/index.js +1 -0
- package/src/middleware/api.js +1 -1
- package/test-setup-config.js +22 -7
- package/{webpack-bundle-analyze-plugin.js → webpack-plugins/webpack-bundle-analyze-plugin.js} +0 -0
- package/{webpack-less-plugin.js → webpack-plugins/webpack-less-plugin.js} +0 -0
- package/{webpack-relative-resolver.js → webpack-plugins/webpack-relative-resolver.js} +0 -0
- package/{webpack-root-resolver.js → webpack-plugins/webpack-root-resolver.js} +0 -0
- package/{webpack-sentry-plugin.js → webpack-plugins/webpack-sentry-plugin.js} +0 -0
- package/{webpack-svg-plugin.js → webpack-plugins/webpack-svg-plugin.js} +0 -0
- package/.surgeignore +0 -2
- package/Dockerfile.slim +0 -27
- package/patches/fixprettier.patch +0 -11
- package/patches/fixstylelint.patch +0 -11
- package/src/components/manage/AnchorPlugin/utils/URLUtils.js +0 -36
- package/src/components/manage/AnchorPlugin/utils/mailRegex.js +0 -2
- package/src/components/manage/AnchorPlugin/utils/telRegex.js +0 -1
- package/src/components/manage/AnchorPlugin/utils/urlRegex.js +0 -54
- package/styleguide.config.js +0 -42
|
@@ -7,15 +7,13 @@ import React, { Component } from 'react';
|
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import { connect } from 'react-redux';
|
|
9
9
|
import { compose } from 'redux';
|
|
10
|
-
import { Map } from 'immutable';
|
|
11
10
|
import { readAsDataURL } from 'promise-file-reader';
|
|
12
11
|
import { Button, Dimmer, Loader, Message } from 'semantic-ui-react';
|
|
13
|
-
import { stateFromHTML } from 'draft-js-import-html';
|
|
14
|
-
import { Editor, DefaultDraftBlockRenderMap, EditorState } from 'draft-js';
|
|
15
12
|
import { isEqual } from 'lodash';
|
|
16
13
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
17
14
|
import cx from 'classnames';
|
|
18
15
|
|
|
16
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
19
17
|
import { flattenToAppURL, getBaseUrl } from '@plone/volto/helpers';
|
|
20
18
|
import { createContent } from '@plone/volto/actions';
|
|
21
19
|
import { Icon, SidebarPortal, LinkMore } from '@plone/volto/components';
|
|
@@ -51,32 +49,12 @@ const messages = defineMessages({
|
|
|
51
49
|
},
|
|
52
50
|
});
|
|
53
51
|
|
|
54
|
-
const blockTitleRenderMap = Map({
|
|
55
|
-
unstyled: {
|
|
56
|
-
element: 'h1',
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const blockDescriptionRenderMap = Map({
|
|
61
|
-
unstyled: {
|
|
62
|
-
element: 'div',
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(
|
|
67
|
-
blockTitleRenderMap,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const extendedDescripBlockRenderMap = DefaultDraftBlockRenderMap.merge(
|
|
71
|
-
blockDescriptionRenderMap,
|
|
72
|
-
);
|
|
73
|
-
|
|
74
52
|
/**
|
|
75
53
|
* Edit image block class.
|
|
76
54
|
* @class Edit
|
|
77
55
|
* @extends Component
|
|
78
56
|
*/
|
|
79
|
-
class
|
|
57
|
+
class EditComponent extends Component {
|
|
80
58
|
/**
|
|
81
59
|
* Property types.
|
|
82
60
|
* @property {Object} propTypes Property types.
|
|
@@ -126,7 +104,32 @@ class Edit extends Component {
|
|
|
126
104
|
uploading: false,
|
|
127
105
|
};
|
|
128
106
|
|
|
107
|
+
const { Map } = this.props.immutableLib;
|
|
108
|
+
|
|
129
109
|
if (!__SERVER__) {
|
|
110
|
+
const { DefaultDraftBlockRenderMap, EditorState } = props.draftJs;
|
|
111
|
+
const { stateFromHTML } = props.draftJsImportHtml;
|
|
112
|
+
|
|
113
|
+
const blockTitleRenderMap = Map({
|
|
114
|
+
unstyled: {
|
|
115
|
+
element: 'h1',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const blockDescriptionRenderMap = Map({
|
|
120
|
+
unstyled: {
|
|
121
|
+
element: 'div',
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this.extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(
|
|
126
|
+
blockTitleRenderMap,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
this.extendedDescripBlockRenderMap = DefaultDraftBlockRenderMap.merge(
|
|
130
|
+
blockDescriptionRenderMap,
|
|
131
|
+
);
|
|
132
|
+
|
|
130
133
|
let titleEditorState;
|
|
131
134
|
let descriptionEditorState;
|
|
132
135
|
if (props.data && props.data.title) {
|
|
@@ -187,6 +190,9 @@ class Edit extends Component {
|
|
|
187
190
|
});
|
|
188
191
|
}
|
|
189
192
|
|
|
193
|
+
const { EditorState } = this.props.draftJs;
|
|
194
|
+
const { stateFromHTML } = this.props.draftJsImportHtml;
|
|
195
|
+
|
|
190
196
|
if (
|
|
191
197
|
nextProps.data.title &&
|
|
192
198
|
this.props.data.title !== nextProps.data.title &&
|
|
@@ -299,6 +305,7 @@ class Edit extends Component {
|
|
|
299
305
|
if (__SERVER__) {
|
|
300
306
|
return <div />;
|
|
301
307
|
}
|
|
308
|
+
const { Editor } = this.props.draftJs;
|
|
302
309
|
const placeholder =
|
|
303
310
|
this.props.data.placeholder ||
|
|
304
311
|
this.props.intl.formatMessage(messages.placeholder);
|
|
@@ -374,7 +381,7 @@ class Edit extends Component {
|
|
|
374
381
|
readOnly={!this.props.editable}
|
|
375
382
|
onChange={this.onChangeTitle}
|
|
376
383
|
editorState={this.state.titleEditorState}
|
|
377
|
-
blockRenderMap={extendedBlockRenderMap}
|
|
384
|
+
blockRenderMap={this.extendedBlockRenderMap}
|
|
378
385
|
handleReturn={() => true}
|
|
379
386
|
placeholder={this.props.intl.formatMessage(messages.title)}
|
|
380
387
|
blockStyleFn={() => 'title-editor'}
|
|
@@ -416,7 +423,7 @@ class Edit extends Component {
|
|
|
416
423
|
readOnly={!this.props.editable}
|
|
417
424
|
onChange={this.onChangeDescription}
|
|
418
425
|
editorState={this.state.descriptionEditorState}
|
|
419
|
-
blockRenderMap={extendedDescripBlockRenderMap}
|
|
426
|
+
blockRenderMap={this.extendedDescripBlockRenderMap}
|
|
420
427
|
handleReturn={() => true}
|
|
421
428
|
placeholder={this.props.intl.formatMessage(
|
|
422
429
|
messages.description,
|
|
@@ -460,6 +467,10 @@ class Edit extends Component {
|
|
|
460
467
|
}
|
|
461
468
|
}
|
|
462
469
|
|
|
470
|
+
const Edit = injectLazyLibs(['draftJs', 'immutableLib', 'draftJsImportHtml'])(
|
|
471
|
+
EditComponent,
|
|
472
|
+
);
|
|
473
|
+
|
|
463
474
|
export default compose(
|
|
464
475
|
injectIntl,
|
|
465
476
|
connect(
|
|
@@ -5,6 +5,12 @@ import { Provider } from 'react-intl-redux';
|
|
|
5
5
|
|
|
6
6
|
import Edit from './Edit';
|
|
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
|
global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
|
|
9
15
|
|
|
10
16
|
const mockStore = configureStore();
|
|
@@ -9,7 +9,7 @@ const View = (props) => {
|
|
|
9
9
|
const { data, path, pathname } = props;
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
|
-
<div className={cx('block listing', data.variation)}>
|
|
12
|
+
<div className={cx('block listing', data.variation || 'default')}>
|
|
13
13
|
<ListingBody {...props} path={path ?? pathname} />
|
|
14
14
|
</div>
|
|
15
15
|
);
|
|
@@ -5,19 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { Component } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import Editor from 'draft-js-plugins-editor';
|
|
9
|
-
import { convertFromRaw, EditorState, RichUtils } from 'draft-js';
|
|
10
|
-
import createInlineToolbarPlugin from 'draft-js-inline-toolbar-plugin';
|
|
11
|
-
import isSoftNewlineEvent from 'draft-js/lib/isSoftNewlineEvent';
|
|
12
8
|
import { includes } from 'lodash';
|
|
13
9
|
import config from '@plone/volto/registry';
|
|
14
10
|
|
|
11
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
12
|
+
import loadable from '@loadable/component';
|
|
13
|
+
|
|
14
|
+
const Editor = loadable(() => import('draft-js-plugins-editor'));
|
|
15
|
+
|
|
15
16
|
/**
|
|
16
17
|
* Edit text cell class.
|
|
17
18
|
* @class Cell
|
|
18
19
|
* @extends Component
|
|
19
20
|
*/
|
|
20
|
-
class
|
|
21
|
+
class CellComponent extends Component {
|
|
21
22
|
/**
|
|
22
23
|
* Property types.
|
|
23
24
|
* @property {Object} propTypes Property types.
|
|
@@ -54,12 +55,16 @@ class Cell extends Component {
|
|
|
54
55
|
constructor(props) {
|
|
55
56
|
super(props);
|
|
56
57
|
|
|
58
|
+
const { EditorState, convertFromRaw } = props.draftJs;
|
|
59
|
+
const createInlineToolbarPlugin = props.draftJsInlineToolbarPlugin.default;
|
|
60
|
+
|
|
57
61
|
if (!__SERVER__) {
|
|
62
|
+
this.draftConfig = config.settings.richtextEditorSettings(props);
|
|
58
63
|
let editorState;
|
|
59
64
|
editorState = EditorState.createWithContent(convertFromRaw(props.value));
|
|
60
65
|
|
|
61
66
|
const inlineToolbarPlugin = createInlineToolbarPlugin({
|
|
62
|
-
structure:
|
|
67
|
+
structure: this.draftConfig.richTextEditorInlineToolbarButtons,
|
|
63
68
|
});
|
|
64
69
|
|
|
65
70
|
this.state = {
|
|
@@ -125,7 +130,8 @@ class Cell extends Component {
|
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
const { InlineToolbar } = this.state.inlineToolbarPlugin;
|
|
128
|
-
const
|
|
133
|
+
const isSoftNewlineEvent = this.props.draftJsLibIsSoftNewlineEvent.default;
|
|
134
|
+
const { RichUtils } = this.props.draftJs;
|
|
129
135
|
|
|
130
136
|
return (
|
|
131
137
|
<div>
|
|
@@ -135,11 +141,11 @@ class Cell extends Component {
|
|
|
135
141
|
editorState={this.state.editorState}
|
|
136
142
|
plugins={[
|
|
137
143
|
this.state.inlineToolbarPlugin,
|
|
138
|
-
...
|
|
144
|
+
...this.draftConfig.richTextEditorPlugins,
|
|
139
145
|
]}
|
|
140
|
-
blockRenderMap={
|
|
141
|
-
blockStyleFn={
|
|
142
|
-
customStyleMap={
|
|
146
|
+
blockRenderMap={this.draftConfig.extendedBlockRenderMap}
|
|
147
|
+
blockStyleFn={this.draftConfig.blockStyleFn}
|
|
148
|
+
customStyleMap={this.draftConfig.customStyleMap}
|
|
143
149
|
handleReturn={(e) => {
|
|
144
150
|
if (isSoftNewlineEvent(e)) {
|
|
145
151
|
this.onChange(
|
|
@@ -155,10 +161,10 @@ class Cell extends Component {
|
|
|
155
161
|
anchorKey,
|
|
156
162
|
);
|
|
157
163
|
const blockType = currentContentBlock.getType();
|
|
158
|
-
if (!includes(
|
|
164
|
+
if (!includes(this.draftConfig.listBlockTypes, blockType)) {
|
|
159
165
|
this.props.onSelectBlock(
|
|
160
166
|
this.props.onAddBlock(
|
|
161
|
-
|
|
167
|
+
this.draftConfig.defaultBlockType,
|
|
162
168
|
this.props.index + 1,
|
|
163
169
|
),
|
|
164
170
|
);
|
|
@@ -178,4 +184,24 @@ class Cell extends Component {
|
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
|
|
181
|
-
export
|
|
187
|
+
export const Cell = injectLazyLibs([
|
|
188
|
+
'draftJs',
|
|
189
|
+
'draftJsBlockBreakoutPlugin',
|
|
190
|
+
'draftJsCreateBlockStyleButton',
|
|
191
|
+
'draftJsCreateInlineStyleButton',
|
|
192
|
+
'draftJsFilters',
|
|
193
|
+
'draftJsImportHtml',
|
|
194
|
+
'draftJsInlineToolbarPlugin',
|
|
195
|
+
'draftJsLibIsSoftNewlineEvent',
|
|
196
|
+
'immutableLib',
|
|
197
|
+
])(CellComponent);
|
|
198
|
+
|
|
199
|
+
const Preloader = (props) => {
|
|
200
|
+
const [loaded, setLoaded] = React.useState(false);
|
|
201
|
+
React.useEffect(() => {
|
|
202
|
+
Editor.load().then(() => setLoaded(true));
|
|
203
|
+
}, []);
|
|
204
|
+
return loaded ? <Cell {...props} /> : null;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export default Preloader;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import renderer from 'react-test-renderer';
|
|
3
|
-
import Cell from './Cell';
|
|
3
|
+
import { Cell } from './Cell';
|
|
4
4
|
|
|
5
5
|
global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
|
|
6
6
|
|
|
7
|
+
jest.mock('@plone/volto/helpers/Loadable/Loadable');
|
|
8
|
+
beforeAll(
|
|
9
|
+
async () =>
|
|
10
|
+
await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
|
|
11
|
+
);
|
|
12
|
+
|
|
7
13
|
test('renders a cell component', () => {
|
|
8
14
|
const component = renderer.create(
|
|
9
15
|
<Cell onChange={() => {}} onSelectCell={() => {}} />,
|
|
@@ -5,15 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { Component } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
+
import { compose } from 'redux';
|
|
8
9
|
import { map, remove } from 'lodash';
|
|
9
10
|
import { Button, Segment, Table, Form } from 'semantic-ui-react';
|
|
10
|
-
import { convertToRaw } from 'draft-js';
|
|
11
11
|
import { Portal } from 'react-portal';
|
|
12
12
|
import cx from 'classnames';
|
|
13
13
|
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|
14
14
|
|
|
15
15
|
import Cell from '@plone/volto/components/manage/Blocks/Table/Cell';
|
|
16
16
|
import { Field, Icon } from '@plone/volto/components';
|
|
17
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
17
18
|
|
|
18
19
|
import rowBeforeSVG from '@plone/volto/icons/row-before.svg';
|
|
19
20
|
import rowAfterSVG from '@plone/volto/icons/row-after.svg';
|
|
@@ -208,6 +209,9 @@ class Edit extends Component {
|
|
|
208
209
|
this.toggleBasic = this.toggleBasic.bind(this);
|
|
209
210
|
this.toggleCelled = this.toggleCelled.bind(this);
|
|
210
211
|
this.toggleStriped = this.toggleStriped.bind(this);
|
|
212
|
+
|
|
213
|
+
const { convertToRaw } = props.draftJs;
|
|
214
|
+
this.convertToRaw = convertToRaw;
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
/**
|
|
@@ -261,7 +265,7 @@ class Edit extends Component {
|
|
|
261
265
|
*/
|
|
262
266
|
onChangeCell(row, cell, editorState) {
|
|
263
267
|
const table = { ...this.props.data.table };
|
|
264
|
-
table.rows[row].cells[cell].value = convertToRaw(
|
|
268
|
+
table.rows[row].cells[cell].value = this.convertToRaw(
|
|
265
269
|
editorState.getCurrentContent(),
|
|
266
270
|
);
|
|
267
271
|
this.props.onChangeBlock(this.props.block, {
|
|
@@ -740,4 +744,4 @@ class Edit extends Component {
|
|
|
740
744
|
}
|
|
741
745
|
}
|
|
742
746
|
|
|
743
|
-
export default injectIntl(Edit);
|
|
747
|
+
export default compose(injectLazyLibs(['draftJs']), injectIntl)(Edit);
|
|
@@ -5,6 +5,12 @@ import { Provider } from 'react-intl-redux';
|
|
|
5
5
|
|
|
6
6
|
import Edit from './Edit';
|
|
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 an edit table block component', () => {
|
|
@@ -37,8 +37,8 @@ const View = ({ data }) =>
|
|
|
37
37
|
{cell.value && cell.value.blocks && cell.value.blocks[0].text
|
|
38
38
|
? redraft(
|
|
39
39
|
cell.value,
|
|
40
|
-
config.settings.ToHTMLRenderers,
|
|
41
|
-
config.settings.ToHTMLOptions,
|
|
40
|
+
config.settings.richtextViewSettings.ToHTMLRenderers,
|
|
41
|
+
config.settings.richtextViewSettings.ToHTMLOptions,
|
|
42
42
|
)
|
|
43
43
|
: '\u00A0'}
|
|
44
44
|
</Table.Cell>
|
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { Component } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import
|
|
9
|
-
import { convertFromRaw, convertToRaw, EditorState, RichUtils } from 'draft-js';
|
|
10
|
-
import createInlineToolbarPlugin from 'draft-js-inline-toolbar-plugin';
|
|
8
|
+
import { compose } from 'redux';
|
|
11
9
|
|
|
12
|
-
import isSoftNewlineEvent from 'draft-js/lib/isSoftNewlineEvent';
|
|
13
10
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
14
11
|
import { includes, isEqual } from 'lodash';
|
|
15
|
-
import { filterEditorState } from 'draftjs-filters';
|
|
16
12
|
import config from '@plone/volto/registry';
|
|
17
13
|
|
|
14
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
18
15
|
import { BlockChooserButton } from '@plone/volto/components';
|
|
19
16
|
|
|
17
|
+
import loadable from '@loadable/component';
|
|
18
|
+
|
|
19
|
+
const Editor = loadable(() => import('draft-js-plugins-editor'));
|
|
20
|
+
|
|
20
21
|
const messages = defineMessages({
|
|
21
22
|
text: {
|
|
22
23
|
id: 'Type text…',
|
|
@@ -29,7 +30,7 @@ const messages = defineMessages({
|
|
|
29
30
|
* @class Edit
|
|
30
31
|
* @extends Component
|
|
31
32
|
*/
|
|
32
|
-
class
|
|
33
|
+
export class EditComponent extends Component {
|
|
33
34
|
/**
|
|
34
35
|
* Property types.
|
|
35
36
|
* @property {Object} propTypes Property types.
|
|
@@ -77,6 +78,13 @@ class Edit extends Component {
|
|
|
77
78
|
constructor(props) {
|
|
78
79
|
super(props);
|
|
79
80
|
|
|
81
|
+
const { settings } = config;
|
|
82
|
+
|
|
83
|
+
this.draftConfig = settings.richtextEditorSettings(props);
|
|
84
|
+
|
|
85
|
+
const { EditorState, convertFromRaw } = props.draftJs;
|
|
86
|
+
const createInlineToolbarPlugin = props.draftJsInlineToolbarPlugin.default;
|
|
87
|
+
|
|
80
88
|
if (!__SERVER__) {
|
|
81
89
|
let editorState;
|
|
82
90
|
if (props.data && props.data.text) {
|
|
@@ -88,7 +96,7 @@ class Edit extends Component {
|
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
const inlineToolbarPlugin = createInlineToolbarPlugin({
|
|
91
|
-
structure:
|
|
99
|
+
structure: this.draftConfig.richTextEditorInlineToolbarButtons,
|
|
92
100
|
});
|
|
93
101
|
|
|
94
102
|
this.state = {
|
|
@@ -128,6 +136,7 @@ class Edit extends Component {
|
|
|
128
136
|
//nothing is selected, move focus to end
|
|
129
137
|
// See https://github.com/draft-js-plugins/draft-js-plugins/issues/800
|
|
130
138
|
setTimeout(this.node.focus, 0);
|
|
139
|
+
const { EditorState } = this.props.draftJs;
|
|
131
140
|
|
|
132
141
|
this.setState({
|
|
133
142
|
editorState: EditorState.moveFocusToEnd(this.state.editorState),
|
|
@@ -137,6 +146,7 @@ class Edit extends Component {
|
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
componentDidUpdate(prevProps) {
|
|
149
|
+
const { convertToRaw, EditorState, convertFromRaw } = this.props.draftJs;
|
|
140
150
|
if (
|
|
141
151
|
!isEqual(this.props.data, prevProps.data) &&
|
|
142
152
|
!isEqual(
|
|
@@ -179,6 +189,9 @@ class Edit extends Component {
|
|
|
179
189
|
const shouldFilterPaste =
|
|
180
190
|
editorState.getLastChangeType() === 'insert-fragment';
|
|
181
191
|
|
|
192
|
+
const { convertToRaw } = this.props.draftJs;
|
|
193
|
+
const { filterEditorState } = this.props.draftJsFilters;
|
|
194
|
+
|
|
182
195
|
if (
|
|
183
196
|
!isEqual(
|
|
184
197
|
convertToRaw(editorState.getCurrentContent()),
|
|
@@ -217,6 +230,8 @@ class Edit extends Component {
|
|
|
217
230
|
* @returns {string} Markup for the component.
|
|
218
231
|
*/
|
|
219
232
|
render() {
|
|
233
|
+
// console.log('draft config', this.draftConfig);
|
|
234
|
+
|
|
220
235
|
if (__SERVER__) {
|
|
221
236
|
return <div />;
|
|
222
237
|
}
|
|
@@ -229,7 +244,10 @@ class Edit extends Component {
|
|
|
229
244
|
const disableNewBlocks =
|
|
230
245
|
this.props.data?.disableNewBlocks || this.props.detached;
|
|
231
246
|
const { InlineToolbar } = this.state.inlineToolbarPlugin;
|
|
232
|
-
const { settings } = config;
|
|
247
|
+
// const { settings } = config;
|
|
248
|
+
|
|
249
|
+
const isSoftNewlineEvent = this.props.draftJsLibIsSoftNewlineEvent.default;
|
|
250
|
+
const { RichUtils } = this.props.draftJs;
|
|
233
251
|
|
|
234
252
|
return (
|
|
235
253
|
<>
|
|
@@ -239,11 +257,12 @@ class Edit extends Component {
|
|
|
239
257
|
editorState={this.state.editorState}
|
|
240
258
|
plugins={[
|
|
241
259
|
this.state.inlineToolbarPlugin,
|
|
242
|
-
...settings.richTextEditorPlugins,
|
|
260
|
+
// ...settings.richTextEditorPlugins,
|
|
261
|
+
...this.draftConfig.richTextEditorPlugins,
|
|
243
262
|
]}
|
|
244
|
-
blockRenderMap={
|
|
245
|
-
blockStyleFn={
|
|
246
|
-
customStyleMap={
|
|
263
|
+
blockRenderMap={this.draftConfig.extendedBlockRenderMap}
|
|
264
|
+
blockStyleFn={this.draftConfig.blockStyleFn}
|
|
265
|
+
customStyleMap={this.draftConfig.customStyleMap}
|
|
247
266
|
placeholder={placeholder}
|
|
248
267
|
handleReturn={(e) => {
|
|
249
268
|
if (isSoftNewlineEvent(e)) {
|
|
@@ -260,7 +279,7 @@ class Edit extends Component {
|
|
|
260
279
|
anchorKey,
|
|
261
280
|
);
|
|
262
281
|
const blockType = currentContentBlock.getType();
|
|
263
|
-
if (!includes(
|
|
282
|
+
if (!includes(this.draftConfig.listBlockTypes, blockType)) {
|
|
264
283
|
this.props.onSelectBlock(
|
|
265
284
|
this.props.onAddBlock(
|
|
266
285
|
config.settings.defaultBlockType,
|
|
@@ -329,4 +348,27 @@ class Edit extends Component {
|
|
|
329
348
|
}
|
|
330
349
|
}
|
|
331
350
|
|
|
332
|
-
export
|
|
351
|
+
export const Edit = compose(
|
|
352
|
+
injectIntl,
|
|
353
|
+
injectLazyLibs([
|
|
354
|
+
'draftJs',
|
|
355
|
+
'draftJsLibIsSoftNewlineEvent',
|
|
356
|
+
'draftJsFilters',
|
|
357
|
+
'draftJsInlineToolbarPlugin',
|
|
358
|
+
'draftJsBlockBreakoutPlugin',
|
|
359
|
+
'draftJsCreateInlineStyleButton',
|
|
360
|
+
'draftJsCreateBlockStyleButton',
|
|
361
|
+
'immutableLib',
|
|
362
|
+
// TODO: add all plugin dependencies, also in Wysiwyg and Cell
|
|
363
|
+
]),
|
|
364
|
+
)(EditComponent);
|
|
365
|
+
|
|
366
|
+
const Preloader = (props) => {
|
|
367
|
+
const [loaded, setLoaded] = React.useState(false);
|
|
368
|
+
React.useEffect(() => {
|
|
369
|
+
Editor.load().then(() => setLoaded(true));
|
|
370
|
+
}, []);
|
|
371
|
+
return loaded ? <Edit {...props} /> : null;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
export default Preloader;
|
|
@@ -3,12 +3,18 @@ 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 Edit from './Edit';
|
|
6
|
+
import { Edit } from './Edit';
|
|
7
7
|
|
|
8
8
|
const mockStore = configureStore();
|
|
9
9
|
|
|
10
10
|
global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
|
|
11
11
|
|
|
12
|
+
jest.mock('@plone/volto/helpers/Loadable/Loadable');
|
|
13
|
+
beforeAll(
|
|
14
|
+
async () =>
|
|
15
|
+
await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
|
|
16
|
+
);
|
|
17
|
+
|
|
12
18
|
test('renders an edit text block component', () => {
|
|
13
19
|
const store = mockStore({
|
|
14
20
|
intl: {
|
|
@@ -17,8 +17,8 @@ const View = ({ data }) =>
|
|
|
17
17
|
data.text ? (
|
|
18
18
|
redraft(
|
|
19
19
|
data.text,
|
|
20
|
-
config.settings.ToHTMLRenderers,
|
|
21
|
-
config.settings.ToHTMLOptions,
|
|
20
|
+
config.settings.richtextViewSettings.ToHTMLRenderers,
|
|
21
|
+
config.settings.richtextViewSettings.ToHTMLOptions,
|
|
22
22
|
)
|
|
23
23
|
) : (
|
|
24
24
|
<br />
|
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { Component } from 'react';
|
|
7
|
-
import {
|
|
7
|
+
import { compose } from 'redux';
|
|
8
8
|
import PropTypes from 'prop-types';
|
|
9
|
-
import { stateFromHTML } from 'draft-js-import-html';
|
|
10
9
|
import { isEqual } from 'lodash';
|
|
11
|
-
import { Editor, DefaultDraftBlockRenderMap, EditorState } from 'draft-js';
|
|
12
10
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
11
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
13
12
|
import config from '@plone/volto/registry';
|
|
14
13
|
|
|
15
14
|
const messages = defineMessages({
|
|
@@ -19,14 +18,6 @@ const messages = defineMessages({
|
|
|
19
18
|
},
|
|
20
19
|
});
|
|
21
20
|
|
|
22
|
-
const blockRenderMap = Map({
|
|
23
|
-
unstyled: {
|
|
24
|
-
element: 'h1',
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(blockRenderMap);
|
|
29
|
-
|
|
30
21
|
/**
|
|
31
22
|
* Edit title block class.
|
|
32
23
|
* @class Edit
|
|
@@ -71,6 +62,19 @@ class Edit extends Component {
|
|
|
71
62
|
super(props);
|
|
72
63
|
|
|
73
64
|
if (!__SERVER__) {
|
|
65
|
+
const { Map } = props.immutableLib;
|
|
66
|
+
const { DefaultDraftBlockRenderMap, EditorState } = props.draftJs;
|
|
67
|
+
const { stateFromHTML } = props.draftJsImportHtml;
|
|
68
|
+
|
|
69
|
+
const blockRenderMap = Map({
|
|
70
|
+
unstyled: {
|
|
71
|
+
element: 'h1',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(
|
|
76
|
+
blockRenderMap,
|
|
77
|
+
);
|
|
74
78
|
let editorState;
|
|
75
79
|
if (props.properties && props.properties.title) {
|
|
76
80
|
const contentState = stateFromHTML(props.properties.title);
|
|
@@ -117,6 +121,9 @@ class Edit extends Component {
|
|
|
117
121
|
this.props.properties.title !== nextProps.properties.title &&
|
|
118
122
|
!this.state.focus
|
|
119
123
|
) {
|
|
124
|
+
const { stateFromHTML } = this.props.draftJsImportHtml;
|
|
125
|
+
const { EditorState } = this.props.draftJs;
|
|
126
|
+
|
|
120
127
|
const contentState = stateFromHTML(nextProps.properties.title);
|
|
121
128
|
this.setState({
|
|
122
129
|
editorState: nextProps.properties.title
|
|
@@ -156,6 +163,8 @@ class Edit extends Component {
|
|
|
156
163
|
return <div />;
|
|
157
164
|
}
|
|
158
165
|
|
|
166
|
+
const { Editor } = this.props.draftJs;
|
|
167
|
+
|
|
159
168
|
const placeholder =
|
|
160
169
|
this.props.data.placeholder ||
|
|
161
170
|
this.props.intl.formatMessage(messages.title);
|
|
@@ -165,7 +174,7 @@ class Edit extends Component {
|
|
|
165
174
|
readOnly={!this.props.editable}
|
|
166
175
|
onChange={this.onChange}
|
|
167
176
|
editorState={this.state.editorState}
|
|
168
|
-
blockRenderMap={extendedBlockRenderMap}
|
|
177
|
+
blockRenderMap={this.extendedBlockRenderMap}
|
|
169
178
|
handleReturn={() => {
|
|
170
179
|
if (this.props.data.disableNewBlocks) {
|
|
171
180
|
return 'handled';
|
|
@@ -208,4 +217,7 @@ class Edit extends Component {
|
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
|
|
211
|
-
export default
|
|
220
|
+
export default compose(
|
|
221
|
+
injectLazyLibs(['draftJs', 'immutableLib', 'draftJsImportHtml']),
|
|
222
|
+
injectIntl,
|
|
223
|
+
)(Edit);
|
|
@@ -5,6 +5,12 @@ import { Provider } from 'react-intl-redux';
|
|
|
5
5
|
|
|
6
6
|
import Edit from './Edit';
|
|
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
|
global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
|
|
@@ -267,7 +267,9 @@ class ContentsUploadModal extends Component {
|
|
|
267
267
|
<Table.Row className="upload-row" key={file.name}>
|
|
268
268
|
<Table.Cell>{file.name}</Table.Cell>
|
|
269
269
|
<Table.Cell>
|
|
270
|
-
|
|
270
|
+
{file.lastModifiedDate && (
|
|
271
|
+
<FormattedRelativeDate date={file.lastModifiedDate} />
|
|
272
|
+
)}
|
|
271
273
|
</Table.Cell>
|
|
272
274
|
<Table.Cell>
|
|
273
275
|
{filesize(file.size, { round: 0 })}
|
|
@@ -18,6 +18,8 @@ import { setSidebarTab } from '@plone/volto/actions';
|
|
|
18
18
|
import expandSVG from '@plone/volto/icons/left-key.svg';
|
|
19
19
|
import collapseSVG from '@plone/volto/icons/right-key.svg';
|
|
20
20
|
|
|
21
|
+
import config from '@plone/volto/registry';
|
|
22
|
+
|
|
21
23
|
const messages = defineMessages({
|
|
22
24
|
document: {
|
|
23
25
|
id: 'Document',
|
|
@@ -98,7 +100,9 @@ class Sidebar extends Component {
|
|
|
98
100
|
onToggleExpanded() {
|
|
99
101
|
const { cookies } = this.props;
|
|
100
102
|
cookies.set('sidebar_expanded', !this.state.expanded, {
|
|
101
|
-
expires: new Date(
|
|
103
|
+
expires: new Date(
|
|
104
|
+
new Date().getTime() + config.settings.cookieExpires * 1000,
|
|
105
|
+
),
|
|
102
106
|
path: '/',
|
|
103
107
|
});
|
|
104
108
|
this.setState({
|
|
@@ -232,7 +232,9 @@ class Toolbar extends Component {
|
|
|
232
232
|
handleShrink = () => {
|
|
233
233
|
const { cookies } = this.props;
|
|
234
234
|
cookies.set('toolbar_expanded', !this.state.expanded, {
|
|
235
|
-
expires: new Date(
|
|
235
|
+
expires: new Date(
|
|
236
|
+
new Date().getTime() + config.settings.cookieExpires * 1000,
|
|
237
|
+
),
|
|
236
238
|
path: '/',
|
|
237
239
|
});
|
|
238
240
|
this.setState(
|