@cdc/core 4.24.5 → 4.24.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.
Files changed (109) hide show
  1. package/assets/icon-gear-multi.svg +23 -0
  2. package/components/AdvancedEditor/AdvancedEditor.tsx +93 -0
  3. package/components/AdvancedEditor/advanced-editor-styles.css +3 -0
  4. package/components/AdvancedEditor/index.ts +1 -0
  5. package/components/Alert/components/Alert.styles.css +15 -0
  6. package/components/Alert/components/Alert.tsx +39 -0
  7. package/components/Alert/index.tsx +3 -0
  8. package/components/DataTable/DataTable.tsx +127 -32
  9. package/components/DataTable/DataTableStandAlone.tsx +4 -25
  10. package/components/DataTable/components/DataTableEditorPanel.tsx +4 -4
  11. package/components/DataTable/components/ExpandCollapse.tsx +1 -1
  12. package/components/DataTable/helpers/chartCellMatrix.tsx +6 -12
  13. package/components/DataTable/helpers/getChartCellValue.ts +9 -5
  14. package/components/DataTable/helpers/getDataSeriesColumns.ts +10 -7
  15. package/components/DataTable/helpers/getRowType.ts +6 -0
  16. package/components/DataTable/helpers/mapCellMatrix.tsx +3 -3
  17. package/components/DataTable/types/TableConfig.ts +2 -1
  18. package/components/EditorPanel/ColumnsEditor.tsx +3 -30
  19. package/components/EditorPanel/DataTableEditor.tsx +66 -22
  20. package/components/EditorPanel/FieldSetWrapper.tsx +51 -0
  21. package/components/EditorPanel/FootnotesEditor.tsx +77 -0
  22. package/components/EditorPanel/Inputs.tsx +13 -4
  23. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +268 -0
  24. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +306 -0
  25. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +40 -0
  26. package/components/EditorPanel/VizFilterEditor/index.ts +1 -0
  27. package/components/EditorWrapper/EditorWrapper.tsx +3 -4
  28. package/components/EditorWrapper/index.ts +1 -0
  29. package/components/Filters.tsx +520 -0
  30. package/components/Footnotes/Footnotes.tsx +25 -0
  31. package/components/Footnotes/FootnotesStandAlone.tsx +45 -0
  32. package/components/Footnotes/footnotes.css +5 -0
  33. package/components/Footnotes/index.ts +1 -0
  34. package/components/Layout/components/Responsive.tsx +14 -4
  35. package/components/Layout/components/Sidebar/components/Sidebar.tsx +14 -5
  36. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +23 -20
  37. package/components/Layout/components/Visualization/index.tsx +19 -6
  38. package/components/Layout/components/Visualization/visualizations.scss +32 -26
  39. package/components/Layout/styles/editor.scss +0 -8
  40. package/components/Legend/Legend.Gradient.tsx +133 -0
  41. package/components/LegendShape.tsx +28 -0
  42. package/components/MultiSelect/MultiSelect.tsx +41 -11
  43. package/components/MultiSelect/multiselect.styles.css +0 -3
  44. package/components/NestedDropdown/NestedDropdown.tsx +47 -52
  45. package/components/NestedDropdown/nesteddropdown.styles.css +19 -25
  46. package/components/Table/Table.tsx +8 -5
  47. package/components/Table/components/Cell.tsx +2 -2
  48. package/components/Table/components/Row.tsx +25 -7
  49. package/components/_stories/Footnotes.stories.tsx +17 -0
  50. package/components/_stories/Layout.Debug.stories.tsx +91 -0
  51. package/components/_stories/_mocks/bar-chart-suppressed.json +474 -0
  52. package/components/_stories/styles.scss +14 -1
  53. package/components/createBarElement.jsx +4 -4
  54. package/components/inputs/InputSelect.tsx +17 -6
  55. package/components/ui/Icon.tsx +22 -16
  56. package/components/ui/Title/Title.scss +0 -8
  57. package/helpers/DataTransform.ts +2 -2
  58. package/helpers/addValuesToFilters.ts +135 -0
  59. package/helpers/cove/accessibility.ts +17 -4
  60. package/helpers/cove/fontSettings.ts +2 -0
  61. package/helpers/coveUpdateWorker.ts +30 -9
  62. package/helpers/filterVizData.ts +49 -0
  63. package/helpers/formatConfigBeforeSave.ts +95 -0
  64. package/helpers/gatherQueryParams.ts +14 -7
  65. package/helpers/getGradientLegendWidth.ts +15 -0
  66. package/helpers/getTextWidth.ts +18 -0
  67. package/helpers/lineChartHelpers.js +2 -1
  68. package/helpers/pivotData.ts +18 -0
  69. package/helpers/queryStringUtils.ts +29 -0
  70. package/helpers/scaling.ts +7 -0
  71. package/helpers/tests/addValuesToFilters.test.ts +55 -0
  72. package/helpers/tests/filterVizData.test.ts +31 -0
  73. package/helpers/tests/invertValue.test.ts +35 -0
  74. package/helpers/tests/updateFieldFactory.test.ts +1 -0
  75. package/helpers/updateFieldFactory.ts +1 -1
  76. package/helpers/updatePaletteNames.ts +19 -0
  77. package/helpers/{useDataVizClasses.js → useDataVizClasses.ts} +3 -2
  78. package/helpers/ver/4.24.5.ts +3 -3
  79. package/helpers/ver/4.24.7.ts +123 -0
  80. package/helpers/ver/4.24.9.ts +63 -0
  81. package/helpers/ver/tests/4.24.9.test.ts +22 -0
  82. package/helpers/ver/versionNeedsUpdate.ts +9 -0
  83. package/package.json +6 -4
  84. package/styles/_button-section.scss +7 -2
  85. package/styles/_data-table.scss +0 -1
  86. package/styles/_global.scss +6 -2
  87. package/styles/base.scss +4 -0
  88. package/styles/filters.scss +4 -0
  89. package/styles/v2/themes/_color-definitions.scss +1 -0
  90. package/types/Annotation.ts +46 -0
  91. package/types/Axis.ts +3 -2
  92. package/types/ConfigureData.ts +1 -1
  93. package/types/Dimensions.ts +1 -0
  94. package/types/Footnotes.ts +17 -0
  95. package/types/General.ts +5 -0
  96. package/types/Runtime.ts +2 -7
  97. package/types/Table.ts +6 -0
  98. package/types/Visualization.ts +31 -9
  99. package/types/VizFilter.ts +39 -7
  100. package/LICENSE +0 -201
  101. package/components/AdvancedEditor.jsx +0 -74
  102. package/components/EditorPanel/VizFilterEditor.tsx +0 -234
  103. package/components/Filters.jsx +0 -461
  104. package/components/LegendCircle.jsx +0 -17
  105. package/helpers/queryStringUtils.js +0 -26
  106. package/helpers/updatePaletteNames.js +0 -16
  107. package/types/BaseVisualizationType.ts +0 -1
  108. /package/components/{Waiting.jsx → Waiting.tsx} +0 -0
  109. /package/helpers/ver/{4.23.4.ts → 4.24.4.ts} +0 -0
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
3
+ <defs>
4
+ <style>
5
+ .cls-1 {
6
+ fill: #231f20;
7
+ stroke-width: 0px;
8
+ }
9
+ </style>
10
+ </defs>
11
+ <g>
12
+ <path class="cls-1" d="M218.85,262.45c-.39-37.16-30.83-66.97-68.01-66.58-37.16.41-66.97,30.86-66.58,68.03.4,37.15,30.86,66.94,68.02,66.57,37.18-.41,66.98-30.86,66.57-68.02ZM151.88,292.62c-4.07.04-7.95-.74-11.49-2.19-10.62-4.36-18.15-14.75-18.28-26.94-.17-16.27,12.86-29.59,29.13-29.76,4.06-.04,7.95.74,11.48,2.19,10.62,4.35,18.15,14.74,18.28,26.94.18,16.25-12.87,29.59-29.12,29.76Z"/>
13
+ <path class="cls-1" d="M250.22,280.09c.99-5.85,1.5-11.87,1.44-18.01-.05-3.92-.34-7.82-.82-11.64l23.73-17.16c-1.72-7.65-4.12-15.04-7.14-22.1l-30.29.03c-4.82-7.93-10.7-15.12-17.46-21.39l7.59-30.08c-6.16-4.7-12.74-8.82-19.71-12.35l-24.62,20.71c-7.91-2.64-16.26-4.28-24.91-4.81l-4.17-8.61-10.1-20.88c-7.89.37-15.6,1.46-23.03,3.17l-5.77,33c-7.53,2.96-14.62,6.81-21.11,11.39l-30.76-13.8c-5.69,5.27-10.89,11.08-15.55,17.29l16.85,29.12c-3.92,6.93-7.04,14.4-9.23,22.24l-32.12,9.11c-.72,5.75-1.1,11.59-1.03,17.54.02,1.92.1,3.82.2,5.73l30.61,11.08c1.43,8.61,3.98,16.85,7.44,24.55l-17.93,26.42c4.21,6.54,9,12.7,14.3,18.34l28.94-10.58c6.98,6.15,14.83,11.31,23.32,15.33l3.06,29.9c7.33,2.28,14.93,3.88,22.74,4.81l14.52-25.22c1.14.03,2.29.07,3.45.04,9.06-.08,17.81-1.4,26.14-3.74l20.52,19.93c7.2-3.04,14.06-6.7,20.53-10.93l-4.95-27.85c7.91-6.47,14.82-14.1,20.44-22.65l28.22,2.05c3.53-6.85,6.48-14.04,8.74-21.52l-22.06-18.46ZM152.4,341.41c-43.21.47-78.62-34.19-79.08-77.4-.46-43.2,34.19-78.61,77.4-79.08,43.21-.46,78.6,34.21,79.06,77.41.47,43.2-34.18,78.6-77.39,79.07Z"/>
14
+ </g>
15
+ <g>
16
+ <path class="cls-1" d="M345.31,139.47c-7.44-19.18-29.04-28.71-48.23-21.26-19.18,7.45-28.7,29.05-21.25,48.24,7.45,19.18,29.04,28.69,48.24,21.25,19.19-7.46,28.71-29.05,21.25-48.24ZM316.47,168.16c-2.1.82-4.26,1.17-6.38,1.1-6.35-.19-12.28-4.11-14.73-10.4-3.26-8.4.9-17.85,9.3-21.11,2.1-.82,4.26-1.17,6.38-1.1,6.35.19,12.28,4.1,14.73,10.4,3.26,8.39-.9,17.85-9.29,21.1Z"/>
17
+ <path class="cls-1" d="M365.01,142.5c-.63-3.23-1.54-6.45-2.76-9.62-.79-2.02-1.7-3.99-2.69-5.87l8.96-13.52c-2.38-3.63-5.07-7-8.01-10.06l-15.7,5.92c-4.05-3.17-8.5-5.75-13.22-7.69l-1.93-17.07c-4.11-1.24-8.33-2.09-12.62-2.56l-8.73,15.54c-4.61.17-9.26.95-13.85,2.36l-3.84-3.65-9.31-8.86c-4.02,1.73-7.8,3.79-11.32,6.13l3.44,18.23c-3.33,3-6.25,6.38-8.72,10.02l-18.64-1.16c-1.93,3.84-3.49,7.87-4.69,11.99l14.41,11.81c-.68,4.36-.85,8.84-.45,13.33l-14.87,10.98c.75,3.12,1.69,6.22,2.88,9.29.38.99.8,1.96,1.22,2.93l18.03-.22c2.42,4.19,5.35,7.96,8.64,11.28l-4.15,17.19c3.46,2.57,7.14,4.83,10.99,6.72l12.94-11.13c4.82,1.83,9.89,2.98,15.07,3.4l7.42,14.9c4.24-.25,8.5-.9,12.73-1.94l2.61-15.9c.6-.21,1.2-.41,1.8-.65,4.68-1.81,8.96-4.2,12.82-7.04l14.52,6.33c3.14-2.98,5.99-6.21,8.51-9.67l-7.99-13.47c2.84-4.9,4.94-10.2,6.18-15.73l15.03-4.44c.49-4.24.62-8.54.34-12.86l-15.03-5.27ZM326.25,193.35c-22.31,8.66-47.42-2.4-56.08-24.71-8.66-22.3,2.4-47.42,24.71-56.08,22.31-8.66,47.41,2.41,56.07,24.72,8.66,22.3-2.4,47.41-24.7,56.07Z"/>
18
+ </g>
19
+ <g>
20
+ <path class="cls-1" d="M219.71,100.11c13.58-15.46,12.07-39.01-3.4-52.6-15.47-13.58-39.02-12.06-52.61,3.41-13.58,15.46-12.05,39.01,3.41,52.6,15.47,13.58,39.02,12.06,52.6-3.41ZM180.95,87.77c-1.69-1.49-3-3.24-3.92-5.15-2.76-5.72-2.02-12.79,2.43-17.87,5.95-6.77,16.25-7.44,23.02-1.49,1.69,1.49,3,3.24,3.92,5.15,2.77,5.72,2.03,12.79-2.43,17.87-5.94,6.76-16.25,7.44-23.01,1.49Z"/>
21
+ <path class="cls-1" d="M226.11,118.99c2.57-2.05,5.01-4.34,7.26-6.89,1.43-1.64,2.75-3.35,3.97-5.1l16.13,1.71c2.12-3.79,3.87-7.72,5.23-11.75l-12.5-11.19c.94-5.05,1.18-10.19.72-15.28l14.26-9.59c-.8-4.22-1.99-8.35-3.55-12.38l-17.81-.57c-2.28-4.01-5.12-7.78-8.49-11.2l1.47-5.09,3.56-12.34c-3.39-2.77-6.97-5.17-10.67-7.21l-14.59,11.47c-4.2-1.57-8.54-2.6-12.92-3.11l-7.57-17.07c-4.3.06-8.59.54-12.81,1.37l-3.83,18.24c-4.18,1.41-8.23,3.33-12.03,5.75l-16.61-8.13c-2.42,2.1-4.74,4.37-6.91,6.85-.7.8-1.37,1.61-2.04,2.44l8.52,15.89c-2.6,4.08-4.59,8.42-6.02,12.87l-17.16,4.25c-.68,4.26-.99,8.56-.89,12.85l15.84,6.34c.6,5.12,1.92,10.15,3.94,14.94l-9.8,13.46c2.18,3.65,4.72,7.12,7.59,10.4l15.31-5.02c.46.44.92.87,1.41,1.29,3.76,3.32,7.86,6.01,12.16,8.13l1.09,15.81c4.09,1.41,8.27,2.44,12.5,3.09l8.26-13.31c5.65.26,11.32-.33,16.8-1.78l10.87,11.29c3.99-1.52,7.86-3.39,11.57-5.64l-2.26-15.77ZM163.11,108.07c-17.98-15.79-19.75-43.18-3.96-61.15,15.79-17.98,43.17-19.75,61.16-3.96,17.98,15.8,19.74,43.18,3.95,61.15-15.79,17.98-43.16,19.75-61.15,3.96Z"/>
22
+ </g>
23
+ </svg>
@@ -0,0 +1,93 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import MapIcon from '../../assets/map-folded.svg'
3
+ import ChartIcon from '../../assets/icon-chart-bar.svg'
4
+ import MarkupIncludeIcon from '../../assets/icon-code.svg'
5
+ import { FilterFunction, JsonEditor, UpdateFunction } from 'json-edit-react'
6
+ import { formatConfigBeforeSave as stripConfig } from '../../helpers/formatConfigBeforeSave'
7
+ import './advanced-editor-styles.css'
8
+ import _ from 'lodash'
9
+
10
+ export const AdvancedEditor = ({ loadConfig, config, convertStateToConfig, onExpandCollapse = () => {} }) => {
11
+ const [advancedToggle, _setAdvancedToggle] = useState(false)
12
+ const [configTextboxValue, setConfigTextbox] = useState<Record<string, any>>({})
13
+ const setAdvancedToggle = val => {
14
+ _setAdvancedToggle(val)
15
+ onExpandCollapse()
16
+ }
17
+
18
+ const collapseFields: FilterFunction = input => {
19
+ if (['datasets', 'data', 'originalFormattedData', 'formattedData'].includes(String(input.key))) return true
20
+ return false
21
+ }
22
+
23
+ const onUpdate: UpdateFunction = val => {
24
+ setConfigTextbox(val.newData)
25
+ }
26
+
27
+ useEffect(() => {
28
+ let parsedConfig = stripConfig(config)
29
+ if (config.type !== 'dashboard') {
30
+ parsedConfig = convertStateToConfig()
31
+ }
32
+
33
+ setConfigTextbox(parsedConfig)
34
+ }, [config])
35
+
36
+ const typeLookup = {
37
+ chart: ['Charts', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
38
+ dashboard: ['Dashboard', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
39
+ map: ['Maps', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html', <MapIcon />],
40
+ 'markup-include': ['Markup Include', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/Markup-Include.html', <MarkupIncludeIcon />]
41
+ }
42
+
43
+ if (!config.type) return <></>
44
+ return (
45
+ <>
46
+ <a href={typeLookup[config.type][1]} target='_blank' rel='noopener noreferrer' className='guidance-link'>
47
+ {typeLookup[config.type][2]}
48
+ <div>
49
+ <span className='heading-3'>Get Help with {typeLookup[config.type][0]}</span>
50
+ <p>Examples and documentation</p>
51
+ </div>
52
+ </a>
53
+ <div className='advanced'>
54
+ <span className='advanced-toggle-link' onClick={() => setAdvancedToggle(!advancedToggle)}>
55
+ <span>{advancedToggle ? `— ` : `+ `}</span>Advanced Options
56
+ </span>
57
+ {advancedToggle && (
58
+ <React.Fragment>
59
+ <section className='error-box py-2 px-3 my-2'>
60
+ <div>
61
+ <strong className='pt-1'>Warning</strong>
62
+ <p>This can cause serious errors in your visualization.</p>
63
+ </div>
64
+ </section>
65
+ <p className='pb-2'>
66
+ This tool displays the actual <acronym title='JavaScript Object Notation'>JSON</acronym> configuration that is generated by this editor and allows you to edit properties directly and apply them.
67
+ </p>
68
+ <button
69
+ className='btn '
70
+ onClick={() => {
71
+ navigator.clipboard.writeText(JSON.stringify(configTextboxValue))
72
+ }}
73
+ >
74
+ Copy to Clipboard
75
+ </button>
76
+ <JsonEditor className='advanced-json-editor' data={configTextboxValue} onUpdate={onUpdate} rootName='' collapse={collapseFields} />
77
+ <button
78
+ className='btn full-width'
79
+ onClick={() => {
80
+ loadConfig(configTextboxValue)
81
+ setAdvancedToggle(!advancedToggle)
82
+ }}
83
+ >
84
+ Apply
85
+ </button>
86
+ </React.Fragment>
87
+ )}
88
+ </div>
89
+ </>
90
+ )
91
+ }
92
+
93
+ export default AdvancedEditor
@@ -0,0 +1,3 @@
1
+ .advanced-json-editor {
2
+ width: 100vw;
3
+ }
@@ -0,0 +1 @@
1
+ export { default } from './AdvancedEditor'
@@ -0,0 +1,15 @@
1
+ .cdc-open-viz-module {
2
+ .accordion__panel > .alert {
3
+ margin-bottom: 15px;
4
+ }
5
+
6
+ .alert {
7
+ align-items: center;
8
+ justify-content: center;
9
+ display: inline-flex;
10
+
11
+ .cove-icon {
12
+ margin: 5px;
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,39 @@
1
+ import Icon from '../../ui/Icon'
2
+
3
+ import DOMPurify from 'dompurify'
4
+ import React from 'react'
5
+
6
+ import './Alert.styles.css'
7
+
8
+ type AlertProps = {
9
+ // type of alert for styling the alert box
10
+ type?: 'success' | 'danger' | 'info'
11
+ // message to display in the alert box
12
+ message?: string
13
+ // size of the icon in the alert box
14
+ iconSize?: number
15
+ // heading for the alert box
16
+ heading?: string
17
+ }
18
+
19
+ const Alert: React.FC<AlertProps> = ({ type = 'info', message = '', iconSize = 21, heading }) => {
20
+ // sanitize the text for setting dangerouslySetInnerHTML
21
+ const sanitizedData = () => ({
22
+ __html: DOMPurify.sanitize(message)
23
+ })
24
+
25
+ // reset styles to avoid conflicts in wcms
26
+ const styleResets = { width: 'unset', height: 'unset', paddingRight: '5px' }
27
+
28
+ return (
29
+ <div className={`alert alert-${type} p-1`} role='alert'>
30
+ {heading && <h4 className='alert-heading'>{heading}</h4>}
31
+ {type === 'success' && <Icon display='check' size={iconSize} />}
32
+ {type === 'danger' && <Icon display='warningCircle' size={iconSize} />}
33
+ {type === 'info' && <Icon display='info' size={iconSize} />}
34
+ <span dangerouslySetInnerHTML={sanitizedData()} />
35
+ </div>
36
+ )
37
+ }
38
+
39
+ export default Alert
@@ -0,0 +1,3 @@
1
+ import Alert from './components/Alert'
2
+
3
+ export default Alert
@@ -19,6 +19,7 @@ import boxplotCellMatrix from './helpers/boxplotCellMatrix'
19
19
  import removeNullColumns from './helpers/removeNullColumns'
20
20
  import { TableConfig } from './types/TableConfig'
21
21
  import { Column } from '../../types/Column'
22
+ import { pivotData } from '../../helpers/pivotData'
22
23
 
23
24
  export type DataTableProps = {
24
25
  applyLegendToRow?: Function
@@ -52,11 +53,39 @@ export type DataTableProps = {
52
53
 
53
54
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
54
55
  const DataTable = (props: DataTableProps) => {
55
- const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData: parentRuntimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
56
- const runtimeData = removeNullColumns(parentRuntimeData)
56
+ const {
57
+ config,
58
+ dataConfig,
59
+ tableTitle,
60
+ vizTitle,
61
+ rawData,
62
+ runtimeData: parentRuntimeData,
63
+ headerColor,
64
+ expandDataTable,
65
+ columns,
66
+ viewport,
67
+ formatLegendLocation,
68
+ tabbingId,
69
+ wrapColumns
70
+ } = props
71
+ const runtimeData = useMemo(() => {
72
+ const data = removeNullColumns(parentRuntimeData)
73
+ if (config.table.pivot) {
74
+ const { columnName, valueColumn } = config.table.pivot
75
+ if (columnName && valueColumn) {
76
+ return pivotData(data, columnName, valueColumn)
77
+ }
78
+ }
79
+ return data
80
+ }, [parentRuntimeData, config.table.pivot?.columnName, config.table.pivot?.valueColumn])
81
+
57
82
  const [expanded, setExpanded] = useState(expandDataTable)
58
83
 
59
- const [sortBy, setSortBy] = useState<any>({ column: config.type === 'map' ? 'geo' : 'date', asc: false, colIndex: null })
84
+ const [sortBy, setSortBy] = useState<any>({
85
+ column: config.type === 'map' ? 'geo' : 'date',
86
+ asc: false,
87
+ colIndex: null
88
+ })
60
89
 
61
90
  const [accessibilityLabel, setAccessibilityLabel] = useState('')
62
91
 
@@ -75,7 +104,8 @@ const DataTable = (props: DataTableProps) => {
75
104
  // Change accessibility label depending on expanded status
76
105
  useEffect(() => {
77
106
  const expandedLabel = 'Accessible data table.'
78
- const collapsedLabel = 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
107
+ const collapsedLabel =
108
+ 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
79
109
 
80
110
  if (expanded === true && accessibilityLabel !== expandedLabel) {
81
111
  setAccessibilityLabel(expandedLabel)
@@ -130,7 +160,9 @@ const DataTable = (props: DataTableProps) => {
130
160
 
131
161
  const caption = useMemo(() => {
132
162
  if (config.type === 'map') {
133
- return config.table.caption ? config.table.caption : `Data table showing data for the ${mapLookup[config.general.geoType]} figure.`
163
+ return config.table.caption
164
+ ? config.table.caption
165
+ : `Data table showing data for the ${mapLookup[config.general.geoType]} figure.`
134
166
  } else {
135
167
  return config.table.caption ? config.table.caption : `Data table showing data for the ${config.type} figure.`
136
168
  }
@@ -176,48 +208,102 @@ const DataTable = (props: DataTableProps) => {
176
208
  }
177
209
  }
178
210
 
211
+ const getMediaControlsClasses = () => {
212
+ const classes = ['download-links']
213
+ const isLegendOnBottom = config?.legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(viewport)
214
+ if (config.brush?.active && !isLegendOnBottom) classes.push('brush-active')
215
+ if (config.brush?.active && config.legend.hide) classes.push('brush-active')
216
+ return classes
217
+ }
218
+
179
219
  return (
180
220
  <ErrorBoundary component='DataTable'>
181
- <MediaControls.Section classes={['download-links']}>
221
+ <MediaControls.Section classes={getMediaControlsClasses()}>
182
222
  <MediaControls.Link config={config} dashboardDataConfig={dataConfig} />
183
- {(config.table.download || config.general?.showDownloadButton) && <DownloadButton rawData={getDownloadData()} fileName={`${vizTitle || 'data-table'}.csv`} headerColor={headerColor} />}
223
+ {(config.table.download || config.general?.showDownloadButton) && (
224
+ <DownloadButton
225
+ rawData={getDownloadData()}
226
+ fileName={`${vizTitle || 'data-table'}.csv`}
227
+ headerColor={headerColor}
228
+ />
229
+ )}
184
230
  </MediaControls.Section>
185
- <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
231
+ <section
232
+ id={tabbingId.replace('#', '')}
233
+ className={`data-table-container ${viewport} w-100`}
234
+ aria-label={accessibilityLabel}
235
+ >
186
236
  <SkipTo skipId={skipId} skipMessage='Skip Data Table' />
187
- {config.table.collapsible !== false && <ExpandCollapse expanded={expanded} setExpanded={setExpanded} fontSize={config.fontSize} tableTitle={tableTitle} viewport={viewport} />}
237
+ {config.table.collapsible !== false && (
238
+ <ExpandCollapse
239
+ expanded={expanded}
240
+ setExpanded={setExpanded}
241
+ fontSize={config.fontSize}
242
+ tableTitle={tableTitle}
243
+ viewport={viewport}
244
+ />
245
+ )}
188
246
  <div className='table-container' style={limitHeight}>
189
247
  <Table
248
+ preliminaryData={config.preliminaryData}
190
249
  viewport={viewport}
191
250
  wrapColumns={wrapColumns}
192
- childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData, isVertical, sortBy, hasRowType, viewport })}
251
+ childrenMatrix={
252
+ config.type === 'map'
253
+ ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData, viewport })
254
+ : chartCellMatrix({ rows, ...props, runtimeData, isVertical, sortBy, hasRowType, viewport })
255
+ }
193
256
  tableName={config.type}
194
257
  caption={caption}
195
258
  stickyHeader
196
259
  hasRowType={hasRowType}
197
- headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
198
- tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}${isVertical ? '' : ' horizontal'}`, 'aria-live': 'assertive', 'aria-rowcount': config?.data?.length ? config.data.length : -1, hidden: !expanded }}
260
+ headContent={
261
+ config.type === 'map' ? (
262
+ <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} />
263
+ ) : (
264
+ <ChartHeader
265
+ data={runtimeData}
266
+ {...props}
267
+ hasRowType={hasRowType}
268
+ isVertical={isVertical}
269
+ sortBy={sortBy}
270
+ setSortBy={setSortBy}
271
+ />
272
+ )
273
+ }
274
+ tableOptions={{
275
+ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}${
276
+ isVertical ? '' : ' horizontal'
277
+ }`,
278
+ 'aria-live': 'assertive',
279
+ 'aria-rowcount': config?.data?.length ? config.data.length : -1,
280
+ hidden: !expanded
281
+ }}
199
282
  fontSize={config.fontSize}
200
283
  />
201
284
 
202
285
  {/* REGION Data Table */}
203
- {noRelativeRegions && config.regions && config.regions.length > 0 && config.visualizationType !== 'Box Plot' && (
204
- <Table
205
- viewport={viewport}
206
- wrapColumns={wrapColumns}
207
- childrenMatrix={regionCellMatrix({ config })}
208
- tableName={config.visualizationType}
209
- caption='Table of the highlighted regions in the visualization'
210
- headContent={
211
- <tr>
212
- <th>Region Name</th>
213
- <th>Start Date</th>
214
- <th>End Date</th>
215
- </tr>
216
- }
217
- tableOptions={{ className: 'region-table data-table' }}
218
- fontSize={config.fontSize}
219
- />
220
- )}
286
+ {noRelativeRegions &&
287
+ config.regions &&
288
+ config.regions.length > 0 &&
289
+ config.visualizationType !== 'Box Plot' && (
290
+ <Table
291
+ viewport={viewport}
292
+ wrapColumns={wrapColumns}
293
+ childrenMatrix={regionCellMatrix({ config })}
294
+ tableName={config.visualizationType}
295
+ caption='Table of the highlighted regions in the visualization'
296
+ headContent={
297
+ <tr>
298
+ <th>Region Name</th>
299
+ <th>Start Date</th>
300
+ <th>End Date</th>
301
+ </tr>
302
+ }
303
+ tableOptions={{ className: 'region-table data-table' }}
304
+ fontSize={config.fontSize}
305
+ />
306
+ )}
221
307
  </div>
222
308
  </section>
223
309
  <div id={skipId} className='cdcdataviz-sr-only'>
@@ -229,7 +315,11 @@ const DataTable = (props: DataTableProps) => {
229
315
  // Render Data Table for Box Plots
230
316
  return (
231
317
  <ErrorBoundary component='DataTable'>
232
- <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
318
+ <section
319
+ id={tabbingId.replace('#', '')}
320
+ className={`data-table-container ${viewport} w-100`}
321
+ aria-label={accessibilityLabel}
322
+ >
233
323
  <SkipTo skipId={skipId} skipMessage='Skip Data Table' />
234
324
  <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
235
325
  <div className='table-container' style={limitHeight}>
@@ -241,7 +331,12 @@ const DataTable = (props: DataTableProps) => {
241
331
  caption={caption}
242
332
  stickyHeader
243
333
  headContent={<BoxplotHeader categories={config.boxplot.categories} />}
244
- tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`, 'aria-live': 'assertive', 'aria-rowcount': 11, hidden: !expanded }}
334
+ tableOptions={{
335
+ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`,
336
+ 'aria-live': 'assertive',
337
+ 'aria-rowcount': 11,
338
+ hidden: !expanded
339
+ }}
245
340
  fontSize={config.fontSize}
246
341
  />
247
342
  </div>
@@ -5,6 +5,7 @@ import DataTable from './DataTable'
5
5
  import DataTableEditorPanel from './components/DataTableEditorPanel'
6
6
  import Filters from '../Filters'
7
7
  import { TableConfig } from './types/TableConfig'
8
+ import { filterVizData } from '../../helpers/filterVizData'
8
9
 
9
10
  type StandAloneProps = {
10
11
  visualizationKey: string
@@ -14,34 +15,12 @@ type StandAloneProps = {
14
15
  updateConfig?: (Visualization) => void
15
16
  }
16
17
 
17
- // filterData is copied from ./packages/chart/src/helpers/filterData.ts
18
- // consider moving this to a shared location
19
- const filterData = (filters, data) => {
20
- if (!filters) return data
21
- const filteredData: any[] = []
22
-
23
- data.forEach(row => {
24
- let add = true
25
- filters
26
- .filter(filter => filter.type !== 'url')
27
- .forEach(filter => {
28
- if (row[filter.columnName] != filter.active) {
29
- add = false
30
- }
31
- })
32
-
33
- if (add) filteredData.push(row)
34
- })
35
-
36
- return filteredData
37
- }
38
-
39
18
  const DataTableStandAlone: React.FC<StandAloneProps> = ({ visualizationKey, config, updateConfig, viewport, isEditor }) => {
40
- const [filteredData, setFilteredData] = useState<Record<string, any>[]>(filterData(config.filters, config.formattedData))
19
+ const [filteredData, setFilteredData] = useState<Record<string, any>[]>(filterVizData(config.filters, config.formattedData))
41
20
 
42
21
  useEffect(() => {
43
22
  // when using editor changes to filter should update the data
44
- setFilteredData(filterData(config.filters, config.formattedData))
23
+ setFilteredData(filterVizData(config.filters, config?.formattedData?.length > 0 ? config.formattedData : config.data))
45
24
  }, [config.filters])
46
25
 
47
26
  if (isEditor)
@@ -53,7 +32,7 @@ const DataTableStandAlone: React.FC<StandAloneProps> = ({ visualizationKey, conf
53
32
 
54
33
  return (
55
34
  <>
56
- <Filters config={config} setConfig={updateConfig} setFilteredData={setFilteredData} filterData={filterData} filteredData={filteredData} excludedData={config.formattedData} />
35
+ <Filters config={config} setConfig={updateConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={config.formattedData} />
57
36
  <DataTable expandDataTable={true} config={config} rawData={config.data} runtimeData={filteredData} tabbingId={visualizationKey} tableTitle={config.table.label} viewport={viewport || 'lg'} />
58
37
  </>
59
38
  )
@@ -1,4 +1,4 @@
1
- import { AccordionItem, AccordionItemButton, AccordionItemHeading, AccordionItemPanel } from 'react-accessible-accordion'
1
+ import { Accordion, AccordionItem, AccordionItemButton, AccordionItemHeading, AccordionItemPanel } from 'react-accessible-accordion'
2
2
  import DataTableEditor from '../../EditorPanel/DataTableEditor'
3
3
  import { Visualization } from '@cdc/core/types/Visualization'
4
4
  import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
@@ -25,9 +25,9 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
25
25
  })
26
26
  }
27
27
 
28
- const columns = Object.keys(config.originalFormattedData[0] || {})
28
+ const columns = Object.keys(config.originalFormattedData?.[0] || {})
29
29
  return (
30
- <>
30
+ <Accordion allowZeroExpanded={true}>
31
31
  <AccordionItem>
32
32
  <AccordionItemHeading>
33
33
  <AccordionItemButton>Filters</AccordionItemButton>
@@ -52,7 +52,7 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
52
52
  <DataTableEditor config={config} columns={columns} updateField={updateField} isDashboard={true} />
53
53
  </AccordionItemPanel>
54
54
  </AccordionItem>
55
- </>
55
+ </Accordion>
56
56
  )
57
57
  }
58
58
 
@@ -1,7 +1,7 @@
1
1
  import Icon from '../../ui/Icon'
2
+ import { fontSizes } from '../../../helpers/cove/fontSettings'
2
3
 
3
4
  const ExpandCollapse = ({ expanded, setExpanded, tableTitle, fontSize, viewport }) => {
4
- const fontSizes = { small: 16, medium: 18, large: 20 }
5
5
  const titleFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[fontSize]}px`
6
6
  return (
7
7
  <div
@@ -1,4 +1,4 @@
1
- import LegendCircle from '@cdc/core/components/LegendCircle'
1
+ import LegendShape from '@cdc/core/components/LegendShape'
2
2
  import { customSort } from './customSort'
3
3
  import { getSeriesName } from './getSeriesName'
4
4
  import { DataTableProps } from '../DataTable'
@@ -6,6 +6,7 @@ import { getChartCellValue } from './getChartCellValue'
6
6
  import { getDataSeriesColumns } from './getDataSeriesColumns'
7
7
  import { ReactNode } from 'react'
8
8
  import { CellMatrix, GroupCellMatrix } from '../../Table/types/CellMatrix'
9
+ import { getRowType } from './getRowType'
9
10
 
10
11
  type ChartRowsProps = DataTableProps & {
11
12
  rows: string[]
@@ -15,7 +16,7 @@ type ChartRowsProps = DataTableProps & {
15
16
  hasRowType?: boolean
16
17
  }
17
18
 
18
- const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
19
+ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, hasRowType }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
19
20
  const groupBy = config.table?.groupBy
20
21
  const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
21
22
 
@@ -58,15 +59,8 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
58
59
  } else {
59
60
  return rows.map(row => {
60
61
  if (hasRowType) {
61
- let rowType
62
- let rowValues = []
63
- dataSeriesColumns.forEach((column, j) => {
64
- if (column.match(/row[_-]?type/i)) {
65
- rowType = getChartCellValue(row, column, config, runtimeData)
66
- } else {
67
- rowValues.push(getChartCellValue(row, column, config, runtimeData))
68
- }
69
- })
62
+ const rowType = getRowType(runtimeData[row])
63
+ const rowValues = dataSeriesColumns.map(column => getChartCellValue(row, column, config, runtimeData))
70
64
  return [rowType, ...rowValues]
71
65
  } else {
72
66
  return dataSeriesColumns.map((column, j) => getChartCellValue(row, column, config, runtimeData))
@@ -80,7 +74,7 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
80
74
  config.visualizationType !== 'Pie'
81
75
  ? [
82
76
  <>
83
- {colorScale && colorScale(seriesName) && <LegendCircle viewport={viewport} fill={colorScale(seriesName)} />}
77
+ {colorScale && colorScale(seriesName) && <LegendShape fill={colorScale(seriesName)} />}
84
78
  {seriesName}
85
79
  </>
86
80
  ]
@@ -60,19 +60,23 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
60
60
  const isValueMatch = String(pd.value) === String(labelValue)
61
61
  // check entered suppression column against table key
62
62
  const isColumnMatch = !pd.column || pd.column === column
63
- if (isValueMatch && isColumnMatch && pd.displayTable && pd.type === 'suppression') {
63
+ const barSeriesExist = config.runtime?.barSeriesKeys?.includes(column)
64
+ const lineSeriesExist = config.runtime?.lineSeriesKeys?.includes(column)
65
+ const showSymbol = config.general.showSuppressedSymbol
66
+ if (isValueMatch && isColumnMatch && pd.displayTable && pd.type === 'suppression' && config.visualizationSubType !== 'stacked') {
64
67
  switch (config.visualizationType) {
65
68
  case 'Combo':
66
- cellValue = config.runtime.barSeriesKeys.includes(column) ? pd.iconCode : config.runtime.lineSeriesKeys.includes(column) ? pd.lineCode : ''
69
+ cellValue = barSeriesExist && showSymbol ? pd.iconCode : lineSeriesExist && showSymbol ? pd.lineCode : ''
67
70
  break
68
71
  case 'Bar':
69
- cellValue = pd.iconCode
72
+ cellValue = !showSymbol ? '' : pd.iconCode
70
73
  break
71
74
  case 'Line':
72
- cellValue = pd.lineCode
75
+ cellValue = !showSymbol ? '' : pd.lineCode
73
76
  break
74
77
  }
75
78
  }
76
79
  })
77
- return cellValue
80
+ const shoMissingDataCellValue = config.general?.showMissingDataLabel && (!labelValue || labelValue === 'null')
81
+ return shoMissingDataCellValue ? 'N/A' : cellValue
78
82
  }
@@ -3,15 +3,16 @@ import _ from 'lodash'
3
3
  import { Column } from '../../../types/Column'
4
4
 
5
5
  export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, runtimeData: Object[]): string[] => {
6
- const configColumns: Record<string, Column> = _.cloneDeep(config.columns) || {}
6
+ if (config.visualizationType === 'Sankey') return Object.keys(config?.data?.[0]?.tableData[0])
7
+ const configColumns = _.cloneDeep(config.columns) || ({} as Record<string, Column>)
7
8
  const excludeColumns = Object.values(configColumns)
8
9
  .filter(column => column.dataTable === false)
9
10
  .map(column => column.name)
10
11
  let tmpSeriesColumns: string[] = []
11
12
  if (config.visualizationType !== 'Pie') {
12
13
  tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey] : [] //, ...config.runtime.seriesLabelsAll
13
- if (config.series) {
14
- config.series.forEach(element => {
14
+ if (config.runtime?.series) {
15
+ config.runtime?.series.forEach(element => {
15
16
  tmpSeriesColumns.push(element.dataKey)
16
17
  })
17
18
  } else if (runtimeData && runtimeData.length > 0) {
@@ -21,12 +22,15 @@ export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, r
21
22
  tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey, config.yAxis?.dataKey] : [config.yAxis?.dataKey]
22
23
  }
23
24
 
25
+ const dataColumns = Object.keys(runtimeData[0] || {})
26
+
24
27
  // then add the additional Columns
25
- Object.keys(configColumns).forEach(function (key) {
26
- var value = configColumns[key]
28
+ Object.values(configColumns).forEach(function (value) {
29
+ if (!value.name) return
27
30
  // add if not the index AND it is enabled to be added to data table
28
31
  const alreadyAdded = tmpSeriesColumns.includes(value.name)
29
- if (value.name !== config.xAxis?.dataKey && value.dataTable === true && !alreadyAdded) {
32
+ const columnIsInData = dataColumns.includes(value.name) // null columns are excluded from data
33
+ if (value.name !== config.xAxis?.dataKey && value.dataTable === true && !alreadyAdded && columnIsInData) {
30
34
  tmpSeriesColumns.push(value.name)
31
35
  }
32
36
  })
@@ -53,6 +57,5 @@ export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, r
53
57
  })
54
58
 
55
59
  tmpSeriesColumns.sort((a, b) => columnOrderingHash[a] - columnOrderingHash[b])
56
-
57
60
  return tmpSeriesColumns
58
61
  }
@@ -0,0 +1,6 @@
1
+ import { RowType } from '../../Table/types/RowType'
2
+
3
+ export const getRowType = (row: Record<string, any>): RowType => {
4
+ const rowTypeKey = Object.keys(row).find(key => key.match(/row[_-]?type/i))
5
+ return row[rowTypeKey]
6
+ }