@cdc/editor 4.23.3 → 4.23.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/editor",
3
- "version": "4.23.3",
3
+ "version": "4.23.5",
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.3",
28
- "@cdc/core": "^4.23.3",
29
- "@cdc/dashboard": "^4.23.3",
30
- "@cdc/data-bite": "^4.23.3",
31
- "@cdc/map": "^4.23.3",
32
- "@cdc/markup-include": "^4.23.3",
33
- "@cdc/waffle-chart": "^4.23.3",
27
+ "@cdc/chart": "^4.23.5",
28
+ "@cdc/core": "^4.23.5",
29
+ "@cdc/dashboard": "^4.23.5",
30
+ "@cdc/data-bite": "^4.23.5",
31
+ "@cdc/map": "^4.23.5",
32
+ "@cdc/markup-include": "^4.23.5",
33
+ "@cdc/waffle-chart": "^4.23.5",
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": "6fa3b11db159d38538f18023fe70b67a29e7d327"
46
+ "gitHead": "34add3436994ca3cf13e51f313add4d70377f53e"
47
47
  }
package/src/CdcEditor.jsx CHANGED
@@ -19,7 +19,7 @@ import Tabs from './components/Tabs'
19
19
 
20
20
  import './scss/main.scss'
21
21
 
22
- export default function CdcEditor({ config: configObj = { newViz: true }, hostname, containerEl, sharepath }) {
22
+ export default function CdcEditor({ config: configObj = { newViz: true }, hostname, containerEl, sharepath, isDebug }) {
23
23
  const [config, setConfig] = useState(configObj)
24
24
  const [tempConfig, setTempConfig] = useState(null)
25
25
  const [errors, setErrors] = useState([])
@@ -146,7 +146,8 @@ export default function CdcEditor({ config: configObj = { newViz: true }, hostna
146
146
  setGlobalActive,
147
147
  tempConfig,
148
148
  setTempConfig,
149
- sharepath
149
+ sharepath,
150
+ isDebug
150
151
  }
151
152
 
152
153
  let configureDisabled = true
@@ -18,6 +18,7 @@ import PairedBarIcon from '@cdc/core/assets/icon-chart-bar-paired.svg'
18
18
  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
+ import AreaChartIcon from '@cdc/core/assets/icon-area-chart.svg'
21
22
 
22
23
  export default function ChooseTab() {
23
24
  const { config, setConfig, setGlobalActive, tempConfig, setTempConfig } = useContext(ConfigContext)
@@ -205,14 +206,14 @@ export default function ChooseTab() {
205
206
  <Tooltip.Content>Display a scatter plot</Tooltip.Content>
206
207
  </Tooltip>
207
208
  </li>
208
- {/* <li>
209
+ <li>
209
210
  <Tooltip>
210
211
  <Tooltip.Target>
211
- <IconButton label='Area Chart' type='chart' subType='Area Chart' orientation='vertical' icon={<GenericIcon />} />
212
+ <IconButton label='Area Chart' type='chart' subType='Area Chart' orientation='vertical' icon={<AreaChartIcon />} />
212
213
  </Tooltip.Target>
213
214
  <Tooltip.Content>Display an area chart</Tooltip.Content>
214
215
  </Tooltip>
215
- </li> */}
216
+ </li>
216
217
  </ul>
217
218
 
218
219
  <div className='heading-2'>Maps</div>
@@ -13,7 +13,7 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
13
13
  import ConfigContext from '../ConfigContext'
14
14
 
15
15
  export default function ConfigureTab({ containerEl }) {
16
- const { config, setTempConfig, hostname } = useContext(ConfigContext)
16
+ const { config, setTempConfig, hostname, isDebug } = useContext(ConfigContext)
17
17
 
18
18
  let { type } = config
19
19
 
@@ -21,37 +21,37 @@ export default function ConfigureTab({ containerEl }) {
21
21
  case 'map':
22
22
  return (
23
23
  <ErrorBoundary component='CdcMap'>
24
- <CdcMap isEditor={true} config={config} hostname={hostname} setConfig={setTempConfig} containerEl={containerEl} />
24
+ <CdcMap isEditor={true} isDebug={isDebug} config={config} hostname={hostname} setConfig={setTempConfig} containerEl={containerEl} />
25
25
  </ErrorBoundary>
26
26
  )
27
27
  case 'chart':
28
28
  return (
29
29
  <ErrorBoundary component='CdcChart'>
30
- <CdcChart isEditor={true} config={config} setConfig={setTempConfig} />
30
+ <CdcChart isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
31
31
  </ErrorBoundary>
32
32
  )
33
33
  case 'dashboard':
34
34
  return (
35
35
  <ErrorBoundary component='CdcDashboard'>
36
- <CdcDashboard isEditor={true} config={config} setConfig={setTempConfig} />
36
+ <CdcDashboard isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
37
37
  </ErrorBoundary>
38
38
  )
39
39
  case 'data-bite':
40
40
  return (
41
41
  <ErrorBoundary component='CdcDashboard'>
42
- <CdcDataBite isEditor={true} config={config} setConfig={setTempConfig} />
42
+ <CdcDataBite isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
43
43
  </ErrorBoundary>
44
44
  )
45
45
  case 'waffle-chart':
46
46
  return (
47
47
  <ErrorBoundary component='CdcDashboard'>
48
- <CdcWaffleChart isEditor={true} config={config} setConfig={setTempConfig} />
48
+ <CdcWaffleChart isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
49
49
  </ErrorBoundary>
50
50
  )
51
51
  case 'markup-include':
52
52
  return (
53
53
  <ErrorBoundary component='CdcDashboard'>
54
- <CdcMarkupInclude isEditor={true} config={config} setConfig={setTempConfig} />
54
+ <CdcMarkupInclude isEditor={true} isDebug={isDebug} config={config} setConfig={setTempConfig} />
55
55
  </ErrorBoundary>
56
56
  )
57
57
  default:
@@ -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'
@@ -294,8 +299,6 @@ export default function DataImport() {
294
299
  setEditingDataset(undefined)
295
300
  }
296
301
  setAddingDataset(false)
297
- setExternalURL('')
298
- setKeepURL(false)
299
302
  } catch (err) {
300
303
  setErrors(err)
301
304
  }
@@ -320,6 +323,34 @@ export default function DataImport() {
320
323
  setConfig(newConfig)
321
324
  }, []) // eslint-disable-line
322
325
 
326
+ useEffect(() => {
327
+ const asyncWrapper = async () => {
328
+ if (config.type === 'dashboard') {
329
+ Object.keys(config.datasets).forEach(async datasetKey => {
330
+ if (config.datasets[datasetKey].preview) {
331
+ if (config.datasets[datasetKey].dataUrl) {
332
+ const remoteData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
333
+ if (Array.isArray(remoteData)) {
334
+ setAsyncPreviewData(remoteData)
335
+ }
336
+ } else if (Array.isArray(config.datasets[datasetKey].data)) {
337
+ setAsyncPreviewData(config.datasets[datasetKey].data)
338
+ }
339
+ }
340
+ })
341
+ } else {
342
+ if (config.dataUrl) {
343
+ const remoteData = await fetchRemoteData(config.dataUrl)
344
+ if (Array.isArray(remoteData)) {
345
+ setAsyncPreviewData(remoteData)
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ asyncWrapper()
352
+ }, [config.datasets]) // eslint-disable-line
353
+
323
354
  const updateDescriptionProp = (visualizationKey, datasetKey, key, value) => {
324
355
  if (config.type === 'dashboard') {
325
356
  let dataDescription = { ...config.datasets[datasetKey].dataDescription, [key]: value }
@@ -395,9 +426,9 @@ export default function DataImport() {
395
426
  return (
396
427
  //todo convert to modal
397
428
  <>
398
- <button className='btn danger' onClick={() => resetEditor({ type: config.type, visualizationType: config.visualizationType }, 'Resetting will remove your data and settings. Do you want to continue?')}>
399
- Clear
400
- <CloseIcon />
429
+ <button className='btn danger' onClick={() => resetEditor({ type: config.type, visualizationType: config.visualizationType }, 'Resetting will remove your data and settings. Do you want to continue?')}>
430
+ Clear
431
+ <CloseIcon />
401
432
  </button>
402
433
  {/* DEV-851 link to replace file should pop file dialog */}
403
434
  {config.dataFileSourceType === 'file' && (
@@ -406,7 +437,8 @@ export default function DataImport() {
406
437
  <p>
407
438
  <span>or replace file</span>
408
439
  </p>
409
- </div>)}
440
+ </div>
441
+ )}
410
442
  </>
411
443
  )
412
444
  }
@@ -475,7 +507,7 @@ export default function DataImport() {
475
507
  if (config.type === 'dashboard') {
476
508
  readyToConfigure = Object.keys(config.datasets).length > 0
477
509
  Object.keys(config.datasets).forEach(datasetKey => {
478
- if (config.datasets[datasetKey].preview) {
510
+ if (config.datasets[datasetKey].preview && Array.isArray(config.datasets[datasetKey].data)) {
479
511
  previewData = config.datasets[datasetKey].data
480
512
  }
481
513
  })
@@ -491,6 +523,139 @@ export default function DataImport() {
491
523
  readyToConfigure = true
492
524
  }
493
525
 
526
+ const urlFilters = (
527
+ <>
528
+ {config.filters &&
529
+ config.filters.map((filter, i) =>
530
+ filter.type !== 'url' ? (
531
+ <></>
532
+ ) : (
533
+ <fieldset key={filter.key} className='edit-block url-filters-block'>
534
+ <button
535
+ onClick={e => {
536
+ let newFilters = [...config.filters]
537
+ newFilters.splice(i, 1)
538
+ setConfig({ ...config, filters: newFilters, runtimeDataUrl: undefined })
539
+ }}
540
+ >
541
+ Remove
542
+ </button>
543
+ <label>
544
+ <span class='edit-label column-heading'>
545
+ Label
546
+ <Tooltip style={{ textTransform: 'none' }}>
547
+ <Tooltip.Target>
548
+ <Icon display='question' />
549
+ </Tooltip.Target>
550
+ <Tooltip.Content>
551
+ <p style={{ padding: '0.5rem' }}>The label that will appear above the dropdown filter.</p>
552
+ </Tooltip.Content>
553
+ </Tooltip>
554
+ </span>{' '}
555
+ <input
556
+ type='text'
557
+ defaultValue={filter.label}
558
+ onChange={e => {
559
+ let newFilters = [...config.filters]
560
+ newFilters[i].label = e.target.value
561
+ setConfig({ ...config, filters: newFilters })
562
+ }}
563
+ />
564
+ </label>
565
+ <label>
566
+ <span class='edit-label column-heading'>
567
+ Query string parameter
568
+ <Tooltip style={{ textTransform: 'none' }}>
569
+ <Tooltip.Target>
570
+ <Icon display='question' />
571
+ </Tooltip.Target>
572
+ <Tooltip.Content>
573
+ <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>
574
+ </Tooltip.Content>
575
+ </Tooltip>
576
+ </span>{' '}
577
+ <input
578
+ type='text'
579
+ defaultValue={filter.queryParameter}
580
+ onChange={e => {
581
+ let newFilters = [...config.filters]
582
+ newFilters[i].queryParameter = e.target.value
583
+ setConfig({ ...config, filters: newFilters })
584
+ }}
585
+ />
586
+ </label>
587
+ <label>
588
+ <span class='edit-label column-heading'>Values</span>{' '}
589
+ </label>
590
+ <ul className='value-list'>
591
+ {filter.orderedValues &&
592
+ filter.orderedValues.map((value, valueIndex) => (
593
+ <li>
594
+ {value}
595
+ <input
596
+ type='text'
597
+ placeholder='Enter value display name here'
598
+ value={filter.labels ? filter.labels[value] : undefined}
599
+ className='url-value-label'
600
+ onChange={e => {
601
+ let newFilters = [...config.filters]
602
+
603
+ newFilters[i].labels = newFilters[i].labels || {}
604
+ newFilters[i].labels[value] = e.target.value
605
+
606
+ setConfig({ ...config, filters: newFilters })
607
+ }}
608
+ />
609
+ <button
610
+ onClick={() => {
611
+ let newFilters = [...config.filters]
612
+
613
+ if (newFilters[i].labels) {
614
+ delete newFilters[i].labels[newFilters[i].orderedValues[valueIndex]]
615
+ }
616
+
617
+ newFilters[i].orderedValues.splice(valueIndex, 1)
618
+ setConfig({ ...config, filters: newFilters })
619
+ }}
620
+ >
621
+ X
622
+ </button>
623
+ </li>
624
+ ))}
625
+ </ul>
626
+ <form
627
+ onSubmit={e => {
628
+ e.preventDefault()
629
+ if (!config.filters[i].orderedValues || config.filters[i].orderedValues.indexOf(e.target[0].value) === -1) {
630
+ let newFilters = [...config.filters]
631
+ newFilters[i].orderedValues = newFilters[i].orderedValues || []
632
+ newFilters[i].orderedValues.push(e.target[0].value)
633
+ newFilters[i].values = newFilters[i].orderedValues
634
+ if (!newFilters[i].active) newFilters[i].active = e.target[0].value
635
+ e.target[0].value = ''
636
+ setConfig({ ...config, filters: newFilters })
637
+ }
638
+ }}
639
+ >
640
+ <input type='text' placeholder='Enter new value name here' />{' '}
641
+ <button type='submit' style={{ marginTop: '1em' }}>
642
+ Add New Value
643
+ </button>
644
+ </form>
645
+ </fieldset>
646
+ )
647
+ )}
648
+ <button
649
+ className='btn full-width'
650
+ onClick={() => {
651
+ setConfig({ ...config, filters: config.filters ? [...config.filters, { type: 'url', key: Date.now() }] : [{ type: 'url', key: Date.now() }] })
652
+ }}
653
+ >
654
+ Add New URL Filter
655
+ </button>
656
+ </>
657
+ )
658
+
494
659
  const showDataDesigner = config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Scatter Plot'
495
660
 
496
661
  return (
@@ -560,7 +725,7 @@ export default function DataImport() {
560
725
  <>
561
726
  <div className='heading-3'>Data Source</div>
562
727
  <div className='file-loaded-area'>
563
- {config.dataFileSourceType === 'file' && (
728
+ {(config.dataFileSourceType === 'file' || !config.dataFileSourceType) && (
564
729
  <div className='data-source-options'>
565
730
  <div className={isDragActive2 ? 'drag-active cdcdataviz-file-selector loaded-file' : 'cdcdataviz-file-selector loaded-file'} {...getRootProps2()}>
566
731
  <input {...getInputProps2()} />
@@ -577,10 +742,13 @@ export default function DataImport() {
577
742
  )}
578
743
 
579
744
  {config.dataFileSourceType === 'url' && (
580
- <div className='url-source-options'>
581
- <div>{loadFileFromUrl(externalURL)}</div>
582
- <div>{resetButton()}</div>
583
- </div>
745
+ <>
746
+ <div className='url-source-options'>
747
+ <div>{loadFileFromUrl(externalURL)}</div>
748
+ <div>{resetButton()}</div>
749
+ </div>
750
+ {config.dataUrl && (config.type === 'chart' || config.type === 'map') && urlFilters}
751
+ </>
584
752
  )}
585
753
  </div>
586
754
  </>
@@ -614,10 +782,10 @@ export default function DataImport() {
614
782
  {errors &&
615
783
  (errors.map
616
784
  ? errors.map((message, index) => (
617
- <div className='error-box slim mt-2' key={`error-${message}`}>
618
- <span>{message}</span> <CloseIcon className='inline-icon dismiss-error' onClick={() => setErrors(errors.filter((val, i) => i !== index))} />
619
- </div>
620
- ))
785
+ <div className='error-box slim mt-2' key={`error-${message}`}>
786
+ <span>{message}</span> <CloseIcon className='inline-icon dismiss-error' onClick={() => setErrors(errors.filter((val, i) => i !== index))} />
787
+ </div>
788
+ ))
621
789
  : errors.message)}
622
790
  <p className='footnote'>
623
791
  Supported file types: {Object.keys(supportedDataTypes).join(', ')}. Maximum file size {maxFileSize}MB.
@@ -627,29 +795,24 @@ export default function DataImport() {
627
795
  <SampleDataContext.Provider value={{ loadData, editingDataset, config }}>
628
796
  <SampleData.Buttons />
629
797
  </SampleDataContext.Provider>
630
- </div >
631
- )
632
- }
798
+ </div>
799
+ )}
633
800
 
634
- {
635
- config.type === 'dashboard' && !addingDataset && (
636
- <p>
637
- <button className='btn btn-primary' onClick={() => setAddingDataset(true)}>
638
- + Add More Files
639
- </button>
640
- </p>
641
- )
642
- }
801
+ {config.type === 'dashboard' && !addingDataset && (
802
+ <p>
803
+ <button className='btn btn-primary' onClick={() => setAddingDataset(true)}>
804
+ + Add More Files
805
+ </button>
806
+ </p>
807
+ )}
643
808
 
644
- {
645
- readyToConfigure && (
646
- <p>
647
- <button className='btn btn-primary' onClick={() => setGlobalActive(2)}>
648
- Configure your visualization
649
- </button>
650
- </p>
651
- )
652
- }
809
+ {readyToConfigure && (
810
+ <p>
811
+ <button className='btn btn-primary' onClick={() => setGlobalActive(2)}>
812
+ Configure your visualization
813
+ </button>
814
+ </p>
815
+ )}
653
816
 
654
817
  <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
818
  <div>
@@ -657,9 +820,9 @@ export default function DataImport() {
657
820
  <p>Documentation and examples on formatting data and configuring visualizations.</p>
658
821
  </div>
659
822
  </a>
660
- </div >
823
+ </div>
661
824
  <div className='right-col'>
662
- <PreviewDataTable data={previewData} />
825
+ <PreviewDataTable data={asyncPreviewData || previewData} />
663
826
  </div>
664
827
  </>
665
828
  )
@@ -28,12 +28,12 @@ const sampleData = {
28
28
  text: 'Scatter Plot Sample Data',
29
29
  fileName: 'valid-scatterplot.csv',
30
30
  data: validScatterPlot
31
+ },
32
+ {
33
+ text: 'Area Chart Sample Data',
34
+ fileName: 'valid-area-chart.json',
35
+ data: validAreaChart
31
36
  }
32
- // {
33
- // text: 'Area Chart Sample Data',
34
- // fileName: 'valid-area-chart.json',
35
- // data: validAreaChart
36
- // }
37
37
  ],
38
38
  maps: [
39
39
  {
package/src/index.jsx CHANGED
@@ -9,8 +9,10 @@ let activeTab = Number.parseInt(standaloneParams.get('active')) - 1 || null
9
9
 
10
10
  let domContainer = document.getElementsByClassName('react-container')[0]
11
11
 
12
+ let isDebug = window.location.href.includes('debug=true')
13
+
12
14
  ReactDOM.createRoot(domContainer).render(
13
15
  <React.StrictMode>
14
- <CdcEditor startingTab={activeTab} containerEl={domContainer} />
15
- </React.StrictMode>,
16
+ <CdcEditor startingTab={activeTab} containerEl={domContainer} isDebug={isDebug} />
17
+ </React.StrictMode>
16
18
  )