@cdc/editor 4.24.5 → 4.24.9-1

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.24.5",
3
+ "version": "4.24.9-1",
4
4
  "description": "React component for generating a new component entry",
5
5
  "moduleName": "CdcEditor",
6
6
  "main": "dist/cdceditor",
@@ -11,8 +11,9 @@
11
11
  "preview": "vite preview",
12
12
  "graph": "nx graph",
13
13
  "prepublishOnly": "lerna run --scope @cdc/editor build",
14
- "test": "vitest watch --reporter verbose",
15
- "test:ui": "vitest --ui"
14
+ "test": "vitest run --reporter verbose",
15
+ "test-watch": "vitest watch --reporter verbose",
16
+ "test-watch:ui": "vitest --ui"
16
17
  },
17
18
  "repository": {
18
19
  "type": "git",
@@ -24,13 +25,13 @@
24
25
  },
25
26
  "license": "Apache-2.0",
26
27
  "dependencies": {
27
- "@cdc/chart": "^4.24.5",
28
- "@cdc/core": "^4.24.5",
29
- "@cdc/dashboard": "^4.24.5",
30
- "@cdc/data-bite": "^4.24.5",
31
- "@cdc/map": "^4.24.5",
32
- "@cdc/markup-include": "^4.24.5",
33
- "@cdc/waffle-chart": "^4.24.5",
28
+ "@cdc/chart": "^4.24.9-1",
29
+ "@cdc/core": "^4.24.9-1",
30
+ "@cdc/dashboard": "^4.24.9-1",
31
+ "@cdc/data-bite": "^4.24.9-1",
32
+ "@cdc/map": "^4.24.9-1",
33
+ "@cdc/markup-include": "^4.24.9-1",
34
+ "@cdc/waffle-chart": "^4.24.9-1",
34
35
  "axios": "^1.6.0",
35
36
  "d3": "^7.0.0",
36
37
  "html-react-parser": "^3.0.8",
@@ -43,5 +44,5 @@
43
44
  "react": "^18.2.0",
44
45
  "react-dom": "^18.2.0"
45
46
  },
46
- "gitHead": "def85aaf4cd9dc1983e80f2900199f35de82af95"
47
+ "gitHead": "4a77c2fa79c8fa6074da3b6dfee3d8e32f0b2586"
47
48
  }
package/src/CdcEditor.tsx CHANGED
@@ -16,7 +16,7 @@ import ChooseTab from './components/ChooseTab'
16
16
  import ConfigureTab from './components/ConfigureTab'
17
17
  import TabPane from './components/TabPane'
18
18
  import { GlobalTabs as Tabs } from './components/Tabs'
19
- import { formatConfigBeforeSave as stripConfig } from './helpers/formatConfigBeforeSave'
19
+ import { formatConfigBeforeSave as stripConfig } from '@cdc/core/helpers/formatConfigBeforeSave'
20
20
  import { saveConfigToWindow as updateVizConfig } from './helpers/saveConfigToWindow'
21
21
  import { legacyConfigSupport } from './helpers/legacyConfigSupport'
22
22
 
