@cdc/core 4.24.2 → 4.24.3
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/assets/icon-sankey.svg +1 -0
- package/assets/icon-table.svg +1 -0
- package/components/DataTable/DataTable.tsx +28 -10
- package/components/DataTable/DataTableStandAlone.tsx +15 -0
- package/components/DataTable/components/CellAnchor.tsx +3 -1
- package/components/DataTable/components/ChartHeader.tsx +48 -12
- package/components/DataTable/components/DataTableEditorPanel.tsx +42 -0
- package/components/DataTable/components/MapHeader.tsx +10 -5
- package/components/DataTable/helpers/customColumns.ts +4 -2
- package/components/DataTable/helpers/getChartCellValue.ts +4 -2
- package/components/DataTable/helpers/getDataSeriesColumns.ts +9 -1
- package/components/DataTable/types/TableConfig.ts +6 -7
- package/components/EditorPanel/ColumnsEditor.tsx +311 -0
- package/components/EditorPanel/DataTableEditor.tsx +27 -28
- package/components/Filters.jsx +27 -10
- package/components/MultiSelect/MultiSelect.tsx +39 -20
- package/components/MultiSelect/multiselect.styles.css +44 -27
- package/components/NestedDropdown/NestedDropdown.tsx +257 -0
- package/components/NestedDropdown/index.ts +1 -0
- package/components/NestedDropdown/nesteddropdown.styles.css +70 -0
- package/components/Table/Table.tsx +1 -1
- package/components/_stories/MultiSelect.stories.tsx +10 -1
- package/components/_stories/NestedDropdown.stories.tsx +58 -0
- package/components/createBarElement.jsx +117 -0
- package/components/elements/ScreenReaderText.tsx +8 -0
- package/components/elements/SkipTo.tsx +14 -0
- package/components/ui/Icon.tsx +5 -1
- package/components/ui/Title/Title.scss +7 -1
- package/components/ui/Title/index.tsx +3 -3
- package/components/ui/Tooltip.jsx +1 -1
- package/data/colorPalettes.js +1 -6
- package/helpers/cove/accessibility.ts +23 -0
- package/helpers/cove/date.ts +19 -0
- package/helpers/coveUpdateWorker.js +4 -0
- package/helpers/isDomainExternal.js +14 -0
- package/helpers/queryStringUtils.js +26 -0
- package/helpers/tests/updateFieldFactory.test.ts +89 -0
- package/helpers/updateFieldFactory.ts +38 -0
- package/helpers/useDataVizClasses.js +2 -2
- package/helpers/ver/4.24.3.js +25 -0
- package/package.json +4 -3
- package/styles/_data-table.scss +0 -13
- package/styles/base.scss +8 -0
- package/types/Axis.ts +1 -0
- package/types/BaseVisualizationType.ts +1 -0
- package/types/ConfidenceInterval.ts +1 -0
- package/types/Legend.ts +18 -0
- package/types/Region.ts +10 -0
- package/types/Runtime.ts +2 -0
- package/types/Table.ts +1 -1
- package/types/UpdateFieldFunc.ts +1 -1
- package/types/Visualization.ts +17 -5
- package/components/DataTable/components/SkipNav.tsx +0 -7
- package/helpers/cove/date.js +0 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><defs><style>.c{stroke-linecap:round;stroke-width:3px;}.c,.d,.e{stroke:#000;}.e{fill:none;}</style></defs><g id="a"/><g id="b"><path class="c" d="M6.88,39.98V5.57"/><path class="c" d="M89.42,22.98V5.57"/><path class="c" d="M89.42,52.79V27.38"/><path class="c" d="M89.42,76v-18.41"/><path class="c" d="M6.88,76v-28.43"/><rect class="e" x="12.3" y="5.98" width="71.62" height="9.69"/><path class="d" d="M83.92,16.67s-11.46,1.07-27.63,11.61-27.1,16.32-35.66,17.97c-4.59,.88-8.34,.72-8.34,.72v5.31s3.75,.17,8.34-.72c8.56-1.65,19.49-7.42,35.66-17.97s27.63-11.61,27.63-11.61v-5.31Z"/><path class="e" d="M12.3,16.67s41.36,10.71,71.62,10.71v8.71s-31.58,1.88-71.62-10.71v-8.71Z"/><path class="d" d="M83.92,53.29s-13.18,1.49-35.77,9.06c0,0-18.05,7.37-35.85,7.37v-15.89c17.8,0,35.85-7.37,35.85-7.37,22.59-7.57,35.77-9.06,35.77-9.06v15.89Z"/><rect class="d" x="12.3" y="71" width="71.62" height="5.44"/><path class="e" d="M83.92,57.58l-8.82-2.38c-6.78-1.93-13.27-4.73-19.33-8.34l-14.69-8.76c-5.94-3.54-12.3-6.31-18.93-8.25l-9.85-2.88v12.14l9.85,2.88c6.64,1.94,13,4.71,18.93,8.25l14.69,8.76c6.05,3.61,12.55,6.41,19.33,8.34l8.82,2.38v-12.14Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 256V160H224v96H64zm0 64H224v96H64V320zm224 96V320H448v96H288zM448 256H288V160H448v96zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z"/></svg>
|
|
@@ -9,7 +9,7 @@ import { customSort } from './helpers/customSort'
|
|
|
9
9
|
import ChartHeader from './components/ChartHeader'
|
|
10
10
|
import BoxplotHeader from './components/BoxplotHeader'
|
|
11
11
|
import MapHeader from './components/MapHeader'
|
|
12
|
-
import
|
|
12
|
+
import SkipTo from '../elements/SkipTo'
|
|
13
13
|
import ExpandCollapse from './components/ExpandCollapse'
|
|
14
14
|
import mapCellMatrix from './helpers/mapCellMatrix'
|
|
15
15
|
import Table from '../Table'
|
|
@@ -39,7 +39,7 @@ export type DataTableProps = {
|
|
|
39
39
|
navigationHandler?: Function
|
|
40
40
|
outerContainerRef?: Function
|
|
41
41
|
rawData: Object[]
|
|
42
|
-
runtimeData: Object[]
|
|
42
|
+
runtimeData: Object[] & Record<string, Object>
|
|
43
43
|
setFilteredCountryCode?: Function // used for Maps only
|
|
44
44
|
showDownloadButton?: boolean
|
|
45
45
|
tabbingId: string
|
|
@@ -53,7 +53,6 @@ export type DataTableProps = {
|
|
|
53
53
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
54
54
|
const DataTable = (props: DataTableProps) => {
|
|
55
55
|
const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
|
|
56
|
-
|
|
57
56
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
58
57
|
|
|
59
58
|
const [sortBy, setSortBy] = useState<any>({ column: config.type === 'map' ? 'geo' : 'date', asc: false, colIndex: null })
|
|
@@ -91,7 +90,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
91
90
|
case 'Box Plot':
|
|
92
91
|
if (!config.boxplot) return <Loading />
|
|
93
92
|
break
|
|
94
|
-
case 'Line' || 'Bar' || 'Combo' || 'Pie' || 'Deviation Bar' || 'Paired Bar':
|
|
93
|
+
case 'Line' || 'Bar' || 'Combo' || 'Pie' || 'Deviation Bar' || 'Paired Bar' || 'Sankey' || 'Table':
|
|
95
94
|
if (!runtimeData) return <Loading />
|
|
96
95
|
break
|
|
97
96
|
default:
|
|
@@ -115,7 +114,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
115
114
|
dataA = _runtimeData[a][sortBy.column]
|
|
116
115
|
dataB = _runtimeData[b][sortBy.column]
|
|
117
116
|
}
|
|
118
|
-
if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date'
|
|
117
|
+
if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
|
|
119
118
|
dataA = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[a][config.xAxis.dataKey])
|
|
120
119
|
dataB = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[b][config.xAxis.dataKey])
|
|
121
120
|
}
|
|
@@ -142,12 +141,25 @@ const DataTable = (props: DataTableProps) => {
|
|
|
142
141
|
// If a relative region is found we don't want to display the data table.
|
|
143
142
|
// Takes backwards compatibility into consideration, ie !region.toType || !region.fromType
|
|
144
143
|
const noRelativeRegions = config?.regions?.every(region => {
|
|
145
|
-
|
|
144
|
+
const toTypeFixed = region.toType === 'Fixed'
|
|
145
|
+
const fromTypeFixed = region.fromType === 'Fixed'
|
|
146
|
+
const toIsNotSet = !region.toType
|
|
147
|
+
const fromIsNotSet = !region.fromType
|
|
148
|
+
const BothFixed = toTypeFixed && fromTypeFixed
|
|
149
|
+
const NeitherFixed = toIsNotSet && fromIsNotSet
|
|
150
|
+
const ToFixedFromNotSet = toTypeFixed && fromIsNotSet
|
|
151
|
+
const FromFixedToNotSet = fromTypeFixed && toIsNotSet
|
|
152
|
+
|
|
153
|
+
return BothFixed || NeitherFixed || ToFixedFromNotSet || FromFixedToNotSet
|
|
146
154
|
})
|
|
147
155
|
|
|
148
156
|
// prettier-ignore
|
|
149
157
|
const tableData = useMemo(() => (
|
|
150
|
-
|
|
158
|
+
config.data?.[0]?.tableData
|
|
159
|
+
? config.data?.[0]?.tableData
|
|
160
|
+
: config.visualizationType === 'Sankey'
|
|
161
|
+
? config.data?.[0]?.tableData
|
|
162
|
+
: config.visualizationType === 'Pie'
|
|
151
163
|
? [config.yAxis.dataKey]
|
|
152
164
|
: config.visualizationType === 'Box Plot'
|
|
153
165
|
? Object.entries(config.boxplot.tableData[0])
|
|
@@ -169,10 +181,10 @@ const DataTable = (props: DataTableProps) => {
|
|
|
169
181
|
<ErrorBoundary component='DataTable'>
|
|
170
182
|
<MediaControls.Section classes={['download-links']}>
|
|
171
183
|
<MediaControls.Link config={config} dashboardDataConfig={dataConfig} />
|
|
172
|
-
{(config.table.download || config.general?.showDownloadButton) && <DownloadButton rawData={getDownloadData()} fileName={`${vizTitle || 'data-table'}.csv`} headerColor={headerColor}
|
|
184
|
+
{(config.table.download || config.general?.showDownloadButton) && <DownloadButton rawData={getDownloadData()} fileName={`${vizTitle || 'data-table'}.csv`} headerColor={headerColor} />}
|
|
173
185
|
</MediaControls.Section>
|
|
174
186
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
175
|
-
<
|
|
187
|
+
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
176
188
|
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
|
|
177
189
|
<div className='table-container' style={limitHeight}>
|
|
178
190
|
<Table
|
|
@@ -205,6 +217,9 @@ const DataTable = (props: DataTableProps) => {
|
|
|
205
217
|
)}
|
|
206
218
|
</div>
|
|
207
219
|
</section>
|
|
220
|
+
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
221
|
+
Skipped data table.
|
|
222
|
+
</div>
|
|
208
223
|
</ErrorBoundary>
|
|
209
224
|
)
|
|
210
225
|
} else {
|
|
@@ -212,7 +227,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
212
227
|
return (
|
|
213
228
|
<ErrorBoundary component='DataTable'>
|
|
214
229
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
215
|
-
<
|
|
230
|
+
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
216
231
|
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
|
|
217
232
|
<div className='table-container' style={limitHeight}>
|
|
218
233
|
<Table
|
|
@@ -226,6 +241,9 @@ const DataTable = (props: DataTableProps) => {
|
|
|
226
241
|
/>
|
|
227
242
|
</div>
|
|
228
243
|
</section>
|
|
244
|
+
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
245
|
+
Skipped data table.
|
|
246
|
+
</div>
|
|
229
247
|
</ErrorBoundary>
|
|
230
248
|
)
|
|
231
249
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ViewPort } from '../../types/ViewPort'
|
|
2
|
+
import { Visualization } from '../../types/Visualization'
|
|
3
|
+
import DataTable from './DataTable'
|
|
4
|
+
|
|
5
|
+
type StandAloneProps = {
|
|
6
|
+
visualizationKey: string
|
|
7
|
+
config: Visualization
|
|
8
|
+
viewport?: ViewPort
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const DataTableStandAlone: React.FC<StandAloneProps> = ({ visualizationKey, config, viewport }) => {
|
|
12
|
+
return <DataTable expandDataTable={true} config={config} rawData={config.data} runtimeData={config.formattedData} tabbingId={visualizationKey} tableTitle={config.table.label} viewport={viewport || 'lg'} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default DataTableStandAlone
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import ExternalIcon from '@cdc/core/assets/external-link.svg'
|
|
2
|
+
import isDomainExternal from '@cdc/core/helpers/isDomainExternal'
|
|
3
|
+
|
|
2
4
|
// Optionally wrap cell with anchor if config defines a navigation url
|
|
3
5
|
const CellAnchor = ({ markup, row, columns, navigationHandler, mapZoomHandler }) => {
|
|
4
6
|
if (columns.navigate && row[columns.navigate.name]) {
|
|
@@ -16,7 +18,7 @@ const CellAnchor = ({ markup, row, columns, navigationHandler, mapZoomHandler })
|
|
|
16
18
|
}}
|
|
17
19
|
>
|
|
18
20
|
{markup}
|
|
19
|
-
<ExternalIcon className='inline-icon' />
|
|
21
|
+
{isDomainExternal(row[columns.navigate.name]) && <ExternalIcon className='inline-icon' />}
|
|
20
22
|
</span>
|
|
21
23
|
)
|
|
22
24
|
} else if (mapZoomHandler) {
|
|
@@ -2,6 +2,7 @@ import { getChartCellValue } from '../helpers/getChartCellValue'
|
|
|
2
2
|
import { getSeriesName } from '../helpers/getSeriesName'
|
|
3
3
|
import { getDataSeriesColumns } from '../helpers/getDataSeriesColumns'
|
|
4
4
|
import { DownIcon, UpIcon } from './Icons'
|
|
5
|
+
import ScreenReaderText from '@cdc/core/components/elements/ScreenReaderText'
|
|
5
6
|
|
|
6
7
|
type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy; groupBy?; hasRowType? }
|
|
7
8
|
|
|
@@ -16,6 +17,45 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, has
|
|
|
16
17
|
dataSeriesColumns = groupHeaderRemoved
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
|
|
21
|
+
const handleHeaderClasses = (sortBy, text) => {
|
|
22
|
+
let classes = ['sort']
|
|
23
|
+
if (sortBy.column === text && sortBy.asc) {
|
|
24
|
+
classes.push('sort-asc')
|
|
25
|
+
}
|
|
26
|
+
if (sortBy.column === text && sortBy.desc) {
|
|
27
|
+
classes.push('sort-desc')
|
|
28
|
+
}
|
|
29
|
+
return classes.join(' ')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const ScreenReaderSortByText = ({ text, config, sortBy }) => {
|
|
33
|
+
const notApplicableText = 'Not Applicable'
|
|
34
|
+
let columnHeaderText = `${text} `
|
|
35
|
+
if (text !== '__series__' || text !== '') {
|
|
36
|
+
columnHeaderText = `${text} `
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ((text === '__series__' || text === '') && !config.table.indexLabel) {
|
|
40
|
+
columnHeaderText = notApplicableText
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ((text === '__series__' || text === '') && config.table.indexLabel) {
|
|
44
|
+
columnHeaderText = config.table.indexLabel
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (columnHeaderText === notApplicableText) return
|
|
48
|
+
|
|
49
|
+
return <span className='cdcdataviz-sr-only'>{`Press command, modifier, or enter key to sort by ${columnHeaderText} in ${sortBy.column !== columnHeaderText ? 'ascending' : sortBy.column === 'desc' ? 'descending' : 'ascending'} order`}</span>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ColumnHeadingText = ({ column, text, config }) => {
|
|
53
|
+
let notApplicableText = 'Not Applicable'
|
|
54
|
+
if (text === '__series__' && config.table.indexLabel) return `${config.table.indexLabel} `
|
|
55
|
+
if (text === '__series__' && !config.table.indexLabel) return <ScreenReaderText as='span'>{notApplicableText}</ScreenReaderText>
|
|
56
|
+
return text
|
|
57
|
+
}
|
|
58
|
+
|
|
19
59
|
if (isVertical) {
|
|
20
60
|
if (hasRowType) {
|
|
21
61
|
// find the row type column and place it at the beginning of the array
|
|
@@ -25,6 +65,7 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, has
|
|
|
25
65
|
dataSeriesColumns.splice(rowTypeIndex, 1)
|
|
26
66
|
}
|
|
27
67
|
}
|
|
68
|
+
|
|
28
69
|
return (
|
|
29
70
|
<tr>
|
|
30
71
|
{dataSeriesColumns.map((column, index) => {
|
|
@@ -35,7 +76,6 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, has
|
|
|
35
76
|
style={{ minWidth: (config.table.cellMinWidth || 0) + 'px' }}
|
|
36
77
|
key={`col-header-${column}__${index}`}
|
|
37
78
|
tabIndex={0}
|
|
38
|
-
title={text}
|
|
39
79
|
role='columnheader'
|
|
40
80
|
scope='col'
|
|
41
81
|
onClick={() => {
|
|
@@ -48,14 +88,12 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, has
|
|
|
48
88
|
setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false, colIndex: index })
|
|
49
89
|
}
|
|
50
90
|
}}
|
|
51
|
-
className={
|
|
91
|
+
className={handleHeaderClasses(sortBy, text)}
|
|
52
92
|
{...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
|
|
53
93
|
>
|
|
54
|
-
{text}
|
|
94
|
+
<ColumnHeadingText text={text} column={column} config={config} />
|
|
55
95
|
{column === sortBy.column && <span className={'sort-icon'}>{!sortBy.asc ? <UpIcon /> : <DownIcon />}</span>}
|
|
56
|
-
<
|
|
57
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
|
|
58
|
-
</button>
|
|
96
|
+
<ScreenReaderSortByText sortBy={sortBy} config={config} text={text} />
|
|
59
97
|
</th>
|
|
60
98
|
)
|
|
61
99
|
})}
|
|
@@ -68,12 +106,12 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, has
|
|
|
68
106
|
{['__series__', ...Object.keys(data)].slice(sliceVal).map((row, index) => {
|
|
69
107
|
let column = config.xAxis?.dataKey
|
|
70
108
|
let text = row !== '__series__' ? getChartCellValue(row, column, config, data) : '__series__'
|
|
109
|
+
|
|
71
110
|
return (
|
|
72
111
|
<th
|
|
73
112
|
style={{ minWidth: (config.table.cellMinWidth || 0) + 'px' }}
|
|
74
113
|
key={`col-header-${text}__${index}`}
|
|
75
114
|
tabIndex={0}
|
|
76
|
-
title={text}
|
|
77
115
|
role='columnheader'
|
|
78
116
|
scope='col'
|
|
79
117
|
onClick={() => {
|
|
@@ -84,14 +122,12 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, has
|
|
|
84
122
|
setSortBy({ column: text, asc: sortBy.column === text ? !sortBy.asc : false, colIndex: index })
|
|
85
123
|
}
|
|
86
124
|
}}
|
|
87
|
-
className={sortBy
|
|
125
|
+
className={handleHeaderClasses(sortBy, text)}
|
|
88
126
|
{...(sortBy.column === text ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
|
|
89
127
|
>
|
|
90
|
-
{text
|
|
128
|
+
<ColumnHeadingText text={text} column={column} config={config} />
|
|
91
129
|
{index === sortBy.colIndex && <span className={'sort-icon'}>{!sortBy.asc ? <UpIcon /> : <DownIcon />}</span>}
|
|
92
|
-
<
|
|
93
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === text ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
|
|
94
|
-
</button>
|
|
130
|
+
<ScreenReaderSortByText text={text} config={config} sortBy={sortBy} />
|
|
95
131
|
</th>
|
|
96
132
|
)
|
|
97
133
|
})}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AccordionItem, AccordionItemButton, AccordionItemHeading, AccordionItemPanel } from 'react-accessible-accordion'
|
|
2
|
+
import DataTableEditor from '../../EditorPanel/DataTableEditor'
|
|
3
|
+
import { Visualization } from '@cdc/core/types/Visualization'
|
|
4
|
+
import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
|
|
5
|
+
import { useMemo } from 'react'
|
|
6
|
+
import ColumnsEditor from '../../EditorPanel/ColumnsEditor'
|
|
7
|
+
|
|
8
|
+
type DataTableEditorProps = {
|
|
9
|
+
config: Visualization
|
|
10
|
+
updateConfig: Function
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateConfig }) => {
|
|
14
|
+
const updateField = useMemo(() => updateFieldFactory(config, updateConfig), [JSON.stringify(config)])
|
|
15
|
+
const deleteColumn = columnName => {
|
|
16
|
+
const newColumns = config.columns
|
|
17
|
+
|
|
18
|
+
delete newColumns[columnName]
|
|
19
|
+
|
|
20
|
+
updateConfig({
|
|
21
|
+
...config,
|
|
22
|
+
columns: newColumns
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const columns = Object.keys(config.columns || {})
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<ColumnsEditor config={config} updateField={updateField} deleteColumn={deleteColumn} />
|
|
30
|
+
<AccordionItem>
|
|
31
|
+
<AccordionItemHeading>
|
|
32
|
+
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
33
|
+
</AccordionItemHeading>
|
|
34
|
+
<AccordionItemPanel>
|
|
35
|
+
<DataTableEditor config={config} columns={columns} updateField={updateField} isDashboard={true} />
|
|
36
|
+
</AccordionItemPanel>
|
|
37
|
+
</AccordionItem>
|
|
38
|
+
</>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default DataTableEditorPanel
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { DataTableProps } from '../DataTable'
|
|
2
2
|
import { DownIcon, UpIcon } from './Icons'
|
|
3
|
+
import ScreenReaderText from '../../elements/ScreenReaderText'
|
|
3
4
|
|
|
4
5
|
type MapHeaderProps = DataTableProps & {
|
|
5
6
|
sortBy: { column; asc }
|
|
6
7
|
setSortBy: Function
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
const ColumnHeadingText = ({ column, text, config }) => {
|
|
11
|
+
let notApplicableText = 'Not Applicable'
|
|
12
|
+
if (text === '__series__' && config.table.indexLabel) return `${config.table.indexLabel} `
|
|
13
|
+
if (text === '__series__' && !config.table.indexLabel) return <ScreenReaderText as='span'>{notApplicableText}</ScreenReaderText>
|
|
14
|
+
return text
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
const MapHeader = ({ columns, config, indexTitle, sortBy, setSortBy }: MapHeaderProps) => {
|
|
10
18
|
return (
|
|
11
19
|
<tr>
|
|
@@ -27,7 +35,6 @@ const MapHeader = ({ columns, config, indexTitle, sortBy, setSortBy }: MapHeader
|
|
|
27
35
|
key={`col-header-${column}__${index}`}
|
|
28
36
|
id={column}
|
|
29
37
|
tabIndex={0}
|
|
30
|
-
title={text}
|
|
31
38
|
role='columnheader'
|
|
32
39
|
scope='col'
|
|
33
40
|
onClick={() => {
|
|
@@ -41,11 +48,9 @@ const MapHeader = ({ columns, config, indexTitle, sortBy, setSortBy }: MapHeader
|
|
|
41
48
|
className={sortBy.column === column ? (sortBy.asc ? 'sort sort-asc' : 'sort sort-desc') : 'sort'}
|
|
42
49
|
{...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
|
|
43
50
|
>
|
|
44
|
-
{text}
|
|
51
|
+
<ColumnHeadingText text={text} config={config} column={column} />
|
|
45
52
|
{sortBy.column === column && <span className={'sort-icon'}>{!sortBy.asc ? <UpIcon /> : <DownIcon />}</span>}
|
|
46
|
-
<
|
|
47
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
|
|
48
|
-
</button>
|
|
53
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} order`}</span>
|
|
49
54
|
</th>
|
|
50
55
|
)
|
|
51
56
|
})}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
type RuntimeData = Object[] & Record<string, Object>
|
|
2
|
+
|
|
1
3
|
// removes null and excluded columns
|
|
2
|
-
const customColumns = (runtimeData: Object[] |
|
|
4
|
+
const customColumns = (runtimeData: Object[] | RuntimeData, excludeColumns: string[] = []): RuntimeData => {
|
|
3
5
|
if (!Array.isArray(runtimeData)) {
|
|
4
6
|
// currently we don't support Record types
|
|
5
7
|
return runtimeData
|
|
@@ -18,7 +20,7 @@ const customColumns = (runtimeData: Object[] | Record<string, Object>, excludeCo
|
|
|
18
20
|
if (runtimeDataMemo[key] === true) row[key] = d[key]
|
|
19
21
|
})
|
|
20
22
|
return row
|
|
21
|
-
})
|
|
23
|
+
}) as RuntimeData
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseDate, formatDate } from '@cdc/core/helpers/cove/date'
|
|
2
2
|
import { formatNumber } from '@cdc/core/helpers/cove/number'
|
|
3
|
+
import { TableConfig } from '../types/TableConfig'
|
|
3
4
|
|
|
4
5
|
// if its additional column, return formatting params
|
|
5
6
|
const isAdditionalColumn = (column, config) => {
|
|
@@ -23,8 +24,9 @@ const isAdditionalColumn = (column, config) => {
|
|
|
23
24
|
return formattingParams
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
export const getChartCellValue = (row, column, config, runtimeData) => {
|
|
27
|
-
if (config.table.customTableConfig) return runtimeData[row][column]
|
|
27
|
+
export const getChartCellValue = (row: string, column: string, config: TableConfig, runtimeData: Object[]) => {
|
|
28
|
+
if (config.table.customTableConfig || config.visualizationType === 'Sankey' || runtimeData?.[0]?.tableData) return runtimeData[row][column]
|
|
29
|
+
|
|
28
30
|
const rowObj = runtimeData[row]
|
|
29
31
|
let cellValue // placeholder for formatting below
|
|
30
32
|
let labelValue = rowObj[column] // just raw X axis string
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { TableConfig } from '../types/TableConfig'
|
|
2
|
+
|
|
3
|
+
export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, runtimeData: Object[]): string[] => {
|
|
2
4
|
if (config.table.customTableConfig) return runtimeData[0] ? Object.keys(runtimeData[0]) : []
|
|
5
|
+
if (config.type === 'table') {
|
|
6
|
+
const excludeColumns = Object.values(config.columns)
|
|
7
|
+
.filter(column => column.dataTable === false)
|
|
8
|
+
.map(column => column.name)
|
|
9
|
+
return Object.keys(runtimeData[0]).filter(columnName => !excludeColumns.includes(columnName))
|
|
10
|
+
}
|
|
3
11
|
let tmpSeriesColumns
|
|
4
12
|
if (config.visualizationType !== 'Pie') {
|
|
5
13
|
tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey] : [] //, ...config.runtime.seriesLabelsAll
|
|
@@ -2,9 +2,11 @@ import { Axis } from '@cdc/core/types/Axis'
|
|
|
2
2
|
import { Series } from '@cdc/core/types/Series'
|
|
3
3
|
import { Runtime } from '@cdc/core/types/Runtime'
|
|
4
4
|
import { Table } from '@cdc/core/types/Table'
|
|
5
|
+
import { Region } from '@cdc/core/types/Region'
|
|
5
6
|
import { BoxPlot } from '../../../types/BoxPlot'
|
|
6
7
|
import { General } from '../../../types/General'
|
|
7
8
|
import { Column } from '../../../types/Column'
|
|
9
|
+
import { Legend } from '@cdc/core/types/Legend'
|
|
8
10
|
|
|
9
11
|
export type TableConfig = {
|
|
10
12
|
type?: string
|
|
@@ -12,17 +14,14 @@ export type TableConfig = {
|
|
|
12
14
|
xAxis?: Axis
|
|
13
15
|
yAxis?: Axis
|
|
14
16
|
boxplot?: BoxPlot
|
|
15
|
-
visualizationType
|
|
17
|
+
visualizationType: string
|
|
16
18
|
general?: General
|
|
17
19
|
columns?: Record<string, Column>
|
|
18
|
-
legend?:
|
|
19
|
-
specialClasses?: { key: string; label: string; value: string }[]
|
|
20
|
-
hide?: boolean
|
|
21
|
-
}
|
|
20
|
+
legend?: Legend
|
|
22
21
|
series?: Series
|
|
23
|
-
regions?:
|
|
22
|
+
regions?: Region[]
|
|
24
23
|
runtimeSeriesLabels?: Object
|
|
25
24
|
dataFormat?: Object
|
|
26
|
-
runtime
|
|
25
|
+
runtime?: Runtime
|
|
27
26
|
data: Object[]
|
|
28
27
|
}
|