@plone/volto 19.0.0-alpha.36 → 19.0.0-alpha.38

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.
Files changed (181) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +1 -1
  3. package/locales/af/LC_MESSAGES/volto.po +29 -3
  4. package/locales/af.json +1 -1
  5. package/locales/ar/LC_MESSAGES/volto.po +29 -3
  6. package/locales/ar.json +1 -1
  7. package/locales/bg/LC_MESSAGES/volto.po +29 -3
  8. package/locales/bg.json +1 -1
  9. package/locales/bn/LC_MESSAGES/volto.po +29 -3
  10. package/locales/bn.json +1 -1
  11. package/locales/ca/LC_MESSAGES/volto.po +32 -6
  12. package/locales/ca.json +1 -1
  13. package/locales/cs/LC_MESSAGES/volto.po +30 -4
  14. package/locales/cs.json +1 -1
  15. package/locales/cy/LC_MESSAGES/volto.po +29 -3
  16. package/locales/cy.json +1 -1
  17. package/locales/da/LC_MESSAGES/volto.po +29 -3
  18. package/locales/da.json +1 -1
  19. package/locales/de/LC_MESSAGES/volto.po +32 -6
  20. package/locales/de.json +1 -1
  21. package/locales/el/LC_MESSAGES/volto.po +29 -3
  22. package/locales/el.json +1 -1
  23. package/locales/en/LC_MESSAGES/volto.po +25 -0
  24. package/locales/en.json +1 -1
  25. package/locales/en_AU/LC_MESSAGES/volto.po +29 -3
  26. package/locales/en_AU.json +1 -1
  27. package/locales/en_GB/LC_MESSAGES/volto.po +29 -3
  28. package/locales/en_GB.json +1 -1
  29. package/locales/eo/LC_MESSAGES/volto.po +29 -3
  30. package/locales/eo.json +1 -1
  31. package/locales/es/LC_MESSAGES/volto.po +30 -5
  32. package/locales/es.json +1 -1
  33. package/locales/et/LC_MESSAGES/volto.po +29 -3
  34. package/locales/et.json +1 -1
  35. package/locales/eu/LC_MESSAGES/volto.po +30 -5
  36. package/locales/eu.json +1 -1
  37. package/locales/fa/LC_MESSAGES/volto.po +29 -3
  38. package/locales/fa.json +1 -1
  39. package/locales/fi/LC_MESSAGES/volto.po +30 -4
  40. package/locales/fi.json +1 -1
  41. package/locales/fr/LC_MESSAGES/volto.po +208 -183
  42. package/locales/fr.json +1 -1
  43. package/locales/fu/LC_MESSAGES/volto.po +29 -3
  44. package/locales/fu.json +1 -1
  45. package/locales/gl/LC_MESSAGES/volto.po +27 -2
  46. package/locales/gl.json +1 -1
  47. package/locales/he/LC_MESSAGES/volto.po +29 -3
  48. package/locales/he.json +1 -1
  49. package/locales/hi/LC_MESSAGES/volto.po +34 -8
  50. package/locales/hi.json +1 -1
  51. package/locales/hr/LC_MESSAGES/volto.po +30 -4
  52. package/locales/hr.json +1 -1
  53. package/locales/hu/LC_MESSAGES/volto.po +29 -3
  54. package/locales/hu.json +1 -1
  55. package/locales/hy/LC_MESSAGES/volto.po +29 -3
  56. package/locales/hy.json +1 -1
  57. package/locales/id/LC_MESSAGES/volto.po +29 -3
  58. package/locales/id.json +1 -1
  59. package/locales/it/LC_MESSAGES/volto.po +29 -4
  60. package/locales/it.json +1 -1
  61. package/locales/ja/LC_MESSAGES/volto.po +29 -3
  62. package/locales/ja.json +1 -1
  63. package/locales/ka/LC_MESSAGES/volto.po +29 -3
  64. package/locales/ka.json +1 -1
  65. package/locales/kn/LC_MESSAGES/volto.po +29 -3
  66. package/locales/kn.json +1 -1
  67. package/locales/ko/LC_MESSAGES/volto.po +29 -3
  68. package/locales/ko.json +1 -1
  69. package/locales/lt/LC_MESSAGES/volto.po +30 -4
  70. package/locales/lt.json +1 -1
  71. package/locales/lv/LC_MESSAGES/volto.po +29 -3
  72. package/locales/lv.json +1 -1
  73. package/locales/mi/LC_MESSAGES/volto.po +29 -3
  74. package/locales/mi.json +1 -1
  75. package/locales/mk/LC_MESSAGES/volto.po +29 -3
  76. package/locales/mk.json +1 -1
  77. package/locales/my/LC_MESSAGES/volto.po +29 -3
  78. package/locales/my.json +1 -1
  79. package/locales/nb_NO/LC_MESSAGES/volto.po +29 -3
  80. package/locales/nb_NO.json +1 -1
  81. package/locales/nl/LC_MESSAGES/volto.po +69 -43
  82. package/locales/nl.json +1 -1
  83. package/locales/nn/LC_MESSAGES/volto.po +29 -3
  84. package/locales/nn.json +1 -1
  85. package/locales/pl/LC_MESSAGES/volto.po +30 -4
  86. package/locales/pl.json +1 -1
  87. package/locales/pt/LC_MESSAGES/volto.po +30 -4
  88. package/locales/pt.json +1 -1
  89. package/locales/pt_BR/LC_MESSAGES/volto.po +54 -29
  90. package/locales/pt_BR.json +1 -1
  91. package/locales/rm/LC_MESSAGES/volto.po +29 -3
  92. package/locales/rm.json +1 -1
  93. package/locales/ro/LC_MESSAGES/volto.po +30 -5
  94. package/locales/ro.json +1 -1
  95. package/locales/ru/LC_MESSAGES/volto.po +30 -4
  96. package/locales/ru.json +1 -1
  97. package/locales/sk/LC_MESSAGES/volto.po +30 -4
  98. package/locales/sk.json +1 -1
  99. package/locales/sl/LC_MESSAGES/volto.po +29 -3
  100. package/locales/sl.json +1 -1
  101. package/locales/sm/LC_MESSAGES/volto.po +29 -3
  102. package/locales/sm.json +1 -1
  103. package/locales/sq/LC_MESSAGES/volto.po +29 -3
  104. package/locales/sq.json +1 -1
  105. package/locales/sr/LC_MESSAGES/volto.po +30 -4
  106. package/locales/sr.json +1 -1
  107. package/locales/sr@cyrl/LC_MESSAGES/volto.po +29 -3
  108. package/locales/sr@cyrl.json +1 -1
  109. package/locales/sr@latn/LC_MESSAGES/volto.po +29 -3
  110. package/locales/sr@latn.json +1 -1
  111. package/locales/sv/LC_MESSAGES/volto.po +31 -5
  112. package/locales/sv.json +1 -1
  113. package/locales/ta/LC_MESSAGES/volto.po +30 -5
  114. package/locales/ta.json +1 -1
  115. package/locales/te/LC_MESSAGES/volto.po +29 -3
  116. package/locales/te.json +1 -1
  117. package/locales/th/LC_MESSAGES/volto.po +29 -3
  118. package/locales/th.json +1 -1
  119. package/locales/to/LC_MESSAGES/volto.po +29 -3
  120. package/locales/to.json +1 -1
  121. package/locales/tr/LC_MESSAGES/volto.po +29 -4
  122. package/locales/tr.json +1 -1
  123. package/locales/uk/LC_MESSAGES/volto.po +30 -4
  124. package/locales/uk.json +1 -1
  125. package/locales/vi/LC_MESSAGES/volto.po +29 -3
  126. package/locales/vi.json +1 -1
  127. package/locales/volto.pot +26 -1
  128. package/locales/zh_CN/LC_MESSAGES/volto.po +29 -4
  129. package/locales/zh_CN.json +1 -1
  130. package/locales/zh_Hant/LC_MESSAGES/volto.po +29 -3
  131. package/locales/zh_Hant.json +1 -1
  132. package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +29 -3
  133. package/locales/zh_Hant_HK.json +1 -1
  134. package/package.json +9 -9
  135. package/src/components/manage/BlockChooser/BlockChooser.jsx +7 -10
  136. package/src/components/manage/Blocks/Block/Edit.jsx +19 -10
  137. package/src/components/manage/Blocks/Block/Order/Item.jsx +9 -4
  138. package/src/components/manage/Contents/DropZoneContent.jsx +1 -0
  139. package/src/components/manage/Controlpanels/BlockType.tsx +2 -3
  140. package/src/components/manage/Controlpanels/ContentTypes.jsx +9 -2
  141. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +58 -5
  142. package/src/components/manage/Controlpanels/Users/UsersControlpanel.ssr.test.jsx +624 -0
  143. package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +8 -0
  144. package/src/components/manage/Form/Form.jsx +6 -1
  145. package/src/components/manage/Form/ModalForm.jsx +164 -88
  146. package/src/components/manage/Sidebar/ObjectBrowser.jsx +7 -0
  147. package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +7 -3
  148. package/src/components/manage/Sidebar/ObjectBrowserBody.test.jsx +52 -0
  149. package/src/components/manage/Sidebar/Sidebar.jsx +2 -0
  150. package/src/components/manage/Toolbar/Toolbar.jsx +89 -7
  151. package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +1 -0
  152. package/src/components/manage/Widgets/TokenWidget.jsx +142 -186
  153. package/src/components/theme/Search/Search.jsx +230 -327
  154. package/src/components/theme/Search/Search.test.jsx +5 -10
  155. package/src/components/theme/Sitemap/Sitemap.jsx +22 -30
  156. package/src/components/theme/Sitemap/Sitemap.test.jsx +18 -0
  157. package/src/components/theme/Widgets/DateWidget.jsx +4 -5
  158. package/src/components/theme/Widgets/DatetimeWidget.jsx +4 -5
  159. package/src/components/theme/Widgets/RichTextWidget.jsx +1 -1
  160. package/src/config/index.js +1 -0
  161. package/src/helpers/I18n/I18n.test.ts +44 -0
  162. package/src/helpers/I18n/I18n.ts +31 -0
  163. package/src/helpers/Utils/Date.js +26 -1
  164. package/src/helpers/Utils/Date.test.js +237 -0
  165. package/src/helpers/index.js +1 -0
  166. package/theme/themes/pastanaga/collections/form.overrides +21 -0
  167. package/theme/themes/pastanaga/elements/button.overrides +30 -3
  168. package/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +1 -1
  169. package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +2 -6
  170. package/types/components/manage/Controlpanels/Users/UsersControlpanel.ssr.test.d.ts +1 -0
  171. package/types/components/manage/Controlpanels/index.d.ts +1 -1
  172. package/types/components/manage/Multilingual/ManageTranslations.d.ts +1 -1
  173. package/types/components/manage/Sidebar/ObjectBrowser.d.ts +1 -1
  174. package/types/components/manage/Sidebar/ObjectBrowserBody.test.d.ts +1 -0
  175. package/types/components/manage/Widgets/ImageWidget.d.ts +1 -1
  176. package/types/components/manage/Widgets/InternalUrlWidget.d.ts +1 -1
  177. package/types/components/manage/Widgets/UrlWidget.d.ts +1 -1
  178. package/types/components/manage/Widgets/index.d.ts +2 -2
  179. package/types/components/theme/Search/Search.d.ts +1 -1
  180. package/types/helpers/I18n/I18n.d.ts +20 -0
  181. package/types/helpers/index.d.ts +1 -0
