@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 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
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "14.0.0-alpha.25",
12
+ "version": "14.0.0-alpha.26",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -0,0 +1,9 @@
1
+ import { getQueryStringResults } from '@plone/volto/actions';
2
+
3
+ export default ({ dispatch, data, path }) => {
4
+ return [
5
+ dispatch(
6
+ getQueryStringResults(path, { ...data, fullobjects: 1 }, data.block),
7
+ ),
8
+ ];
9
+ };
@@ -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: { dispatch } }) =>
204
- __SERVER__ && dispatch(getContent(getBaseUrl(location.pathname))),
240
+ promise: ({ location, store }) =>
241
+ __SERVER__ && fetchContent({ store, location }),
205
242
  },
206
243
  {
207
244
  key: 'navigation',
@@ -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
- `;