@cdc/core 4.23.11 → 4.24.2

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 (57) hide show
  1. package/components/DataTable/DataTable.tsx +39 -10
  2. package/components/DataTable/components/ChartHeader.tsx +17 -5
  3. package/components/DataTable/components/ExpandCollapse.tsx +1 -1
  4. package/components/DataTable/helpers/chartCellMatrix.tsx +16 -2
  5. package/components/DataTable/helpers/customColumns.ts +25 -0
  6. package/components/DataTable/helpers/customSort.ts +9 -0
  7. package/components/DataTable/helpers/getChartCellValue.ts +2 -1
  8. package/components/DataTable/helpers/getDataSeriesColumns.ts +2 -1
  9. package/components/DataTable/helpers/getSeriesName.ts +15 -20
  10. package/components/DataTable/helpers/mapCellMatrix.tsx +4 -0
  11. package/components/DataTable/types/TableConfig.ts +11 -40
  12. package/components/EditorPanel/DataTableEditor.tsx +133 -0
  13. package/components/EditorPanel/Inputs.tsx +150 -0
  14. package/components/Filters.jsx +17 -15
  15. package/components/MediaControls.jsx +1 -1
  16. package/components/MultiSelect/MultiSelect.tsx +95 -0
  17. package/components/MultiSelect/index.ts +1 -0
  18. package/components/MultiSelect/multiselect.styles.css +50 -0
  19. package/components/Table/Table.tsx +23 -3
  20. package/components/Table/components/Cell.tsx +3 -3
  21. package/components/Table/components/GroupRow.tsx +6 -2
  22. package/components/Table/components/Row.tsx +9 -2
  23. package/components/Table/types/RowType.ts +5 -0
  24. package/components/_stories/DataTable.stories.tsx +41 -0
  25. package/components/_stories/EditorPanel.stories.tsx +53 -0
  26. package/components/_stories/Inputs.stories.tsx +37 -0
  27. package/components/_stories/MultiSelect.stories.tsx +24 -0
  28. package/components/_stories/_mocks/row_type.json +42 -0
  29. package/components/inputs/{InputSelect.jsx → InputSelect.tsx} +15 -5
  30. package/components/managers/DataDesigner.tsx +8 -8
  31. package/components/ui/{Icon.jsx → Icon.tsx} +3 -3
  32. package/components/ui/_stories/Colors.stories.tsx +92 -0
  33. package/components/ui/_stories/Icon.stories.tsx +17 -10
  34. package/helpers/DataTransform.ts +30 -2
  35. package/helpers/fetchRemoteData.js +5 -5
  36. package/helpers/getFileExtension.ts +28 -5
  37. package/helpers/getViewport.ts +23 -0
  38. package/helpers/isSolr.js +13 -0
  39. package/helpers/withDevTools.ts +50 -0
  40. package/package.json +2 -2
  41. package/styles/_data-table.scss +2 -5
  42. package/styles/_global-variables.scss +75 -0
  43. package/styles/base.scss +89 -69
  44. package/types/Action.ts +1 -0
  45. package/types/Axis.ts +39 -2
  46. package/types/BoxPlot.ts +21 -0
  47. package/types/Column.ts +16 -0
  48. package/types/FilterBehavior.ts +1 -0
  49. package/types/General.ts +9 -0
  50. package/types/Runtime.ts +21 -1
  51. package/types/Series.ts +1 -1
  52. package/types/Table.ts +21 -0
  53. package/types/UpdateFieldFunc.ts +1 -0
  54. package/types/ViewPort.ts +2 -0
  55. package/types/Visualization.ts +15 -9
  56. package/types/WCMSProps.ts +11 -0
  57. package/helpers/getViewport.js +0 -21
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useState, useMemo } from 'react'
2
+ import { timeParse } from 'd3-time-format'
2
3
 
3
4
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
4
5
  import MediaControls from '@cdc/core/components/MediaControls'
@@ -15,12 +16,14 @@ import Table from '../Table'
15
16
  import chartCellMatrix from './helpers/chartCellMatrix'
16
17
  import regionCellMatrix from './helpers/regionCellMatrix'
