@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.
- package/assets/icon-close.svg +1 -1
- package/components/DataTable/DataTable.tsx +18 -13
- package/components/DataTable/components/CellAnchor.tsx +1 -1
- package/components/DataTable/components/ChartHeader.tsx +2 -1
- package/components/DataTable/components/MapHeader.tsx +1 -0
- package/components/DataTable/helpers/chartCellMatrix.tsx +2 -1
- package/components/DataTable/helpers/mapCellMatrix.tsx +17 -7
- package/components/DownloadButton.tsx +17 -2
- package/components/EditorPanel/DataTableEditor.tsx +1 -1
- package/components/EditorPanel/Inputs.tsx +12 -4
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +2 -1
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +3 -1
- package/components/Filters/Filters.tsx +168 -429
- package/components/Filters/components/Dropdown.tsx +39 -0
- package/components/Filters/components/Tabs.tsx +82 -0
- package/components/Filters/helpers/getChangedFilters.ts +31 -0
- package/components/Filters/helpers/getNestedOptions.ts +2 -2
- package/components/Filters/helpers/getNewRuntime.ts +35 -0
- package/components/Filters/helpers/handleSorting.ts +2 -2
- package/components/Filters/helpers/tests/getChangedFilters.test.ts +92 -0
- package/components/Filters/helpers/tests/getNestedOptions.test.ts +31 -0
- package/components/Filters/helpers/tests/getNewRuntime.test.ts +82 -0
- package/components/Filters/index.ts +1 -1
- package/components/Layout/components/Visualization/index.tsx +3 -3
- package/components/Legend/Legend.Gradient.tsx +66 -23
- package/components/MultiSelect/multiselect.styles.css +2 -0
- package/components/NestedDropdown/NestedDropdown.tsx +2 -2
- package/components/RichTooltip/RichTooltip.tsx +37 -0
- package/components/RichTooltip/richTooltip.css +16 -0
- package/components/Table/Table.tsx +142 -142
- package/components/Table/components/Row.tsx +1 -1
- package/components/Table/table.styles.css +10 -0
- package/components/_stories/DataTable.stories.tsx +9 -2
- package/components/_stories/Table.stories.tsx +1 -1
- package/components/_stories/styles.scss +0 -4
- package/components/ui/Accordion.jsx +8 -1
- package/components/ui/Title/index.tsx +4 -1
- package/components/ui/Title/{Title.scss → title.styles.css} +0 -2
- package/components/ui/_stories/Colors.stories.mdx +220 -0
- package/components/ui/_stories/IconGallery.stories.mdx +14 -0
- package/components/ui/_stories/Title.stories.tsx +29 -4
- package/components/ui/accordion.styles.css +3 -0
- package/data/colorPalettes.js +0 -1
- package/dist/cove-main.css +3 -8
- package/dist/cove-main.css.map +1 -1
- package/helpers/constants.ts +6 -0
- package/helpers/cove/accessibility.ts +7 -8
- package/helpers/coveUpdateWorker.ts +5 -1
- package/helpers/filterOrderOptions.ts +17 -0
- package/helpers/isNumber.ts +20 -0
- package/helpers/isRightAlignedTableValue.js +1 -1
- package/helpers/pivotData.ts +16 -11
- package/helpers/tests/pivotData.test.ts +74 -0
- package/helpers/ver/4.25.3.ts +25 -2
- package/helpers/ver/4.25.4.ts +33 -0
- package/helpers/ver/tests/4.25.4.test.ts +24 -0
- package/helpers/viewports.ts +4 -0
- package/package.json +2 -3
- package/styles/_global-variables.scss +3 -0
- package/styles/_reset.scss +0 -6
- package/styles/v2/main.scss +0 -5
- package/types/General.ts +1 -0
- package/types/Legend.ts +1 -0
- package/LICENSE +0 -201
- package/components/ui/_stories/Colors.stories.tsx +0 -92
- package/components/ui/_stories/Icon.stories.tsx +0 -29
- package/helpers/cove/fontSettings.ts +0 -2
- package/helpers/isNumber.js +0 -24
- 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,
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
const _newFilters = addValuesToFilters(newFilters, excludedData)
|
|
136
102
|
setConfig({
|
|
137
103
|
...visualizationConfig,
|
|
138
|
-
filters:
|
|
104
|
+
filters: _newFilters
|
|
139
105
|
})
|
|
140
106
|
}
|
|
141
107
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
314
|
+
{BUTTON_TEXT.apply}
|
|
576
315
|
</Button>
|
|
577
316
|
<Button secondary disabled={initialFiltersActive} onClick={handleReset}>
|
|
578
|
-
{
|
|
317
|
+
{BUTTON_TEXT.resetText}
|
|
579
318
|
</Button>
|
|
580
319
|
</div>
|
|
581
320
|
) : (
|