@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.
Files changed (40) hide show
  1. package/components/AdvancedEditor/AdvancedEditor.tsx +11 -9
  2. package/components/DataTable/DataTable.tsx +48 -26
  3. package/components/DataTable/components/ChartHeader.tsx +1 -1
  4. package/components/DataTable/helpers/getChartCellValue.ts +11 -5
  5. package/components/DataTable/helpers/getDataSeriesColumns.ts +7 -3
  6. package/components/DataTable/helpers/mapCellMatrix.tsx +64 -33
  7. package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +80 -0
  8. package/components/EditorPanel/DataTableEditor.tsx +28 -18
  9. package/components/EditorPanel/Inputs.tsx +2 -1
  10. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +23 -0
  11. package/components/Filters/Filters.tsx +20 -8
  12. package/components/Layout/components/Visualization/visualizations.scss +1 -1
  13. package/components/MediaControls.jsx +14 -7
  14. package/components/NestedDropdown/NestedDropdown.tsx +5 -1
  15. package/components/NestedDropdown/nesteddropdown.styles.css +8 -4
  16. package/components/elements/Button.jsx +4 -2
  17. package/dist/cove-main.css +98 -151
  18. package/dist/cove-main.css.map +1 -1
  19. package/helpers/DataTransform.ts +2 -2
  20. package/helpers/addValuesToFilters.ts +1 -1
  21. package/helpers/coveUpdateWorker.ts +12 -7
  22. package/helpers/formatConfigBeforeSave.ts +30 -8
  23. package/helpers/isRightAlignedTableValue.js +5 -1
  24. package/helpers/isSolr.ts +13 -0
  25. package/helpers/labelHash.ts +21 -0
  26. package/helpers/pivotData.ts +14 -7
  27. package/helpers/tests/formatConfigBeforeSave.test.ts +68 -0
  28. package/helpers/tests/pivotData.test.ts +23 -19
  29. package/helpers/ver/4.25.3.ts +20 -0
  30. package/helpers/ver/tests/versionNeedsUpdate.test.ts +28 -0
  31. package/package.json +2 -2
  32. package/styles/_global-variables.scss +2 -1
  33. package/styles/_global.scss +18 -9
  34. package/styles/base.scss +42 -0
  35. package/styles/filters.scss +5 -11
  36. package/styles/v2/components/button.scss +48 -12
  37. package/styles/v2/themes/_color-definitions.scss +1 -4
  38. package/types/General.ts +0 -1
  39. package/types/Table.ts +2 -0
  40. 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/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
39
- dashboard: ['Dashboard', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
40
- map: ['Maps', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html', <MapIcon />],
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 href={typeLookup[config.type][1]} target='_blank' rel='noopener noreferrer' className='guidance-link'>
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?: Function
36
+ displayGeoName?: (row: string) => string
36
37
  expandDataTable: boolean
37
- formatLegendLocation?: Function
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?: Function // used for Maps only
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
- if (config.table.pivot) {
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
- const { columnName, valueColumns } = config.table.pivot
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?.valueColumn])
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
- if (config.table.showDownloadLinkBelow) {
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 rawData.map(row => ({ FullGeoName: formatLegendLocation(row[config.columns.geo.name]), ...row }))
253
+ return csvData.map(row => ({ FullGeoName: formatLegendLocation(row[config.columns.geo.name]), ...row }))
235
254
  } else {
236
- return rawData
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
- classes.push('mt-4', 'mb-2')
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
- classes.push('mt-2')
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
- {(config.table.download || config.general?.showDownloadButton) && (
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
- {config.general?.showDownloadButton && !config.table.showDownloadLinkBelow && <TableMediaControls />}
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.general?.showDownloadButton && config.table.showDownloadLinkBelow && (
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 === 'pivotColumn') return ''
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
- let inthere = false
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 === column) {
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 === 'pivotColumn') return -1
61
- if (b === 'pivotColumn') return 1
60
+ if (a === '_pivotedFrom') return -1
61
+ if (b === '_pivotedFrom') return 1
62
62
  return columnOrderingHash[a] - columnOrderingHash[b]
63
63
  })
64
- return tmpSeriesColumns
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
- if (
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
- // check for special classes
63
- let specialValFound = ''
64
- let columnName = config.columns[column].name
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