@plone/volto 16.0.0-rc.1 → 16.0.0-rc.2
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/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +17 -0
- package/cypress/support/commands.js +24 -0
- package/package.json +2 -1
- package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +1 -0
- package/packages/volto-slate/src/elementEditor/PluginEditor.jsx +1 -0
- package/pyvenv.cfg +3 -0
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +25 -1
- package/src/components/manage/Form/BlockDataForm.jsx +24 -2
- package/src/components/manage/Form/BlockDataForm.test.jsx +24 -3
- package/src/components/manage/Form/Field.jsx +2 -10
- package/src/components/manage/Form/InlineForm.jsx +17 -19
- package/src/components/manage/Form/InlineForm.test.jsx +9 -4
- package/src/components/manage/Widgets/ObjectListWidget.jsx +25 -17
- package/src/helpers/Blocks/Blocks.js +50 -7
- package/src/helpers/Blocks/Blocks.test.js +234 -0
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 16.0.0-rc.2 (2022-11-20)
|
|
4
|
+
|
|
5
|
+
### Bugfix
|
|
6
|
+
|
|
7
|
+
- Overhaul how block defaults are computed. See https://github.com/plone/volto/pull/3925 for more details @tiberiuichim
|
|
8
|
+
- Cover an additional edge case for defaults @tiberiuichim
|
|
9
|
+
|
|
10
|
+
### Internal
|
|
11
|
+
|
|
12
|
+
- Update to Plone 6 RC1 @sneridagh
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
- Document `Sentry` integration move from Volto core to add-on `@plone-collective/volto-sentry` in configuration, upgrade and deployment. @ksuess
|
|
17
|
+
- Remove `sentryOptions` from settings reference. Clean up `deploying/sentry.md`. @stevepiercy
|
|
18
|
+
- Tidy up `upgrade-guide/index.md`. @stevepiercy
|
|
19
|
+
|
|
3
20
|
## 16.0.0-rc.1 (2022-11-18)
|
|
4
21
|
|
|
5
22
|
### Feature
|
|
@@ -195,6 +195,30 @@ Cypress.Commands.add('removeContent', ({ path = '' }) => {
|
|
|
195
195
|
});
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
+
// Get content
|
|
199
|
+
Cypress.Commands.add('getContent', ({ path = '' }) => {
|
|
200
|
+
let api_url, auth;
|
|
201
|
+
if (Cypress.env('API') === 'guillotina') {
|
|
202
|
+
api_url = GUILLOTINA_API_URL;
|
|
203
|
+
auth = {
|
|
204
|
+
user: 'root',
|
|
205
|
+
pass: 'root',
|
|
206
|
+
};
|
|
207
|
+
} else {
|
|
208
|
+
api_url = PLONE_API_URL;
|
|
209
|
+
auth = ploneAuthObj;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return cy.request({
|
|
213
|
+
method: 'get',
|
|
214
|
+
url: `${api_url}/${path}`,
|
|
215
|
+
headers: {
|
|
216
|
+
Accept: 'application/json',
|
|
217
|
+
},
|
|
218
|
+
auth: auth,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
198
222
|
// --- Add DX Content-Type ----------------------------------------------------------
|
|
199
223
|
Cypress.Commands.add('addContentType', (name) => {
|
|
200
224
|
let api_url, auth;
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "16.0.0-rc.
|
|
12
|
+
"version": "16.0.0-rc.2",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@github.com:plone/volto.git"
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"postinstall": "make patches",
|
|
37
37
|
"analyze": "BUNDLE_ANALYZE=true razzle build",
|
|
38
38
|
"start": "razzle start",
|
|
39
|
+
"start:coresandbox": "ADDONS=coresandbox razzle start",
|
|
39
40
|
"build": "razzle build --noninteractive",
|
|
40
41
|
"prepare": "husky install",
|
|
41
42
|
"test": "razzle test --maxWorkers=50%",
|
package/pyvenv.cfg
ADDED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
2
3
|
import EditBlock from './Edit';
|
|
3
4
|
import { DragDropList } from '@plone/volto/components';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getBlocks,
|
|
7
|
+
getBlocksFieldname,
|
|
8
|
+
applyBlockDefaults,
|
|
9
|
+
} from '@plone/volto/helpers';
|
|
5
10
|
import {
|
|
6
11
|
addBlock,
|
|
7
12
|
insertBlock,
|
|
@@ -42,6 +47,7 @@ const BlocksForm = (props) => {
|
|
|
42
47
|
const blockList = getBlocks(properties);
|
|
43
48
|
|
|
44
49
|
const dispatch = useDispatch();
|
|
50
|
+
const intl = useIntl();
|
|
45
51
|
|
|
46
52
|
const ClickOutsideListener = () => {
|
|
47
53
|
onSelectBlock(null);
|
|
@@ -117,6 +123,16 @@ const BlocksForm = (props) => {
|
|
|
117
123
|
current,
|
|
118
124
|
config.experimental.addBlockButton.enabled ? 1 : 0,
|
|
119
125
|
);
|
|
126
|
+
|
|
127
|
+
const blocksFieldname = getBlocksFieldname(newFormData);
|
|
128
|
+
const blockData = newFormData[blocksFieldname][newId];
|
|
129
|
+
newFormData[blocksFieldname][newId] = applyBlockDefaults({
|
|
130
|
+
data: blockData,
|
|
131
|
+
intl,
|
|
132
|
+
metadata,
|
|
133
|
+
properties,
|
|
134
|
+
});
|
|
135
|
+
|
|
120
136
|
onChangeFormData(newFormData);
|
|
121
137
|
return newId;
|
|
122
138
|
};
|
|
@@ -124,6 +140,14 @@ const BlocksForm = (props) => {
|
|
|
124
140
|
const onAddBlock = (type, index) => {
|
|
125
141
|
if (editable) {
|
|
126
142
|
const [id, newFormData] = addBlock(properties, type, index);
|
|
143
|
+
const blocksFieldname = getBlocksFieldname(newFormData);
|
|
144
|
+
const blockData = newFormData[blocksFieldname][id];
|
|
145
|
+
newFormData[blocksFieldname][id] = applyBlockDefaults({
|
|
146
|
+
data: blockData,
|
|
147
|
+
intl,
|
|
148
|
+
metadata,
|
|
149
|
+
properties,
|
|
150
|
+
});
|
|
127
151
|
onChangeFormData(newFormData);
|
|
128
152
|
return id;
|
|
129
153
|
}
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { InlineForm } from '@plone/volto/components';
|
|
2
3
|
import { withVariationSchemaEnhancer } from '@plone/volto/helpers';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const EnhancedBlockDataForm = withVariationSchemaEnhancer(InlineForm);
|
|
5
6
|
|
|
6
|
-
export default BlockDataForm
|
|
7
|
+
export default function BlockDataForm(props) {
|
|
8
|
+
const { onChangeBlock, block } = props;
|
|
9
|
+
|
|
10
|
+
if (!onChangeBlock) {
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.warn(
|
|
13
|
+
'BlockDataForm component is used without passing down onChangeBlock as props',
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const onChangeFormData = React.useCallback(
|
|
18
|
+
(data) => onChangeBlock(block, data),
|
|
19
|
+
[block, onChangeBlock],
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<EnhancedBlockDataForm
|
|
24
|
+
{...props}
|
|
25
|
+
onChangeFormData={onChangeBlock ? onChangeFormData : undefined}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -8,6 +8,24 @@ import { Provider } from 'react-intl-redux';
|
|
|
8
8
|
|
|
9
9
|
const mockStore = configureStore();
|
|
10
10
|
|
|
11
|
+
const withStateManagement = (Component) => ({ ...props }) => {
|
|
12
|
+
const [formData, onChangeFormData] = React.useState(props.formData || {});
|
|
13
|
+
const onChangeField = (id, value) => {
|
|
14
|
+
onChangeFormData({ ...formData, [id]: value });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// NOTE: onChangeBlock here is not "really" implemented
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Component
|
|
21
|
+
{...props}
|
|
22
|
+
onChangeField={onChangeField}
|
|
23
|
+
onChangeBlock={(block, data) => onChangeFormData(data)}
|
|
24
|
+
formData={formData}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
11
29
|
beforeAll(() => {
|
|
12
30
|
config.widgets = {
|
|
13
31
|
id: {},
|
|
@@ -55,6 +73,7 @@ beforeAll(() => {
|
|
|
55
73
|
|
|
56
74
|
describe('BlockDataForm', () => {
|
|
57
75
|
it('should does not add variations to schema when unneeded', () => {
|
|
76
|
+
const WrappedBlockDataForm = withStateManagement(BlockDataForm);
|
|
58
77
|
const store = mockStore({
|
|
59
78
|
intl: {
|
|
60
79
|
locale: 'en',
|
|
@@ -71,7 +90,7 @@ describe('BlockDataForm', () => {
|
|
|
71
90
|
};
|
|
72
91
|
const { container } = render(
|
|
73
92
|
<Provider store={store}>
|
|
74
|
-
<
|
|
93
|
+
<WrappedBlockDataForm
|
|
75
94
|
formData={formData}
|
|
76
95
|
schema={testSchema}
|
|
77
96
|
onChangeField={(id, value) => {}}
|
|
@@ -85,6 +104,7 @@ describe('BlockDataForm', () => {
|
|
|
85
104
|
});
|
|
86
105
|
|
|
87
106
|
it('should does not add variations when only one variation', () => {
|
|
107
|
+
const WrappedBlockDataForm = withStateManagement(BlockDataForm);
|
|
88
108
|
const store = mockStore({
|
|
89
109
|
intl: {
|
|
90
110
|
locale: 'en',
|
|
@@ -101,7 +121,7 @@ describe('BlockDataForm', () => {
|
|
|
101
121
|
};
|
|
102
122
|
const { container } = render(
|
|
103
123
|
<Provider store={store}>
|
|
104
|
-
<
|
|
124
|
+
<WrappedBlockDataForm
|
|
105
125
|
formData={formData}
|
|
106
126
|
schema={testSchema}
|
|
107
127
|
onChangeField={(id, value) => {}}
|
|
@@ -115,6 +135,7 @@ describe('BlockDataForm', () => {
|
|
|
115
135
|
});
|
|
116
136
|
|
|
117
137
|
it('should add variations to schema', () => {
|
|
138
|
+
const WrappedBlockDataForm = withStateManagement(BlockDataForm);
|
|
118
139
|
const store = mockStore({
|
|
119
140
|
intl: {
|
|
120
141
|
locale: 'en',
|
|
@@ -131,7 +152,7 @@ describe('BlockDataForm', () => {
|
|
|
131
152
|
};
|
|
132
153
|
const { container } = render(
|
|
133
154
|
<Provider store={store}>
|
|
134
|
-
<
|
|
155
|
+
<WrappedBlockDataForm
|
|
135
156
|
formData={formData}
|
|
136
157
|
schema={testSchema}
|
|
137
158
|
onChangeField={(id, value) => {}}
|
|
@@ -232,17 +232,9 @@ const DndConnectedField = injectLazyLibs(['reactDnd'])(UnconnectedField);
|
|
|
232
232
|
|
|
233
233
|
const Field = (props) =>
|
|
234
234
|
props.onOrder ? (
|
|
235
|
-
<DndConnectedField
|
|
236
|
-
{...props}
|
|
237
|
-
defaultValue={undefined}
|
|
238
|
-
value={props.value ?? props.default ?? props.defaultValue}
|
|
239
|
-
/>
|
|
235
|
+
<DndConnectedField {...props} />
|
|
240
236
|
) : (
|
|
241
|
-
<UnconnectedField
|
|
242
|
-
{...props}
|
|
243
|
-
defaultValue={undefined}
|
|
244
|
-
value={props.value ?? props.default ?? props.defaultValue}
|
|
245
|
-
/>
|
|
237
|
+
<UnconnectedField {...props} />
|
|
246
238
|
);
|
|
247
239
|
|
|
248
240
|
/**
|
|
@@ -6,6 +6,7 @@ import AnimateHeight from 'react-animate-height';
|
|
|
6
6
|
import { keys, map, isEqual } from 'lodash';
|
|
7
7
|
|
|
8
8
|
import { Field, Icon } from '@plone/volto/components';
|
|
9
|
+
import { applySchemaDefaults } from '@plone/volto/helpers';
|
|
9
10
|
|
|
10
11
|
import upSVG from '@plone/volto/icons/up-key.svg';
|
|
11
12
|
import downSVG from '@plone/volto/icons/down-key.svg';
|
|
@@ -32,6 +33,7 @@ const InlineForm = (props) => {
|
|
|
32
33
|
error, // Such as {message: "It's not good"}
|
|
33
34
|
errors = {},
|
|
34
35
|
formData,
|
|
36
|
+
onChangeFormData,
|
|
35
37
|
onChangeField,
|
|
36
38
|
schema,
|
|
37
39
|
title,
|
|
@@ -49,26 +51,22 @@ const InlineForm = (props) => {
|
|
|
49
51
|
// Will set field values from schema, by matching the default values
|
|
50
52
|
|
|
51
53
|
const objectSchema = typeof schema === 'function' ? schema(props) : schema;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
...accumulator,
|
|
58
|
-
[currentField]: objectSchema.properties[currentField].default,
|
|
59
|
-
}
|
|
60
|
-
: accumulator;
|
|
61
|
-
},
|
|
62
|
-
{},
|
|
63
|
-
),
|
|
64
|
-
...formData,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
Object.keys(initialData).forEach((k) => {
|
|
68
|
-
if (!isEqual(initialData[k], formData?.[k])) {
|
|
69
|
-
onChangeField(k, initialData[k]);
|
|
70
|
-
}
|
|
54
|
+
|
|
55
|
+
const initialData = applySchemaDefaults({
|
|
56
|
+
data: formData,
|
|
57
|
+
schema: objectSchema,
|
|
58
|
+
intl,
|
|
71
59
|
});
|
|
60
|
+
|
|
61
|
+
if (onChangeFormData) {
|
|
62
|
+
onChangeFormData(initialData);
|
|
63
|
+
} else {
|
|
64
|
+
Object.keys(initialData).forEach((k) => {
|
|
65
|
+
if (!isEqual(initialData[k], formData?.[k])) {
|
|
66
|
+
onChangeField(k, initialData[k]);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
72
70
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
73
71
|
}, []);
|
|
74
72
|
|
|
@@ -20,13 +20,18 @@ function NewBaseWidget(name) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const withStateManagement = (Component) => ({ ...props }) => {
|
|
23
|
-
const [formData,
|
|
23
|
+
const [formData, onChangeFormData] = React.useState(props.formData || {});
|
|
24
24
|
const onChangeField = (id, value) => {
|
|
25
|
-
|
|
25
|
+
onChangeFormData({ ...formData, [id]: value });
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
<Component
|
|
29
|
+
<Component
|
|
30
|
+
{...props}
|
|
31
|
+
onChangeField={onChangeField}
|
|
32
|
+
onChangeFormData={onChangeFormData}
|
|
33
|
+
formData={formData}
|
|
34
|
+
/>
|
|
30
35
|
);
|
|
31
36
|
};
|
|
32
37
|
|
|
@@ -114,6 +119,7 @@ describe('Form', () => {
|
|
|
114
119
|
|
|
115
120
|
expect(container).toMatchSnapshot();
|
|
116
121
|
});
|
|
122
|
+
|
|
117
123
|
it('renders a form component with defaults in the schema - Checkboxes', () => {
|
|
118
124
|
const store = mockStore({
|
|
119
125
|
intl: {
|
|
@@ -163,7 +169,6 @@ describe('Form', () => {
|
|
|
163
169
|
/>
|
|
164
170
|
</Provider>,
|
|
165
171
|
);
|
|
166
|
-
|
|
167
172
|
expect(container).toMatchSnapshot();
|
|
168
173
|
});
|
|
169
174
|
it('renders a form component with defaults in the schema - Number field', () => {
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { defineMessages, useIntl } from 'react-intl';
|
|
3
3
|
import { Accordion, Button, Segment } from 'semantic-ui-react';
|
|
4
4
|
import { DragDropList, FormFieldWrapper, Icon } from '@plone/volto/components';
|
|
5
|
+
import { applySchemaDefaults } from '@plone/volto/helpers';
|
|
5
6
|
import ObjectWidget from '@plone/volto/components/manage/Widgets/ObjectWidget';
|
|
6
7
|
|
|
7
8
|
import upSVG from '@plone/volto/icons/up-key.svg';
|
|
@@ -70,14 +71,14 @@ const ObjectListWidget = (props) => {
|
|
|
70
71
|
onChange,
|
|
71
72
|
schemaExtender,
|
|
72
73
|
} = props;
|
|
73
|
-
const [
|
|
74
|
+
const [activeObject, setActiveObject] = React.useState(value.length - 1);
|
|
74
75
|
const intl = useIntl();
|
|
75
76
|
|
|
76
|
-
function
|
|
77
|
+
function handleChangeActiveObject(e, blockProps) {
|
|
77
78
|
const { index } = blockProps;
|
|
78
|
-
const newIndex =
|
|
79
|
+
const newIndex = activeObject === index ? -1 : index;
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
setActiveObject(newIndex);
|
|
81
82
|
}
|
|
82
83
|
const objectSchema = typeof schema === 'function' ? schema(props) : schema;
|
|
83
84
|
|
|
@@ -98,18 +99,25 @@ const ObjectListWidget = (props) => {
|
|
|
98
99
|
}
|
|
99
100
|
onClick={(e) => {
|
|
100
101
|
e.preventDefault();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
const data = {
|
|
103
|
+
'@id': uuid(),
|
|
104
|
+
};
|
|
105
|
+
const objSchema = schemaExtender
|
|
106
|
+
? schemaExtender(schema, data, intl)
|
|
107
|
+
: objectSchema;
|
|
108
|
+
const dataWithDefaults = applySchemaDefaults({
|
|
109
|
+
data,
|
|
110
|
+
schema: objSchema,
|
|
111
|
+
intl,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
onChange(id, [...value, dataWithDefaults]);
|
|
115
|
+
setActiveObject(value.length);
|
|
108
116
|
}}
|
|
109
117
|
>
|
|
110
118
|
<Icon name={addSVG} size="18px" />
|
|
111
119
|
|
|
112
|
-
{/* Custom addMessage in schema, else default to
|
|
120
|
+
{/* Custom addMessage in schema, else default to English */}
|
|
113
121
|
{objectSchema.addMessage ||
|
|
114
122
|
`${intl.formatMessage(messages.add)} ${objectSchema.title}`}
|
|
115
123
|
</Button>
|
|
@@ -158,11 +166,11 @@ const ObjectListWidget = (props) => {
|
|
|
158
166
|
>
|
|
159
167
|
<Accordion key={index} fluid styled>
|
|
160
168
|
<Accordion.Title
|
|
161
|
-
active={
|
|
169
|
+
active={activeObject === index}
|
|
162
170
|
index={index}
|
|
163
|
-
onClick={
|
|
171
|
+
onClick={handleChangeActiveObject}
|
|
164
172
|
aria-label={`${
|
|
165
|
-
|
|
173
|
+
activeObject === index
|
|
166
174
|
? intl.formatMessage(messages.labelCollapseItem)
|
|
167
175
|
: intl.formatMessage(messages.labelShowItem)
|
|
168
176
|
} #${index + 1}`}
|
|
@@ -195,14 +203,14 @@ const ObjectListWidget = (props) => {
|
|
|
195
203
|
>
|
|
196
204
|
<Icon name={deleteSVG} size="20px" color="#e40166" />
|
|
197
205
|
</button>
|
|
198
|
-
{
|
|
206
|
+
{activeObject === index ? (
|
|
199
207
|
<Icon name={upSVG} size="20px" />
|
|
200
208
|
) : (
|
|
201
209
|
<Icon name={downSVG} size="20px" />
|
|
202
210
|
)}
|
|
203
211
|
</div>
|
|
204
212
|
</Accordion.Title>
|
|
205
|
-
<Accordion.Content active={
|
|
213
|
+
<Accordion.Content active={activeObject === index}>
|
|
206
214
|
<Segment>
|
|
207
215
|
<ObjectWidget
|
|
208
216
|
id={`${id}-${index}`}
|
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
* @module helpers/Blocks
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
omit,
|
|
8
|
+
without,
|
|
9
|
+
endsWith,
|
|
10
|
+
find,
|
|
11
|
+
isObject,
|
|
12
|
+
keys,
|
|
13
|
+
toPairs,
|
|
14
|
+
merge,
|
|
15
|
+
} from 'lodash';
|
|
7
16
|
import move from 'lodash-move';
|
|
8
17
|
import { v4 as uuid } from 'uuid';
|
|
9
18
|
import config from '@plone/volto/registry';
|
|
@@ -366,21 +375,55 @@ export function visitBlocks(content, callback) {
|
|
|
366
375
|
}
|
|
367
376
|
}
|
|
368
377
|
|
|
378
|
+
let _logged = false;
|
|
379
|
+
|
|
369
380
|
/**
|
|
370
381
|
* Initializes data with the default values coming from schema
|
|
371
382
|
*/
|
|
372
|
-
export function applySchemaDefaults({ data = {}, schema }) {
|
|
373
|
-
|
|
374
|
-
|
|
383
|
+
export function applySchemaDefaults({ data = {}, schema, intl }) {
|
|
384
|
+
if (!intl && !_logged) {
|
|
385
|
+
// Old code that doesn't pass intl doesn't get ObjectWidget defaults
|
|
386
|
+
// eslint-disable-next-line no-console
|
|
387
|
+
console.warn(
|
|
388
|
+
`You should pass intl to any applySchemaDefaults call. By failing to pass
|
|
389
|
+
the intl object, your ObjectWidget fields will not get default values
|
|
390
|
+
extracted from their schema.`,
|
|
391
|
+
);
|
|
392
|
+
_logged = true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const derivedData = merge(
|
|
396
|
+
Object.keys(schema.properties).reduce((accumulator, currentField) => {
|
|
375
397
|
return typeof schema.properties[currentField].default !== 'undefined'
|
|
376
398
|
? {
|
|
377
399
|
...accumulator,
|
|
378
400
|
[currentField]: schema.properties[currentField].default,
|
|
379
401
|
}
|
|
402
|
+
: intl &&
|
|
403
|
+
schema.properties[currentField].schema &&
|
|
404
|
+
!(schema.properties[currentField].widget === 'object_list') // TODO: this should be renamed as itemSchema
|
|
405
|
+
? {
|
|
406
|
+
...accumulator,
|
|
407
|
+
[currentField]: {
|
|
408
|
+
...applySchemaDefaults({
|
|
409
|
+
data: { ...data[currentField], ...accumulator[currentField] },
|
|
410
|
+
schema:
|
|
411
|
+
typeof schema.properties[currentField].schema === 'function'
|
|
412
|
+
? schema.properties[currentField].schema({
|
|
413
|
+
data: accumulator[currentField],
|
|
414
|
+
formData: accumulator[currentField],
|
|
415
|
+
intl,
|
|
416
|
+
})
|
|
417
|
+
: schema.properties[currentField].schema,
|
|
418
|
+
intl,
|
|
419
|
+
}),
|
|
420
|
+
},
|
|
421
|
+
}
|
|
380
422
|
: accumulator;
|
|
381
423
|
}, {}),
|
|
382
|
-
|
|
383
|
-
|
|
424
|
+
data,
|
|
425
|
+
);
|
|
426
|
+
|
|
384
427
|
return derivedData;
|
|
385
428
|
}
|
|
386
429
|
|
|
@@ -404,7 +447,7 @@ export function applyBlockDefaults({ data, intl, ...rest }, blocksConfig) {
|
|
|
404
447
|
: blockSchema;
|
|
405
448
|
schema = applySchemaEnhancer({ schema, formData: data, intl });
|
|
406
449
|
|
|
407
|
-
return applySchemaDefaults({ data, schema });
|
|
450
|
+
return applySchemaDefaults({ data, schema, intl });
|
|
408
451
|
}
|
|
409
452
|
|
|
410
453
|
export const buildStyleClassNamesFromData = (styles) => {
|
|
@@ -165,6 +165,151 @@ config.blocks.blocksConfig.enhancedBlockCase2 = {
|
|
|
165
165
|
}),
|
|
166
166
|
};
|
|
167
167
|
|
|
168
|
+
const itemSchema = (props) => {
|
|
169
|
+
return {
|
|
170
|
+
title: 'Item',
|
|
171
|
+
addMessage: 'Add',
|
|
172
|
+
fieldsets: [
|
|
173
|
+
{
|
|
174
|
+
id: 'default',
|
|
175
|
+
title: 'Default',
|
|
176
|
+
fields: [
|
|
177
|
+
'href',
|
|
178
|
+
'title',
|
|
179
|
+
'description',
|
|
180
|
+
'preview_image',
|
|
181
|
+
'extraDefault',
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
|
|
186
|
+
properties: {
|
|
187
|
+
href: {
|
|
188
|
+
title: 'Source',
|
|
189
|
+
widget: 'object_browser',
|
|
190
|
+
mode: 'link',
|
|
191
|
+
selectedItemAttrs: [
|
|
192
|
+
'Title',
|
|
193
|
+
'Description',
|
|
194
|
+
'hasPreviewImage',
|
|
195
|
+
'headtitle',
|
|
196
|
+
],
|
|
197
|
+
allowExternals: true,
|
|
198
|
+
},
|
|
199
|
+
title: {
|
|
200
|
+
title: 'title',
|
|
201
|
+
},
|
|
202
|
+
description: {
|
|
203
|
+
title: 'description',
|
|
204
|
+
},
|
|
205
|
+
preview_image: {
|
|
206
|
+
title: 'Image Override',
|
|
207
|
+
widget: 'object_browser',
|
|
208
|
+
mode: 'image',
|
|
209
|
+
allowExternals: true,
|
|
210
|
+
},
|
|
211
|
+
extraDefault: {
|
|
212
|
+
title: 'Extra',
|
|
213
|
+
default: 'Extra default',
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
required: [],
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
config.blocks.blocksConfig.slider = {
|
|
221
|
+
id: 'slider',
|
|
222
|
+
title: 'Slider',
|
|
223
|
+
group: 'Special',
|
|
224
|
+
restricted: false,
|
|
225
|
+
mostUsed: false,
|
|
226
|
+
blockHasOwnFocusManagement: true,
|
|
227
|
+
blockSchema: (props) => ({
|
|
228
|
+
title: 'slider',
|
|
229
|
+
fieldsets: [
|
|
230
|
+
{
|
|
231
|
+
id: 'default',
|
|
232
|
+
title: 'Default',
|
|
233
|
+
fields: [
|
|
234
|
+
'slides',
|
|
235
|
+
'fieldAfterObjectList',
|
|
236
|
+
'href',
|
|
237
|
+
'firstWithDefault',
|
|
238
|
+
'style',
|
|
239
|
+
'anotherWithDefault',
|
|
240
|
+
'yetAnotherWithDefault',
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
properties: {
|
|
245
|
+
slides: {
|
|
246
|
+
widget: 'object_list',
|
|
247
|
+
schema: itemSchema,
|
|
248
|
+
default: [
|
|
249
|
+
{
|
|
250
|
+
'@id': 'asdasdasd-qweqwe-zxczxc',
|
|
251
|
+
extraDefault:
|
|
252
|
+
'Extra default (Manual in parent slider widget default)',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
fieldAfterObjectList: {
|
|
257
|
+
title: 'Field after OL',
|
|
258
|
+
},
|
|
259
|
+
href: {
|
|
260
|
+
widget: 'object_browser',
|
|
261
|
+
mode: 'link',
|
|
262
|
+
selectedItemAttrs: [
|
|
263
|
+
'Title',
|
|
264
|
+
'Description',
|
|
265
|
+
'hasPreviewImage',
|
|
266
|
+
'headtitle',
|
|
267
|
+
],
|
|
268
|
+
allowExternals: true,
|
|
269
|
+
},
|
|
270
|
+
firstWithDefault: {
|
|
271
|
+
title: 'Field with default',
|
|
272
|
+
default: 'Some default value',
|
|
273
|
+
},
|
|
274
|
+
style: {
|
|
275
|
+
widget: 'object',
|
|
276
|
+
schema: {
|
|
277
|
+
title: 'Style',
|
|
278
|
+
fieldsets: [
|
|
279
|
+
{
|
|
280
|
+
id: 'default',
|
|
281
|
+
fields: ['color', 'theme'],
|
|
282
|
+
title: 'Default',
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
properties: {
|
|
286
|
+
color: {
|
|
287
|
+
title: 'Color',
|
|
288
|
+
default: 'red',
|
|
289
|
+
},
|
|
290
|
+
theme: {
|
|
291
|
+
title: 'Theme',
|
|
292
|
+
default: 'primary',
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
required: [],
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
anotherWithDefault: {
|
|
299
|
+
title: 'Field with default 2',
|
|
300
|
+
default: 2,
|
|
301
|
+
type: 'number',
|
|
302
|
+
},
|
|
303
|
+
yetAnotherWithDefault: {
|
|
304
|
+
title: 'Field with default 3',
|
|
305
|
+
default: ['one', 'two'],
|
|
306
|
+
type: 'array',
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
required: [],
|
|
310
|
+
}),
|
|
311
|
+
};
|
|
312
|
+
|
|
168
313
|
config.settings.defaultBlockType = 'text';
|
|
169
314
|
|
|
170
315
|
describe('Blocks', () => {
|
|
@@ -508,6 +653,95 @@ describe('Blocks', () => {
|
|
|
508
653
|
description: 'already filled',
|
|
509
654
|
});
|
|
510
655
|
});
|
|
656
|
+
it('Sets data according to schema default values, top level and styling wrapper object field', () => {
|
|
657
|
+
const data = {
|
|
658
|
+
'@type': 'slider',
|
|
659
|
+
};
|
|
660
|
+
const schema = config.blocks.blocksConfig.slider.blockSchema({ data });
|
|
661
|
+
|
|
662
|
+
// if you don't pass down intl, the ObjectWidget defaults are not applied
|
|
663
|
+
expect(applySchemaDefaults({ schema, data })).toEqual({
|
|
664
|
+
'@type': 'slider',
|
|
665
|
+
anotherWithDefault: 2,
|
|
666
|
+
slides: [
|
|
667
|
+
{
|
|
668
|
+
'@id': 'asdasdasd-qweqwe-zxczxc',
|
|
669
|
+
extraDefault:
|
|
670
|
+
'Extra default (Manual in parent slider widget default)',
|
|
671
|
+
},
|
|
672
|
+
],
|
|
673
|
+
firstWithDefault: 'Some default value',
|
|
674
|
+
yetAnotherWithDefault: ['one', 'two'],
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
expect(applySchemaDefaults({ schema, data, intl: {} })).toEqual({
|
|
678
|
+
'@type': 'slider',
|
|
679
|
+
anotherWithDefault: 2,
|
|
680
|
+
slides: [
|
|
681
|
+
{
|
|
682
|
+
'@id': 'asdasdasd-qweqwe-zxczxc',
|
|
683
|
+
extraDefault:
|
|
684
|
+
'Extra default (Manual in parent slider widget default)',
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
firstWithDefault: 'Some default value',
|
|
688
|
+
style: {
|
|
689
|
+
color: 'red',
|
|
690
|
+
theme: 'primary',
|
|
691
|
+
},
|
|
692
|
+
yetAnotherWithDefault: ['one', 'two'],
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it('Sets data according to schema default values, keeps existing data', () => {
|
|
697
|
+
const schema = {
|
|
698
|
+
properties: {
|
|
699
|
+
style: {
|
|
700
|
+
widget: 'object',
|
|
701
|
+
schema: {
|
|
702
|
+
title: 'Style',
|
|
703
|
+
fieldsets: [
|
|
704
|
+
{
|
|
705
|
+
id: 'default',
|
|
706
|
+
fields: ['color', 'theme'],
|
|
707
|
+
title: 'Default',
|
|
708
|
+
},
|
|
709
|
+
],
|
|
710
|
+
properties: {
|
|
711
|
+
color: {
|
|
712
|
+
title: 'Color',
|
|
713
|
+
default: 'red',
|
|
714
|
+
},
|
|
715
|
+
theme: {
|
|
716
|
+
title: 'Theme',
|
|
717
|
+
default: 'primary',
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
required: [],
|
|
721
|
+
},
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
expect(
|
|
727
|
+
applySchemaDefaults({
|
|
728
|
+
schema,
|
|
729
|
+
data: {
|
|
730
|
+
'@type': 'slider',
|
|
731
|
+
style: {
|
|
732
|
+
theme: 'secondary',
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
intl: {},
|
|
736
|
+
}),
|
|
737
|
+
).toEqual({
|
|
738
|
+
'@type': 'slider',
|
|
739
|
+
style: {
|
|
740
|
+
color: 'red',
|
|
741
|
+
theme: 'secondary',
|
|
742
|
+
},
|
|
743
|
+
});
|
|
744
|
+
});
|
|
511
745
|
});
|
|
512
746
|
|
|
513
747
|
describe('applyBlockDefaults', () => {
|