datajunction-ui 0.0.1-rc.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.
Files changed (71) hide show
  1. package/.env +1 -0
  2. package/.env.local +4 -0
  3. package/.env.production +1 -0
  4. package/.eslintrc.js +20 -0
  5. package/.gitattributes +201 -0
  6. package/.github/pull_request_template.md +11 -0
  7. package/.github/workflows/ci.yml +33 -0
  8. package/.husky/pre-commit +6 -0
  9. package/.nvmrc +1 -0
  10. package/.prettierignore +4 -0
  11. package/.prettierrc +9 -0
  12. package/.stylelintrc +7 -0
  13. package/.vscode/extensions.json +8 -0
  14. package/.vscode/launch.json +15 -0
  15. package/.vscode/settings.json +25 -0
  16. package/Dockerfile +6 -0
  17. package/LICENSE +22 -0
  18. package/README.md +10 -0
  19. package/internals/testing/loadable.mock.tsx +6 -0
  20. package/package.json +150 -0
  21. package/public/favicon.ico +0 -0
  22. package/public/index.html +29 -0
  23. package/public/manifest.json +15 -0
  24. package/public/robots.txt +3 -0
  25. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +45 -0
  26. package/src/app/__tests__/index.test.tsx +14 -0
  27. package/src/app/components/ListGroupItem.jsx +17 -0
  28. package/src/app/components/NamespaceHeader.jsx +40 -0
  29. package/src/app/components/Tab.jsx +26 -0
  30. package/src/app/components/__tests__/ListGroupItem.test.tsx +16 -0
  31. package/src/app/components/__tests__/NamespaceHeader.test.jsx +14 -0
  32. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +26 -0
  33. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +63 -0
  34. package/src/app/components/djgraph/DJNode.jsx +111 -0
  35. package/src/app/components/djgraph/__tests__/DJNode.test.tsx +24 -0
  36. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +73 -0
  37. package/src/app/index.tsx +53 -0
  38. package/src/app/pages/ListNamespacesPage/Loadable.jsx +23 -0
  39. package/src/app/pages/ListNamespacesPage/index.jsx +53 -0
  40. package/src/app/pages/NamespacePage/Loadable.jsx +23 -0
  41. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +45 -0
  42. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +14 -0
  43. package/src/app/pages/NamespacePage/index.jsx +93 -0
  44. package/src/app/pages/NodePage/Loadable.jsx +23 -0
  45. package/src/app/pages/NodePage/NodeColumnTab.jsx +44 -0
  46. package/src/app/pages/NodePage/NodeGraphTab.jsx +160 -0
  47. package/src/app/pages/NodePage/NodeInfoTab.jsx +87 -0
  48. package/src/app/pages/NodePage/NodeStatus.jsx +34 -0
  49. package/src/app/pages/NodePage/index.jsx +100 -0
  50. package/src/app/pages/NotFoundPage/Loadable.tsx +14 -0
  51. package/src/app/pages/NotFoundPage/P.ts +8 -0
  52. package/src/app/pages/NotFoundPage/__tests__/__snapshots__/index.test.tsx.snap +61 -0
  53. package/src/app/pages/NotFoundPage/__tests__/index.test.tsx +21 -0
  54. package/src/app/pages/NotFoundPage/index.tsx +45 -0
  55. package/src/app/pages/Root/Loadable.tsx +23 -0
  56. package/src/app/pages/Root/assets/dj-logo.png +0 -0
  57. package/src/app/pages/Root/index.tsx +42 -0
  58. package/src/app/services/DJService.js +124 -0
  59. package/src/index.tsx +47 -0
  60. package/src/react-app-env.d.ts +4 -0
  61. package/src/reportWebVitals.ts +15 -0
  62. package/src/setupTests.ts +8 -0
  63. package/src/styles/dag-styles.ts +117 -0
  64. package/src/styles/global-styles.ts +588 -0
  65. package/src/styles/index.css +546 -0
  66. package/src/utils/__tests__/__snapshots__/loadable.test.tsx.snap +17 -0
  67. package/src/utils/__tests__/loadable.test.tsx +53 -0
  68. package/src/utils/__tests__/request.test.ts +82 -0
  69. package/src/utils/loadable.tsx +30 -0
  70. package/src/utils/request.ts +54 -0
  71. package/tsconfig.json +31 -0
