@cdc/dashboard 1.1.2 → 9.22.9

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 (36) hide show
  1. package/dist/cdcdashboard.js +159 -48
  2. package/examples/default-filter-control.json +175 -0
  3. package/examples/default-multi-dataset.json +498 -0
  4. package/examples/default.json +36 -348
  5. package/examples/private/chart-issue.json +3467 -0
  6. package/examples/private/no-issue.json +3467 -0
  7. package/examples/private/totals-two.json +104 -0
  8. package/examples/private/totals.json +103 -0
  9. package/examples/temp-example-data.json +130 -0
  10. package/examples/test-example.json +1 -0
  11. package/package.json +14 -8
  12. package/src/CdcDashboard.js +282 -156
  13. package/src/CdcDashboard.jsx +668 -0
  14. package/src/{context.tsx → ConfigContext.js} +0 -0
  15. package/src/components/{Column.js → Column.jsx} +9 -7
  16. package/src/components/DataTable.tsx +55 -54
  17. package/src/components/EditorPanel.js +207 -45
  18. package/src/components/{Grid.js → Grid.jsx} +5 -4
  19. package/src/components/Header.jsx +242 -0
  20. package/src/components/Row.js +1 -0
  21. package/src/components/Row.jsx +181 -0
  22. package/src/components/Row.jsx~HEAD +212 -0
  23. package/src/components/Widget.js +24 -5
  24. package/src/components/Widget.jsx +191 -0
  25. package/src/index.html +14 -11
  26. package/src/scss/editor-panel.scss +53 -49
  27. package/src/scss/grid.scss +61 -14
  28. package/src/scss/main.scss +73 -9
  29. package/LICENSE +0 -201
  30. package/src/components/Header.js +0 -15
  31. package/src/images/icon-close.svg +0 -1
  32. package/src/images/icon-down.svg +0 -1
  33. package/src/images/icon-edit.svg +0 -1
  34. package/src/images/icon-move.svg +0 -8
  35. package/src/images/icon-up.svg +0 -1
  36. package/src/images/warning.svg +0 -1
@@ -1,6 +1,8 @@
1
1
  import React, { useContext } from 'react';
2
2
  import { useDrag } from 'react-dnd';
3
3
  import CloseIcon from '../images/icon-close.svg';
4
+ import GridIcon from '../images/icon-grid.svg';
5
+ import CodeIcon from '../images/icon-code.svg';
4
6
  import EditIcon from '../images/icon-edit.svg';
5
7
  import MoveIcon from '../images/icon-move.svg';
6
8
  import BiteIcon from '@cdc/core/assets/data-bite-graphic.svg';
@@ -9,30 +11,47 @@ import LineIcon from '@cdc/core/assets/chart-line-solid.svg';
9
11
  import PieIcon from '@cdc/core/assets/chart-pie-solid.svg';
10
12
  import UsaIcon from '@cdc/core/assets/usa-graphic.svg';
11
13
  import WorldIcon from '@cdc/core/assets/world-graphic.svg';
14
+ import AlabamaIcon from '@cdc/core/assets/alabama-graphic.svg';
15
+ import FilteredText from '@cdc/core/assets/filtered-text.svg';
12
16
 
13
17
  import Context from '../context';
14
18
 
15
19
  const iconHash = {
16
20
  'data-bite' : <BiteIcon />,
17
- 'Bar' : <BarIcon />,
21
+ 'Bar': <BarIcon />,
22
+ 'Spark Line': <LineIcon />,
23
+ 'waffle-chart' : <GridIcon />,
24
+ 'markup-include' : <CodeIcon />,
18
25
  'Line' : <LineIcon />,
19
26
  'Pie' : <PieIcon />,
20
27
  'us' : <UsaIcon />,
21
- 'world' : <WorldIcon />
28
+ 'us-county': <UsaIcon />,
29
+ 'world' : <WorldIcon />,
30
+ 'single-state': <AlabamaIcon />,
31
+ 'filtered-text' : <FilteredText />,
22
32
  }
