@cdc/core 4.25.11 → 4.26.1
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/_stories/Gallery.Charts.stories.tsx +307 -0
- package/_stories/Gallery.DataBite.stories.tsx +72 -0
- package/_stories/Gallery.Maps.stories.tsx +230 -0
- package/_stories/Gallery.WaffleChart.stories.tsx +187 -0
- package/_stories/PageART.stories.tsx +192 -0
- package/_stories/PageBRFSS.stories.tsx +289 -0
- package/_stories/PageCancerRegistries.stories.tsx +199 -0
- package/_stories/PageEasternEquineEncephalitis.stories.tsx +202 -0
- package/_stories/PageExcessiveAlcoholUse.stories.tsx +196 -0
- package/_stories/PageMaternalMortality.stories.tsx +192 -0
- package/_stories/PageOralHealth.stories.tsx +196 -0
- package/_stories/PageRespiratory.stories.tsx +332 -0
- package/_stories/PageSmokingTobacco.stories.tsx +195 -0
- package/_stories/PageStateDiabetesProfiles.stories.tsx +196 -0
- package/_stories/PageWastewater.stories.tsx +463 -0
- package/assets/icon-magnifying-glass.svg +5 -0
- package/assets/icon-warming-stripes.svg +13 -0
- package/components/AdvancedEditor/AdvancedEditor.tsx +4 -0
- package/components/AdvancedEditor/EmbedEditor.tsx +281 -0
- package/components/ComboBox/ComboBox.tsx +345 -0
- package/components/ComboBox/combobox.styles.css +185 -0
- package/components/ComboBox/index.ts +1 -0
- package/components/DataTable/DataTable.tsx +132 -58
- package/components/DataTable/data-table.css +216 -215
- package/components/DataTable/helpers/mapCellMatrix.tsx +14 -6
- package/components/EditorPanel/ColumnsEditor.tsx +37 -19
- package/components/EditorPanel/DataTableEditor.tsx +51 -25
- package/components/EditorPanel/EditorPanel.styles.css +16 -0
- package/components/EditorPanel/EditorPanel.tsx +144 -0
- package/components/EditorPanel/EditorPanelDispatch.tsx +75 -0
- package/components/EditorPanel/FieldSetWrapper.tsx +66 -23
- package/components/EditorPanel/Inputs.tsx +33 -7
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +236 -175
- package/components/EditorPanel/sections/VisualSection.tsx +169 -0
- package/components/Filters/Filters.tsx +31 -5
- package/components/Filters/helpers/getNestedOptions.ts +2 -1
- package/components/Filters/helpers/handleSorting.ts +1 -1
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +82 -0
- package/components/Layout/components/Visualization/index.tsx +16 -1
- package/components/Layout/components/Visualization/visualizations.scss +7 -0
- package/components/Legend/Legend.Gradient.tsx +1 -1
- package/components/MediaControls.tsx +53 -27
- package/components/ui/Icon.tsx +3 -1
- package/components/ui/Title/index.tsx +30 -2
- package/components/ui/Title/title.styles.css +42 -0
- package/dist/cove-main.css +26 -3
- package/dist/cove-main.css.map +1 -1
- package/generateViteConfig.js +8 -1
- package/helpers/addValuesToFilters.ts +6 -1
- package/helpers/coveUpdateWorker.ts +19 -12
- package/helpers/embedCodeGenerator.ts +109 -0
- package/helpers/getUniqueValues.ts +19 -0
- package/helpers/hashObj.ts +25 -0
- package/helpers/isRightAlignedTableValue.js +5 -0
- package/helpers/metrics/helpers.ts +1 -0
- package/helpers/pivotData.ts +2 -2
- package/helpers/prepareScreenshot.ts +268 -0
- package/helpers/queryStringUtils.ts +29 -0
- package/helpers/tests/prepareScreenshot.test.ts +414 -0
- package/helpers/tests/queryStringUtils.test.ts +381 -0
- package/helpers/tests/testStandaloneBuild.ts +23 -5
- package/helpers/useDataVizClasses.ts +0 -1
- package/helpers/ver/4.26.1.ts +80 -0
- package/hooks/useDataColumns.ts +63 -0
- package/hooks/useFilterManagement.ts +94 -0
- package/hooks/useLegendSeparators.ts +26 -0
- package/hooks/useListManagement.ts +192 -0
- package/package.json +4 -3
- package/styles/_button-section.scss +0 -3
- package/types/Axis.ts +1 -0
- package/types/ForecastingSeriesKey.ts +1 -0
- package/types/MarkupInclude.ts +1 -0
- package/types/Series.ts +3 -0
- package/types/Table.ts +1 -0
- package/types/Visualization.ts +1 -0
- package/types/VizFilter.ts +1 -0
- package/LICENSE +0 -201
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
.cdc-open-viz-module {
|
|
2
|
+
.cove-combobox {
|
|
3
|
+
position: relative;
|
|
4
|
+
width: 100%;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.cove-combobox-wrapper {
|
|
8
|
+
position: relative;
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
width: 100%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
input.cove-combobox-input[role='combobox'] {
|
|
15
|
+
width: 100%;
|
|
16
|
+
display: block;
|
|
17
|
+
padding: 0.5rem 3rem 0.5rem 0.5rem;
|
|
18
|
+
font-family: var(--app-font-secondary);
|
|
19
|
+
font-weight: 300;
|
|
20
|
+
font-size: 0.833rem;
|
|
21
|
+
line-height: normal;
|
|
22
|
+
color: var(--cool-gray-90);
|
|
23
|
+
background-color: white;
|
|
24
|
+
background-clip: padding-box;
|
|
25
|
+
border: 1px solid var(--cool-gray-10) !important;
|
|
26
|
+
border-radius: 0.333rem;
|
|
27
|
+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
28
|
+
appearance: none;
|
|
29
|
+
-webkit-appearance: none;
|
|
30
|
+
-moz-appearance: none;
|
|
31
|
+
cursor: text;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
input.cove-combobox-input[role='combobox']:focus {
|
|
35
|
+
color: var(--cool-gray-90);
|
|
36
|
+
background-color: white;
|
|
37
|
+
border: 1px solid var(--cool-gray-10) !important;
|
|
38
|
+
box-shadow: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
input.cove-combobox-input[role='combobox']:focus-visible {
|
|
42
|
+
outline: dashed 2px rgb(0, 122, 153) !important;
|
|
43
|
+
outline-offset: 3px !important;
|
|
44
|
+
border-radius: 6px !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
input.cove-combobox-input[role='combobox']:disabled {
|
|
48
|
+
background-color: var(--lightestGray);
|
|
49
|
+
color: var(--cool-gray-90);
|
|
50
|
+
opacity: 1;
|
|
51
|
+
cursor: default;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
input.cove-combobox-input[role='combobox']:disabled::placeholder {
|
|
55
|
+
color: var(--cool-gray-90);
|
|
56
|
+
opacity: 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
input.cove-combobox-input[role='combobox']::placeholder {
|
|
60
|
+
color: #6c757d;
|
|
61
|
+
opacity: 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.cove-combobox-button {
|
|
65
|
+
position: absolute;
|
|
66
|
+
right: 0.5rem;
|
|
67
|
+
top: 50%;
|
|
68
|
+
transform: translateY(-50%);
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
width: 1em;
|
|
73
|
+
height: 1em;
|
|
74
|
+
background: transparent;
|
|
75
|
+
border: none;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
padding: 0;
|
|
78
|
+
z-index: 2;
|
|
79
|
+
pointer-events: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.cove-combobox-button:disabled {
|
|
83
|
+
cursor: default;
|
|
84
|
+
opacity: 1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.cove-combobox-button:disabled svg {
|
|
88
|
+
color: var(--cool-gray-90);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.cove-combobox-button svg {
|
|
92
|
+
color: currentColor;
|
|
93
|
+
font-size: 0.8em;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.cove-combobox-listbox {
|
|
97
|
+
position: absolute;
|
|
98
|
+
top: calc(100% + 0.125rem);
|
|
99
|
+
left: 0;
|
|
100
|
+
right: auto;
|
|
101
|
+
width: 150%;
|
|
102
|
+
z-index: 1000;
|
|
103
|
+
max-height: 20rem;
|
|
104
|
+
margin: 0;
|
|
105
|
+
padding: 0;
|
|
106
|
+
overflow-y: auto;
|
|
107
|
+
list-style: none;
|
|
108
|
+
background-color: white;
|
|
109
|
+
border: 1px solid var(--cool-gray-10);
|
|
110
|
+
border-radius: 0.333rem;
|
|
111
|
+
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.cove-combobox-listbox:has(.no-results) {
|
|
115
|
+
pointer-events: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.cove-combobox-option {
|
|
119
|
+
padding: 0.5rem 0.5rem;
|
|
120
|
+
font-family: var(--app-font-secondary);
|
|
121
|
+
font-weight: 300;
|
|
122
|
+
font-size: 0.778rem;
|
|
123
|
+
color: var(--cool-gray-90);
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
user-select: none;
|
|
126
|
+
transition: background-color 0.15s ease-in-out;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.cove-combobox-option .cove-combobox-option-highlight {
|
|
130
|
+
font-weight: 600;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.cove-combobox-option:hover:not(.no-results) {
|
|
134
|
+
background-color: var(--cool-gray-10);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.cove-combobox-option.active:not(.no-results) {
|
|
138
|
+
background-color: var(--cool-gray-10);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.cove-combobox-option.no-results {
|
|
142
|
+
cursor: default;
|
|
143
|
+
font-style: italic;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.cove-combobox-option.no-results:hover {
|
|
147
|
+
background-color: transparent;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.sr-only {
|
|
151
|
+
position: absolute;
|
|
152
|
+
width: 1px;
|
|
153
|
+
height: 1px;
|
|
154
|
+
padding: 0;
|
|
155
|
+
margin: -1px;
|
|
156
|
+
overflow: hidden;
|
|
157
|
+
clip: rect(0, 0, 0, 0);
|
|
158
|
+
white-space: nowrap;
|
|
159
|
+
border: 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@media (prefers-reduced-motion: reduce) {
|
|
163
|
+
.cove-combobox-input,
|
|
164
|
+
.cove-combobox-option {
|
|
165
|
+
transition: none;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.cove-combobox-listbox::-webkit-scrollbar {
|
|
170
|
+
width: 8px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.cove-combobox-listbox::-webkit-scrollbar-track {
|
|
174
|
+
background: #f1f1f1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.cove-combobox-listbox::-webkit-scrollbar-thumb {
|
|
178
|
+
background: #888;
|
|
179
|
+
border-radius: 4px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.cove-combobox-listbox::-webkit-scrollbar-thumb:hover {
|
|
183
|
+
background: #555;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './ComboBox'
|
|
@@ -48,11 +48,22 @@ export type DataTableProps = {
|
|
|
48
48
|
setFilteredCountryCode?: string // used for Maps only
|
|
49
49
|
tabbingId: string
|
|
50
50
|
tableTitle: string
|
|
51
|
+
applyLegendToRow?: (
|
|
52
|
+
rowObj: any,
|
|
53
|
+
config: any,
|
|
54
|
+
runtimeLegend: any,
|
|
55
|
+
legendMemo: any,
|
|
56
|
+
legendSpecialClassLastMemo: any
|
|
57
|
+
) => string[]
|
|
58
|
+
getPatternForRow?: (rowObj: any, config: any) => any
|
|
51
59
|
viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
52
60
|
vizTitle?: string
|
|
53
61
|
// determines if columns should be wrapped in the table
|
|
54
62
|
wrapColumns?: boolean
|
|
55
63
|
interactionLabel?: string
|
|
64
|
+
showDownloadImgButton?: boolean
|
|
65
|
+
showDownloadPdfButton?: boolean
|
|
66
|
+
includeContextInDownload?: boolean
|
|
56
67
|
// Map-specific props (optional)
|
|
57
68
|
legendMemo?: React.MutableRefObject<Map<any, any>>
|
|
58
69
|
legendSpecialClassLastMemo?: React.MutableRefObject<Map<any, any>>
|
|
@@ -76,7 +87,11 @@ const DataTable = (props: DataTableProps) => {
|
|
|
76
87
|
viewport,
|
|
77
88
|
vizTitle,
|
|
78
89
|
wrapColumns,
|
|
79
|
-
interactionLabel = ''
|
|
90
|
+
interactionLabel = '',
|
|
91
|
+
showDownloadImgButton,
|
|
92
|
+
showDownloadPdfButton,
|
|
93
|
+
includeContextInDownload = false,
|
|
94
|
+
imageRef
|
|
80
95
|
} = props
|
|
81
96
|
const runtimeData = useMemo(() => {
|
|
82
97
|
const data = removeNullColumns(parentRuntimeData)
|
|
@@ -145,23 +160,23 @@ const DataTable = (props: DataTableProps) => {
|
|
|
145
160
|
const rows =
|
|
146
161
|
isVertical && sortBy.column
|
|
147
162
|
? rawRows.sort((a, b) => {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
let dataA
|
|
164
|
+
let dataB
|
|
165
|
+
if (config.type === 'map' && config.columns) {
|
|
166
|
+
const sortByColName = config.columns[sortBy.column].name
|
|
167
|
+
dataA = runtimeData[a][sortByColName]
|
|
168
|
+
dataB = runtimeData[b][sortByColName]
|
|
169
|
+
}
|
|
170
|
+
if (['chart', 'dashboard', 'table'].includes(config.type)) {
|
|
171
|
+
dataA = runtimeData[a][sortBy.column]
|
|
172
|
+
dataB = runtimeData[b][sortBy.column]
|
|
173
|
+
}
|
|
174
|
+
if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
|
|
175
|
+
dataA = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[a][config.xAxis.dataKey])
|
|
176
|
+
dataB = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[b][config.xAxis.dataKey])
|
|
177
|
+
}
|
|
178
|
+
return dataA || dataB ? customSort(dataA, dataB, sortBy, config) : 0
|
|
179
|
+
})
|
|
165
180
|
: rawRows
|
|
166
181
|
|
|
167
182
|
const limitHeight = {
|
|
@@ -214,18 +229,14 @@ const DataTable = (props: DataTableProps) => {
|
|
|
214
229
|
const getClassNames = (): string => {
|
|
215
230
|
const classes = ['data-table-container']
|
|
216
231
|
|
|
217
|
-
const hasDownloadLinkAbove =
|
|
232
|
+
const hasDownloadLinkAbove =
|
|
233
|
+
(config.table.download || showDownloadImgButton || showDownloadPdfButton) && !config.table.showDownloadLinkBelow
|
|
218
234
|
const isStandaloneTable = config.type === 'table'
|
|
219
235
|
|
|
220
236
|
if (!hasDownloadLinkAbove && !isStandaloneTable) {
|
|
221
237
|
classes.push('mt-4')
|
|
222
238
|
}
|
|
223
239
|
|
|
224
|
-
const isBrushActive = config?.brush?.active && config.legend?.position !== 'bottom'
|
|
225
|
-
if (isBrushActive) {
|
|
226
|
-
classes.push('brush-active')
|
|
227
|
-
}
|
|
228
|
-
|
|
229
240
|
classes.push(viewport)
|
|
230
241
|
|
|
231
242
|
return classes.join(' ')
|
|
@@ -237,33 +248,76 @@ const DataTable = (props: DataTableProps) => {
|
|
|
237
248
|
const sharedFilterColumns = config.table?.sharedFilterColumns || []
|
|
238
249
|
const vizFilterColumns = (config.filters || []).map(filter => filter.columnName)
|
|
239
250
|
const filterColumns = [...sharedFilterColumns, ...vizFilterColumns]
|
|
251
|
+
const getVisibleColumns = () => {
|
|
252
|
+
if (!config.columns) return []
|
|
253
|
+
|
|
254
|
+
return Object.values(config.columns)
|
|
255
|
+
.filter(col => col.dataTable !== false)
|
|
256
|
+
.map(col => col.name)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const visibleColumns = getVisibleColumns()
|
|
240
260
|
const visibleData =
|
|
241
261
|
config.type === 'map'
|
|
242
262
|
? getMapRowData(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
263
|
+
rows,
|
|
264
|
+
columns,
|
|
265
|
+
config,
|
|
266
|
+
formatLegendLocation,
|
|
267
|
+
runtimeData as Record<string, Object>,
|
|
268
|
+
displayGeoName,
|
|
269
|
+
filterColumns
|
|
270
|
+
)
|
|
251
271
|
: runtimeData.map(d => {
|
|
252
|
-
|
|
253
|
-
|
|
272
|
+
const columnsToInclude =
|
|
273
|
+
config.type === 'table'
|
|
274
|
+
? _.uniq([...filterColumns, ...visibleColumns])
|
|
275
|
+
: _.uniq([...filterColumns, ...dataSeriesColumns])
|
|
276
|
+
return _.pick(d, columnsToInclude)
|
|
277
|
+
})
|
|
254
278
|
const csvData = config.table?.downloadVisibleDataOnly ? visibleData : rawData
|
|
255
279
|
|
|
280
|
+
// Build a map from column name to column config for O(1) lookup
|
|
281
|
+
const columnConfigMap = config.columns
|
|
282
|
+
? Object.values(config.columns).reduce((acc, col) => {
|
|
283
|
+
acc[col.name] = col
|
|
284
|
+
return acc
|
|
285
|
+
}, {} as Record<string, any>)
|
|
286
|
+
: {}
|
|
287
|
+
|
|
288
|
+
// Map column names to labels
|
|
289
|
+
const csvDataUpdated = csvData.map(row => {
|
|
290
|
+
const newRow: Record<string, any> = {}
|
|
291
|
+
Object.keys(row).forEach(key => {
|
|
292
|
+
// Use the column config map for O(1) lookup
|
|
293
|
+
const columnConfig = columnConfigMap[key]
|
|
294
|
+
// Use label if it exists, otherwise use the original key
|
|
295
|
+
const columnLabel = columnConfig?.label || key
|
|
296
|
+
newRow[columnLabel] = row[key]
|
|
297
|
+
})
|
|
298
|
+
return newRow
|
|
299
|
+
})
|
|
300
|
+
|
|
256
301
|
// only use fullGeoName on County maps and no other
|
|
257
302
|
if (config.general?.geoType === 'us-county' || config.table.showFullGeoNameInCSV) {
|
|
258
303
|
// Add column for full Geo name along with State
|
|
259
|
-
return
|
|
304
|
+
return csvDataUpdated.map((row, index) => {
|
|
305
|
+
const originalRow = csvData[index]
|
|
306
|
+
if (!originalRow) {
|
|
307
|
+
console.warn('Data mismatch: originalRow missing.', {
|
|
308
|
+
index,
|
|
309
|
+
csvDataLength: csvData.length,
|
|
310
|
+
csvDataUpdatedLength: csvDataUpdated.length
|
|
311
|
+
})
|
|
312
|
+
return row
|
|
313
|
+
}
|
|
260
314
|
return {
|
|
261
|
-
FullGeoName: formatLegendLocation(
|
|
315
|
+
FullGeoName: formatLegendLocation(originalRow[config.columns.geo.name]),
|
|
262
316
|
...row
|
|
263
317
|
}
|
|
264
318
|
})
|
|
265
319
|
} else {
|
|
266
|
-
return
|
|
320
|
+
return csvDataUpdated
|
|
267
321
|
}
|
|
268
322
|
}
|
|
269
323
|
|
|
@@ -273,9 +327,6 @@ const DataTable = (props: DataTableProps) => {
|
|
|
273
327
|
if (hasDownloadLink) {
|
|
274
328
|
classes.push('mt-4', 'mb-2')
|
|
275
329
|
}
|
|
276
|
-
const isLegendOnBottom = config?.legend?.position === 'bottom' || isLegendWrapViewport(viewport)
|
|
277
|
-
if (config.brush?.active && !isLegendOnBottom) classes.push('brush-active')
|
|
278
|
-
if (config.brush?.active && config.legend.hide) classes.push('brush-active')
|
|
279
330
|
} else {
|
|
280
331
|
if (hasDownloadLink) {
|
|
281
332
|
classes.push('mt-2')
|
|
@@ -287,15 +338,15 @@ const DataTable = (props: DataTableProps) => {
|
|
|
287
338
|
const childrenMatrix =
|
|
288
339
|
config.type === 'map'
|
|
289
340
|
? mapCellMatrix({
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
341
|
+
...props,
|
|
342
|
+
rows,
|
|
343
|
+
wrapColumns,
|
|
344
|
+
runtimeData,
|
|
345
|
+
viewport,
|
|
346
|
+
legendMemo: props.legendMemo || defaultLegendMemo,
|
|
347
|
+
legendSpecialClassLastMemo: props.legendSpecialClassLastMemo || defaultLegendSpecialClassLastMemo,
|
|
348
|
+
runtimeLegend: props.runtimeLegend || defaultRuntimeLegend
|
|
349
|
+
})
|
|
299
350
|
: chartCellMatrix({ rows, ...props, runtimeData, isVertical, sortBy, hasRowType, viewport })
|
|
300
351
|
|
|
301
352
|
const useBottomExpandCollapse = config.table.showBottomCollapse && expanded && Array.isArray(childrenMatrix)
|
|
@@ -303,19 +354,41 @@ const DataTable = (props: DataTableProps) => {
|
|
|
303
354
|
// If every value in a column is a number, record the column index so the header and cells can be right-aligned
|
|
304
355
|
const rightAlignedCols = childrenMatrix.length
|
|
305
356
|
? Object.fromEntries(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
357
|
+
Object.keys(childrenMatrix[0])
|
|
358
|
+
.filter(
|
|
359
|
+
i => childrenMatrix.filter(row => isRightAlignedTableValue(row[i])).length === childrenMatrix.length
|
|
360
|
+
)
|
|
361
|
+
.map(x => [x, true])
|
|
362
|
+
)
|
|
312
363
|
: {}
|
|
313
364
|
|
|
314
365
|
const showCollapseButton = config.table.collapsible !== false && useBottomExpandCollapse
|
|
315
366
|
const TableMediaControls = ({ belowTable }) => {
|
|
316
367
|
const hasDownloadLink = config.table.download
|
|
368
|
+
const hasImageDownloads = showDownloadImgButton || showDownloadPdfButton
|
|
369
|
+
|
|
317
370
|
return (
|
|
318
|
-
<MediaControls.Section classes={getMediaControlsClasses(belowTable, hasDownloadLink)}>
|
|
371
|
+
<MediaControls.Section classes={getMediaControlsClasses(belowTable, hasDownloadLink || hasImageDownloads)}>
|
|
372
|
+
{showDownloadImgButton && (
|
|
373
|
+
<MediaControls.DownloadLink
|
|
374
|
+
type='image'
|
|
375
|
+
title='Download Chart as Image'
|
|
376
|
+
state={config}
|
|
377
|
+
elementToCapture={imageRef}
|
|
378
|
+
interactionLabel={interactionLabel}
|
|
379
|
+
includeContextInDownload={includeContextInDownload}
|
|
380
|
+
/>
|
|
381
|
+
)}
|
|
382
|
+
{showDownloadPdfButton && (
|
|
383
|
+
<MediaControls.DownloadLink
|
|
384
|
+
type='pdf'
|
|
385
|
+
title='Download Chart as PDF'
|
|
386
|
+
state={config}
|
|
387
|
+
elementToCapture={imageRef}
|
|
388
|
+
interactionLabel={interactionLabel}
|
|
389
|
+
includeContextInDownload={includeContextInDownload}
|
|
390
|
+
/>
|
|
391
|
+
)}
|
|
319
392
|
<MediaControls.Link config={config} dashboardDataConfig={dataConfig} interactionLabel={interactionLabel} />
|
|
320
393
|
{hasDownloadLink && (
|
|
321
394
|
<DownloadButton
|
|
@@ -384,8 +457,9 @@ const DataTable = (props: DataTableProps) => {
|
|
|
384
457
|
)
|
|
385
458
|
}
|
|
386
459
|
tableOptions={{
|
|
387
|
-
className: `table table-striped table-width-unset ${
|
|
388
|
-
|
|
460
|
+
className: `table table-striped table-width-unset ${
|
|
461
|
+
expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'
|
|
462
|
+
}${isVertical ? '' : ' horizontal'}`,
|
|
389
463
|
'aria-live': 'assertive',
|
|
390
464
|
'aria-rowcount': config?.data?.length ? config.data.length : -1,
|
|
391
465
|
hidden: !expanded,
|