17
18
  import boxplotCellMatrix from './helpers/boxplotCellMatrix'
19
+ import customColumns from './helpers/customColumns'
18
20
  import { TableConfig } from './types/TableConfig'
21
+ import { Column } from '../../types/Column'
19
22
 
20
23
  export type DataTableProps = {
21
24
  applyLegendToRow?: Function
22
25
  colorScale?: Function
23
- columns?: { navigate: { name: string } }
26
+ columns?: Record<string, Column>
24
27
  config: TableConfig
25
28
  dataConfig?: Object
26
29
  displayDataAsText?: Function
@@ -29,20 +32,27 @@ export type DataTableProps = {
29
32
  formatLegendLocation?: Function
30
33
  groupBy?: string
31
34
  headerColor?: string
35
+ imageRef?: string
32
36
  indexTitle?: string
37
+ isDebug?: boolean
38
+ isEditor?: boolean
33
39
  navigationHandler?: Function
40
+ outerContainerRef?: Function
34
41
  rawData: Object[]
35
42
  runtimeData: Object[] | Record<string, Object> // UNSAFE
36
43
  setFilteredCountryCode?: Function // used for Maps only
44
+ showDownloadButton?: boolean
37
45
  tabbingId: string
38
46
  tableTitle: string
39
47
  viewport: string
40
48
  vizTitle?: string
49
+ // determines if columns should be wrapped in the table
50
+ wrapColumns?: boolean
41
51
  }
42
52
 
43
53
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
44
54
  const DataTable = (props: DataTableProps) => {
45
- const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId } = props
55
+ const { config, dataConfig, tableTitle, vizTitle, rawData, runtimeData, headerColor, expandDataTable, columns, viewport, formatLegendLocation, tabbingId, wrapColumns } = props
46
56
 
47
57
  const [expanded, setExpanded] = useState(expandDataTable)
48
58
 
@@ -89,19 +99,25 @@ const DataTable = (props: DataTableProps) => {
89
99
  break
90
100
  }
91
101
 
92
- const rawRows = Object.keys(runtimeData)
102
+ const _runtimeData = config.table.customTableConfig ? customColumns(rawData, config.table.excludeColumns) : runtimeData
103
+
104
+ const rawRows = Object.keys(_runtimeData).filter(column => column != 'columns')
93
105
  const rows = isVertical
94
106
  ? rawRows.sort((a, b) => {
95
107
  let dataA
96
108
  let dataB
97
109
  if (config.type === 'map' && config.columns) {
98
110
  const sortByColName = config.columns[sortBy.column].name
99
- dataA = runtimeData[a][sortByColName]
100
- dataB = runtimeData[b][sortByColName]
111
+ dataA = _runtimeData[a][sortByColName]
112
+ dataB = _runtimeData[b][sortByColName]
101
113
  }
102
114
  if (config.type === 'chart' || config.type === 'dashboard') {
103
- dataA = runtimeData[a][sortBy.column]
104
- dataB = runtimeData[b][sortBy.column]
115
+ dataA = _runtimeData[a][sortBy.column]
116
+ dataB = _runtimeData[b][sortBy.column]
117
+ }
118
+ if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date' && config.xAxis.sortDates) {
119
+ dataA = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[a][config.xAxis.dataKey])
120
+ dataB = timeParse(config.runtime.xAxis.dateParseFormat)(_runtimeData[b][config.xAxis.dataKey])
105
121
  }
106
122
  return dataA && dataB ? customSort(dataA, dataB, sortBy, config) : 0
107
123
  })
@@ -112,6 +128,8 @@ const DataTable = (props: DataTableProps) => {
112
128
  OverflowY: 'scroll'
113
129
  }
114
130
 