23
33
 
24
34
  const labelHash = {
25
- 'data-bite' : 'Data Bite',
35
+ 'data-bite': 'Data Bite',
36
+ 'waffle-chart' : 'Waffle Chart',
37
+ 'markup-include' : 'Markup Include',
26
38
  'Bar' : 'Bar',
27
39
  'Line' : 'Line',
28
40
  'Pie' : 'Pie',
29
- 'us' : 'United States',
30
- 'world' : 'World'
41
+ 'Spark Line' : 'Spark Line',
42
+ 'us': 'United States (State- or County-Level)',
43
+ 'us-county': 'United States (State- or County-Level)',
44
+ 'world' : 'World',
45
+ 'single-state': 'U.S. State',
46
+ 'filtered-text':'Filtered Text'
47
+
31
48
  }
32
49
 
33
50
  const Widget = ({ data = {}, addVisualization, type }) => {
34
51
  const { rows, visualizations, config, updateConfig } = useContext(Context)
35
52
 
53
+ console.log('type', type)
54
+
36
55
  const handleWidgetMove = (item, monitor) => {
37
56
  let result = monitor.getDropResult()
38
57
 
@@ -0,0 +1,191 @@
1
+ import React, { useContext, useRef, useEffect } from 'react'
2
+ import { useDrag } from 'react-dnd'
3
+
4
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
5
+ import ConfigContext from '../ConfigContext'
6
+
7
+ import { DataTransform } from '@cdc/core/helpers/DataTransform'
8
+ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
9
+
10
+ import DataDesigner from '@cdc/core/components/managers/DataDesigner'
11
+ import Icon from '@cdc/core/components/ui/Icon'
12
+ import Modal from '@cdc/core/components/ui/Modal'
13
+
14
+ const iconHash = {
15
+ 'data-bite': <Icon display="databite" base/>,
16
+ 'Bar': <Icon display="chartBar" base/>,
17
+ 'waffle-chart': <Icon display="grid" base/>,
18
+ 'markup-include': <Icon display="code" base/>,
19
+ 'Line': <Icon display="chartLine" base/>,
20
+ 'Pie': <Icon display="chartPie" base/>,
21
+ 'us': <Icon display="mapUsa" base/>,
22
+ 'us-county': <Icon display="mapUsa" base/>,
23
+ 'world': <Icon display="mapWorld" base/>,
24
+ 'single-state': <Icon display="mapAl" base/>,
25
+ 'gear': <Icon display="gear" base/>,
26
+ 'tools': <Icon display="tools" base/>
27
+ }
28
+
29
+ const labelHash = {
30
+ 'data-bite': 'Data Bite',
31
+ 'waffle-chart': 'Waffle Chart',
32
+ 'markup-include': 'Markup Include',
33
+ 'Bar': 'Bar',
34
+ 'Line': 'Line',
35
+ 'Pie': 'Pie',
36
+ 'us': 'United States (State- or County-Level)',
37
+ 'us-county': 'United States (State- or County-Level)',
38
+ 'world': 'World',
39
+ 'single-state': 'U.S. State'
40
+ }
41
+
42
+ const Widget = ({ data = {}, addVisualization, type }) => {
43
+ const { overlay } = useGlobalContext()
44
+ const { rows, visualizations, config, updateConfig } = useContext(ConfigContext)
45
+
46
+ const dataRef = useRef();
47
+ dataRef.current = data;
48
+
49
+ const transform = new DataTransform()
50
+
51
+ const handleWidgetMove = (item, monitor) => {
52
+ let result = monitor.getDropResult()
53
+
54
+ if (!result) return null
55
+
56
+ const { rowIdx, colIdx } = result
57
+
58
+ if (undefined !== data.rowIdx) {
59
+ rows[data.rowIdx][data.colIdx].widget = null // Wipe from old position
60
+
61
+ rows[rowIdx][colIdx].widget = data.uid // Add to new row and col
62
+ } else {
63
+ // Item does not exist, instantiate a new one
64
+ const newViz = addVisualization()
65
+ visualizations[newViz.uid] = newViz // Add to widgets collection
66
+ rows[rowIdx][colIdx].widget = newViz.uid // Store reference in rows collection under the specific column
67
+ }
68
+
69
+ updateConfig({ ...config, rows, visualizations })
70
+ }
71
+
72
+ const [ { isDragging, ...collected }, drag ] = useDrag({
73
+ type: 'vis-widget',
74
+ end: handleWidgetMove,
75
+ collect: (monitor) => ({
76
+ isDragging: monitor.isDragging()
77
+ })
78
+ })
79
+
80
+ const deleteWidget = () => {
81
+ rows[data.rowIdx][data.colIdx].widget = null
82
+
83
+ delete visualizations[data.uid]
84
+
85
+ updateConfig({ ...config, rows, visualizations })
86
+ }
87
+
88
+ const editWidget = () => {
89
+ visualizations[data.uid].editing = true
90
+
91
+ updateConfig({ ...config, visualizations })
92
+ }
93
+
94
+ const changeDataset = (uid, value) => {
95
+ delete visualizations[uid].dataDescription
96
+ delete visualizations[uid].formattedData
97
+
98
+ visualizations[uid].dataKey = value
99
+
100
+ updateConfig({ ...config, visualizations })
101
+ }
102
+
103
+ const updateDescriptionProp = async (visualizationKey, datasetKey, key, value) => {
104
+ let dataDescription = { ...dataRef.current.dataDescription, [key]: value }
105
+
106
+ let newData
107
+ if (!config.datasets[datasetKey].data && config.datasets[datasetKey].dataUrl) {
108
+ newData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
109
+ newData = transform.autoStandardize(newData)
110
+ } else {
111
+ newData = config.datasets[datasetKey].data
112
+ }
113
+
114
+ let formattedData = transform.developerStandardize(newData, dataDescription)
115
+
116
+ let newVisualizations = { ...config.visualizations }
117
+ newVisualizations[visualizationKey] = { ...newVisualizations[visualizationKey], data: newData, dataDescription, formattedData }
118
+
119
+ updateConfig({ ...config, visualizations: newVisualizations })
120
+
121
+ overlay?.actions.openOverlay(dataDesignerModal(newVisualizations[visualizationKey]))
122
+ }
123
+
124
+ const dataDesignerModal = (configureData, dataKeyOverride) => {
125
+ const dataKey = !dataKeyOverride && dataKeyOverride !== '' ? (data.dataKey || dataRef.current.dataKey) : dataKeyOverride;
126
+
127
+ return (
128
+ <Modal>
129
+ <Modal.Content>
130
+ <div className="dataset-selector-container">
131
+ Select a dataset:&nbsp;
132
+ <select className="dataset-selector" defaultValue={dataKey} onChange={(e) => {
133
+ changeDataset(data.uid, e.target.value)
134
+ overlay?.actions.openOverlay(dataDesignerModal(data, e.target.value || ''))
135
+ }}>
136
+ <option value="">Select a dataset</option>
137
+ {config.datasets && Object.keys(config.datasets).map(datasetKey => (
138
+ <option key={datasetKey}>{datasetKey}</option>
139
+ ))}
140
+ </select>
141
+ </div>
142
+ {dataKey && <DataDesigner {...{
143
+ configureData,
144
+ visualizationKey: data.uid,
145
+ dataKey: dataKey,
146
+ updateDescriptionProp
147
+ }}/>}
148
+ {configureData.formattedData && <button style={{margin: '1em'}} className="cove-button" onClick={() => overlay?.actions.toggleOverlay()}>Continue</button>}
149
+ </Modal.Content>
150
+ </Modal>
151
+ )
152
+ }
153
+
154
+ useEffect(() => {
155
+ if(data.openModal){
156
+ overlay?.actions.openOverlay(dataDesignerModal(dataRef.current))
157
+
158
+ visualizations[data.uid].openModal = false
159
+
160
+ updateConfig({ ...config, visualizations })
161
+ }
162
+ }, [data.openModal])
163
+
164
+ return (
165
+ <>
166
+ <div className="widget" ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }} {...collected}>
167
+ <Icon display="move" className="drag-icon"/>
168
+ <div className="widget__content">
169
+ {data.rowIdx !== undefined && (
170
+ <div className="widget-menu">
171
+ {data.dataKey && data.dataDescription && data.formattedData &&
172
+ <button title="Configure Visualization" className="btn btn-configure" onClick={editWidget}>{iconHash['tools']}</button>
173
+ }
174
+ <button title="Configure Data" className="btn btn-configure" onClick={() => {
175
+ overlay?.actions.openOverlay(dataDesignerModal(data))
176
+ }}>{iconHash['gear']}</button>
177
+ <div className="widget-menu-item" onClick={deleteWidget}>
178
+ <Icon display="close" base/>
179
+ </div>
180
+ </div>
181
+ )}
182
+ {iconHash[type]}
183
+ <span>{labelHash[type]}</span>
184
+ {data.newViz && <span onClick={editWidget} className="config-needed">Configuration needed</span>}
185
+ </div>
186
+ </div>
187
+ </>
188
+ )
189
+ }
190
+
191
+ export default Widget
package/src/index.html CHANGED
@@ -1,10 +1,10 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta
6
- name="viewport"
7
- content="width=device-width, initial-scale=1, shrink-to-fit=no"
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1, shrink-to-fit=no"
8
8
  />
