@cdc/core 4.24.7 → 4.24.9-1

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 (69) hide show
  1. package/LICENSE +201 -0
  2. package/assets/icon-gear-multi.svg +23 -0
  3. package/components/Alert/components/Alert.styles.css +15 -0
  4. package/components/Alert/components/Alert.tsx +39 -0
  5. package/components/Alert/index.tsx +3 -0
  6. package/components/DataTable/DataTable.tsx +106 -30
  7. package/components/DataTable/helpers/chartCellMatrix.tsx +3 -3
  8. package/components/DataTable/helpers/getChartCellValue.ts +1 -1
  9. package/components/DataTable/helpers/getDataSeriesColumns.ts +2 -2
  10. package/components/DataTable/helpers/mapCellMatrix.tsx +3 -3
  11. package/components/DataTable/types/TableConfig.ts +1 -1
  12. package/components/EditorPanel/Inputs.tsx +13 -4
  13. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +268 -0
  14. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +161 -82
  15. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +31 -45
  16. package/components/Filters.tsx +223 -180
  17. package/components/Layout/components/Responsive.tsx +14 -4
  18. package/components/Layout/components/Sidebar/components/Sidebar.tsx +14 -5
  19. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +15 -16
  20. package/components/Layout/components/Visualization/index.tsx +7 -1
  21. package/components/Layout/components/Visualization/visualizations.scss +32 -26
  22. package/components/Layout/styles/editor.scss +0 -8
  23. package/components/Legend/Legend.Gradient.tsx +133 -0
  24. package/components/LegendShape.tsx +28 -0
  25. package/components/MultiSelect/MultiSelect.tsx +6 -3
  26. package/components/NestedDropdown/NestedDropdown.tsx +47 -52
  27. package/components/NestedDropdown/nesteddropdown.styles.css +19 -25
  28. package/components/Table/Table.tsx +8 -5
  29. package/components/Table/components/Cell.tsx +2 -2
  30. package/components/Table/components/Row.tsx +25 -7
  31. package/components/_stories/Layout.Debug.stories.tsx +91 -0
  32. package/components/_stories/_mocks/bar-chart-suppressed.json +474 -0
  33. package/components/_stories/styles.scss +13 -1
  34. package/components/createBarElement.jsx +4 -4
  35. package/components/ui/Icon.tsx +21 -14
  36. package/components/ui/Title/Title.scss +0 -8
  37. package/helpers/DataTransform.ts +2 -2
  38. package/helpers/addValuesToFilters.ts +95 -16
  39. package/helpers/cove/accessibility.ts +16 -4
  40. package/helpers/coveUpdateWorker.ts +24 -10
  41. package/helpers/filterVizData.ts +23 -4
  42. package/helpers/formatConfigBeforeSave.ts +22 -2
  43. package/helpers/gatherQueryParams.ts +12 -2
  44. package/helpers/getGradientLegendWidth.ts +15 -0
  45. package/helpers/getTextWidth.ts +18 -0
  46. package/helpers/scaling.ts +7 -0
  47. package/helpers/tests/addValuesToFilters.test.ts +55 -0
  48. package/helpers/tests/filterVizData.test.ts +31 -0
  49. package/helpers/tests/gatherQueryParams.test.ts +22 -0
  50. package/helpers/tests/invertValue.test.ts +35 -0
  51. package/helpers/updatePaletteNames.ts +19 -0
  52. package/helpers/{useDataVizClasses.js → useDataVizClasses.ts} +3 -2
  53. package/helpers/ver/4.24.5.ts +3 -3
  54. package/helpers/ver/4.24.7.ts +34 -3
  55. package/helpers/ver/4.24.9.ts +63 -0
  56. package/helpers/ver/tests/4.24.9.test.ts +22 -0
  57. package/helpers/ver/versionNeedsUpdate.ts +9 -0
  58. package/package.json +3 -3
  59. package/styles/_button-section.scss +1 -1
  60. package/styles/_global.scss +6 -2
  61. package/styles/filters.scss +4 -0
  62. package/types/Axis.ts +3 -0
  63. package/types/Dimensions.ts +1 -0
  64. package/types/General.ts +1 -1
  65. package/types/VizFilter.ts +24 -3
  66. package/components/LegendCircle.jsx +0 -17
  67. package/helpers/updatePaletteNames.js +0 -16
  68. /package/components/{Waiting.jsx → Waiting.tsx} +0 -0
  69. /package/helpers/ver/{4.23.4.ts → 4.24.4.ts} +0 -0