131
+ const hasRowType = !!Object.keys(rawData?.[0] || {}).find((v: string) => v.match(/row[_-]?type/i))
132
+
115
133
  const caption = useMemo(() => {
116
134
  if (config.type === 'map') {
117
135
  return config.table.caption ? config.table.caption : `Data table showing data for the ${mapLookup[config.general.geoType]} figure.`
@@ -120,6 +138,13 @@ const DataTable = (props: DataTableProps) => {
120
138
  }
121
139
  }, [config.table.caption])
122
140
 
141
+ // Determines if a relative region is being shown to the user.
142
+ // If a relative region is found we don't want to display the data table.
143
+ // Takes backwards compatibility into consideration, ie !region.toType || !region.fromType
144
+ const noRelativeRegions = config?.regions?.every(region => {
145
+ return (region.toType === 'Fixed' && region.fromType === 'Fixed') || (!region.toType && !region.fromType) || (!region.toType && region.fromType === 'Fixed') || (!region.fromType && region.toType === 'Fixed')
146
+ })
147
+
123
148
  // prettier-ignore
124
149
  const tableData = useMemo(() => (
125
150
  config.visualizationType === 'Pie'
@@ -151,17 +176,20 @@ const DataTable = (props: DataTableProps) => {
151
176
  <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
152
177
  <div className='table-container' style={limitHeight}>
153
178
  <Table
154
- childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, ...props }) : chartCellMatrix({ rows, ...props, isVertical, sortBy })}
179
+ wrapColumns={wrapColumns}
180
+ childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType })}
155
181
  tableName={config.type}
156
182
  caption={caption}
157
183
  stickyHeader
158
- headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={runtimeData} {...props} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
184
+ hasRowType={hasRowType}
185
+ headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={_runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
159
186
  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 }}
160
187
  />
161
188
 
162
189
  {/* REGION Data Table */}
