@cdc/core 4.24.4 → 4.24.5

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.
Files changed (38) hide show
  1. package/components/DataTable/DataTable.tsx +13 -14
  2. package/components/DataTable/DataTableStandAlone.tsx +51 -4
  3. package/components/DataTable/components/ChartHeader.tsx +3 -2
  4. package/components/DataTable/components/DataTableEditorPanel.tsx +20 -3
  5. package/components/DataTable/helpers/chartCellMatrix.tsx +2 -1
  6. package/components/DataTable/helpers/getChartCellValue.ts +21 -1
  7. package/components/DataTable/helpers/getDataSeriesColumns.ts +37 -16
  8. package/components/DataTable/helpers/getSeriesName.ts +2 -1
  9. package/components/DataTable/helpers/{customColumns.ts → removeNullColumns.ts} +3 -3
  10. package/components/DataTable/types/TableConfig.ts +11 -22
  11. package/components/EditorPanel/ColumnsEditor.tsx +304 -261
  12. package/components/EditorPanel/DataTableEditor.tsx +119 -64
  13. package/components/EditorPanel/VizFilterEditor.tsx +234 -0
  14. package/components/EditorWrapper/EditorWrapper.tsx +48 -0
  15. package/components/EditorWrapper/editor-wrapper.style.css +14 -0
  16. package/components/Filters.jsx +70 -54
  17. package/components/Layout/components/Visualization/index.tsx +5 -3
  18. package/components/MediaControls.jsx +1 -1
  19. package/components/_stories/DataTable.stories.tsx +1 -2
  20. package/components/_stories/EditorPanel.stories.tsx +1 -0
  21. package/components/_stories/styles.scss +0 -1
  22. package/helpers/DataTransform.ts +9 -32
  23. package/helpers/coveUpdateWorker.ts +4 -2
  24. package/helpers/footnoteSymbols.ts +11 -0
  25. package/helpers/useDataVizClasses.js +0 -4
  26. package/helpers/ver/4.23.4.ts +27 -0
  27. package/helpers/ver/4.24.5.ts +32 -0
  28. package/package.json +2 -2
  29. package/styles/_reset.scss +7 -6
  30. package/types/Axis.ts +2 -0
  31. package/types/Column.ts +1 -0
  32. package/types/Legend.ts +1 -0
  33. package/types/MarkupInclude.ts +26 -0
  34. package/types/Runtime.ts +1 -0
  35. package/types/Series.ts +1 -1
  36. package/types/Table.ts +15 -14
  37. package/types/Visualization.ts +11 -4
  38. package/types/VizFilter.ts +13 -0
@@ -16,7 +16,7 @@ import Table from '../Table'
16
16
  import chartCellMatrix from './helpers/chartCellMatrix'
17
17
  import regionCellMatrix from './helpers/regionCellMatrix'
18
18
  import boxplotCellMatrix from './helpers/boxplotCellMatrix'
19
- import customColumns from './helpers/customColumns'
19
+ import removeNullColumns from './helpers/removeNullColumns'
20
20
  import { TableConfig } from './types/TableConfig'
21
21
  import { Column } from '../../types/Column'
22
22
 