@@ -1,53 +1,39 @@
1
- import { VizFilter } from '../../../../types/VizFilter'
2
- import { filterOrderOptions } from '../../../Filters'
3
1
  import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
4
2
 
5
3
  type FilterOrderProps = {
6
- filterIndex: number
7
- filter: VizFilter
8
- updateFilterProp: (prop: string, index: number, value: string) => void
9
- handleFilterOrder: (sourceIndex: number, destinationIndex: number, filterIndex: number, filter: VizFilter) => void
4
+ orderedValues: string[]
5
+ handleFilterOrder?: (index1: number, index2: number) => void
10
6
  }
11
-
12
- const FilterOrder: React.FC<FilterOrderProps> = ({ filterIndex, filter, updateFilterProp, handleFilterOrder }) => {
7
+ const FilterOrder: React.FC<FilterOrderProps> = ({ orderedValues, handleFilterOrder }) => {
13
8
  return (
14
- <label>
15
- <span className='edit-filterOrder column-heading'>Filter Order</span>
16
- <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', filterIndex, e.target.value)}>
17
- {filterOrderOptions.map((option, index) => {
18
- return (
19
- <option value={option.value} key={`filter-${index}`}>
20
- {option.label}
21
- </option>
22
- )
23
- })}
24
- </select>
25
-
26
- {filter.order === 'cust' && (
27
- <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, filterIndex, filter)}>
28
- <Droppable droppableId='filter_order'>
29
- {provided => (
30
- <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
31
- {filter?.values.map((value, index) => {
32
- return (
33
- <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
34
- {(provided, snapshot) => (
35
- <li>
36
- <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={provided.draggableProps.style} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
37
- {value}
38
- </div>
39
- </li>
40
- )}
41
- </Draggable>
42
- )
43
- })}
44
- {provided.placeholder}
45
- </ul>
46
- )}
47
- </Droppable>
48
- </DragDropContext>
49
- )}
50
- </label>
9
+ <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source?.index, destination?.index)}>
10
+ <Droppable droppableId='filter_order'>
11
+ {provided => (
12
+ <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
13
+ {orderedValues?.map((value, index) => {
14
+ return (
15
+ <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
16
+ {(provided, snapshot) => (
17
+ <li>
18
+ <div
19
+ className={snapshot.isDragging ? 'currently-dragging' : ''}
20
+ style={provided.draggableProps.style}
21
+ ref={provided.innerRef}
22
+ {...provided.draggableProps}
23
+ {...provided.dragHandleProps}
24
+ >
25
+ {value}
26
+ </div>
27
+ </li>
28
+ )}
29
+ </Draggable>
30
+ )
31
+ })}
32
+ {provided.placeholder}
33
+ </ul>
34
+ )}
35
+ </Droppable>
36
+ </DragDropContext>
51
37
  )
52
38
  }
53
39
 
@@ -1,21 +1,21 @@
1
- import { useState, useEffect } from 'react'
1
+ import { useState, useEffect, useMemo } from 'react'
2
2
  import { useId } from 'react'
3
3
 
4
4
  // CDC
5
5
  import Button from './elements/Button'
6
6
  import { getQueryParams, updateQueryString } from '../helpers/queryStringUtils'
7
-
8
- // Third Party
9
- import PropTypes from 'prop-types'
10
7
  import MultiSelect from './MultiSelect'
11
8
  import { Visualization } from '../types/Visualization'
12
- import { MultiSelectFilter, VizFilter } from '../types/VizFilter'
9
+ import { MultiSelectFilter, OrderBy, VizFilter } from '../types/VizFilter'
13
10
  import { filterVizData } from '../helpers/filterVizData'
14
11
  import { addValuesToFilters } from '../helpers/addValuesToFilters'
12
+ import { DimensionsType } from '../types/Dimensions'
13
+ import NestedDropdown from './NestedDropdown'
14
+ import _ from 'lodash'
15
15
 
16
- export const filterStyleOptions = ['dropdown', 'pill', 'tab', 'tab bar', 'multi-select']
16
+ export const filterStyleOptions = ['dropdown', 'nested-dropdown', 'pill', 'tab', 'tab bar', 'multi-select']
17
17
 