9
9
  <style>
10
10
  body {
@@ -12,14 +12,17 @@
12
12
  display: flex;
13
13
  flex-direction: column;
14
14
  justify-content: center;
15
+ min-height: unset !important;
15
16
  }
16
17
  .react-container + .react-container {
17
18
  margin-top: 3rem;
18
19
  }
19
20
  </style>
20
- </head>
21
+ </head>
21
22
  <body>
22
- <div class="react-container" data-config="/examples/default.json"></div>
23
+ <!--<div class="react-container" data-config="/examples/default.json"></div>-->
24
+ <!--<div class="react-container" data-config="/examples/default-multi-dataset.json"></div>-->
25
+ <div class="react-container" data-config="/examples/default-filter-control.json"></div>
23
26
  <noscript>You need to enable JavaScript to run this app.</noscript>
24
- </body>
25
- </html>
27
+ </body>
28
+ </html>
@@ -11,6 +11,11 @@
11
11
  }
12
12
 
13
13
  .editor-panel {
14
+ //TODO: Remove after COVE refactor
15
+ &.cove {
16
+ @import "@cdc/core/styles/v2/layout/tooltip.scss";
17
+ }
18
+
14
19
  .two-col-inputs {
15
20
  display: flex;
16
21
  margin-top: 1em;
@@ -150,14 +155,14 @@
150
155
  transform: rotate(-45deg) translateY(-50%);
151
156
  transition: .1s all;
152
157
  }
153
-
158
+
154
159
  .accordion__button[aria-expanded='true']::before,
155
160
  .accordion__button[aria-selected='true']::before {
156
161
  transform: rotate(45deg) translateY(-50%);
157
162
  margin-right: 1.3em;
158
163
  transition: .1s all;
159
164
  }
160
-
165
+
161
166
  .accordion__panel {
162
167
  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
163
168
  padding: 1em 1.25em 2em;
@@ -166,7 +171,7 @@
166
171
  font-size: .8em;
167
172
  }
168
173
  }