@@ -52,7 +52,8 @@ export type DataTableProps = {
52
52
 
53
53
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
54
54
  const DataTable = (props: DataTableProps) => {
55
- const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
55
+ const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData: parentRuntimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
56
+ const runtimeData = removeNullColumns(parentRuntimeData)
56
57
  const [expanded, setExpanded] = useState(expandDataTable)
57
58
 
58
59
  const [sortBy, setSortBy] = useState<any>({ column: config.type === 'map' ? 'geo' : 'date', asc: false, colIndex: null })
@@ -98,25 +99,23 @@ const DataTable = (props: DataTableProps) => {
98
99
  break
99
100
  }
100
101
 
101
- const _runtimeData = config.table.customTableConfig ? customColumns(rawData, config.table.excludeColumns) : runtimeData
102
-
103
- const rawRows = Object.keys(_runtimeData).filter(column => column != 'columns')
102
+ const rawRows = Object.keys(runtimeData).filter(column => column != 'columns')
104
103
  const rows = isVertical
105
104
  ? rawRows.sort((a, b) => {
106
105
  let dataA
107
106
  let dataB
108
107
  if (config.type === 'map' && config.columns) {
109
108
  const sortByColName = config.columns[sortBy.column].name
110
- dataA = _runtimeData[a][sortByColName]
111
- dataB = _runtimeData[b][sortByColName]
109
+ dataA = runtimeData[a][sortByColName]
110
+ dataB = runtimeData[b][sortByColName]
112
111
  }
113
112
  if (config.type === 'chart' || config.type === 'dashboard') {
114
- dataA = _runtimeData[a][sortBy.column]
115
- dataB = _runtimeData[b][sortBy.column]
113
+ dataA = runtimeData[a][sortBy.column]
114
+ dataB = runtimeData[b][sortBy.column]
116
115
  }
117
116
  if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
118
- dataA = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[a][config.xAxis.dataKey])
119
- dataB = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[b][config.xAxis.dataKey])
117
+ dataA = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[a][config.xAxis.dataKey])
118
+ dataB = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[b][config.xAxis.dataKey])
120
119
  }
121
120
  return dataA && dataB ? customSort(dataA, dataB, sortBy, config) : 0
122
121
  })
@@ -185,17 +184,17 @@ const DataTable = (props: DataTableProps) => {
185
184
  </MediaControls.Section>
186
185
  <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
187
186
  <SkipTo skipId={skipId} skipMessage='Skip Data Table' />
188
- <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} fontSize={config.fontSize} viewport={viewport} />
187
+ {config.table.collapsible !== false && <ExpandCollapse expanded={expanded} setExpanded={setExpanded} fontSize={config.fontSize} tableTitle={tableTitle} viewport={viewport} />}
189
188
  <div className='table-container' style={limitHeight}>
190
189
  <Table
191
190
  viewport={viewport}
192
191
  wrapColumns={wrapColumns}
193
- childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType, viewport })}
192
+ childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData, isVertical, sortBy, hasRowType, viewport })}
194
193
  tableName={config.type}
195
194
  caption={caption}
196
195
  stickyHeader
197
196
  hasRowType={hasRowType}
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} />}
197
+ headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
199
198
  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
199
  fontSize={config.fontSize}
201
200
  />
@@ -1,15 +1,62 @@
1
+ import { useEffect, useState } from 'react'
1
2
  import { ViewPort } from '../../types/ViewPort'
2
- import { Visualization } from '../../types/Visualization'
3
+ import EditorWrapper from '../EditorWrapper/EditorWrapper'
3
4
  import DataTable from './DataTable'
5
+ import DataTableEditorPanel from './components/DataTableEditorPanel'
6
+ import Filters from '../Filters'
7
+ import { TableConfig } from './types/TableConfig'
4
8
 
5
9
  type StandAloneProps = {
6
10
  visualizationKey: string
7
- config: Visualization
11
+ config: TableConfig
8
12
  viewport?: ViewPort
13
+ isEditor?: boolean
14
+ updateConfig?: (Visualization) => void
9
15
  }
10
16
 
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'} />
17
+ // filterData is copied from ./packages/chart/src/helpers/filterData.ts
18
+ // consider moving this to a shared location
19
+ const filterData = (filters, data) => {
20
+ if (!filters) return data
21
+ const filteredData: any[] = []
22
+
23
+ data.forEach(row => {
24
+ let add = true
25
+ filters
26
+ .filter(filter => filter.type !== 'url')
27
+ .forEach(filter => {
28
+ if (row[filter.columnName] != filter.active) {
29
+ add = false
30
+ }
31
+ })
32
+
33
+ if (add) filteredData.push(row)
34
+ })
35
+
36
+ return filteredData
37
+ }
38
+
39
+ const DataTableStandAlone: React.FC<StandAloneProps> = ({ visualizationKey, config, updateConfig, viewport, isEditor }) => {
40
+ const [filteredData, setFilteredData] = useState<Record<string, any>[]>(filterData(config.filters, config.formattedData))
41
+
42
+ useEffect(() => {
43
+ // when using editor changes to filter should update the data
44
+ setFilteredData(filterData(config.filters, config.formattedData))
45
+ }, [config.filters])
46
+
47
+ if (isEditor)
48
+ return (
49
+ <EditorWrapper component={DataTableStandAlone} visualizationKey={visualizationKey} visualizationConfig={config} updateConfig={updateConfig} type={'Table'} viewport={viewport}>
50
+ <DataTableEditorPanel key={visualizationKey} config={config} updateConfig={updateConfig} />
51
+ </EditorWrapper>
52
+ )
53
+
54
+ return (
55
+ <>
56
+ <Filters config={config} setConfig={updateConfig} setFilteredData={setFilteredData} filterData={filterData} filteredData={filteredData} excludedData={config.formattedData} />
57
+ <DataTable expandDataTable={true} config={config} rawData={config.data} runtimeData={filteredData} tabbingId={visualizationKey} tableTitle={config.table.label} viewport={viewport || 'lg'} />
58
+ </>
59
+ )
13
60
  }