18
- export const filterOrderOptions = [
18
+ export const filterOrderOptions: { label: string; value: OrderBy }[] = [
19
19
  {
20
20
  label: 'Ascending Alphanumeric',
21
21
  value: 'asc'
@@ -31,27 +31,19 @@ export const filterOrderOptions = [
31
31
  ]
32
32
 
33
33
  export const handleSorting = singleFilter => {
34
- const { order } = singleFilter
35
-
36
- const sortAsc = (a, b) => {
37
- return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
34
+ const singleFilterValues = _.cloneDeep(singleFilter.values)
35
+ if (singleFilter.order === 'cust' && singleFilter.filterStyle !== 'nested-dropdown') {
36
+ singleFilter.values = singleFilter.orderedValues?.length ? singleFilter.orderedValues : singleFilterValues
37
+ return singleFilter
38
38
  }
39
39
 
40
- const sortDesc = (a, b) => {
41
- return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
40
+ const sort = (a, b) => {
41
+ const asc = singleFilter.order !== 'desc'
42
+ return (asc ? a : b).toString().localeCompare((asc ? b : a).toString(), 'en', { numeric: true })
42
43
  }
43
44
 
44
- if (!order || order === '') {
45
- singleFilter.order = 'asc'
46
- }
47
-
48
- if (order === 'desc') {
49
- singleFilter.values = singleFilter.values.sort(sortDesc)
50
- }
45
+ singleFilter.values = singleFilterValues.sort(sort)
51
46
 
52
- if (order === 'asc') {
53
- singleFilter.values = singleFilter.values.sort(sortAsc)
54
- }
55
47
  return singleFilter
56
48
  }
57
49
 
@@ -83,7 +75,9 @@ export const useFilters = props => {
83
75
  const [movedItem] = updatedValues.splice(idx1, 1)
84
76
  updatedValues.splice(idx2, 0, movedItem)
85
77
 
86
- const filtersCopy = hasStandardFilterBehavior.includes(visualizationConfig.type) ? [...visualizationConfig.filters] : [...filteredData]
78
+ const filtersCopy = hasStandardFilterBehavior.includes(visualizationConfig.type)
79
+ ? [...visualizationConfig.filters]
80
+ : [...filteredData]
87
81
  const filterItem = { ...filtersCopy[filterIndex] }
88
82
 
89
83
  // Overwrite filterItem.values since thats what we map through in the editor panel
@@ -102,17 +96,20 @@ export const useFilters = props => {
102
96
  setConfig({ ...visualizationConfig, filters: filtersCopy })
103
97
  }
104
98
 
105
- const announceChange = text => {}
106
-
107
99
  const changeFilterActive = (index, value) => {
108
- const newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
100
+ let newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
109
101
 
110
102
  if (visualizationConfig.filterBehavior === 'Apply Button') {
111
103
  newFilters[index].queuedActive = value
112
104
  setShowApplyButton(true)
113
105
  } else {
114
106
  const newFilter = newFilters[index]
115
- newFilter.active = value
107
+ if (newFilter.filterStyle !== 'nested-dropdown') {
108
+ newFilter.active = value
109
+ } else {
110
+ newFilter.active = value[0]
111
+ newFilter.subGrouping.active = value[1]
112
+ }
116
113
 
117
114
  const queryParams = getQueryParams()
118
115
  if (newFilter.setByQueryParameter && queryParams[newFilter.setByQueryParameter] !== newFilter.active) {
@@ -120,10 +117,14 @@ export const useFilters = props => {
120
117
  updateQueryString(queryParams)
121
118
  }
122
119
  }
123
- setConfig({
124
- ...visualizationConfig,
125
- filters: newFilters
126
- })
120
+
121
+ if (!visualizationConfig.dynamicSeries) {
122
+ newFilters = addValuesToFilters(newFilters, excludedData)
123
+ setConfig({
124
+ ...visualizationConfig,
125
+ filters: newFilters
126
+ })
127
+ }
127
128
 
128
129
  // Used for setting active filter, fromHash breaks the filteredData functionality.
129
130
  if (visualizationConfig.type === 'map' && visualizationConfig.filterBehavior === 'Filter Change') {
@@ -131,8 +132,52 @@ export const useFilters = props => {
131
132
  }
132
133
 
133
134
  // If we're on a chart and not using the apply button
134
- if (hasStandardFilterBehavior.includes(visualizationConfig.type) && visualizationConfig.filterBehavior === 'Filter Change') {
135
- setFilteredData(filterVizData(newFilters, excludedData))
135
+ if (
136
+ hasStandardFilterBehavior.includes(visualizationConfig.type) &&
137
+ visualizationConfig.filterBehavior === 'Filter Change'
138
+ ) {
139
+ const newFilteredData = filterVizData(newFilters, excludedData)
140
+ setFilteredData(newFilteredData)
141
+
142
+ if (visualizationConfig.dynamicSeries) {
143
+ const runtime = visualizationConfig.runtime || {}
144
+ runtime.series = []
145
+ runtime.seriesLabels = {}
146
+ runtime.seriesLabelsAll = []
147
+
148
+ if (newFilteredData && newFilteredData.length && newFilteredData.length > 0) {
149
+ Object.keys(newFilteredData[0]).forEach(seriesKey => {
150
+ if (
151
+ seriesKey !== visualizationConfig.xAxis.dataKey &&
152
+ newFilteredData[0][seriesKey] &&
153
+ (!visualizationConfig.filters ||
154
+ visualizationConfig.filters?.filter(filter => filter.columnName === seriesKey).length === 0) &&
155
+ (!visualizationConfig.columns || Object.keys(visualizationConfig.columns).indexOf(seriesKey) === -1)
156
+ ) {
157
+ runtime.series.push({
158
+ dataKey: seriesKey,
159
+ type: visualizationConfig.dynamicSeriesType,
160
+ lineType: visualizationConfig.dynamicSeriesLineType,
161
+ tooltip: true
162
+ })
163
+ }
164
+ })
165
+ }
166
+
167
+ runtime.seriesKeys = runtime.series
168
+ ? runtime.series.map(series => {
169
+ runtime.seriesLabels[series.dataKey] = series.name || series.label || series.dataKey
170
+ runtime.seriesLabelsAll.push(series.name || series.dataKey)
171
+ return series.dataKey
172
+ })
173
+ : []
174
+
175
+ setConfig({
176
+ ...visualizationConfig,
177
+ filters: newFilters,
178
+ runtime
179
+ })
180
+ }
136
181
  }
137
182
  }
138
183
 
@@ -209,7 +254,6 @@ export const useFilters = props => {
209
254
  return {
210
255
  handleApplyButton,
211
256
  changeFilterActive,
212
- announceChange,
213
257
  showApplyButton,
214
258
  handleReset,
215
259
  filterConstants,
@@ -221,9 +265,15 @@ export const useFilters = props => {
221
265
  }
222
266
 
223
267
  type FilterProps = {
224
- filteredData
225
- dimensions
268
+ filteredData: Object[]
269
+ dimensions: DimensionsType
226
270
  config: Visualization
271
+ // function for updating the runtime filters
272
+ setFilteredData: Function
273
+ // updating function for setting fitlerBehavior
274
+ setConfig: Function
275
+ // exclusions
276
+ exclusions: any[]
227
277
  }
228
278
 
229
279
  const Filters = (props: FilterProps) => {
@@ -238,7 +288,6 @@ const Filters = (props: FilterProps) => {
238
288
  const {
239
289
  handleApplyButton,
240
290
  changeFilterActive,
241
- announceChange,
242
291
  showApplyButton,
243
292
  handleReset,
244
293
  filterConstants,
@@ -247,7 +296,7 @@ const Filters = (props: FilterProps) => {
247
296
 
248
297
  useEffect(() => {
249
298
  if (!dimensions) return
250
- if (dimensions[0] < 768 && filters?.length > 0) {
299
+ if (Number(dimensions[0]) < 768 && filters?.length > 0) {
251
300
  setMobileFilterStyle(true)
252
301
  } else {
253
302
  setMobileFilterStyle(false)
@@ -261,40 +310,7 @@ const Filters = (props: FilterProps) => {
261
310
  }
262
311
  }, [changeFilterActive, selectedFilter])
263
312
 
264
- const Filters = props => props.children
265
-
266
- const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : visualizationConfig?.visualizationType === 'Spark Line' ? null : theme]
267
- // Exterior Section Wrapper
268
- Filters.Section = ({ children }) => {
269
- return (
270
- visualizationConfig?.filters && (
271
- <section className={filterSectionClassList.join(' ')}>
272
- <p className='filters-section__intro-text'>
273
- {filters?.some(f => f.active) ? filterConstants.introText : ''} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
274
- </p>
275
- <div className='filters-section__wrapper'>{children}</div>
276
- </section>
277
- )
278
- )
279
- }
280
-
281
- // Apply/Reset Buttons
282
- Filters.ApplyBehavior = () => {
283
- if (filterBehavior !== 'Apply Button') return
284
- const applyButtonClasses = [general?.headerColor ? general.headerColor : theme, 'apply']
285
- return (
286
- <div className='filters-section__buttons'>
287
- <Button onClick={() => handleApplyButton(filters)} disabled={!showApplyButton} className={applyButtonClasses.join(' ')}>
288
- {filterConstants.buttonText}
289
- </Button>
290
- <a href='#!' role='button' onClick={handleReset}>
291
- {filterConstants.resetText}
292
- </a>
293
- </div>
294
- )
295
- }
296
-
297
- Filters.TabBar = props => {
313
+ const TabBar = props => {
298
314
  const { filter: singleFilter, index: outerIndex } = props
299
315
  return (
300
316
  <section className='single-filters__tab-bar'>
@@ -324,11 +340,7 @@ const Filters = (props: FilterProps) => {
324
340
  )
325
341
  }
326
342
 
327
- Filters.Pills = props => props.pills
328
-
329
- Filters.Tabs = props => props.tabs
330
-
331
- Filters.Dropdown = props => {
343
+ const Dropdown = props => {
332
344
  const { index: outerIndex, label, active, filters } = props
333
345
  return (
334
346
  <select
@@ -340,7 +352,6 @@ const Filters = (props: FilterProps) => {
340
352
  value={active}
341
353
  onChange={e => {
342
354
  changeFilterActive(outerIndex, e.target.value)
343
- announceChange(`Filter ${label} value has been changed to ${e.target.value}, please reference the data table to see updated values.`)
344
355
  }}
345
356
  >
346
357
  {filters}
@@ -348,130 +359,162 @@ const Filters = (props: FilterProps) => {
348
359
  )
349
360
  }
350
361
 
362
+ const vizFiltersWithValues = useMemo(() => {
363
+ // Here charts is using config.filters where maps is using a runtime value
364
+ let vizfilters = type === 'map' ? filteredData : filters
365
+ if (!vizfilters) return []
366
+ if (vizfilters.fromHash) delete vizfilters.fromHash // support for Maps config
367
+ return addValuesToFilters(vizfilters as VizFilter[], visualizationConfig.data)
368
+ }, [filters, filteredData])
369
+
351
370
  // Resolve Filter Styles
352
- Filters.Style = () => {
353
- if (filters || filteredData) {
354
- // Here charts is using config.filters where maps is using a runtime value
355
- let filtersToLoop = type === 'map' ? filteredData : filters
356
-
357
- // Remove fromHash if it exists on filters to loop so we can loop nicely
358
- delete filtersToLoop.fromHash
359
-
360
- return addValuesToFilters<VizFilter>(filtersToLoop, visualizationConfig.data).map((singleFilter: VizFilter, outerIndex) => {
361
- if (singleFilter.showDropdown === false) return
362
-
363
- const values = []
364
- const pillValues = []
365
- const tabValues = []
366
- const tabBarValues = []
367
-
368
- const { active, queuedActive, label, filterStyle } = singleFilter as VizFilter
369
-
370
- handleSorting(singleFilter)
371
-
372
- singleFilter.values?.forEach((filterOption, index) => {
373
- const pillClassList = ['pill', active === filterOption ? 'pill--active' : null, theme && theme]
374
- const tabClassList = ['tab', active === filterOption && 'tab--active', theme && theme]
375
-
376
- pillValues.push(
377
- <div className='pill__wrapper' key={`pill-${index}`}>
378
- <button
379
- id={`${filterOption}-${outerIndex}-${index}-${id}`}
380
- className={pillClassList.join(' ')}
381
- onKeyDown={e => {
382
- if (e.keyCode === 13) {
383
- changeFilterActive(outerIndex, filterOption)
384
- setSelectedFilter(e.target)
385
- }
386
- }}
387
- onClick={e => {
388
- changeFilterActive(outerIndex, filterOption)
389
- setSelectedFilter(e.target)
390
- }}
391
- name={label}
392
- >
393
- {filterOption}
394
- </button>
395
- </div>
396
- )
371
+ const Style = () => {
372
+ return vizFiltersWithValues.map((singleFilter: VizFilter, outerIndex) => {
373
+ if (singleFilter.showDropdown === false) return
397
374
 
398
- values.push(
399
- <option key={index} value={filterOption} aria-label={filterOption}>
400
- {singleFilter.labels && singleFilter.labels[filterOption] ? singleFilter.labels[filterOption] : filterOption}
401
- </option>
402
- )
375
+ const DropdownOptions = []
376
+ const Pills = []
377
+ const Tabs = []
378
+
379
+ const { active, queuedActive, label, filterStyle } = singleFilter as VizFilter
403
380
 
404
- tabValues.push(
381
+ handleSorting(singleFilter)
382
+
383
+ singleFilter.values?.forEach((filterOption, index) => {
384
+ const pillClassList = ['pill', active === filterOption ? 'pill--active' : null, theme && theme]
385
+ const tabClassList = ['tab', active === filterOption && 'tab--active', theme && theme]
386
+
387
+ Pills.push(
388
+ <div className='pill__wrapper' key={`pill-${index}`}>
405
389
  <button
406
390
  id={`${filterOption}-${outerIndex}-${index}-${id}`}
407
- className={tabClassList.join(' ')}
408
- onClick={e => {
409
- changeFilterActive(outerIndex, filterOption)
410
- setSelectedFilter(e.target)
411
- }}
391
+ className={pillClassList.join(' ')}
412
392
  onKeyDown={e => {
413
393
  if (e.keyCode === 13) {
414
394
  changeFilterActive(outerIndex, filterOption)
415
395
  setSelectedFilter(e.target)
416
396
  }
417
397
  }}
398
+ onClick={e => {
399
+ changeFilterActive(outerIndex, filterOption)
400
+ setSelectedFilter(e.target)
401
+ }}
402
+ name={label}
418
403
  >
419
404
  {filterOption}
420
405
  </button>
421
- )
406
+ </div>
407
+ )
422
408
 
423
- tabBarValues.push(filterOption)
424
- })
409
+ DropdownOptions.push(
410
+ <option key={index} value={filterOption} aria-label={filterOption}>
411
+ {singleFilter.labels && singleFilter.labels[filterOption]
412
+ ? singleFilter.labels[filterOption]
413
+ : filterOption}
414
+ </option>
415
+ )
425
416
 
426
- const classList = ['single-filters', mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`]
427
-
428
- return (
429
- <div className={classList.join(' ')} key={outerIndex}>
430
- <>
431
- {label && <label htmlFor={`filter-${outerIndex}`}>{label}</label>}
432
- {filterStyle === 'tab' && !mobileFilterStyle && <Filters.Tabs tabs={tabValues} />}
433
- {filterStyle === 'pill' && !mobileFilterStyle && <Filters.Pills pills={pillValues} />}
434
- {filterStyle === 'tab bar' && !mobileFilterStyle && <Filters.TabBar filter={singleFilter} index={outerIndex} />}
435
- {(filterStyle === 'dropdown' || mobileFilterStyle) && <Filters.Dropdown filter={singleFilter} index={outerIndex} label={label} active={queuedActive || active} filters={values} />}
436
- {filterStyle === 'multi-select' && (
437
- <MultiSelect
438
- options={singleFilter.values.map(v => ({ value: v, label: v }))}
439
- fieldName={outerIndex}
440
- updateField={(_section, _subSection, fieldName, value) => changeFilterActive(fieldName, value)}
441
- selected={singleFilter.active as string[]}
442
- limit={(singleFilter as MultiSelectFilter).selectLimit || 5}
443
- />
444
- )}
445
- </>
446
- </div>
417
+ Tabs.push(
418
+ <button
419
+ id={`${filterOption}-${outerIndex}-${index}-${id}`}
420
+ className={tabClassList.join(' ')}
421
+ onClick={e => {
422
+ changeFilterActive(outerIndex, filterOption)
423
+ setSelectedFilter(e.target)
424
+ }}
425
+ onKeyDown={e => {
426
+ if (e.keyCode === 13) {
427
+ changeFilterActive(outerIndex, filterOption)
428
+ setSelectedFilter(e.target)
429
+ }
430
+ }}
431
+ >
432
+ {filterOption}
433
+ </button>
447
434
  )
448
435
  })
449
- }
436
+
437
+ const classList = [
438
+ 'single-filters',
439
+ mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`
440
+ ]
441
+ const mobileExempt = ['nested-dropdown', 'multi-select'].includes(filterStyle)
442
+ const showDefaultDropdown = (filterStyle === 'dropdown' || mobileFilterStyle) && !mobileExempt
443
+ return (
444
+ <div className={classList.join(' ')} key={outerIndex}>
445
+ <>
446
+ {label && <label htmlFor={`filter-${outerIndex}`}>{label}</label>}
447
+ {filterStyle === 'tab' && !mobileFilterStyle && Tabs}
448
+ {filterStyle === 'pill' && !mobileFilterStyle && Pills}
449
+ {filterStyle === 'tab bar' && !mobileFilterStyle && <TabBar filter={singleFilter} index={outerIndex} />}
450
+ {filterStyle === 'multi-select' && (
451
+ <MultiSelect
452
+ options={singleFilter.values.map(v => ({ value: v, label: v }))}
453
+ fieldName={outerIndex}
454
+ updateField={(_section, _subSection, fieldName, value) => changeFilterActive(fieldName, value)}
455
+ selected={singleFilter.active as string[]}
456
+ limit={(singleFilter as MultiSelectFilter).selectLimit || 5}
457
+ />
458
+ )}
459
+ {filterStyle === 'nested-dropdown' && (
460
+ <NestedDropdown
461
+ currentFilter={singleFilter}
462
+ listLabel={label}
463
+ handleSelectedItems={value => changeFilterActive(outerIndex, value)}
464
+ />
465
+ )}
466
+ {showDefaultDropdown && (
467
+ <Dropdown
468
+ filter={singleFilter}
469
+ index={outerIndex}
470
+ label={label}
471
+ active={queuedActive || active}
472
+ filters={DropdownOptions}
473
+ />
474
+ )}
475
+ </>
476
+ </div>
477
+ )
478
+ })
450
479
  }
451
480
 
452
481
  if (visualizationConfig?.filters?.length === 0) return
482
+ const filterSectionClassList = [
483
+ `filters-section legend_${visualizationConfig?.legend?.hide ? 'hidden' : 'visible'}_${
484
+ visualizationConfig?.legend?.position || ''
485
+ }`,
486
+ type === 'map' ? general.headerColor : visualizationConfig?.visualizationType === 'Spark Line' ? null : theme
487
+ ]
453
488
  return (
454
- <Filters>
455
- <Filters.Section>
456
- <Filters.Style />
457
- <Filters.ApplyBehavior />
458
- </Filters.Section>
459
- </Filters>
489
+ <section className={filterSectionClassList.join(' ')}>
490
+ <p className='filters-section__intro-text'>
491
+ {filters?.some(f => f.active && f.showDropdown) ? filterConstants.introText : ''}{' '}
492
+ {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
493
+ </p>
494
+ <div className='filters-section__wrapper'>
495
+ {' '}
496
+ <>
497
+ <Style />
498
+ {filterBehavior === 'Apply Button' ? (
499
+ <div className='filters-section__buttons'>
500
+ <Button
501
+ onClick={() => handleApplyButton(filters)}
502
+ disabled={!showApplyButton}
503
+ className={[general?.headerColor ? general.headerColor : theme, 'apply'].join(' ')}
504
+ >
505
+ {filterConstants.buttonText}
506
+ </Button>
507
+ <a href='#!' role='button' onClick={handleReset}>
508
+ {filterConstants.resetText}
509
+ </a>
510
+ </div>
511
+ ) : (
512
+ <></>
513
+ )}
514
+ </>
515
+ </div>
516
+ </section>
460
517
  )
461
518
  }
462
519
 
463
- Filters.propTypes = {
464
- // runtimeFilters in place
465
- filteredData: PropTypes.array,
466
- // function for updating the runtime filters
467
- setFilteredData: PropTypes.func,
468
- // the full apps config
469
- config: PropTypes.object,
470
- // updating function for setting fitlerBehavior
471
- setConfig: PropTypes.func,
472
- // exclusions
473
- excludedData: PropTypes.array,
474
- dimensions: PropTypes.array
475
- }
476
-
477
520
  export default Filters