@cdc/core 4.25.3 → 4.25.5-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/assets/icon-close.svg +1 -1
  2. package/components/DataTable/DataTable.tsx +18 -13
  3. package/components/DataTable/components/CellAnchor.tsx +1 -1
  4. package/components/DataTable/components/ChartHeader.tsx +2 -1
  5. package/components/DataTable/components/MapHeader.tsx +1 -0
  6. package/components/DataTable/helpers/chartCellMatrix.tsx +2 -1
  7. package/components/DataTable/helpers/mapCellMatrix.tsx +17 -7
  8. package/components/DownloadButton.tsx +17 -2
  9. package/components/EditorPanel/DataTableEditor.tsx +1 -1
  10. package/components/EditorPanel/Inputs.tsx +12 -4
  11. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +2 -1
  12. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +3 -1
  13. package/components/Filters/Filters.tsx +168 -429
  14. package/components/Filters/components/Dropdown.tsx +39 -0
  15. package/components/Filters/components/Tabs.tsx +82 -0
  16. package/components/Filters/helpers/getChangedFilters.ts +31 -0
  17. package/components/Filters/helpers/getNestedOptions.ts +2 -2
  18. package/components/Filters/helpers/getNewRuntime.ts +35 -0
  19. package/components/Filters/helpers/handleSorting.ts +2 -2
  20. package/components/Filters/helpers/tests/getChangedFilters.test.ts +92 -0
  21. package/components/Filters/helpers/tests/getNestedOptions.test.ts +31 -0
  22. package/components/Filters/helpers/tests/getNewRuntime.test.ts +82 -0
  23. package/components/Filters/index.ts +1 -1
  24. package/components/Layout/components/Visualization/index.tsx +3 -3
  25. package/components/Legend/Legend.Gradient.tsx +66 -23
  26. package/components/MultiSelect/multiselect.styles.css +2 -0
  27. package/components/NestedDropdown/NestedDropdown.tsx +2 -2
  28. package/components/RichTooltip/RichTooltip.tsx +37 -0
  29. package/components/RichTooltip/richTooltip.css +16 -0
  30. package/components/Table/Table.tsx +142 -142
  31. package/components/Table/components/Row.tsx +1 -1
  32. package/components/Table/table.styles.css +10 -0
  33. package/components/_stories/DataTable.stories.tsx +9 -2
  34. package/components/_stories/Table.stories.tsx +1 -1
  35. package/components/_stories/styles.scss +0 -4
  36. package/components/ui/Accordion.jsx +8 -1
  37. package/components/ui/Title/index.tsx +4 -1
  38. package/components/ui/Title/{Title.scss → title.styles.css} +0 -2
  39. package/components/ui/_stories/Colors.stories.mdx +220 -0
  40. package/components/ui/_stories/IconGallery.stories.mdx +14 -0
  41. package/components/ui/_stories/Title.stories.tsx +29 -4
  42. package/components/ui/accordion.styles.css +3 -0
  43. package/data/colorPalettes.js +0 -1
  44. package/dist/cove-main.css +3 -8
  45. package/dist/cove-main.css.map +1 -1
  46. package/helpers/constants.ts +6 -0
  47. package/helpers/cove/accessibility.ts +7 -8
  48. package/helpers/coveUpdateWorker.ts +5 -1
  49. package/helpers/filterOrderOptions.ts +17 -0
  50. package/helpers/isNumber.ts +20 -0
  51. package/helpers/isRightAlignedTableValue.js +1 -1
  52. package/helpers/pivotData.ts +16 -11
  53. package/helpers/tests/pivotData.test.ts +74 -0
  54. package/helpers/ver/4.25.3.ts +25 -2
  55. package/helpers/ver/4.25.4.ts +33 -0
  56. package/helpers/ver/tests/4.25.4.test.ts +24 -0
  57. package/helpers/viewports.ts +4 -0
  58. package/package.json +2 -3
  59. package/styles/_global-variables.scss +3 -0
  60. package/styles/_reset.scss +0 -6
  61. package/styles/v2/main.scss +0 -5
  62. package/types/General.ts +1 -0
  63. package/types/Legend.ts +1 -0
  64. package/LICENSE +0 -201
  65. package/components/ui/_stories/Colors.stories.tsx +0 -92
  66. package/components/ui/_stories/Icon.stories.tsx +0 -29
  67. package/helpers/cove/fontSettings.ts +0 -2
  68. package/helpers/isNumber.js +0 -24
  69. package/helpers/isNumberLog.js +0 -18
