@cdc/core 4.23.5 → 4.23.7

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="M87.87,5.89H11.35c-1.56,0-2.84,1.28-2.84,2.84v14.78c0,1.56,1.28,2.84,2.84,2.84l76.52-.19c1.56,0,2.84-1.28,2.84-2.84V8.73c0-1.56-1.28-2.84-2.84-2.84ZM25.95,14.33l-5.19,5.19c-.29,.29-.77,.29-1.06,0l-5.19-5.19c-.29-.29-.29-.77,0-1.06h11.43c.29,.29,.29,.77,0,1.06Zm60.97,10.22H32.18V7.7h54.73V24.55Z"/><path transform="translate(0, 30)" d="M87.87,5.89H11.35c-1.56,0-2.84,1.28-2.84,2.84v14.78c0,1.56,1.28,2.84,2.84,2.84l76.52-.19c1.56,0,2.84-1.28,2.84-2.84V8.73c0-1.56-1.28-2.84-2.84-2.84ZM25.95,14.33l-5.19,5.19c-.29,.29-.77,.29-1.06,0l-5.19-5.19c-.29-.29-.29-.77,0-1.06h11.43c.29,.29,.29,.77,0,1.06Zm60.97,10.22H32.18V7.7h54.73V24.55Z"/></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"><path d="M18.29,39.57c0-1.03,.23-1.79,.68-2.28,.45-.49,1.06-.74,1.83-.74s1.42,.25,1.87,.74c.45,.49,.68,1.25,.68,2.29s-.23,1.79-.68,2.28c-.45,.5-1.06,.74-1.83,.74s-1.42-.25-1.87-.74c-.45-.49-.68-1.25-.68-2.29Zm1.71-.02c0,.75,.09,1.25,.26,1.5,.13,.19,.31,.28,.54,.28s.42-.09,.55-.28c.17-.25,.25-.75,.25-1.5s-.08-1.24-.25-1.49c-.13-.19-.31-.29-.55-.29s-.41,.09-.54,.28c-.17,.26-.26,.76-.26,1.5Zm2.47,9.08h-1.62l6.1-12.09h1.58l-6.05,12.09Zm3.55-3.01c0-1.03,.23-1.79,.68-2.28,.45-.49,1.07-.74,1.85-.74s1.4,.25,1.86,.74c.45,.5,.68,1.26,.68,2.28s-.23,1.79-.68,2.29c-.45,.49-1.06,.74-1.83,.74s-1.42-.25-1.87-.74c-.45-.49-.68-1.26-.68-2.29Zm1.71,0c0,.75,.09,1.24,.26,1.49,.13,.19,.31,.29,.54,.29s.41-.09,.54-.28c.17-.25,.26-.75,.26-1.5s-.08-1.24-.25-1.5c-.13-.19-.31-.28-.55-.28s-.41,.09-.54,.28c-.17,.26-.26,.76-.26,1.5Z"/><path d="M92.33,52.83v-20.42c0-1.61-1.31-2.93-2.93-2.93H6.65c-1.61,0-2.93,1.31-2.93,2.93v20.42c0,1.61,1.31,2.93,2.93,2.93H89.4c1.61,0,2.93-1.31,2.93-2.93Zm-52.34-2.93H9.58v-14.56h30.41v14.56Zm5.86-14.56h40.63v14.56H45.85v-14.56Z"/></svg>
@@ -8,7 +8,12 @@ export const AdvancedEditor = ({ loadConfig, state, convertStateToConfig }) => {
8
8
  const [configTextboxValue, setConfigTextbox] = useState({})
9
9
 
10
10
  useEffect(() => {
11
- const parsedData = convertStateToConfig()
11
+ let parsedData = state
12
+ if (state.type !== 'dashboard') {
13
+ parsedData = convertStateToConfig()
14
+ } else {
15
+ parsedData = JSON.parse(JSON.stringify(parsedData))
16
+ }
12
17
  const formattedData = JSON.stringify(parsedData, undefined, 2)
13
18
 
14
19
  setConfigTextbox(formattedData)
@@ -16,6 +21,7 @@ export const AdvancedEditor = ({ loadConfig, state, convertStateToConfig }) => {
16
21
 
17
22
  const typeLookup = {
18
23
  chart: ['Charts', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
24
+ dashboard: ['Dashboard', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
19
25
  map: ['Maps', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html', <MapIcon />],
20
26
  'markup-include': ['Markup Include', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/Markup-Include.html', <MarkupIncludeIcon />]
21
27
  }
@@ -1,22 +1,30 @@
1
1
  import React, { useEffect, useState, memo, useMemo } from 'react'
2
2
 
3
3
  import Papa from 'papaparse'
4
- import ExternalIcon from '../assets/external-link.svg' // TODO: Move to Icon component
4
+ import ExternalIcon from '../assets/external-link.svg'
5
5
  import Icon from '@cdc/core/components/ui/Icon'
6
6
 
7
7
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
8
8
  import LegendCircle from '@cdc/core/components/LegendCircle'
9
- import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
9
+ import MediaControls from '@cdc/core/components/MediaControls'
10
+
11
+ import { parseDate, formatDate } from '@cdc/core/helpers/cove/date'
12
+ import { formatNumber } from '@cdc/core/helpers/cove/number'
10
13
 
11
14
  import Loading from '@cdc/core/components/Loading'
12
15
 
13
16
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
14
17
  const DataTable = props => {
15
- const { config, tableTitle, indexTitle, vizTitle, rawData, runtimeData, headerColor, expandDataTable, columns, displayDataAsText, formatNumber, applyLegendToRow, displayGeoName, navigationHandler, viewport, formatLegendLocation, tabbingId, parseDate, formatDate, isDebug } = props
16
- if (isDebug) console.log('core/DataTable: props=', props)
17
- if (isDebug) console.log('core/DataTable: runtimeData=', runtimeData)
18
- if (isDebug) console.log('core/DataTable: rawData=', rawData)
19
- if (isDebug) console.log('core/DataTable: config=', config)
18
+ const { config, tableTitle, indexTitle, vizTitle, rawData, runtimeData, headerColor, expandDataTable, columns, displayDataAsText, applyLegendToRow, displayGeoName, navigationHandler, viewport, formatLegendLocation, tabbingId, isDebug } = props
19
+
20
+ /* eslint-disable no-console */
21
+ if (isDebug) {
22
+ console.log('core/DataTable: props=', props)
23
+ console.log('core/DataTable: runtimeData=', runtimeData)
24
+ console.log('core/DataTable: rawData=', rawData)
25
+ console.log('core/DataTable: config=', config)
26
+ }
27
+ /* eslint-enable no-console */
20
28
 
21
29
  const [expanded, setExpanded] = useState(expandDataTable)
22
30
 
@@ -137,7 +145,7 @@ const DataTable = props => {
137
145
  const DownloadButton = memo(() => {
138
146
  if (rawData !== undefined) {
139
147
  let csvData
140
- if (config.type === 'chart' || config.general.type === 'bubble') {
148
+ if (config.type === 'chart' || config.general.type === 'bubble' || !config.table.showFullGeoNameInCSV) {
141
149
  // Just Unparse
142
150
  csvData = Papa.unparse(rawData)
143
151
  } else if ((config.general.geoType !== 'single-state' && config.general.geoType !== 'us-county') || config.general.type === 'us-geocode') {
@@ -197,16 +205,19 @@ const DataTable = props => {
197
205
 
198
206
  const rows = Object.keys(runtimeData).sort((a, b) => {
199
207
  let sortVal
200
- if (config.columns.length > 0) {
208
+ if (config.type === 'map' && config.columns) {
201
209
  sortVal = customSort(runtimeData[a][config.columns[sortBy.column].name], runtimeData[b][config.columns[sortBy.column].name])
202
210
  }
211
+ if (config.type === 'chart') {
212
+ sortVal = customSort(runtimeData[a][sortBy.column], runtimeData[b][sortBy.column])
213
+ }
203
214
  if (!sortBy.asc) return sortVal
204
215
  if (sortVal === 0) return 0
205
216
  if (sortVal < 0) return 1
206
217
  return -1
207
218
  })
208
219
 
209
- function genMapRows(rows) {
220
+ const genMapRows = rows => {
210
221
  const allrows = rows.map(row => {
211
222
  return (
212
223
  <tr role='row'>
@@ -299,12 +310,18 @@ const DataTable = props => {
299
310
  }
300
311
  }
301
312
 
302
- function genChartHeader(columns, data) {
313
+ const genChartHeader = (columns, data) => {
314
+ if (!data) return
303
315
  return (
304
316
  <tr>
305
317
  {dataSeriesColumns().map(column => {
306
318
  let custLabel = getLabel(column) ? getLabel(column) : column
307
319
  let text = column === config.xAxis.dataKey ? config.table.indexLabel : custLabel
320
+
321
+ // If a user sets the name on a series use that.
322
+ let userUpdatedSeriesName = config.series.filter(series => series.dataKey === column)?.[0]?.name
323
+ if (userUpdatedSeriesName) text = userUpdatedSeriesName
324
+
308
325
  return (
309
326
  <th
310
327
  key={`col-header-${column}`}
@@ -334,38 +351,66 @@ const DataTable = props => {
334
351
  )
335
352
  }
336
353
 
337
- function genChartRows(rows) {
338
- const allrows = rows.map(row => {
354
+ // if its additional column, return formatting params
355
+ const isAdditionalColumn = column => {
356
+ let inthere = false
357
+ let formattingParams = {}
358
+ Object.keys(config.columns).forEach(keycol => {
359
+ if (config.columns[keycol].name === column) {
360
+ inthere = true
361
+ formattingParams = {
362
+ addColPrefix: config.columns[keycol].prefix,
363
+ addColSuffix: config.columns[keycol].suffix,
364
+ addColRoundTo: config.columns[keycol].roundToPlace ? config.columns[keycol].roundToPlace : '',
365
+ addColCommas: config.columns[keycol].commas
366
+ }
367
+ }
368
+ })
369
+ return formattingParams
370
+ }
371
+
372
+ const genChartRows = rows => {
373
+ const allRows = rows.map(row => {
339
374
  return (
340
375
  <tr role='row'>
341
- {dataSeriesColumns()
342
- //.filter(column => columns[column].dataTable === true && columns[column].name)
343
- .map(column => {
344
- let cellValue
345
- if (column === config.xAxis.dataKey) {
346
- const rowObj = runtimeData[row]
347
- //const legendColor = applyLegendToRow(rowObj)
348
- var labelValue = rowObj[column] // just raw X axis string
349
- labelValue = getCellAnchor(labelValue, rowObj)
350
- // no colors on row headers for charts bc it's Date not data
351
- // Remove this - <LegendCircle fill={legendColor[row]} />
352
- cellValue = <>{labelValue}</>
376
+ {dataSeriesColumns().map(column => {
377
+ const rowObj = runtimeData[row]
378
+ let cellValue // placeholder for formatting below
379
+ let labelValue = rowObj[column] // just raw X axis string
380
+ if (column === config.xAxis.dataKey) {
381
+ // not the prettiest, but helper functions work nicely here.
382
+ cellValue = <>{config.xAxis.type === 'date' ? formatDate(config.xAxis.dateDisplayFormat, parseDate(config.xAxis.dateParseFormat, labelValue)) : labelValue}</>
383
+ } else {
384
+ let resolvedAxis = 'left'
385
+ let leftAxisItems = config.series.filter(item => item?.axis === 'Left')
386
+ let rightAxisItems = config.series.filter(item => item?.axis === 'Right')
387
+
388
+ leftAxisItems.map(leftSeriesItem => {
389
+ if (leftSeriesItem.dataKey === column) resolvedAxis = 'left'
390
+ })
391
+
392
+ rightAxisItems.map(rightSeriesItem => {
393
+ if (rightSeriesItem.dataKey === column) resolvedAxis = 'right'
394
+ })
395
+
396
+ let addColParams = isAdditionalColumn(column)
397
+ if (addColParams) {
398
+ cellValue = formatNumber(runtimeData[row][column], resolvedAxis, true, config, addColParams)
353
399
  } else {
354
- cellValue = displayDataAsText(runtimeData[row][column], column)
400
+ cellValue = formatNumber(runtimeData[row][column], resolvedAxis, true, config)
355
401
  }
402
+ }
356
403
 
357
- //MAP SPECIFIC- change to CHART specific
358
- // onClick = { e => (config.general.type === 'bubble' && config.general.allowMapZoom && config.general.geoType === 'world' ? setFilteredCountryCode(row) : true)}
359
- return (
360
- <td tabIndex='0' role='gridcell' id={`${runtimeData[config.runtime.originalXAxis.dataKey]}--${row}`}>
361
- {cellValue}
362
- </td>
363
- )
364
- })}
404
+ return (
405
+ <td tabIndex='0' role='gridcell' id={`${runtimeData[config.runtime.originalXAxis.dataKey]}--${row}`}>
406
+ {cellValue}
407
+ </td>
408
+ )
409
+ })}
365
410
  </tr>
366
411
  )
367
412
  })
368
- return allrows
413
+ return allRows
369
414
  }
370
415
 
371
416
  const limitHeight = {
@@ -391,7 +436,7 @@ const DataTable = props => {
391
436
  [config.runtime.seriesKeys]) // eslint-disable-line
392
437
 
393
438
  if (config.visualizationType !== 'Box Plot') {
394
- function genMapHeader(columns) {
439
+ const genMapHeader = columns => {
395
440
  return (
396
441
  <tr>
397
442
  {Object.keys(columns)
@@ -406,9 +451,11 @@ const DataTable = props => {
406
451
  if (config.type === 'map' && (text === undefined || text === '')) {
407
452
  text = 'Location'
408
453
  }
454
+
409
455
  return (
410
456
  <th
411
457
  key={`col-header-${column}`}
458
+ id={column}
412
459
  tabIndex='0'
413
460
  title={text}
414
461
  role='columnheader'
@@ -437,10 +484,10 @@ const DataTable = props => {
437
484
 
438
485
  return (
439
486
  <ErrorBoundary component='DataTable'>
440
- <CoveMediaControls.Section classes={['download-links']}>
441
- <CoveMediaControls.Link config={config} />
487
+ <MediaControls.Section classes={['download-links']}>
488
+ <MediaControls.Link config={config} />
442
489
  {(config.table.download || config.general.showDownloadButton) && <DownloadButton />}
443
- </CoveMediaControls.Section>
490
+ </MediaControls.Section>
444
491
  <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
445
492
  <a id='skip-nav' className='cdcdataviz-sr-only-focusable' href={`#${skipId}`}>
446
493
  Skip Navigation or Skip to Content
@@ -503,7 +550,7 @@ const DataTable = props => {
503
550
  )
504
551
  } else {
505
552
  // Render Data Table for Box Plots
506
- function genBoxplotHeader(categories) {
553
+ const genBoxplotHeader = categories => {
507
554
  let columns = ['Measures', ...categories]
508
555
  return (
509
556
  <tr>
@@ -557,7 +604,7 @@ const DataTable = props => {
557
604
  if (Number(rowid) === 10) return plot.values.length > 0 ? plot.values.toString() : '-'
558
605
  return <p>-</p>
559
606
  }
560
- function genBoxplotRows(rows) {
607
+ const genBoxplotRows = rows => {
561
608
  // get list of data keys for each row
562
609
  let dataKeys = rows.map(row => {
563
610
  return row[0]
@@ -590,10 +637,10 @@ const DataTable = props => {
590
637
  return (
591
638
  <ErrorBoundary component='DataTable'>
592
639
  {/* cove media results in error so disabling for now (TT)
593
- <CoveMediaControls.Section classes={['download-links']}>
594
- <CoveMediaControls.Link config={config} />
640
+ <MediaControls.Section classes={['download-links']}>
641
+ <MediaControls.Link config={config} />
595
642
  {config.general.showDownloadButton && <DownloadButton />}
596
- </CoveMediaControls.Section>
643
+ </MediaControls.Section>
597
644
  */}
598
645
  <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
599
646
  <a id='skip-nav' className='cdcdataviz-sr-only-focusable' href={`#${skipId}`}>
@@ -312,6 +312,8 @@ const Filters = props => {
312
312
  delete filtersToLoop.fromHash
313
313
 
314
314
  return filtersToLoop.map((singleFilter, outerIndex) => {
315
+ if(singleFilter.showDropdown === false) return
316
+
315
317
  const values = []
316
318
  const pillValues = []
317
319
  const tabValues = []
@@ -134,11 +134,11 @@ const Section = ({ children, classes }) => {
134
134
  )
135
135
  }
136
136
 
137
- const CoveMediaControls = () => null
137
+ const MediaControls = () => null
138
138
 
139
- CoveMediaControls.Section = Section
140
- CoveMediaControls.Link = Link
141
- CoveMediaControls.Button = Button
142
- CoveMediaControls.generateMedia = generateMedia
139
+ MediaControls.Section = Section
140
+ MediaControls.Link = Link
141
+ MediaControls.Button = Button
142
+ MediaControls.generateMedia = generateMedia
143
143
 
144
- export default CoveMediaControls
144
+ export default MediaControls
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+ import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
3
+ import { Draggable } from '@hello-pangea/dnd'
4
+ import Icon from '@cdc/core/components/ui/Icon'
5
+
6
+ const RepeatableGroupSection = props => {
7
+ return <div className='repeatable-group'>{props.children}</div>
8
+ }
9
+
10
+ export const RepeatableGroupItem = props => {
11
+ const { item, index, title } = props
12
+
13
+ return (
14
+ <Draggable key={item} draggableId={`draggable-repeatable-item-${item}`} index={index}>
15
+ {(provided, snapshot) => (
16
+ <div key={index} className={snapshot.isDragging ? 'currently-dragging' : ''} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
17
+ <Accordion allowZeroExpanded>
18
+ <AccordionItem className='series-item series-item--chart'>
19
+ <AccordionItemHeading className='series-item__title'>
20
+ <AccordionItemButton className={''}>
21
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
22
+ Title Section
23
+ </AccordionItemButton>
24
+ </AccordionItemHeading>
25
+ <AccordionItemPanel>panel section to be built</AccordionItemPanel>
26
+ </AccordionItem>
27
+ </Accordion>
28
+ </div>
29
+ )}
30
+ </Draggable>
31
+ )
32
+ }
33
+
34
+ export const RepeatableGroup = {
35
+ Section: RepeatableGroupSection,
36
+ Item: RepeatableGroupItem
37
+ }
@@ -33,7 +33,7 @@ const InputSelect = memo(({ label, value, options, fieldName, section = null, su
33
33
  }
34
34
 
35
35
  return (
36
- <label>
36
+ <label style={{ width: '100%', display: 'block' }}>
37
37
  {label && <span className='edit-label cove-input__label'>{label}</span>}
38
38
  <select
39
39
  className={required && !value ? 'warning' : ''}
@@ -21,6 +21,7 @@ const Accordion = ({ children }) => {
21
21
  <AccordionItem className='cove-accordion__item' key={index}>
22
22
  <AccordionItemHeading className='cove-accordion__heading'>
23
23
  <AccordionItemButton className='cove-accordion__button'>
24
+ {section.props.icon}
24
25
  {section.props.title}
25
26
  {section.props.tooltipText ? (
26
27
  <Tooltip position='bottom'>
@@ -43,6 +44,8 @@ const Accordion = ({ children }) => {
43
44
  Accordion.Section = AccordionSection
44
45
 
45
46
  Accordion.Section.propTypes = {
47
+ /* Icon for the accordion label */
48
+ icon: PropTypes.node,
46
49
  /* Title for the accordion label*/
47
50
  title: PropTypes.string,
48
51
  /* Tooltip for the accordion label*/
@@ -27,7 +27,8 @@ import iconWarningCircle from '../../assets/icon-warning-circle.svg'
27
27
  import iconWarningTriangle from '../../assets/icon-warning-triangle.svg'
28
28
  import iconGear from '../../assets/icon-gear.svg'
29
29
  import iconTools from '../../assets/icon-tools.svg'
30
- import iconText from '../../assets/filtered-text.svg'
30
+ import iconText from '../../assets/icon-filtered-text.svg'
31
+ import iconDropdowns from '../../assets/icon-filter-dropdowns.svg'
31
32
  import iconPlus from '../../assets/icon-plus.svg'
32
33
  import iconMinus from '../../assets/icon-minus.svg'
33
34
 
@@ -62,7 +63,8 @@ const iconHash = {
62
63
  tools: iconTools,
63
64
  plus: iconPlus,
64
65
  minus: iconMinus,
65
- 'filtered-text': iconText
66
+ 'filtered-text': iconText,
67
+ 'filter-dropdowns': iconDropdowns
66
68
  }
67
69
 
68
70
  const Icon = ({ display = null, base, alt = '', size, color, style, ...attributes }) => {
@@ -9,42 +9,30 @@ import '../../styles/v2/components/ui/tooltip.scss'
9
9
  const TooltipTarget = () => null
10
10
  const TooltipContent = () => null
11
11
 
12
- const Tooltip = ({
13
- place = 'top',
14
- trigger = 'hover',
15
- float = false,
16
- shadow = true,
17
- border = false,
18
- children,
19
- style,
20
- ...attributes
21
- }) => {
22
-
12
+ const Tooltip = ({ place = 'top', trigger = 'hover', float = false, shadow = true, border = false, children, style, ...attributes }) => {
23
13
  const tooltipTargetChildren = children.find(el => el.type === TooltipTarget)
24
14
  const tooltipContentChildren = children.find(el => el.type === TooltipContent)
25
15
 
26
16
  const uid = 'tooltip-' + useId()
27
17
 
28
- const generateTriggerEvent = (trigger) => {
18
+ const generateTriggerEvent = trigger => {
29
19
  const eventList = {
30
- 'hover': null,
31
- 'focus': 'focus',
32
- 'click': 'click focus'
20
+ hover: null,
21
+ focus: 'focus',
22
+ click: 'click focus'
33
23
  }
34
24
  return eventList[trigger]
35
25
  }
36
26
 
37
27
  return (
38
- <span className="cove-tooltip" style={style} {...attributes}>
39
- <a id={uid} className="cove-tooltip--target"
40
- data-tooltip-float={float}
41
- data-tooltip-place={place}
42
- data-tooltip-events={generateTriggerEvent()}
43
- >
28
+ <span className='cove-tooltip' style={style} {...attributes}>
29
+ <a id={uid} className='cove-tooltip--target' data-tooltip-float={float} data-tooltip-place={place} data-tooltip-events={generateTriggerEvent()}>
44
30
  {tooltipTargetChildren ? tooltipTargetChildren.props.children : null}
45
31
  </a>
32
+ {/* prettier-ignore */}
46
33
  <ReactTooltip
47
- id={uid} anchorId={uid}
34
+ id={uid}
35
+ anchorId={uid}
48
36
  className={'cove-tooltip__content' + (' place-' + place) + (!float ? ' cove-tooltip__content--animated' : '') + (trigger === 'click' ? ' interactive' : '') + (border ? (' cove-tooltip--border') : '') + (shadow ? ' has-shadow' : '')}
49
37
  globalEventOff="click"
50
38
  >