@cdc/core 4.25.8 → 4.25.11
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/StoryRenderingTests.stories.tsx +164 -0
- package/components/AdvancedEditor/AdvancedEditor.tsx +32 -9
- package/components/CustomColorsEditor/CustomColorsEditor.css +299 -0
- package/components/CustomColorsEditor/CustomColorsEditor.tsx +209 -0
- package/components/CustomColorsEditor/index.ts +1 -0
- package/components/DataTable/DataTable.tsx +56 -38
- package/components/DataTable/DataTableStandAlone.tsx +8 -3
- package/components/DataTable/components/ChartHeader.tsx +44 -14
- package/components/DataTable/components/DataTableEditorPanel.tsx +12 -2
- package/components/DataTable/components/ExpandCollapse.tsx +10 -1
- package/components/DataTable/components/MapHeader.tsx +24 -13
- package/components/DataTable/data-table.css +12 -0
- package/components/DataTable/helpers/chartCellMatrix.tsx +11 -8
- package/components/DataTable/helpers/mapCellMatrix.tsx +33 -4
- package/components/DataTable/helpers/standardizeState.js +2 -2
- package/components/DataTable/helpers/tests/standardizeState.test.js +54 -0
- package/components/DownloadButton.tsx +40 -14
- package/components/EditorPanel/DataTableEditor.tsx +3 -3
- package/components/EditorPanel/EditorPanel.styles.css +423 -0
- package/components/EditorPanel/FootnotesEditor.tsx +44 -37
- package/components/EditorPanel/Inputs.tsx +12 -2
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +35 -62
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +12 -2
- package/components/EditorPanel/components/MarkupHighlightedTextField.tsx +227 -0
- package/components/EditorPanel/components/MarkupVariablesEditor.tsx +450 -0
- package/components/EditorPanel/components/PanelMarkup.tsx +59 -0
- package/components/ErrorBoundary.jsx +3 -1
- package/components/Filters/Filters.tsx +52 -24
- package/components/Filters/components/Dropdown.tsx +6 -1
- package/components/Filters/components/Tabs.tsx +1 -0
- package/components/Footnotes/Footnotes.tsx +35 -25
- package/components/Footnotes/FootnotesStandAlone.tsx +42 -6
- package/components/HeaderThemeSelector/HeaderThemeSelector.css +43 -0
- package/components/HeaderThemeSelector/HeaderThemeSelector.stories.tsx +74 -0
- package/components/HeaderThemeSelector/HeaderThemeSelector.tsx +61 -0
- package/components/HeaderThemeSelector/index.ts +2 -0
- package/components/Layout/styles/editor.scss +2 -1
- package/components/Legend/Legend.Gradient.tsx +3 -6
- package/components/LegendShape.tsx +121 -3
- package/components/Loader/Loader.tsx +1 -1
- package/components/MediaControls.tsx +72 -21
- package/components/PaletteConversionModal.tsx +90 -0
- package/components/PaletteSelector/DeveloperPaletteRollback.tsx +114 -0
- package/components/PaletteSelector/PaletteSelector.css +94 -0
- package/components/PaletteSelector/PaletteSelector.tsx +112 -0
- package/components/PaletteSelector/index.ts +2 -0
- package/components/RichTooltip/RichTooltip.tsx +1 -0
- package/components/Table/Table.tsx +3 -1
- package/components/Table/components/Cell.tsx +23 -2
- package/components/Table/components/Row.tsx +5 -3
- package/components/_stories/BlurStrokeTest.stories.tsx +1 -1
- package/components/_stories/DataTable.stories.tsx +1 -1
- package/components/_stories/Filters.stories.tsx +21 -2
- package/components/_stories/Footnotes.CSV.stories.tsx +247 -0
- package/components/_stories/Footnotes.stories.tsx +769 -4
- package/components/_stories/Inputs.stories.tsx +3 -3
- package/components/_stories/MultiSelect.stories.tsx +3 -3
- package/components/_stories/NestedDropdown.stories.tsx +1 -1
- package/components/_stories/Table.stories.tsx +1 -1
- package/components/_stories/styles.scss +0 -1
- package/components/elements/_stories/Button.stories.tsx +1 -1
- package/components/elements/_stories/Card.stories.tsx +1 -1
- package/components/inputs/InputToggle.tsx +2 -0
- package/components/managers/DataDesigner.tsx +10 -9
- package/components/managers/_stories/DataDesigner.stories.tsx +1 -1
- package/components/ui/Accordion.jsx +1 -1
- package/components/ui/Tooltip.tsx +2 -1
- package/components/ui/_stories/Accordion.stories.tsx +1 -1
- package/components/ui/_stories/ColorPaletteMigration.stories.mdx +275 -0
- package/components/ui/_stories/Colors.stories.tsx +330 -0
- package/components/ui/_stories/IconGallery.stories.tsx +316 -0
- package/components/ui/_stories/Title.stories.tsx +1 -1
- package/components/ui/accordion.styles.css +57 -0
- package/contexts/EditorContext.ts +18 -0
- package/contexts/editor.actions.ts +28 -0
- package/contexts/editor.reducer.ts +94 -0
- package/data/chartColorPalettes.ts +118 -0
- package/data/colorPalettes.ts +9 -0
- package/data/mapColorPalettes.ts +45 -0
- package/data/sharedPalettes.ts +50 -0
- package/dist/cove-main.css +63 -14
- package/dist/cove-main.css.map +1 -1
- package/generateViteConfig.js +80 -0
- package/helpers/addValuesToFilters.ts +7 -3
- package/helpers/cloneConfig.ts +31 -0
- package/helpers/configDataHelpers.ts +128 -0
- package/helpers/configHelpers.ts +27 -0
- package/helpers/constants.ts +42 -2
- package/helpers/cove/number.ts +33 -12
- package/helpers/coveUpdateWorker.ts +15 -3
- package/helpers/fetchRemoteData.ts +3 -15
- package/helpers/filterColorPalettes.ts +152 -0
- package/helpers/generateColorsArray.ts +13 -0
- package/helpers/getColorPaletteVersion.ts +33 -0
- package/helpers/getPaletteAccessor.ts +18 -0
- package/helpers/markupProcessor.ts +220 -0
- package/helpers/mergeCustomOrderValues.ts +37 -0
- package/helpers/metrics/helpers.ts +42 -19
- package/helpers/metrics/types.ts +48 -9
- package/helpers/metrics/utils.ts +34 -0
- package/helpers/palettes/colorDistributions.ts +56 -0
- package/helpers/palettes/migratePaletteName.ts +150 -0
- package/helpers/palettes/standardizePaletteNames.ts +77 -0
- package/helpers/palettes/utils.ts +267 -0
- package/helpers/parseCsvWithQuotes.ts +65 -0
- package/helpers/queryStringUtils.ts +13 -0
- package/helpers/testing.ts +358 -0
- package/helpers/tests/addValuesToFilters.test.ts +1 -2
- package/helpers/tests/generateColorsArray.test.ts +24 -0
- package/helpers/tests/markupProcessor.test.ts +538 -0
- package/helpers/tests/testStandaloneBuild.ts +44 -0
- package/helpers/useMarkupVariables.ts +31 -0
- package/helpers/vegaConfig.ts +0 -1
- package/helpers/ver/4.24.10.ts +2 -1
- package/helpers/ver/4.24.11.ts +2 -1
- package/helpers/ver/4.24.3.ts +2 -1
- package/helpers/ver/4.24.4.ts +2 -1
- package/helpers/ver/4.24.5.ts +2 -1
- package/helpers/ver/4.24.7.ts +2 -1
- package/helpers/ver/4.24.9.ts +2 -1
- package/helpers/ver/4.25.1.ts +2 -1
- package/helpers/ver/4.25.10.ts +36 -0
- package/helpers/ver/4.25.11.ts +13 -0
- package/helpers/ver/4.25.3.ts +2 -1
- package/helpers/ver/4.25.4.ts +2 -1
- package/helpers/ver/4.25.6.ts +2 -1
- package/helpers/ver/4.25.7.ts +2 -1
- package/helpers/ver/4.25.8.ts +2 -1
- package/helpers/ver/4.25.9.ts +293 -0
- package/helpers/ver/tests/4.25.10.test.ts +204 -0
- package/helpers/ver/tests/4.25.8.test.ts +1 -1
- package/helpers/ver/tests/4.25.9.test.ts +51 -0
- package/helpers/viewports.ts +2 -0
- package/hooks/useColorPalette.ts +79 -0
- package/package.json +13 -4
- package/styles/_common-components.css +73 -0
- package/styles/_global.scss +32 -10
- package/styles/base.scss +8 -55
- package/styles/cove-main.scss +3 -1
- package/styles/filters.scss +10 -3
- package/styles/v2/base/index.scss +0 -1
- package/styles/v2/components/button.scss +4 -3
- package/styles/v2/components/editor.scss +16 -7
- package/styles/v2/layout/_data-table.scss +3 -2
- package/styles/v2/themes/_color-definitions.scss +18 -17
- package/styles/v2/utils/_breakpoints.scss +1 -1
- package/styles/v2/utils/index.scss +0 -1
- package/styles/waiting.scss +1 -1
- package/testing-setup.js +32 -0
- package/types/MarkupInclude.ts +8 -2
- package/types/MarkupVariable.ts +19 -0
- package/types/VizFilter.ts +2 -0
- package/vitest.config.ts +16 -0
- package/components/ui/_stories/Colors.stories.mdx +0 -220
- package/components/ui/_stories/IconGallery.stories.mdx +0 -14
- package/data/colorPalettes.js +0 -171
- package/helpers/formatConfigBeforeSave.ts +0 -135
- package/helpers/tests/formatConfigBeforeSave.test.ts +0 -68
- package/styles/_mixins.scss +0 -13
- package/styles/v2/base/_typography.scss +0 -0
- package/styles/v2/components/guidance-block.scss +0 -74
- package/styles/v2/utils/_functions.scss +0 -0
- /package/{styles/_typography.scss → testBuild.js} +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { TextField } from '../EditorPanel/Inputs'
|
|
3
|
+
import './CustomColorsEditor.css'
|
|
4
|
+
|
|
5
|
+
interface CustomColorsEditorProps {
|
|
6
|
+
colors: string[]
|
|
7
|
+
onChange: (colors: string[]) => void
|
|
8
|
+
label?: string
|
|
9
|
+
minColors?: number
|
|
10
|
+
maxColors?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const CustomColorsEditor: React.FC<CustomColorsEditorProps> = ({
|
|
14
|
+
colors = [],
|
|
15
|
+
onChange,
|
|
16
|
+
label = 'Custom Colors',
|
|
17
|
+
minColors = 1,
|
|
18
|
+
maxColors = 20
|
|
19
|
+
}) => {
|
|
20
|
+
const [draggedIndex, setDraggedIndex] = useState<number | null>(null)
|
|
21
|
+
|
|
22
|
+
const handleColorChange = (index: number, newColor: string) => {
|
|
23
|
+
const newColors = [...colors]
|
|
24
|
+
newColors[index] = newColor
|
|
25
|
+
onChange(newColors)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const handleAddColor = () => {
|
|
29
|
+
if (colors.length < maxColors) {
|
|
30
|
+
// Add a new color (default to the last color or a neutral color)
|
|
31
|
+
const defaultColor = colors.length > 0 ? colors[colors.length - 1] : '#3366cc'
|
|
32
|
+
onChange([...colors, defaultColor])
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleRemoveColor = (index: number) => {
|
|
37
|
+
if (colors.length > minColors) {
|
|
38
|
+
const newColors = colors.filter((_, i) => i !== index)
|
|
39
|
+
onChange(newColors)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleMoveUp = (index: number) => {
|
|
44
|
+
if (index > 0) {
|
|
45
|
+
const newColors = [...colors]
|
|
46
|
+
;[newColors[index - 1], newColors[index]] = [newColors[index], newColors[index - 1]]
|
|
47
|
+
onChange(newColors)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const handleMoveDown = (index: number) => {
|
|
52
|
+
if (index < colors.length - 1) {
|
|
53
|
+
const newColors = [...colors]
|
|
54
|
+
;[newColors[index], newColors[index + 1]] = [newColors[index + 1], newColors[index]]
|
|
55
|
+
onChange(newColors)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const handleDragStart = (index: number) => {
|
|
60
|
+
setDraggedIndex(index)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const handleDragOver = (e: React.DragEvent, index: number) => {
|
|
64
|
+
e.preventDefault()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const handleDrop = (e: React.DragEvent, dropIndex: number) => {
|
|
68
|
+
e.preventDefault()
|
|
69
|
+
|
|
70
|
+
if (draggedIndex === null || draggedIndex === dropIndex) {
|
|
71
|
+
setDraggedIndex(null)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const newColors = [...colors]
|
|
76
|
+
const draggedColor = newColors[draggedIndex]
|
|
77
|
+
newColors.splice(draggedIndex, 1)
|
|
78
|
+
newColors.splice(dropIndex, 0, draggedColor)
|
|
79
|
+
|
|
80
|
+
onChange(newColors)
|
|
81
|
+
setDraggedIndex(null)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className="custom-colors-editor">
|
|
86
|
+
<label className="custom-colors-label">{label}</label>
|
|
87
|
+
|
|
88
|
+
<div className="custom-colors-notice">
|
|
89
|
+
<svg className="notice-icon" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
90
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
|
91
|
+
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
|
|
92
|
+
</svg>
|
|
93
|
+
<span>
|
|
94
|
+
<strong>Accessibility Notice:</strong> When using custom colors, conduct a{' '}
|
|
95
|
+
Section 508 review to ensure adequate color contrast and accessibility compliance.{' '}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div className="custom-colors-preview">
|
|
100
|
+
{colors.map((color, index) => (
|
|
101
|
+
<div
|
|
102
|
+
key={index}
|
|
103
|
+
className="preview-swatch"
|
|
104
|
+
style={{ backgroundColor: color }}
|
|
105
|
+
title={`Color ${index + 1}: ${color}`}
|
|
106
|
+
/>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div className="custom-colors-list">
|
|
111
|
+
{colors.map((color, index) => (
|
|
112
|
+
<div
|
|
113
|
+
key={index}
|
|
114
|
+
className={`custom-color-item ${draggedIndex === index ? 'dragging' : ''}`}
|
|
115
|
+
draggable
|
|
116
|
+
onDragStart={() => handleDragStart(index)}
|
|
117
|
+
onDragOver={(e) => handleDragOver(e, index)}
|
|
118
|
+
onDrop={(e) => handleDrop(e, index)}
|
|
119
|
+
>
|
|
120
|
+
<div className="color-item-controls">
|
|
121
|
+
<span className="color-item-drag-handle" title="Drag to reorder">
|
|
122
|
+
⋮⋮
|
|
123
|
+
</span>
|
|
124
|
+
{/* <span className="color-item-number">{index + 1}.</span> */}
|
|
125
|
+
|
|
126
|
+
{/* <input
|
|
127
|
+
type="color"
|
|
128
|
+
value={color}
|
|
129
|
+
onChange={(e) => handleColorChange(index, e.target.value)}
|
|
130
|
+
className="color-picker"
|
|
131
|
+
title="Click to change color"
|
|
132
|
+
aria-label={`Color picker for color ${index + 1}`}
|
|
133
|
+
/> */}
|
|
134
|
+
|
|
135
|
+
<div className="color-input-wrapper">
|
|
136
|
+
<TextField
|
|
137
|
+
type="text"
|
|
138
|
+
value={color}
|
|
139
|
+
label=""
|
|
140
|
+
section="colors"
|
|
141
|
+
subsection={null}
|
|
142
|
+
fieldName={`color-${index}`}
|
|
143
|
+
updateField={(section, subsection, fieldName, value) => {
|
|
144
|
+
handleColorChange(index, value)
|
|
145
|
+
}}
|
|
146
|
+
placeholder="#000000"
|
|
147
|
+
maxLength={15}
|
|
148
|
+
className="color-text-input"
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div className="color-item-buttons">
|
|
153
|
+
<button
|
|
154
|
+
type="button"
|
|
155
|
+
onClick={() => handleMoveUp(index)}
|
|
156
|
+
disabled={index === 0}
|
|
157
|
+
className="btn-move"
|
|
158
|
+
title="Move up"
|
|
159
|
+
aria-label="Move color up"
|
|
160
|
+
>
|
|
161
|
+
↑
|
|
162
|
+
</button>
|
|
163
|
+
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
onClick={() => handleMoveDown(index)}
|
|
167
|
+
disabled={index === colors.length - 1}
|
|
168
|
+
className="btn-move"
|
|
169
|
+
title="Move down"
|
|
170
|
+
aria-label="Move color down"
|
|
171
|
+
>
|
|
172
|
+
↓
|
|
173
|
+
</button>
|
|
174
|
+
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
onClick={() => handleRemoveColor(index)}
|
|
178
|
+
disabled={colors.length <= minColors}
|
|
179
|
+
className="btn-remove"
|
|
180
|
+
title="Remove color"
|
|
181
|
+
aria-label="Remove color"
|
|
182
|
+
>
|
|
183
|
+
×
|
|
184
|
+
</button>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
))}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
onClick={handleAddColor}
|
|
194
|
+
disabled={colors.length >= maxColors}
|
|
195
|
+
className="btn-add-color"
|
|
196
|
+
>
|
|
197
|
+
+ Add Color
|
|
198
|
+
</button>
|
|
199
|
+
|
|
200
|
+
<div className="custom-colors-info">
|
|
201
|
+
{colors.length} color{colors.length !== 1 ? 's' : ''}
|
|
202
|
+
{colors.length < minColors && ` (minimum ${minColors} required)`}
|
|
203
|
+
{colors.length >= maxColors && ` (maximum reached)`}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default CustomColorsEditor
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CustomColorsEditor } from './CustomColorsEditor'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useState, useMemo } from 'react'
|
|
1
|
+
import { useEffect, useState, useMemo, useRef } from 'react'
|
|
2
2
|
import { timeParse } from 'd3-time-format'
|
|
3
3
|
|
|
4
4
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
@@ -53,6 +53,10 @@ export type DataTableProps = {
|
|
|
53
53
|
// determines if columns should be wrapped in the table
|
|
54
54
|
wrapColumns?: boolean
|
|
55
55
|
interactionLabel?: string
|
|
56
|
+
// Map-specific props (optional)
|
|
57
|
+
legendMemo?: React.MutableRefObject<Map<any, any>>
|
|
58
|
+
legendSpecialClassLastMemo?: React.MutableRefObject<Map<any, any>>
|
|
59
|
+
runtimeLegend?: any
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
const DataTable = (props: DataTableProps) => {
|
|
@@ -95,6 +99,11 @@ const DataTable = (props: DataTableProps) => {
|
|
|
95
99
|
|
|
96
100
|
const [accessibilityLabel, setAccessibilityLabel] = useState('')
|
|
97
101
|
|
|
102
|
+
// Create default refs for map-specific props when not provided
|
|
103
|
+
const defaultLegendMemo = useRef(new Map())
|
|
104
|
+
const defaultLegendSpecialClassLastMemo = useRef(new Map())
|
|
105
|
+
const defaultRuntimeLegend = null
|
|
106
|
+
|
|
98
107
|
const isVertical = !(config.type === 'chart' && !config.table?.showVertical)
|
|
99
108
|
|
|
100
109
|
const rand = Math.random().toString(16).substr(2, 8)
|
|
@@ -136,23 +145,23 @@ const DataTable = (props: DataTableProps) => {
|
|
|
136
145
|
const rows =
|
|
137
146
|
isVertical && sortBy.column
|
|
138
147
|
? rawRows.sort((a, b) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
let dataA
|
|
149
|
+
let dataB
|
|
150
|
+
if (config.type === 'map' && config.columns) {
|
|
151
|
+
const sortByColName = config.columns[sortBy.column].name
|
|
152
|
+
dataA = runtimeData[a][sortByColName]
|
|
153
|
+
dataB = runtimeData[b][sortByColName]
|
|
154
|
+
}
|
|
155
|
+
if (['chart', 'dashboard', 'table'].includes(config.type)) {
|
|
156
|
+
dataA = runtimeData[a][sortBy.column]
|
|
157
|
+
dataB = runtimeData[b][sortBy.column]
|
|
158
|
+
}
|
|
159
|
+
if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
|
|
160
|
+
dataA = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[a][config.xAxis.dataKey])
|
|
161
|
+
dataB = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[b][config.xAxis.dataKey])
|
|
162
|
+
}
|
|
163
|
+
return dataA || dataB ? customSort(dataA, dataB, sortBy, config) : 0
|
|
164
|
+
})
|
|
156
165
|
: rawRows
|
|
157
166
|
|
|
158
167
|
const limitHeight = {
|
|
@@ -231,17 +240,17 @@ const DataTable = (props: DataTableProps) => {
|
|
|
231
240
|
const visibleData =
|
|
232
241
|
config.type === 'map'
|
|
233
242
|
? getMapRowData(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
243
|
+
rows,
|
|
244
|
+
columns,
|
|
245
|
+
config,
|
|
246
|
+
formatLegendLocation,
|
|
247
|
+
runtimeData as Record<string, Object>,
|
|
248
|
+
displayGeoName,
|
|
249
|
+
filterColumns
|
|
250
|
+
)
|
|
242
251
|
: runtimeData.map(d => {
|
|
243
|
-
|
|
244
|
-
|
|
252
|
+
return _.pick(d, [...filterColumns, ...dataSeriesColumns])
|
|
253
|
+
})
|
|
245
254
|
const csvData = config.table?.downloadVisibleDataOnly ? visibleData : rawData
|
|
246
255
|
|
|
247
256
|
// only use fullGeoName on County maps and no other
|
|
@@ -277,7 +286,16 @@ const DataTable = (props: DataTableProps) => {
|
|
|
277
286
|
|
|
278
287
|
const childrenMatrix =
|
|
279
288
|
config.type === 'map'
|
|
280
|
-
? mapCellMatrix({
|
|
289
|
+
? mapCellMatrix({
|
|
290
|
+
...props,
|
|
291
|
+
rows,
|
|
292
|
+
wrapColumns,
|
|
293
|
+
runtimeData,
|
|
294
|
+
viewport,
|
|
295
|
+
legendMemo: props.legendMemo || defaultLegendMemo,
|
|
296
|
+
legendSpecialClassLastMemo: props.legendSpecialClassLastMemo || defaultLegendSpecialClassLastMemo,
|
|
297
|
+
runtimeLegend: props.runtimeLegend || defaultRuntimeLegend
|
|
298
|
+
})
|
|
281
299
|
: chartCellMatrix({ rows, ...props, runtimeData, isVertical, sortBy, hasRowType, viewport })
|
|
282
300
|
|
|
283
301
|
const useBottomExpandCollapse = config.table.showBottomCollapse && expanded && Array.isArray(childrenMatrix)
|
|
@@ -285,12 +303,12 @@ const DataTable = (props: DataTableProps) => {
|
|
|
285
303
|
// If every value in a column is a number, record the column index so the header and cells can be right-aligned
|
|
286
304
|
const rightAlignedCols = childrenMatrix.length
|
|
287
305
|
? Object.fromEntries(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
306
|
+
Object.keys(childrenMatrix[0])
|
|
307
|
+
.filter(
|
|
308
|
+
i => childrenMatrix.filter(row => isRightAlignedTableValue(row[i])).length === childrenMatrix.length
|
|
309
|
+
)
|
|
310
|
+
.map(x => [x, true])
|
|
311
|
+
)
|
|
294
312
|
: {}
|
|
295
313
|
|
|
296
314
|
const showCollapseButton = config.table.collapsible !== false && useBottomExpandCollapse
|
|
@@ -305,6 +323,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
305
323
|
fileName={`${vizTitle || 'data-table'}.csv`}
|
|
306
324
|
headerColor={headerColor}
|
|
307
325
|
interactionLabel={interactionLabel}
|
|
326
|
+
config={config}
|
|
308
327
|
/>
|
|
309
328
|
)}
|
|
310
329
|
</MediaControls.Section>
|
|
@@ -365,9 +384,8 @@ const DataTable = (props: DataTableProps) => {
|
|
|
365
384
|
)
|
|
366
385
|
}
|
|
367
386
|
tableOptions={{
|
|
368
|
-
className: `table table-striped table-width-unset ${
|
|
369
|
-
|
|
370
|
-
}${isVertical ? '' : ' horizontal'}`,
|
|
387
|
+
className: `table table-striped table-width-unset ${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'
|
|
388
|
+
}${isVertical ? '' : ' horizontal'}`,
|
|
371
389
|
'aria-live': 'assertive',
|
|
372
390
|
'aria-rowcount': config?.data?.length ? config.data.length : -1,
|
|
373
391
|
hidden: !expanded,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import { ViewPort } from '../../types/ViewPort'
|
|
3
|
-
import
|
|
4
|
-
import EditorWrapper from '../EditorWrapper/EditorWrapper'
|
|
3
|
+
import EditorWrapper from '../EditorWrapper'
|
|
5
4
|
import DataTable from './DataTable'
|
|
6
5
|
import DataTableEditorPanel from './components/DataTableEditorPanel'
|
|
7
6
|
import Filters from '../Filters'
|
|
@@ -73,7 +72,13 @@ const DataTableStandAlone: React.FC<StandAloneProps> = ({
|
|
|
73
72
|
viewport={viewport || 'lg'}
|
|
74
73
|
interactionLabel={interactionLabel}
|
|
75
74
|
/>
|
|
76
|
-
<FootnotesStandAlone
|
|
75
|
+
<FootnotesStandAlone
|
|
76
|
+
config={config.footnotes}
|
|
77
|
+
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
78
|
+
markupVariables={config['markupVariables']}
|
|
79
|
+
enableMarkupVariables={config['enableMarkupVariables']}
|
|
80
|
+
data={config.data}
|
|
81
|
+
/>
|
|
77
82
|
</>
|
|
78
83
|
)
|
|
79
84
|
}
|
|
@@ -7,6 +7,7 @@ import { getNewSortBy } from '../helpers/getNewSortBy'
|
|
|
7
7
|
import parse from 'html-react-parser'
|
|
8
8
|
import { ChartConfig } from '@cdc/chart/src/types/ChartConfig'
|
|
9
9
|
import { publishAnalyticsEvent } from '../../../helpers/metrics/helpers'
|
|
10
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
10
11
|
|
|
11
12
|
type ChartHeaderProps = {
|
|
12
13
|
data
|
|
@@ -61,9 +62,8 @@ const ChartHeader = ({
|
|
|
61
62
|
if (columnHeaderText === notApplicableText) return
|
|
62
63
|
|
|
63
64
|
return (
|
|
64
|
-
<span className='cdcdataviz-sr-only'>{`Press command, modifier, or enter key to sort by ${columnHeaderText} in ${
|
|
65
|
-
|
|
66
|
-
} order`}</span>
|
|
65
|
+
<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'
|
|
66
|
+
} order`}</span>
|
|
67
67
|
)
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -116,18 +116,29 @@ const ChartHeader = ({
|
|
|
116
116
|
scope='col'
|
|
117
117
|
onClick={() => {
|
|
118
118
|
if (hasRowType) return
|
|
119
|
-
publishAnalyticsEvent(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
'click',
|
|
124
|
-
interactionLabel
|
|
125
|
-
|
|
119
|
+
publishAnalyticsEvent({
|
|
120
|
+
vizType: config.type,
|
|
121
|
+
vizSubType: getVizSubType(config),
|
|
122
|
+
eventType: `data_table_sort`,
|
|
123
|
+
eventAction: 'click',
|
|
124
|
+
eventLabel: interactionLabel,
|
|
125
|
+
vizTitle: getVizTitle(config),
|
|
126
|
+
specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
|
|
127
|
+
})
|
|
126
128
|
setSortBy(newSortBy)
|
|
127
129
|
}}
|
|
128
130
|
onKeyDown={e => {
|
|
129
131
|
if (hasRowType) return
|
|
130
|
-
if (e.
|
|
132
|
+
if (e.key === 'Enter') {
|
|
133
|
+
publishAnalyticsEvent({
|
|
134
|
+
vizType: config.type,
|
|
135
|
+
vizSubType: getVizSubType(config),
|
|
136
|
+
eventType: `data_table_sort`,
|
|
137
|
+
eventAction: 'keyboard',
|
|
138
|
+
eventLabel: interactionLabel,
|
|
139
|
+
vizTitle: getVizTitle(config),
|
|
140
|
+
specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
|
|
141
|
+
})
|
|
131
142
|
setSortBy(newSortBy)
|
|
132
143
|
}
|
|
133
144
|
}}
|
|
@@ -137,7 +148,7 @@ const ChartHeader = ({
|
|
|
137
148
|
: { 'aria-sort': 'descending' }
|
|
138
149
|
: null)}
|
|
139
150
|
>
|
|
140
|
-
<ColumnHeadingText text={text}
|
|
151
|
+
<ColumnHeadingText text={text} config={config} />
|
|
141
152
|
{isSortedCol && <SortIcon ascending={sortByAsc} />}
|
|
142
153
|
<ScreenReaderSortByText sortBy={sortBy} config={config} text={text} />
|
|
143
154
|
</th>
|
|
@@ -171,10 +182,29 @@ const ChartHeader = ({
|
|
|
171
182
|
role='columnheader'
|
|
172
183
|
scope='col'
|
|
173
184
|
onClick={() => {
|
|
185
|
+
if (hasRowType) return
|
|
186
|
+
publishAnalyticsEvent({
|
|
187
|
+
vizType: config.type,
|
|
188
|
+
vizSubType: getVizSubType(config),
|
|
189
|
+
eventType: `data_table_sort`,
|
|
190
|
+
eventAction: 'click',
|
|
191
|
+
eventLabel: interactionLabel,
|
|
192
|
+
vizTitle: getVizTitle(config),
|
|
193
|
+
specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
|
|
194
|
+
})
|
|
174
195
|
setSortBy(newSortBy)
|
|
175
196
|
}}
|
|
176
197
|
onKeyDown={e => {
|
|
177
|
-
if (e.
|
|
198
|
+
if (e.key === 'Enter') {
|
|
199
|
+
publishAnalyticsEvent({
|
|
200
|
+
vizType: config.type,
|
|
201
|
+
vizSubType: getVizSubType(config),
|
|
202
|
+
eventType: `data_table_sort`,
|
|
203
|
+
eventAction: 'keyboard',
|
|
204
|
+
eventLabel: interactionLabel,
|
|
205
|
+
vizTitle: getVizTitle(config),
|
|
206
|
+
specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
|
|
207
|
+
})
|
|
178
208
|
setSortBy(newSortBy)
|
|
179
209
|
}
|
|
180
210
|
}}
|
|
@@ -184,7 +214,7 @@ const ChartHeader = ({
|
|
|
184
214
|
: { 'aria-sort': 'descending' }
|
|
185
215
|
: null)}
|
|
186
216
|
>
|
|
187
|
-
<ColumnHeadingText text={text}
|
|
217
|
+
<ColumnHeadingText text={text} config={config} />
|
|
188
218
|
{isSortedCol && <SortIcon ascending={sortByAsc} />}
|
|
189
219
|
|
|
190
220
|
<ScreenReaderSortByText text={text} config={config} sortBy={sortBy} />
|
|
@@ -34,7 +34,17 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
|
|
|
34
34
|
})
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const columns = Object.keys(
|
|
37
|
+
const columns = Object.keys(
|
|
38
|
+
config.originalFormattedData?.[0] || config.formattedData?.[0] || config.data?.[0] || {}
|
|
39
|
+
)
|
|
40
|
+
// If no data is available, fallback to column names from config.columns
|
|
41
|
+
const columnsFromConfig = config.columns
|
|
42
|
+
? Object.values(config.columns)
|
|
43
|
+
.map(col => col.name)
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
: []
|
|
46
|
+
const finalColumns = columns.length > 0 ? columns : columnsFromConfig
|
|
47
|
+
|
|
38
48
|
return (
|
|
39
49
|
<Accordion allowZeroExpanded={true}>
|
|
40
50
|
<AccordionItem>
|
|
@@ -63,7 +73,7 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
|
|
|
63
73
|
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
64
74
|
</AccordionItemHeading>
|
|
65
75
|
<AccordionItemPanel>
|
|
66
|
-
<DataTableEditor config={config} columns={
|
|
76
|
+
<DataTableEditor config={config} columns={finalColumns} updateField={updateField} isDashboard={true} />
|
|
67
77
|
</AccordionItemPanel>
|
|
68
78
|
</AccordionItem>
|
|
69
79
|
<AccordionItem>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
1
2
|
import { publishAnalyticsEvent } from '../../../helpers/metrics/helpers'
|
|
2
3
|
import { Visualization } from '../../../types/Visualization'
|
|
3
4
|
import Icon from '../../ui/Icon'
|
|
@@ -17,7 +18,15 @@ const ExpandCollapse = ({ expanded, setExpanded, tableTitle, config, interaction
|
|
|
17
18
|
role='button'
|
|
18
19
|
className={expanded ? 'data-table-heading p-3' : 'collapsed data-table-heading p-3'}
|
|
19
20
|
onClick={() => {
|
|
20
|
-
publishAnalyticsEvent(
|
|
21
|
+
publishAnalyticsEvent({
|
|
22
|
+
vizType: config?.type,
|
|
23
|
+
vizSubType: getVizSubType(config),
|
|
24
|
+
eventType: 'expand_collapse_toggled',
|
|
25
|
+
eventAction: 'click',
|
|
26
|
+
eventLabel: interactionLabel,
|
|
27
|
+
vizTitle: getVizTitle(config),
|
|
28
|
+
specifics: expanded ? 'collapsed' : 'expanded'
|
|
29
|
+
})
|
|
21
30
|
setExpanded(!expanded)
|
|
22
31
|
}}
|
|
23
32
|
tabIndex={0}
|
|
@@ -3,6 +3,7 @@ import ScreenReaderText from '../../elements/ScreenReaderText'
|
|
|
3
3
|
import { SortIcon } from './SortIcon'
|
|
4
4
|
import { getNewSortBy } from '../helpers/getNewSortBy'
|
|
5
5
|
import { publishAnalyticsEvent } from '../../../helpers/metrics/helpers'
|
|
6
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
6
7
|
|
|
7
8
|
type MapHeaderProps = DataTableProps & {
|
|
8
9
|
sortBy: { column; asc }
|
|
@@ -56,17 +57,28 @@ const MapHeader = ({
|
|
|
56
57
|
role='columnheader'
|
|
57
58
|
scope='col'
|
|
58
59
|
onClick={() => {
|
|
59
|
-
publishAnalyticsEvent(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
'click',
|
|
64
|
-
interactionLabel
|
|
65
|
-
|
|
60
|
+
publishAnalyticsEvent({
|
|
61
|
+
vizType: config.type,
|
|
62
|
+
vizSubType: getVizSubType(config),
|
|
63
|
+
eventType: `data_table_sort`,
|
|
64
|
+
eventAction: 'click',
|
|
65
|
+
eventLabel: interactionLabel,
|
|
66
|
+
vizTitle: getVizTitle(config),
|
|
67
|
+
specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
|
|
68
|
+
})
|
|
66
69
|
setSortBy(newSortBy)
|
|
67
70
|
}}
|
|
68
71
|
onKeyDown={e => {
|
|
69
|
-
if (e.
|
|
72
|
+
if (e.key === 'Enter') {
|
|
73
|
+
publishAnalyticsEvent({
|
|
74
|
+
vizType: config.type,
|
|
75
|
+
vizSubType: getVizSubType(config),
|
|
76
|
+
eventType: `data_table_sort`,
|
|
77
|
+
eventAction: 'keyboard',
|
|
78
|
+
eventLabel: interactionLabel,
|
|
79
|
+
vizTitle: getVizTitle(config),
|
|
80
|
+
specifics: `column: ${newSortBy.column || 'none'}, order: ${newSortBy.asc === true ? 'asc' : newSortBy.asc === false ? 'desc' : 'none'}`
|
|
81
|
+
})
|
|
70
82
|
setSortBy(newSortBy)
|
|
71
83
|
}
|
|
72
84
|
}}
|
|
@@ -77,15 +89,14 @@ const MapHeader = ({
|
|
|
77
89
|
: { 'aria-sort': 'descending' }
|
|
78
90
|
: null)}
|
|
79
91
|
>
|
|
80
|
-
<ColumnHeadingText text={text} config={config}
|
|
92
|
+
<ColumnHeadingText text={text} config={config} />
|
|
81
93
|
<SortIcon ascending={sortByAsc} />
|
|
82
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
|
|
83
|
-
|
|
84
|
-
} order`}</span>
|
|
94
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
|
|
95
|
+
} order`}</span>
|
|
85
96
|
</th>
|
|
86
97
|
)
|
|
87
98
|
})}
|
|
88
|
-
</tr>
|
|
99
|
+
</tr >
|
|
89
100
|
)
|
|
90
101
|
}
|
|
91
102
|
|
|
@@ -145,7 +145,13 @@ table.data-table {
|
|
|
145
145
|
|
|
146
146
|
svg {
|
|
147
147
|
margin-left: 1rem;
|
|
148
|
+
|
|
149
|
+
&.legend-shape-svg {
|
|
150
|
+
display: flex;
|
|
151
|
+
margin-left: 0 !important;
|
|
152
|
+
}
|
|
148
153
|
}
|
|
154
|
+
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
td a {
|
|
@@ -160,6 +166,12 @@ table.data-table {
|
|
|
160
166
|
text-decoration: none;
|
|
161
167
|
}
|
|
162
168
|
|
|
169
|
+
td div a {
|
|
170
|
+
position: relative;
|
|
171
|
+
padding: 0;
|
|
172
|
+
display: inline;
|
|
173
|
+
}
|
|
174
|
+
|
|
163
175
|
td span.table-link {
|
|
164
176
|
text-decoration: underline;
|
|
165
177
|
cursor: pointer;
|