@@ -3,18 +3,22 @@ import _ from 'lodash'
3
3
 
4
4
  // CDC
5
5
  import Button from '../elements/Button'
6
- import { getQueryParams, updateQueryString } from '../../helpers/queryStringUtils'
7
6
  import MultiSelect from '../MultiSelect'
8
7
  import { Visualization } from '../../types/Visualization'
9
- import { MultiSelectFilter, OrderBy, VizFilter } from '../../types/VizFilter'
10
- import { filterVizData } from '../../helpers/filterVizData'
8
+ import { MultiSelectFilter, VizFilter } from '../../types/VizFilter'
11
9
  import { addValuesToFilters } from '../../helpers/addValuesToFilters'
12
10
  import { DimensionsType } from '../../types/Dimensions'
13
11
  import NestedDropdown from '../NestedDropdown'
14
12
  import { getNestedOptions } from './helpers/getNestedOptions'
15
- import { applyQueuedActive } from './helpers/applyQueuedActive'
16
- import { handleSorting } from './helpers/handleSorting'
17
13
  import { getWrappingStatuses } from './helpers/filterWrapping'
14
+ import { handleSorting } from './helpers/handleSorting'
15
+ import { getChangedFilters } from './helpers/getChangedFilters'
16
+ import { getNewRuntime } from './helpers/getNewRuntime'
17
+ import { filterVizData } from '../../helpers/filterVizData'
18
+ import { getQueryParams, updateQueryString } from '../../helpers/queryStringUtils'
19
+ import { applyQueuedActive } from './helpers/applyQueuedActive'
20
+ import Tabs from './components/Tabs'
21
+ import Dropdown from './components/Dropdown'
18
22
 
