@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.
- package/assets/icon-close.svg +1 -1
- package/components/AdvancedEditor/AdvancedEditor.tsx +11 -9
- package/components/DataTable/DataTable.tsx +50 -31
- package/components/DataTable/components/CellAnchor.tsx +1 -1
- package/components/DataTable/components/ChartHeader.tsx +3 -2
- package/components/DataTable/components/MapHeader.tsx +1 -0
- package/components/DataTable/helpers/chartCellMatrix.tsx +2 -1
- package/components/DataTable/helpers/getChartCellValue.ts +11 -5
- package/components/DataTable/helpers/getDataSeriesColumns.ts +7 -3
- package/components/DataTable/helpers/mapCellMatrix.tsx +80 -39
- package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +80 -0
- package/components/DownloadButton.tsx +17 -2
- package/components/EditorPanel/DataTableEditor.tsx +29 -19
- package/components/EditorPanel/Inputs.tsx +13 -4
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +2 -1
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +26 -1
- package/components/Filters/Filters.tsx +172 -421
- 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/Layout/components/Visualization/visualizations.scss +1 -1
- package/components/Legend/Legend.Gradient.tsx +66 -23
- package/components/MediaControls.jsx +14 -7
- 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/elements/Button.jsx +4 -2
- 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 +101 -159
- package/dist/cove-main.css.map +1 -1
- package/helpers/DataTransform.ts +2 -2
- package/helpers/addValuesToFilters.ts +1 -1
- package/helpers/constants.ts +6 -0
- package/helpers/cove/accessibility.ts +7 -8
- package/helpers/coveUpdateWorker.ts +17 -8
- package/helpers/filterOrderOptions.ts +17 -0
- package/helpers/formatConfigBeforeSave.ts +30 -8
- package/helpers/isNumber.ts +20 -0
- package/helpers/isRightAlignedTableValue.js +6 -2
- package/helpers/isSolr.ts +13 -0
- package/helpers/labelHash.ts +21 -0
- package/helpers/pivotData.ts +30 -18
- package/helpers/tests/formatConfigBeforeSave.test.ts +68 -0
- package/helpers/tests/pivotData.test.ts +96 -18
- package/helpers/ver/4.25.3.ts +43 -0
- package/helpers/ver/4.25.4.ts +33 -0
- package/helpers/ver/tests/4.25.4.test.ts +24 -0
- package/helpers/ver/tests/versionNeedsUpdate.test.ts +28 -0
- package/helpers/viewports.ts +4 -0
- package/package.json +2 -3
- package/styles/_global-variables.scss +5 -1
- package/styles/_global.scss +18 -9
- package/styles/_reset.scss +0 -6
- package/styles/base.scss +42 -0
- package/styles/filters.scss +5 -11
- package/styles/v2/components/button.scss +48 -12
- package/styles/v2/main.scss +0 -5
- package/styles/v2/themes/_color-definitions.scss +1 -4
- package/types/General.ts +1 -1
- package/types/Legend.ts +1 -0
- package/types/Table.ts +2 -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
- 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,
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
const _newFilters = addValuesToFilters(newFilters, excludedData)
|
|
134
102
|
setConfig({
|
|
135
103
|
...visualizationConfig,
|
|
136
|
-
filters:
|
|
104
|
+
filters: _newFilters
|
|
137
105
|
})
|
|
138
106
|
}
|
|
139
107
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
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
|
|
551
|
-
{' '}
|
|
225
|
+
<div className='d-flex flex-wrap w-100 filters-section__wrapper align-items-end'>
|
|
552
226
|
<>
|
|
553
|
-
|
|
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
|
-
{
|
|
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
|
<></>
|