169
-
174
+
170
175
  .advanced {
171
176
  padding: 0 1em 1em;
172
177
  text-align: left;
@@ -178,7 +183,6 @@
178
183
  display: block;
179
184
  text-align: left;
180
185
  cursor: pointer;
181
- color: rgba(0,0,0,.5);
182
186
  text-decoration: underline;
183
187
  span {
184
188
  text-decoration: none;
@@ -199,17 +203,17 @@
199
203
  box-sizing: border-box
200
204
  }
201
205
  }
202
-
206
+
203
207
  @keyframes fadein {
204
208
  0% {
205
209
  opacity: 0;
206
210
  }
207
-
211
+
208
212
  100% {
209
213
  opacity: 1;
210
214
  }
211
215
  }
212
-
216
+
213
217
  .heading-2 {
214
218
  background: #565656;
215
219
  color: #fff;
@@ -219,7 +223,7 @@
219
223
  border-bottom:#565656 3px solid;
220
224
  z-index: 3;
221
225
  }
222
-
226
+
223
227
  label {
224
228
  text-transform: uppercase;
225
229
  display: block;
@@ -292,17 +296,17 @@
292
296
  }
293
297
  }
294
298
  }
295
-
299
+
296
300
  fieldset {
297
301
  display: block;
298
302
  }