19
23
  export const VIZ_FILTER_STYLE = {
20
24
  dropdown: 'dropdown',
@@ -26,167 +30,96 @@ export const VIZ_FILTER_STYLE = {
26
30
  multiSelect: 'multi-select'
27
31
  } as const
28
32
 
29
- export const DROPDOWN_STYLES = 'py-2 ps-2 w-100 d-block'
30
-
31
33
  export type VizFilterStyle = (typeof VIZ_FILTER_STYLE)[keyof typeof VIZ_FILTER_STYLE]
32
34
 
33
35
  export const filterStyleOptions = Object.values(VIZ_FILTER_STYLE)
34
36
 
35
- export const filterOrderOptions: { label: string; value: OrderBy }[] = [
36
- {
37
- label: 'Ascending Alphanumeric',
38
- value: 'asc'
39
- },
40
- {
41
- label: 'Descending Alphanumeric',
42
- value: 'desc'
43
- },
44
- {
45
- label: 'Custom',
46
- value: 'cust'
47
- },
48
- { label: 'Order By Data Column', value: 'column' }
49
- ]
50
-
51
- export const useFilters = props => {
37
+ const BUTTON_TEXT = {
38
+ apply: 'Apply',
39
+ resetText: 'Clear Filters'
40
+ }
41
+
42
+ type FilterProps = {
43
+ filteredData: Object[]
44
+ dimensions: DimensionsType
45
+ config: Visualization
46
+ // function for updating the runtime filters
47
+ setFilteredData: Function
48
+ // updating function for setting fitlerBehavior
49
+ setConfig: Function
50
+ standaloneMap?: boolean
51
+ excludedData?: Object[]
52
+ getUniqueValues: Function
53
+ }
54
+
55
+ const Filters: React.FC<FilterProps> = ({
56
+ config: visualizationConfig,
57
+ filteredData,
58
+ dimensions,
59
+ standaloneMap,
60
+ setConfig,
61
+ setFilteredData,
62
+ excludedData,
63
+ getUniqueValues
64
+ }) => {
65
+ const { filters, general, theme, filterBehavior } = visualizationConfig
52
66
  const [showApplyButton, setShowApplyButton] = useState(false)
67
+ // Handle Wrapping Filters
68
+ const [wrappingFilters, setWrappingFilters] = useState<
69
+ Record<string, { highestWrappedWidth: number; isDropdown: boolean }>
70
+ >({})
53
71
 
54
- // Deconstructing: notice, adding more descriptive visualizationConfig name over config
55
- // visualizationConfig feels more robust for all vis types so that its not confused with config/state/etc.
56
- const {
57
- config: visualizationConfig,
58
- setConfig,
59
- filteredData,
60
- setFilteredData,
61
- excludedData,
62
- getUniqueValues,
63
- standaloneMap
64
- } = props
65
- const { data } = visualizationConfig
66
-
67
- /**
68
- * Re-orders a filter based on two indices and updates the runtime filters array and filters state
69
- * @param {number} idx1 - The index of the original position of the filter value.
70
- * @param {number} idx2 - The index of the new position for the filter value.
71
- * @param {number} filterIndex - The index of the filter item within the array of filter items.
72
- * @param {object} filter - The filter item itself, which contains an array of filter values.
73
- * @return {void} None. This function only updates the state of the component.
74
- *
75
- * @modifies {object} - The filter object passed in as a parameter
76
- * @modifies {array} - The filteredData state if visualizationConfig.type equals 'map'
77
- * @modifies {object} - The visualizationConfig state
78
- */
79
- const handleFilterOrder = (idx1, idx2, filterIndex, filter) => {
80
- // Create a shallow copy of the filter values array & update position of the values
81
- const updatedValues = [...filter.values]
82
- const [movedItem] = updatedValues.splice(idx1, 1)
83
- updatedValues.splice(idx2, 0, movedItem)
84
-
85
- const filtersCopy = !standaloneMap ? [...visualizationConfig.filters] : [...filteredData]
86
- const filterItem = { ...filtersCopy[filterIndex] }
87
-
88
- // Overwrite filterItem.values since thats what we map through in the editor panel
89
- filterItem.values = updatedValues
90
- filterItem.orderedValues = updatedValues
91
- if (!filterItem.active) filterItem.active = filterItem.defaultValue ? filterItem.defaultValue : updatedValues[0]
92
- filterItem.order = 'cust'
93
-
94
- // Update the filters
95
- filtersCopy[filterIndex] = filterItem
72
+ const wrappingFilterRefs = useRef({})
96
73
 
97
- if (standaloneMap) {
98
- setFilteredData(filtersCopy)
74
+ useEffect(() => {
75
+ const filterWrappingStatusesToUpdate = getWrappingStatuses(wrappingFilterRefs, wrappingFilters, filters)
76
+
77
+ if (filterWrappingStatusesToUpdate.length) {
78
+ const validStatuses = filterWrappingStatusesToUpdate.filter(Boolean) as [string, any][]
79
+ setWrappingFilters({ ...wrappingFilters, ...Object.fromEntries(validStatuses) })
99
80
  }
81
+ }, [filters, dimensions?.[0]])
82
+ // end of Handle Wrapping Filters
100
83
 
101
- setConfig({ ...visualizationConfig, filters: filtersCopy })
102
- }
84
+ const initialActiveFilters = useMemo(() => {
85
+ if (!filteredData) return []
86
+ return filters.map(filter => filter.active)
87
+ }, [])
88
+
89
+ const initialFiltersActive = useMemo(() => {
90
+ const activeFilters = filters.map(filter => filter.active)
91
+ return initialActiveFilters.every(filter => activeFilters.includes(filter))
92
+ }, [filters])
103
93
 
104
94
  const changeFilterActive = (index, value) => {
105
95
  let newFilters = standaloneMap ? [...filteredData] : [...visualizationConfig.filters]
106
96
 
107
- const newFilter = newFilters[index]
108
- if (visualizationConfig.filterBehavior === 'Apply Button') {
109
- newFilter.queuedActive = value
110
- setShowApplyButton(true)
111
- } else {
112
- if (newFilter.filterStyle !== 'nested-dropdown') {
113
- newFilter.active = value
114
- } else {
115
- newFilter.active = value[0]
116
- newFilter.subGrouping.active = value[1]
117
- }
118
-
119
- const queryParams = getQueryParams()
120
- if (newFilter.setByQueryParameter && queryParams[newFilter.setByQueryParameter] !== newFilter.active) {
121
- queryParams[newFilter.setByQueryParameter] = newFilter.active
122
- updateQueryString(queryParams)
123
- }
124
- if (
125
- newFilter?.subGrouping?.setByQueryParameter &&
126
- queryParams[newFilter?.subGrouping?.setByQueryParameter] !== newFilter?.subGrouping.active
127
- ) {
128
- queryParams[newFilter?.subGrouping?.setByQueryParameter] = newFilter.subGrouping.active
129
- updateQueryString(queryParams)
130
- }
131
- setFilteredData(newFilters)
132
- }
97
+ newFilters = getChangedFilters(newFilters, index, value, visualizationConfig.filterBehavior)
98
+ if (visualizationConfig.filterBehavior === 'Apply Button') setShowApplyButton(true)
133
99
 
134
100
  if (!visualizationConfig.dynamicSeries) {
135
- newFilters = addValuesToFilters(newFilters, excludedData)
101
+ const _newFilters = addValuesToFilters(newFilters, excludedData)
136
102
  setConfig({
137
103
  ...visualizationConfig,
138
- filters: newFilters
104
+ filters: _newFilters
139
105
  })
140
106
  }
141
107
 
142
- // Used for setting active filter, fromHash breaks the filteredData functionality.
143
- if (standaloneMap && visualizationConfig.filterBehavior === 'Filter Change') {
144
- setFilteredData(newFilters)
145
- }
146
-
147
- // If we're on a chart and not using the apply button
148
- if (!standaloneMap && visualizationConfig.filterBehavior === 'Filter Change') {
149
- const newFilteredData = filterVizData(newFilters, excludedData)
150
- setFilteredData(newFilteredData)
151
-
152
- if (visualizationConfig.dynamicSeries) {
153
- const runtime = visualizationConfig.runtime || {}
154
- runtime.series = []
155
- runtime.seriesLabels = {}
156
- runtime.seriesLabelsAll = []
157
-
158
- if (newFilteredData && newFilteredData.length && newFilteredData.length > 0) {
159
- Object.keys(newFilteredData[0]).forEach(seriesKey => {
160
- if (
161
- seriesKey !== visualizationConfig.xAxis.dataKey &&
162
- newFilteredData[0][seriesKey] &&
163
- (!visualizationConfig.filters ||
164
- visualizationConfig.filters?.filter(filter => filter.columnName === seriesKey).length === 0) &&
165
- (!visualizationConfig.columns || Object.keys(visualizationConfig.columns).indexOf(seriesKey) === -1)
166
- ) {
167
- runtime.series.push({
168
- dataKey: seriesKey,
169
- type: visualizationConfig.dynamicSeriesType,
170
- lineType: visualizationConfig.dynamicSeriesLineType,
171
- tooltip: true
172
- })
173
- }
108
+ if (visualizationConfig.filterBehavior === 'Filter Change') {
109
+ if (standaloneMap) {
110
+ setFilteredData(newFilters)
111
+ } else {
112
+ const newFilteredData = filterVizData(newFilters, excludedData)
113
+ setFilteredData(newFilteredData)
114
+
115
+ if (visualizationConfig.dynamicSeries) {
116
+ const runtime = getNewRuntime(visualizationConfig, newFilteredData)
117
+ setConfig({
118
+ ...visualizationConfig,
119
+ filters: newFilters,
120
+ runtime
174
121
  })
175
122
  }
176
-
177
- runtime.seriesKeys = runtime.series
178
- ? runtime.series.map(series => {
179
- runtime.seriesLabels[series.dataKey] = series.name || series.label || series.dataKey
180
- runtime.seriesLabelsAll.push(series.name || series.dataKey)
181
- return series.dataKey
182
- })
183
- : []
184
-
185
- setConfig({
186
- ...visualizationConfig,
187
- filters: newFilters,
188
- runtime
189
- })
190
123
  }
191
124
  }
192
125
  }
@@ -227,7 +160,7 @@ export const useFilters = props => {
227
160
  const queryParams = getQueryParams()
228
161
  newFilters.forEach((filter, i) => {
229
162
  if (!filter.values || filter.values.length === 0) {
230
- filter.values = getUniqueValues(data, filter.columnName)
163
+ filter.values = getUniqueValues(visualizationConfig.data, filter.columnName)
231
164
  }
232
165
 
233
166
  newFilters[i].active = handleSorting(filter).values[0]
@@ -251,156 +184,14 @@ export const useFilters = props => {
251
184
  }
252
185
  }
253
186
 
254
- const filterConstants = {
255
- buttonText: 'Apply',
256
- resetText: 'Clear Filters'
257
- }
258
-
259
- // prettier-ignore
260
- return {
261
- handleApplyButton,
262
- changeFilterActive,
263
- showApplyButton,
264
- handleReset,
265
- filterConstants,
266
- filterStyleOptions,
267
- filterOrderOptions,
268
- handleFilterOrder,
269
- handleSorting
270
- }
271
- }
272
-
273
- type FilterProps = {
274
- filteredData: Object[]
275
- dimensions: DimensionsType
276
- config: Visualization
277
- // function for updating the runtime filters
278
- setFilteredData: Function
279
- // updating function for setting fitlerBehavior
280
- setConfig: Function
281
- standaloneMap?: boolean
282
- }
283
-
284
- const Filters = (props: FilterProps) => {
285
- const { config: visualizationConfig, filteredData, dimensions, standaloneMap } = props
286
- const { filters, general, theme, filterBehavior } = visualizationConfig
287
- const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
288
- const [selectedFilter, setSelectedFilter] = useState<EventTarget>(null)
289
- const [wrappingFilters, setWrappingFilters] = useState({})
290
- const [initialActiveFilters, setInitialActiveFilters] = useState([])
291
-
292
- useEffect(() => {
293
- if (!filteredData) return
294
-
295
- setInitialActiveFilters(filters.map(filter => filter.active))
296
- }, [])
297
-
298
- const activeFilters = filters.map(filter => filter.active)
299
- const initialFiltersActive = initialActiveFilters.every((filter, i) => activeFilters.includes(filter))
300
- const id = useId()
301
-
302
- const wrappingFilterRefs = useRef({})
303
- const filterWrappingStatusesToUpdate = getWrappingStatuses(wrappingFilterRefs, wrappingFilters, filters)
304
-
305
- if (filterWrappingStatusesToUpdate.length) {
306
- const validStatuses = filterWrappingStatusesToUpdate.filter(Boolean) as [string, any][]
307
- setWrappingFilters({ ...wrappingFilters, ...Object.fromEntries(validStatuses) })
308
- }
309
-
310
- // useFilters hook provides data and logic for handling various filter functions
311
- // prettier-ignore
312
- const {
313
- handleApplyButton,
314
- changeFilterActive,
315
- showApplyButton,
316
- handleReset,
317
- filterConstants,
318
- handleSorting
319
- } = useFilters(props)
320
-
321
- useEffect(() => {
322
- if (!dimensions) return
323
- const [width] = dimensions
324
-
187
+ const mobileFilterStyle = useMemo(() => {
188
+ if (!dimensions) false
189
+ const [width] = dimensions || []
325
190
  const isMobile = Number(width) < 768
326
191
  const isTabSimple = filters?.some(filter => filter.filterStyle === VIZ_FILTER_STYLE.tabSimple)
327
192
 
328
- const defaultToMobile = isMobile && filters?.length && !isTabSimple
329
-
330
- setMobileFilterStyle(defaultToMobile)
331
- }, [dimensions])
332
-
333
- useEffect(() => {
334
- const noLongerTabSimple = Object.keys(wrappingFilters).filter(columnName => {
335
- const filter = filters.find(filter => filter.columnName === columnName)
336
- if (!filter) return false
337
- return filter.filterStyle !== VIZ_FILTER_STYLE.tabSimple
338
- })
339
-
340
- if (!noLongerTabSimple.length) return
341
-
342
- setWrappingFilters(
343
- Object.fromEntries(
344
- Object.entries(wrappingFilters).filter(([columnName]) => !noLongerTabSimple.includes(columnName))
345
- )
346
- )
347
- }, [filters])
348
-
349
- useEffect(() => {
350
- if (selectedFilter) {
351
- const el = document.getElementById(selectedFilter.id)
352
- if (el) el.focus()
353
- }
354
- }, [changeFilterActive, selectedFilter])
355
-
356
- const TabBar = props => {
357
- const { filter: singleFilter, index: outerIndex } = props
358
- return (
359
- <section className='single-filters__tab-bar'>
360
- {singleFilter.values.map((filter, index) => {
361
- const buttonClassList = ['button__tab-bar', singleFilter.active === filter ? 'button__tab-bar--active' : '']
362
- return (
363
- <button
364
- id={`${filter}-${outerIndex}-${index}-${id}`}
365
- className={buttonClassList.join(' ')}
366
- key={filter}
367
- onClick={e => {
368
- changeFilterActive(outerIndex, filter)
369
- setSelectedFilter(e.target)
370
- }}
371
- onKeyDown={e => {
372
- if (e.keyCode === 13) {
373
- changeFilterActive(outerIndex, filter)
374
- setSelectedFilter(e.target)
375
- }
376
- }}
377
- >
378
- {filter}
379
- </button>
380
- )
381
- })}
382
- </section>
383
- )
384
- }
385
-
386
- const Dropdown = props => {
387
- const { index: outerIndex, label, active, filters } = props
388
- return (
389
- <select
390
- id={`filter-${outerIndex}`}
391
- name={label}
392
- aria-label={`Filter by ${label}`}
393
- className={`cove-form-select ${DROPDOWN_STYLES}`}
394
- data-index='0'
395
- value={active}
396
- onChange={e => {
397
- changeFilterActive(outerIndex, e.target.value)
398
- }}
399
- >
400
- {filters}
401
- </select>
402
- )
403
- }
193
+ return isMobile && filters?.length && !isTabSimple
194
+ }, [dimensions?.[0]])
404
195
 
405
196
  const vizFiltersWithValues = useMemo(() => {
406
197
  // Here charts is using config.filters where maps is using a runtime value
@@ -410,139 +201,6 @@ const Filters = (props: FilterProps) => {
410
201
  return addValuesToFilters(vizfilters as VizFilter[], visualizationConfig.data)
411
202
  }, [filters, filteredData])
412
203
 
413
- // Resolve Filter Styles
414
- const Style = () => {
415
- return vizFiltersWithValues.map((singleFilter: VizFilter, outerIndex) => {
416
- if (singleFilter.showDropdown === false) return
417
-
418
- const DropdownOptions = []
419
- const Pills = []
420
- const Tabs = []
421
- const isTabSimple = singleFilter.filterStyle === 'tab-simple'
422
-
423
- const { active, queuedActive, label, filterStyle, columnName } = singleFilter as VizFilter
424
- const { isDropdown } = wrappingFilters[columnName] || {}
425
-
426
- handleSorting(singleFilter)
427
- singleFilter.values?.forEach((filterOption, index) => {
428
- const isActive = active === filterOption
429
-
430
- const pillClassList = ['pill', isActive ? 'pill--active' : null, theme && theme]
431
- const tabClassList = ['tab', isActive && 'tab--active', theme && theme, isTabSimple && 'tab--simple']
432
-
433
- Pills.push(
434
- <div className='pill__wrapper' key={`pill-${index}`}>
435
- <button
436
- id={`${filterOption}-${outerIndex}-${index}-${id}`}
437
- className={pillClassList.join(' ')}
438
- onKeyDown={e => {
439
- if (e.keyCode === 13) {
440
- changeFilterActive(outerIndex, filterOption)
441
- setSelectedFilter(e.target)
442
- }
443
- }}
444
- onClick={e => {
445
- changeFilterActive(outerIndex, filterOption)
446
- setSelectedFilter(e.target)
447
- }}
448
- name={label}
449
- >
450
- {filterOption}
451
- </button>
452
- </div>
453
- )
454
-
455
- DropdownOptions.push(
456
- <option key={index} value={filterOption} aria-label={filterOption}>
457
- {singleFilter.labels && singleFilter.labels[filterOption]
458
- ? singleFilter.labels[filterOption]
459
- : filterOption}
460
- </option>
461
- )
462
-
463
- Tabs.push(
464
- <button
465
- id={`${filterOption}-${outerIndex}-${index}-${id}`}
466
- className={tabClassList.join(' ')}
467
- onClick={e => {
468
- changeFilterActive(outerIndex, filterOption)
469
- setSelectedFilter(e.target)
470
- }}
471
- onKeyDown={e => {
472
- if (e.keyCode === 13) {
473
- changeFilterActive(outerIndex, filterOption)
474
- setSelectedFilter(e.target)
475
- }
476
- }}
477
- >
478
- {filterOption}
479
- </button>
480
- )
481
- })
482
-
483
- const classList = [
484
- 'single-filters',
485
- 'form-group',
486
- mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`
487
- ]
488
- const mobileExempt = ['nested-dropdown', 'multi-select', VIZ_FILTER_STYLE.tabSimple].includes(filterStyle)
489
- const showDefaultDropdown = ((filterStyle === 'dropdown' || mobileFilterStyle) && !mobileExempt) || isDropdown
490
- const [nestedActiveGroup, nestedActiveSubGroup] = useMemo<string[]>(() => {
491
- if (filterStyle !== 'nested-dropdown') return []
492
- return (singleFilter.queuedActive || [singleFilter.active, singleFilter.subGrouping?.active]) as [
493
- string,
494
- string
495
- ]
496
- }, [singleFilter])
497
- const hideLabelMargin = isTabSimple && !showDefaultDropdown
498
- return (
499
- <div className={classList.join(' ')} key={outerIndex} ref={el => (wrappingFilterRefs.current[columnName] = el)}>
500
- <>
501
- {label && (
502
- <label className={`font-weight-bold mb-${hideLabelMargin ? '0' : '2'}`} htmlFor={`filter-${outerIndex}`}>
503
- {label}
504
- </label>
505
- )}
506
- {filterStyle === 'tab' && !mobileFilterStyle && Tabs}
507
- {filterStyle === 'tab-simple' && !showDefaultDropdown && (
508
- <div className='tab-simple-container d-flex w-100'>{Tabs}</div>
509
- )}
510
- {filterStyle === 'pill' && !mobileFilterStyle && Pills}
511
- {filterStyle === 'tab bar' && !mobileFilterStyle && <TabBar filter={singleFilter} index={outerIndex} />}
512
- {filterStyle === 'multi-select' && (
513
- <MultiSelect
514
- options={singleFilter.values.map(v => ({ value: v, label: v }))}
515
- fieldName={outerIndex}
516
- updateField={(_section, _subSection, fieldName, value) => changeFilterActive(fieldName, value)}
517
- selected={singleFilter.active as string[]}
518
- limit={(singleFilter as MultiSelectFilter).selectLimit || 5}
519
- />
520
- )}
521
- {filterStyle === 'nested-dropdown' && (
522
- <NestedDropdown
523
- activeGroup={nestedActiveGroup}
524
- activeSubGroup={nestedActiveSubGroup}
525
- filterIndex={outerIndex}
526
- options={getNestedOptions(singleFilter)}
527
- listLabel={label}
528
- handleSelectedItems={value => changeFilterActive(outerIndex, value)}
529
- />
530
- )}
531
- {showDefaultDropdown && (
532
- <Dropdown
533
- filter={singleFilter}
534
- index={outerIndex}
535
- label={label}
536
- active={queuedActive || active}
537
- filters={DropdownOptions}
538
- />
539
- )}
540
- </>
541
- </div>
542
- )
543
- })
544
- }
545
-
546
204
  if (visualizationConfig?.filters?.length === 0) return
547
205
 
548
206
  const getClasses = () => {
@@ -554,15 +212,96 @@ const Filters = (props: FilterProps) => {
554
212
  return [baseClass, conditionalClass, legendClass, 'w-100'].filter(Boolean)
555
213
  }
556
214
 
215
+ const getNestedGroup = (singleFilter: VizFilter): string[] => {
216
+ if (singleFilter.filterStyle !== 'nested-dropdown') return []
217
+ return (singleFilter.queuedActive || [singleFilter.active, singleFilter.subGrouping?.active]) as [string, string]
218
+ }
219
+
557
220
  return (
558
221
  <section className={getClasses().join(' ')}>
559
222
  {visualizationConfig.filterIntro && (
560
223
  <p className='filters-section__intro-text mb-3'>{visualizationConfig.filterIntro}</p>
561
224
  )}
562
225
  <div className='d-flex flex-wrap w-100 filters-section__wrapper align-items-end'>
563
- {' '}
564
226
  <>
565
- <Style />
227
+ {vizFiltersWithValues.map((singleFilter: VizFilter, outerIndex) => {
228
+ if (singleFilter.showDropdown === false) return
229
+ const { label, filterStyle, columnName } = singleFilter as VizFilter
230
+
231
+ handleSorting(singleFilter)
232
+
233
+ const classList = [
234
+ 'single-filters',
235
+ 'form-group',
236
+ mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`
237
+ ]
238
+ const mobileExempt = ['nested-dropdown', 'multi-select', VIZ_FILTER_STYLE.tabSimple].includes(filterStyle)
239
+ const { isDropdown } = wrappingFilters[columnName] || {}
240
+ const showDefaultDropdown =
241
+ ((filterStyle === 'dropdown' || mobileFilterStyle) && !mobileExempt) || isDropdown
242
+ const [nestedActiveGroup, nestedActiveSubGroup] = getNestedGroup(singleFilter)
243
+ const hideLabelMargin = singleFilter.filterStyle === 'tab-simple' && !showDefaultDropdown
244
+ return (
245
+ <div
246
+ className={classList.join(' ')}
247
+ key={outerIndex}
248
+ ref={el => (wrappingFilterRefs.current[columnName] = el)}
249
+ >
250
+ {label && (
251
+ <label
252
+ className={`font-weight-bold mb-${hideLabelMargin ? '0' : '2'}`}
253
+ htmlFor={`filter-${outerIndex}`}
254
+ >
255
+ {label}
256
+ </label>
257
+ )}
258
+ {showDefaultDropdown && (
259
+ <Dropdown
260
+ filter={singleFilter}
261
+ index={outerIndex}
262
+ label={label}
263
+ changeFilterActive={changeFilterActive}
264
+ />
265
+ )}
266
+ {['tab', 'tab bar', 'pill'].includes(filterStyle) && !mobileFilterStyle && (
267
+ <Tabs
268
+ filter={singleFilter}
269
+ index={outerIndex}
270
+ changeFilterActive={changeFilterActive}
271
+ theme={theme}
272
+ />
273
+ )}
274
+ {filterStyle === 'tab-simple' && !showDefaultDropdown && (
275
+ <Tabs
276
+ filter={singleFilter}
277
+ index={outerIndex}
278
+ changeFilterActive={changeFilterActive}
279
+ theme={theme}
280
+ />
281
+ )}
282
+
283
+ {filterStyle === 'multi-select' && (
284
+ <MultiSelect
285
+ options={singleFilter.values.map(v => ({ value: v, label: v }))}
286
+ fieldName={outerIndex}
287
+ updateField={(_section, _subSection, fieldName, value) => changeFilterActive(fieldName, value)}
288
+ selected={singleFilter.active as string[]}
289
+ limit={(singleFilter as MultiSelectFilter).selectLimit || 5}
290
+ />
291
+ )}
292
+ {filterStyle === 'nested-dropdown' && (
293
+ <NestedDropdown
294
+ activeGroup={nestedActiveGroup}
295
+ activeSubGroup={nestedActiveSubGroup}
296
+ filterIndex={outerIndex}
297
+ options={getNestedOptions(singleFilter)}
298
+ listLabel={label}
299
+ handleSelectedItems={value => changeFilterActive(outerIndex, value)}
300
+ />
301
+ )}
302
+ </div>
303
+ )
304
+ })}
566
305
  {filterBehavior === 'Apply Button' ? (
567
306
  <div className='filters-section__buttons'>
568
307
  <Button
@@ -572,10 +311,10 @@ const Filters = (props: FilterProps) => {
572
311
  disabled={!showApplyButton}
573
312
  className={[general?.headerColor ? general.headerColor : theme, 'apply', 'me-2'].join(' ')}
574
313
  >
575
- {filterConstants.buttonText}
314
+ {BUTTON_TEXT.apply}
576
315
  </Button>
577
316
  <Button secondary disabled={initialFiltersActive} onClick={handleReset}>
578
- {filterConstants.resetText}
317
+ {BUTTON_TEXT.resetText}
579
318
  </Button>
580
319
  </div>
581
320
  ) : (