14
61
 
15
62
  export default DataTableStandAlone
@@ -4,9 +4,10 @@ import { getDataSeriesColumns } from '../helpers/getDataSeriesColumns'
4
4
  import { DownIcon, UpIcon } from './Icons'
5
5
  import ScreenReaderText from '@cdc/core/components/elements/ScreenReaderText'
6
6
 
7
- type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy; groupBy?; hasRowType? }
7
+ type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy; hasRowType? }
8
8
 
9
- const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, hasRowType }: ChartHeaderProps) => {
9
+ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, hasRowType }: ChartHeaderProps) => {
10
+ const groupBy = config.table?.groupBy
10
11
  if (!data) return
11
12
  let dataSeriesColumns = getDataSeriesColumns(config, isVertical, data)
12
13
  if (groupBy) {
@@ -4,6 +4,8 @@ import { Visualization } from '@cdc/core/types/Visualization'
4
4
  import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
5
5
  import { useMemo } from 'react'
6
6
  import ColumnsEditor from '../../EditorPanel/ColumnsEditor'
7
+ import VizFilterEditor from '../../EditorPanel/VizFilterEditor'
8
+ import _ from 'lodash'
7
9
 
8
10
  type DataTableEditorProps = {
9
11
  config: Visualization
@@ -13,7 +15,7 @@ type DataTableEditorProps = {
13
15
  const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateConfig }) => {
14
16
  const updateField = useMemo(() => updateFieldFactory(config, updateConfig), [JSON.stringify(config)])
15
17
  const deleteColumn = columnName => {
16
- const newColumns = config.columns
18
+ const newColumns = _.cloneDeep(config.columns)
17
19
 
18
20
  delete newColumns[columnName]
19
21
 
@@ -23,10 +25,25 @@ const DataTableEditorPanel: React.FC<DataTableEditorProps> = ({ config, updateCo
23
25
  })
24
26
  }
25
27
 
26
- const columns = Object.keys(config.columns || {})
28
+ const columns = Object.keys(config.originalFormattedData[0] || {})
27
29
  return (
28
30
  <>
29
- <ColumnsEditor config={config} updateField={updateField} deleteColumn={deleteColumn} />
31
+ <AccordionItem>
32
+ <AccordionItemHeading>
33
+ <AccordionItemButton>Filters</AccordionItemButton>
34
+ </AccordionItemHeading>
35
+ <AccordionItemPanel>
36
+ <VizFilterEditor config={config} updateField={updateField} rawData={config.originalFormattedData} />
37
+ </AccordionItemPanel>
38
+ </AccordionItem>
39
+ <AccordionItem>
40
+ <AccordionItemHeading>
41
+ <AccordionItemButton>Columns</AccordionItemButton>
42
+ </AccordionItemHeading>
43
+ <AccordionItemPanel>
44
+ <ColumnsEditor config={config} updateField={updateField} deleteColumn={deleteColumn} />
45
+ </AccordionItemPanel>
46
+ </AccordionItem>
30
47
  <AccordionItem>
31
48
  <AccordionItemHeading>
32
49
  <AccordionItemButton>Data Table</AccordionItemButton>
@@ -15,7 +15,8 @@ type ChartRowsProps = DataTableProps & {
15
15
  hasRowType?: boolean
16
16
  }
17
17
 
18
- const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
18
+ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
19
+ const groupBy = config.table?.groupBy
19
20
  const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
20
21
 
21
22
  const dataSeriesColumnsSorted = () => {
@@ -25,7 +25,7 @@ const isAdditionalColumn = (column, config) => {
25
25
  }
26
26
 
27
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]
28
+ if (config.visualizationType === 'Sankey' || runtimeData?.[0]?.tableData) return runtimeData[row][column]
29
29
 
30
30
  const rowObj = runtimeData[row]
31
31
  let cellValue // placeholder for formatting below
@@ -54,5 +54,25 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
54
54
  }
55
55
  }
56
56
 
57
+ // suppress cell value
58
+ config.preliminaryData?.forEach(pd => {
59
+ // check entered suppression value against cell value
60
+ const isValueMatch = String(pd.value) === String(labelValue)
61
+ // check entered suppression column against table key
62
+ const isColumnMatch = !pd.column || pd.column === column
63
+ if (isValueMatch && isColumnMatch && pd.displayTable && pd.type === 'suppression') {
64
+ switch (config.visualizationType) {
65
+ case 'Combo':
66
+ cellValue = config.runtime.barSeriesKeys.includes(column) ? pd.iconCode : config.runtime.lineSeriesKeys.includes(column) ? pd.lineCode : ''
67
+ break
68
+ case 'Bar':
69
+ cellValue = pd.iconCode
70
+ break
71
+ case 'Line':
72
+ cellValue = pd.lineCode
73
+ break
74
+ }
75
+ }
76
+ })
57
77
  return cellValue
58
78
  }
@@ -1,14 +1,13 @@
1
1
  import { TableConfig } from '../types/TableConfig'
2
+ import _ from 'lodash'
3
+ import { Column } from '../../../types/Column'
2
4
 
3
5
  export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, runtimeData: Object[]): string[] => {
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
- }
11
- let tmpSeriesColumns
6
+ const configColumns: Record<string, Column> = _.cloneDeep(config.columns) || {}
7
+ const excludeColumns = Object.values(configColumns)
8
+ .filter(column => column.dataTable === false)
9
+ .map(column => column.name)
10
+ let tmpSeriesColumns: string[] = []
12
11
  if (config.visualizationType !== 'Pie') {
13
12
  tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey] : [] //, ...config.runtime.seriesLabelsAll