299
-
303
+
300
304
  .primary-fieldset {
301
305
  padding: 0;
302
306
  margin: 0;
303
307
  border: 0;
304
308
  }
305
-
309
+
306
310
  ul.column-edit {
307
311
  list-style: none;
308
312
  li {
@@ -321,11 +325,11 @@
321
325
  }
322
326
  }
323
327
  }
324
-
328
+
325
329
  &.hidden {
326
330
  display: none;
327
331
  }
328
-
332
+
329
333
  .emove-column {
330
334
  float: right;
331
335
  text-transform: uppercase;
@@ -341,7 +345,7 @@
341
345
  cursor: pointer;
342
346
  font-weight: bold;
343
347
  }
344
-
348
+
345
349
  .edit-block {
346
350
  padding-left: 1em;
347
351
  border-left: rgba(0, 0, 0, 0.2) 4px solid;
@@ -370,7 +374,7 @@
370
374
  text-transform: none;
371
375
  font-weight: normal;
372
376
  }
373
-
377
+
374
378
  .btn {
375
379
  margin-top: 1em;
376
380
  }
@@ -378,7 +382,7 @@
378
382
  list-style: none;
379
383
  > li {
380
384
  margin-right: .3em;
381
- margin-bottom: .3em;
385
+ margin-bottom: .3em;
382
386
  }
383
387
  }
384
388
  .sort-list li > div {
@@ -389,21 +393,21 @@
389
393
  background: #F1F1F1;
390
394
  padding: .4em .6em;
391
395
  font-size: .8em;
392
- margin-bottom: .3em;
396
+ margin-bottom: .3em;
393
397
  cursor: move;
394
398
  }
395
-
399
+
396
400
  .info {
397
401
  font-size: .8em;
398
402
  line-height: 1.4em;
399
403
  font-style: italic;
400
404
  padding-top: 10px;
401
405
  }
402
-
406
+
403
407
  .react-tags__search {
404
408
  width: 100%;
405
409
  }
406
-
410
+
407
411
  .react-tags {
408
412
  position: relative;
409
413
  input.react-tags__search-input {
@@ -415,15 +419,15 @@
415
419
  display: inline
416
420
  }
417
421
  }
418
-
422
+
419
423
  .react-tags.is-focused {
420
424
  border-color: rgba(0, 0, 0, 0.7);
421
425
  }
422
-
426
+
423
427
  .react-tags__selected {
424
428
  display: inline;
425
429
  }
426
-
430
+
427
431
  .react-tags__selected-tag {
428
432
  display: inline-block;
429
433
  box-sizing: border-box;
@@ -435,67 +439,67 @@
435
439
  margin-right: .3em;
436
440
  margin-bottom: .3em;
437
441
  }
438
-
442
+
439
443
  .react-tags__selected-tag:after {
440
444
  content: '\2715';
441
445
  color: #AAA;
442
446
  margin-left: 8px;
443
447
  }