@@ -45,15 +45,15 @@ const CdcEditor: React.FC<WCMSProps> = ({ config: configObj, hostname, container
45
45
  const [state, dispatch] = useReducer(editorReducer, initialState)
46
46
 
47
47
  const setTempConfigAndUpdate = config => {
48
- updateVizConfig(JSON.parse(JSON.stringify(config)))
48
+ updateVizConfig(_.cloneDeep(config))
49
49
  dispatch({ type: 'EDITOR_TEMP_SAVE', payload: config })
50
50
  }
51
51
 
52
52
  const resizeObserver = new ResizeObserver(entries => {
53
53
  const container = entries[0]
54
- let { width } = container.contentRect
55
- let newViewport = getViewport(width)
56
- dispatch({ type: 'EDITOR_SET_VIEWPORT', payload: newViewport })
54
+ const { width } = container.contentRect
55
+ const newViewport = getViewport(width)
56
+ if (state.currentViewport !== newViewport) dispatch({ type: 'EDITOR_SET_VIEWPORT', payload: newViewport })
57
57
  })
58
58
 
59
59
  const outerContainerRef = useCallback(node => {
@@ -1,8 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react'
2
2
  import CdcEditor from '../CdcEditor'
3
- import StandAloneTableConfig from './_mock/standalone-table.json'
4
- import BlankConfig from './_mock/blank-config.json'
5
- import PivotFilterConfig from './_mock/pivot-filter.json'
6
3
 
7
4
  const meta: Meta<typeof CdcEditor> = {
8
5
  title: 'Components/Pages/CdcEditor',
@@ -14,6 +11,6 @@ type Story = StoryObj<typeof CdcEditor>
14
11
 
15
12
  export const DefaultEditor: Story = {
16
13
  args: {
17
- config: BlankConfig
14
+ config: {}
18
15
  }
19
16
  }
@@ -26,6 +26,7 @@ import ForecastIcon from '@cdc/core/assets/icon-chart-forecast.svg'
26
26
  import DeviationIcon from '@cdc/core/assets/icon-deviation-bar.svg'
27
27
  import SankeyIcon from '@cdc/core/assets/icon-sankey.svg'
28
28
  import { Visualization } from '@cdc/core/types/Visualization'
29
+ import Icon from '@cdc/core/components/ui/Icon'
29
30
 
30
31
  export default function ChooseTab() {
31
32
  const { config, tempConfig } = useContext(ConfigContext)
@@ -68,17 +69,17 @@ export default function ChooseTab() {
68
69
  classNames = config.type === type && isSubType && !isHorizontalStackedChart ? 'active' : ''
69
70
  }
70
71
 
71
- let setTypes = () => {
72
+ const setTypes = () => {
72
73
  if (type === config.type) {
73
74
  if (subType !== config.visualizationType) {
74
75
  dispatch({ type: 'EDITOR_SET_CONFIG', payload: { ...config, newViz: true, visualizationType: subType } })
75
76
  }
76
77
  dispatch({ type: 'EDITOR_SET_GLOBALACTIVE', payload: 1 })
77
78
  } else {
78
- let confirmation = !config.type || window.confirm('Changing visualization type will clear configuration settings. Do you want to continue?')
79
+ const confirmation = !config.type || window.confirm('Changing visualization type will clear configuration settings. Do you want to continue?')
79
80
 
80
81
  if (confirmation) {
81
- let newConfig = {
82
+ const newConfig = {
82
83
  newViz: true,
83
84
  datasets: {},
84
85
  type
@@ -112,6 +113,22 @@ export default function ChooseTab() {
112
113
  )
113
114
  }
114
115
 
116
+ const handleUpload = e => {
117
+ const file = e.target.files[0]
118
+ const reader = new FileReader()
119
+ reader.onload = e => {
120
+ const text = e.target.result
121
+ try {
122
+ const newConfig = JSON.parse(text as string)
123
+ dispatch({ type: 'EDITOR_SET_CONFIG', payload: newConfig })
124
+ dispatch({ type: 'EDITOR_SET_GLOBALACTIVE', payload: 1 })
125
+ } catch (e) {
126
+ alert('Invalid JSON')
127
+ }
128
+ }
129
+ reader.readAsText(file)
130
+ }
131
+
115
132
  return (
116
133
  <div className='choose-vis'>
117
134
  <a href='https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/index.html' target='_blank' rel='noopener noreferrer' className='guidance-link' style={{ marginTop: 0, marginBottom: '2rem' }}>
@@ -309,6 +326,21 @@ export default function ChooseTab() {
309
326
  </Tooltip>
310
327
  </li>
311
328
  </ul>
329
+ <hr />
330
+ <div className='form-group'>
331
+ <label htmlFor='uploadConfig'>
332
+ Upload Custom Configuration{' '}
333
+ <Tooltip style={{ textTransform: 'none' }}>
334
+ <Tooltip.Target>
335
+ <Icon display='warningCircle' style={{ marginLeft: '0.5rem' }} />
336
+ </Tooltip.Target>
337
+ <Tooltip.Content>
338
+ <p>Make sure you have properly validated the configuration before uploading.</p>
339
+ </Tooltip.Content>
340
+ </Tooltip>
341
+ </label>
342
+ <input type='file' accept='.txt,.json' className='form-control-file' id='uploadConfig' onChange={handleUpload} />
343
+ </div>
312
344
  </div>
313
345
  )
314
346
  }
@@ -132,6 +132,10 @@ export default function DataImport() {
132
132
  throw errorMessages.failedFetch
133
133
  }
134
134
 
135
+ if (config.type === 'dashboard') {
136
+ setExternalURL('')
137
+ }
138
+
135
139
  return responseBlob
136
140
  }
137
141
 
@@ -435,6 +439,7 @@ export default function DataImport() {
435
439
 
436
440
  let configureData,
437
441
  readyToConfigure = false
442
+
438
443
  if (config.type === 'dashboard') {
439
444
  readyToConfigure = Object.keys(config.datasets).length > 0
440
445
  } else {
@@ -442,6 +447,10 @@ export default function DataImport() {
442
447
  readyToConfigure = !!config.formattedData || (config.data && config.dataDescription && transform.autoStandardize(config.data))
443
448
  }
444
449
 
450
+ if (config.visualizationType === 'Sankey' && config.data) {
451
+ readyToConfigure = true
452
+ }
453
+
445
454
  // Box plots skip the data description steps.
446
455
  // If we have data and the visualizations is a box plot proceed...
447
456
  if ((config.visualizationType === 'Box Plot' && config.data) || config.visualizationType === 'Scatter Plot') {
@@ -581,7 +590,7 @@ export default function DataImport() {
581
590
  </>
582
591
  )
583
592
 
584
- const showDataDesigner = config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Scatter Plot'
593
+ const showDataDesigner = !['Box Plot', 'Scatter Plot', 'Sankey'].includes(config?.visualizationType)
585
594
 
586
595
  return (
587
596
  <>
@@ -71,37 +71,45 @@ const PreviewDataTable = () => {
71
71
 
72
72
  const dispatch = useContext(EditorDispatchContext)
73
73
 
74
- const fetchAsyncData = async () => {
75
- if (config.type === 'dashboard') {
76
- Object.keys(config.datasets).forEach(async datasetKey => {
77
- if (config.datasets[datasetKey].preview) {
78
- if (config.datasets[datasetKey].dataUrl) {
79
- const remoteData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
80
- if (Array.isArray(remoteData)) {
81
- setTableData(remoteData)
82
- }
83
- } else if (Array.isArray(config.datasets[datasetKey].data)) {
84
- setTableData(config.datasets[datasetKey].data)
85
- }
86
- }
87
- })
88
- } else {
89
- if (config.dataUrl) {
90
- const remoteData = await fetchRemoteData(config.dataUrl)
74
+ const fetchDatasetData = async (datasetKey, datasetConfig) => {
75
+ if (datasetConfig.preview) {
76
+ if (datasetConfig.dataUrl) {
77
+ const remoteData = await fetchRemoteData(datasetConfig.dataUrl)
91
78
  if (Array.isArray(remoteData)) {
92
79
  setTableData(remoteData)
93
80
  }
81
+ } else if (Array.isArray(datasetConfig.data)) {
82
+ setTableData(datasetConfig.data)
94
83
  }
95
84
  }
96
85
  }
97
86
 
87
+ const handleDashboardData = async datasets => {
88
+ for (const datasetKey of Object.keys(datasets)) {
89
+ await fetchDatasetData(datasetKey, datasets[datasetKey])
90
+ }
91
+ }
92
+
98
93
  useEffect(() => {
99
- if (!config.data) {
100
- fetchAsyncData()
101
- } else {
102
- setTableData(previewData)
94
+ const loadData = async () => {
95
+ if (!config.data) {
96
+ if (config.type === 'dashboard') {
97
+ await handleDashboardData(config.datasets)
98
+ } else {
99
+ if (config.dataUrl) {
100
+ const remoteData = await fetchRemoteData(config.dataUrl)
101
+ if (Array.isArray(remoteData)) {
102
+ setTableData(remoteData)
103
+ }
104
+ }
105
+ }
106
+ } else {
107
+ setTableData(previewData)
108
+ }
103
109
  }
104
- }, [config.data]) // eslint-disable-line
110
+
111
+ loadData()
112
+ }, [config, config.data, previewData]) // eslint-disable-line
105
113
 
106
114
  const tableColumns = useMemo(() => {
107
115
  if (!tableData) return []
@@ -3,6 +3,7 @@ import SampleDataContext from '../samples/SampleDataContext'
3
3
 
4
4
  // Data Samples
5
5
  import validMapData from './../samples/valid-data-map.csv?raw'
6
+ import validMapDataFootnotes from './../samples/valid-data-map-footnotes.csv?raw'
6
7
  import validChartData from './../samples/valid-data-chart.csv?raw'
7
8
  import validCountyMapData from './../samples/valid-county-data.csv?raw'
8
9
  import validGeoPoint from './../samples/valid-geo-point.csv?raw'
@@ -11,10 +12,10 @@ import validBoxPlotData from './../samples/valid-boxplot.csv?raw'
11
12
  import validAreaChart from './../samples/valid-area-chart.json?raw'
12
13
  import validWorldGeocodeData from './../samples/valid-world-geocode.json?raw'
13
14
  import validForecastData from './../samples/valid-forecast-data.csv?raw'
14
- import validForestPlotData from './../samples/valid-forest-plot-data.csv?raw'
15
15
  import vaidWorldData from './../samples/valid-world-data.json?raw'
16
16
  import validRegionData from './../samples/valid-region-data.json?raw'
17
17
  import validSankeyData from './../samples/valid-sankey-data.json?raw'
18
+ import pivotData from './../samples/pivotData.json?raw'
18
19
 
19
20
  // Add additional data to samples
20
21
  const sampleData = {
@@ -44,15 +45,15 @@ const sampleData = {
44
45
  fileName: 'valid-forecast-data.csv',
45
46
  data: validForecastData
46
47
  },
47
- // {
48
- // text: 'Forest Plot Data',
49
- // fileName: 'valid-forest-plot-data.csv',
50
- // data: validForestPlotData
51
- // },
52
48
  {
53
49
  text: 'Sankey Chart Data',
54
50
  fileName: 'valid-sankey-data.json',
55
51
  data: validSankeyData
52
+ },
53
+ {
54
+ text: 'Pivot Table Data',
55
+ fileName: 'pivotData.json',
56
+ data: pivotData
56
57
  }
57
58
  ],
58
59
  maps: [
@@ -61,6 +62,11 @@ const sampleData = {
61
62
  fileName: 'valid-data-map.csv',
62
63
  data: validMapData
63
64
  },
65
+ {
66
+ text: 'United States: State Sample Data Footnotes',
67
+ fileName: 'valid-data-map-footnotes.csv',
68
+ data: validMapDataFootnotes
69
+ },
64
70
  {
65
71
  text: 'United States: County Sample Data',
66
72
  fileName: 'valid-county-data.csv',
@@ -1,4 +1,4 @@
1
- import { formatConfigBeforeSave } from './formatConfigBeforeSave'
1
+ import { formatConfigBeforeSave } from '@cdc/core/helpers/formatConfigBeforeSave'
2
2
 
3
3
  export const saveConfigToWindow = newTempConfig => {
4
4
  if (null !== newTempConfig) {
@@ -0,0 +1,11 @@
1
+ [
2
+ { "Year": 2019, "Country": "USA", "Rate": 2.5 },
3
+ { "Year": 2019, "Country": "Canada", "Rate": 3.2 },
4
+ { "Year": 2019, "Country": "Germany", "Rate": 1.8 },
5
+ { "Year": 2020, "Country": "USA", "Rate": 2.7 },
6
+ { "Year": 2020, "Country": "Canada", "Rate": 3.5 },
7
+ { "Year": 2020, "Country": "Germany", "Rate": 2.1 },
8
+ { "Year": 2021, "Country": "USA", "Rate": 3.0 },
9
+ { "Year": 2021, "Country": "Canada", "Rate": 3.8 },
10
+ { "Year": 2021, "Country": "Germany", "Rate": 2.4 }
11
+ ]
@@ -0,0 +1,8 @@
1
+ Location,symbolCol,textCol,orderCol
2
+ School,¶,Footnote 4,4
3
+ School,§,Footnote 3,3
4
+ School,†,Footnote 2,2
5
+ School,*,Footnote 1,1
6
+ Vehicle,*,Some Vehicle Footnote,1
7
+ Work,*,Some Work Footnote,1
8
+ Home,*,Some Home Footnote,1
@@ -1,79 +0,0 @@
1
- {
2
- "dashboard": {
3
- "theme": "theme-blue"
4
- },
5
- "rows": [
6
- [
7
- {
8
- "width": 12
9
- },
10
- {},
11
- {}
12
- ]
13
- ],
14
- "visualizations": {},
15
- "table": {
16
- "label": "Data Table",
17
- "show": true,
18
- "showDownloadUrl": false,
19
- "showVertical": true
20
- },
21
- "newViz": true,
22
- "datasets": {
23
- "https://data.cdc.gov/resource/vdgb-f9s3.json": {
24
- "data": [
25
- {
26
- "surveyname": "American Indian Adult Tobacco Survey",
27
- "surveynameabbrev": "AI ATS",
28
- "year": "2007",
29
- "e_cigarettes": "No",
30
- "question": "During the past 12 months, have you stopped smoking for one day or longer because you were trying to quit smoking?",
31
- "responses": "Don't know/Not sure"
32
- },
33
- {
34
- "surveyname": "American Indian Adult Tobacco Survey",
35
- "surveynameabbrev": "AI ATS",
36
- "year": "2007",
37
- "e_cigarettes": "No",
38
- "question": "During the past 12 months, have you stopped smoking for one day or longer because you were trying to quit smoking?",
39
- "responses": "Refused"
40
- },
41
- {
42
- "surveyname": "American Indian Adult Tobacco Survey",
43
- "surveynameabbrev": "AI ATS",
44
- "year": "2007",
45
- "e_cigarettes": "No",
46
- "question": "During the past 12 months, have you stopped smoking for one day or longer because you were trying to quit smoking?",
47
- "responses": "No"
48
- }
49
- ]
50
- }
51
- },
52
- "data": [
53
- {
54
- "surveyname": "American Indian Adult Tobacco Survey",
55
- "surveynameabbrev": "AI ATS",
56
- "year": "2007",
57
- "e_cigarettes": "No",
58
- "question": "During the past 12 months, have you stopped smoking for one day or longer because you were trying to quit smoking?",
59
- "responses": "Don't know/Not sure"
60
- },
61
- {
62
- "surveyname": "American Indian Adult Tobacco Survey",
63
- "surveynameabbrev": "AI ATS",
64
- "year": "2007",
65
- "e_cigarettes": "No",
66
- "question": "During the past 12 months, have you stopped smoking for one day or longer because you were trying to quit smoking?",
67
- "responses": "Refused"
68
- },
69
- {
70
- "surveyname": "American Indian Adult Tobacco Survey",
71
- "surveynameabbrev": "AI ATS",
72
- "year": "2007",
73
- "e_cigarettes": "No",
74
- "question": "During the past 12 months, have you stopped smoking for one day or longer because you were trying to quit smoking?",
75
- "responses": "No"
76
- }
77
- ],
78
- "type": "dashboard"
79
- }
@@ -1,36 +0,0 @@
1
- import _ from 'lodash'
2
-
3
- export const formatConfigBeforeSave = configToStrip => {
4
- let strippedConfig = { ...configToStrip }
5
-
6
- if (strippedConfig.type === 'dashboard') {
7
- if (strippedConfig.datasets) {
8
- Object.keys(strippedConfig.datasets).forEach(datasetKey => {
9
- delete strippedConfig.datasets[datasetKey].formattedData
10
- if (strippedConfig.datasets[datasetKey].dataUrl) {
11
- delete strippedConfig.datasets[datasetKey].data
12
- }
13
- })
14
- }
15
- if (strippedConfig.visualizations) {
16
- Object.keys(strippedConfig.visualizations).forEach(vizKey => {
17
- strippedConfig.visualizations[vizKey] = _.omit(strippedConfig.visualizations[vizKey], ['runtime', 'formattedData', 'data'])
18
- })
19
- }
20
- if (strippedConfig.rows) {
21
- strippedConfig.rows.forEach(row => {
22
- if (row.dataKey) {
23
- row = _.omit(row, ['data', 'formattedData'])
24
- }
25
- })
26
- }
27
- } else {
28
- delete strippedConfig.runtime
29
- delete strippedConfig.formattedData
30
- if (strippedConfig.dataUrl) {
31
- delete strippedConfig.data
32
- }
33
- }
34
-
35
- return strippedConfig
36
- }