@eeacms/volto-group-block 8.1.1 → 10.0.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/.eslintrc.js +7 -2
- package/CHANGELOG.md +18 -40
- package/DEVELOP.md +23 -17
- package/README.md +45 -19
- package/cypress.config.js +9 -0
- package/docker-compose.yml +1 -1
- package/jest-addon.config.js +10 -4
- package/locales/de/LC_MESSAGES/volto.po +0 -20
- package/locales/en/LC_MESSAGES/volto.po +0 -20
- package/locales/it/LC_MESSAGES/volto.po +0 -20
- package/locales/ro/LC_MESSAGES/volto.po +0 -20
- package/locales/volto.pot +0 -20
- package/package.json +1 -1
- package/src/components/manage/Blocks/Group/CounterComponent.jsx +13 -4
- package/src/components/manage/Blocks/Group/CounterComponent.test.jsx +116 -1
- package/src/components/manage/Blocks/Group/DefaultBody.jsx +30 -47
- package/src/components/manage/Blocks/Group/DefaultBody.test.jsx +28 -5
- package/src/components/manage/Blocks/Group/Edit.jsx +48 -19
- package/src/components/manage/Blocks/Group/Edit.test.jsx +17 -1
- package/src/components/manage/Blocks/Group/LayoutSchema.jsx +8 -0
- package/src/components/manage/Blocks/Group/View.test.jsx +1 -1
- package/src/components/manage/Blocks/Group/editor.less +5 -30
- package/src/components/manage/Blocks/Group/EditBlockWrapper.jsx +0 -197
- package/src/components/manage/Blocks/Group/EditBlockWrapper.test.jsx +0 -58
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@ const countTextInEachBlock =
|
|
|
56
56
|
: countCharsWithSpaces(foundText);
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
const countTextInBlocks = (blocksObject, ignoreSpaces, maxChars) => {
|
|
59
|
+
export const countTextInBlocks = (blocksObject, ignoreSpaces, maxChars) => {
|
|
60
60
|
const { countTextIn, skipBlocksInGroups = [] } =
|
|
61
61
|
config.blocks?.blocksConfig?.group || {};
|
|
62
62
|
// use obj ref to update value - if you send number it will not be updated
|
|
@@ -82,12 +82,21 @@ const countTextInBlocks = (blocksObject, ignoreSpaces, maxChars) => {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
const CounterComponent = ({ data, setSidebarTab, setSelectedBlock }) => {
|
|
85
|
-
const
|
|
86
|
-
const
|
|
85
|
+
const maxChars = parseInt(data.maxChars) || 0;
|
|
86
|
+
const maxCharsOverflowPercent = parseInt(data.maxCharsOverflowPercent) || 0;
|
|
87
|
+
const { ignoreSpaces } = data;
|
|
88
|
+
const charCount =
|
|
89
|
+
data.charCount ?? countTextInBlocks(data?.data, ignoreSpaces, maxChars);
|
|
90
|
+
|
|
91
|
+
const overflowLimit =
|
|
92
|
+
maxCharsOverflowPercent > 0
|
|
93
|
+
? Math.ceil((maxChars * (100 + maxCharsOverflowPercent)) / 100)
|
|
94
|
+
: maxChars;
|
|
95
|
+
|
|
87
96
|
const counterClass =
|
|
88
97
|
charCount < Math.ceil(maxChars / 1.05)
|
|
89
98
|
? 'info'
|
|
90
|
-
: charCount
|
|
99
|
+
: charCount <= overflowLimit
|
|
91
100
|
? 'warning'
|
|
92
101
|
: 'danger';
|
|
93
102
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import CounterComponent from './CounterComponent';
|
|
4
|
-
import '@testing-library/jest-dom
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
5
5
|
|
|
6
6
|
jest.mock('@plone/volto/registry', () => ({
|
|
7
7
|
blocks: {
|
|
@@ -231,4 +231,119 @@ describe('CounterComponent', () => {
|
|
|
231
231
|
expect(setSidebarTab).toHaveBeenCalledWith(1);
|
|
232
232
|
expect(setSelectedBlock).toHaveBeenCalled();
|
|
233
233
|
});
|
|
234
|
+
|
|
235
|
+
it('should render warning class when chars are within overflow zone (maxCharsOverflowPercent=10)', () => {
|
|
236
|
+
const { container, getByText } = render(
|
|
237
|
+
<CounterComponent
|
|
238
|
+
data={{
|
|
239
|
+
maxChars: 100,
|
|
240
|
+
maxCharsOverflowPercent: 10,
|
|
241
|
+
data: {
|
|
242
|
+
blocks: {
|
|
243
|
+
block1: { '@type': 'text', plaintext: 'a'.repeat(105) },
|
|
244
|
+
},
|
|
245
|
+
blocks_layout: {
|
|
246
|
+
items: ['block1'],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
}}
|
|
250
|
+
setSidebarTab={setSidebarTab}
|
|
251
|
+
setSelectedBlock={setSelectedBlock}
|
|
252
|
+
/>,
|
|
253
|
+
);
|
|
254
|
+
expect(getByText('5 characters over the limit')).toBeInTheDocument();
|
|
255
|
+
expect(container.querySelector('.counter.warning')).toBeInTheDocument();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should render danger class when chars exceed overflow zone (maxCharsOverflowPercent=10)', () => {
|
|
259
|
+
const { container, getByText } = render(
|
|
260
|
+
<CounterComponent
|
|
261
|
+
data={{
|
|
262
|
+
maxChars: 100,
|
|
263
|
+
maxCharsOverflowPercent: 10,
|
|
264
|
+
data: {
|
|
265
|
+
blocks: {
|
|
266
|
+
block1: { '@type': 'text', plaintext: 'a'.repeat(111) },
|
|
267
|
+
},
|
|
268
|
+
blocks_layout: {
|
|
269
|
+
items: ['block1'],
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
}}
|
|
273
|
+
setSidebarTab={setSidebarTab}
|
|
274
|
+
setSelectedBlock={setSelectedBlock}
|
|
275
|
+
/>,
|
|
276
|
+
);
|
|
277
|
+
expect(getByText('11 characters over the limit')).toBeInTheDocument();
|
|
278
|
+
expect(container.querySelector('.counter.danger')).toBeInTheDocument();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should render warning class when chars equal maxChars with no overflow percent', () => {
|
|
282
|
+
const { container, getByText } = render(
|
|
283
|
+
<CounterComponent
|
|
284
|
+
data={{
|
|
285
|
+
maxChars: 100,
|
|
286
|
+
data: {
|
|
287
|
+
blocks: {
|
|
288
|
+
block1: { '@type': 'text', plaintext: 'a'.repeat(100) },
|
|
289
|
+
},
|
|
290
|
+
blocks_layout: {
|
|
291
|
+
items: ['block1'],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
}}
|
|
295
|
+
setSidebarTab={setSidebarTab}
|
|
296
|
+
setSelectedBlock={setSelectedBlock}
|
|
297
|
+
/>,
|
|
298
|
+
);
|
|
299
|
+
expect(getByText('0 characters remaining out of 100')).toBeInTheDocument();
|
|
300
|
+
expect(container.querySelector('.counter.warning')).toBeInTheDocument();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should render danger class when negative overflow percent falls back to maxChars', () => {
|
|
304
|
+
const { container, getByText } = render(
|
|
305
|
+
<CounterComponent
|
|
306
|
+
data={{
|
|
307
|
+
maxChars: 100,
|
|
308
|
+
maxCharsOverflowPercent: -5,
|
|
309
|
+
data: {
|
|
310
|
+
blocks: {
|
|
311
|
+
block1: { '@type': 'text', plaintext: 'a'.repeat(104) },
|
|
312
|
+
},
|
|
313
|
+
blocks_layout: {
|
|
314
|
+
items: ['block1'],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
}}
|
|
318
|
+
setSidebarTab={setSidebarTab}
|
|
319
|
+
setSelectedBlock={setSelectedBlock}
|
|
320
|
+
/>,
|
|
321
|
+
);
|
|
322
|
+
expect(getByText('4 characters over the limit')).toBeInTheDocument();
|
|
323
|
+
expect(container.querySelector('.counter.danger')).toBeInTheDocument();
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should use persisted charCount from data instead of recomputing', () => {
|
|
327
|
+
const { container, getByText } = render(
|
|
328
|
+
<CounterComponent
|
|
329
|
+
data={{
|
|
330
|
+
maxChars: 100,
|
|
331
|
+
charCount: 42,
|
|
332
|
+
data: {
|
|
333
|
+
blocks: {
|
|
334
|
+
block1: { '@type': 'text', plaintext: 'a'.repeat(90) },
|
|
335
|
+
},
|
|
336
|
+
blocks_layout: {
|
|
337
|
+
items: ['block1'],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
}}
|
|
341
|
+
setSidebarTab={setSidebarTab}
|
|
342
|
+
setSelectedBlock={setSelectedBlock}
|
|
343
|
+
/>,
|
|
344
|
+
);
|
|
345
|
+
// Should use charCount=42 from data, not 90 from blocks
|
|
346
|
+
expect(getByText('58 characters remaining out of 100')).toBeInTheDocument();
|
|
347
|
+
expect(container.querySelector('.counter.info')).toBeInTheDocument();
|
|
348
|
+
});
|
|
234
349
|
});
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import EditBlockWrapper from './EditBlockWrapper';
|
|
1
|
+
import { BlocksForm, RenderBlocks } from '@plone/volto/components';
|
|
2
|
+
import { countTextInBlocks } from './CounterComponent';
|
|
4
3
|
import { useLocation } from 'react-router-dom';
|
|
5
4
|
|
|
6
|
-
import helpSVG from '@plone/volto/icons/help.svg';
|
|
7
|
-
|
|
8
5
|
const GroupBlockDefaultBody = (props) => {
|
|
9
6
|
const location = useLocation();
|
|
10
7
|
const {
|
|
@@ -16,10 +13,8 @@ const GroupBlockDefaultBody = (props) => {
|
|
|
16
13
|
selected,
|
|
17
14
|
selectedBlock,
|
|
18
15
|
onSelectBlock,
|
|
19
|
-
setSelectedBlock,
|
|
20
16
|
manage,
|
|
21
17
|
childBlocksForm,
|
|
22
|
-
multiSelected,
|
|
23
18
|
formDescription,
|
|
24
19
|
isEditMode,
|
|
25
20
|
} = props;
|
|
@@ -41,6 +36,8 @@ const GroupBlockDefaultBody = (props) => {
|
|
|
41
36
|
allowedBlocks={data.allowedBlocks}
|
|
42
37
|
title={data.placeholder}
|
|
43
38
|
description={instructions}
|
|
39
|
+
isMainForm={false}
|
|
40
|
+
stopPropagation={selectedBlock}
|
|
44
41
|
onSelectBlock={(id, l, e) => {
|
|
45
42
|
const isMultipleSelection = e
|
|
46
43
|
? e.shiftKey || e.ctrlKey || e.metaKey
|
|
@@ -48,58 +45,44 @@ const GroupBlockDefaultBody = (props) => {
|
|
|
48
45
|
onSelectBlock(id, isMultipleSelection, e, selectedBlock);
|
|
49
46
|
}}
|
|
50
47
|
onChangeFormData={(newFormData) => {
|
|
51
|
-
|
|
48
|
+
const newData = {
|
|
52
49
|
...data,
|
|
53
50
|
data: newFormData,
|
|
54
|
-
}
|
|
51
|
+
};
|
|
52
|
+
if (data.maxChars) {
|
|
53
|
+
newData.charCount = countTextInBlocks(
|
|
54
|
+
newFormData,
|
|
55
|
+
data.ignoreSpaces,
|
|
56
|
+
data.maxChars,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
onChangeBlock(block, newData);
|
|
55
60
|
}}
|
|
56
61
|
onChangeField={(id, value) => {
|
|
57
62
|
if (['blocks', 'blocks_layout'].indexOf(id) > -1) {
|
|
58
63
|
blockState[id] = value;
|
|
59
|
-
|
|
64
|
+
const newChildData = {
|
|
65
|
+
...data.data,
|
|
66
|
+
...blockState,
|
|
67
|
+
};
|
|
68
|
+
const newData = {
|
|
60
69
|
...data,
|
|
61
|
-
data:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
data: newChildData,
|
|
71
|
+
};
|
|
72
|
+
if (data.maxChars) {
|
|
73
|
+
newData.charCount = countTextInBlocks(
|
|
74
|
+
newChildData,
|
|
75
|
+
data.ignoreSpaces,
|
|
76
|
+
data.maxChars,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
onChangeBlock(block, newData);
|
|
66
80
|
} else {
|
|
67
81
|
onChangeField(id, value);
|
|
68
82
|
}
|
|
69
83
|
}}
|
|
70
84
|
pathname={pathname}
|
|
71
|
-
|
|
72
|
-
{({ draginfo }, editBlock, blockProps) => (
|
|
73
|
-
<EditBlockWrapper
|
|
74
|
-
draginfo={draginfo}
|
|
75
|
-
blockProps={blockProps}
|
|
76
|
-
disabled={data.disableInnerButtons}
|
|
77
|
-
extraControls={
|
|
78
|
-
<>
|
|
79
|
-
{instructions && (
|
|
80
|
-
<>
|
|
81
|
-
<Button
|
|
82
|
-
icon
|
|
83
|
-
basic
|
|
84
|
-
title="Section help"
|
|
85
|
-
onClick={() => {
|
|
86
|
-
setSelectedBlock();
|
|
87
|
-
const tab = manage ? 0 : 1;
|
|
88
|
-
props.setSidebarTab(tab);
|
|
89
|
-
}}
|
|
90
|
-
>
|
|
91
|
-
<Icon name={helpSVG} className="" size="19px" />
|
|
92
|
-
</Button>
|
|
93
|
-
</>
|
|
94
|
-
)}
|
|
95
|
-
</>
|
|
96
|
-
}
|
|
97
|
-
multiSelected={multiSelected.includes(blockProps.block)}
|
|
98
|
-
>
|
|
99
|
-
{editBlock}
|
|
100
|
-
</EditBlockWrapper>
|
|
101
|
-
)}
|
|
102
|
-
</BlocksForm>
|
|
85
|
+
/>
|
|
103
86
|
) : (
|
|
104
87
|
<RenderBlocks
|
|
105
88
|
location={location}
|
|
@@ -3,7 +3,9 @@ import { render } from '@testing-library/react';
|
|
|
3
3
|
import { Provider } from 'react-intl-redux';
|
|
4
4
|
import DefaultBody from './DefaultBody';
|
|
5
5
|
import configureStore from 'redux-mock-store';
|
|
6
|
-
import '@testing-library/jest-dom
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
|
|
8
|
+
const mockBlocksForm = jest.fn();
|
|
7
9
|
|
|
8
10
|
jest.mock('react-router-dom', () => ({
|
|
9
11
|
...jest.requireActual('react-router-dom'),
|
|
@@ -15,7 +17,10 @@ jest.mock('react-router-dom', () => ({
|
|
|
15
17
|
}));
|
|
16
18
|
|
|
17
19
|
jest.mock('@plone/volto/components', () => ({
|
|
18
|
-
BlocksForm: jest.fn(() =>
|
|
20
|
+
BlocksForm: jest.fn((props) => {
|
|
21
|
+
mockBlocksForm(props);
|
|
22
|
+
return <div className="blocks-form">RenderBlocks</div>;
|
|
23
|
+
}),
|
|
19
24
|
RenderBlocks: jest.fn(() => <div>RenderBlocks</div>),
|
|
20
25
|
}));
|
|
21
26
|
|
|
@@ -28,6 +33,10 @@ const store = mockStore({
|
|
|
28
33
|
});
|
|
29
34
|
|
|
30
35
|
describe('DefaultBody', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
mockBlocksForm.mockClear();
|
|
38
|
+
});
|
|
39
|
+
|
|
31
40
|
it('renders children', () => {
|
|
32
41
|
const props = {
|
|
33
42
|
data: {
|
|
@@ -55,14 +64,26 @@ describe('DefaultBody Edit', () => {
|
|
|
55
64
|
variation: {},
|
|
56
65
|
allowedBlocks: ['listing'],
|
|
57
66
|
},
|
|
67
|
+
childBlocksForm: {
|
|
68
|
+
blocks: {
|
|
69
|
+
a: {
|
|
70
|
+
'@type': 'slate',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
blocks_layout: {
|
|
74
|
+
items: ['a'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
58
77
|
metadata: {},
|
|
59
78
|
properties: {},
|
|
60
79
|
variation: {},
|
|
61
80
|
onSelectBlock: jest.fn(),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
onChangeBlock: jest.fn(),
|
|
82
|
+
onChangeField: jest.fn(),
|
|
83
|
+
selectedBlock: 'a',
|
|
65
84
|
selected: true,
|
|
85
|
+
manage: true,
|
|
86
|
+
pathname: '/',
|
|
66
87
|
};
|
|
67
88
|
|
|
68
89
|
const { getByText } = render(
|
|
@@ -71,5 +92,7 @@ describe('DefaultBody Edit', () => {
|
|
|
71
92
|
</Provider>,
|
|
72
93
|
);
|
|
73
94
|
expect(getByText('RenderBlocks')).toBeInTheDocument();
|
|
95
|
+
expect(mockBlocksForm).toHaveBeenCalledTimes(1);
|
|
96
|
+
expect(mockBlocksForm.mock.calls[0][0].children).toBeUndefined();
|
|
74
97
|
});
|
|
75
98
|
});
|
|
@@ -18,7 +18,7 @@ import PropTypes from 'prop-types';
|
|
|
18
18
|
import { Segment } from 'semantic-ui-react';
|
|
19
19
|
import EditSchema from './EditSchema';
|
|
20
20
|
|
|
21
|
-
import CounterComponent from './CounterComponent';
|
|
21
|
+
import CounterComponent, { countTextInBlocks } from './CounterComponent';
|
|
22
22
|
import './editor.less';
|
|
23
23
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
24
24
|
import { compose } from 'redux';
|
|
@@ -115,20 +115,29 @@ const Edit = (props) => {
|
|
|
115
115
|
});
|
|
116
116
|
const selectedIndex =
|
|
117
117
|
data.data.blocks_layout.items.indexOf(selectedBlock) + 1;
|
|
118
|
-
|
|
119
|
-
...data,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
...data.data.blocks_layout.items.slice(selectedIndex),
|
|
128
|
-
],
|
|
129
|
-
},
|
|
118
|
+
const newChildData = {
|
|
119
|
+
...data?.data,
|
|
120
|
+
...newBlockData,
|
|
121
|
+
blocks_layout: {
|
|
122
|
+
items: [
|
|
123
|
+
...data.data.blocks_layout.items.slice(0, selectedIndex),
|
|
124
|
+
...pastedBlocks,
|
|
125
|
+
...data.data.blocks_layout.items.slice(selectedIndex),
|
|
126
|
+
],
|
|
130
127
|
},
|
|
131
|
-
}
|
|
128
|
+
};
|
|
129
|
+
const newData = {
|
|
130
|
+
...data,
|
|
131
|
+
data: newChildData,
|
|
132
|
+
};
|
|
133
|
+
if (data.maxChars) {
|
|
134
|
+
newData.charCount = countTextInBlocks(
|
|
135
|
+
newChildData,
|
|
136
|
+
data.ignoreSpaces,
|
|
137
|
+
data.maxChars,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
onChangeBlock(block, newData);
|
|
132
141
|
};
|
|
133
142
|
|
|
134
143
|
React.useEffect(() => {
|
|
@@ -137,10 +146,14 @@ const Edit = (props) => {
|
|
|
137
146
|
childBlocksForm.blocks_layout.items[0] !== selectedBlock
|
|
138
147
|
) {
|
|
139
148
|
setSelectedBlock(childBlocksForm.blocks_layout.items[0]);
|
|
140
|
-
|
|
149
|
+
const newData = {
|
|
141
150
|
...data,
|
|
142
151
|
data: childBlocksForm,
|
|
143
|
-
}
|
|
152
|
+
};
|
|
153
|
+
if (data.maxChars) {
|
|
154
|
+
newData.charCount = 0;
|
|
155
|
+
}
|
|
156
|
+
onChangeBlock(block, newData);
|
|
144
157
|
}
|
|
145
158
|
}, [onChangeBlock, childBlocksForm, selectedBlock, block, data, data_blocks]);
|
|
146
159
|
|
|
@@ -154,7 +167,9 @@ const Edit = (props) => {
|
|
|
154
167
|
<fieldset
|
|
155
168
|
role="presentation"
|
|
156
169
|
id={props.data.id}
|
|
157
|
-
className={cx('section-block', props.data.className
|
|
170
|
+
className={cx('section-block', props.data.className, {
|
|
171
|
+
'disable-inner-buttons': data.disableInnerButtons,
|
|
172
|
+
})}
|
|
158
173
|
onKeyDown={(e) => {
|
|
159
174
|
handleKeyDown(e, props.index, props.block, props.blockNode.current);
|
|
160
175
|
}}
|
|
@@ -218,10 +233,24 @@ const Edit = (props) => {
|
|
|
218
233
|
title={props.intl.formatMessage(messages.sectionGroupSettings)}
|
|
219
234
|
formData={data}
|
|
220
235
|
onChangeField={(id, value) => {
|
|
221
|
-
|
|
236
|
+
const newData = {
|
|
222
237
|
...props.data,
|
|
223
238
|
[id]: value,
|
|
224
|
-
}
|
|
239
|
+
};
|
|
240
|
+
const effectiveMaxChars =
|
|
241
|
+
id === 'maxChars' ? value : props.data.maxChars;
|
|
242
|
+
const effectiveIgnoreSpaces =
|
|
243
|
+
id === 'ignoreSpaces' ? value : props.data.ignoreSpaces;
|
|
244
|
+
if (effectiveMaxChars) {
|
|
245
|
+
newData.charCount = countTextInBlocks(
|
|
246
|
+
props.data?.data,
|
|
247
|
+
effectiveIgnoreSpaces,
|
|
248
|
+
effectiveMaxChars,
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
delete newData.charCount;
|
|
252
|
+
}
|
|
253
|
+
props.onChangeBlock(props.block, newData);
|
|
225
254
|
}}
|
|
226
255
|
/>
|
|
227
256
|
)}
|
|
@@ -4,7 +4,7 @@ import configureStore from 'redux-mock-store';
|
|
|
4
4
|
import { Provider } from 'react-intl-redux';
|
|
5
5
|
import thunk from 'redux-thunk';
|
|
6
6
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
7
|
-
import '@testing-library/jest-dom
|
|
7
|
+
import '@testing-library/jest-dom';
|
|
8
8
|
|
|
9
9
|
const mockStore = configureStore([thunk]);
|
|
10
10
|
const store = mockStore({
|
|
@@ -90,6 +90,22 @@ describe('Edit', () => {
|
|
|
90
90
|
expect(getByRole('presentation')).toBeInTheDocument();
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
it('adds a root modifier when inner buttons are disabled', () => {
|
|
94
|
+
const { getByRole } = render(
|
|
95
|
+
<Provider store={store}>
|
|
96
|
+
<Edit
|
|
97
|
+
{...props}
|
|
98
|
+
data={{
|
|
99
|
+
...props.data,
|
|
100
|
+
disableInnerButtons: true,
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
</Provider>,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(getByRole('presentation')).toHaveClass('disable-inner-buttons');
|
|
107
|
+
});
|
|
108
|
+
|
|
93
109
|
it('should call ArrowUp keydown', () => {
|
|
94
110
|
const mockOnFocusPreviousBlock = jest.fn();
|
|
95
111
|
const { getByRole } = render(
|
|
@@ -11,6 +11,7 @@ const Schema = {
|
|
|
11
11
|
'allowedBlocks',
|
|
12
12
|
'as',
|
|
13
13
|
'maxChars',
|
|
14
|
+
'maxCharsOverflowPercent',
|
|
14
15
|
'ignoreSpaces',
|
|
15
16
|
'readOnlySettings',
|
|
16
17
|
'disableInnerButtons',
|
|
@@ -68,6 +69,13 @@ const Schema = {
|
|
|
68
69
|
type: 'integer',
|
|
69
70
|
factory: 'Integer',
|
|
70
71
|
},
|
|
72
|
+
maxCharsOverflowPercent: {
|
|
73
|
+
title: 'Maximum Characters Overflow Percent',
|
|
74
|
+
description:
|
|
75
|
+
'The percentage of characters that can overflow the maximum characters limit before showing a warning: 0-100',
|
|
76
|
+
type: 'integer',
|
|
77
|
+
factory: 'Integer',
|
|
78
|
+
},
|
|
71
79
|
ignoreSpaces: {
|
|
72
80
|
title: 'Ignore spaces',
|
|
73
81
|
description: 'Ignore spaces while calculating maximum characters',
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import View from './View';
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
4
|
import { RenderBlocks } from '@plone/volto/components';
|
|
5
|
-
import '@testing-library/jest-dom
|
|
5
|
+
import '@testing-library/jest-dom';
|
|
6
6
|
|
|
7
7
|
jest.mock('@plone/volto/components', () => ({
|
|
8
8
|
RenderBlocks: jest.fn(() => <div>RenderBlocks</div>),
|
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
margin-bottom: 1rem;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
.block-add-button {
|
|
14
|
-
display: none !important;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
13
|
.block.group.selected::before,
|
|
18
14
|
.block.group:hover::before {
|
|
19
15
|
border-style: dashed;
|
|
@@ -70,32 +66,11 @@
|
|
|
70
66
|
margin-top: 0.5rem;
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.block-toolbar {
|
|
81
|
-
position: absolute;
|
|
82
|
-
z-index: 3;
|
|
83
|
-
right: -9px;
|
|
84
|
-
display: flex;
|
|
85
|
-
border: none;
|
|
86
|
-
border: 1px solid @borderColor;
|
|
87
|
-
border-bottom: 1px solid @pageBackground;
|
|
88
|
-
margin-top: -45px;
|
|
89
|
-
background-color: @pageBackground;
|
|
90
|
-
border-top-left-radius: 1rem;
|
|
91
|
-
border-top-right-radius: 1rem;
|
|
92
|
-
|
|
93
|
-
.ui.basic.button {
|
|
94
|
-
padding: 8px 5px;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.ui.basic.button:hover {
|
|
98
|
-
background: transparent !important;
|
|
69
|
+
.section-block.disable-inner-buttons {
|
|
70
|
+
.drag.handle.wrapper,
|
|
71
|
+
.delete-button,
|
|
72
|
+
.block-add-button {
|
|
73
|
+
display: none !important;
|
|
99
74
|
}
|
|
100
75
|
}
|
|
101
76
|
}
|