@cdc/core 4.24.4 → 4.24.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.
- package/components/AdvancedEditor/AdvancedEditor.tsx +93 -0
- package/components/AdvancedEditor/advanced-editor-styles.css +3 -0
- package/components/AdvancedEditor/index.ts +1 -0
- package/components/DataTable/DataTable.tsx +33 -15
- package/components/DataTable/DataTableStandAlone.tsx +30 -4
- package/components/DataTable/components/ChartHeader.tsx +3 -2
- package/components/DataTable/components/DataTableEditorPanel.tsx +23 -6
- package/components/DataTable/components/ExpandCollapse.tsx +1 -1
- package/components/DataTable/helpers/chartCellMatrix.tsx +5 -10
- package/components/DataTable/helpers/getChartCellValue.ts +26 -2
- package/components/DataTable/helpers/getDataSeriesColumns.ts +40 -16
- package/components/DataTable/helpers/getRowType.ts +6 -0
- package/components/DataTable/helpers/getSeriesName.ts +2 -1
- package/components/DataTable/helpers/{customColumns.ts → removeNullColumns.ts} +3 -3
- package/components/DataTable/types/TableConfig.ts +12 -22
- package/components/EditorPanel/ColumnsEditor.tsx +278 -262
- package/components/EditorPanel/DataTableEditor.tsx +159 -60
- package/components/EditorPanel/FieldSetWrapper.tsx +51 -0
- package/components/EditorPanel/FootnotesEditor.tsx +77 -0
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +227 -0
- package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +54 -0
- package/components/EditorPanel/VizFilterEditor/index.ts +1 -0
- package/components/EditorWrapper/EditorWrapper.tsx +47 -0
- package/components/EditorWrapper/editor-wrapper.style.css +14 -0
- package/components/EditorWrapper/index.ts +1 -0
- package/components/{Filters.jsx → Filters.tsx} +102 -70
- package/components/Footnotes/Footnotes.tsx +25 -0
- package/components/Footnotes/FootnotesStandAlone.tsx +45 -0
- package/components/Footnotes/footnotes.css +5 -0
- package/components/Footnotes/index.ts +1 -0
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +8 -4
- package/components/Layout/components/Visualization/index.tsx +14 -5
- package/components/MediaControls.jsx +1 -1
- package/components/MultiSelect/MultiSelect.tsx +36 -9
- package/components/MultiSelect/multiselect.styles.css +0 -3
- package/components/_stories/DataTable.stories.tsx +1 -2
- package/components/_stories/EditorPanel.stories.tsx +1 -0
- package/components/_stories/Footnotes.stories.tsx +17 -0
- package/components/inputs/InputSelect.tsx +17 -6
- package/components/ui/Icon.tsx +1 -2
- package/helpers/DataTransform.ts +9 -32
- package/helpers/addValuesToFilters.ts +56 -0
- package/helpers/cove/accessibility.ts +1 -0
- package/helpers/cove/fontSettings.ts +2 -0
- package/helpers/coveUpdateWorker.ts +11 -2
- package/helpers/filterVizData.ts +30 -0
- package/helpers/footnoteSymbols.ts +11 -0
- package/helpers/formatConfigBeforeSave.ts +90 -0
- package/helpers/gatherQueryParams.ts +14 -7
- package/helpers/lineChartHelpers.js +2 -1
- package/helpers/pivotData.ts +18 -0
- package/helpers/queryStringUtils.ts +29 -0
- package/helpers/tests/updateFieldFactory.test.ts +1 -0
- package/helpers/updateFieldFactory.ts +1 -1
- package/helpers/useDataVizClasses.js +0 -4
- package/helpers/ver/4.23.4.ts +27 -0
- package/helpers/ver/4.24.5.ts +32 -0
- package/helpers/ver/4.24.7.ts +92 -0
- package/package.json +6 -4
- package/styles/_button-section.scss +6 -1
- package/styles/_data-table.scss +0 -1
- package/styles/_reset.scss +7 -6
- package/styles/base.scss +4 -0
- package/styles/v2/themes/_color-definitions.scss +1 -0
- package/types/Annotation.ts +46 -0
- package/types/Column.ts +1 -0
- package/types/ConfigureData.ts +1 -1
- package/types/Footnotes.ts +17 -0
- package/types/General.ts +5 -0
- package/types/Legend.ts +1 -0
- package/types/MarkupInclude.ts +26 -0
- package/types/Runtime.ts +3 -7
- package/types/Series.ts +1 -1
- package/types/Table.ts +21 -14
- package/types/Visualization.ts +40 -11
- package/types/VizFilter.ts +24 -0
- package/LICENSE +0 -201
- package/components/AdvancedEditor.jsx +0 -74
- package/helpers/queryStringUtils.js +0 -26
- package/types/BaseVisualizationType.ts +0 -1
|
@@ -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 @@
|
|
|
1
|
+
export { default } from './AdvancedEditor'
|
|
@@ -16,9 +16,10 @@ import Table from '../Table'
|
|
|
16
16
|
import chartCellMatrix from './helpers/chartCellMatrix'
|
|
17
17
|
import regionCellMatrix from './helpers/regionCellMatrix'
|
|
18
18
|
import boxplotCellMatrix from './helpers/boxplotCellMatrix'
|
|
19
|
-
import
|
|
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,7 +53,18 @@ 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, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
|
|
56
|
+
const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData: parentRuntimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
|
|
57
|
+
const runtimeData = useMemo(() => {
|
|
58
|
+
const data = removeNullColumns(parentRuntimeData)
|
|
59
|
+
if (config.table.pivot) {
|
|
60
|
+
const { columnName, valueColumn } = config.table.pivot
|
|
61
|
+
if (columnName && valueColumn) {
|
|
62
|
+
return pivotData(data, columnName, valueColumn)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return data
|
|
66
|
+
}, [parentRuntimeData, config.table.pivot?.columnName, config.table.pivot?.valueColumn])
|
|
67
|
+
|
|
56
68
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
57
69
|
|
|
58
70
|
const [sortBy, setSortBy] = useState<any>({ column: config.type === 'map' ? 'geo' : 'date', asc: false, colIndex: null })
|
|
@@ -98,25 +110,23 @@ const DataTable = (props: DataTableProps) => {
|
|
|
98
110
|
break
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
const rawRows = Object.keys(_runtimeData).filter(column => column != 'columns')
|
|
113
|
+
const rawRows = Object.keys(runtimeData).filter(column => column != 'columns')
|
|
104
114
|
const rows = isVertical
|
|
105
115
|
? rawRows.sort((a, b) => {
|
|
106
116
|
let dataA
|
|
107
117
|
let dataB
|
|
108
118
|
if (config.type === 'map' && config.columns) {
|
|
109
119
|
const sortByColName = config.columns[sortBy.column].name
|
|
110
|
-
dataA =
|
|
111
|
-
dataB =
|
|
120
|
+
dataA = runtimeData[a][sortByColName]
|
|
121
|
+
dataB = runtimeData[b][sortByColName]
|
|
112
122
|
}
|
|
113
123
|
if (config.type === 'chart' || config.type === 'dashboard') {
|
|
114
|
-
dataA =
|
|
115
|
-
dataB =
|
|
124
|
+
dataA = runtimeData[a][sortBy.column]
|
|
125
|
+
dataB = runtimeData[b][sortBy.column]
|
|
116
126
|
}
|
|
117
127
|
if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
|
|
118
|
-
dataA = timeParse(config.runtime.xAxis.dateParseFormat)(
|
|
119
|
-
dataB = timeParse(config.runtime.xAxis.dateParseFormat)(
|
|
128
|
+
dataA = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[a][config.xAxis.dataKey])
|
|
129
|
+
dataB = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[b][config.xAxis.dataKey])
|
|
120
130
|
}
|
|
121
131
|
return dataA && dataB ? customSort(dataA, dataB, sortBy, config) : 0
|
|
122
132
|
})
|
|
@@ -177,25 +187,33 @@ const DataTable = (props: DataTableProps) => {
|
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
189
|
|
|
190
|
+
const getMediaControlsClasses = () => {
|
|
191
|
+
const classes = ['download-links']
|
|
192
|
+
const isLegendOnBottom = config?.legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(viewport)
|
|
193
|
+
if (config.brush?.active && !isLegendOnBottom) classes.push('brush-active')
|
|
194
|
+
if (config.brush?.active && config.legend.hide) classes.push('brush-active')
|
|
195
|
+
return classes
|
|
196
|
+
}
|
|
197
|
+
|
|
180
198
|
return (
|
|
181
199
|
<ErrorBoundary component='DataTable'>
|
|
182
|
-
<MediaControls.Section classes={
|
|
200
|
+
<MediaControls.Section classes={getMediaControlsClasses()}>
|
|
183
201
|
<MediaControls.Link config={config} dashboardDataConfig={dataConfig} />
|
|
184
202
|
{(config.table.download || config.general?.showDownloadButton) && <DownloadButton rawData={getDownloadData()} fileName={`${vizTitle || 'data-table'}.csv`} headerColor={headerColor} />}
|
|
185
203
|
</MediaControls.Section>
|
|
186
204
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
187
205
|
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
188
|
-
<ExpandCollapse expanded={expanded} setExpanded={setExpanded}
|
|
206
|
+
{config.table.collapsible !== false && <ExpandCollapse expanded={expanded} setExpanded={setExpanded} fontSize={config.fontSize} tableTitle={tableTitle} viewport={viewport} />}
|
|
189
207
|
<div className='table-container' style={limitHeight}>
|
|
190
208
|
<Table
|
|
191
209
|
viewport={viewport}
|
|
192
210
|
wrapColumns={wrapColumns}
|
|
193
|
-
childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData
|
|
211
|
+
childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData, isVertical, sortBy, hasRowType, viewport })}
|
|
194
212
|
tableName={config.type}
|
|
195
213
|
caption={caption}
|
|
196
214
|
stickyHeader
|
|
197
215
|
hasRowType={hasRowType}
|
|
198
|
-
headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={
|
|
216
|
+
headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
|
|
199
217
|
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 }}
|
|
200
218
|
fontSize={config.fontSize}
|
|
201
219
|
/>
|
|
@@ -1,15 +1,41 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
1
2
|
import { ViewPort } from '../../types/ViewPort'
|
|
2
|
-
import
|
|
3
|
+
import EditorWrapper from '../EditorWrapper/EditorWrapper'
|
|
3
4
|
import DataTable from './DataTable'
|
|
5
|
+
import DataTableEditorPanel from './components/DataTableEditorPanel'
|
|
6
|
+
import Filters from '../Filters'
|
|
7
|
+
import { TableConfig } from './types/TableConfig'
|
|
8
|
+
import { filterVizData } from '../../helpers/filterVizData'
|
|
4
9
|
|
|
5
10
|
type StandAloneProps = {
|
|
6
11
|
visualizationKey: string
|
|
7
|
-
config:
|
|
12
|
+
config: TableConfig
|
|
8
13
|
viewport?: ViewPort
|
|
14
|
+
isEditor?: boolean
|
|
15
|
+
updateConfig?: (Visualization) => void
|
|
9
16
|
}
|
|
10
17
|
|
|
11
|
-
const DataTableStandAlone: React.FC<StandAloneProps> = ({ visualizationKey, config, viewport }) => {
|
|
12
|
-
|
|
18
|
+
const DataTableStandAlone: React.FC<StandAloneProps> = ({ visualizationKey, config, updateConfig, viewport, isEditor }) => {
|
|
19
|
+
const [filteredData, setFilteredData] = useState<Record<string, any>[]>(filterVizData(config.filters, config.formattedData))
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// when using editor changes to filter should update the data
|
|
23
|
+
setFilteredData(filterVizData(config.filters, config?.formattedData?.length > 0 ? config.formattedData : config.data))
|
|
24
|
+
}, [config.filters])
|
|
25
|
+
|
|
26
|
+
if (isEditor)
|
|
27
|
+
return (
|
|
28
|
+
<EditorWrapper component={DataTableStandAlone} visualizationKey={visualizationKey} visualizationConfig={config} updateConfig={updateConfig} type={'Table'} viewport={viewport}>
|
|
29
|
+
<DataTableEditorPanel key={visualizationKey} config={config} updateConfig={updateConfig} />
|
|
30
|
+
</EditorWrapper>
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<Filters config={config} setConfig={updateConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={config.formattedData} />
|
|
36
|
+
<DataTable expandDataTable={true} config={config} rawData={config.data} runtimeData={filteredData} tabbingId={visualizationKey} tableTitle={config.table.label} viewport={viewport || 'lg'} />
|
|
37
|
+
</>
|
|
38
|
+
)
|
|
13
39
|
}
|
|
14
40
|
|
|
15
41
|
export default DataTableStandAlone
|
|
@@ -4,9 +4,10 @@ import { getDataSeriesColumns } from '../helpers/getDataSeriesColumns'
|
|
|
4
4
|
import { DownIcon, UpIcon } from './Icons'
|
|
5
5
|
import ScreenReaderText from '@cdc/core/components/elements/ScreenReaderText'
|
|
6
6
|
|
|
7
|
-
type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy;
|
|
7
|
+
type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy; hasRowType? }
|
|
8
8
|
|
|
9
|
-
const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy,
|
|
9
|
+
const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, hasRowType }: ChartHeaderProps) => {
|
|
10
|
+
const groupBy = config.table?.groupBy
|
|
10
11
|
if (!data) return
|
|
11
12
|
let dataSeriesColumns = getDataSeriesColumns(config, isVertical, data)
|
|
12
13
|
if (groupBy) {
|
|
@@ -1,9 +1,11 @@
|
|
|
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'
|
|
5
5
|
import { useMemo } from 'react'
|
|
6
6
|
import ColumnsEditor from '../../EditorPanel/ColumnsEditor'
|
|
7
|
+
import VizFilterEditor from '../../EditorPanel/VizFilterEditor'
|
|
8
|
+
import _ from 'lodash'
|
|
7
9
|
|
|
8
10
|
type DataTableEditorProps = {
|
|
9
11
|
config: Visualization
|
|
@@ -13,7 +15,7 @@ type DataTableEditorProps = {
|
|
|
13
15
|
const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateConfig }) => {
|
|
14
16
|
const updateField = useMemo(() => updateFieldFactory(config, updateConfig), [JSON.stringify(config)])
|
|
15
17
|
const deleteColumn = columnName => {
|
|
16
|
-
const newColumns = config.columns
|
|
18
|
+
const newColumns = _.cloneDeep(config.columns)
|
|
17
19
|
|
|
18
20
|
delete newColumns[columnName]
|
|
19
21
|
|
|
@@ -23,10 +25,25 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
|
|
|
23
25
|
})
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const columns = Object.keys(config.
|
|
28
|
+
const columns = Object.keys(config.originalFormattedData?.[0] || {})
|
|
27
29
|
return (
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
+
<Accordion allowZeroExpanded={true}>
|
|
31
|
+
<AccordionItem>
|
|
32
|
+
<AccordionItemHeading>
|
|
33
|
+
<AccordionItemButton>Filters</AccordionItemButton>
|
|
34
|
+
</AccordionItemHeading>
|
|
35
|
+
<AccordionItemPanel>
|
|
36
|
+
<VizFilterEditor config={config} updateField={updateField} rawData={config.originalFormattedData} />
|
|
37
|
+
</AccordionItemPanel>
|
|
38
|
+
</AccordionItem>
|
|
39
|
+
<AccordionItem>
|
|
40
|
+
<AccordionItemHeading>
|
|
41
|
+
<AccordionItemButton>Columns</AccordionItemButton>
|
|
42
|
+
</AccordionItemHeading>
|
|
43
|
+
<AccordionItemPanel>
|
|
44
|
+
<ColumnsEditor config={config} updateField={updateField} deleteColumn={deleteColumn} />
|
|
45
|
+
</AccordionItemPanel>
|
|
46
|
+
</AccordionItem>
|
|
30
47
|
<AccordionItem>
|
|
31
48
|
<AccordionItemHeading>
|
|
32
49
|
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
@@ -35,7 +52,7 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
|
|
|
35
52
|
<DataTableEditor config={config} columns={columns} updateField={updateField} isDashboard={true} />
|
|
36
53
|
</AccordionItemPanel>
|
|
37
54
|
</AccordionItem>
|
|
38
|
-
|
|
55
|
+
</Accordion>
|
|
39
56
|
)
|
|
40
57
|
}
|
|
41
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
|
|
@@ -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,8 @@ type ChartRowsProps = DataTableProps & {
|
|
|
15
16
|
hasRowType?: boolean
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale,
|
|
19
|
+
const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
|
|
20
|
+
const groupBy = config.table?.groupBy
|
|
19
21
|
const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
|
|
20
22
|
|
|
21
23
|
const dataSeriesColumnsSorted = () => {
|
|
@@ -57,15 +59,8 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
|
|
|
57
59
|
} else {
|
|
58
60
|
return rows.map(row => {
|
|
59
61
|
if (hasRowType) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
dataSeriesColumns.forEach((column, j) => {
|
|
63
|
-
if (column.match(/row[_-]?type/i)) {
|
|
64
|
-
rowType = getChartCellValue(row, column, config, runtimeData)
|
|
65
|
-
} else {
|
|
66
|
-
rowValues.push(getChartCellValue(row, column, config, runtimeData))
|
|
67
|
-
}
|
|
68
|
-
})
|
|
62
|
+
const rowType = getRowType(runtimeData[row])
|
|
63
|
+
const rowValues = dataSeriesColumns.map(column => getChartCellValue(row, column, config, runtimeData))
|
|
69
64
|
return [rowType, ...rowValues]
|
|
70
65
|
} else {
|
|
71
66
|
return dataSeriesColumns.map((column, j) => getChartCellValue(row, column, config, runtimeData))
|
|
@@ -25,7 +25,7 @@ const isAdditionalColumn = (column, config) => {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const getChartCellValue = (row: string, column: string, config: TableConfig, runtimeData: Object[]) => {
|
|
28
|
-
if (config.
|
|
28
|
+
if (config.visualizationType === 'Sankey' || runtimeData?.[0]?.tableData) return runtimeData[row][column]
|
|
29
29
|
|
|
30
30
|
const rowObj = runtimeData[row]
|
|
31
31
|
let cellValue // placeholder for formatting below
|
|
@@ -54,5 +54,29 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
// suppress cell value
|
|
58
|
+
config.preliminaryData?.forEach(pd => {
|
|
59
|
+
// check entered suppression value against cell value
|
|
60
|
+
const isValueMatch = String(pd.value) === String(labelValue)
|
|
61
|
+
// check entered suppression column against table key
|
|
62
|
+
const isColumnMatch = !pd.column || pd.column === column
|
|
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') {
|
|
67
|
+
switch (config.visualizationType) {
|
|
68
|
+
case 'Combo':
|
|
69
|
+
cellValue = barSeriesExist && showSymbol ? pd.iconCode : lineSeriesExist && showSymbol ? pd.lineCode : ''
|
|
70
|
+
break
|
|
71
|
+
case 'Bar':
|
|
72
|
+
cellValue = !showSymbol ? '' : pd.iconCode
|
|
73
|
+
break
|
|
74
|
+
case 'Line':
|
|
75
|
+
cellValue = !showSymbol ? '' : pd.lineCode
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
const shoMissingDataCellValue = config.general?.showMissingDataLabel && (!labelValue || labelValue === 'null')
|
|
81
|
+
return shoMissingDataCellValue ? 'N/A' : cellValue
|
|
58
82
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { TableConfig } from '../types/TableConfig'
|
|
2
|
+
import _ from 'lodash'
|
|
3
|
+
import { Column } from '../../../types/Column'
|
|
2
4
|
|
|
3
5
|
export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, runtimeData: Object[]): string[] => {
|
|
4
|
-
if (config.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
let tmpSeriesColumns
|
|
6
|
+
if (config.visualizationType === 'Sankey') return Object.keys(config?.data?.[0]?.tableData[0])
|
|
7
|
+
const configColumns = _.cloneDeep(config.columns) || ({} as Record<string, Column>)
|
|
8
|
+
const excludeColumns = Object.values(configColumns)
|
|
9
|
+
.filter(column => column.dataTable === false)
|
|
10
|
+
.map(column => column.name)
|
|
11
|
+
let tmpSeriesColumns: string[] = []
|
|
12
12
|
if (config.visualizationType !== 'Pie') {
|
|
13
13
|
tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey] : [] //, ...config.runtime.seriesLabelsAll
|
|
14
14
|
if (config.series) {
|
|
@@ -22,16 +22,40 @@ export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, r
|
|
|
22
22
|
tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey, config.yAxis?.dataKey] : [config.yAxis?.dataKey]
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const dataColumns = Object.keys(runtimeData[0] || {})
|
|
26
|
+
|
|
25
27
|
// then add the additional Columns
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
Object.values(configColumns).forEach(function (value) {
|
|
29
|
+
if (!value.name) return
|
|
30
|
+
// add if not the index AND it is enabled to be added to data table
|
|
31
|
+
const alreadyAdded = tmpSeriesColumns.includes(value.name)
|
|
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) {
|
|
34
|
+
tmpSeriesColumns.push(value.name)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const columnOrderingHash = Object.values(configColumns).reduce((acc, column) => {
|
|
39
|
+
// subtract 1 to switch from cardinal positioning to index
|
|
40
|
+
if (column.order !== undefined) {
|
|
41
|
+
acc[column.name] = column.order - 1
|
|
42
|
+
}
|
|
43
|
+
return acc
|
|
44
|
+
}, {})
|
|
45
|
+
|
|
46
|
+
tmpSeriesColumns = tmpSeriesColumns.filter(columnName => !excludeColumns.includes(columnName))
|
|
47
|
+
|
|
48
|
+
tmpSeriesColumns.forEach((columnName, index) => {
|
|
49
|
+
if (columnOrderingHash[columnName] === undefined) {
|
|
50
|
+
if (Object.values(columnOrderingHash).includes(index)) {
|
|
51
|
+
// add 1 to place unsorted columns behind sorted columns
|
|
52
|
+
columnOrderingHash[columnName] = index + 1
|
|
53
|
+
} else {
|
|
54
|
+
columnOrderingHash[columnName] = index
|
|
32
55
|
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
56
|
+
}
|
|
57
|
+
})
|
|
35
58
|
|
|
59
|
+
tmpSeriesColumns.sort((a, b) => columnOrderingHash[a] - columnOrderingHash[b])
|
|
36
60
|
return tmpSeriesColumns
|
|
37
61
|
}
|
|
@@ -17,5 +17,6 @@ export const getSeriesName = (column: string, config: TableConfig) => {
|
|
|
17
17
|
}
|
|
18
18
|
if (config.runtimeSeriesLabels && config.runtimeSeriesLabels[column]) return config.runtimeSeriesLabels[column]
|
|
19
19
|
const columnIsDataKey = column === config.xAxis?.dataKey
|
|
20
|
-
|
|
20
|
+
const indexLabel = config.table?.indexLabel
|
|
21
|
+
return columnIsDataKey && indexLabel ? indexLabel : getLabel(column, config)
|
|
21
22
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type RuntimeData = Object[] & Record<string, Object>
|
|
2
2
|
|
|
3
3
|
// removes null and excluded columns
|
|
4
|
-
const
|
|
4
|
+
const removeNullColumns = (runtimeData: Object[] | RuntimeData): RuntimeData => {
|
|
5
5
|
if (!Array.isArray(runtimeData)) {
|
|
6
6
|
// currently we don't support Record types
|
|
7
7
|
return runtimeData
|
|
@@ -10,7 +10,7 @@ const customColumns = (runtimeData: Object[] | RuntimeData, excludeColumns: stri
|
|
|
10
10
|
runtimeData.forEach(row => {
|
|
11
11
|
Object.keys(row).forEach(key => {
|
|
12
12
|
if (runtimeDataMemo[key] === undefined) runtimeDataMemo[key] = null
|
|
13
|
-
if (row[key] !== null
|
|
13
|
+
if (row[key] !== null) runtimeDataMemo[key] = true
|
|
14
14
|
})
|
|
15
15
|
})
|
|
16
16
|
return runtimeData.map(d => {
|
|
@@ -24,4 +24,4 @@ const customColumns = (runtimeData: Object[] | RuntimeData, excludeColumns: stri
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export default
|
|
27
|
+
export default removeNullColumns
|
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
import { Axis } from '
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { Table } from '@cdc/core/types/Table'
|
|
5
|
-
import { Region } from '@cdc/core/types/Region'
|
|
1
|
+
import { Axis } from '../../../types/Axis'
|
|
2
|
+
import { Runtime } from '../../../types/Runtime'
|
|
3
|
+
import { Region } from '../../../types/Region'
|
|
6
4
|
import { BoxPlot } from '../../../types/BoxPlot'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { Legend } from '@cdc/core/types/Legend'
|
|
5
|
+
import { PreliminaryDataItem } from '@cdc/chart/src/components/LineChart/LineChartProps'
|
|
6
|
+
import { Visualization } from '../../../types/Visualization'
|
|
10
7
|
|
|
11
|
-
export type TableConfig = {
|
|
12
|
-
type?: string
|
|
13
|
-
table: Table
|
|
14
|
-
xAxis?: Axis
|
|
15
|
-
yAxis?: Axis
|
|
8
|
+
export type TableConfig = Visualization & {
|
|
16
9
|
boxplot?: BoxPlot
|
|
17
|
-
|
|
18
|
-
general?: General
|
|
19
|
-
columns?: Record<string, Column>
|
|
20
|
-
legend?: Legend
|
|
21
|
-
series?: Series
|
|
10
|
+
fontSize: 'small' | 'medium' | 'large'
|
|
22
11
|
regions?: Region[]
|
|
23
|
-
runtimeSeriesLabels?: Object
|
|
24
|
-
dataFormat?: Object
|
|
25
12
|
runtime?: Runtime
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
runtimeSeriesLabels?: Object
|
|
14
|
+
xAxis?: Axis
|
|
15
|
+
yAxis?: Axis
|
|
16
|
+
preliminaryData: PreliminaryDataItem[]
|
|
17
|
+
brush: { active: boolean }
|
|
28
18
|
}
|