@@ -0,0 +1,624 @@
1
+ import { renderToString } from 'react-dom/server';
2
+ import configureStore from 'redux-mock-store';
3
+ import { Provider } from 'react-intl-redux';
4
+ import { StaticRouter } from 'react-router-dom';
5
+ import config from '@plone/volto/registry';
6
+
7
+ import UsersControlpanel from './UsersControlpanel';
8
+
9
+ const mockStore = configureStore();
10
+
11
+ vi.mock('../../Toolbar/Toolbar', () => ({
12
+ default: vi.fn(() => null),
13
+ }));
14
+
15
+ vi.mock(
16
+ '@plone/volto/components/manage/Controlpanels/Users/RenderUsers',
17
+ () => ({
18
+ default: vi.fn(() => <tr data-testid="render-users-row" />),
19
+ }),
20
+ );
21
+
22
+ vi.mock('@plone/volto/icons/clear.svg', () => ({
23
+ __esModule: true,
24
+ default: 'clear-svg-mock',
25
+ }));
26
+
27
+ vi.mock('@plone/volto/icons/add-user.svg', () => ({
28
+ __esModule: true,
29
+ default: 'add-user-svg-mock',
30
+ }));
31
+
32
+ vi.mock('@plone/volto/icons/save.svg', () => ({
33
+ __esModule: true,
34
+ default: 'save-svg-mock',
35
+ }));
36
+
37
+ vi.mock('@plone/volto/icons/plone.svg', () => ({
38
+ __esModule: true,
39
+ default: 'plone-svg-mock',
40
+ }));
41
+
42
+ vi.mock('react-toastify', () => ({
43
+ toast: {
44
+ success: vi.fn(),
45
+ error: vi.fn(),
46
+ },
47
+ }));
48
+
49
+ beforeAll(() => {
50
+ config.views.errorViews = {
51
+ 401: () => <div className="error-401">Unauthorized</div>,
52
+ 403: () => <div className="error-403">Forbidden</div>,
53
+ 404: () => <div className="error-404">Not Found</div>,
54
+ corsError: () => <div className="error-cors">CORS Error</div>,
55
+ };
56
+ });
57
+
58
+ describe('UsersControlpanel SSR', () => {
59
+ const baseRouterState = {
60
+ router: {
61
+ location: { pathname: '/controlpanel/users' },
62
+ },
63
+ reduxAsyncConnect: {},
64
+ };
65
+
66
+ describe('SSR Unauthorized (401) rendering', () => {
67
+ it('renders 401 error during SSR without throwing', () => {
68
+ const staticContext = {};
69
+
70
+ const store = mockStore({
71
+ userSession: {
72
+ token: null,
73
+ },
74
+ roles: {
75
+ roles: [],
76
+ error: {
77
+ status: 401,
78
+ message: 'Unauthorized',
79
+ },
80
+ loading: false,
81
+ },
82
+ users: {
83
+ users: [],
84
+ create: { loading: false },
85
+ user: {},
86
+ },
87
+ groups: {
88
+ groups: [],
89
+ },
90
+ authRole: {
91
+ authenticatedRole: [],
92
+ },
93
+ controlpanels: {
94
+ controlpanel: {
95
+ data: {
96
+ many_users: false,
97
+ },
98
+ },
99
+ },
100
+ userschema: {},
101
+ intl: {
102
+ locale: 'en',
103
+ messages: {},
104
+ },
105
+ ...baseRouterState,
106
+ });
107
+
108
+ expect(() => {
109
+ renderToString(
110
+ <Provider store={store}>
111
+ <StaticRouter
112
+ location="/controlpanel/users"
113
+ context={staticContext}
114
+ >
115
+ <UsersControlpanel staticContext={staticContext} />
116
+ </StaticRouter>
117
+ </Provider>,
118
+ );
119
+ }).not.toThrow();
120
+ });
121
+
122
+ it('renders Error component with 401 status during SSR', () => {
123
+ const staticContext = {};
124
+
125
+ const store = mockStore({
126
+ userSession: {
127
+ token: null,
128
+ },
129
+ roles: {
130
+ roles: [],
131
+ error: {
132
+ status: 401,
133
+ message: 'Unauthorized',
134
+ },
135
+ loading: false,
136
+ },
137
+ users: {
138
+ users: [],
139
+ create: { loading: false },
140
+ user: {},
141
+ },
142
+ groups: {
143
+ groups: [],
144
+ },
145
+ authRole: {
146
+ authenticatedRole: [],
147
+ },
148
+ controlpanels: {
149
+ controlpanel: {
150
+ data: {
151
+ many_users: false,
152
+ },
153
+ },
154
+ },
155
+ userschema: {},
156
+ intl: {
157
+ locale: 'en',
158
+ messages: {},
159
+ },
160
+ ...baseRouterState,
161
+ });
162
+
163
+ const html = renderToString(
164
+ <Provider store={store}>
165
+ <StaticRouter location="/controlpanel/users" context={staticContext}>
166
+ <UsersControlpanel staticContext={staticContext} />
167
+ </StaticRouter>
168
+ </Provider>,
169
+ );
170
+
171
+ expect(html).toContain('error-401');
172
+ expect(html).toContain('Unauthorized');
173
+ });
174
+
175
+ it('passes staticContext to Error component', () => {
176
+ const staticContext = {};
177
+
178
+ const store = mockStore({
179
+ userSession: {
180
+ token: null,
181
+ },
182
+ roles: {
183
+ roles: [],
184
+ error: {
185
+ status: 401,
186
+ message: 'Unauthorized',
187
+ },
188
+ loading: false,
189
+ },
190
+ users: {
191
+ users: [],
192
+ create: { loading: false },
193
+ user: {},
194
+ },
195
+ groups: {
196
+ groups: [],
197
+ },
198
+ authRole: {
199
+ authenticatedRole: [],
200
+ },
201
+ controlpanels: {
202
+ controlpanel: {
203
+ data: {
204
+ many_users: false,
205
+ },
206
+ },
207
+ },
208
+ userschema: {},
209
+ intl: {
210
+ locale: 'en',
211
+ messages: {},
212
+ },
213
+ ...baseRouterState,
214
+ });
215
+
216
+ renderToString(
217
+ <Provider store={store}>
218
+ <StaticRouter location="/controlpanel/users" context={staticContext}>
219
+ <UsersControlpanel staticContext={staticContext} />
220
+ </StaticRouter>
221
+ </Provider>,
222
+ );
223
+
224
+ expect(staticContext).toBeDefined();
225
+ });
226
+
227
+ it('renders Error component that receives staticContext prop', () => {
228
+ const staticContext = {};
229
+
230
+ const store = mockStore({
231
+ userSession: {
232
+ token: null,
233
+ },
234
+ roles: {
235
+ roles: [],
236
+ error: {
237
+ status: 401,
238
+ message: 'Unauthorized',
239
+ },
240
+ loading: false,
241
+ },
242
+ users: {
243
+ users: [],
244
+ create: { loading: false },
245
+ user: {},
246
+ },
247
+ groups: {
248
+ groups: [],
249
+ },
250
+ authRole: {
251
+ authenticatedRole: [],
252
+ },
253
+ controlpanels: {
254
+ controlpanel: {
255
+ data: {
256
+ many_users: false,
257
+ },
258
+ },
259
+ },
260
+ userschema: {},
261
+ intl: {
262
+ locale: 'en',
263
+ messages: {},
264
+ },
265
+ ...baseRouterState,
266
+ });
267
+
268
+ const html = renderToString(
269
+ <Provider store={store}>
270
+ <StaticRouter location="/controlpanel/users" context={staticContext}>
271
+ <UsersControlpanel staticContext={staticContext} />
272
+ </StaticRouter>
273
+ </Provider>,
274
+ );
275
+
276
+ expect(html).toContain('error-401');
277
+ expect(html).toContain('Unauthorized');
278
+ expect(staticContext).toBeDefined();
279
+ });
280
+ });
281
+
282
+ describe('SSR does not render Error during loading', () => {
283
+ it('does not render Error when loadRolesRequest.loading is true', () => {
284
+ const staticContext = {};
285
+
286
+ const store = mockStore({
287
+ userSession: {
288
+ token: null,
289
+ },
290
+ roles: {
291
+ roles: [],
292
+ error: {
293
+ status: 401,
294
+ message: 'Unauthorized',
295
+ },
296
+ loading: true,
297
+ },
298
+ users: {
299
+ users: [],
300
+ create: { loading: false },
301
+ user: {},
302
+ },
303
+ groups: {
304
+ groups: [],
305
+ },
306
+ authRole: {
307
+ authenticatedRole: [],
308
+ },
309
+ controlpanels: {
310
+ controlpanel: {
311
+ data: {
312
+ many_users: false,
313
+ },
314
+ },
315
+ },
316
+ userschema: {},
317
+ intl: {
318
+ locale: 'en',
319
+ messages: {},
320
+ },
321
+ ...baseRouterState,
322
+ });
323
+
324
+ const html = renderToString(
325
+ <Provider store={store}>
326
+ <StaticRouter location="/controlpanel/users" context={staticContext}>
327
+ <UsersControlpanel staticContext={staticContext} />
328
+ </StaticRouter>
329
+ </Provider>,
330
+ );
331
+
332
+ expect(html).not.toContain('error-401');
333
+ expect(html).not.toContain('Unauthorized');
334
+ });
335
+
336
+ it('does not throw during SSR when loadRolesRequest is loading', () => {
337
+ const staticContext = {};
338
+
339
+ const store = mockStore({
340
+ userSession: {
341
+ token: null,
342
+ },
343
+ roles: {
344
+ roles: [],
345
+ error: {
346
+ status: 401,
347
+ message: 'Unauthorized',
348
+ },
349
+ loading: true,
350
+ },
351
+ users: {
352
+ users: [],
353
+ create: { loading: false },
354
+ user: {},
355
+ },
356
+ groups: {
357
+ groups: [],
358
+ },
359
+ authRole: {
360
+ authenticatedRole: [],
361
+ },
362
+ controlpanels: {
363
+ controlpanel: {
364
+ data: {
365
+ many_users: false,
366
+ },
367
+ },
368
+ },
369
+ userschema: {},
370
+ intl: {
371
+ locale: 'en',
372
+ messages: {},
373
+ },
374
+ ...baseRouterState,
375
+ });
376
+
377
+ expect(() => {
378
+ renderToString(
379
+ <Provider store={store}>
380
+ <StaticRouter
381
+ location="/controlpanel/users"
382
+ context={staticContext}
383
+ >
384
+ <UsersControlpanel staticContext={staticContext} />
385
+ </StaticRouter>
386
+ </Provider>,
387
+ );
388
+ }).not.toThrow();
389
+ });
390
+ });
391
+
392
+ describe('SSR without browser APIs', () => {
393
+ it('renders without window/document dependencies', () => {
394
+ const staticContext = {};
395
+
396
+ const store = mockStore({
397
+ userSession: {
398
+ token: null,
399
+ },
400
+ roles: {
401
+ roles: [],
402
+ error: null,
403
+ loading: false,
404
+ },
405
+ users: {
406
+ users: [],
407
+ create: { loading: false },
408
+ user: {},
409
+ },
410
+ groups: {
411
+ groups: [],
412
+ },
413
+ authRole: {
414
+ authenticatedRole: [],
415
+ },
416
+ controlpanels: {
417
+ controlpanel: {
418
+ data: {
419
+ many_users: false,
420
+ },
421
+ },
422
+ },
423
+ userschema: {
424
+ loaded: true,
425
+ userschema: {
426
+ fieldsets: [{ fields: ['username'] }],
427
+ properties: {
428
+ username: { type: 'string' },
429
+ },
430
+ },
431
+ },
432
+ intl: {
433
+ locale: 'en',
434
+ messages: {},
435
+ },
436
+ ...baseRouterState,
437
+ });
438
+
439
+ expect(() => {
440
+ renderToString(
441
+ <Provider store={store}>
442
+ <StaticRouter
443
+ location="/controlpanel/users"
444
+ context={staticContext}
445
+ >
446
+ <UsersControlpanel staticContext={staticContext} />
447
+ </StaticRouter>
448
+ </Provider>,
449
+ );
450
+ }).not.toThrow();
451
+ });
452
+
453
+ it('works in pure Node.js environment without DOM', () => {
454
+ const staticContext = {};
455
+
456
+ const store = mockStore({
457
+ userSession: {
458
+ token: null,
459
+ },
460
+ roles: {
461
+ roles: [],
462
+ error: {
463
+ status: 401,
464
+ message: 'Unauthorized',
465
+ },
466
+ loading: false,
467
+ },
468
+ users: {
469
+ users: [],
470
+ create: { loading: false },
471
+ user: {},
472
+ },
473
+ groups: {
474
+ groups: [],
475
+ },
476
+ authRole: {
477
+ authenticatedRole: [],
478
+ },
479
+ controlpanels: {
480
+ controlpanel: {
481
+ data: {
482
+ many_users: false,
483
+ },
484
+ },
485
+ },
486
+ userschema: {},
487
+ intl: {
488
+ locale: 'en',
489
+ messages: {},
490
+ },
491
+ ...baseRouterState,
492
+ });
493
+
494
+ let threwError = false;
495
+ let errorMessage = '';
496
+
497
+ try {
498
+ renderToString(
499
+ <Provider store={store}>
500
+ <StaticRouter
501
+ location="/controlpanel/users"
502
+ context={staticContext}
503
+ >
504
+ <UsersControlpanel staticContext={staticContext} />
505
+ </StaticRouter>
506
+ </Provider>,
507
+ );
508
+ } catch (error) {
509
+ threwError = true;
510
+ errorMessage = error.message || String(error);
511
+ }
512
+
513
+ expect(threwError).toBe(false);
514
+ expect(errorMessage).toBe('');
515
+ });
516
+ });
517
+
518
+ describe('effectiveError logic for SSR', () => {
519
+ it('renders Error when loadRolesRequest has error and not loading', () => {
520
+ const staticContext = {};
521
+
522
+ const store = mockStore({
523
+ userSession: {
524
+ token: null,
525
+ },
526
+ roles: {
527
+ roles: [],
528
+ error: {
529
+ status: 403,
530
+ message: 'Forbidden',
531
+ },
532
+ loading: false,
533
+ },
534
+ users: {
535
+ users: [],
536
+ create: { loading: false },
537
+ user: {},
538
+ },
539
+ groups: {
540
+ groups: [],
541
+ },
542
+ authRole: {
543
+ authenticatedRole: [],
544
+ },
545
+ controlpanels: {
546
+ controlpanel: {
547
+ data: {
548
+ many_users: false,
549
+ },
550
+ },
551
+ },
552
+ userschema: {},
553
+ intl: {
554
+ locale: 'en',
555
+ messages: {},
556
+ },
557
+ ...baseRouterState,
558
+ });
559
+
560
+ const html = renderToString(
561
+ <Provider store={store}>
562
+ <StaticRouter location="/controlpanel/users" context={staticContext}>
563
+ <UsersControlpanel staticContext={staticContext} />
564
+ </StaticRouter>
565
+ </Provider>,
566
+ );
567
+
568
+ expect(html).toContain('error-403') ||
569
+ expect(html).toContain('error-404');
570
+ });
571
+
572
+ it('passes staticContext to Error for 401 response', () => {
573
+ const staticContext = {};
574
+
575
+ const store = mockStore({
576
+ userSession: {
577
+ token: null,
578
+ },
579
+ roles: {
580
+ roles: [],
581
+ error: {
582
+ status: 401,
583
+ message: 'Unauthorized',
584
+ },
585
+ loading: false,
586
+ },
587
+ users: {
588
+ users: [],
589
+ create: { loading: false },
590
+ user: {},
591
+ },
592
+ groups: {
593
+ groups: [],
594
+ },
595
+ authRole: {
596
+ authenticatedRole: [],
597
+ },
598
+ controlpanels: {
599
+ controlpanel: {
600
+ data: {
601
+ many_users: false,
602
+ },
603
+ },
604
+ },
605
+ userschema: {},
606
+ intl: {
607
+ locale: 'en',
608
+ messages: {},
609
+ },
610
+ ...baseRouterState,
611
+ });
612
+
613
+ renderToString(
614
+ <Provider store={store}>
615
+ <StaticRouter location="/controlpanel/users" context={staticContext}>
616
+ <UsersControlpanel staticContext={staticContext} />
617
+ </StaticRouter>
618
+ </Provider>,
619
+ );
620
+
621
+ expect(staticContext).toBeDefined();
622
+ });
623
+ });
624
+ });
@@ -39,6 +39,10 @@ describe('UsersControlpanel', () => {
39
39
  locale: 'en',
40
40
  messages: {},
41
41
  },
42
+ router: {
43
+ location: { pathname: '/controlpanel/users' },
44
+ },
45
+ reduxAsyncConnect: {},
42
46
  });
43
47
  const { container } = render(
44
48
  <Provider store={store}>
@@ -85,6 +89,10 @@ describe('UsersControlpanel', () => {
85
89
  locale: 'en',
86
90
  messages: {},
87
91
  },
92
+ router: {
93
+ location: { pathname: '/controlpanel/users' },
94
+ },
95
+ reduxAsyncConnect: {},
88
96
  });
89
97
 
90
98
  render(
@@ -972,7 +972,12 @@ class Form extends Component {
972
972
  onTabChange={this.onTabChange}
973
973
  activeIndex={this.state.activeIndex}
974
974
  panes={map(schema.fieldsets, (item) => ({
975
- menuItem: item.title,
975
+ menuItem: {
976
+ key: item.id,
977
+ content: item.title,
978
+ as: 'button',
979
+ type: 'button',
980
+ },
976
981
  render: () => [
977
982
  !settings.verticalFormTabs && this.props.title && (
978
983
  <Segment secondary attached key={this.props.title}>