@plone/volto 14.0.0-alpha.25 → 14.0.0-alpha.26
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 +6 -0
- package/package.json +1 -1
- package/src/components/manage/Blocks/Listing/getAsyncData.js +9 -0
- package/src/components/theme/App/App.jsx +39 -2
- package/src/config/Blocks.jsx +2 -0
- package/src/helpers/Blocks/Blocks.js +22 -0
- package/src/helpers/Blocks/Blocks.test.js +61 -0
- package/src/components/manage/History/__snapshots__/History.test.jsx.snap +0 -216
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 14.0.0-alpha.26 (2021-11-01)
|
|
4
|
+
|
|
5
|
+
### Feature
|
|
6
|
+
|
|
7
|
+
- Provide Server-Side Rendering capabilities for blocks with async-based content (such as the listing block). A block needs to provide its own `getAsyncData` implementation, which is similar to an `asyncConnect` wrapper promise. @tiberiuichim @sneridagh
|
|
8
|
+
|
|
3
9
|
## 14.0.0-alpha.25 (2021-11-01)
|
|
4
10
|
|
|
5
11
|
### Feature
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@ import trim from 'lodash/trim';
|
|
|
18
18
|
import cx from 'classnames';
|
|
19
19
|
import config from '@plone/volto/registry';
|
|
20
20
|
import { PluggablesProvider } from '@plone/volto/components/manage/Pluggable';
|
|
21
|
+
import { visitBlocks } from '@plone/volto/helpers/Blocks/Blocks';
|
|
21
22
|
|
|
22
23
|
import Error from '@plone/volto/error';
|
|
23
24
|
|
|
@@ -191,6 +192,42 @@ export const __test__ = connect(
|
|
|
191
192
|
{},
|
|
192
193
|
)(App);
|
|
193
194
|
|
|
195
|
+
export const fetchContent = async ({ store, location }) => {
|
|
196
|
+
const content = await store.dispatch(
|
|
197
|
+
getContent(getBaseUrl(location.pathname)),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const promises = [];
|
|
201
|
+
const { blocksConfig } = config.blocks;
|
|
202
|
+
|
|
203
|
+
const visitor = ([id, data]) => {
|
|
204
|
+
const blockType = data['@type'];
|
|
205
|
+
const { getAsyncData } = blocksConfig[blockType];
|
|
206
|
+
if (getAsyncData) {
|
|
207
|
+
const p = getAsyncData({
|
|
208
|
+
store,
|
|
209
|
+
dispatch: store.dispatch,
|
|
210
|
+
path: location.pathname,
|
|
211
|
+
location,
|
|
212
|
+
id,
|
|
213
|
+
data,
|
|
214
|
+
});
|
|
215
|
+
if (!p?.length) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
'You should return a list of promises from getAsyncData',
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
promises.push(...p);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
visitBlocks(content, visitor);
|
|
225
|
+
|
|
226
|
+
await Promise.allSettled(promises);
|
|
227
|
+
|
|
228
|
+
return content;
|
|
229
|
+
};
|
|
230
|
+
|
|
194
231
|
export default compose(
|
|
195
232
|
asyncConnect([
|
|
196
233
|
{
|
|
@@ -200,8 +237,8 @@ export default compose(
|
|
|
200
237
|
},
|
|
201
238
|
{
|
|
202
239
|
key: 'content',
|
|
203
|
-
promise: ({ location, store
|
|
204
|
-
__SERVER__ &&
|
|
240
|
+
promise: ({ location, store }) =>
|
|
241
|
+
__SERVER__ && fetchContent({ store, location }),
|
|
205
242
|
},
|
|
206
243
|
{
|
|
207
244
|
key: 'navigation',
|
package/src/config/Blocks.jsx
CHANGED
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
SelectFacet,
|
|
58
58
|
CheckboxFacet,
|
|
59
59
|
} from '@plone/volto/components/manage/Blocks/Search/components';
|
|
60
|
+
import getListingBlockAsyncData from '@plone/volto/components/manage/Blocks/Listing/getAsyncData';
|
|
60
61
|
|
|
61
62
|
defineMessages({
|
|
62
63
|
title: {
|
|
@@ -278,6 +279,7 @@ const blocksConfig = {
|
|
|
278
279
|
template: SummaryListingBlockTemplate,
|
|
279
280
|
},
|
|
280
281
|
],
|
|
282
|
+
getAsyncData: getListingBlockAsyncData,
|
|
281
283
|
},
|
|
282
284
|
video: {
|
|
283
285
|
id: 'video',
|
|
@@ -337,3 +337,25 @@ export function emptyBlocksForm() {
|
|
|
337
337
|
blocks_layout: { items: [id] },
|
|
338
338
|
};
|
|
339
339
|
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Recursively discover blocks in data and call the provided callback
|
|
343
|
+
*/
|
|
344
|
+
export function visitBlocks(content, callback) {
|
|
345
|
+
const queue = getBlocks(content);
|
|
346
|
+
while (queue.length > 0) {
|
|
347
|
+
const [id, blockdata] = queue.shift();
|
|
348
|
+
callback([id, blockdata]);
|
|
349
|
+
|
|
350
|
+
// assumes that a block value is like: {blocks, blocks_layout} or
|
|
351
|
+
// { data: {blocks, blocks_layout}}
|
|
352
|
+
if (Object.keys(blockdata || {}).indexOf('blocks') > -1) {
|
|
353
|
+
queue.push(...getBlocks(blockdata));
|
|
354
|
+
// getBlocks(blockdata).forEach((tuple) => queue.push(tuple));
|
|
355
|
+
}
|
|
356
|
+
if (Object.keys(blockdata?.data || {}).indexOf('blocks') > -1) {
|
|
357
|
+
queue.push(...getBlocks(blockdata.data));
|
|
358
|
+
// getBlocks(blockdata.data).forEach((tuple) => queue.push(tuple));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
mutateBlock,
|
|
14
14
|
nextBlockId,
|
|
15
15
|
previousBlockId,
|
|
16
|
+
visitBlocks,
|
|
16
17
|
} from './Blocks';
|
|
17
18
|
|
|
18
19
|
import config from '@plone/volto/registry';
|
|
@@ -300,4 +301,64 @@ describe('Blocks', () => {
|
|
|
300
301
|
).toBe('b');
|
|
301
302
|
});
|
|
302
303
|
});
|
|
304
|
+
|
|
305
|
+
describe('visitBlocks', () => {
|
|
306
|
+
it('visit blocks', () => {
|
|
307
|
+
const d = {
|
|
308
|
+
data: {
|
|
309
|
+
blocks: {
|
|
310
|
+
'1': {
|
|
311
|
+
blocks: {
|
|
312
|
+
'2': {},
|
|
313
|
+
'3': {
|
|
314
|
+
data: {
|
|
315
|
+
blocks: {
|
|
316
|
+
'11': {},
|
|
317
|
+
'12': {},
|
|
318
|
+
'13': {},
|
|
319
|
+
},
|
|
320
|
+
blocks_layout: {
|
|
321
|
+
items: ['11', '12', '13'],
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
'7': {
|
|
326
|
+
blocks: {
|
|
327
|
+
'8': {},
|
|
328
|
+
'9': {},
|
|
329
|
+
'10': {},
|
|
330
|
+
},
|
|
331
|
+
blocks_layout: {
|
|
332
|
+
items: ['8', '9', '10'],
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
blocks_layout: {
|
|
337
|
+
items: ['2', '3', '7'],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
'4': {
|
|
341
|
+
blocks: {
|
|
342
|
+
'5': {},
|
|
343
|
+
'6': {},
|
|
344
|
+
},
|
|
345
|
+
blocks_layout: {
|
|
346
|
+
items: ['5', '6'],
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
blocks_layout: {
|
|
351
|
+
items: ['1', '4'],
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const a = [];
|
|
357
|
+
visitBlocks(d.data, (x) => {
|
|
358
|
+
a.push(x);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
expect(a.length).toBe(13);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
303
364
|
});
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`History renders a history component 1`] = `
|
|
4
|
-
<div
|
|
5
|
-
className="ui container"
|
|
6
|
-
id="page-history"
|
|
7
|
-
>
|
|
8
|
-
<div
|
|
9
|
-
className="ui raised segments"
|
|
10
|
-
>
|
|
11
|
-
<div
|
|
12
|
-
className="ui segment primary"
|
|
13
|
-
>
|
|
14
|
-
History of
|
|
15
|
-
<q>
|
|
16
|
-
Blog
|
|
17
|
-
</q>
|
|
18
|
-
</div>
|
|
19
|
-
<div
|
|
20
|
-
className="ui secondary segment"
|
|
21
|
-
>
|
|
22
|
-
You can view the history of your item below.
|
|
23
|
-
</div>
|
|
24
|
-
<table
|
|
25
|
-
className="ui selectable single line attached compact table"
|
|
26
|
-
>
|
|
27
|
-
<thead
|
|
28
|
-
className=""
|
|
29
|
-
>
|
|
30
|
-
<tr
|
|
31
|
-
className=""
|
|
32
|
-
>
|
|
33
|
-
<th
|
|
34
|
-
className="one wide"
|
|
35
|
-
>
|
|
36
|
-
#
|
|
37
|
-
</th>
|
|
38
|
-
<th
|
|
39
|
-
className="four wide"
|
|
40
|
-
>
|
|
41
|
-
What
|
|
42
|
-
</th>
|
|
43
|
-
<th
|
|
44
|
-
className="four wide"
|
|
45
|
-
>
|
|
46
|
-
Who
|
|
47
|
-
</th>
|
|
48
|
-
<th
|
|
49
|
-
className="four wide"
|
|
50
|
-
>
|
|
51
|
-
When
|
|
52
|
-
</th>
|
|
53
|
-
<th
|
|
54
|
-
className="four wide"
|
|
55
|
-
>
|
|
56
|
-
Change Note
|
|
57
|
-
</th>
|
|
58
|
-
<th
|
|
59
|
-
className=""
|
|
60
|
-
/>
|
|
61
|
-
</tr>
|
|
62
|
-
</thead>
|
|
63
|
-
<tbody
|
|
64
|
-
className=""
|
|
65
|
-
>
|
|
66
|
-
<tr
|
|
67
|
-
className=""
|
|
68
|
-
>
|
|
69
|
-
<td
|
|
70
|
-
className=""
|
|
71
|
-
>
|
|
72
|
-
<span />
|
|
73
|
-
</td>
|
|
74
|
-
<td
|
|
75
|
-
className=""
|
|
76
|
-
>
|
|
77
|
-
<span>
|
|
78
|
-
Publish
|
|
79
|
-
(Private → Published)
|
|
80
|
-
</span>
|
|
81
|
-
</td>
|
|
82
|
-
<td
|
|
83
|
-
className=""
|
|
84
|
-
>
|
|
85
|
-
Web Admin
|
|
86
|
-
</td>
|
|
87
|
-
<td
|
|
88
|
-
className=""
|
|
89
|
-
>
|
|
90
|
-
<span
|
|
91
|
-
title="Sunday, April 23, 2017 3:38 AM"
|
|
92
|
-
>
|
|
93
|
-
a few seconds ago
|
|
94
|
-
</span>
|
|
95
|
-
</td>
|
|
96
|
-
<td
|
|
97
|
-
className=""
|
|
98
|
-
>
|
|
99
|
-
|
|
100
|
-
</td>
|
|
101
|
-
<td
|
|
102
|
-
className=""
|
|
103
|
-
/>
|
|
104
|
-
</tr>
|
|
105
|
-
<tr
|
|
106
|
-
className=""
|
|
107
|
-
>
|
|
108
|
-
<td
|
|
109
|
-
className=""
|
|
110
|
-
>
|
|
111
|
-
<span />
|
|
112
|
-
</td>
|
|
113
|
-
<td
|
|
114
|
-
className=""
|
|
115
|
-
>
|
|
116
|
-
<span>
|
|
117
|
-
Edited
|
|
118
|
-
</span>
|
|
119
|
-
</td>
|
|
120
|
-
<td
|
|
121
|
-
className=""
|
|
122
|
-
>
|
|
123
|
-
Web Admin
|
|
124
|
-
</td>
|
|
125
|
-
<td
|
|
126
|
-
className=""
|
|
127
|
-
>
|
|
128
|
-
<span
|
|
129
|
-
title="Sunday, April 23, 2017 3:38 AM"
|
|
130
|
-
>
|
|
131
|
-
a few seconds ago
|
|
132
|
-
</span>
|
|
133
|
-
</td>
|
|
134
|
-
<td
|
|
135
|
-
className=""
|
|
136
|
-
>
|
|
137
|
-
Changed text
|
|
138
|
-
</td>
|
|
139
|
-
<td
|
|
140
|
-
className=""
|
|
141
|
-
>
|
|
142
|
-
<div
|
|
143
|
-
aria-expanded={false}
|
|
144
|
-
className="ui dropdown"
|
|
145
|
-
onBlur={[Function]}
|
|
146
|
-
onChange={[Function]}
|
|
147
|
-
onClick={[Function]}
|
|
148
|
-
onFocus={[Function]}
|
|
149
|
-
onMouseDown={[Function]}
|
|
150
|
-
role="listbox"
|
|
151
|
-
tabIndex={0}
|
|
152
|
-
>
|
|
153
|
-
<div
|
|
154
|
-
aria-atomic={true}
|
|
155
|
-
aria-live="polite"
|
|
156
|
-
className="text"
|
|
157
|
-
role="alert"
|
|
158
|
-
/>
|
|
159
|
-
<i
|
|
160
|
-
aria-hidden="true"
|
|
161
|
-
className="ellipsis horizontal icon"
|
|
162
|
-
onClick={[Function]}
|
|
163
|
-
/>
|
|
164
|
-
<div
|
|
165
|
-
className="menu transition left"
|
|
166
|
-
/>
|
|
167
|
-
</div>
|
|
168
|
-
</td>
|
|
169
|
-
</tr>
|
|
170
|
-
<tr
|
|
171
|
-
className=""
|
|
172
|
-
>
|
|
173
|
-
<td
|
|
174
|
-
className=""
|
|
175
|
-
>
|
|
176
|
-
<span />
|
|
177
|
-
</td>
|
|
178
|
-
<td
|
|
179
|
-
className=""
|
|
180
|
-
>
|
|
181
|
-
<span>
|
|
182
|
-
Create
|
|
183
|
-
(Private)
|
|
184
|
-
</span>
|
|
185
|
-
</td>
|
|
186
|
-
<td
|
|
187
|
-
className=""
|
|
188
|
-
>
|
|
189
|
-
Web Admin
|
|
190
|
-
</td>
|
|
191
|
-
<td
|
|
192
|
-
className=""
|
|
193
|
-
>
|
|
194
|
-
<span
|
|
195
|
-
title="Sunday, April 23, 2017 3:38 AM"
|
|
196
|
-
>
|
|
197
|
-
a few seconds ago
|
|
198
|
-
</span>
|
|
199
|
-
</td>
|
|
200
|
-
<td
|
|
201
|
-
className=""
|
|
202
|
-
>
|
|
203
|
-
|
|
204
|
-
</td>
|
|
205
|
-
<td
|
|
206
|
-
className=""
|
|
207
|
-
/>
|
|
208
|
-
</tr>
|
|
209
|
-
</tbody>
|
|
210
|
-
</table>
|
|
211
|
-
</div>
|
|
212
|
-
<div
|
|
213
|
-
id="Portal"
|
|
214
|
-
/>
|
|
215
|
-
</div>
|
|
216
|
-
`;
|