444
-
448
+
445
449
  .react-tags__selected-tag:hover,
446
450
  .react-tags__selected-tag:focus {
447
451
  border-color: #B1B1B1;
448
452
  }
449
-
453
+
450
454
  .react-tags__search {
451
455
  display: inline-block;
452
-
456
+
453
457
  /* prevent autoresize overflowing the container */
454
458
  max-width: 100%;
455
459
  }
456
-
460
+
457
461
  @media screen and (min-width: 30em) {
458
-
462
+
459
463
  .react-tags__search {
460
464
  /* this will become the offsetParent for suggestions */
461
465
  position: relative;
462
466
  }
463
-
467
+
464
468
  }
465
-
469
+
466
470
  .react-tags__search input {
467
471
  /* prevent autoresize overflowing the container */
468
472
  max-width: 100%;
469
-
473
+
470
474
  /* emove styles and layout from this element */
471
475
  margin: 0;
472
476
  outline: none;
473
477
  padding: .5em .3em;
474
-
478
+
475
479
  /* match the font styles */
476
480
  font-size: inherit;
477
481
  line-height: inherit;
478
482
  }
479
-
483
+
480
484
  .react-tags__search input::-ms-clear {
481
485
  display: none;
482
486
  }
483
-
487
+
484
488
  .react-tags__suggestions {
485
489
  position: absolute;
486
490
  top: 100%;
487
491
  left: 0;
488
492
  width: 100%;
489
493
  }
490
-
494
+
491
495
  @media screen and (min-width: 30em) {
492
-
496
+
493
497
  .react-tags__suggestions {
494
498
  width: 240px;
495
499
  }
496
-
500
+
497
501
  }
498
-
502
+
499
503
  .react-tags__suggestions ul {
500
504
  margin: 4px -1px;
501
505
  padding: 0;
@@ -505,27 +509,27 @@
505
509
  border-radius: 2px;
506
510
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
507
511
  }
508
-
512
+
509
513
  .react-tags__suggestions li {
510
514
  border-bottom: 1px solid #ddd;
511
515
  padding: 6px 8px;
512
516
  }
513
-
517
+
514
518
  .react-tags__suggestions li mark {
515
519
  text-decoration: underline;
516
520
  background: none;
517
521
  font-weight: 600;
518
522
  }
519
-
523
+
520
524
  .react-tags__suggestions li:hover {
521
525
  cursor: pointer;
522
526
  background: #eee;
523
527
  }
524
-
528
+
525
529
  .react-tags__suggestions li.is-active {
526
530
  background: #b7cfe0;
527
531
  }
528
-
532
+
529
533
  .react-tags__suggestions li.is-disabled {
530
534
  opacity: 0.5;
531
535
  cursor: auto;
@@ -541,7 +545,7 @@
541
545
  border: 1px solid gray;
542
546
  }
543
547
  }
544
-
548
+
545
549
  .viz-icon {
546
550
  background-color: #FFF;
547
551
  color: #565656;
@@ -557,7 +561,7 @@
557
561
 
558
562
  &:hover {
559
563
  background: #F2F2F2;
560
- transition: .2s all;
564
+ transition: .2s all;
561
565
  }
562
566
 
563
567
  svg {
@@ -571,7 +575,7 @@
571
575
  }
572
576
  }
573
577
  }
574
- }
578
+ }
575
579
  .editor-toggle {
576
580
  background: #F2F2F2;
577
581
  border-radius: 60px;
@@ -596,7 +600,7 @@
596
600
  }
597
601
  &.collapsed {
598
602
  left: 1em;
599
- margin-left: 0;
603
+ margin-left: 0;
600
604
  &:before {
601
605
  content: "\00bb";
602
606
  }
@@ -644,4 +648,4 @@
644
648
  }
645
649
  }
646
650
  }
647
- }
651
+ }