@cdc/core 4.25.2-25 → 4.25.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/components/AdvancedEditor/AdvancedEditor.tsx +11 -9
- package/components/DataTable/DataTable.tsx +48 -26
- package/components/DataTable/components/ChartHeader.tsx +1 -1
- package/components/DataTable/helpers/getChartCellValue.ts +11 -5
- package/components/DataTable/helpers/getDataSeriesColumns.ts +7 -3
- package/components/DataTable/helpers/mapCellMatrix.tsx +64 -33
- package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +80 -0
- package/components/EditorPanel/DataTableEditor.tsx +28 -18
- package/components/EditorPanel/Inputs.tsx +2 -1
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +23 -0
- package/components/Filters/Filters.tsx +20 -8
- package/components/Layout/components/Visualization/visualizations.scss +1 -1
- package/components/MediaControls.jsx +14 -7
- package/components/NestedDropdown/NestedDropdown.tsx +5 -1
- package/components/NestedDropdown/nesteddropdown.styles.css +8 -4
- package/components/elements/Button.jsx +4 -2
- package/dist/cove-main.css +98 -151
- package/dist/cove-main.css.map +1 -1
- package/helpers/DataTransform.ts +2 -2
- package/helpers/addValuesToFilters.ts +1 -1
- package/helpers/coveUpdateWorker.ts +12 -7
- package/helpers/formatConfigBeforeSave.ts +30 -8
- package/helpers/isRightAlignedTableValue.js +5 -1
- package/helpers/isSolr.ts +13 -0
- package/helpers/labelHash.ts +21 -0
- package/helpers/pivotData.ts +14 -7
- package/helpers/tests/formatConfigBeforeSave.test.ts +68 -0
- package/helpers/tests/pivotData.test.ts +23 -19
- package/helpers/ver/4.25.3.ts +20 -0
- package/helpers/ver/tests/versionNeedsUpdate.test.ts +28 -0
- package/package.json +2 -2
- package/styles/_global-variables.scss +2 -1
- package/styles/_global.scss +18 -9
- package/styles/base.scss +42 -0
- package/styles/filters.scss +5 -11
- package/styles/v2/components/button.scss +48 -12
- package/styles/v2/themes/_color-definitions.scss +1 -4
- package/types/General.ts +0 -1
- package/types/Table.ts +2 -0
- package/helpers/isSolr.js +0 -13
|
@@ -35,20 +35,22 @@ export const AdvancedEditor = ({ loadConfig, config, convertStateToConfig, onExp
|
|
|
35
35
|
}, [config])
|
|
36
36
|
|
|
37
37
|
const typeLookup = {
|
|
38
|
-
chart: ['Charts', 'https://www.cdc.gov/
|
|
39
|
-
dashboard: ['Dashboard', 'https://www.cdc.gov/
|
|
40
|
-
map: ['Maps', 'https://www.cdc.gov/
|
|
41
|
-
'markup-include': [
|
|
42
|
-
'Markup Include',
|
|
43
|
-
'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/Markup-Include.html',
|
|
44
|
-
<MarkupIncludeIcon />
|
|
45
|
-
]
|
|
38
|
+
chart: ['Charts', 'https://www.cdc.gov/cove/index.html', <ChartIcon />],
|
|
39
|
+
dashboard: ['Dashboard', 'https://www.cdc.gov/cove/index.html', <ChartIcon />],
|
|
40
|
+
map: ['Maps', 'https://www.cdc.gov/cove/index.html', <MapIcon />],
|
|
41
|
+
'markup-include': ['Markup Include', 'https://www.cdc.gov/cove/index.html', <MarkupIncludeIcon />]
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
if (!config.type) return <></>
|
|
49
45
|
return (
|
|
50
46
|
<>
|
|
51
|
-
<a
|
|
47
|
+
<a
|
|
48
|
+
href={typeLookup[config.type][1]}
|
|
49
|
+
target='_blank'
|
|
50
|
+
rel='noopener noreferrer'
|
|
51
|
+
className='guidance-link'
|
|
52
|
+
style={{ cursor: 'pointer !important' }}
|
|
53
|
+
>
|
|
52
54
|
{typeLookup[config.type][2]}
|
|
53
55
|
<div>
|
|
54
56
|
<span className='heading-3'>Get Help with {typeLookup[config.type][0]}</span>
|
|
@@ -11,7 +11,7 @@ import BoxplotHeader from './components/BoxplotHeader'
|
|
|
11
11
|
import MapHeader from './components/MapHeader'
|
|
12
12
|
import SkipTo from '../elements/SkipTo'
|
|
13
13
|
import ExpandCollapse from './components/ExpandCollapse'
|
|
14
|
-
import mapCellMatrix from './helpers/mapCellMatrix'
|
|
14
|
+
import mapCellMatrix, { getMapRowData } from './helpers/mapCellMatrix'
|
|
15
15
|
import Table from '../Table'
|
|
16
16
|
import chartCellMatrix from './helpers/chartCellMatrix'
|
|
17
17
|
import regionCellMatrix from './helpers/regionCellMatrix'
|
|
@@ -24,6 +24,7 @@ import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
|
|
|
24
24
|
import isRightAlignedTableValue from '@cdc/core/helpers/isRightAlignedTableValue'
|
|
25
25
|
import './data-table.css'
|
|
26
26
|
import _ from 'lodash'
|
|
27
|
+
import { getDataSeriesColumns } from './helpers/getDataSeriesColumns'
|
|
27
28
|
|
|
28
29
|
export type DataTableProps = {
|
|
29
30
|
applyLegendToRow?: Function
|
|
@@ -32,9 +33,9 @@ export type DataTableProps = {
|
|
|
32
33
|
config: TableConfig
|
|
33
34
|
dataConfig?: Object
|
|
34
35
|
defaultSortBy?: string
|
|
35
|
-
displayGeoName?:
|
|
36
|
+
displayGeoName?: (row: string) => string
|
|
36
37
|
expandDataTable: boolean
|
|
37
|
-
formatLegendLocation?:
|
|
38
|
+
formatLegendLocation?: (row: string) => string
|
|
38
39
|
groupBy?: string
|
|
39
40
|
headerColor?: string
|
|
40
41
|
imageRef?: string
|
|
@@ -45,8 +46,7 @@ export type DataTableProps = {
|
|
|
45
46
|
outerContainerRef?: Function
|
|
46
47
|
rawData: Object[]
|
|
47
48
|
runtimeData: Object[] & Record<string, Object>
|
|
48
|
-
setFilteredCountryCode?:
|
|
49
|
-
showDownloadButton?: boolean
|
|
49
|
+
setFilteredCountryCode?: string // used for Maps only
|
|
50
50
|
tabbingId: string
|
|
51
51
|
tableTitle: string
|
|
52
52
|
viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
@@ -61,6 +61,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
61
61
|
config,
|
|
62
62
|
dataConfig,
|
|
63
63
|
defaultSortBy,
|
|
64
|
+
displayGeoName,
|
|
64
65
|
tableTitle,
|
|
65
66
|
vizTitle,
|
|
66
67
|
rawData,
|
|
@@ -75,19 +76,15 @@ const DataTable = (props: DataTableProps) => {
|
|
|
75
76
|
} = props
|
|
76
77
|
const runtimeData = useMemo(() => {
|
|
77
78
|
const data = removeNullColumns(parentRuntimeData)
|
|
78
|
-
|
|
79
|
+
const { columnName, valueColumns } = config.table.pivot || {}
|
|
80
|
+
if (columnName && valueColumns) {
|
|
79
81
|
const excludeColumns = Object.values(config.columns || {})
|
|
80
82
|
.filter(column => column.dataTable === false)
|
|
81
83
|
.map(col => col.name)
|
|
82
|
-
|
|
83
|
-
if (columnName && valueColumns) {
|
|
84
|
-
// remove excluded columns so that they aren't included in the pivot calculation
|
|
85
|
-
const _data = data.map(row => _.omit(row, excludeColumns))
|
|
86
|
-
return pivotData(_data, columnName, valueColumns)
|
|
87
|
-
}
|
|
84
|
+
return pivotData(data, columnName, valueColumns, excludeColumns)
|
|
88
85
|
}
|
|
89
86
|
return data
|
|
90
|
-
}, [parentRuntimeData, config.table.pivot?.columnName, config.table.pivot?.
|
|
87
|
+
}, [parentRuntimeData, config.table.pivot?.columnName, config.table.pivot?.valueColumns])
|
|
91
88
|
|
|
92
89
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
93
90
|
const [sortBy, setSortBy] = useState<any>({
|
|
@@ -208,11 +205,13 @@ const DataTable = (props: DataTableProps) => {
|
|
|
208
205
|
[config.runtime?.seriesKeys]) // eslint-disable-line
|
|
209
206
|
|
|
210
207
|
const hasNoData = runtimeData.length === 0
|
|
211
|
-
|
|
212
208
|
const getClassNames = (): string => {
|
|
213
209
|
const classes = ['data-table-container']
|
|
214
210
|
|
|
215
|
-
|
|
211
|
+
const hasDownloadLinkAbove = config.table.download && !config.table.showDownloadLinkBelow
|
|
212
|
+
const isStandaloneTable = config.type === 'table'
|
|
213
|
+
|
|
214
|
+
if (!hasDownloadLinkAbove && !isStandaloneTable) {
|
|
216
215
|
classes.push('mt-4')
|
|
217
216
|
}
|
|
218
217
|
|
|
@@ -228,24 +227,48 @@ const DataTable = (props: DataTableProps) => {
|
|
|
228
227
|
|
|
229
228
|
if (config.visualizationType !== 'Box Plot') {
|
|
230
229
|
const getDownloadData = () => {
|
|
230
|
+
const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
|
|
231
|
+
const sharedFilterColumns = config.table?.sharedFilterColumns || []
|
|
232
|
+
const vizFilterColumns = (config.filters || []).map(filter => filter.columnName)
|
|
233
|
+
const filterColumns = [...sharedFilterColumns, ...vizFilterColumns]
|
|
234
|
+
const visibleData =
|
|
235
|
+
config.type === 'map'
|
|
236
|
+
? getMapRowData(
|
|
237
|
+
rows,
|
|
238
|
+
columns,
|
|
239
|
+
config,
|
|
240
|
+
formatLegendLocation,
|
|
241
|
+
runtimeData as Record<string, Object>,
|
|
242
|
+
displayGeoName,
|
|
243
|
+
filterColumns
|
|
244
|
+
)
|
|
245
|
+
: runtimeData.map(d => {
|
|
246
|
+
return _.pick(d, [...filterColumns, ...dataSeriesColumns])
|
|
247
|
+
})
|
|
248
|
+
const csvData = config.table?.downloadVisibleDataOnly ? visibleData : rawData
|
|
249
|
+
|
|
231
250
|
// only use fullGeoName on County maps and no other
|
|
232
251
|
if (config.general?.geoType === 'us-county') {
|
|
233
252
|
// Add column for full Geo name along with State
|
|
234
|
-
return
|
|
253
|
+
return csvData.map(row => ({ FullGeoName: formatLegendLocation(row[config.columns.geo.name]), ...row }))
|
|
235
254
|
} else {
|
|
236
|
-
return
|
|
255
|
+
return csvData
|
|
237
256
|
}
|
|
238
257
|
}
|
|
239
258
|
|
|
240
|
-
const getMediaControlsClasses = belowTable => {
|
|
259
|
+
const getMediaControlsClasses = (belowTable, hasDownloadLink) => {
|
|
241
260
|
const classes = ['download-links']
|
|
242
261
|
if (!belowTable) {
|
|
243
|
-
|
|
262
|
+
if (hasDownloadLink) {
|
|
263
|
+
classes.push('mt-4', 'mb-2')
|
|
264
|
+
}
|
|
244
265
|
const isLegendOnBottom = config?.legend?.position === 'bottom' || isLegendWrapViewport(viewport)
|
|
245
266
|
if (config.brush?.active && !isLegendOnBottom) classes.push('brush-active')
|
|
246
267
|
if (config.brush?.active && config.legend.hide) classes.push('brush-active')
|
|
247
268
|
} else {
|
|
248
|
-
|
|
269
|
+
if (hasDownloadLink) {
|
|
270
|
+
classes.push('mt-2')
|
|
271
|
+
}
|
|
249
272
|
}
|
|
250
273
|
return classes
|
|
251
274
|
}
|
|
@@ -267,10 +290,11 @@ const DataTable = (props: DataTableProps) => {
|
|
|
267
290
|
: {}
|
|
268
291
|
|
|
269
292
|
const TableMediaControls = ({ belowTable }) => {
|
|
293
|
+
const hasDownloadLink = config.table.download
|
|
270
294
|
return (
|
|
271
|
-
<MediaControls.Section classes={getMediaControlsClasses(belowTable)}>
|
|
295
|
+
<MediaControls.Section classes={getMediaControlsClasses(belowTable, hasDownloadLink)}>
|
|
272
296
|
<MediaControls.Link config={config} dashboardDataConfig={dataConfig} />
|
|
273
|
-
{
|
|
297
|
+
{hasDownloadLink && (
|
|
274
298
|
<DownloadButton
|
|
275
299
|
rawData={getDownloadData()}
|
|
276
300
|
fileName={`${vizTitle || 'data-table'}.csv`}
|
|
@@ -283,7 +307,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
283
307
|
|
|
284
308
|
return (
|
|
285
309
|
<ErrorBoundary component='DataTable'>
|
|
286
|
-
{
|
|
310
|
+
{!config.table.showDownloadLinkBelow && <TableMediaControls />}
|
|
287
311
|
<section id={tabbingId.replace('#', '')} className={getClassNames()} aria-label={accessibilityLabel}>
|
|
288
312
|
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
289
313
|
{config.table.collapsible !== false && (
|
|
@@ -357,9 +381,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
357
381
|
)}
|
|
358
382
|
</div>
|
|
359
383
|
</section>
|
|
360
|
-
{config.
|
|
361
|
-
<TableMediaControls belowTable={true} />
|
|
362
|
-
)}
|
|
384
|
+
{config.table.showDownloadLinkBelow && <TableMediaControls belowTable={true} />}
|
|
363
385
|
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
364
386
|
Skipped data table.
|
|
365
387
|
</div>
|
|
@@ -54,7 +54,7 @@ const ChartHeader = ({
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const ColumnHeadingText = ({ column, text, config }) => {
|
|
57
|
-
if (text === '
|
|
57
|
+
if (text === '_pivotedFrom') return ''
|
|
58
58
|
let notApplicableText = 'Not Applicable'
|
|
59
59
|
if (text === '__series__' && config.table.indexLabel) return `${config.table.indexLabel} `
|
|
60
60
|
if (text === '__series__' && !config.table.indexLabel)
|
|
@@ -2,16 +2,22 @@ import { parseDate, formatDate } from '@cdc/core/helpers/cove/date'
|
|
|
2
2
|
import { formatNumber } from '../../../helpers/cove/number'
|
|
3
3
|
import { TableConfig } from '../types/TableConfig'
|
|
4
4
|
|
|
5
|
+
const isPivotColumn = (columnName, config) => {
|
|
6
|
+
const tableHasPivotColumnConfigured = config.table.pivot?.valueColumns?.length
|
|
7
|
+
const originalColumnNames = Object.keys(config.data?.[0] || {})
|
|
8
|
+
const columnIsPivot = originalColumnNames.length && !originalColumnNames.includes(columnName)
|
|
9
|
+
return tableHasPivotColumnConfigured && columnIsPivot
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
// if its additional column, return formatting params
|
|
6
|
-
const isAdditionalColumn = (column, config) => {
|
|
7
|
-
|
|
13
|
+
const isAdditionalColumn = (column: string, config, rowData) => {
|
|
14
|
+
const columnName = isPivotColumn(column, config) ? rowData._pivotedFrom : column
|
|
8
15
|
let formattingParams = {}
|
|
9
16
|
const { columns } = config
|
|
10
17
|
if (columns) {
|
|
11
18
|
Object.keys(columns).forEach(keycol => {
|
|
12
19
|
const col = columns[keycol]
|
|
13
|
-
if (col.name ===
|
|
14
|
-
inthere = true
|
|
20
|
+
if (col.name === columnName) {
|
|
15
21
|
formattingParams = {
|
|
16
22
|
addColPrefix: col.prefix,
|
|
17
23
|
addColSuffix: col.suffix,
|
|
@@ -54,7 +60,7 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
|
|
|
54
60
|
if (rightSeriesItem.dataKey === column) resolvedAxis = 'right'
|
|
55
61
|
})
|
|
56
62
|
|
|
57
|
-
let addColParams = isAdditionalColumn(column, config)
|
|
63
|
+
let addColParams = isAdditionalColumn(column, config, rowObj)
|
|
58
64
|
if (addColParams) {
|
|
59
65
|
cellValue = config.dataFormat
|
|
60
66
|
? formatNumber(runtimeData[row][column], resolvedAxis, false, config, addColParams)
|
|
@@ -57,9 +57,13 @@ export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, r
|
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
tmpSeriesColumns.sort((a, b) => {
|
|
60
|
-
if (a === '
|
|
61
|
-
if (b === '
|
|
60
|
+
if (a === '_pivotedFrom') return -1
|
|
61
|
+
if (b === '_pivotedFrom') return 1
|
|
62
62
|
return columnOrderingHash[a] - columnOrderingHash[b]
|
|
63
63
|
})
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
return tmpSeriesColumns.filter(colName => {
|
|
66
|
+
// remove metadata added by pivotData function
|
|
67
|
+
return colName !== '_pivotedFrom'
|
|
68
|
+
})
|
|
65
69
|
}
|
|
@@ -3,11 +3,70 @@ import CellAnchor from '../components/CellAnchor'
|
|
|
3
3
|
import { DataTableProps } from '../DataTable'
|
|
4
4
|
import { ReactNode } from 'react'
|
|
5
5
|
import { displayDataAsText } from '@cdc/core/helpers/displayDataAsText'
|
|
6
|
+
import _ from 'lodash'
|
|
6
7
|
|
|
7
8
|
type MapRowsProps = DataTableProps & {
|
|
8
9
|
rows: string[]
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
const getGeoLabel = (config, row, formatLegendLocation, displayGeoName) => {
|
|
13
|
+
const { geoType, type } = config.general
|
|
14
|
+
let labelValue
|
|
15
|
+
if (!['single-state', 'us-county'].includes(geoType) || type === 'us-geocode') {
|
|
16
|
+
labelValue = displayGeoName(row)
|
|
17
|
+
labelValue = String(labelValue).startsWith('region') ? _.capitalize(labelValue) : labelValue
|
|
18
|
+
} else {
|
|
19
|
+
labelValue = formatLegendLocation(row)
|
|
20
|
+
}
|
|
21
|
+
return labelValue
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const getDataValue = (config, rowData, column) => {
|
|
25
|
+
// check for special classes
|
|
26
|
+
let specialValFound = ''
|
|
27
|
+
let columnName = config.columns[column]?.name
|
|
28
|
+
const { specialClasses } = config.legend
|
|
29
|
+
if (specialClasses && specialClasses.length && typeof specialClasses[0] === 'object') {
|
|
30
|
+
specialClasses.forEach(specialClass => {
|
|
31
|
+
if (specialClass.key === columnName) {
|
|
32
|
+
if (String(rowData[specialClass.key]) === specialClass.value) {
|
|
33
|
+
specialValFound = specialClass.label
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
return specialValFound || rowData[columnName]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const getMapRowData = (
|
|
42
|
+
rows: string[],
|
|
43
|
+
columns: Record<string, { label; name?; dataTable }>,
|
|
44
|
+
config: Record<string, Object>,
|
|
45
|
+
formatLegendLocation: (row: string) => string,
|
|
46
|
+
runtimeData: Record<string, Object>,
|
|
47
|
+
displayGeoName: (row: string) => string,
|
|
48
|
+
filterColumns: string[]
|
|
49
|
+
) => {
|
|
50
|
+
return rows.map((row: string) => {
|
|
51
|
+
const dataRow = {}
|
|
52
|
+
;[
|
|
53
|
+
...filterColumns,
|
|
54
|
+
...Object.keys(columns).filter(column => columns[column].dataTable === true && columns[column].name)
|
|
55
|
+
].map(column => {
|
|
56
|
+
const label = columns[column]?.label || columns[column]?.name || column
|
|
57
|
+
if (column === 'geo') {
|
|
58
|
+
dataRow[label] = getGeoLabel(config, row, formatLegendLocation, displayGeoName)
|
|
59
|
+
} else if (filterColumns.includes(column)) {
|
|
60
|
+
dataRow[label] = runtimeData[row][column]
|
|
61
|
+
} else {
|
|
62
|
+
const dataValue = getDataValue(config, runtimeData[row], column)
|
|
63
|
+
dataRow[label] = displayDataAsText(dataValue, column, config)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
return dataRow
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
11
70
|
const mapCellArray = ({
|
|
12
71
|
rows,
|
|
13
72
|
columns,
|
|
@@ -23,30 +82,15 @@ const mapCellArray = ({
|
|
|
23
82
|
Object.keys(columns)
|
|
24
83
|
.filter(column => columns[column].dataTable === true && columns[column].name)
|
|
25
84
|
.map(column => {
|
|
26
|
-
let cellValue
|
|
27
|
-
|
|
28
85
|
if (column === 'geo') {
|
|
29
86
|
const rowObj = runtimeData[row]
|
|
30
87
|
const legendColor = applyLegendToRow(rowObj)
|
|
31
|
-
|
|
32
|
-
let labelValue
|
|
88
|
+
const labelValue = getGeoLabel(config, row, formatLegendLocation, displayGeoName)
|
|
33
89
|
const mapZoomHandler =
|
|
34
90
|
config.general.type === 'bubble' && config.general.allowMapZoom && config.general.geoType === 'world'
|
|
35
91
|
? () => setFilteredCountryCode(row)
|
|
36
92
|
: undefined
|
|
37
|
-
|
|
38
|
-
(config.general.geoType !== 'single-state' && config.general.geoType !== 'us-county') ||
|
|
39
|
-
config.general.type === 'us-geocode'
|
|
40
|
-
) {
|
|
41
|
-
const capitalize = str => {
|
|
42
|
-
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
43
|
-
}
|
|
44
|
-
labelValue = displayGeoName(row)
|
|
45
|
-
labelValue = String(labelValue).startsWith('region') ? capitalize(labelValue) : labelValue
|
|
46
|
-
} else {
|
|
47
|
-
labelValue = formatLegendLocation(row)
|
|
48
|
-
}
|
|
49
|
-
cellValue = (
|
|
93
|
+
return (
|
|
50
94
|
<div className='col-12'>
|
|
51
95
|
<LegendShape fill={legendColor[0]} />
|
|
52
96
|
<CellAnchor
|
|
@@ -59,23 +103,10 @@ const mapCellArray = ({
|
|
|
59
103
|
</div>
|
|
60
104
|
)
|
|
61
105
|
} else {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const { specialClasses } = config.legend
|
|
66
|
-
if (specialClasses && specialClasses.length && typeof specialClasses[0] === 'object') {
|
|
67
|
-
specialClasses.forEach(specialClass => {
|
|
68
|
-
if (specialClass.key === columnName) {
|
|
69
|
-
if (String(runtimeData[row][specialClass.key]) === specialClass.value) {
|
|
70
|
-
specialValFound = specialClass.label
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
cellValue = displayDataAsText(specialValFound || runtimeData[row][columnName], column, config)
|
|
106
|
+
const rowData = runtimeData[row]
|
|
107
|
+
const dataValue = getDataValue(config, rowData, column)
|
|
108
|
+
return displayDataAsText(dataValue, column, config)
|
|
76
109
|
}
|
|
77
|
-
|
|
78
|
-
return cellValue
|
|
79
110
|
})
|
|
80
111
|
)
|
|
81
112
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getMapRowData } from '../mapCellMatrix'
|
|
3
|
+
|
|
4
|
+
describe('getMapRowData', () => {
|
|
5
|
+
const columns = {
|
|
6
|
+
geo: { dataTable: true, name: 'geo', label: 'Geo' },
|
|
7
|
+
column1: { dataTable: true, name: 'column1', label: 'Column 1' },
|
|
8
|
+
column2: { dataTable: true, name: 'column2', label: 'Column 2' }
|
|
9
|
+
}
|
|
10
|
+
const config = {
|
|
11
|
+
general: { geoType: 'us-state', type: 'map' },
|
|
12
|
+
columns: {
|
|
13
|
+
geo: { name: 'geo' },
|
|
14
|
+
column1: { name: 'column1' },
|
|
15
|
+
column2: { name: 'column2' }
|
|
16
|
+
},
|
|
17
|
+
legend: { specialClasses: [] }
|
|
18
|
+
}
|
|
19
|
+
const formatLegendLocation = row => row
|
|
20
|
+
const rows = ['row2', 'row1']
|
|
21
|
+
const runtimeData = {
|
|
22
|
+
row1: { geo: 'region1', column1: 'data1', column2: 'data2', somethingElse: 'data5' },
|
|
23
|
+
row2: { geo: 'region2', column1: 'data3', column2: 'data4', somethingElse: 'data6' }
|
|
24
|
+
}
|
|
25
|
+
const displayGeoName = row => `displayGeoName -> ${row}`
|
|
26
|
+
const sharedFilterColumns = ['somethingElse']
|
|
27
|
+
|
|
28
|
+
it('should map rows to data rows correctly', () => {
|
|
29
|
+
const result = getMapRowData(
|
|
30
|
+
rows,
|
|
31
|
+
columns,
|
|
32
|
+
config,
|
|
33
|
+
formatLegendLocation,
|
|
34
|
+
runtimeData,
|
|
35
|
+
displayGeoName,
|
|
36
|
+
sharedFilterColumns
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual([
|
|
40
|
+
{
|
|
41
|
+
Geo: 'displayGeoName -> row2',
|
|
42
|
+
'Column 1': 'data3',
|
|
43
|
+
'Column 2': 'data4',
|
|
44
|
+
somethingElse: 'data6'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
Geo: 'displayGeoName -> row1',
|
|
48
|
+
'Column 1': 'data1',
|
|
49
|
+
'Column 2': 'data2',
|
|
50
|
+
somethingElse: 'data5'
|
|
51
|
+
}
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
config.general.geoType = 'us-county'
|
|
55
|
+
const result2 = getMapRowData(
|
|
56
|
+
rows,
|
|
57
|
+
columns,
|
|
58
|
+
config,
|
|
59
|
+
formatLegendLocation,
|
|
60
|
+
runtimeData,
|
|
61
|
+
displayGeoName,
|
|
62
|
+
sharedFilterColumns
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
expect(result2).toEqual([
|
|
66
|
+
{
|
|
67
|
+
Geo: 'row2',
|
|
68
|
+
'Column 1': 'data3',
|
|
69
|
+
'Column 2': 'data4',
|
|
70
|
+
somethingElse: 'data6'
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
Geo: 'row1',
|
|
74
|
+
'Column 1': 'data1',
|
|
75
|
+
'Column 2': 'data2',
|
|
76
|
+
somethingElse: 'data5'
|
|
77
|
+
}
|
|
78
|
+
])
|
|
79
|
+
})
|
|
80
|
+
})
|
|
@@ -83,7 +83,7 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
83
83
|
</Tooltip>
|
|
84
84
|
}
|
|
85
85
|
/>
|
|
86
|
-
{config.type !== 'table'
|
|
86
|
+
{config.type !== 'table' && (
|
|
87
87
|
<CheckBox
|
|
88
88
|
value={config.table.show}
|
|
89
89
|
fieldName='show'
|
|
@@ -108,15 +108,6 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
108
108
|
</Tooltip>
|
|
109
109
|
}
|
|
110
110
|
/>
|
|
111
|
-
) : (
|
|
112
|
-
<CheckBox
|
|
113
|
-
value={config.general?.showDownloadButton}
|
|
114
|
-
fieldName='showDownloadButton'
|
|
115
|
-
label='Show Download CSV link'
|
|
116
|
-
section='general'
|
|
117
|
-
updateField={updateField}
|
|
118
|
-
className='column-heading'
|
|
119
|
-
/>
|
|
120
111
|
)}
|
|
121
112
|
|
|
122
113
|
{config.visualizationType !== 'Box Plot' && config.type !== 'table' && (
|
|
@@ -234,6 +225,33 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
234
225
|
updateField={updateField}
|
|
235
226
|
/>
|
|
236
227
|
)}
|
|
228
|
+
<CheckBox
|
|
229
|
+
value={config.table.download}
|
|
230
|
+
fieldName='download'
|
|
231
|
+
label='Show Download CSV Link'
|
|
232
|
+
section='table'
|
|
233
|
+
updateField={updateField}
|
|
234
|
+
/>
|
|
235
|
+
{config.table.download && (
|
|
236
|
+
<>
|
|
237
|
+
<CheckBox
|
|
238
|
+
value={config.table.showDownloadLinkBelow}
|
|
239
|
+
fieldName='showDownloadLinkBelow'
|
|
240
|
+
className='ms-4'
|
|
241
|
+
label='Show Link Below Table'
|
|
242
|
+
section='table'
|
|
243
|
+
updateField={updateField}
|
|
244
|
+
/>
|
|
245
|
+
<CheckBox
|
|
246
|
+
value={config.table.downloadVisibleDataOnly}
|
|
247
|
+
fieldName='downloadVisibleDataOnly'
|
|
248
|
+
className='ms-4'
|
|
249
|
+
label='Download only visible data'
|
|
250
|
+
section='table'
|
|
251
|
+
updateField={updateField}
|
|
252
|
+
/>
|
|
253
|
+
</>
|
|
254
|
+
)}
|
|
237
255
|
{isDashboard && config.type !== 'table' && (
|
|
238
256
|
<CheckBox
|
|
239
257
|
value={config.table.showDataTableLink}
|
|
@@ -261,14 +279,6 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
261
279
|
updateField={updateField}
|
|
262
280
|
/>
|
|
263
281
|
)}
|
|
264
|
-
|
|
265
|
-
<CheckBox
|
|
266
|
-
value={config.table.showDownloadLinkBelow}
|
|
267
|
-
fieldName='showDownloadLinkBelow'
|
|
268
|
-
label='Show Download Link Below Table'
|
|
269
|
-
section='table'
|
|
270
|
-
updateField={updateField}
|
|
271
|
-
/>
|
|
272
282
|
<label>
|
|
273
283
|
<span className='edit-label column-heading'>Table Cell Min Width</span>
|
|
274
284
|
<input
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { memo, useEffect, useState } from 'react'
|
|
2
2
|
import { useDebounce } from 'use-debounce'
|
|
3
|
+
import { DROPDOWN_STYLES } from '../Filters/Filters'
|
|
3
4
|
|
|
4
5
|
export type Input = {
|
|
5
6
|
label: string
|
|
@@ -194,7 +195,7 @@ const Select = memo((props: SelectProps) => {
|
|
|
194
195
|
{tooltip}
|
|
195
196
|
</span>
|
|
196
197
|
<select
|
|
197
|
-
className={`cove-form-select ${required && !value ? 'warning' : ''}`}
|
|
198
|
+
className={`cove-form-select ${required && !value ? 'warning' : ''} ${DROPDOWN_STYLES}`}
|
|
198
199
|
name={fieldName}
|
|
199
200
|
value={value}
|
|
200
201
|
onChange={event => {
|
|
@@ -37,6 +37,15 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
37
37
|
updateField('filters', filterIndex, prop, value)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
const updateFilterDefaultValue = (index, value) => {
|
|
41
|
+
const filters = _.cloneDeep(config.filters)
|
|
42
|
+
const currentFilter = { ...filters[index], orderedValues: filters[index].values }
|
|
43
|
+
currentFilter.defaultValue = value
|
|
44
|
+
currentFilter.active = value
|
|
45
|
+
filters[index] = currentFilter
|
|
46
|
+
updateField(null, null, 'filters', filters)
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
const updateFilterStyle = (index, style: VizFilterStyle) => {
|
|
41
50
|
const filters = _.cloneDeep(config.filters)
|
|
42
51
|
const currentFilter = { ...filters[index], orderedValues: filters[index].values }
|
|
@@ -160,6 +169,20 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
160
169
|
options={filterStyleOptions}
|
|
161
170
|
/>
|
|
162
171
|
|
|
172
|
+
<Select
|
|
173
|
+
value={filter.defaultValue}
|
|
174
|
+
options={
|
|
175
|
+
filter.resetLabel
|
|
176
|
+
? [filter.resetLabel, ...config.filters?.[filterIndex].values]
|
|
177
|
+
: config.filters?.[filterIndex].values
|
|
178
|
+
}
|
|
179
|
+
updateField={(_section, _subSection, _key, value) => {
|
|
180
|
+
updateFilterDefaultValue(filterIndex, value)
|
|
181
|
+
}}
|
|
182
|
+
label='Filter Default Value'
|
|
183
|
+
initial='Select'
|
|
184
|
+
/>
|
|
185
|
+
|
|
163
186
|
{filter.filterStyle !== 'nested-dropdown' ? (
|
|
164
187
|
<>
|
|
165
188
|
<Select
|