@@ -0,0 +1,546 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue',
4
+ 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji',
5
+ 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
6
+ -webkit-font-smoothing: antialiased;
7
+ -moz-osx-font-smoothing: grayscale;
8
+ }
9
+ *,
10
+ ::after,
11
+ ::before {
12
+ box-sizing: border-box;
13
+ }
14
+ div {
15
+ display: block;
16
+ }
17
+ p {
18
+ display: block;
19
+ margin-block-start: 1em;
20
+ margin-block-end: 1em;
21
+ margin-inline-start: 0px;
22
+ margin-inline-end: 0px;
23
+ width: 100%;
24
+ }
25
+ a,
26
+ a:hover {
27
+ text-decoration: none;
28
+ font-family: 'Cerebri Sans', sans-serif;
29
+ }
30
+ code {
31
+ font-family: Consolas, source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
32
+ monospace;
33
+ }
34
+ .link-body-emphasis {
35
+ font-weight: normal;
36
+ color: #5e5e5e;
37
+ text-decoration: none;
38
+ }
39
+
40
+ .link-body-emphasis:hover {
41
+ color: #3f4254;
42
+ }
43
+
44
+ .link-table {
45
+ color: #3b506c;
46
+ font-weight: normal;
47
+ text-decoration: none;
48
+ }
49
+ .breadcrumb-chevron {
50
+ --bs-breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%236c757d'%3E%3Cpath fill-rule='evenodd' d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E");
51
+ gap: 0.5rem;
52
+ }
53
+ .breadcrumb-item + .breadcrumb-item::before {
54
+ float: left;
55
+ padding-right: var(--bs-breadcrumb-item-padding-x);
56
+ color: var(--bs-breadcrumb-divider-color);
57
+ content: var(--bs-breadcrumb-divider, '/');
58
+ }
59
+ .rounded-3 {
60
+ border-radius: 0.5rem !important;
61
+ }
62
+
63
+ .bg-body-tertiary {
64
+ --bs-bg-opacity: 1;
65
+ background-color: rgba(248, 249, 250, 1) !important;
66
+ }
67
+ .p-3 {
68
+ padding: 1rem !important;
69
+ }
70
+ .fw-semibold {
71
+ font-weight: 600 !important;
72
+ }
73
+ .breadcrumb {
74
+ --bs-breadcrumb-padding-x: 0;
75
+ --bs-breadcrumb-padding-y: 0;
76
+ --bs-breadcrumb-margin-bottom: 1rem;
77
+ --bs-breadcrumb-bg: #ffffff;
78
+ --bs-breadcrumb-border-radius: 0.5rem;
79
+ --bs-breadcrumb-divider-color: rgba(33, 37, 41, 0.75);
80
+ --bs-breadcrumb-item-padding-x: 0.5rem;
81
+ --bs-breadcrumb-item-active-color: rgba(33, 37, 41, 0.75);
82
+ display: flex;
83
+ flex-wrap: wrap;
84
+ padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);
85
+ margin-bottom: var(--bs-breadcrumb-margin-bottom);
86
+ list-style: none;
87
+ line-height: 1em;
88
+ background-color: var(--bs-breadcrumb-bg);
89
+ border-radius: var(--bs-breadcrumb-border-radius);
90
+ }
91
+ dl,
92
+ ol,
93
+ ul {
94
+ margin-top: 0;
95
+ margin-bottom: 1rem;
96
+ }
97
+
98
+ .list-group {
99
+ width: 100%;
100
+ /*min-width: fit-content;*/
101
+ max-width: fit-content;
102
+ /*margin-inline: 1.5rem;*/
103
+ border-radius: 0.375rem;
104
+ }
105
+ .list-group-item {
106
+ position: relative;
107
+ display: block;
108
+ padding: 0.5rem 1rem;
109
+ color: #212529;
110
+ text-decoration: none;
111
+ background-color: #ffffff;
112
+ /*border: 1px solid #dee2e6;*/
113
+ min-width: fit-content;
114
+ }
115
+ .list-group-item + .list-group-item {
116
+ border-top-width: 0;
117
+ }
118
+ .list-group-item:first-child {
119
+ border-top-left-radius: inherit;
120
+ border-top-right-radius: inherit;
121
+ }
122
+ .d-flex {
123
+ display: flex !important;
124
+ }
125
+ .gap-2 {
126
+ gap: 0.5rem !important;
127
+ }
128
+ .justify-content-between {
129
+ justify-content: space-between !important;
130
+ }
131
+ .w-100 {
132
+ width: 100% !important;
133
+ }
134
+ .mb-0 {
135
+ margin-bottom: 0 !important;
136
+ }
137
+ .h6,
138
+ h6 {
139
+ margin: 0;
140
+ /*background-color: #f9fbfd;*/
141
+ text-transform: uppercase;
142
+ font-size: 0.8125rem;
143
+ font-weight: 600;
144
+ letter-spacing: 0.08em;
145
+ color: #95aac9;
146
+ }
147
+ .py-3 {
148
+ padding-top: 1rem !important;
149
+ padding-bottom: 1rem !important;
150
+ }
151
+ .align-items-center {
152
+ align-items: center !important;
153
+ }
154
+ .p-4 {
155
+ padding: 1.5rem !important;
156
+ }
157
+ .flex-md-row {
158
+ flex-direction: row !important;
159
+ }
160
+ .py-md-5 {
161
+ padding-top: 3rem !important;
162
+ padding-bottom: 3rem !important;
163
+ }
164
+ .badge {
165
+ vertical-align: middle;
166
+ }
167
+ .badge.bg-secondary-soft {
168
+ color: #6e84a3;
169
+ }
170
+ .badge.rounded-pill {
171
+ padding-right: 0.6em;
172
+ padding-left: 0.6em;
173
+ }
174
+ .bg-secondary-soft {
175
+ background-color: #e2e6ed !important;
176
+ }
177
+ .rounded-pill {
178
+ border-radius: 50rem !important;
179
+ }
180
+ .badge {
181
+ --bs-badge-padding-x: 0.5em;
182
+ --bs-badge-padding-y: 0.33em;
183
+ --bs-badge-font-size: 76%;
184
+ --bs-badge-font-weight: 400;
185
+ --bs-badge-color: #fff;
186
+ --bs-badge-border-radius: 0.375rem;
187
+ display: inline-block;
188
+ padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);
189
+ font-size: var(--bs-badge-font-size);
190
+ font-weight: var(--bs-badge-font-weight);
191
+ line-height: 1;
192
+ color: var(--bs-badge-color);
193
+ text-align: center;
194
+ white-space: nowrap;
195
+ vertical-align: baseline;
196
+ border-radius: var(--bs-badge-border-radius);
197
+ }
198
+ table {
199
+ display: table;
200
+ border-collapse: collapse;
201
+ box-sizing: border-box;
202
+ text-indent: initial;
203
+ border-spacing: 2px;
204
+ border-color: gray;
205
+ caption-side: bottom;
206
+ }
207
+ tr {
208
+ display: table-row;
209
+ vertical-align: inherit;
210
+ border-color: inherit;
211
+ }
212
+ .card-table {
213
+ margin-bottom: 0;
214
+ width: 100%;
215
+ font-size: 0.8125rem;
216
+ color: #12263f;
217
+ vertical-align: top;
218
+ border-color: #12263f;
219
+ }
220
+
221
+ .card {
222
+ box-shadow: 0 0.75rem 1.5rem rgba(18, 38, 63, 0.03);
223
+ position: relative;
224
+ display: flex;
225
+ flex-direction: column;
226
+ min-width: 0;
227
+ word-wrap: break-word;
228
+ background-color: #fff;
229
+ background-clip: border-box;
230
+ /*border: 1px solid #edf2f9;*/
231
+ border-radius: 1rem;
232
+ margin: 4rem;
233
+ }
234
+
235
+ .table-responsive {
236
+ overflow-x: auto;
237
+ -webkit-overflow-scrolling: touch;
238
+ }
239
+ .table > thead {
240
+ vertical-align: bottom;
241
+ }
242
+
243
+ tbody,
244
+ td,
245
+ tfoot,
246
+ th,
247
+ thead,
248
+ tr {
249
+ border: 0 solid;
250
+ border-color: inherit;
251
+ }
252
+
253
+ thead {
254
+ display: table-header-group;
255
+ vertical-align: middle;
256
+ border-color: inherit;
257
+ }
258
+
259
+ tr {
260
+ display: table-row;
261
+ vertical-align: inherit;
262
+ border-color: inherit;
263
+ }
264
+
265
+ .card-table tbody td:first-child,
266
+ .card-table thead th:first-child {
267
+ padding-left: 1.5rem !important;
268
+ }
269
+ .table thead th {
270
+ background-color: #f9fbfd;
271
+ text-transform: uppercase;
272
+ font-size: 0.8125rem;
273
+ font-weight: 600;
274
+ letter-spacing: 0.08em;
275
+ color: #95aac9;
276
+ }
277
+
278
+ .card-table thead th {
279
+ border-top-width: 0;
280
+ }
281
+ .table thead th {
282
+ font-size: 0.625rem;
283
+ }
284
+ .table thead th,
285
+ td,
286
+ tbody th {
287
+ vertical-align: middle;
288
+ text-align: center;
289
+ }
290
+ .table [data-sort],
291
+ .table-nowrap td,
292
+ .table-nowrap th {
293
+ white-space: nowrap;
294
+ }
295
+ .table thead th {
296
+ background-color: #f9fbfd;
297
+ text-transform: uppercase;
298
+ font-size: 0.8125rem;
299
+ font-weight: 600;
300
+ letter-spacing: 0.08em;
301
+ color: #95aac9;
302
+ padding: 1rem;
303
+ }
304
+ .text-start {
305
+ text-align: left !important;
306
+ }
307
+ .card-inner-table {
308
+ margin-top: 2rem;
309
+ }
310
+ .card-inner-table thead th {
311
+ background-color: #fff;
312
+ }
313
+ .table td,
314
+ .table th {
315
+ border-top: 1px solid #edf2f9;
316
+ border-bottom: 0;
317
+ padding: 1rem;
318
+ }
319
+ .card-inner-table td,
320
+ .card-inner-table th {
321
+ border-top: 1px dashed #edf2f9;
322
+ }
323
+ .mid {
324
+ flex: 0 0 auto;
325
+ width: 100%;
326
+ }
327
+ .table > :not(caption) > * > * {
328
+ background-color: transparent;
329
+ border-bottom-width: 1px;
330
+ box-shadow: inset 0 0 0 9999px transparent;
331
+ }
332
+
333
+ /* Nodes */
334
+ .node_type {
335
+ background-color: #fad7dd !important;
336
+ color: #e63757;
337
+ text-transform: uppercase;
338
+ vertical-align: middle;
339
+ padding: 0.44rem;
340
+ border-radius: 0.375rem;
341
+ cursor: crosshair;
342
+ }
343
+
344
+ /* Nodes */
345
+ .node_type__source {
346
+ /*background-color: #84ea4d50!important;*/
347
+ /*color: #4e8033;*/
348
+
349
+ background-color: #ccf7e5 !important;
350
+ color: #00b368;
351
+ }
352
+
353
+ .node_type__transform {
354
+ background-color: #ccefff !important;
355
+ color: #0063b4;
356
+ }
357
+
358
+ .node_type__metric {
359
+ background-color: #fad7dd !important;
360
+ color: #a2283e;
361
+ }
362
+
363
+ .node_type__dimension {
364
+ background-color: #ffefd0 !important;
365
+ color: #a96621;
366
+ }
367
+
368
+ .node_type__cube {
369
+ background-color: #c2180750 !important;
370
+ color: #c21807;
371
+ }
372
+
373
+ .status__valid {
374
+ color: #00b368;
375
+ }
376
+
377
+ .status {
378
+ text-transform: capitalize;
379
+ vertical-align: middle;
380
+ text-align: center;
381
+ }
382
+
383
+ .align-items-center {
384
+ align-items: center !important;
385
+ }
386
+ .col {
387
+ flex: 0 1;
388
+ padding: 1.5rem;
389
+ }
390
+
391
+ .col.active {
392
+ /*background-color: #f5fbff;*/
393
+ padding-bottom: 1.15rem;
394
+ color: #a1a5b7;
395
+ border-bottom: solid 0.2em #2c7be5;
396
+ text-align: center;
397
+ }
398
+
399
+ .row > * {
400
+ flex-shrink: 0;
401
+ width: 100%;
402
+ max-width: 100%;
403
+ padding-right: calc(var(--bs-gutter-x) * 0.5);
404
+ padding-left: calc(var(--bs-gutter-x) * 0.5);
405
+ margin-top: var(--bs-gutter-y);
406
+ }
407
+ .row {
408
+ --bs-gutter-x: 1.5rem;
409
+ --bs-gutter-y: 0;
410
+ display: flex;
411
+ flex-wrap: wrap;
412
+ /*margin-top: 3rem;*/
413
+ /*margin-bottom: calc(-.75*var(--bs-gutter-x));*/
414
+ /*margin-left: 3.4rem;*/
415
+ }
416
+
417
+ .header {
418
+ --bs-header-margin-bottom: 1.5rem;
419
+ --bs-header-spacing-y: 1.5rem;
420
+ --bs-header-body-border-width: 1px;
421
+ --bs-header-body-border-color: #e3ebf6;
422
+ --bs-header-body-border-color-dark: rgba(227, 235, 246, 0.1);
423
+ /*margin-bottom: var(--bs-header-margin-bottom);*/
424
+ height: 90px;
425
+ transition: none;
426
+ display: flex;
427
+ align-items: center;
428
+ background-color: transparent;
429
+ /*box-shadow: 0px 10px 30px 0px rgba(82, 63, 105, 0.05);*/
430
+ /*border-bottom: var(--bs-app-header-base-border-bottom);*/
431
+ }
432
+ *,
433
+ :after,
434
+ :before {
435
+ box-sizing: border-box;
436
+ }
437
+ .header-tabs {
438
+ margin-bottom: calc(var(--bs-header-spacing-y) * -1);
439
+ border-bottom-width: 0;
440
+ }
441
+ .nav-tabs .nav-item:first-child {
442
+ margin-left: 0;
443
+ }
444
+ .nav-tabs .nav-item {
445
+ margin-left: 0.75rem;
446
+ margin-right: 0.75rem;
447
+ }
448
+ .nav-tabs .nav-item.show .nav-link,
449
+ .nav-tabs .nav-link.active {
450
+ color: #12263f;
451
+ background-color: transparent;
452
+ border-color: #2c7be5;
453
+ }
454
+ .header-tabs .nav-link {
455
+ /*padding-top: var(--bs-header-spacing-y);*/
456
+ /*padding-bottom: var(--bs-header-spacing-y);*/
457
+ }
458
+ .nav-link {
459
+ display: block;
460
+ color: #a1a5b7;
461
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
462
+ border-color 0.15s ease-in-out;
463
+ }
464
+ .nav-overflow {
465
+ display: flex;
466
+ flex-wrap: nowrap;
467
+ overflow-x: auto;
468
+ padding-bottom: 1px;
469
+ }
470
+
471
+ pre {
472
+ border-radius: 1rem;
473
+ padding-right: 2rem;
474
+ }
475
+
476
+ .container {
477
+ max-width: 1320px;
478
+ width: 100%;
479
+ padding-left: 30px !important;
480
+ padding-right: 30px !important;
481
+ }
482
+
483
+ .logo {
484
+ margin-right: 3.75rem !important;
485
+ flex-grow: 0 !important;
486
+ }
487
+ .logo img {
488
+ padding: 10px;
489
+ margin-bottom: -1.2rem;
490
+ }
491
+
492
+ .menu-lg-row > .menu-item {
493
+ display: flex;
494
+ align-items: center;
495
+ }
496
+ .menu-item {
497
+ display: block;
498
+ padding: 0.15rem 0;
499
+ }
500
+ .me-lg-2 {
501
+ margin-right: 0.5rem !important;
502
+ }
503
+ .menu,
504
+ .menu-wrapper {
505
+ display: flex;
506
+ padding: 0;
507
+ margin: 0;
508
+ list-style: none;
509
+ }
510
+ .menu-item .menu-link {
511
+ cursor: pointer;
512
+ align-items: center;
513
+ flex: 0 0 100%;
514
+ padding: 0.65rem 1rem;
515
+ transition: none;
516
+ outline: none !important;
517
+ font-family: Inter, Helvetica, sans-serif;
518
+ }
519
+ .menu-title a {
520
+ color: #7e8299;
521
+ }
522
+ .menu-title a:hover {
523
+ color: #3f4254;
524
+ }
525
+
526
+ .node__header,
527
+ .mid {
528
+ background-color: #f4f4f4;
529
+ padding-bottom: 2rem;
530
+ }
531
+
532
+ .rounded-box {
533
+ border-radius: 1rem !important;
534
+ background-color: #ffffff;
535
+ max-width: fit-content;
536
+ }
537
+ .card-header {
538
+ align-items: center;
539
+ margin: 0.5rem;
540
+ margin-left: 0;
541
+ padding: 0 2.25rem 0 2.25rem;
542
+ }
543
+
544
+ .text-gray-400 {
545
+ color: #b5b5c3 !important;
546
+ }
@@ -0,0 +1,17 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`loadable should render LazyComponent after waiting for it to load 1`] = `
4
+ <div>
5
+ My lazy-loaded component
6
+ </div>
7
+ `;
8
+
9
+ exports[`loadable should render fallback if given one 1`] = `
10
+ <div>
11
+ Loading
12
+ </div>
13
+ `;
14
+
15
+ exports[`loadable should render null by default 1`] = `null`;
16
+
17
+ exports[`loadable should render null by default with empty options 1`] = `null`;
@@ -0,0 +1,53 @@
1
+ import * as React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { lazyLoad } from 'utils/loadable';
4
+
5
+ const LoadingIndicator = () => <div>Loading</div>;
6
+
7
+ const LazyComponenWithDefaultExport = lazyLoad(
8
+ () => import('../../../internals/testing/loadable.mock'),
9
+ );
10
+
11
+ const LazyComponentWithExportedFunction = lazyLoad(
12
+ () => import('../../../internals/testing/loadable.mock'),
13
+ module => module.ExportedFunc,
14
+ );
15
+
16
+ const LazyComponentWithFallback = lazyLoad(
17
+ () => import('../../../internals/testing/loadable.mock'),
18
+ undefined,
19
+ {
20
+ fallback: <LoadingIndicator />,
21
+ },
22
+ );
23
+
24
+ describe('loadable', () => {
25
+ it('should render null by default', () => {
26
+ const {
27
+ container: { firstChild },
28
+ } = render(<LazyComponenWithDefaultExport />);
29
+ expect(firstChild).toMatchSnapshot();
30
+ });
31
+
32
+ it('should render null by default with empty options', () => {
33
+ const {
34
+ container: { firstChild },
35
+ } = render(<LazyComponentWithExportedFunction />);
36
+ expect(firstChild).toMatchSnapshot();
37
+ });
38
+
39
+ it('should render fallback if given one', () => {
40
+ const {
41
+ container: { firstChild },
42
+ } = render(<LazyComponentWithFallback />);
43
+ expect(firstChild).toMatchSnapshot();
44
+ });
45
+
46
+ it('should render LazyComponent after waiting for it to load', async () => {
47
+ const {
48
+ container: { firstChild },
49
+ } = render(<LazyComponentWithExportedFunction />);
50
+ LazyComponentWithExportedFunction({});
51
+ expect(firstChild).toMatchSnapshot();
52
+ });
53
+ });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Test the request function
3
+ */
4
+
5
+ import 'whatwg-fetch';
6
+ import { request } from '../request';
7
+
8
+ declare let window: { fetch: jest.Mock };
9
+
10
+ describe('request', () => {
11
+ // Before each test, stub the fetch function
12
+ beforeEach(() => {
13
+ window.fetch = jest.fn();
14
+ });
15
+
16
+ describe('stubbing successful response', () => {
17
+ // Before each test, pretend we got a successful response
18
+ beforeEach(() => {
19
+ const res = new Response('{"hello":"world"}', {
20
+ status: 200,
21
+ headers: {
22
+ 'Content-type': 'application/json',
23
+ },
24
+ });
25
+
26
+ window.fetch.mockReturnValue(Promise.resolve(res));
27
+ });
28
+
29
+ it('should format the response correctly', done => {
30
+ request('/thisurliscorrect')
31
+ .catch(done)
32
+ .then(json => {
33
+ expect(json.hello).toBe('world');
34
+ done();
35
+ });
36
+ });
37
+ });
38
+
39
+ describe('stubbing 204 response', () => {
40
+ // Before each test, pretend we got a successful response
41
+ beforeEach(() => {
42
+ const res = new Response('', {
43
+ status: 204,
44
+ statusText: 'No Content',
45
+ });
46
+
47
+ window.fetch.mockReturnValue(Promise.resolve(res));
48
+ });
49
+
50
+ it('should return null on 204 response', done => {
51
+ request('/thisurliscorrect')
52
+ .catch(done)
53
+ .then(json => {
54
+ expect(json).toBeNull();
55
+ done();
56
+ });
57
+ });
58
+ });
59
+
60
+ describe('stubbing error response', () => {
61
+ // Before each test, pretend we got an unsuccessful response
62
+ beforeEach(() => {
63
+ const res = new Response('', {
64
+ status: 404,
65
+ statusText: 'Not Found',
66
+ headers: {
67
+ 'Content-type': 'application/json',
68
+ },
69
+ });
70
+
71
+ window.fetch.mockReturnValue(Promise.resolve(res));
72
+ });
73
+
74
+ it('should catch errors', done => {
75
+ request('/thisdoesntexist').catch(err => {
76
+ expect(err.response.status).toBe(404);
77
+ expect(err.response.statusText).toBe('Not Found');
78
+ done();
79
+ });
80
+ });
81
+ });
82
+ });
@@ -0,0 +1,30 @@
1
+ import React, { lazy, Suspense } from 'react';
2
+
3
+ interface Opts {
4
+ fallback: React.ReactNode;
5
+ }
6
+ type Unpromisify<T> = T extends Promise<infer P> ? P : never;
7
+
8
+ export const lazyLoad = <
9
+ T extends Promise<any>,
10
+ U extends React.ComponentType<any>,
11
+ >(
12
+ importFunc: () => T,
13
+ selectorFunc?: (s: Unpromisify<T>) => U,
14
+ opts: Opts = { fallback: null },
15
+ ) => {
16
+ let lazyFactory: () => Promise<{ default: U }> = importFunc;
17
+
18
+ if (selectorFunc) {
19
+ lazyFactory = () =>
20
+ importFunc().then(module => ({ default: selectorFunc(module) }));
21
+ }
22
+
23
+ const LazyComponent = lazy(lazyFactory);
24
+
25
+ return (props: React.ComponentProps<U>): JSX.Element => (
26
+ <Suspense fallback={opts.fallback!}>
27
+ <LazyComponent {...props} />
28
+ </Suspense>
29
+ );
30
+ };