@cdc/core 4.23.7 → 4.23.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.
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><path d="M89.56,65.41H16.36V8.8c0-1.1-.9-2-2-2h-5.48c-1.1,0-2,.9-2,2V72.89c0,1.1,.9,2,2,2H89.56c1.1,0,2-.9,2-2v-5.48c0-1.1-.9-2-2-2Z"/><path d="M54.66,55.34c-17.76,0-33.46-17.8-34.12-18.56l-.98-1.13v-4.55l3.25,3.72c.15,.18,15.47,17.53,31.86,17.53h0c7.31,0,12.37-1.68,17.27-3.3,4.31-1.42,8.77-2.9,14.44-3.05l1.5-.04v3l-1.42,.04c-5.24,.14-9.29,1.48-13.59,2.9-5.13,1.69-10.43,3.45-18.22,3.45h0Z"/><path d="M87.84,59.04c-4.73-1.24-7.41-1.66-13.05-1.93-2.82-.14-5.22,.16-8,.5-2.73,.33-5.81,.71-10.06,.79-13.13,.27-23.77-4.63-37.17-17.31v-1.57c13.19,12.48,24.31,18.13,37.15,17.88,4.2-.08,7.26-.46,9.96-.79,2.83-.34,5.27-.64,8.17-.5,5.73,.28,8.2,.62,13,1.88v1.05Z"/><path d="M57.02,49.28c-19.55,0-30.91-14.65-37.69-23.41v-1.54c6.67,8.61,18.64,23.95,37.69,23.95,5.87,0,14.11-3.23,18.39-6.23,2.43-1.7,3.84-2.77,4.87-3.55,2.24-1.7,2.7-2.05,7.71-4.44l-.03,1.12c-4.92,2.34-4.91,2.47-7.08,4.12-1.04,.79-2.45,1.86-4.9,3.57-4.4,3.08-12.9,6.41-18.96,6.41Z"/></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><defs><style>.b{stroke-width:3px;}.b,.c,.d{stroke-linecap:round;}.b,.c,.d,.e{stroke:#000;}.c,.d,.e{fill:none;}.d{stroke-dasharray:0 0 0 4.07;}.e{stroke-width:2px;}</style></defs><path d="M14.15,8.39v63.29c0,.87,.89,1.59,1.98,1.59h5.42c1.09,0,1.98-.72,1.98-1.59l-.13-63.29c0-.87-.89-1.59-1.98-1.59h-5.28c-1.09,0-1.98,.72-1.98,1.59Z"/><g><line class="c" x1="53.51" y1="10.09" x2="53.51" y2="10.09"/><line class="d" x1="53.51" y1="14.16" x2="53.51" y2="69.05"/><line class="c" x1="53.51" y1="71.08" x2="53.51" y2="71.08"/></g><g><path class="b" d="M29.78,16.97h34.43"/><path class="b" d="M29.78,21.48V12.45"/><path class="b" d="M65.64,21.48V12.45"/></g><g><path class="b" d="M50.62,33.5h28.85"/><path class="b" d="M50.62,38.02v-9.03"/><path class="b" d="M80.67,38.02v-9.03"/></g><g><path class="b" d="M28.21,50.03h19.26"/><path class="b" d="M28.21,54.55v-9.03"/><path class="b" d="M48.27,54.55v-9.03"/></g><g><path class="b" d="M40.69,66.57h33.99"/><path class="b" d="M40.69,71.08v-9.03"/><path class="b" d="M76.1,71.08v-9.03"/></g><circle class="e" cx="47.47" cy="16.97" r="2.2"/><circle class="e" cx="65.64" cy="33.5" r="2.2"/><circle class="e" cx="38.49" cy="50.03" r="2.2"/><circle class="e" cx="58.31" cy="66.57" r="2.2"/></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><defs><style>.b{fill:none;}.b,.c{stroke:#000;stroke-linejoin:round;}</style></defs><path d="M89.93,65.41H16.73V8.8c0-1.1-.9-2-2-2h-5.48c-1.1,0-2,.9-2,2V72.89c0,1.1,.9,2,2,2H89.93c1.1,0,2-.9,2-2v-5.48c0-1.1-.9-2-2-2Z"/><polygon points="22.7 54.03 22.7 59.31 89.66 59.31 89.66 37.5 70.66 47.11 52.54 42.02 43.1 48.03 35.85 44.59 22.7 54.03"/><polygon class="b" points="22.7 47.3 22.7 59.31 89.66 59.31 89.66 25.48 70.66 35.35 52.54 35.3 43.1 40.03 36.46 31.3 22.7 47.3"/><polygon class="c" points="70.66 23.24 52.54 15.09 43.1 21.1 35.85 25.48 22.7 27.11 22.7 47.3 36.46 31.3 43.1 40.03 52.54 35.3 70.66 35.35 89.66 25.48 89.66 10.58 70.66 23.24"/></svg>
@@ -37,6 +37,25 @@ const DataTable = props => {
37
37
  // Catch all sorting method used on load by default but also on user click
38
38
  // Having a custom method means we can add in any business logic we want going forward
39
39
  const customSort = (a, b) => {
40
+ const isDateA = Date.parse(a)
41
+ const isDateB = Date.parse(b)
42
+
43
+ const isNumberA = !isNaN(a)
44
+ const isNumberB = !isNaN(b)
45
+
46
+ if (isDateA && isDateB) {
47
+ return sortBy.asc ? new Date(a) - new Date(b) : new Date(b) - new Date(a)
48
+ }
49
+ if (isNumberA && isNumberB) {
50
+ return sortBy.asc ? Number(a) - Number(b) : Number(b) - Number(a)
51
+ }
52
+ if (typeof a === 'string' && typeof b === 'string') {
53
+ return sortBy.asc ? a.localeCompare(b) : b.localeCompare(a)
54
+ }
55
+
56
+ return 0
57
+ }
58
+ const customSortX = (a, b) => {
40
59
  const digitRegex = /\d+/
41
60
 
42
61
  const hasNumber = value => digitRegex.test(value)
@@ -45,6 +64,11 @@ const DataTable = props => {
45
64
  a = a === null || a === undefined ? '' : a
46
65
  b = b === null || b === undefined ? '' : b
47
66
 
67
+ // check for dates first
68
+ if (!isNaN(Date.parse(a)) && !isNaN(Date.parse(b))) {
69
+ return Date.parse(a) - Date.parse(b)
70
+ }
71
+
48
72
  // convert any strings that are actually numbers to proper data type
49
73
  const aNum = Number(a)
50
74
 
@@ -145,15 +169,13 @@ const DataTable = props => {
145
169
  const DownloadButton = memo(() => {
146
170
  if (rawData !== undefined) {
147
171
  let csvData
148
- if (config.type === 'chart' || config.general.type === 'bubble' || !config.table.showFullGeoNameInCSV) {
149
- // Just Unparse
150
- csvData = Papa.unparse(rawData)
151
- } else if ((config.general.geoType !== 'single-state' && config.general.geoType !== 'us-county') || config.general.type === 'us-geocode') {
152
- // Unparse + Add column for full Geo name
153
- csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: displayGeoName(row[config.columns.geo.name]), ...row })))
154
- } else {
172
+ // only use fullGeoName on County maps and no other
173
+ if (config.general.geoType === 'us-county') {
155
174
  // Unparse + Add column for full Geo name along with State
156
175
  csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: formatLegendLocation(row[config.columns.geo.name]), ...row })))
176
+ } else {
177
+ // Just Unparse
178
+ csvData = Papa.unparse(rawData)
157
179
  }
158
180
 
159
181
  const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
@@ -204,17 +226,18 @@ const DataTable = props => {
204
226
  }
205
227
 
206
228
  const rows = Object.keys(runtimeData).sort((a, b) => {
207
- let sortVal
229
+ let sortVal = 0
208
230
  if (config.type === 'map' && config.columns) {
209
231
  sortVal = customSort(runtimeData[a][config.columns[sortBy.column].name], runtimeData[b][config.columns[sortBy.column].name])
210
232
  }
211
233
  if (config.type === 'chart') {
212
234
  sortVal = customSort(runtimeData[a][sortBy.column], runtimeData[b][sortBy.column])
213
235
  }
214
- if (!sortBy.asc) return sortVal
215
- if (sortVal === 0) return 0
216
- if (sortVal < 0) return 1
217
- return -1
236
+ return sortVal
237
+ // if (!sortBy.asc) return sortVal
238
+ // if (sortVal === 0) return 0
239
+ // if (sortVal < 0) return 1
240
+ // return -1
218
241
  })
219
242
 
220
243
  const genMapRows = rows => {
@@ -341,6 +364,7 @@ const DataTable = props => {
341
364
  {...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
342
365
  >
343
366
  {text}
367
+ {sortBy.column === column && <span className={'sort-icon'}>{!sortBy.asc ? upIcon : downIcon}</span>}
344
368
  <button>
345
369
  <span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
346
370
  </button>
@@ -395,9 +419,9 @@ const DataTable = props => {
395
419
 
396
420
  let addColParams = isAdditionalColumn(column)
397
421
  if (addColParams) {
398
- cellValue = formatNumber(runtimeData[row][column], resolvedAxis, true, config, addColParams)
422
+ cellValue = formatNumber(runtimeData[row][column], resolvedAxis, false, config, addColParams)
399
423
  } else {
400
- cellValue = formatNumber(runtimeData[row][column], resolvedAxis, true, config)
424
+ cellValue = formatNumber(runtimeData[row][column], resolvedAxis, false, config)
401
425
  }
402
426
  }
403
427
 
@@ -413,6 +437,17 @@ const DataTable = props => {
413
437
  return allRows
414
438
  }
415
439
 
440
+ const upIcon = (
441
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'>
442
+ <path d='M0 5l5-5 5 5z' />
443
+ </svg>
444
+ )
445
+ const downIcon = (
446
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'>
447
+ <path d='M0 0l5 5 5-5z' />
448
+ </svg>
449
+ )
450
+
416
451
  const limitHeight = {
417
452
  maxHeight: config.table.limitHeight && `${config.table.height}px`,
418
453
  overflowY: 'scroll'
@@ -472,6 +507,7 @@ const DataTable = props => {
472
507
  {...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
473
508
  >
474
509
  {text}
510
+ {sortBy.column === column && <span className={'sort-icon'}>{!sortBy.asc ? upIcon : downIcon}</span>}
475
511
  <button>
476
512
  <span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
477
513
  </button>
@@ -0,0 +1,39 @@
1
+ import useDataVizClasses from '../../helpers/useDataVizClasses'
2
+
3
+ const Legend = props => {
4
+ const { config } = props
5
+
6
+ if (!config) return
7
+ const { legendClasses } = useDataVizClasses(config)
8
+
9
+ return (
10
+ <aside id='legend' className={legendClasses.aside.join(' ')} tabIndex={0} aria-label='legend' role='region'>
11
+ <section className={legendClasses.section.join(' ') || ''}>
12
+ <Legend.ResetButton />
13
+ </section>
14
+ </aside>
15
+ )
16
+ }
17
+
18
+ const ResetButton = props => {
19
+ const { config, resetLegendToggles, setAccessibleStatus } = props
20
+ const { legendClasses } = useDataVizClasses(config)
21
+
22
+ const handleReset = e => {
23
+ e.preventDefault()
24
+ resetLegendToggles()
25
+ setAccessibleStatus('Legend has been reset, please reference the data table to see updated values.')
26
+ }
27
+
28
+ if (config.runtime.disabledAmt === 0) return <></>
29
+
30
+ return (
31
+ <button onClick={handleReset} className={legendClasses.resetButton.join(' ') || ''}>
32
+ Reset
33
+ </button>
34
+ )
35
+ }
36
+
37
+ Legend.ResetButton = ResetButton
38
+
39
+ export default Legend
@@ -7,8 +7,7 @@ import { DATA_TABLE_VERTICAL, DATA_TABLE_HORIZONTAL, DATA_TABLE_SINGLE_ROW, DATA
7
7
  import '../../styles/v2/components/data-designer.scss'
8
8
 
9
9
  const DataDesigner = props => {
10
- const { configureData, updateDescriptionProp, visualizationKey, dataKey } = props
11
-
10
+ const { configureData, updateDescriptionProp, visualizationKey, dataKey, config, setConfig } = props
12
11
 
13
12
  return (
14
13
  <div className='cove-data-designer__container'>
@@ -166,66 +165,203 @@ const DataDesigner = props => {
166
165
  </select>
167
166
  </div>
168
167
  <div className='mb-2'>
169
- <div className='mb-1'>Which properties in the dataset represent the numeric value? (all remaining properties will be treated as filters)</div>
168
+ <div className='mb-1'>Which properties in the dataset represent the numeric value? (all remaining properties will be treated as filters)</div>
170
169
  {configureData.dataDescription.valueKeys && configureData.dataDescription.valueKeys.length > 0 && (
171
- <ul className="value-list">
170
+ <ul className='value-list'>
172
171
  {configureData.dataDescription.valueKeys.map((valueKey, index) => (
173
- <li key={`value-keys-list-${index}`}>{valueKey}<button onClick={() => {
174
- let newValueKeys = configureData.dataDescription.valueKeys;
175
- newValueKeys.splice(index, 1);
176
- updateDescriptionProp(visualizationKey, dataKey, 'valueKeys', newValueKeys)
177
- }}>X</button></li>
172
+ <li key={`value-keys-list-${index}`}>
173
+ {valueKey}
174
+ <button
175
+ onClick={() => {
176
+ let newValueKeys = configureData.dataDescription.valueKeys
177
+ newValueKeys.splice(index, 1)
178
+ updateDescriptionProp(visualizationKey, dataKey, 'valueKeys', newValueKeys)
179
+ }}
180
+ >
181
+ X
182
+ </button>
183
+ </li>
178
184
  ))}
179
185
  </ul>
180
186
  )}
181
187
  <select
182
188
  onChange={e => {
183
- if(e.target.value && (!configureData.dataDescription.valueKeys || configureData.dataDescription.valueKeys.indexOf(e.target.value) === -1)){
189
+ if (e.target.value && (!configureData.dataDescription.valueKeys || configureData.dataDescription.valueKeys.indexOf(e.target.value) === -1)) {
184
190
  updateDescriptionProp(visualizationKey, dataKey, 'valueKeys', [...(configureData.dataDescription.valueKeys || []), e.target.value])
185
191
  }
186
192
  }}
187
193
  >
188
194
  <option value=''>Choose an option</option>
189
- {Object.keys(configureData.data[0]).filter(value => !configureData.dataDescription.valueKeys || configureData.dataDescription.valueKeys.indexOf(value) === -1).map((value, index) => (
190
- <option value={value} key={`value-keys-option-${index}`}>
191
- {value}
192
- </option>
193
- ))}
195
+ {Object.keys(configureData.data[0])
196
+ .filter(value => !configureData.dataDescription.valueKeys || configureData.dataDescription.valueKeys.indexOf(value) === -1)
197
+ .map((value, index) => (
198
+ <option value={value} key={`value-keys-option-${index}`}>
199
+ {value}
200
+ </option>
201
+ ))}
194
202
  </select>
195
203
  </div>
196
204
  <div className='mb-2'>
197
- <div className='mb-1'>(Optional) Which properties in the dataset should be ignored? (will not be used or treated as filters)</div>
205
+ <div className='mb-1'>(Optional) Which properties in the dataset should be ignored? (will not be used or treated as filters)</div>
198
206
  {configureData.dataDescription.ignoredKeys && configureData.dataDescription.ignoredKeys.length > 0 && (
199
- <ul className="value-list">
207
+ <ul className='value-list'>
200
208
  {configureData.dataDescription.ignoredKeys.map((ignoredKey, index) => (
201
- <li key={`value-keys-list-${index}`}>{ignoredKey}<button onClick={() => {
202
- let newIgnoredKeys = configureData.dataDescription.ignoredKeys;
203
- newIgnoredKeys.splice(index, 1);
204
- updateDescriptionProp(visualizationKey, dataKey, 'ignoredKeys', newIgnoredKeys)
205
- }}>X</button></li>
209
+ <li key={`value-keys-list-${index}`}>
210
+ {ignoredKey}
211
+ <button
212
+ onClick={() => {
213
+ let newIgnoredKeys = configureData.dataDescription.ignoredKeys
214
+ newIgnoredKeys.splice(index, 1)
215
+ updateDescriptionProp(visualizationKey, dataKey, 'ignoredKeys', newIgnoredKeys)
216
+ }}
217
+ >
218
+ X
219
+ </button>
220
+ </li>
206
221
  ))}
207
222
  </ul>
208
223
  )}
209
224
  <select
210
225
  onChange={e => {
211
- if(e.target.value){
226
+ if (e.target.value) {
212
227
  updateDescriptionProp(visualizationKey, dataKey, 'ignoredKeys', [...(configureData.dataDescription.ignoredKeys || []), e.target.value])
213
228
  }
214
- e.target.value = '';
229
+ e.target.value = ''
215
230
  }}
216
231
  >
217
232
  <option value=''>Choose an option</option>
218
- {Object.keys(configureData.data[0]).filter(value => !configureData.dataDescription.ignoredKeys || configureData.dataDescription.ignoredKeys.indexOf(value) === -1).map((value, index) => (
219
- <option value={value} key={`ignored-keys-option-${index}`}>
220
- {value}
221
- </option>
222
- ))}
233
+ {Object.keys(configureData.data[0])
234
+ .filter(value => !configureData.dataDescription.ignoredKeys || configureData.dataDescription.ignoredKeys.indexOf(value) === -1)
235
+ .map((value, index) => (
236
+ <option value={value} key={`ignored-keys-option-${index}`}>
237
+ {value}
238
+ </option>
239
+ ))}
223
240
  </select>
224
241
  </div>
225
242
  </>
226
243
  )}
227
244
  </>
228
245
  )}
246
+
247
+ {config?.visualizationType === 'Forest Plot' && (
248
+ <>
249
+ <div className='mb-2'>
250
+ <div className='mb-1'>Which column represents the date/category column?</div>
251
+ <select
252
+ onChange={e => {
253
+ setConfig({
254
+ ...config,
255
+ xAxis: {
256
+ ...config.xAxis,
257
+ dataKey: e.target.value
258
+ }
259
+ })
260
+ }}
261
+ defaultValue={'Select'}
262
+ >
263
+ <option value=''>Choose an option</option>
264
+ {Object.keys(configureData.data[0]).map((value, index) => (
265
+ <option value={value} key={index}>
266
+ {value}
267
+ </option>
268
+ ))}
269
+ </select>
270
+ </div>
271
+
272
+ <div className='mb-2'>
273
+ <div className='mb-1'>Which column represents your estimate field?</div>
274
+ <select
275
+ onChange={e => {
276
+ setConfig({
277
+ ...config,
278
+ forestPlot: {
279
+ ...config.forestPlot,
280
+ estimateField: e.target.value
281
+ }
282
+ })
283
+ }}
284
+ defaultValue={'Select'}
285
+ >
286
+ <option value=''>Choose an option</option>
287
+ {Object.keys(configureData.data[0]).map((value, index) => (
288
+ <option value={value} key={index}>
289
+ {value}
290
+ </option>
291
+ ))}
292
+ </select>
293
+ </div>
294
+
295
+ <div className='mb-2'>
296
+ <div className='mb-1'>Which column represents the low confidence interval?</div>
297
+ <select
298
+ onChange={e => {
299
+ setConfig({
300
+ ...config,
301
+ forestPlot: {
302
+ ...config.forestPlot,
303
+ lower: e.target.value
304
+ }
305
+ })
306
+ }}
307
+ defaultValue={'Select'}
308
+ >
309
+ <option value=''>Choose an option</option>
310
+ {Object.keys(configureData.data[0]).map((value, index) => (
311
+ <option value={value} key={index}>
312
+ {value}
313
+ </option>
314
+ ))}
315
+ </select>
316
+ </div>
317
+
318
+ <div className='mb-2'>
319
+ <div className='mb-1'>Which column represents the high confidence interval?</div>
320
+ <select
321
+ onChange={e => {
322
+ setConfig({
323
+ ...config,
324
+ forestPlot: {
325
+ ...config.forestPlot,
326
+ upper: e.target.value
327
+ }
328
+ })
329
+ }}
330
+ defaultValue={'Select'}
331
+ >
332
+ <option value=''>Choose an option</option>
333
+ {Object.keys(configureData.data[0]).map((value, index) => (
334
+ <option value={value} key={index}>
335
+ {value}
336
+ </option>
337
+ ))}
338
+ </select>
339
+ </div>
340
+
341
+ <div className='mb-2'>
342
+ <div className='mb-1'>Which shape do you want to use in your forest plot?</div>
343
+ <select
344
+ onChange={e => {
345
+ setConfig({
346
+ ...config,
347
+ forestPlot: {
348
+ ...config.forestPlot,
349
+ shape: e.target.value
350
+ }
351
+ })
352
+ }}
353
+ defaultValue={'Select'}
354
+ >
355
+ <option value=''>Choose an option</option>
356
+ {['text', 'circle', 'square', 'diamond'].map((value, index) => (
357
+ <option value={value} key={index}>
358
+ {value}
359
+ </option>
360
+ ))}
361
+ </select>
362
+ </div>
363
+ </>
364
+ )}
229
365
  </>
230
366
  )}
231
367
  {configureData.dataDescription && configureData.formattedData && <div>Data configured successfully</div>}
@@ -250,6 +250,23 @@ export class DataTransform {
250
250
  if (testing) console.log('## cleanedData =', cleanedupData)
251
251
  return cleanedupData
252
252
  }
253
+
254
+ // clean out %, $, commas from numbers when needing to do sorting!
255
+ cleanDataPoint(data, testing = false) {
256
+ if (testing) console.log('clean', data)
257
+ let cleaned = ''
258
+
259
+ // remove comma and dollar signs
260
+ let tmp = ''
261
+ if (typeof data === 'string') {
262
+ tmp = data!== null && data !== '' ? data.replace(/[,\$\%]/g, '') : ''
263
+ } else {
264
+ tmp = data !== null && data !== '' ? data : ''
265
+ }
266
+
267
+ if (testing) console.log('## cleanedData =', tmp)
268
+ return tmp
269
+ }
253
270
  }
254
271
 
255
272
  export default DataTransform
@@ -30,7 +30,7 @@ const formatNumber = (num, axis, shouldAbbreviate = false, config = null, addCol
30
30
  if (isNegative) {
31
31
  num = Math.abs(num)
32
32
  }
33
-
33
+
34
34
  // destructure dataFormat values
35
35
  let {
36
36
  dataFormat: { commas, abbreviated, roundTo, prefix, suffix, rightRoundTo, bottomRoundTo, rightPrefix, rightSuffix, bottomPrefix, bottomSuffix, bottomAbbreviated }
@@ -45,27 +45,27 @@ const formatNumber = (num, axis, shouldAbbreviate = false, config = null, addCol
45
45
  let original = num
46
46
  let stringFormattingOptions
47
47
  if (axis === 'left') {
48
- let roundToPlace
49
- if (addColRoundTo !== undefined) {
50
- // if its an Additional Column
51
- roundToPlace = addColRoundTo ? Number(addColRoundTo) : 0
52
- } else {
53
- roundToPlace = roundTo ? Number(roundTo) : 0
54
- }
55
- // Need to prevent negative values in rounding
56
- if (roundToPlace < 0) roundToPlace = 0
57
- let useCommas
58
- if (addColCommas !== undefined) {
59
- // if its an Additional Column
60
- useCommas = addColCommas ? true : false
61
- } else {
62
- useCommas = config.dataFormat.commas ? true : false
63
- }
64
- stringFormattingOptions = {
65
- useGrouping: useCommas,
66
- minimumFractionDigits: roundToPlace,
67
- maximumFractionDigits: roundToPlace
68
- }
48
+ let roundToPlace
49
+ if (addColRoundTo !== undefined) {
50
+ // if its an Additional Column
51
+ roundToPlace = addColRoundTo ? Number(addColRoundTo) : 0
52
+ } else {
53
+ roundToPlace = roundTo ? Number(roundTo) : 0
54
+ }
55
+ // Need to prevent negative values in rounding
56
+ if (roundToPlace < 0) roundToPlace = 0
57
+ let useCommas
58
+ if (addColCommas !== undefined) {
59
+ // if its an Additional Column
60
+ useCommas = addColCommas ? true : false
61
+ } else {
62
+ useCommas = config.dataFormat.commas ? true : false
63
+ }
64
+ stringFormattingOptions = {
65
+ useGrouping: useCommas,
66
+ minimumFractionDigits: roundToPlace,
67
+ maximumFractionDigits: roundToPlace
68
+ }
69
69
  }
70
70
 
71
71
  if (axis === 'right') {
@@ -124,7 +124,7 @@ const formatNumber = (num, axis, shouldAbbreviate = false, config = null, addCol
124
124
  }
125
125
 
126
126
  if (addColPrefix !== undefined && axis === 'left') {
127
- result = addColPrefix + result
127
+ result = addColPrefix + result
128
128
  } else {
129
129
  if (prefix && axis === 'left') {
130
130
  result = prefix + result
@@ -163,4 +163,10 @@ const formatNumber = (num, axis, shouldAbbreviate = false, config = null, addCol
163
163
  return String(result)
164
164
  }
165
165
 
166
- export { formatNumber }
166
+ const getFontSize = (size = 'medium') => {
167
+ const fontSize = { small: 16, medium: 18, large: 20 }
168
+
169
+ return fontSize[size]
170
+ }
171
+
172
+ export { formatNumber, getFontSize }
@@ -1,4 +1,5 @@
1
- export default function useDataVizClasses(config) {
1
+ export default function useDataVizClasses(config, viewport = null) {
2
+ const { legend } = config
2
3
  let lineDatapointClass = ''
3
4
  let barBorderClass = ''
4
5
 
@@ -36,5 +37,31 @@ export default function useDataVizClasses(config) {
36
37
  height: '100px'
37
38
  }
38
39
 
39
- return { innerContainerClasses, contentClasses, barBorderClass, lineDatapointClass, sparkLineStyles }
40
+ // Starting work on combining legend classes.
41
+ // Using short circuiting to check between charts & maps for now.
42
+ const getListPosition = () => {
43
+ if (legend?.position === 'side' && legend?.singleColumn) return 'legend-container__ul--single-column'
44
+ if (legend?.position === 'bottom' && legend?.singleRow) return 'single-row'
45
+ if (legend?.verticalSorted && !legend?.singleRow) return 'vertical-sorted'
46
+ return ''
47
+ }
48
+
49
+ const getUlClasses = () => {
50
+ const ulClasses = ['legend-container__ul']
51
+ ulClasses.push(getListPosition())
52
+ return ulClasses
53
+ }
54
+ const legendOuterClasses = [`${legend?.position}`, `${getListPosition()}`, `cdcdataviz-sr-focusable`, `${viewport}`]
55
+
56
+ const legendClasses = {
57
+ aside: legendOuterClasses,
58
+ section: ['legend-container'],
59
+ ul: getUlClasses(),
60
+ li: ['single-legend-item', 'legend-container__li'],
61
+ title: ['legend-container__title'],
62
+ resetButton: ['legend-container__reset-button', 'btn', 'clear'],
63
+ description: ['legend-container__description']
64
+ }
65
+
66
+ return { innerContainerClasses, contentClasses, barBorderClass, lineDatapointClass, sparkLineStyles, legendClasses }
40
67
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/core",
3
- "version": "4.23.7",
3
+ "version": "4.23.9",
4
4
  "description": "Core components, styles, hooks, and helpers, for the CDC Open Visualization project",
5
5
  "moduleName": "CdcCore",
6
6
  "main": "dist/cdccore",
@@ -94,6 +94,7 @@ table.data-table {
94
94
  background-size: 10px 5px;
95
95
  }
96
96
 
97
+ /* doesnt work
97
98
  th.sort-asc,
98
99
  td.sort-asc {
99
100
  background-image: url(../assets/icon-caret-filled-up.svg);
@@ -103,6 +104,19 @@ table.data-table {
103
104
  td.sort-desc {
104
105
  background-image: url(../assets/icon-caret-filled-down.svg);
105
106
  }
107
+ */
108
+
109
+ // format the white triangle sort icon in data table headers
110
+ .sort-icon {
111
+ fill: white;
112
+ width: 30px;
113
+ float: right;
114
+ align-self: center;
115
+ position: absolute;
116
+ right: 10px;
117
+ top: 50%;
118
+ transform: translateY(-50%);
119
+ }
106
120
 
107
121
  th:last-child,
108
122
  td:last-child {
@@ -150,16 +150,6 @@
150
150
  background-size: 10px 5px;
151
151
  }
152
152
 
153
- .sort-asc {
154
- //TODO find fix
155
- //background-image: url(~@cdc/core/assets/icon-caret-filled-up.svg);
156
- }
157
-
158
- .sort-desc {
159
- //TODO find fix
160
- //background-image: url(~@cdc/core/assets/icon-caret-filled-down.svg);
161
- }
162
-
163
153
  .resizer {
164
154
  cursor: e-resize;
165
155
  width: 10px;
@@ -171,6 +161,7 @@
171
161
  }
172
162
  }
173
163
 
164
+
174
165
  .cove-data-table__footer {
175
166
  margin-top: 1rem;
176
167
  }