@cdc/dashboard 4.25.3 → 4.25.6-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 (63) hide show
  1. package/Dynamic_Data.md +79 -0
  2. package/Override_Data.md +39 -0
  3. package/dist/cdcdashboard.js +76052 -77984
  4. package/examples/legend-issue-data.json +1874 -0
  5. package/examples/legend-issue.json +749 -0
  6. package/examples/map.json +628 -0
  7. package/examples/special-classes.json +54340 -0
  8. package/index.html +1 -26
  9. package/package.json +10 -15
  10. package/src/CdcDashboardComponent.tsx +65 -216
  11. package/src/_stories/Dashboard.stories.tsx +2 -0
  12. package/src/_stories/_mock/api-filter-map.json +43 -1
  13. package/src/components/CollapsibleVisualizationRow.tsx +4 -6
  14. package/src/components/DashboardEditors.tsx +143 -0
  15. package/src/components/DashboardFilters/DashboardFilters.tsx +205 -205
  16. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +286 -287
  17. package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +129 -0
  18. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +680 -652
  19. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +198 -198
  20. package/src/components/DataDesignerModal.tsx +33 -14
  21. package/src/components/Header/Header.tsx +7 -9
  22. package/src/components/MultiConfigTabs/multiconfigtabs.styles.css +3 -0
  23. package/src/components/Row.tsx +2 -24
  24. package/src/components/VisualizationRow.tsx +191 -214
  25. package/src/helpers/getVizConfig.ts +93 -80
  26. package/src/helpers/getVizRowColumnLocator.ts +0 -1
  27. package/src/helpers/reloadURLHelpers.ts +11 -6
  28. package/src/helpers/shouldLoadAllFilters.ts +30 -30
  29. package/src/index.tsx +2 -1
  30. package/src/scss/main.scss +0 -5
  31. package/src/store/dashboard.actions.ts +61 -62
  32. package/src/store/dashboard.reducer.ts +15 -11
  33. package/src/types/ConfigRow.ts +0 -1
  34. package/src/types/Dashboard.ts +1 -1
  35. package/src/types/DashboardConfig.ts +1 -1
  36. package/src/types/SharedFilter.ts +2 -0
  37. package/examples/private/DEV-10120.json +0 -1294
  38. package/examples/private/DEV-10527.json +0 -845
  39. package/examples/private/DEV-10586.json +0 -54319
  40. package/examples/private/DEV-10856.json +0 -54319
  41. package/examples/private/DEV-9199.json +0 -606
  42. package/examples/private/DEV-9644.json +0 -20092
  43. package/examples/private/DEV-9684.json +0 -2135
  44. package/examples/private/DEV-9932.json +0 -95
  45. package/examples/private/DEV-9989.json +0 -229
  46. package/examples/private/art-dashboard.json +0 -18174
  47. package/examples/private/art-scratch.json +0 -2406
  48. package/examples/private/bird-flu-2.json +0 -440
  49. package/examples/private/bird-flu.json +0 -413
  50. package/examples/private/dashboard-config-ehdi.json +0 -29915
  51. package/examples/private/dashboard-map-filter.json +0 -815
  52. package/examples/private/dashboard-margins.js +0 -15
  53. package/examples/private/dataset.json +0 -1452
  54. package/examples/private/dev-10856-2.json +0 -1348
  55. package/examples/private/ehdi-data.json +0 -29502
  56. package/examples/private/exposure-source-h5-data.csv +0 -26
  57. package/examples/private/fatal-data.csv +0 -3159
  58. package/examples/private/feelings.json +0 -1
  59. package/examples/private/gaza-issue.json +0 -1214
  60. package/examples/private/markup.json +0 -115
  61. package/examples/private/nhis.json +0 -1792
  62. package/examples/private/workforce.json +0 -2041
  63. package/src/types/DataSet.ts +0 -11
