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