14
13
  if (config.series) {
@@ -23,15 +22,37 @@ export const getDataSeriesColumns = (config: TableConfig, isVertical: boolean, r
23
22
  }
24
23
 
25
24
  // then add the additional Columns
26
- if (config.columns && Object.keys(config.columns).length > 0) {
27
- Object.keys(config.columns).forEach(function (key) {
28
- var value = config.columns[key]
29
- // add if not the index AND it is enabled to be added to data table
30
- if (value.name !== config.xAxis?.dataKey && value.dataTable === true) {
31
- tmpSeriesColumns.push(value.name)
25
+ Object.keys(configColumns).forEach(function (key) {
26
+ var value = configColumns[key]
27
+ // add if not the index AND it is enabled to be added to data table
28
+ const alreadyAdded = tmpSeriesColumns.includes(value.name)
29
+ if (value.name !== config.xAxis?.dataKey && value.dataTable === true && !alreadyAdded) {
30
+ tmpSeriesColumns.push(value.name)
31
+ }
32
+ })
33
+
34
+ const columnOrderingHash = Object.values(configColumns).reduce((acc, column) => {
35
+ // subtract 1 to switch from cardinal positioning to index
36
+ if (column.order !== undefined) {
37
+ acc[column.name] = column.order - 1
38
+ }
39
+ return acc
40
+ }, {})
41
+
42
+ tmpSeriesColumns = tmpSeriesColumns.filter(columnName => !excludeColumns.includes(columnName))
43
+
44
+ tmpSeriesColumns.forEach((columnName, index) => {
45
+ if (columnOrderingHash[columnName] === undefined) {
46
+ if (Object.values(columnOrderingHash).includes(index)) {
47
+ // add 1 to place unsorted columns behind sorted columns
48
+ columnOrderingHash[columnName] = index + 1
49
+ } else {
50
+ columnOrderingHash[columnName] = index
32
51
  }
33
- })
34
- }
52
+ }
53
+ })
54
+
55
+ tmpSeriesColumns.sort((a, b) => columnOrderingHash[a] - columnOrderingHash[b])
35
56
 
36
57
  return tmpSeriesColumns
37
58
  }
@@ -17,5 +17,6 @@ export const getSeriesName = (column: string, config: TableConfig) => {
17
17
  }
18
18
  if (config.runtimeSeriesLabels && config.runtimeSeriesLabels[column]) return config.runtimeSeriesLabels[column]
19
19
  const columnIsDataKey = column === config.xAxis?.dataKey
20
- return columnIsDataKey ? config.table.indexLabel : getLabel(column, config)
20
+ const indexLabel = config.table?.indexLabel
21
+ return columnIsDataKey && indexLabel ? indexLabel : getLabel(column, config)
21
22
  }