@@ -0,0 +1,143 @@
1
+ import CdcChart from '@cdc/chart/src/CdcChartComponent'
2
+ import { APIFilterDropdowns } from './DashboardFilters'
3
+ import CdcMapComponent from '@cdc/map/src/CdcMapComponent'
4
+ import CdcDataBite from '@cdc/data-bite/src/CdcDataBite'
5
+ import CdcWaffleChart from '@cdc/waffle-chart/src/CdcWaffleChart'
6
+ import CdcMarkupInclude from '@cdc/markup-include/src/CdcMarkupInclude'
7
+ import CdcFilteredText from '@cdc/filtered-text/src/CdcFilteredText'
8
+ import DashboardSharedFilters from './DashboardFilters'
9
+ import DataTableStandAlone from '@cdc/core/components/DataTable/DataTableStandAlone'
10
+ import _ from 'lodash'
11
+ import { AnyVisualization } from '@cdc/core/types/Visualization'
12
+ import { DashboardState } from '../store/dashboard.reducer'
13
+
14
+ type DashboardEditorProps = {
15
+ visualizationKey: string
16
+ visualizationConfig: AnyVisualization
17
+ _updateConfig: (config: any) => void
18
+ isDebug?: boolean
19
+ setSharedFilter?: Function
20
+ apiFilterDropdowns?: APIFilterDropdowns
21
+ state: DashboardState
22
+ }
23
+
24
+ const DashboardEditors: React.FC<DashboardEditorProps> = ({
25
+ visualizationKey,
26
+ visualizationConfig,
27
+ _updateConfig,
28
+ isDebug,
29
+ setSharedFilter,
30
+ apiFilterDropdowns,
31
+ state
32
+ }) => {
33
+ const setsSharedFilter =
34
+ state.config.dashboard.sharedFilters &&
35
+ state.config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === visualizationKey).length > 0
36
+ const setSharedFilterValue = setsSharedFilter
37
+ ? state.config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === visualizationKey)[0].active
38
+ : undefined
39
+ switch (visualizationConfig.type) {
40
+ case 'chart':
41
+ return (
42
+ <CdcChart
43
+ key={visualizationKey}
44
+ config={visualizationConfig}
45
+ isEditor={true}
46
+ isDebug={isDebug}
47
+ setConfig={_updateConfig}
48
+ setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
49
+ setSharedFilterValue={setSharedFilterValue}
50
+ dashboardConfig={state.config}
51
+ datasets={state.config.datasets}
52
+ isDashboard={true}
53
+ />
54
+ )
55
+ case 'map':
56
+ return (
57
+ <CdcMapComponent
58
+ key={visualizationKey}
59
+ config={visualizationConfig}
60
+ isEditor={true}
61
+ isDebug={isDebug}
62
+ setConfig={_updateConfig}
63
+ setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
64
+ setSharedFilterValue={setSharedFilterValue}
65
+ isDashboard={true}
66
+ showLoader={false}
67
+ dashboardConfig={state.config}
68
+ datasets={state.config.datasets}
69
+ />
70
+ )
71
+
72
+ case 'data-bite':
73
+ return (
74
+ <CdcDataBite
75
+ key={visualizationKey}
76
+ config={{ ...visualizationConfig, newViz: true }}
77
+ isEditor={true}
78
+ setConfig={_updateConfig}
79
+ isDashboard={true}
80
+ />
81
+ )
82
+
83
+ case 'waffle-chart':
84
+ return (
85
+ <CdcWaffleChart
86
+ key={visualizationKey}
87
+ config={visualizationConfig}
88
+ isEditor={true}
89
+ setConfig={_updateConfig}
90
+ isDashboard={true}
91
+ />
92
+ )
93
+
94
+ case 'markup-include':
95
+ return (
96
+ <CdcMarkupInclude
97
+ key={visualizationKey}
98
+ config={visualizationConfig}
99
+ isEditor={true}
100
+ setConfig={_updateConfig}
101
+ isDashboard={true}
102
+ datasets={state.config.datasets}
103
+ />
104
+ )
105
+
106
+ case 'filtered-text':
107
+ return (
108
+ <CdcFilteredText
109
+ key={visualizationKey}
110
+ config={visualizationConfig}
111
+ isEditor={true}
112
+ setConfig={_updateConfig}
113
+ isDashboard={true}
114
+ />
115
+ )
116
+
117
+ case 'dashboardFilters':
118
+ return (
119
+ <DashboardSharedFilters
120
+ isEditor={true}
121
+ visualizationConfig={visualizationConfig}
122
+ apiFilterDropdowns={apiFilterDropdowns}
123
+ setConfig={_updateConfig}
124
+ />
125
+ )
126
+
127
+ case 'table':
128
+ return (
129
+ <DataTableStandAlone
130
+ visualizationKey={visualizationKey}
131
+ config={visualizationConfig}
132
+ isEditor={true}
133
+ updateConfig={_updateConfig}
134
+ datasets={state.config.datasets}
135
+ />
136
+ )
137
+
138
+ default:
139
+ return null
140
+ }
141
+ }
142
+
143
+ export default DashboardEditors
@@ -1,205 +1,205 @@
1
- import React from 'react'
2
- import MultiSelect from '@cdc/core/components/MultiSelect'
3
- import { SharedFilter } from '../../types/SharedFilter'
4
- import { APIFilterDropdowns, DropdownOptions } from './DashboardFiltersWrapper'
5
- import { FILTER_STYLE } from '../../types/FilterStyles'
6
- import { NestedOptions, ValueTextPair } from '@cdc/core/components/NestedDropdown/nestedDropdownHelpers'
7
- import NestedDropdown from '@cdc/core/components/NestedDropdown'
8
- import { MouseEventHandler } from 'react'
9
- import Loader from '@cdc/core/components/Loader'
10
- import _ from 'lodash'
11
- import { DROPDOWN_STYLES } from '@cdc/core/components/Filters/Filters'
12
-
13
- type DashboardFilterProps = {
14
- show: number[]
15
- filters: SharedFilter[]
16
- apiFilterDropdowns: APIFilterDropdowns
17
- handleOnChange: (index: number, value: string | string[]) => void
18
- showSubmit: boolean
19
- applyFilters: MouseEventHandler<HTMLButtonElement>
20
- applyFiltersButtonText?: string
21
- }
22
-
23
- const DashboardFilters: React.FC<DashboardFilterProps> = ({
24
- show,
25
- filters: sharedFilters,
26
- apiFilterDropdowns,
27
- handleOnChange,
28
- showSubmit,
29
- applyFilters,
30
- applyFiltersButtonText
31
- }) => {
32
- const nullVal = (filter: SharedFilter) => {
33
- const val = filter.queuedActive || filter.active
34
- return val === null || val === undefined || val === ''
35
- }
36
-
37
- const updateField = (_section, _subsection, fieldName, value) => {
38
- handleOnChange(fieldName, value) // fieldName is the sharedFilterIndex
39
- }
40
-
41
- const stripDuplicateLabelIncrement = (label: string): string => {
42
- // converts 'Label (1)' to 'Label'
43
- return label.replace(/\s\(\d+\)$/, '')
44
- }
45
-
46
- const getNestedDropdownOptions = (options?: DropdownOptions): NestedOptions => {
47
- if (!options) return []
48
- const getValueTextTuple = (value: string, text?: string): ValueTextPair => (text ? [value, text] : [value])
49
- return options.map(({ value, text, subOptions }) => [
50
- getValueTextTuple(value, text),
51
- (subOptions || []).map(({ value, text }) => getValueTextTuple(value, text))
52
- ])
53
- }
54
-
55
- return (
56
- <form className='d-flex flex-wrap'>
57
- {sharedFilters.map((filter, filterIndex) => {
58
- const urlFilterType = filter.type === 'urlfilter'
59
- const label = stripDuplicateLabelIncrement(filter.key || '')
60
-
61
- if (
62
- (!urlFilterType && !filter.showDropdown && filter.filterStyle !== FILTER_STYLE.nestedDropdown) ||
63
- (show && !show.includes(filterIndex))
64
- )
65
- return <React.Fragment key={`${filter.key}-filtersection-${filterIndex}-option`} />
66
- const values: JSX.Element[] = []
67
-
68
- const _key = filter.apiFilter?.apiEndpoint
69
- const loading = apiFilterDropdowns[_key] === null
70
-
71
- const multiValues: { value; label }[] = []
72
- const nestedOptions: NestedOptions = Object.entries(filter?.subGrouping?.valuesLookup || {}).map(
73
- ([key, data]) => [
74
- [key, key], // Main option: [value, text]
75
- Array.isArray(data?.values) ? data.values.map(value => [value, value]) : [] // Ensure `values` is an array
76
- ]
77
- )
78
-
79
- const activeSubGroupValue = _.get(
80
- filter?.subGrouping?.valuesLookup,
81
- [filter?.active as string, 'values', 0],
82
- null // Default to null if the path is invalid
83
- )
84
- if (_key && apiFilterDropdowns[_key]) {
85
- // URL Filter
86
- if (filter.filterStyle !== FILTER_STYLE.nestedDropdown) {
87
- apiFilterDropdowns[_key].forEach(({ text, value }, index) => {
88
- values.push(
89
- <option key={`${value}-option-${index}`} value={value}>
90
- {text}
91
- </option>
92
- )
93
- multiValues.push({ value, label: text })
94
- })
95
- }
96
- } else {
97
- // Data Filter
98
- const orderedFilterValues = filter.orderedValues || filter.values
99
- orderedFilterValues?.forEach((filterOption, index) => {
100
- const labeledOpt = filter.labels && filter.labels[filterOption]
101
- const resetLabelHasMatch = (filterOption || labeledOpt) === filter.resetLabel
102
-
103
- if (!resetLabelHasMatch) {
104
- values.push(
105
- <option key={`${filter.key}-option-${index}`} value={filterOption}>
106
- {labeledOpt || filterOption}
107
- </option>
108
- )
109
- } else {
110
- // add label to the front of list if it matches with reset label
111
- values.unshift(
112
- <option key={`${filter.key}-option-${index}`} value={filterOption}>
113
- {labeledOpt || filterOption}
114
- </option>
115
- )
116
- }
117
-
118
- multiValues.push({ value: filterOption, label: labeledOpt || filterOption })
119
- })
120
- }
121
-
122
- const isDisabled = !values.length
123
- // push reset label only if it does not includes in filter values options
124
- if (filter.resetLabel && !filter.values.includes(filter.resetLabel)) {
125
- values.unshift(
126
- <option key={`${filter.resetLabel}-option`} value={filter.resetLabel}>
127
- {filter.resetLabel}
128
- </option>
129
- )
130
- }
131
-
132
- const formGroupClass = `form-group me-4 mb-1${loading ? ' loading-filter' : ''}`
133
- return (
134
- <div className={formGroupClass} key={`${filter.key}-filtersection-${filterIndex}`}>
135
- {label && (
136
- <label className='font-weight-bold mb-2' htmlFor={`filter-${filterIndex}`}>
137
- {label}
138
- </label>
139
- )}
140
- {filter.filterStyle === FILTER_STYLE.multiSelect ? (
141
- <MultiSelect
142
- label={label}
143
- options={multiValues}
144
- fieldName={filterIndex}
145
- updateField={updateField}
146
- selected={filter.active as string[]}
147
- limit={filter.selectLimit || 5}
148
- loading={loading}
149
- />
150
- ) : filter.filterStyle === FILTER_STYLE.nestedDropdown ? (
151
- <NestedDropdown
152
- activeGroup={(filter.queuedActive?.[0] || filter.active) as string}
153
- activeSubGroup={_key ? filter.queuedActive?.[1] || filter.subGrouping?.active : activeSubGroupValue}
154
- filterIndex={filterIndex}
155
- options={_key ? getNestedDropdownOptions(apiFilterDropdowns[_key]) : nestedOptions}
156
- listLabel={label}
157
- handleSelectedItems={value => updateField(null, null, filterIndex, value)}
158
- loading={loading}
159
- />
160
- ) : (
161
- <>
162
- <select
163
- id={`filter-${filterIndex}`}
164
- className={`cove-form-select ${DROPDOWN_STYLES}`}
165
- data-index='0'
166
- value={loading ? 'Loading...' : filter.queuedActive || filter.active}
167
- onChange={val => {
168
- handleOnChange(filterIndex, val.target.value)
169
- }}
170
- disabled={loading || isDisabled}
171
- >
172
- {loading && <option value='Loading...'>Loading...</option>}
173
- {nullVal(filter) && (
174
- <option key={`select`} value=''>
175
- {filter.resetLabel || '- Select -'}
176
- </option>
177
- )}
178
- {values}
179
- </select>
180
- {loading && <Loader spinnerType={'text-secondary'} />}
181
- </>
182
- )}
183
- </div>
184
- )
185
- })}
186
- {showSubmit && (
187
- <button
188
- className='btn btn-primary mb-1'
189
- onClick={applyFilters}
190
- disabled={show.some(filterIndex => {
191
- const emptyFilterValues = [undefined, '', '- Select -']
192
- return (
193
- emptyFilterValues.includes(sharedFilters[filterIndex].queuedActive) &&
194
- emptyFilterValues.includes(sharedFilters[filterIndex].active)
195
- )
196
- })}
197
- >
198
- {applyFiltersButtonText || 'GO!'}
199
- </button>
200
- )}
201
- </form>
202
- )
203
- }
204
-
205
- export default DashboardFilters
1
+ import React from 'react'
2
+ import MultiSelect from '@cdc/core/components/MultiSelect'
3
+ import { SharedFilter } from '../../types/SharedFilter'
4
+ import { APIFilterDropdowns, DropdownOptions } from './DashboardFiltersWrapper'
5
+ import { FILTER_STYLE } from '../../types/FilterStyles'
6
+ import { NestedOptions, ValueTextPair } from '@cdc/core/components/NestedDropdown/nestedDropdownHelpers'
7
+ import NestedDropdown from '@cdc/core/components/NestedDropdown'
8
+ import { MouseEventHandler } from 'react'
9
+ import Loader from '@cdc/core/components/Loader'
10
+ import _ from 'lodash'
11
+ import { DROPDOWN_STYLES } from '@cdc/core/components/Filters/components/Dropdown'
12
+
13
+ type DashboardFilterProps = {
14
+ show: number[]
15
+ filters: SharedFilter[]
16
+ apiFilterDropdowns: APIFilterDropdowns
17
+ handleOnChange: (index: number, value: string | string[]) => void
18
+ showSubmit: boolean
19
+ applyFilters: MouseEventHandler<HTMLButtonElement>
20
+ applyFiltersButtonText?: string
21
+ }
22
+
23
+ const DashboardFilters: React.FC<DashboardFilterProps> = ({
24
+ show,
25
+ filters: sharedFilters,
26
+ apiFilterDropdowns,
27
+ handleOnChange,
28
+ showSubmit,
29
+ applyFilters,
30
+ applyFiltersButtonText
31
+ }) => {
32
+ const nullVal = (filter: SharedFilter) => {
33
+ const val = filter.queuedActive || filter.active
34
+ return val === null || val === undefined || val === ''
35
+ }
36
+
37
+ const updateField = (_section, _subsection, fieldName, value) => {
38
+ handleOnChange(fieldName, value) // fieldName is the sharedFilterIndex
39
+ }
40
+
41
+ const stripDuplicateLabelIncrement = (label: string): string => {
42
+ // converts 'Label (1)' to 'Label'
43
+ return label.replace(/\s\(\d+\)$/, '')
44
+ }
45
+
46
+ const getNestedDropdownOptions = (options?: DropdownOptions): NestedOptions => {
47
+ if (!options) return []
48
+ const getValueTextTuple = (value: string, text?: string): ValueTextPair => (text ? [value, text] : [value])
49
+ return options.map(({ value, text, subOptions }) => [
50
+ getValueTextTuple(value, text),
51
+ (subOptions || []).map(({ value, text }) => getValueTextTuple(value, text))
52
+ ])
53
+ }
54
+
55
+ return (
56
+ <form className='d-flex flex-wrap'>
57
+ {sharedFilters.map((filter, filterIndex) => {
58
+ const urlFilterType = filter.type === 'urlfilter'
59
+ const label = stripDuplicateLabelIncrement(filter.key || '')
60
+
61
+ if (
62
+ (!urlFilterType && !filter.showDropdown && filter.filterStyle !== FILTER_STYLE.nestedDropdown) ||
63
+ (show && !show.includes(filterIndex))
64
+ )
65
+ return <React.Fragment key={`${filter.key}-filtersection-${filterIndex}-option`} />
66
+ const values: JSX.Element[] = []
67
+
68
+ const _key = filter.apiFilter?.apiEndpoint
69
+ const loading = apiFilterDropdowns[_key] === null
70
+
71
+ const multiValues: { value; label }[] = []
72
+ const nestedOptions: NestedOptions = Object.entries(filter?.subGrouping?.valuesLookup || {}).map(
73
+ ([key, data]) => [
74
+ [key, key], // Main option: [value, text]
75
+ Array.isArray(data?.values) ? data.values.map(value => [value, value]) : [] // Ensure `values` is an array
76
+ ]
77
+ )
78
+
79
+ const activeSubGroupValue = _.get(
80
+ filter?.subGrouping?.valuesLookup,
81
+ [filter?.active as string, 'values', 0],
82
+ null // Default to null if the path is invalid
83
+ )
84
+ if (_key && apiFilterDropdowns[_key]) {
85
+ // URL Filter
86
+ if (filter.filterStyle !== FILTER_STYLE.nestedDropdown) {
87
+ apiFilterDropdowns[_key].forEach(({ text, value }, index) => {
88
+ values.push(
89
+ <option key={`${value}-option-${index}`} value={value}>
90
+ {text}
91
+ </option>
92
+ )
93
+ multiValues.push({ value, label: text })
94
+ })
95
+ }
96
+ } else {
97
+ // Data Filter
98
+ const orderedFilterValues = filter.orderedValues || filter.values
99
+ orderedFilterValues?.forEach((filterOption, index) => {
100
+ const labeledOpt = filter.labels && filter.labels[filterOption]
101
+ const resetLabelHasMatch = (filterOption || labeledOpt) === filter.resetLabel
102
+
103
+ if (!resetLabelHasMatch) {
104
+ values.push(
105
+ <option key={`${filter.key}-option-${index}`} value={filterOption}>
106
+ {labeledOpt || filterOption}
107
+ </option>
108
+ )
109
+ } else {
110
+ // add label to the front of list if it matches with reset label
111
+ values.unshift(
112
+ <option key={`${filter.key}-option-${index}`} value={filterOption}>
113
+ {labeledOpt || filterOption}
114
+ </option>
115
+ )
116
+ }
117
+
118
+ multiValues.push({ value: filterOption, label: labeledOpt || filterOption })
119
+ })
120
+ }
121
+
122
+ const isDisabled = !values.length
123
+ // push reset label only if it does not includes in filter values options
124
+ if (filter.resetLabel && !filter.values.includes(filter.resetLabel)) {
125
+ values.unshift(
126
+ <option key={`${filter.resetLabel}-option`} value={filter.resetLabel}>
127
+ {filter.resetLabel}
128
+ </option>
129
+ )
130
+ }
131
+
132
+ const formGroupClass = `form-group me-4 mb-1${loading ? ' loading-filter' : ''}`
133
+ return (
134
+ <div className={formGroupClass} key={`${filter.key}-filtersection-${filterIndex}`}>
135
+ {label && (
136
+ <label className='font-weight-bold mb-2' htmlFor={`filter-${filterIndex}`}>
137
+ {label}
138
+ </label>
139
+ )}
140
+ {filter.filterStyle === FILTER_STYLE.multiSelect ? (
141
+ <MultiSelect
142
+ label={label}
143
+ options={multiValues}
144
+ fieldName={filterIndex}
145
+ updateField={updateField}
146
+ selected={filter.active as string[]}
147
+ limit={filter.selectLimit || 5}
148
+ loading={loading}
149
+ />
150
+ ) : filter.filterStyle === FILTER_STYLE.nestedDropdown ? (
151
+ <NestedDropdown
152
+ activeGroup={(filter.queuedActive?.[0] || filter.active) as string}
153
+ activeSubGroup={_key ? filter.queuedActive?.[1] || filter.subGrouping?.active : activeSubGroupValue}
154
+ filterIndex={filterIndex}
155
+ options={_key ? getNestedDropdownOptions(apiFilterDropdowns[_key]) : nestedOptions}
156
+ listLabel={label}
157
+ handleSelectedItems={value => updateField(null, null, filterIndex, value)}
158
+ loading={loading}
159
+ />
160
+ ) : (
161
+ <>
162
+ <select
163
+ id={`filter-${filterIndex}`}
164
+ className={`cove-form-select ${DROPDOWN_STYLES}`}
165
+ data-index='0'
166
+ value={loading ? 'Loading...' : filter.queuedActive || filter.active}
167
+ onChange={val => {
168
+ handleOnChange(filterIndex, val.target.value)
169
+ }}
170
+ disabled={loading || isDisabled}
171
+ >
172
+ {loading && <option value='Loading...'>Loading...</option>}
173
+ {nullVal(filter) && (
174
+ <option key={`select`} value=''>
175
+ {filter.resetLabel || '- Select -'}
176
+ </option>
177
+ )}
178
+ {values}
179
+ </select>
180
+ {loading && <Loader spinnerType={'text-secondary'} />}
181
+ </>
182
+ )}
183
+ </div>
184
+ )
185
+ })}
186
+ {showSubmit && (
187
+ <button
188
+ className='btn btn-primary mb-1'
189
+ onClick={applyFilters}
190
+ disabled={show.some(filterIndex => {
191
+ const emptyFilterValues = [undefined, '', '- Select -']
192
+ return (
193
+ emptyFilterValues.includes(sharedFilters[filterIndex].queuedActive) &&
194
+ emptyFilterValues.includes(sharedFilters[filterIndex].active)
195
+ )
196
+ })}
197
+ >
198
+ {applyFiltersButtonText || 'GO!'}
199
+ </button>
200
+ )}
201
+ </form>
202
+ )
203
+ }
204
+
205
+ export default DashboardFilters