@cdc/editor 4.23.4 → 4.23.6
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/dist/cdceditor.js +172802 -170270
- package/package.json +9 -9
- package/src/components/ChooseTab.jsx +21 -5
- package/src/components/ConfigureTab.jsx +13 -11
- package/src/components/DataImport.jsx +202 -41
- package/src/components/SampleData.jsx +9 -3
- package/src/samples/valid-forecast-data.csv +366 -0
- package/src/scss/data-import.scss +55 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/editor",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.6",
|
|
4
4
|
"description": "React component for generating a new component entry",
|
|
5
5
|
"moduleName": "CdcEditor",
|
|
6
6
|
"main": "dist/cdceditor",
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
},
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@cdc/chart": "^4.23.
|
|
28
|
-
"@cdc/core": "^4.23.
|
|
29
|
-
"@cdc/dashboard": "^4.23.
|
|
30
|
-
"@cdc/data-bite": "^4.23.
|
|
31
|
-
"@cdc/map": "^4.23.
|
|
32
|
-
"@cdc/markup-include": "^4.23.
|
|
33
|
-
"@cdc/waffle-chart": "^4.23.
|
|
27
|
+
"@cdc/chart": "^4.23.6",
|
|
28
|
+
"@cdc/core": "^4.23.6",
|
|
29
|
+
"@cdc/dashboard": "^4.23.6",
|
|
30
|
+
"@cdc/data-bite": "^4.23.6",
|
|
31
|
+
"@cdc/map": "^4.23.6",
|
|
32
|
+
"@cdc/markup-include": "^4.23.6",
|
|
33
|
+
"@cdc/waffle-chart": "^4.23.6",
|
|
34
34
|
"axios": "^0.21.1",
|
|
35
35
|
"d3": "^7.0.0",
|
|
36
36
|
"html-react-parser": "^3.0.8",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"react": "^18.2.0",
|
|
44
44
|
"react-dom": "^18.2.0"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "aaed0388b487adfeb3e7e278b4ce74df09cbaade"
|
|
47
47
|
}
|
|
@@ -19,6 +19,8 @@ import HorizontalStackIcon from '@cdc/core/assets/icon-chart-bar-stacked.svg'
|
|
|
19
19
|
import ScatterPlotIcon from '@cdc/core/assets/icon-chart-scatterplot.svg'
|
|
20
20
|
import BoxPlotIcon from '@cdc/core/assets/icon-chart-box-whisker.svg'
|
|
21
21
|
import AreaChartIcon from '@cdc/core/assets/icon-area-chart.svg'
|
|
22
|
+
import GaugeChartIcon from '@cdc/core/assets/icon-linear-gauge.svg'
|
|
23
|
+
import InfoIcon from '@cdc/core/assets/icon-info.svg'
|
|
22
24
|
|
|
23
25
|
export default function ChooseTab() {
|
|
24
26
|
const { config, setConfig, setGlobalActive, tempConfig, setTempConfig } = useContext(ConfigContext)
|
|
@@ -37,18 +39,16 @@ export default function ChooseTab() {
|
|
|
37
39
|
let isSubType = false
|
|
38
40
|
let isHorizontalStackedChart = false
|
|
39
41
|
let classNames
|
|
40
|
-
|
|
41
42
|
if (type === 'map' && config.general) {
|
|
42
43
|
let geoType = config.general.geoType
|
|
43
44
|
isSubType = subType === geoType
|
|
44
45
|
}
|
|
45
|
-
|
|
46
46
|
if (type === 'chart') {
|
|
47
47
|
isSubType = subType === config.visualizationType
|
|
48
48
|
isHorizontalStackedChart = orientation === config.orientation && stacked === true
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
if (type === 'dashboard' || type === 'data-bite' || type === '
|
|
51
|
+
if (type === 'dashboard' || type === 'data-bite' || type === 'markup-include') isSubType = true
|
|
52
52
|
|
|
53
53
|
// TODO: sorry, we should refactor this at some point.
|
|
54
54
|
// trying to get this out for 4.22.5 - this is so stacked horizontal and bar charts aren't highlighted at the same time.
|
|
@@ -137,11 +137,19 @@ export default function ChooseTab() {
|
|
|
137
137
|
<li>
|
|
138
138
|
<Tooltip>
|
|
139
139
|
<Tooltip.Target>
|
|
140
|
-
<IconButton label='Waffle Chart' type='
|
|
140
|
+
<IconButton label='Waffle Chart' type='chart' subType='Waffle' icon={<WaffleChartIcon />} />
|
|
141
141
|
</Tooltip.Target>
|
|
142
142
|
<Tooltip.Content>Highlight a piece of data in relationship to a data set.</Tooltip.Content>
|
|
143
143
|
</Tooltip>
|
|
144
144
|
</li>
|
|
145
|
+
{/* <li>
|
|
146
|
+
<Tooltip>
|
|
147
|
+
<Tooltip.Target>
|
|
148
|
+
<IconButton label='Gauge Chart' type='chart' subType='Gauge' icon={<GaugeChartIcon />} />
|
|
149
|
+
</Tooltip.Target>
|
|
150
|
+
<Tooltip.Content>Specify the calculation of a single data point (such as a percentage value) and present it on a horizontal scale.</Tooltip.Content>
|
|
151
|
+
</Tooltip>
|
|
152
|
+
</li> */}
|
|
145
153
|
</ul>
|
|
146
154
|
|
|
147
155
|
<div className='heading-2'>Charts</div>
|
|
@@ -206,13 +214,21 @@ export default function ChooseTab() {
|
|
|
206
214
|
<Tooltip.Content>Display a scatter plot</Tooltip.Content>
|
|
207
215
|
</Tooltip>
|
|
208
216
|
</li>
|
|
209
|
-
|
|
217
|
+
<li>
|
|
210
218
|
<Tooltip>
|
|
211
219
|
<Tooltip.Target>
|
|
212
220
|
<IconButton label='Area Chart' type='chart' subType='Area Chart' orientation='vertical' icon={<AreaChartIcon />} />
|
|
213
221
|
</Tooltip.Target>
|
|
214
222
|
<Tooltip.Content>Display an area chart</Tooltip.Content>
|
|
215
223
|
</Tooltip>
|
|
224
|
+
</li>
|
|
225
|
+
{/* <li>
|
|
226
|
+
<Tooltip>
|
|
227
|
+
<Tooltip.Target>
|
|
228
|
+
<IconButton label='Forecast Chart' type='chart' subType='Forecasting' orientation='vertical' icon={<InfoIcon />} />
|
|
229
|
+
</Tooltip.Target>
|
|
230
|
+
<Tooltip.Content>Display a forecasting chart</Tooltip.Content>
|
|
231
|
+
</Tooltip>
|
|
216
232
|
</li> */}
|
|
217
233
|
</ul>
|
|
218
234
|
|
|
@@ -25,11 +25,19 @@ export default function ConfigureTab({ containerEl }) {
|
|
|
25
25
|
</ErrorBoundary>
|
|
26
26
|
)
|
|
27
27
|
case 'chart':
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
if (config.visualizationType === 'Waffle' || config.visualizationType === 'Gauge') {
|
|
29
|
+
return (
|
|
30
|
+
<ErrorBoundary component='CdcWaffleChart'>
|
|
31
|
+
<CdcWaffleChart isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
|
|
32
|
+
</ErrorBoundary>
|
|
33
|
+
)
|
|
34
|
+
} else {
|
|
35
|
+
return (
|
|
36
|
+
<ErrorBoundary component='CdcChart'>
|
|
37
|
+
<CdcChart isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
|
|
38
|
+
</ErrorBoundary>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
33
41
|
case 'dashboard':
|
|
34
42
|
return (
|
|
35
43
|
<ErrorBoundary component='CdcDashboard'>
|
|
@@ -42,12 +50,6 @@ export default function ConfigureTab({ containerEl }) {
|
|
|
42
50
|
<CdcDataBite isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
|
|
43
51
|
</ErrorBoundary>
|
|
44
52
|
)
|
|
45
|
-
case 'waffle-chart':
|
|
46
|
-
return (
|
|
47
|
-
<ErrorBoundary component='CdcDashboard'>
|
|
48
|
-
<CdcWaffleChart isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
|
|
49
|
-
</ErrorBoundary>
|
|
50
|
-
)
|
|
51
53
|
case 'markup-include':
|
|
52
54
|
return (
|
|
53
55
|
<ErrorBoundary component='CdcDashboard'>
|
|
@@ -17,7 +17,10 @@ import SampleData from './SampleData'
|
|
|
17
17
|
import FileUploadIcon from '../assets/icons/file-upload-solid.svg'
|
|
18
18
|
import CloseIcon from '@cdc/core/assets/icon-close.svg'
|
|
19
19
|
|
|
20
|
+
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
20
21
|
import DataDesigner from '@cdc/core/components/managers/DataDesigner'
|
|
22
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
23
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
21
24
|
|
|
22
25
|
import '../scss/data-import.scss'
|
|
23
26
|
|
|
@@ -36,6 +39,8 @@ export default function DataImport() {
|
|
|
36
39
|
|
|
37
40
|
const [editingDataset, setEditingDataset] = useState()
|
|
38
41
|
|
|
42
|
+
const [asyncPreviewData, setAsyncPreviewData] = useState()
|
|
43
|
+
|
|
39
44
|
const supportedDataTypes = {
|
|
40
45
|
'.csv': 'text/csv',
|
|
41
46
|
'.json': 'application/json'
|
|
@@ -204,7 +209,6 @@ export default function DataImport() {
|
|
|
204
209
|
// Validate parsed data and set if no issues.
|
|
205
210
|
try {
|
|
206
211
|
text = transform.autoStandardize(text)
|
|
207
|
-
|
|
208
212
|
if (config.data && config.series) {
|
|
209
213
|
if (dataExists(text, config.series, config?.xAxis.dataKey)) {
|
|
210
214
|
if (config.type === 'dashboard') {
|
|
@@ -257,7 +261,6 @@ export default function DataImport() {
|
|
|
257
261
|
} else {
|
|
258
262
|
if (config.type === 'dashboard') {
|
|
259
263
|
let newDatasets = { ...config.datasets }
|
|
260
|
-
|
|
261
264
|
Object.keys(newDatasets).forEach(datasetKey => (newDatasets[datasetKey].preview = false))
|
|
262
265
|
|
|
263
266
|
newDatasets[editingDatasetKey || fileSource] = {
|
|
@@ -294,8 +297,6 @@ export default function DataImport() {
|
|
|
294
297
|
setEditingDataset(undefined)
|
|
295
298
|
}
|
|
296
299
|
setAddingDataset(false)
|
|
297
|
-
setExternalURL('')
|
|
298
|
-
setKeepURL(false)
|
|
299
300
|
} catch (err) {
|
|
300
301
|
setErrors(err)
|
|
301
302
|
}
|
|
@@ -320,6 +321,34 @@ export default function DataImport() {
|
|
|
320
321
|
setConfig(newConfig)
|
|
321
322
|
}, []) // eslint-disable-line
|
|
322
323
|
|
|
324
|
+
useEffect(() => {
|
|
325
|
+
const asyncWrapper = async () => {
|
|
326
|
+
if (config.type === 'dashboard') {
|
|
327
|
+
Object.keys(config.datasets).forEach(async datasetKey => {
|
|
328
|
+
if (config.datasets[datasetKey].preview) {
|
|
329
|
+
if (config.datasets[datasetKey].dataUrl) {
|
|
330
|
+
const remoteData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
|
|
331
|
+
if (Array.isArray(remoteData)) {
|
|
332
|
+
setAsyncPreviewData(remoteData)
|
|
333
|
+
}
|
|
334
|
+
} else if (Array.isArray(config.datasets[datasetKey].data)) {
|
|
335
|
+
setAsyncPreviewData(config.datasets[datasetKey].data)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
} else {
|
|
340
|
+
if (config.dataUrl) {
|
|
341
|
+
const remoteData = await fetchRemoteData(config.dataUrl)
|
|
342
|
+
if (Array.isArray(remoteData)) {
|
|
343
|
+
setAsyncPreviewData(remoteData)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
asyncWrapper()
|
|
350
|
+
}, [config.datasets]) // eslint-disable-line
|
|
351
|
+
|
|
323
352
|
const updateDescriptionProp = (visualizationKey, datasetKey, key, value) => {
|
|
324
353
|
if (config.type === 'dashboard') {
|
|
325
354
|
let dataDescription = { ...config.datasets[datasetKey].dataDescription, [key]: value }
|
|
@@ -395,9 +424,9 @@ export default function DataImport() {
|
|
|
395
424
|
return (
|
|
396
425
|
//todo convert to modal
|
|
397
426
|
<>
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
427
|
+
<button className='btn danger' onClick={() => resetEditor({ type: config.type, visualizationType: config.visualizationType }, 'Resetting will remove your data and settings. Do you want to continue?')}>
|
|
428
|
+
Clear
|
|
429
|
+
<CloseIcon />
|
|
401
430
|
</button>
|
|
402
431
|
{/* DEV-851 link to replace file should pop file dialog */}
|
|
403
432
|
{config.dataFileSourceType === 'file' && (
|
|
@@ -406,7 +435,8 @@ export default function DataImport() {
|
|
|
406
435
|
<p>
|
|
407
436
|
<span>or replace file</span>
|
|
408
437
|
</p>
|
|
409
|
-
</div>
|
|
438
|
+
</div>
|
|
439
|
+
)}
|
|
410
440
|
</>
|
|
411
441
|
)
|
|
412
442
|
}
|
|
@@ -475,7 +505,7 @@ export default function DataImport() {
|
|
|
475
505
|
if (config.type === 'dashboard') {
|
|
476
506
|
readyToConfigure = Object.keys(config.datasets).length > 0
|
|
477
507
|
Object.keys(config.datasets).forEach(datasetKey => {
|
|
478
|
-
if (config.datasets[datasetKey].preview) {
|
|
508
|
+
if (config.datasets[datasetKey].preview && Array.isArray(config.datasets[datasetKey].data)) {
|
|
479
509
|
previewData = config.datasets[datasetKey].data
|
|
480
510
|
}
|
|
481
511
|
})
|
|
@@ -491,6 +521,139 @@ export default function DataImport() {
|
|
|
491
521
|
readyToConfigure = true
|
|
492
522
|
}
|
|
493
523
|
|
|
524
|
+
const urlFilters = (
|
|
525
|
+
<>
|
|
526
|
+
{config.filters &&
|
|
527
|
+
config.filters.map((filter, i) =>
|
|
528
|
+
filter.type !== 'url' ? (
|
|
529
|
+
<></>
|
|
530
|
+
) : (
|
|
531
|
+
<fieldset key={filter.key} className='edit-block url-filters-block'>
|
|
532
|
+
<button
|
|
533
|
+
onClick={e => {
|
|
534
|
+
let newFilters = [...config.filters]
|
|
535
|
+
newFilters.splice(i, 1)
|
|
536
|
+
setConfig({ ...config, filters: newFilters, runtimeDataUrl: undefined })
|
|
537
|
+
}}
|
|
538
|
+
>
|
|
539
|
+
Remove
|
|
540
|
+
</button>
|
|
541
|
+
<label>
|
|
542
|
+
<span class='edit-label column-heading'>
|
|
543
|
+
Label
|
|
544
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
545
|
+
<Tooltip.Target>
|
|
546
|
+
<Icon display='question' />
|
|
547
|
+
</Tooltip.Target>
|
|
548
|
+
<Tooltip.Content>
|
|
549
|
+
<p style={{ padding: '0.5rem' }}>The label that will appear above the dropdown filter.</p>
|
|
550
|
+
</Tooltip.Content>
|
|
551
|
+
</Tooltip>
|
|
552
|
+
</span>{' '}
|
|
553
|
+
<input
|
|
554
|
+
type='text'
|
|
555
|
+
defaultValue={filter.label}
|
|
556
|
+
onChange={e => {
|
|
557
|
+
let newFilters = [...config.filters]
|
|
558
|
+
newFilters[i].label = e.target.value
|
|
559
|
+
setConfig({ ...config, filters: newFilters })
|
|
560
|
+
}}
|
|
561
|
+
/>
|
|
562
|
+
</label>
|
|
563
|
+
<label>
|
|
564
|
+
<span class='edit-label column-heading'>
|
|
565
|
+
Query string parameter
|
|
566
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
567
|
+
<Tooltip.Target>
|
|
568
|
+
<Icon display='question' />
|
|
569
|
+
</Tooltip.Target>
|
|
570
|
+
<Tooltip.Content>
|
|
571
|
+
<p style={{ padding: '0.5rem' }}>Name of the query string parameter that will be appended to the URL above with the values provided below.</p>
|
|
572
|
+
</Tooltip.Content>
|
|
573
|
+
</Tooltip>
|
|
574
|
+
</span>{' '}
|
|
575
|
+
<input
|
|
576
|
+
type='text'
|
|
577
|
+
defaultValue={filter.queryParameter}
|
|
578
|
+
onChange={e => {
|
|
579
|
+
let newFilters = [...config.filters]
|
|
580
|
+
newFilters[i].queryParameter = e.target.value
|
|
581
|
+
setConfig({ ...config, filters: newFilters })
|
|
582
|
+
}}
|
|
583
|
+
/>
|
|
584
|
+
</label>
|
|
585
|
+
<label>
|
|
586
|
+
<span class='edit-label column-heading'>Values</span>{' '}
|
|
587
|
+
</label>
|
|
588
|
+
<ul className='value-list'>
|
|
589
|
+
{filter.orderedValues &&
|
|
590
|
+
filter.orderedValues.map((value, valueIndex) => (
|
|
591
|
+
<li>
|
|
592
|
+
{value}
|
|
593
|
+
<input
|
|
594
|
+
type='text'
|
|
595
|
+
placeholder='Enter value display name here'
|
|
596
|
+
value={filter.labels ? filter.labels[value] : undefined}
|
|
597
|
+
className='url-value-label'
|
|
598
|
+
onChange={e => {
|
|
599
|
+
let newFilters = [...config.filters]
|
|
600
|
+
|
|
601
|
+
newFilters[i].labels = newFilters[i].labels || {}
|
|
602
|
+
newFilters[i].labels[value] = e.target.value
|
|
603
|
+
|
|
604
|
+
setConfig({ ...config, filters: newFilters })
|
|
605
|
+
}}
|
|
606
|
+
/>
|
|
607
|
+
<button
|
|
608
|
+
onClick={() => {
|
|
609
|
+
let newFilters = [...config.filters]
|
|
610
|
+
|
|
611
|
+
if (newFilters[i].labels) {
|
|
612
|
+
delete newFilters[i].labels[newFilters[i].orderedValues[valueIndex]]
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
newFilters[i].orderedValues.splice(valueIndex, 1)
|
|
616
|
+
setConfig({ ...config, filters: newFilters })
|
|
617
|
+
}}
|
|
618
|
+
>
|
|
619
|
+
X
|
|
620
|
+
</button>
|
|
621
|
+
</li>
|
|
622
|
+
))}
|
|
623
|
+
</ul>
|
|
624
|
+
<form
|
|
625
|
+
onSubmit={e => {
|
|
626
|
+
e.preventDefault()
|
|
627
|
+
if (!config.filters[i].orderedValues || config.filters[i].orderedValues.indexOf(e.target[0].value) === -1) {
|
|
628
|
+
let newFilters = [...config.filters]
|
|
629
|
+
newFilters[i].orderedValues = newFilters[i].orderedValues || []
|
|
630
|
+
newFilters[i].orderedValues.push(e.target[0].value)
|
|
631
|
+
newFilters[i].values = newFilters[i].orderedValues
|
|
632
|
+
if (!newFilters[i].active) newFilters[i].active = e.target[0].value
|
|
633
|
+
e.target[0].value = ''
|
|
634
|
+
setConfig({ ...config, filters: newFilters })
|
|
635
|
+
}
|
|
636
|
+
}}
|
|
637
|
+
>
|
|
638
|
+
<input type='text' placeholder='Enter new value name here' />{' '}
|
|
639
|
+
<button type='submit' style={{ marginTop: '1em' }}>
|
|
640
|
+
Add New Value
|
|
641
|
+
</button>
|
|
642
|
+
</form>
|
|
643
|
+
</fieldset>
|
|
644
|
+
)
|
|
645
|
+
)}
|
|
646
|
+
<button
|
|
647
|
+
className='btn full-width'
|
|
648
|
+
onClick={() => {
|
|
649
|
+
setConfig({ ...config, filters: config.filters ? [...config.filters, { type: 'url', key: Date.now() }] : [{ type: 'url', key: Date.now() }] })
|
|
650
|
+
}}
|
|
651
|
+
>
|
|
652
|
+
Add New URL Filter
|
|
653
|
+
</button>
|
|
654
|
+
</>
|
|
655
|
+
)
|
|
656
|
+
|
|
494
657
|
const showDataDesigner = config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Scatter Plot'
|
|
495
658
|
|
|
496
659
|
return (
|
|
@@ -560,7 +723,7 @@ export default function DataImport() {
|
|
|
560
723
|
<>
|
|
561
724
|
<div className='heading-3'>Data Source</div>
|
|
562
725
|
<div className='file-loaded-area'>
|
|
563
|
-
{config.dataFileSourceType === 'file' && (
|
|
726
|
+
{(config.dataFileSourceType === 'file' || !config.dataFileSourceType) && (
|
|
564
727
|
<div className='data-source-options'>
|
|
565
728
|
<div className={isDragActive2 ? 'drag-active cdcdataviz-file-selector loaded-file' : 'cdcdataviz-file-selector loaded-file'} {...getRootProps2()}>
|
|
566
729
|
<input {...getInputProps2()} />
|
|
@@ -577,10 +740,13 @@ export default function DataImport() {
|
|
|
577
740
|
)}
|
|
578
741
|
|
|
579
742
|
{config.dataFileSourceType === 'url' && (
|
|
580
|
-
|
|
581
|
-
<div>
|
|
582
|
-
|
|
583
|
-
|
|
743
|
+
<>
|
|
744
|
+
<div className='url-source-options'>
|
|
745
|
+
<div>{loadFileFromUrl(externalURL)}</div>
|
|
746
|
+
<div>{resetButton()}</div>
|
|
747
|
+
</div>
|
|
748
|
+
{config.dataUrl && (config.type === 'chart' || config.type === 'map') && urlFilters}
|
|
749
|
+
</>
|
|
584
750
|
)}
|
|
585
751
|
</div>
|
|
586
752
|
</>
|
|
@@ -614,10 +780,10 @@ export default function DataImport() {
|
|
|
614
780
|
{errors &&
|
|
615
781
|
(errors.map
|
|
616
782
|
? errors.map((message, index) => (
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
783
|
+
<div className='error-box slim mt-2' key={`error-${message}`}>
|
|
784
|
+
<span>{message}</span> <CloseIcon className='inline-icon dismiss-error' onClick={() => setErrors(errors.filter((val, i) => i !== index))} />
|
|
785
|
+
</div>
|
|
786
|
+
))
|
|
621
787
|
: errors.message)}
|
|
622
788
|
<p className='footnote'>
|
|
623
789
|
Supported file types: {Object.keys(supportedDataTypes).join(', ')}. Maximum file size {maxFileSize}MB.
|
|
@@ -627,29 +793,24 @@ export default function DataImport() {
|
|
|
627
793
|
<SampleDataContext.Provider value={{ loadData, editingDataset, config }}>
|
|
628
794
|
<SampleData.Buttons />
|
|
629
795
|
</SampleDataContext.Provider>
|
|
630
|
-
</div
|
|
631
|
-
)
|
|
632
|
-
}
|
|
796
|
+
</div>
|
|
797
|
+
)}
|
|
633
798
|
|
|
634
|
-
{
|
|
635
|
-
|
|
636
|
-
<
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
)
|
|
642
|
-
}
|
|
799
|
+
{config.type === 'dashboard' && !addingDataset && (
|
|
800
|
+
<p>
|
|
801
|
+
<button className='btn btn-primary' onClick={() => setAddingDataset(true)}>
|
|
802
|
+
+ Add More Files
|
|
803
|
+
</button>
|
|
804
|
+
</p>
|
|
805
|
+
)}
|
|
643
806
|
|
|
644
|
-
{
|
|
645
|
-
|
|
646
|
-
<
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
)
|
|
652
|
-
}
|
|
807
|
+
{readyToConfigure && (
|
|
808
|
+
<p>
|
|
809
|
+
<button className='btn btn-primary' onClick={() => setGlobalActive(2)}>
|
|
810
|
+
Configure your visualization
|
|
811
|
+
</button>
|
|
812
|
+
</p>
|
|
813
|
+
)}
|
|
653
814
|
|
|
654
815
|
<a href='https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html' target='_blank' rel='noopener noreferrer' className='guidance-link'>
|
|
655
816
|
<div>
|
|
@@ -657,9 +818,9 @@ export default function DataImport() {
|
|
|
657
818
|
<p>Documentation and examples on formatting data and configuring visualizations.</p>
|
|
658
819
|
</div>
|
|
659
820
|
</a>
|
|
660
|
-
</div
|
|
821
|
+
</div>
|
|
661
822
|
<div className='right-col'>
|
|
662
|
-
<PreviewDataTable data={previewData} />
|
|
823
|
+
<PreviewDataTable data={asyncPreviewData || previewData} />
|
|
663
824
|
</div>
|
|
664
825
|
</>
|
|
665
826
|
)
|
|
@@ -10,6 +10,7 @@ import validScatterPlot from './../samples/valid-scatterplot.csv?raw'
|
|
|
10
10
|
import validBoxPlotData from './../samples/valid-boxplot.csv?raw'
|
|
11
11
|
import validAreaChart from './../samples/valid-area-chart.json?raw'
|
|
12
12
|
import validWorldGeocodeData from './../samples/valid-world-geocode.json?raw'
|
|
13
|
+
import validForecastData from './../samples/valid-forecast-data.csv?raw'
|
|
13
14
|
|
|
14
15
|
// Add additional data to samples
|
|
15
16
|
const sampleData = {
|
|
@@ -28,11 +29,16 @@ const sampleData = {
|
|
|
28
29
|
text: 'Scatter Plot Sample Data',
|
|
29
30
|
fileName: 'valid-scatterplot.csv',
|
|
30
31
|
data: validScatterPlot
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
text: 'Area Chart Sample Data',
|
|
35
|
+
fileName: 'valid-area-chart.json',
|
|
36
|
+
data: validAreaChart
|
|
31
37
|
}
|
|
32
38
|
// {
|
|
33
|
-
// text: '
|
|
34
|
-
// fileName: 'valid-
|
|
35
|
-
// data:
|
|
39
|
+
// text: 'Forecast Chart Data',
|
|
40
|
+
// fileName: 'valid-forecast-data.csv',
|
|
41
|
+
// data: validForecastData
|
|
36
42
|
// }
|
|
37
43
|
],
|
|
38
44
|
maps: [
|