@@ -1,7 +1,7 @@
1
1
  type RuntimeData = Object[] & Record<string, Object>
2
2
 
3
3
  // removes null and excluded columns
4
- const customColumns = (runtimeData: Object[] | RuntimeData, excludeColumns: string[] = []): RuntimeData => {
4
+ const removeNullColumns = (runtimeData: Object[] | RuntimeData): RuntimeData => {
5
5
  if (!Array.isArray(runtimeData)) {
6
6
  // currently we don't support Record types
7
7
  return runtimeData
@@ -10,7 +10,7 @@ const customColumns = (runtimeData: Object[] | RuntimeData, excludeColumns: stri
10
10
  runtimeData.forEach(row => {
11
11
  Object.keys(row).forEach(key => {
12
12
  if (runtimeDataMemo[key] === undefined) runtimeDataMemo[key] = null
13
- if (row[key] !== null && !excludeColumns.includes(key)) runtimeDataMemo[key] = true
13
+ if (row[key] !== null) runtimeDataMemo[key] = true
14
14
  })
15
15
  })
16
16
  return runtimeData.map(d => {
@@ -24,4 +24,4 @@ const customColumns = (runtimeData: Object[] | RuntimeData, excludeColumns: stri
24
24
  }
25
25
  }
26
26
 
27
- export default customColumns
27
+ export default removeNullColumns
@@ -1,28 +1,17 @@
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
- import { Table } from '@cdc/core/types/Table'
5
- import { Region } from '@cdc/core/types/Region'
1
+ import { Axis } from '../../../types/Axis'
2
+ import { Runtime } from '../../../types/Runtime'
3
+ import { Region } from '../../../types/Region'
6
4
  import { BoxPlot } from '../../../types/BoxPlot'
7
- import { General } from '../../../types/General'
8
- import { Column } from '../../../types/Column'
9
- import { Legend } from '@cdc/core/types/Legend'
5
+ import { PreliminaryDataItem } from '@cdc/chart/src/components/LineChart/LineChartProps'
6
+ import { Visualization } from '../../../types/Visualization'
10
7
 
11
- export type TableConfig = {
12
- type?: string
13
- table: Table
14
- xAxis?: Axis
15
- yAxis?: Axis
8
+ export type TableConfig = Visualization & {
16
9
  boxplot?: BoxPlot
17
- visualizationType: string
18
- general?: General
19
- columns?: Record<string, Column>
20
- legend?: Legend
21
- series?: Series
10
+ fontSize: 'small' | 'medium' | 'large'
22
11
  regions?: Region[]
23
- runtimeSeriesLabels?: Object
24
- dataFormat?: Object
25
12
  runtime?: Runtime
26
- data: Object[]
27
- fontSize: 'small' | 'medium' | 'large'
13
+ runtimeSeriesLabels?: Object
14
+ xAxis?: Axis
15
+ yAxis?: Axis
16
+ preliminaryData: PreliminaryDataItem[]
28
17
  }