@cdc/core 4.23.10-alpha → 4.23.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/assets/icon-deviation-bar.svg +1 -0
- package/components/DataTable/DataTable.tsx +205 -0
- package/components/DataTable/components/BoxplotHeader.tsx +16 -0
- package/components/DataTable/components/CellAnchor.tsx +44 -0
- package/components/DataTable/components/ChartHeader.tsx +91 -0
- package/components/DataTable/components/ExpandCollapse.tsx +21 -0
- package/components/DataTable/components/Icons.tsx +10 -0
- package/components/DataTable/components/MapHeader.tsx +56 -0
- package/components/DataTable/components/SkipNav.tsx +7 -0
- package/components/DataTable/helpers/boxplotCellMatrix.tsx +64 -0
- package/components/DataTable/helpers/chartCellMatrix.tsx +78 -0
- package/components/DataTable/helpers/customSort.ts +55 -0
- package/components/DataTable/helpers/getChartCellValue.ts +55 -0
- package/components/DataTable/helpers/getDataSeriesColumns.ts +28 -0
- package/components/DataTable/helpers/getSeriesName.ts +26 -0
- package/components/DataTable/helpers/mapCellMatrix.tsx +56 -0
- package/components/DataTable/helpers/regionCellMatrix.tsx +13 -0
- package/components/DataTable/helpers/standardizeState.js +76 -0
- package/components/DataTable/index.ts +1 -0
- package/components/DataTable/types/TableConfig.ts +57 -0
- package/components/DownloadButton.tsx +29 -0
- package/components/LegendCircle.jsx +2 -2
- package/components/Table/Table.tsx +49 -0
- package/components/Table/components/Cell.tsx +9 -0
- package/components/Table/components/GroupRow.tsx +16 -0
- package/components/Table/components/Row.tsx +19 -0
- package/components/Table/index.ts +1 -0
- package/components/Table/types/CellMatrix.ts +4 -0
- package/components/_stories/DataTable.stories.tsx +62 -0
- package/components/_stories/Table.stories.tsx +53 -0
- package/components/_stories/_mocks/dashboard_no_filter.json +121 -0
- package/components/_stories/_mocks/example-city-state.json +808 -0
- package/components/_stories/styles.scss +9 -0
- package/components/managers/{DataDesigner.jsx → DataDesigner.tsx} +96 -87
- package/components/ui/Title/Title.scss +95 -0
- package/components/ui/Title/index.tsx +34 -0
- package/components/ui/_stories/Title.stories.tsx +21 -0
- package/helpers/DataTransform.ts +41 -18
- package/helpers/cove/string.ts +11 -0
- package/package.json +2 -2
- package/styles/_data-table.scss +1 -0
- package/styles/heading-colors.scss +0 -3
- package/styles/v2/layout/_component.scss +0 -11
- package/types/Axis.ts +6 -0
- package/types/Color.ts +5 -0
- package/types/ComponentStyles.ts +7 -0
- package/types/ComponentThemes.ts +13 -0
- package/types/EditorColumnProperties.ts +8 -0
- package/types/Runtime.ts +9 -0
- package/types/Series.ts +1 -0
- package/types/Visualization.ts +21 -0
- package/components/DataTable.jsx +0 -759
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parseDate, formatDate } from '@cdc/core/helpers/cove/date'
|
|
2
|
+
import { formatNumber } from '@cdc/core/helpers/cove/number'
|
|
3
|
+
|
|
4
|
+
// if its additional column, return formatting params
|
|
5
|
+
const isAdditionalColumn = (column, config) => {
|
|
6
|
+
let inthere = false
|
|
7
|
+
let formattingParams = {}
|
|
8
|
+
const { columns } = config
|
|
9
|
+
if (columns) {
|
|
10
|
+
Object.keys(columns).forEach(keycol => {
|
|
11
|
+
const col = columns[keycol]
|
|
12
|
+
if (col.name === column) {
|
|
13
|
+
inthere = true
|
|
14
|
+
formattingParams = {
|
|
15
|
+
addColPrefix: col.prefix,
|
|
16
|
+
addColSuffix: col.suffix,
|
|
17
|
+
addColRoundTo: col.roundToPlace ? col.roundToPlace : '',
|
|
18
|
+
addColCommas: col.commas
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
return formattingParams
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const getChartCellValue = (row, column, config, runtimeData) => {
|
|
27
|
+
const rowObj = runtimeData[row]
|
|
28
|
+
let cellValue // placeholder for formatting below
|
|
29
|
+
let labelValue = rowObj[column] // just raw X axis string
|
|
30
|
+
if (column === config.xAxis?.dataKey) {
|
|
31
|
+
// not the prettiest, but helper functions work nicely here.
|
|
32
|
+
cellValue = config.xAxis?.type === 'date' ? formatDate(config.xAxis?.dateDisplayFormat, parseDate(config.xAxis?.dateParseFormat, labelValue)) : labelValue
|
|
33
|
+
} else {
|
|
34
|
+
let resolvedAxis = 'left'
|
|
35
|
+
let leftAxisItems = config.series ? config.series.filter(item => item?.axis === 'Left') : []
|
|
36
|
+
let rightAxisItems = config.series ? config.series.filter(item => item?.axis === 'Right') : []
|
|
37
|
+
|
|
38
|
+
leftAxisItems.map(leftSeriesItem => {
|
|
39
|
+
if (leftSeriesItem.dataKey === column) resolvedAxis = 'left'
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
rightAxisItems.map(rightSeriesItem => {
|
|
43
|
+
if (rightSeriesItem.dataKey === column) resolvedAxis = 'right'
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
let addColParams = isAdditionalColumn(column, config)
|
|
47
|
+
if (addColParams) {
|
|
48
|
+
cellValue = config.dataFormat ? formatNumber(runtimeData[row][column], resolvedAxis, false, config, addColParams) : runtimeData[row][column]
|
|
49
|
+
} else {
|
|
50
|
+
cellValue = config.dataFormat ? formatNumber(runtimeData[row][column], resolvedAxis, false, config) : runtimeData[row][column]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return cellValue
|
|
55
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const getDataSeriesColumns = (config, isVertical, runtimeData): string[] => {
|
|
2
|
+
let tmpSeriesColumns
|
|
3
|
+
if (config.visualizationType !== 'Pie') {
|
|
4
|
+
tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey] : [] //, ...config.runtime.seriesLabelsAll
|
|
5
|
+
if (config.series) {
|
|
6
|
+
config.series.forEach(element => {
|
|
7
|
+
tmpSeriesColumns.push(element.dataKey)
|
|
8
|
+
})
|
|
9
|
+
} else if (runtimeData && runtimeData.length > 0) {
|
|
10
|
+
tmpSeriesColumns = Object.keys(runtimeData[0])
|
|
11
|
+
}
|
|
12
|
+
} else {
|
|
13
|
+
tmpSeriesColumns = [config.xAxis?.dataKey, config.yAxis?.dataKey] //Object.keys(runtimeData[0])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// then add the additional Columns
|
|
17
|
+
if (config.columns && Object.keys(config.columns).length > 0) {
|
|
18
|
+
Object.keys(config.columns).forEach(function (key) {
|
|
19
|
+
var value = config.columns[key]
|
|
20
|
+
// add if not the index AND it is enabled to be added to data table
|
|
21
|
+
if (value.name !== config.xAxis?.dataKey && value.dataTable === true) {
|
|
22
|
+
tmpSeriesColumns.push(value.name)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return tmpSeriesColumns
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const getLabel = (name, config) => {
|
|
2
|
+
let custLabel = ''
|
|
3
|
+
if (config.columns && Object.keys(config.columns).length > 0) {
|
|
4
|
+
Object.keys(config.columns).forEach(function (key) {
|
|
5
|
+
var tmpColumn = config.columns[key]
|
|
6
|
+
// add if not the index AND it is enabled to be added to data table
|
|
7
|
+
if (tmpColumn.name === name) {
|
|
8
|
+
custLabel = tmpColumn.label
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
return custLabel
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getSeriesName = (column, config) => {
|
|
16
|
+
// If a user sets the name on a series use that.
|
|
17
|
+
let userUpdatedSeriesName = config.series ? config.series.filter(series => series.dataKey === column)?.[0]?.name : ''
|
|
18
|
+
if (userUpdatedSeriesName) return userUpdatedSeriesName
|
|
19
|
+
|
|
20
|
+
if (config.runtimeSeriesLabels && config.runtimeSeriesLabels[column]) return config.runtimeSeriesLabels[column]
|
|
21
|
+
|
|
22
|
+
let custLabel = getLabel(column, config) ? getLabel(column, config) : column
|
|
23
|
+
let text = column === config.xAxis?.dataKey ? config.table.indexLabel : custLabel
|
|
24
|
+
|
|
25
|
+
return text
|
|
26
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
2
|
+
import CellAnchor from '../components/CellAnchor'
|
|
3
|
+
import { DataTableProps } from '../DataTable'
|
|
4
|
+
import { ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
type MapRowsProps = DataTableProps & {
|
|
7
|
+
rows: string[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode }: MapRowsProps): ReactNode[][] => {
|
|
11
|
+
return rows.map(row =>
|
|
12
|
+
Object.keys(columns)
|
|
13
|
+
.filter(column => columns[column].dataTable === true && columns[column].name)
|
|
14
|
+
.map(column => {
|
|
15
|
+
let cellValue
|
|
16
|
+
|
|
17
|
+
if (column === 'geo') {
|
|
18
|
+
const rowObj = runtimeData[row]
|
|
19
|
+
const legendColor = applyLegendToRow(rowObj)
|
|
20
|
+
|
|
21
|
+
let labelValue
|
|
22
|
+
const mapZoomHandler = config.general.type === 'bubble' && config.general.allowMapZoom && config.general.geoType === 'world' ? () => setFilteredCountryCode(row) : undefined
|
|
23
|
+
if ((config.general.geoType !== 'single-state' && config.general.geoType !== 'us-county') || config.general.type === 'us-geocode') {
|
|
24
|
+
labelValue = displayGeoName(row)
|
|
25
|
+
} else {
|
|
26
|
+
labelValue = formatLegendLocation(row)
|
|
27
|
+
}
|
|
28
|
+
cellValue = (
|
|
29
|
+
<div className='col-12'>
|
|
30
|
+
<LegendCircle fill={legendColor[0]} />
|
|
31
|
+
<CellAnchor markup={labelValue} row={rowObj} columns={columns} navigationHandler={navigationHandler} mapZoomHandler={mapZoomHandler} />
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
} else {
|
|
35
|
+
// check for special classes
|
|
36
|
+
let specialValFound = ''
|
|
37
|
+
let columnName = config.columns[column].name
|
|
38
|
+
const { specialClasses } = config.legend
|
|
39
|
+
if (specialClasses && specialClasses.length && typeof specialClasses[0] === 'object') {
|
|
40
|
+
specialClasses.forEach(specialClass => {
|
|
41
|
+
if (specialClass.key === columnName) {
|
|
42
|
+
if (String(runtimeData[row][specialClass.key]) === specialClass.value) {
|
|
43
|
+
specialValFound = specialClass.label
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
cellValue = displayDataAsText(specialValFound || runtimeData[row][columnName], column)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return cellValue
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default mapCellArray
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
const regionCellMatrix = ({ config }): ReactNode[][] => {
|
|
4
|
+
return config.regions.map(region => {
|
|
5
|
+
if (config.visualizationType === 'Box Plot') return []
|
|
6
|
+
if (!Object.keys(region).includes('from') || !Object.keys(region).includes('to')) return []
|
|
7
|
+
// region.from and region.to had formatDate(parseDate()) on it
|
|
8
|
+
// but they returned undefined - removed both for now (TT)
|
|
9
|
+
return [region.label, region.from, region.to]
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default regionCellMatrix
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const states = {
|
|
2
|
+
AL: 'Alabama',
|
|
3
|
+
AK: 'Alaska',
|
|
4
|
+
AS: 'American Samoa',
|
|
5
|
+
AZ: 'Arizona',
|
|
6
|
+
AR: 'Arkansas',
|
|
7
|
+
CA: 'California',
|
|
8
|
+
CO: 'Colorado',
|
|
9
|
+
CT: 'Connecticut',
|
|
10
|
+
DE: 'Delaware',
|
|
11
|
+
DC: 'District Of Columbia',
|
|
12
|
+
FM: 'Federated States Of Micronesia',
|
|
13
|
+
FL: 'Florida',
|
|
14
|
+
GA: 'Georgia',
|
|
15
|
+
GU: 'Guam',
|
|
16
|
+
HI: 'Hawaii',
|
|
17
|
+
ID: 'Idaho',
|
|
18
|
+
IL: 'Illinois',
|
|
19
|
+
IN: 'Indiana',
|
|
20
|
+
IA: 'Iowa',
|
|
21
|
+
KS: 'Kansas',
|
|
22
|
+
KY: 'Kentucky',
|
|
23
|
+
LA: 'Louisiana',
|
|
24
|
+
ME: 'Maine',
|
|
25
|
+
MH: 'Marshall Islands',
|
|
26
|
+
MD: 'Maryland',
|
|
27
|
+
MA: 'Massachusetts',
|
|
28
|
+
MI: 'Michigan',
|
|
29
|
+
MN: 'Minnesota',
|
|
30
|
+
MS: 'Mississippi',
|
|
31
|
+
MO: 'Missouri',
|
|
32
|
+
MT: 'Montana',
|
|
33
|
+
NE: 'Nebraska',
|
|
34
|
+
NV: 'Nevada',
|
|
35
|
+
NH: 'New Hampshire',
|
|
36
|
+
NJ: 'New Jersey',
|
|
37
|
+
NM: 'New Mexico',
|
|
38
|
+
NY: 'New York',
|
|
39
|
+
NC: 'North Carolina',
|
|
40
|
+
ND: 'North Dakota',
|
|
41
|
+
MP: 'Northern Mariana Islands',
|
|
42
|
+
OH: 'Ohio',
|
|
43
|
+
OK: 'Oklahoma',
|
|
44
|
+
OR: 'Oregon',
|
|
45
|
+
PW: 'Palau',
|
|
46
|
+
PA: 'Pennsylvania',
|
|
47
|
+
PR: 'Puerto Rico',
|
|
48
|
+
RI: 'Rhode Island',
|
|
49
|
+
SC: 'South Carolina',
|
|
50
|
+
SD: 'South Dakota',
|
|
51
|
+
TN: 'Tennessee',
|
|
52
|
+
TX: 'Texas',
|
|
53
|
+
UT: 'Utah',
|
|
54
|
+
VT: 'Vermont',
|
|
55
|
+
VI: 'Virgin Islands',
|
|
56
|
+
VA: 'Virginia',
|
|
57
|
+
WA: 'Washington',
|
|
58
|
+
WV: 'West Virginia',
|
|
59
|
+
WI: 'Wisconsin',
|
|
60
|
+
WY: 'Wyoming'
|
|
61
|
+
}
|
|
62
|
+
export const standardizeStateName = value => {
|
|
63
|
+
if (typeof value !== 'string' || !isNaN(Number(value))) {
|
|
64
|
+
return value
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const upperValue = value.toUpperCase()
|
|
68
|
+
|
|
69
|
+
// Handle U.S. Virgin Islands variations
|
|
70
|
+
if (['U.S. VIRGIN ISLANDS', 'VI', 'US VIRGIN ISLANDS', 'VIRGIN ISLANDS'].includes(upperValue)) {
|
|
71
|
+
return 'U.S. VIRGIN ISLANDS'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Return full name for state abbreviation or the original name
|
|
75
|
+
return states[upperValue] || value
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './DataTable'
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Axis } from '@cdc/core/types/Axis'
|
|
2
|
+
import { Series } from '@cdc/core/types/Series'
|
|
3
|
+
import { Runtime } from '@cdc/core/types/Runtime'
|
|
4
|
+
|
|
5
|
+
export type TableConfig = {
|
|
6
|
+
type?: string
|
|
7
|
+
table: {
|
|
8
|
+
showVertical?: boolean
|
|
9
|
+
indexLabel: string
|
|
10
|
+
limitHeight: boolean
|
|
11
|
+
height: string | number
|
|
12
|
+
caption: string
|
|
13
|
+
download: boolean
|
|
14
|
+
}
|
|
15
|
+
xAxis?: Axis
|
|
16
|
+
yAxis?: Axis
|
|
17
|
+
boxplot?: {
|
|
18
|
+
tableData: Object[]
|
|
19
|
+
labels: {
|
|
20
|
+
mean: string
|
|
21
|
+
maximum: string
|
|
22
|
+
minimum: string
|
|
23
|
+
iqr: string
|
|
24
|
+
median: string
|
|
25
|
+
q1: string
|
|
26
|
+
q3: string
|
|
27
|
+
outliers: string
|
|
28
|
+
values: string
|
|
29
|
+
total: string
|
|
30
|
+
lowerBounds: string
|
|
31
|
+
upperBounds: string
|
|
32
|
+
}
|
|
33
|
+
plots: []
|
|
34
|
+
categories: string[]
|
|
35
|
+
}
|
|
36
|
+
visualizationType?: string
|
|
37
|
+
general?: {
|
|
38
|
+
geoType: string
|
|
39
|
+
type: string
|
|
40
|
+
showDownloadButton: boolean
|
|
41
|
+
allowMapZoom?: boolean
|
|
42
|
+
}
|
|
43
|
+
columns?: {
|
|
44
|
+
geo: {
|
|
45
|
+
name: string
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
legend?: {
|
|
49
|
+
specialClasses: { key: string; label: string; value: string }[]
|
|
50
|
+
}
|
|
51
|
+
series?: Series
|
|
52
|
+
regions?: { label: string; from: string; to: string }[]
|
|
53
|
+
runtimeSeriesLabels?: Object
|
|
54
|
+
dataFormat?: Object
|
|
55
|
+
runtime: Runtime
|
|
56
|
+
data: Object[]
|
|
57
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Papa from 'papaparse'
|
|
2
|
+
|
|
3
|
+
type DownloadButtonProps = {
|
|
4
|
+
rawData: Object
|
|
5
|
+
fileName: string
|
|
6
|
+
headerColor: string
|
|
7
|
+
skipId: string | number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DownloadButton = ({ rawData, fileName, headerColor, skipId }: DownloadButtonProps) => {
|
|
11
|
+
const csvData = Papa.unparse(rawData)
|
|
12
|
+
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
|
|
13
|
+
|
|
14
|
+
const saveBlob = () => {
|
|
15
|
+
//@ts-ignore
|
|
16
|
+
if (typeof window.navigator.msSaveBlob === 'function') {
|
|
17
|
+
//@ts-ignore
|
|
18
|
+
navigator.msSaveBlob(blob, fileName)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<a download={fileName} type='button' onClick={saveBlob} href={URL.createObjectURL(blob)} aria-label='Download this data in a CSV file format.' className={`${headerColor} no-border`} id={`${skipId}`} data-html2canvas-ignore role='button'>
|
|
24
|
+
Download Data (CSV)
|
|
25
|
+
</a>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default DownloadButton
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
-
export default function LegendCircle({ fill, borderColor }) {
|
|
3
|
+
export default function LegendCircle({ fill, borderColor, display = 'inline-block' }) {
|
|
4
4
|
const styles = {
|
|
5
5
|
marginRight: '5px',
|
|
6
6
|
borderRadius: '300px',
|
|
7
7
|
verticalAlign: 'middle',
|
|
8
|
-
display:
|
|
8
|
+
display: display,
|
|
9
9
|
height: '1em',
|
|
10
10
|
width: '1em',
|
|
11
11
|
border: borderColor ? `${borderColor} 1px solid` : 'rgba(0,0,0,.3) 1px solid',
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import Row from './components/Row'
|
|
3
|
+
import GroupRow from './components/GroupRow'
|
|
4
|
+
import { CellMatrix, GroupCellMatrix } from './types/CellMatrix'
|
|
5
|
+
|
|
6
|
+
type TableProps = {
|
|
7
|
+
childrenMatrix: CellMatrix | GroupCellMatrix
|
|
8
|
+
tableName: string
|
|
9
|
+
caption: string
|
|
10
|
+
stickyHeader?: boolean
|
|
11
|
+
headContent: ReactNode
|
|
12
|
+
tableOptions: {
|
|
13
|
+
className: string
|
|
14
|
+
'aria-live'?: 'off' | 'assertive' | 'polite'
|
|
15
|
+
hidden?: boolean
|
|
16
|
+
'aria-rowcount'?: number
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type Position = 'sticky'
|
|
21
|
+
|
|
22
|
+
const Table = ({ childrenMatrix, tableName, caption, stickyHeader, headContent, tableOptions }: TableProps) => {
|
|
23
|
+
const headStyle = stickyHeader ? { position: 'sticky' as Position, top: 0, zIndex: 999 } : {}
|
|
24
|
+
const isGroupedMatrix = !Array.isArray(childrenMatrix)
|
|
25
|
+
return (
|
|
26
|
+
<table {...tableOptions}>
|
|
27
|
+
<caption className='visually-hidden'>{caption}</caption>
|
|
28
|
+
<thead style={headStyle}>{headContent}</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
{isGroupedMatrix
|
|
31
|
+
? Object.keys(childrenMatrix).flatMap(groupName => {
|
|
32
|
+
let colSpan = 0
|
|
33
|
+
const rows = childrenMatrix[groupName].map((row, i) => {
|
|
34
|
+
colSpan = row.length
|
|
35
|
+
const key = `${tableName}-${groupName}-row-${i}`
|
|
36
|
+
return <Row key={key} rowKey={key} childRow={row} />
|
|
37
|
+
})
|
|
38
|
+
return [<GroupRow label={groupName} colSpan={colSpan} key={`${tableName}-${groupName}`} />, ...rows]
|
|
39
|
+
})
|
|
40
|
+
: childrenMatrix.map((childRow, i) => {
|
|
41
|
+
const key = `${tableName}-row-${i}`
|
|
42
|
+
return <Row key={key} rowKey={key} childRow={childRow} />
|
|
43
|
+
})}
|
|
44
|
+
</tbody>
|
|
45
|
+
</table>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default Table
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type GroupRowProps = {
|
|
2
|
+
label: string
|
|
3
|
+
colSpan: number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const GroupRow = ({ label, colSpan }: GroupRowProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<tr>
|
|
9
|
+
<th scope='colgroup' colSpan={colSpan}>
|
|
10
|
+
{label}
|
|
11
|
+
</th>
|
|
12
|
+
</tr>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default GroupRow
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import Cell from './Cell'
|
|
3
|
+
|
|
4
|
+
type RowProps = {
|
|
5
|
+
childRow: ReactNode[]
|
|
6
|
+
rowKey: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const Row = ({ childRow, rowKey }: RowProps) => {
|
|
10
|
+
return (
|
|
11
|
+
<tr>
|
|
12
|
+
{childRow.map((child, i) => (
|
|
13
|
+
<Cell key={rowKey + '__' + i}>{child}</Cell>
|
|
14
|
+
))}
|
|
15
|
+
</tr>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default Row
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Table'
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
|
|
3
|
+
import DataTable from '../DataTable'
|
|
4
|
+
import './styles.scss'
|
|
5
|
+
import Example_1 from './_mocks/dashboard_no_filter.json'
|
|
6
|
+
import CityStateExample from './_mocks/example-city-state.json'
|
|
7
|
+
import { displayGeoName } from '@cdc/map/src/helpers/displayGeoName'
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof DataTable> = {
|
|
10
|
+
title: 'Components/Organisms/DataTable',
|
|
11
|
+
component: DataTable
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default meta
|
|
15
|
+
|
|
16
|
+
type Story = StoryObj<typeof DataTable>
|
|
17
|
+
|
|
18
|
+
const datasetKey = 'dashboard_example_map.csv'
|
|
19
|
+
|
|
20
|
+
export const Primary: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
config: Example_1,
|
|
23
|
+
dataConfig: Example_1.datasets[datasetKey],
|
|
24
|
+
rawData: Example_1.datasets[datasetKey].data,
|
|
25
|
+
runtimeData: Example_1.datasets[datasetKey].data,
|
|
26
|
+
expandDataTable: true,
|
|
27
|
+
tableTitle: 'COVE DataTable',
|
|
28
|
+
viewport: 'lg',
|
|
29
|
+
tabbingId: datasetKey
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const CityState: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
config: CityStateExample,
|
|
36
|
+
dataConfig: CityStateExample,
|
|
37
|
+
rawData: CityStateExample.data,
|
|
38
|
+
runtimeData: CityStateExample.data,
|
|
39
|
+
expandDataTable: true,
|
|
40
|
+
tableTitle: 'CityStateExample DataTable',
|
|
41
|
+
viewport: 'lg',
|
|
42
|
+
tabbingId: '#asdf',
|
|
43
|
+
columns: CityStateExample.columns,
|
|
44
|
+
applyLegendToRow: () => ['#000'],
|
|
45
|
+
displayGeoName,
|
|
46
|
+
displayDataAsText: d => d
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Grouped: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
config: Example_1,
|
|
53
|
+
dataConfig: Example_1.datasets[datasetKey],
|
|
54
|
+
rawData: Example_1.datasets[datasetKey].data,
|
|
55
|
+
runtimeData: Example_1.datasets[datasetKey].data,
|
|
56
|
+
expandDataTable: true,
|
|
57
|
+
tableTitle: 'COVE DataTable',
|
|
58
|
+
groupBy: 'TimeZone',
|
|
59
|
+
viewport: 'lg',
|
|
60
|
+
tabbingId: datasetKey
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
|
|
3
|
+
import Table from '../Table'
|
|
4
|
+
import { ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Table> = {
|
|
7
|
+
title: 'Components/Molecules/Table',
|
|
8
|
+
component: Table
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Table>
|
|
14
|
+
|
|
15
|
+
function createMatrix(): ReactNode[][] {
|
|
16
|
+
const base = ['a', 'b', 'c'].map(el => <>{el}</>)
|
|
17
|
+
return [base, base]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Ungrouped: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
childrenMatrix: createMatrix(),
|
|
23
|
+
tableName: 'COVE Table',
|
|
24
|
+
headContent: (
|
|
25
|
+
<tr>
|
|
26
|
+
<th>first</th>
|
|
27
|
+
<th>second</th>
|
|
28
|
+
<th>third</th>
|
|
29
|
+
</tr>
|
|
30
|
+
),
|
|
31
|
+
tableOptions: { className: 'table table-bordered' }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createGroupMatrix(): Record<string, ReactNode[][]> {
|
|
36
|
+
const base = ['a', 'b', 'c'].map(el => <>{el}</>)
|
|
37
|
+
return { group_1: [base, base], group_2: [base, base] }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Grouped: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
childrenMatrix: createGroupMatrix(),
|
|
43
|
+
tableName: 'COVE Table',
|
|
44
|
+
headContent: (
|
|
45
|
+
<tr>
|
|
46
|
+
<th>first</th>
|
|
47
|
+
<th>second</th>
|
|
48
|
+
<th>third</th>
|
|
49
|
+
</tr>
|
|
50
|
+
),
|
|
51
|
+
tableOptions: { className: 'table table-bordered' }
|
|
52
|
+
}
|
|
53
|
+
}
|