@cdc/core 4.24.2 → 4.24.4
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-command.svg +3 -0
- package/assets/icon-rotate-left.svg +3 -0
- package/assets/icon-sankey.svg +1 -0
- package/assets/icon-table.svg +1 -0
- package/components/AdvancedEditor.jsx +9 -0
- package/components/DataTable/DataTable.tsx +37 -13
- 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/ExpandCollapse.tsx +22 -16
- package/components/DataTable/components/MapHeader.tsx +10 -5
- package/components/DataTable/helpers/chartCellMatrix.tsx +2 -2
- 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/helpers/mapCellMatrix.tsx +2 -2
- package/components/DataTable/types/TableConfig.ts +7 -7
- package/components/EditorPanel/ColumnsEditor.tsx +312 -0
- package/components/EditorPanel/DataTableEditor.tsx +42 -27
- package/components/Filters.jsx +35 -17
- package/components/Layout/components/Responsive.tsx +184 -0
- package/components/Layout/components/Sidebar/components/Sidebar.tsx +47 -0
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +902 -0
- package/components/Layout/components/Sidebar/index.tsx +3 -0
- package/components/Layout/components/Visualization/index.tsx +79 -0
- package/components/Layout/components/Visualization/visualizations.scss +33 -0
- package/components/Layout/index.tsx +11 -0
- package/components/Layout/styles/editor-grid-view.scss +156 -0
- package/components/Layout/styles/editor-utils.scss +197 -0
- package/components/Layout/styles/editor.scss +144 -0
- package/components/LegendCircle.jsx +4 -3
- package/components/MediaControls.jsx +1 -1
- 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 +8 -6
- package/components/Table/components/Row.tsx +6 -2
- package/components/Table/types/RowType.ts +3 -0
- package/components/Waiting.jsx +11 -1
- package/components/_stories/MultiSelect.stories.tsx +10 -1
- package/components/_stories/NestedDropdown.stories.tsx +58 -0
- package/components/_stories/styles.scss +1 -0
- package/components/createBarElement.jsx +120 -0
- package/components/elements/ScreenReaderText.tsx +8 -0
- package/components/elements/SkipTo.tsx +46 -0
- package/components/managers/DataDesigner.tsx +18 -18
- package/components/ui/Icon.tsx +9 -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 → coveUpdateWorker.ts} +9 -5
- 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 +7 -7
- package/helpers/ver/4.24.3.ts +56 -0
- package/package.json +4 -3
- package/styles/_data-table.scss +8 -13
- package/styles/_global.scss +7 -4
- package/styles/_variables.scss +3 -0
- package/styles/base.scss +4 -14
- package/styles/v2/base/index.scss +1 -1
- package/styles/v2/components/ui/tooltip.scss +0 -21
- package/types/Axis.ts +3 -0
- package/types/BaseVisualizationType.ts +1 -0
- package/types/ConfidenceInterval.ts +1 -0
- package/types/ConfigureData.ts +8 -0
- package/types/DataDescription.ts +9 -0
- package/types/Legend.ts +18 -0
- package/types/Region.ts +10 -0
- package/types/Runtime.ts +2 -0
- package/types/Table.ts +2 -1
- package/types/UpdateFieldFunc.ts +1 -1
- package/types/Visualization.ts +19 -10
- package/components/DataTable/components/SkipNav.tsx +0 -7
- package/helpers/cove/date.js +0 -9
- package/helpers/ver/4.23.js +0 -10
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" fill="currentColor">
|
|
2
|
+
<path d="M404.83356,297.97872H354.67715V214.177h50.15641a107.08854,107.08854,0,1,0,0-214.177C345.64284,0,297.51145,47.97571,297.82294,107.01074v50.46778H214.33287V107.01074A107.09452,107.09452,0,0,0,107.32225,0C47.97572,0,0,47.97571,0,107.01074c0,59.3464,48.28721,107.6336,107.32225,107.16629h50.15628v83.80169H107.32225C47.97572,297.97872,0,345.79862,0,404.83352a107.16644,107.16644,0,1,0,214.33287,0V354.83291h83.49007v50.00061a107.08879,107.08879,0,1,0,107.01062-106.8548ZM354.67715,106.85493a50.2348,50.2348,0,1,1,50.0006,50.46791h-50.0006ZM157.47853,404.83352a50.2348,50.2348,0,1,1-50.0006-50.00061h50.0006Zm0-247.51068h-50.0006a50.2348,50.2348,0,1,1,50.0006-50.46791ZM297.97862,297.97872H214.33287V214.33285h83.64575Zm106.69913,157.3227a50.15888,50.15888,0,0,1-50.0006-50.4679V354.83291h50.0006a50.2348,50.2348,0,1,1,0,100.46851Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" fill="currentColor">
|
|
2
|
+
<path d="M480 256c0 123.4-100.5 223.9-223.9 223.9c-48.84 0-95.17-15.58-134.2-44.86c-14.12-10.59-16.97-30.66-6.375-44.81c10.59-14.12 30.62-16.94 44.81-6.375c27.84 20.91 61 31.94 95.88 31.94C344.3 415.8 416 344.1 416 256s-71.69-159.8-159.8-159.8c-37.46 0-73.09 13.49-101.3 36.64l45.12 45.14c17.01 17.02 4.955 46.1-19.1 46.1H35.17C24.58 224.1 16 215.5 16 204.9V59.04c0-24.04 29.07-36.08 46.07-19.07l47.6 47.63C149.9 52.71 201.5 32.11 256.1 32.11C379.5 32.11 480 132.6 480 256z"/>
|
|
3
|
+
</svg>
|
|
@@ -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>
|
|
@@ -51,6 +51,15 @@ export const AdvancedEditor = ({ loadConfig, state, convertStateToConfig }) => {
|
|
|
51
51
|
<p className='pb-2'>
|
|
52
52
|
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.
|
|
53
53
|
</p>
|
|
54
|
+
<button
|
|
55
|
+
className='btn'
|
|
56
|
+
onClick={() => {
|
|
57
|
+
navigator.clipboard.writeText(configTextboxValue)
|
|
58
|
+
alert('Copied!')
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Copy to Clipboard
|
|
62
|
+
</button>
|
|
54
63
|
<textarea value={configTextboxValue} onChange={event => setConfigTextbox(event.target.value)} />
|
|
55
64
|
<button className='btn full-width' onClick={() => loadConfig(JSON.parse(configTextboxValue))}>
|
|
56
65
|
Apply
|
|
@@ -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,12 +39,12 @@ 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
|
|
46
46
|
tableTitle: string
|
|
47
|
-
viewport:
|
|
47
|
+
viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
48
48
|
vizTitle?: string
|
|
49
49
|
// determines if columns should be wrapped in the table
|
|
50
50
|
wrapColumns?: boolean
|
|
@@ -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,26 +181,29 @@ 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
|
-
<
|
|
176
|
-
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
|
|
187
|
+
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
188
|
+
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} fontSize={config.fontSize} viewport={viewport} />
|
|
177
189
|
<div className='table-container' style={limitHeight}>
|
|
178
190
|
<Table
|
|
191
|
+
viewport={viewport}
|
|
179
192
|
wrapColumns={wrapColumns}
|
|
180
|
-
childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType })}
|
|
193
|
+
childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType, viewport })}
|
|
181
194
|
tableName={config.type}
|
|
182
195
|
caption={caption}
|
|
183
196
|
stickyHeader
|
|
184
197
|
hasRowType={hasRowType}
|
|
185
198
|
headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={_runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
|
|
186
199
|
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
|
+
fontSize={config.fontSize}
|
|
187
201
|
/>
|
|
188
202
|
|
|
189
203
|
{/* REGION Data Table */}
|
|
190
204
|
{noRelativeRegions && config.regions && config.regions.length > 0 && config.visualizationType !== 'Box Plot' && (
|
|
191
205
|
<Table
|
|
206
|
+
viewport={viewport}
|
|
192
207
|
wrapColumns={wrapColumns}
|
|
193
208
|
childrenMatrix={regionCellMatrix({ config })}
|
|
194
209
|
tableName={config.visualizationType}
|
|
@@ -201,10 +216,14 @@ const DataTable = (props: DataTableProps) => {
|
|
|
201
216
|
</tr>
|
|
202
217
|
}
|
|
203
218
|
tableOptions={{ className: 'region-table data-table' }}
|
|
219
|
+
fontSize={config.fontSize}
|
|
204
220
|
/>
|
|
205
221
|
)}
|
|
206
222
|
</div>
|
|
207
223
|
</section>
|
|
224
|
+
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
225
|
+
Skipped data table.
|
|
226
|
+
</div>
|
|
208
227
|
</ErrorBoundary>
|
|
209
228
|
)
|
|
210
229
|
} else {
|
|
@@ -212,10 +231,11 @@ const DataTable = (props: DataTableProps) => {
|
|
|
212
231
|
return (
|
|
213
232
|
<ErrorBoundary component='DataTable'>
|
|
214
233
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
215
|
-
<
|
|
234
|
+
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
216
235
|
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
|
|
217
236
|
<div className='table-container' style={limitHeight}>
|
|
218
237
|
<Table
|
|
238
|
+
viewport={viewport}
|
|
219
239
|
wrapColumns={wrapColumns}
|
|
220
240
|
childrenMatrix={boxplotCellMatrix({ rows: tableData, config })}
|
|
221
241
|
tableName={config.visualizationType}
|
|
@@ -223,9 +243,13 @@ const DataTable = (props: DataTableProps) => {
|
|
|
223
243
|
stickyHeader
|
|
224
244
|
headContent={<BoxplotHeader categories={config.boxplot.categories} />}
|
|
225
245
|
tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`, 'aria-live': 'assertive', 'aria-rowcount': 11, hidden: !expanded }}
|
|
246
|
+
fontSize={config.fontSize}
|
|
226
247
|
/>
|
|
227
248
|
</div>
|
|
228
249
|
</section>
|
|
250
|
+
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
251
|
+
Skipped data table.
|
|
252
|
+
</div>
|
|
229
253
|
</ErrorBoundary>
|
|
230
254
|
)
|
|
231
255
|
}
|
|
@@ -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,21 +1,27 @@
|
|
|
1
1
|
import Icon from '../../ui/Icon'
|
|
2
2
|
|
|
3
|
-
const ExpandCollapse = ({ expanded, setExpanded, tableTitle }) =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
const ExpandCollapse = ({ expanded, setExpanded, tableTitle, fontSize, viewport }) => {
|
|
4
|
+
const fontSizes = { small: 16, medium: 18, large: 20 }
|
|
5
|
+
const titleFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[fontSize]}px`
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
style={{ fontSize: titleFontSize }}
|
|
9
|
+
role='button'
|
|
10
|
+
className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
11
|
+
onClick={() => {
|
|
12
12
|
setExpanded(!expanded)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
}}
|
|
14
|
+
tabIndex={0}
|
|
15
|
+
onKeyDown={e => {
|
|
16
|
+
if (e.keyCode === 13) {
|
|
17
|
+
setExpanded(!expanded)
|
|
18
|
+
}
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<Icon display={expanded ? 'minus' : 'plus'} base />
|
|
22
|
+
{tableTitle}
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
export default ExpandCollapse
|
|
@@ -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
|
})}
|
|
@@ -15,7 +15,7 @@ type ChartRowsProps = DataTableProps & {
|
|
|
15
15
|
hasRowType?: boolean
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
|
|
18
|
+
const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
|
|
19
19
|
const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
|
|
20
20
|
|
|
21
21
|
const dataSeriesColumnsSorted = () => {
|
|
@@ -79,7 +79,7 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
|
|
|
79
79
|
config.visualizationType !== 'Pie'
|
|
80
80
|
? [
|
|
81
81
|
<>
|
|
82
|
-
{colorScale && colorScale(seriesName) && <LegendCircle fill={colorScale(seriesName)} />}
|
|
82
|
+
{colorScale && colorScale(seriesName) && <LegendCircle viewport={viewport} fill={colorScale(seriesName)} />}
|
|
83
83
|
{seriesName}
|
|
84
84
|
</>
|
|
85
85
|
]
|
|
@@ -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
|
|
@@ -7,7 +7,7 @@ type MapRowsProps = DataTableProps & {
|
|
|
7
7
|
rows: string[]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode }: MapRowsProps): ReactNode[][] => {
|
|
10
|
+
const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode, viewport }: MapRowsProps): ReactNode[][] => {
|
|
11
11
|
return rows.map(row =>
|
|
12
12
|
Object.keys(columns)
|
|
13
13
|
.filter(column => columns[column].dataTable === true && columns[column].name)
|
|
@@ -31,7 +31,7 @@ const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, di
|
|
|
31
31
|
}
|
|
32
32
|
cellValue = (
|
|
33
33
|
<div className='col-12'>
|
|
34
|
-
<LegendCircle fill={legendColor[0]} />
|
|
34
|
+
<LegendCircle viewport={viewport} fill={legendColor[0]} />
|
|
35
35
|
<CellAnchor markup={labelValue} row={rowObj} columns={columns} navigationHandler={navigationHandler} mapZoomHandler={mapZoomHandler} />
|
|
36
36
|
</div>
|
|
37
37
|
)
|
|
@@ -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,15 @@ 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[]
|
|
27
|
+
fontSize: 'small' | 'medium' | 'large'
|
|
28
28
|
}
|