@cdc/dashboard 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.
@@ -2,6 +2,8 @@ import React, { useState, useEffect, useContext } from 'react'
2
2
 
3
3
  import ConfigContext from '../ConfigContext'
4
4
 
5
+ import { DataTransform } from '@cdc/core/helpers/DataTransform'
6
+ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
5
7
  import { useGlobalContext } from '@cdc/core/components/GlobalContext'
6
8
  import Modal from '@cdc/core/components/ui/Modal'
7
9
 
@@ -12,6 +14,8 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
12
14
 
13
15
  const [columns, setColumns] = useState([])
14
16
 
17
+ const transform = new DataTransform()
18
+
15
19
  const changeConfigValue = (parentObj, key, value) => {
16
20
  let newConfig = { ...config }
17
21
  if (!newConfig[parentObj]) newConfig[parentObj] = {}
@@ -44,6 +48,13 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
44
48
 
45
49
  dashboardConfig.sharedFilters.splice(index, 1)
46
50
 
51
+ // Ensures URL filters refresh after filter removal
52
+ if(dashboardConfig.datasets){
53
+ Object.keys(dashboardConfig.datasets).forEach(datasetKey => {
54
+ delete dashboardConfig.datasets[datasetKey].runtimeDataUrl
55
+ })
56
+ }
57
+
47
58
  updateConfig({ ...config, dashboard: dashboardConfig })
48
59
 
49
60
  overlay?.actions.toggleOverlay()
@@ -89,7 +100,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
89
100
  if (config.datasets[dataKeys[i]].dataDescription) {
90
101
  try {
91
102
  config.datasets[dataKeys[i]].data = transform.autoStandardize(config.datasets[dataKeys[i]].data)
92
- config.datasets[dataKeys[i]].data = transform.developerStandardize(config.datasets[dataKeys[i]].data, config.datasets[dataKey].dataDescription)
103
+ config.datasets[dataKeys[i]].data = transform.developerStandardize(config.datasets[dataKeys[i]].data, config.datasets[dataKeys[i]].dataDescription)
93
104
  } catch (e) {
94
105
  //Data not able to be standardized, leave as is
95
106
  }
@@ -97,7 +108,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
97
108
  }
98
109
 
99
110
  if (config.datasets[dataKeys[i]].data) {
100
- config.datasets[dataKeys[i]].data.map(row => {
111
+ config.datasets[dataKeys[i]].data.forEach(row => {
101
112
  Object.keys(row).forEach(columnName => (columns[columnName] = true))
102
113
  })
103
114
  }
@@ -127,6 +138,14 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
127
138
  overlay?.actions.openOverlay(filterModal(newFilter, index))
128
139
  }
129
140
 
141
+ const updateFilterPropByFunction = (index, func) => {
142
+ let newFilter = { ...filter }
143
+
144
+ newFilter = func(newFilter)
145
+
146
+ overlay?.actions.openOverlay(filterModal(newFilter, index))
147
+ }
148
+
130
149
  const addFilterUsedBy = (filter, index, value) => {
131
150
  if (!filter.usedBy) filter.usedBy = []
132
151
  filter.usedBy.push(value)
@@ -141,6 +160,47 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
141
160
  }
142
161
  }
143
162
 
163
+ const updateLabel = (e, value) => {
164
+ let newLabels = filter.labels || {}
165
+
166
+ newLabels[value] = e.target.value
167
+
168
+
169
+ updateFilterProp('labels', index, newLabels)
170
+ }
171
+
172
+ const removeValue = (valueIndex) => {
173
+ let newLabels = filter.labels || {}
174
+ let newValues = filter.values || []
175
+
176
+ delete newLabels[filter.values[valueIndex]]
177
+ newValues.splice(valueIndex, 1)
178
+
179
+ updateFilterPropByFunction(index, newFilter => {
180
+ newFilter.labels = newLabels
181
+ newFilter.orderedValue = newValues
182
+ return newFilter
183
+ })
184
+ }
185
+
186
+ const addNewValue = (e) => {
187
+ e.preventDefault()
188
+ if (!filter.values || filter.values.indexOf(e.target[0].value) === -1) {
189
+ let newValues = filter.values || []
190
+ newValues.push(e.target[0].value)
191
+
192
+ updateFilterPropByFunction(index, newFilter => {
193
+ newFilter.values = newValues
194
+ if (!newFilter.active) {
195
+ newFilter.active = e.target[0].value
196
+ }
197
+ return newFilter
198
+ })
199
+
200
+ e.target[0].value = ''
201
+ }
202
+ }
203
+
144
204
  return (
145
205
  <Modal>
146
206
  <Modal.Content>
@@ -156,81 +216,146 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
156
216
  Remove Filter
157
217
  </button>
158
218
  <label>
159
- <span className='edit-label column-heading'>Filter: </span>
160
- <select
161
- value={filter.columnName}
162
- onChange={e => {
163
- updateFilterProp('columnName', index, e.target.value)
164
- }}
165
- >
166
- <option value=''>- Select Option -</option>
167
- {columns.map(dataKey => (
168
- <option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
169
- {dataKey}
170
- </option>
171
- ))}
172
- </select>
173
- </label>
174
- <label>
175
- <span className='edit-label column-heading'>Label: </span>
176
- <input
177
- type='text'
178
- value={filter.key}
179
- onChange={e => {
180
- updateFilterProp('key', index, e.target.value)
181
- }}
182
- />
183
- </label>
184
- <label>
185
- <span className='edit-label column-heading'>Show Dropdown</span>
186
- <input
187
- type='checkbox'
188
- defaultChecked={filter.showDropdown === true}
189
- onChange={e => {
190
- updateFilterProp('showDropdown', index, !filter.showDropdown)
191
- }}
192
- />
193
- </label>
194
- <label>
195
- <span className='edit-label column-heading'>Set By: </span>
196
- <select value={filter.setBy} onChange={e => updateFilterProp('setBy', index, e.target.value)}>
197
- <option value=''>- Select Option -</option>
198
- {Object.keys(config.visualizations).map(vizKey => (
199
- <option value={vizKey} key={`set-by-select-item-${vizKey}`}>
200
- {config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
201
- </option>
202
- ))}
203
- </select>
204
- </label>
205
- <label>
206
- <span className='edit-label column-heading'>Used By:</span>
207
- <ul>
208
- {filter.usedBy &&
209
- filter.usedBy.map(vizKey => (
210
- <li key={`used-by-list-item-${vizKey}`}>
211
- <span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
219
+ <span className='edit-label column-heading'>Filter Type:</span>
220
+ <select defaultValue={filter.type || 'data'} onChange={e => updateFilterProp('type', index, e.target.value)}>
221
+ <option value="url">URL</option>
222
+ <option value="data">Data</option>
223
+ </select>
224
+ </label>
225
+ {filter.type === 'url' && <>
226
+ <label>
227
+ <span className='edit-label column-heading'>Label: </span>
228
+ <input
229
+ type='text'
230
+ value={filter.key}
231
+ onChange={e => {
232
+ updateFilterProp('key', index, e.target.value)
233
+ }}
234
+ />
235
+ </label>
236
+ <label>
237
+ <span className='edit-label column-heading'>URL to Filter: </span>
238
+ <select defaultValue={filter.datasetKey || ''} onChange={e => updateFilterProp('datasetKey', index, e.target.value)}>
239
+ <option value=''>- Select Option -</option>
240
+ {Object.keys(config.datasets).map(datasetKey => {
241
+ if(config.datasets[datasetKey].dataUrl){
242
+ return <option key={datasetKey} value={datasetKey}>{(config.datasets[datasetKey].dataUrl).substring(0, 50)}</option>
243
+ }
244
+ return <React.Fragment key={datasetKey}></React.Fragment>;
245
+ })}
246
+ </select>
247
+ </label>
248
+ <label>
249
+ <span className='edit-label column-heading'>Query string parameter</span>{' '}
250
+ <input
251
+ type='text'
252
+ defaultValue={filter.queryParameter}
253
+ onChange={e => updateFilterProp('queryParameter', index, e.target.value)}
254
+ />
255
+ </label>
256
+ <span className='edit-label column-heading'>Values</span>{' '}
257
+ <ul className='value-list'>
258
+ {filter.values &&
259
+ filter.values.map((value, valueIndex) => (
260
+ <li>
261
+ {value}
262
+ <input
263
+ type='text'
264
+ value={filter.labels ? filter.labels[value] : undefined}
265
+ onChange={(e) => updateLabel(e, value)}
266
+ />
212
267
  <button
213
- onClick={e => {
214
- e.preventDefault()
215
- removeFilterUsedBy(filter, index, vizKey)
216
- }}
268
+ onClick={() => removeValue(valueIndex)}
217
269
  >
218
270
  X
219
271
  </button>
220
272
  </li>
221
273
  ))}
222
274
  </ul>
223
- <select onChange={e => addFilterUsedBy(filter, index, e.target.value)}>
224
- <option value=''>- Select Option -</option>
225
- {Object.keys(config.visualizations)
226
- .filter(vizKey => filter.setBy !== vizKey && (!filter.usedBy || filter.usedBy.indexOf(vizKey) === -1) && !config.visualizations[vizKey].usesSharedFilter)
227
- .map(vizKey => (
228
- <option value={vizKey} key={`used-by-select-item-${vizKey}`}>
275
+ <form
276
+ onSubmit={addNewValue}
277
+ >
278
+ <input type='text' /> <button type='submit'>Add New Value</button>
279
+ </form>
280
+ </>}
281
+ {filter.type !== 'url' && <>
282
+ <label>
283
+ <span className='edit-label column-heading'>Filter: </span>
284
+ <select
285
+ value={filter.columnName}
286
+ onChange={e => {
287
+ updateFilterProp('columnName', index, e.target.value)
288
+ }}
289
+ >
290
+ <option value=''>- Select Option -</option>
291
+ {columns.map(dataKey => (
292
+ <option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
293
+ {dataKey}
294
+ </option>
295
+ ))}
296
+ </select>
297
+ </label>
298
+ <label>
299
+ <span className='edit-label column-heading'>Label: </span>
300
+ <input
301
+ type='text'
302
+ value={filter.key}
303
+ onChange={e => {
304
+ updateFilterProp('key', index, e.target.value)
305
+ }}
306
+ />
307
+ </label>
308
+ <label>
309
+ <span className='edit-label column-heading'>Show Dropdown</span>
310
+ <input
311
+ type='checkbox'
312
+ defaultChecked={filter.showDropdown === true}
313
+ onChange={e => {
314
+ updateFilterProp('showDropdown', index, !filter.showDropdown)
315
+ }}
316
+ />
317
+ </label>
318
+ <label>
319
+ <span className='edit-label column-heading'>Set By: </span>
320
+ <select value={filter.setBy} onChange={e => updateFilterProp('setBy', index, e.target.value)}>
321
+ <option value=''>- Select Option -</option>
322
+ {Object.keys(config.visualizations).map(vizKey => (
323
+ <option value={vizKey} key={`set-by-select-item-${vizKey}`}>
229
324
  {config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
230
325
  </option>
231
326
  ))}
232
- </select>
233
- </label>
327
+ </select>
328
+ </label>
329
+ <label>
330
+ <span className='edit-label column-heading'>Used By:</span>
331
+ <ul>
332
+ {filter.usedBy &&
333
+ filter.usedBy.map(vizKey => (
334
+ <li key={`used-by-list-item-${vizKey}`}>
335
+ <span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
336
+ <button
337
+ onClick={e => {
338
+ e.preventDefault()
339
+ removeFilterUsedBy(filter, index, vizKey)
340
+ }}
341
+ >
342
+ X
343
+ </button>
344
+ </li>
345
+ ))}
346
+ </ul>
347
+ <select onChange={e => addFilterUsedBy(filter, index, e.target.value)}>
348
+ <option value=''>- Select Option -</option>
349
+ {Object.keys(config.visualizations)
350
+ .filter(vizKey => filter.setBy !== vizKey && (!filter.usedBy || filter.usedBy.indexOf(vizKey) === -1) && !config.visualizations[vizKey].usesSharedFilter)
351
+ .map(vizKey => (
352
+ <option value={vizKey} key={`used-by-select-item-${vizKey}`}>
353
+ {config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
354
+ </option>
355
+ ))}
356
+ </select>
357
+ </label>
358
+ </>}
234
359
  </fieldset>
235
360
  <button type='button' className='btn btn-primary' style={{ display: 'inline-block', 'margin-right': '1em' }} onClick={overlay?.actions.toggleOverlay}>
236
361
  Cancel
@@ -320,7 +445,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
320
445
  <div className="wrap">
321
446
  <label>
322
447
  <input type='checkbox' defaultChecked={config.table.show} onChange={e => changeConfigValue('table', 'show', e.target.checked)} />
323
- Show Table
448
+ Show Data Table(s)
324
449
  </label><br />
325
450
 
326
451
  <label>
@@ -351,11 +476,11 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
351
476
  <div className="wrap">
352
477
  <label>
353
478
  <input type='checkbox' defaultChecked={config.table.download} onChange={e => changeConfigValue('table', 'download', e.target.checked)} />
354
- Show CSV Button
479
+ Show Download CSV Link
355
480
  </label>
356
481
  <label>
357
482
  <input type='checkbox' defaultChecked={config.table.showDownloadUrl} onChange={e => changeConfigValue('table', 'showDownloadUrl', e.target.checked)} />
358
- Show Link to Dataset
483
+ Show URL to Automatically Updated Data
359
484
  </label>
360
485
  </div>
361
486
  </>
@@ -7,6 +7,7 @@ export default {
7
7
  table: {
8
8
  label: 'Data Table',
9
9
  show: true,
10
- showDownloadUrl: false
10
+ showDownloadUrl: false,
11
+ showVertical: true,
11
12
  }
12
13
  }
package/src/index.jsx CHANGED
@@ -4,11 +4,12 @@ import ReactDOM from 'react-dom/client'
4
4
  import CdcDashboard from './CdcDashboard'
5
5
 
6
6
  let isEditor = window.location.href.includes('editor=true')
7
+ let isDebug = window.location.href.includes('debug=true')
7
8
 
8
9
  let domContainer = document.getElementsByClassName('react-container')[0]
9
10
 
10
11
  ReactDOM.createRoot(domContainer).render(
11
12
  <React.StrictMode>
12
- <CdcDashboard configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} />
13
- </React.StrictMode>,
13
+ <CdcDashboard configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} isDebug={isDebug} />
14
+ </React.StrictMode>
14
15
  )
@@ -48,7 +48,8 @@
48
48
  .wrap {
49
49
  display: inline-flex;
50
50
  flex-wrap: wrap;
51
- width: 20%;
51
+ width: 33%; // changed from 20% so that long checkbox name will fit without wrapping
52
+ font-size: 0.8em;
52
53
 
53
54
  .table-height-input {
54
55
  width: 100%;
@@ -322,4 +323,4 @@
322
323
  display: block;
323
324
  margin: 1em 0;
324
325
  }
325
- }
326
+ }