163
- {config.regions && config.regions.length > 0 && config.visualizationType !== 'Box Plot' && (
190
+ {noRelativeRegions && config.regions && config.regions.length > 0 && config.visualizationType !== 'Box Plot' && (
164
191
  <Table
192
+ wrapColumns={wrapColumns}
165
193
  childrenMatrix={regionCellMatrix({ config })}
166
194
  tableName={config.visualizationType}
167
195
  caption='Table of the highlighted regions in the visualization'
@@ -188,6 +216,7 @@ const DataTable = (props: DataTableProps) => {
188
216
  <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
189
217
  <div className='table-container' style={limitHeight}>
190
218
  <Table
219
+ wrapColumns={wrapColumns}
191
220
  childrenMatrix={boxplotCellMatrix({ rows: tableData, config })}
192
221
  tableName={config.visualizationType}
193
222
  caption={caption}
@@ -3,11 +3,11 @@ import { getSeriesName } from '../helpers/getSeriesName'
3
3
  import { getDataSeriesColumns } from '../helpers/getDataSeriesColumns'
4
4
  import { DownIcon, UpIcon } from './Icons'
5
5
 
6
- type ChartHeaderProps = { data; isVertical; config; runtimeData; setSortBy; sortBy; groupBy? }
6
+ type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy; groupBy?; hasRowType? }
7
7
 
8
- const ChartHeader = ({ data, isVertical, config, runtimeData, setSortBy, sortBy, groupBy }: ChartHeaderProps) => {
8
+ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, groupBy, hasRowType }: ChartHeaderProps) => {
9
9
  if (!data) return
10
- let dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
10
+ let dataSeriesColumns = getDataSeriesColumns(config, isVertical, data)
11
11
  if (groupBy) {
12
12
  let groupHeaderRemoved = dataSeriesColumns.filter(col => col !== groupBy)
13
13
  if (groupHeaderRemoved.length != dataSeriesColumns.length) {
@@ -17,6 +17,14 @@ const ChartHeader = ({ data, isVertical, config, runtimeData, setSortBy, sortBy,
17
17
  }
18
18
  }
19
19
  if (isVertical) {
20
+ if (hasRowType) {
21
+ // find the row type column and place it at the beginning of the array
22
+ const rowTypeRegex = /row[_-]?type/i
23
+ const rowTypeIndex = dataSeriesColumns.findIndex(column => rowTypeRegex.test(column))
24
+ if (rowTypeIndex > -1) {
25
+ dataSeriesColumns.splice(rowTypeIndex, 1)
26
+ }
27
+ }
20
28
  return (
21
29
  <tr>
22
30
  {dataSeriesColumns.map((column, index) => {
@@ -24,15 +32,18 @@ const ChartHeader = ({ data, isVertical, config, runtimeData, setSortBy, sortBy,
24
32
 
25
33
  return (
26
34
  <th
35
+ style={{ minWidth: (config.table.cellMinWidth || 0) + 'px' }}
27
36
  key={`col-header-${column}__${index}`}
28
37
  tabIndex={0}
29
38
  title={text}
30
39
  role='columnheader'
31
40
  scope='col'
32
41
  onClick={() => {
42
+ if (hasRowType) return
33
43
  setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false, colIndex: index })
34
44
  }}
35
45
  onKeyDown={e => {
46
+ if (hasRowType) return
36
47
  if (e.keyCode === 13) {
37
48
  setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false, colIndex: index })
38
49
  }
@@ -54,11 +65,12 @@ const ChartHeader = ({ data, isVertical, config, runtimeData, setSortBy, sortBy,
54
65
  const sliceVal = config.visualizationType === 'Pie' ? 1 : 0
55
66
  return (
56
67
  <tr>
57
- {['__series__', ...Object.keys(runtimeData)].slice(sliceVal).map((row, index) => {
68
+ {['__series__', ...Object.keys(data)].slice(sliceVal).map((row, index) => {
58
69
  let column = config.xAxis?.dataKey
59
- let text = row !== '__series__' ? getChartCellValue(row, column, config, runtimeData) : '__series__'
70
+ let text = row !== '__series__' ? getChartCellValue(row, column, config, data) : '__series__'
60
71
  return (
61
72
  <th
73
+ style={{ minWidth: (config.table.cellMinWidth || 0) + 'px' }}
62
74
  key={`col-header-${text}__${index}`}
63
75
  tabIndex={0}
64
76
  title={text}
@@ -1,4 +1,4 @@
1
- import Icon from '@cdc/core/components/ui/Icon'
1
+ import Icon from '../../ui/Icon'
2
2
 
3
3
  const ExpandCollapse = ({ expanded, setExpanded, tableTitle }) => (
4
4
  <div
@@ -12,9 +12,10 @@ type ChartRowsProps = DataTableProps & {
12
12
  isVertical: boolean
13
13
  sortBy: { colIndex; column }
14
14
  groupBy?: string
15
+ hasRowType?: boolean
15
16
  }
16
17
 
17
- const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
18
+ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
18
19
  const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
19
20
 
20
21
  const dataSeriesColumnsSorted = () => {
@@ -55,7 +56,20 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
55
56
  return cellMatrix
56
57
  } else {
57
58
  return rows.map(row => {
58
- return dataSeriesColumns.map((column, j) => getChartCellValue(row, column, config, runtimeData))
59
+ if (hasRowType) {
60
+ let rowType
61
+ let rowValues = []
62
+ dataSeriesColumns.forEach((column, j) => {
63
+ if (column.match(/row[_-]?type/i)) {
64
+ rowType = getChartCellValue(row, column, config, runtimeData)
65
+ } else {
66
+ rowValues.push(getChartCellValue(row, column, config, runtimeData))
67
+ }
68
+ })
69
+ return [rowType, ...rowValues]
70
+ } else {
71
+ return dataSeriesColumns.map((column, j) => getChartCellValue(row, column, config, runtimeData))
72
+ }
59
73
  })
60
74
  }
61
75
  } else {
@@ -0,0 +1,25 @@
1
+ // removes null and excluded columns
2
+ const customColumns = (runtimeData: Object[] | Record<string, Object>, excludeColumns: string[] = []): Object[] | Record<string, Object> => {
3
+ if (!Array.isArray(runtimeData)) {
4
+ // currently we don't support Record types
5
+ return runtimeData
6
+ } else {
7
+ const runtimeDataMemo = {}
8
+ runtimeData.forEach(row => {
9
+ Object.keys(row).forEach(key => {
10
+ if (runtimeDataMemo[key] === undefined) runtimeDataMemo[key] = null
11
+ if (row[key] !== null && !excludeColumns.includes(key)) runtimeDataMemo[key] = true
12
+ })
13
+ })
14
+ return runtimeData.map(d => {
15
+ const row = {}
16
+ Object.keys(d).forEach(key => {
17
+ if (key.match(/row[_-]?type/i)) row['row_type'] = d[key]
18
+ if (runtimeDataMemo[key] === true) row[key] = d[key]
19
+ })
20
+ return row
21
+ })
22
+ }
23
+ }
24
+
25
+ export default customColumns
@@ -8,7 +8,16 @@ export const customSort = (a, b, sortBy, config) => {
8
8
  if (config.type === 'map') {
9
9
  valueA = standardizeStateName(a)
10
10
  valueB = standardizeStateName(b)
11
+ // sort for Regions table for Map
12
+ if (String(valueA).toLowerCase().includes('region') && String(valueB).toLowerCase().includes('region')) {
13
+ const numberA = parseInt(a.match(/\d+$/)[0], 10)
14
+ const numberB = parseInt(b.match(/\d+$/)[0], 10)
15
+
16
+ // Compare the numeric parts
17
+ return !sortBy.asc ? Number(numberA) - Number(numberB) : Number(numberB) - Number(numberA)
18
+ }
11
19
  }
20
+
12
21
  // Treat booleans and nulls as an empty string
13
22
  valueA = valueA === false || valueA === true || valueA === null ? '' : valueA
14
23
  valueB = valueB === false || valueB === true || valueB === null ? '' : valueB
@@ -24,12 +24,13 @@ const isAdditionalColumn = (column, config) => {
24
24
  }
25
25
 
26
26
  export const getChartCellValue = (row, column, config, runtimeData) => {
27
+ if (config.table.customTableConfig) return runtimeData[row][column]
27
28
  const rowObj = runtimeData[row]
28
29
  let cellValue // placeholder for formatting below
29
30
  let labelValue = rowObj[column] // just raw X axis string
30
31
  if (column === config.xAxis?.dataKey) {
31
32
  // 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
+ cellValue = config.xAxis?.type === 'date' ? formatDate(config.table?.dateDisplayFormat || config.xAxis?.dateDisplayFormat, parseDate(config.xAxis?.dateParseFormat, labelValue)) : labelValue
33
34
  } else {
34
35
  let resolvedAxis = 'left'
35
36
  let leftAxisItems = config.series ? config.series.filter(item => item?.axis === 'Left') : []
@@ -1,4 +1,5 @@
1
1
  export const getDataSeriesColumns = (config, isVertical, runtimeData): string[] => {
2
+ if (config.table.customTableConfig) return runtimeData[0] ? Object.keys(runtimeData[0]) : []
2
3
  let tmpSeriesColumns
3
4
  if (config.visualizationType !== 'Pie') {
4
5
  tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey] : [] //, ...config.runtime.seriesLabelsAll
@@ -10,7 +11,7 @@ export const getDataSeriesColumns = (config, isVertical, runtimeData): string[]
10
11
  tmpSeriesColumns = Object.keys(runtimeData[0])
11
12
  }
12
13
  } else {
13
- tmpSeriesColumns = [config.xAxis?.dataKey, config.yAxis?.dataKey] //Object.keys(runtimeData[0])
14
+ tmpSeriesColumns = isVertical ? [config.xAxis?.dataKey, config.yAxis?.dataKey] : [config.yAxis?.dataKey]
14
15
  }
15
16
 
16
17
  // then add the additional Columns
@@ -1,26 +1,21 @@
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
1
+ import { TableConfig } from '../types/TableConfig'
2
+
3
+ const getLabel = (name: string, config: TableConfig) => {
4
+ const columns = Object.values(config.columns || {})
5
+ const matchingConfiguredColumn = columns.find(col => col.name === name)
6
+ if (matchingConfiguredColumn?.label) {
7
+ return matchingConfiguredColumn.label
12
8
  }
9
+ return name
13
10
  }
14
11
 
15
- export const getSeriesName = (column, config) => {
12
+ export const getSeriesName = (column: string, config: TableConfig) => {
16
13
  // 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
-
14
+ const userDefinedSeries = config.series?.find(series => series.dataKey === column)
15
+ if (userDefinedSeries?.name) {
16
+ return userDefinedSeries.name
17
+ }
20
18
  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
19
+ const columnIsDataKey = column === config.xAxis?.dataKey
20
+ return columnIsDataKey ? config.table.indexLabel : getLabel(column, config)
26
21
  }
@@ -21,7 +21,11 @@ const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, di
21
21
  let labelValue
22
22
  const mapZoomHandler = config.general.type === 'bubble' && config.general.allowMapZoom && config.general.geoType === 'world' ? () => setFilteredCountryCode(row) : undefined
23
23
  if ((config.general.geoType !== 'single-state' && config.general.geoType !== 'us-county') || config.general.type === 'us-geocode') {
24
+ const capitalize = str => {
25
+ return str.charAt(0).toUpperCase() + str.slice(1)
26
+ }
24
27
  labelValue = displayGeoName(row)
28
+ labelValue = String(labelValue).startsWith('region') ? capitalize(labelValue) : labelValue
25
29
  } else {
26
30
  labelValue = formatLegendLocation(row)
27
31
  }
@@ -1,55 +1,26 @@
1
1
  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
+ import { Table } from '@cdc/core/types/Table'
5
+ import { BoxPlot } from '../../../types/BoxPlot'
6
+ import { General } from '../../../types/General'
7
+ import { Column } from '../../../types/Column'
4
8
 
5
9
  export type TableConfig = {
6
10
  type?: string
7
- table: {
8
- showVertical?: boolean
9
- indexLabel: string
10
- limitHeight: boolean
11
- height: string | number
12
- caption: string
13
- download: boolean
14
- }
11
+ table: Table
15
12
  xAxis?: Axis
16
13
  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
- }
14
+ boxplot?: BoxPlot
36
15
  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
- }
16
+ general?: General
17
+ columns?: Record<string, Column>
48
18
  legend?: {
49
- specialClasses: { key: string; label: string; value: string }[]
19
+ specialClasses?: { key: string; label: string; value: string }[]
20
+ hide?: boolean
50
21
  }
51
22
  series?: Series
52
- regions?: { label: string; from: string; to: string }[]
23
+ regions?: { label: string; from: string; to: string; fromType: 'Fixed' | 'Previous Days'; toType: 'Fixed' | 'Last Date' }[]
53
24
  runtimeSeriesLabels?: Object
54
25
  dataFormat?: Object
55
26
  runtime: Runtime
@@ -0,0 +1,133 @@
1
+ import React from 'react'
2
+ import Tooltip from '@cdc/core/components/ui/Tooltip'
3
+ import Icon from '../ui/Icon'
4
+ import { CheckBox, TextField } from './Inputs'
5
+ import type { Table } from '@cdc/core/types/Table'
6
+ import MultiSelect from '../MultiSelect'
7
+ import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
8
+
9
+ interface DataTableProps {
10
+ config: {
11
+ table: Table
12
+ visualizationType: string
13
+ }
14
+ updateField: UpdateFieldFunc<string | boolean | string[] | number>
15
+ isDashboard: boolean
16
+ isLoadedFromUrl: boolean
17
+ columns: string[]
18
+ }
19
+
20
+ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard, isLoadedFromUrl, columns }) => {
21
+ return (
22
+ <>
23
+ <TextField
24
+ value={config.table.label}
25
+ updateField={updateField}
26
+ section='table'
27
+ fieldName='table-label'
28
+ id='tableLabel'
29
+ label='Data Table Title'
30
+ placeholder='Data Table'
31
+ tooltip={
32
+ <Tooltip style={{ textTransform: 'none' }}>
33
+ <Tooltip.Target>
34
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
35
+ </Tooltip.Target>
36
+ <Tooltip.Content>
37
+ <p>Label is required for Data Table for 508 Compliance</p>
38
+ </Tooltip.Content>
39
+ </Tooltip>
40
+ }
41
+ />
42
+ <CheckBox
43
+ value={config.table.show}
44
+ fieldName='show'
45
+ label='Show Data Table'
46
+ section='table'
47
+ updateField={updateField}
48
+ className='column-heading'
49
+ tooltip={
50
+ <Tooltip style={{ textTransform: 'none' }}>
51
+ <Tooltip.Target>
52
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
53
+ </Tooltip.Target>
54
+ <Tooltip.Content>
55
+ <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
56
+ </Tooltip.Content>
57
+ </Tooltip>
58
+ }
59
+ />
60
+ {config.visualizationType !== 'Box Plot' && (
61
+ <CheckBox
62
+ value={config.table.showVertical}
63
+ fieldName='showVertical'
64
+ label='Show Vertical Data'
65
+ section='table'
66
+ updateField={updateField}
67
+ className='column-heading'
68
+ tooltip={
69
+ <Tooltip style={{ textTransform: 'none' }}>
70
+ <Tooltip.Target>
71
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
72
+ </Tooltip.Target>
73
+ <Tooltip.Content>
74
+ <p>This will draw the data table with vertical data instead of horizontal.</p>
75
+ </Tooltip.Content>
76
+ </Tooltip>
77
+ }
78
+ />
79
+ )}
80
+ <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />
81
+ <TextField
82
+ value={config.table.caption}
83
+ updateField={updateField}
84
+ section='table'
85
+ type='textarea'
86
+ fieldName='caption'
87
+ label='Screen Reader Description'
88
+ placeholder=' Data table'
89
+ tooltip={
90
+ <Tooltip style={{ textTransform: 'none' }}>
91
+ <Tooltip.Target>
92
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
93
+ </Tooltip.Target>
94
+ <Tooltip.Content>
95
+ <p>Enter a description of the data table to be read by screen readers.</p>
96
+ </Tooltip.Content>
97
+ </Tooltip>
98
+ }
99
+ />
100
+ <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label='Limit Table Height' updateField={updateField} />
101
+ <CheckBox
102
+ value={config.table.customTableConfig}
103
+ fieldName='customTableConfig'
104
+ label='Customize Table Config'
105
+ section='table'
106
+ updateField={updateField}
107
+ tooltip={
108
+ <Tooltip style={{ textTransform: 'none' }}>
109
+ <Tooltip.Target>
110
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
111
+ </Tooltip.Target>
112
+ <Tooltip.Content>
113
+ <p>This will display all available columns in the data set. It will not show any columns where all of the column cells are null.</p>
114
+ </Tooltip.Content>
115
+ </Tooltip>
116
+ }
117
+ />
118
+ {config.table.customTableConfig && <MultiSelect options={columns.map(c => ({ label: c, value: c }))} fieldName='excludeColumns' label='Exclude Columns' section='table' updateField={updateField} />}
119
+ {config.table.limitHeight && <TextField value={config.table.height} fieldName='height' label='Data Table Height' type='number' min={0} max={500} placeholder='Height(px)' updateField={updateField} />}
120
+ <CheckBox value={config.table.expanded} fieldName='expanded' label='Expanded by Default' section='table' updateField={updateField} />
121
+ {isDashboard && <CheckBox value={config.table.showDataTableLink} fieldName='showDataTableLink' label='Show Data Table Name & Link' section='table' updateField={updateField} />}
122
+ {isLoadedFromUrl && <CheckBox value={config.table.showDownloadUrl} fieldName='showDownloadUrl' label='Show URL to Automatically Updated Data' section='table' updateField={updateField} />}
123
+ <CheckBox value={config.table.download} fieldName='download' label='Show Download CSV Link' section='table' updateField={updateField} />
124
+ <CheckBox value={config.table.showDownloadImgButton} fieldName='showDownloadImgButton' label='Display Image Button' section='table' updateField={updateField} />
125
+ <label>
126
+ <span className='edit-label column-heading'>Table Cell Min Width</span>
127
+ <input type='number' value={config.table.cellMinWidth ? config.table.cellMinWidth : 0} onChange={e => updateField('table', null, 'cellMinWidth', e.target.value)} />
128
+ </label>
129
+ </>
130
+ )
131
+ }
132
+
133
+ export default DataTable