@cdc/dashboard 4.25.8 → 4.25.11
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/dist/{cdcdashboard-fce76882.es.js → cdcdashboard-BnB1QM5d.es.js} +6 -13
- package/dist/{cdcdashboard-c55ac1ea.es.js → cdcdashboard-D6CG2-Hb.es.js} +5 -12
- package/dist/{cdcdashboard-31a33da1.es.js → cdcdashboard-MXgURbdZ.es.js} +6 -13
- package/dist/{cdcdashboard-1a1724a1.es.js → cdcdashboard-dgT_1dIT.es.js} +136 -151
- package/dist/cdcdashboard.js +80040 -75976
- package/examples/api-test/categories.json +18 -0
- package/examples/api-test/chart-data.json +602 -0
- package/examples/api-test/topics.json +47 -0
- package/examples/api-test/years.json +22 -0
- package/examples/markup-axis-label.json +4167 -0
- package/examples/private/DEV-10538.json +407 -0
- package/examples/private/DEV-11405.json +39112 -0
- package/examples/private/big-dashboard.json +39112 -0
- package/examples/private/brfs-2.json +1532 -0
- package/examples/private/brfs.json +2128 -2138
- package/examples/private/clade-2.json +430 -0
- package/examples/private/delete.json +32919 -0
- package/examples/private/diabetes.json +5582 -0
- package/examples/private/example-2.json +49796 -0
- package/examples/private/group-legend-test.json +328 -0
- package/examples/private/map.json +1211 -0
- package/examples/private/markup-footer/burden_toolkit_mortality_diabetes_attributable_deaths_data.csv +14041 -0
- package/examples/private/markup-footer/burden_toolkit_mortality_diabetes_attributable_deaths_per_100000_data.csv +14041 -0
- package/examples/private/markup-footer/burden_toolkit_mortality_qaly_data.csv +18721 -0
- package/examples/private/markup-footer/burden_toolkit_mortality_yll_data.csv +18721 -0
- package/examples/private/markup-footer/mortality-deaths-footnotes-age.csv +3 -0
- package/examples/private/markup-variables.json +1451 -0
- package/examples/private/markup.json +5471 -0
- package/examples/private/mpox.json +38128 -0
- package/examples/private/north-dakota.json +1132 -0
- package/examples/private/ophdst.json +38754 -0
- package/examples/private/pedro.json +1 -0
- package/examples/private/pivot.json +683 -0
- package/examples/private/reset.json +32920 -0
- package/examples/private/sewershed.json +435 -0
- package/examples/private/tobacco.json +1938 -0
- package/examples/test-api-filter-reset.json +132 -0
- package/index.html +2 -2
- package/package.json +16 -10
- package/src/CdcDashboard.tsx +1 -3
- package/src/CdcDashboardComponent.tsx +34 -16
- package/src/DashboardContext.tsx +5 -1
- package/src/_stories/Dashboard.API.stories.tsx +62 -0
- package/src/_stories/Dashboard.stories.tsx +492 -472
- package/src/_stories/_mock/api/cessation.json +1 -0
- package/src/_stories/_mock/api/data-explorer.json +1 -0
- package/src/_stories/_mock/api/explore-by-location.json +1 -0
- package/src/_stories/_mock/api/explore-by-topic.json +1 -0
- package/src/_stories/_mock/api/legislation.json +1 -0
- package/src/_stories/_mock/api/oral-health-data.json +1 -0
- package/src/_stories/_mock/custom-order-new-values.json +116 -0
- package/src/components/CollapsibleVisualizationRow.tsx +1 -1
- package/src/components/DashboardFilters/DashboardFilters.tsx +34 -23
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +29 -12
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +81 -112
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +82 -52
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +130 -31
- package/src/components/DashboardFilters/_stories/DashboardFilters.stories.tsx +80 -21
- package/src/components/DataDesignerModal.tsx +227 -210
- package/src/components/Header/Header.tsx +13 -12
- package/src/components/Toggle/Toggle.tsx +48 -47
- package/src/components/VisualizationRow.tsx +13 -6
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +2 -0
- package/src/components/Widget/Widget.tsx +47 -18
- package/src/helpers/addValuesToDashboardFilters.ts +111 -60
- package/src/helpers/apiFilterHelpers.ts +190 -166
- package/src/helpers/filterData.ts +52 -7
- package/src/helpers/filterResetHelpers.ts +102 -0
- package/src/helpers/formatConfigBeforeSave.ts +137 -0
- package/src/helpers/getVizConfig.ts +36 -18
- package/src/helpers/loadAPIFilters.ts +109 -99
- package/src/helpers/reloadURLHelpers.ts +1 -1
- package/src/helpers/tests/filterResetHelpers.test.ts +532 -0
- package/src/helpers/tests/formatConfigBeforeSave.test.ts +69 -0
- package/src/index.tsx +1 -1
- package/src/scss/editor-panel.scss +3 -431
- package/src/scss/grid.scss +7 -5
- package/src/scss/main.scss +1 -24
- package/src/store/errorMessage/errorMessage.reducer.ts +1 -1
- package/src/types/DashboardFilters.ts +9 -8
- package/src/types/InitialState.ts +12 -12
- package/vite.config.js +1 -1
- package/vitest.config.ts +16 -0
- package/src/coreStyles_dashboard.scss +0 -3
- package/src/helpers/getAutoLoadVisualization.ts +0 -11
- package/src/scss/mixins.scss +0 -47
- package/src/scss/variables.scss +0 -5
- /package/dist/{cdcdashboard-548642e6.es.js → cdcdashboard-Ct2SB0vL.es.js} +0 -0
|
@@ -18,6 +18,7 @@ type DashboardFilterProps = {
|
|
|
18
18
|
showSubmit: boolean
|
|
19
19
|
applyFilters: MouseEventHandler<HTMLButtonElement>
|
|
20
20
|
applyFiltersButtonText?: string
|
|
21
|
+
handleReset?: MouseEventHandler<HTMLButtonElement>
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
@@ -27,7 +28,8 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
27
28
|
handleOnChange,
|
|
28
29
|
showSubmit,
|
|
29
30
|
applyFilters,
|
|
30
|
-
applyFiltersButtonText
|
|
31
|
+
applyFiltersButtonText,
|
|
32
|
+
handleReset
|
|
31
33
|
}) => {
|
|
32
34
|
const nullVal = (filter: SharedFilter) => {
|
|
33
35
|
const val = filter.queuedActive || filter.active
|
|
@@ -76,11 +78,6 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
76
78
|
]
|
|
77
79
|
)
|
|
78
80
|
|
|
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
81
|
if (_key && apiFilterDropdowns[_key]) {
|
|
85
82
|
// URL Filter
|
|
86
83
|
if (filter.filterStyle !== FILTER_STYLE.nestedDropdown) {
|
|
@@ -120,8 +117,8 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
const isDisabled = !values.length
|
|
123
|
-
// push reset label only if it does not includes in filter values
|
|
124
|
-
if (filter.resetLabel && !filter.values.includes(filter.resetLabel)) {
|
|
120
|
+
// push reset label only if it does not includes in filter values options
|
|
121
|
+
if (filter.resetLabel && !filter.values.includes(filter.resetLabel) && !_key) {
|
|
125
122
|
values.unshift(
|
|
126
123
|
<option key={`${filter.resetLabel}-option`} value={filter.resetLabel}>
|
|
127
124
|
{filter.resetLabel}
|
|
@@ -150,7 +147,7 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
150
147
|
) : filter.filterStyle === FILTER_STYLE.nestedDropdown ? (
|
|
151
148
|
<NestedDropdown
|
|
152
149
|
activeGroup={(filter.queuedActive?.[0] || filter.active) as string}
|
|
153
|
-
activeSubGroup={
|
|
150
|
+
activeSubGroup={(filter.queuedActive?.[1] || filter.subGrouping?.active) as string}
|
|
154
151
|
filterIndex={filterIndex}
|
|
155
152
|
options={_key ? getNestedDropdownOptions(apiFilterDropdowns[_key]) : nestedOptions}
|
|
156
153
|
listLabel={label}
|
|
@@ -170,7 +167,14 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
170
167
|
disabled={loading || isDisabled}
|
|
171
168
|
>
|
|
172
169
|
{loading && <option value='Loading...'>Loading...</option>}
|
|
173
|
-
{
|
|
170
|
+
{/* For API filters, show placeholder when no value is selected */}
|
|
171
|
+
{_key && nullVal(filter) && (
|
|
172
|
+
<option key={`reset-label`} value=''>
|
|
173
|
+
{filter.resetLabel || '- Select One -'}
|
|
174
|
+
</option>
|
|
175
|
+
)}
|
|
176
|
+
{/* For non-API filters or when no value is selected, show empty option */}
|
|
177
|
+
{!_key && nullVal(filter) && (
|
|
174
178
|
<option key={`select`} value=''>
|
|
175
179
|
{filter.resetLabel || '- Select -'}
|
|
176
180
|
</option>
|
|
@@ -184,19 +188,26 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
184
188
|
)
|
|
185
189
|
})}
|
|
186
190
|
{showSubmit && (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
191
|
+
<>
|
|
192
|
+
<button
|
|
193
|
+
className='btn btn-primary mb-1 me-2'
|
|
194
|
+
onClick={applyFilters}
|
|
195
|
+
disabled={show.some(filterIndex => {
|
|
196
|
+
const emptyFilterValues = [undefined, '', '- Select -']
|
|
197
|
+
return (
|
|
198
|
+
emptyFilterValues.includes(sharedFilters[filterIndex].queuedActive) &&
|
|
199
|
+
emptyFilterValues.includes(sharedFilters[filterIndex].active)
|
|
200
|
+
)
|
|
201
|
+
})}
|
|
202
|
+
>
|
|
203
|
+
{applyFiltersButtonText || 'GO!'}
|
|
204
|
+
</button>
|
|
205
|
+
{handleReset && (
|
|
206
|
+
<button className='btn btn-link mb-1' onClick={handleReset}>
|
|
207
|
+
Clear Filters
|
|
208
|
+
</button>
|
|
209
|
+
)}
|
|
210
|
+
</>
|
|
200
211
|
)}
|
|
201
212
|
</form>
|
|
202
213
|
)
|
|
@@ -43,9 +43,10 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
43
43
|
return config.dashboard.sharedFilters
|
|
44
44
|
?.map<[number, string]>(({ key }, i) => [i, key])
|
|
45
45
|
.filter(([filterIndex]) => !sharedFilterIndexes.includes(filterIndex)) // filter out already added filters
|
|
46
|
-
.map(([filterIndex, filterName]) => (
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
.map(([filterIndex, filterName]) => ({
|
|
47
|
+
value: String(filterIndex),
|
|
48
|
+
label: `${filterIndex} - ${filterName}`
|
|
49
|
+
}))
|
|
49
50
|
}, [config.visualizations, vizConfig.uid])
|
|
50
51
|
|
|
51
52
|
const openControls = useState({})
|
|
@@ -189,6 +190,27 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
189
190
|
}
|
|
190
191
|
/>
|
|
191
192
|
)}
|
|
193
|
+
{vizConfig.filterBehavior === 'Apply Button' && (
|
|
194
|
+
<CheckBox
|
|
195
|
+
label='Show Clear Filters Button'
|
|
196
|
+
value={vizConfig.showClearButton ?? true}
|
|
197
|
+
updateField={(_section, _subsection, _key, value) => {
|
|
198
|
+
updateConfig({ ...vizConfig, showClearButton: value })
|
|
199
|
+
}}
|
|
200
|
+
tooltip={
|
|
201
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
202
|
+
<Tooltip.Target>
|
|
203
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
204
|
+
</Tooltip.Target>
|
|
205
|
+
<Tooltip.Content>
|
|
206
|
+
<p>
|
|
207
|
+
When enabled, displays a "Clear Filters" button that allows users to reset all filter selections.
|
|
208
|
+
</p>
|
|
209
|
+
</Tooltip.Content>
|
|
210
|
+
</Tooltip>
|
|
211
|
+
}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
192
214
|
</AccordionItemPanel>
|
|
193
215
|
</AccordionItem>
|
|
194
216
|
|
|
@@ -254,8 +276,10 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
254
276
|
</Tooltip.Content>
|
|
255
277
|
</Tooltip>
|
|
256
278
|
</span>
|
|
257
|
-
<
|
|
279
|
+
<Select
|
|
280
|
+
label=''
|
|
258
281
|
value={''}
|
|
282
|
+
options={[{ value: '', label: 'Select' }, ...(existingOptions || [])]}
|
|
259
283
|
onChange={e => {
|
|
260
284
|
updateConfig({
|
|
261
285
|
...vizConfig,
|
|
@@ -263,14 +287,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
263
287
|
})
|
|
264
288
|
setCanAddExisting(false)
|
|
265
289
|
}}
|
|
266
|
-
|
|
267
|
-
{[
|
|
268
|
-
<option key='select' value=''>
|
|
269
|
-
Select
|
|
270
|
-
</option>,
|
|
271
|
-
...existingOptions
|
|
272
|
-
]}
|
|
273
|
-
</select>
|
|
290
|
+
/>
|
|
274
291
|
</label>
|
|
275
292
|
) : (
|
|
276
293
|
<button onClick={() => setCanAddExisting(true)} className='btn btn-primary full-width mt-2'>
|
|
@@ -100,7 +100,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
100
100
|
if (!_dataSet.data && _dataSet.dataUrl) {
|
|
101
101
|
setDataFiltersLoading(true)
|
|
102
102
|
let data = await fetchRemoteData(_dataSet.dataUrl)
|
|
103
|
-
if (_dataSet.dataDescription) {
|
|
103
|
+
if (_dataSet.dataDescription && data && data.length > 0) {
|
|
104
104
|
try {
|
|
105
105
|
data = transform.autoStandardize(data)
|
|
106
106
|
data = transform.developerStandardize(data, _dataSet.dataDescription)
|
|
@@ -110,6 +110,9 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
110
110
|
} finally {
|
|
111
111
|
_dataSet.data = data
|
|
112
112
|
}
|
|
113
|
+
} else if (data) {
|
|
114
|
+
// If no dataDescription but we have data, store it directly
|
|
115
|
+
_dataSet.data = data
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -181,33 +184,25 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
181
184
|
return (
|
|
182
185
|
<>
|
|
183
186
|
{dataFiltersLoading && <Loading />}
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
</label>
|
|
187
|
+
<Select
|
|
188
|
+
label='Filter Type'
|
|
189
|
+
value={filter.type || ''}
|
|
190
|
+
options={[
|
|
191
|
+
{ value: '', label: '- Select Option -' },
|
|
192
|
+
{ value: 'urlfilter', label: 'URL' },
|
|
193
|
+
{ value: 'datafilter', label: 'Data' }
|
|
194
|
+
]}
|
|
195
|
+
onChange={e => selectFilterType(e.target.value)}
|
|
196
|
+
disabled={!!filter.type}
|
|
197
|
+
/>
|
|
196
198
|
{filter.type !== undefined && (
|
|
197
199
|
<>
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
{filterStyles.map(dataKey => (
|
|
205
|
-
<option value={dataKey} key={`filter-style-select-item-${dataKey}`}>
|
|
206
|
-
{dataKey}
|
|
207
|
-
</option>
|
|
208
|
-
))}
|
|
209
|
-
</select>
|
|
210
|
-
</label>
|
|
200
|
+
<Select
|
|
201
|
+
label='Filter Style'
|
|
202
|
+
value={filter.filterStyle || FILTER_STYLE.dropdown}
|
|
203
|
+
options={filterStyles}
|
|
204
|
+
onChange={e => updateFilterProp('filterStyle', e.target.value)}
|
|
205
|
+
/>
|
|
211
206
|
{filter.filterStyle === FILTER_STYLE.dropdown && (
|
|
212
207
|
<label>
|
|
213
208
|
<span className='me-1'>Show Dropdown</span>
|
|
@@ -251,41 +246,31 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
251
246
|
<>
|
|
252
247
|
{!hasDashboardApplyBehavior(config.visualizations) && (
|
|
253
248
|
<>
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
return null
|
|
270
|
-
})}
|
|
271
|
-
</select>
|
|
272
|
-
</label>
|
|
249
|
+
<Select
|
|
250
|
+
label='URL to Filter'
|
|
251
|
+
value={filter.datasetKey || ''}
|
|
252
|
+
options={[
|
|
253
|
+
{ value: '', label: '- Select Option -' },
|
|
254
|
+
...Object.keys(config.datasets)
|
|
255
|
+
.filter(datasetKey => config.datasets[datasetKey].dataUrl)
|
|
256
|
+
.map(datasetKey => ({
|
|
257
|
+
value: datasetKey,
|
|
258
|
+
label: config.datasets[datasetKey].dataUrl
|
|
259
|
+
}))
|
|
260
|
+
]}
|
|
261
|
+
onChange={e => updateFilterProp('datasetKey', e.target.value)}
|
|
262
|
+
/>
|
|
273
263
|
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
<option key={'file-name'} value={'File Name'}>
|
|
285
|
-
File Name
|
|
286
|
-
</option>
|
|
287
|
-
</select>
|
|
288
|
-
</label>
|
|
264
|
+
<Select
|
|
265
|
+
label='Filter By'
|
|
266
|
+
value={filter.filterBy || ''}
|
|
267
|
+
options={[
|
|
268
|
+
{ value: '', label: '- Select Option -' },
|
|
269
|
+
{ value: 'Query String', label: 'Query String' },
|
|
270
|
+
{ value: 'File Name', label: 'File Name' }
|
|
271
|
+
]}
|
|
272
|
+
onChange={e => updateFilterProp('filterBy', e.target.value)}
|
|
273
|
+
/>
|
|
289
274
|
{filter.filterBy === 'File Name' && (
|
|
290
275
|
<>
|
|
291
276
|
<TextField
|
|
@@ -304,9 +289,16 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
304
289
|
}
|
|
305
290
|
/>
|
|
306
291
|
|
|
307
|
-
<
|
|
308
|
-
|
|
309
|
-
|
|
292
|
+
<Select
|
|
293
|
+
label='White Space Replacments'
|
|
294
|
+
value={filter.whitespaceReplacement || 'Keep Spaces'}
|
|
295
|
+
options={[
|
|
296
|
+
{ value: 'Remove Spaces', label: 'Remove Spaces' },
|
|
297
|
+
{ value: 'Replace With Underscore', label: 'Replace With Underscore' },
|
|
298
|
+
{ value: 'Keep Spaces', label: 'Keep Spaces' }
|
|
299
|
+
]}
|
|
300
|
+
onChange={e => updateFilterProp('whitespaceReplacement', e.target.value)}
|
|
301
|
+
tooltip={
|
|
310
302
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
311
303
|
<Tooltip.Target>
|
|
312
304
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
@@ -315,22 +307,8 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
315
307
|
<p>{`Set how whitespace characters will be handled in the file request`}</p>
|
|
316
308
|
</Tooltip.Content>
|
|
317
309
|
</Tooltip>
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
defaultValue={filter.whitespaceReplacement || 'Keep Spaces'}
|
|
321
|
-
onChange={e => updateFilterProp('whitespaceReplacement', e.target.value)}
|
|
322
|
-
>
|
|
323
|
-
<option key={'remove-spaces'} value={'Remove Spaces'}>
|
|
324
|
-
Remove Spaces
|
|
325
|
-
</option>
|
|
326
|
-
<option key={'replace-with-underscore'} value={'Replace With Underscore'}>
|
|
327
|
-
Replace With Underscore
|
|
328
|
-
</option>
|
|
329
|
-
<option key={'keep-spaces'} value={'Keep Spaces'}>
|
|
330
|
-
Keep Spaces
|
|
331
|
-
</option>
|
|
332
|
-
</select>
|
|
333
|
-
</label>
|
|
310
|
+
}
|
|
311
|
+
/>
|
|
334
312
|
</>
|
|
335
313
|
)}
|
|
336
314
|
</>
|
|
@@ -504,22 +482,17 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
504
482
|
<>
|
|
505
483
|
{filter.filterStyle !== FILTER_STYLE.nestedDropdown ? (
|
|
506
484
|
<>
|
|
507
|
-
<
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
{dataKey}
|
|
519
|
-
</option>
|
|
520
|
-
))}
|
|
521
|
-
</select>
|
|
522
|
-
</label>
|
|
485
|
+
<Select
|
|
486
|
+
label='Filter'
|
|
487
|
+
value={filter.columnName || ''}
|
|
488
|
+
options={[
|
|
489
|
+
{ value: '', label: '- Select Option -' },
|
|
490
|
+
...columns.map(col => ({ value: col, label: col }))
|
|
491
|
+
]}
|
|
492
|
+
onChange={e => {
|
|
493
|
+
updateFilterProp('columnName', e.target.value)
|
|
494
|
+
}}
|
|
495
|
+
/>
|
|
523
496
|
|
|
524
497
|
<Select
|
|
525
498
|
value={filter.defaultValue}
|
|
@@ -644,23 +617,19 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
644
617
|
updateField={(_section, _subSection, _key, value) => updateFilterProp('resetLabel', value)}
|
|
645
618
|
/>
|
|
646
619
|
|
|
647
|
-
<
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
})}
|
|
662
|
-
</select>
|
|
663
|
-
</label>
|
|
620
|
+
<Select
|
|
621
|
+
label='Parent Filter'
|
|
622
|
+
value={filter.parents || ''}
|
|
623
|
+
options={[
|
|
624
|
+
{ value: '', label: 'Select a filter' },
|
|
625
|
+
...(config.dashboard.sharedFilters || [])
|
|
626
|
+
.filter(sharedFilter => sharedFilter.key !== filter.key)
|
|
627
|
+
.map(sharedFilter => ({ value: sharedFilter.key, label: sharedFilter.key }))
|
|
628
|
+
]}
|
|
629
|
+
onChange={e => {
|
|
630
|
+
updateFilterProp('parents', e.target.value)
|
|
631
|
+
}}
|
|
632
|
+
/>
|
|
664
633
|
|
|
665
634
|
{!isNestedDropdown && (
|
|
666
635
|
<TextField
|
|
@@ -2,7 +2,7 @@ import { DashboardConfig } from '../../../../types/DashboardConfig'
|
|
|
2
2
|
import { SharedFilter } from '../../../../types/SharedFilter'
|
|
3
3
|
import _ from 'lodash'
|
|
4
4
|
import { SubGrouping } from '@cdc/core/types/VizFilter'
|
|
5
|
-
import { TextField } from '@cdc/core/components/EditorPanel/Inputs'
|
|
5
|
+
import { TextField, Select } from '@cdc/core/components/EditorPanel/Inputs'
|
|
6
6
|
|
|
7
7
|
type NestedDropDownEditorDashboardProps = {
|
|
8
8
|
config: DashboardConfig
|
|
@@ -47,16 +47,24 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
47
47
|
})
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const handleFitlerGroupColumnNameChange =
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
const handleFitlerGroupColumnNameChange = (value: string) => {
|
|
51
|
+
if (!value) {
|
|
52
|
+
updateFilterProp('columnName', '')
|
|
53
|
+
updateFilterProp('defaultValue', '')
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
const [newColumnName, selectedOptionDatasetName] = value.split('|')
|
|
53
57
|
updateFilterProp('columnName', newColumnName)
|
|
58
|
+
updateFilterProp('defaultValue', '') // Reset default value when column changes
|
|
54
59
|
populateSubGroupingOptions(selectedOptionDatasetName, newColumnName)
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
const handleSubGroupColumnNameChange =
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const handleSubGroupColumnNameChange = (value: string) => {
|
|
63
|
+
if (!value) {
|
|
64
|
+
updateFilterProp('subGrouping', { ...subGrouping, columnName: '', valuesLookup: {}, defaultValue: '' })
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
const [newColumnName, selectedOptionDatasetName] = value.split('|')
|
|
60
68
|
|
|
61
69
|
const valuesLookup = filter.values.reduce((acc, groupName) => {
|
|
62
70
|
const values: string[] = _.uniq(
|
|
@@ -77,7 +85,8 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
77
85
|
const newSubGrouping: SubGrouping = {
|
|
78
86
|
...subGrouping,
|
|
79
87
|
columnName: newColumnName,
|
|
80
|
-
valuesLookup
|
|
88
|
+
valuesLookup,
|
|
89
|
+
defaultValue: '' // Reset default value when column changes
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
updateFilterProp('subGrouping', newSubGrouping)
|
|
@@ -92,51 +101,72 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
92
101
|
updateField={(_section, _subSection, _key, value) => updateFilterProp('key', value)}
|
|
93
102
|
/>
|
|
94
103
|
)}
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
104
|
+
<Select
|
|
105
|
+
label='Filter Grouping'
|
|
106
|
+
value={
|
|
107
|
+
filter.columnName
|
|
108
|
+
? `${filter.columnName}|${
|
|
109
|
+
columnNameOptionsInDataset.find(opt => opt.columnName === filter.columnName)?.datasetKey || ''
|
|
110
|
+
}`
|
|
111
|
+
: ''
|
|
112
|
+
}
|
|
113
|
+
options={[
|
|
114
|
+
{ value: '', label: '- Select Option -' },
|
|
115
|
+
...columnNameOptionsInDataset.map(option => ({
|
|
116
|
+
value: `${option.columnName}|${option.datasetKey}`,
|
|
117
|
+
label: option.columnName
|
|
118
|
+
}))
|
|
119
|
+
]}
|
|
120
|
+
onChange={e => handleFitlerGroupColumnNameChange(e.target.value)}
|
|
121
|
+
/>
|
|
122
|
+
<Select
|
|
123
|
+
label='Filter SubGrouping'
|
|
124
|
+
value={
|
|
125
|
+
subGrouping?.columnName
|
|
126
|
+
? `${subGrouping.columnName}|${
|
|
127
|
+
columnNameOptionsInDataset.find(opt => opt.columnName === subGrouping.columnName)?.datasetKey || ''
|
|
128
|
+
}`
|
|
129
|
+
: ''
|
|
130
|
+
}
|
|
131
|
+
options={[
|
|
132
|
+
{ value: '', label: '- Select Option -' },
|
|
133
|
+
...columnNameOptionsInDataset
|
|
134
|
+
.filter(option => option.columnName !== filter.columnName)
|
|
135
|
+
.map(option => ({
|
|
136
|
+
value: `${option.columnName}|${option.datasetKey}`,
|
|
137
|
+
label: option.columnName
|
|
138
|
+
}))
|
|
139
|
+
]}
|
|
140
|
+
onChange={e => handleSubGroupColumnNameChange(e.target.value)}
|
|
141
|
+
/>
|
|
142
|
+
|
|
143
|
+
{/* Default Value for Main Group */}
|
|
144
|
+
{filter.columnName && filter.values && filter.values.length > 0 && (
|
|
145
|
+
<Select
|
|
146
|
+
value={filter.defaultValue}
|
|
147
|
+
options={filter.values}
|
|
148
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('defaultValue', value)}
|
|
149
|
+
label={'Group Default Value'}
|
|
150
|
+
initial={'Select'}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{/* Default Value for Sub Group */}
|
|
155
|
+
{subGrouping?.columnName && (filter.defaultValue || filter.active) && subGrouping.valuesLookup && (
|
|
156
|
+
<Select
|
|
157
|
+
value={subGrouping.defaultValue}
|
|
158
|
+
options={(() => {
|
|
159
|
+
const groupKey = filter.defaultValue || (Array.isArray(filter.active) ? filter.active[0] : filter.active)
|
|
160
|
+
return subGrouping.valuesLookup[groupKey as string]?.values || []
|
|
161
|
+
})()}
|
|
162
|
+
updateField={(_section, _subSection, _key, value) => {
|
|
163
|
+
const newSubGrouping = { ...subGrouping, defaultValue: value }
|
|
164
|
+
updateFilterProp('subGrouping', newSubGrouping)
|
|
122
165
|
}}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<option
|
|
129
|
-
value={option.columnName}
|
|
130
|
-
data-set={option.datasetKey}
|
|
131
|
-
key={`subFilter_${option.datasetKey}_${option.columnName} `}
|
|
132
|
-
>
|
|
133
|
-
{option.columnName}
|
|
134
|
-
</option>
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
})}
|
|
138
|
-
</select>
|
|
139
|
-
</label>
|
|
166
|
+
label={'Sub Group Default Value'}
|
|
167
|
+
initial={'Select'}
|
|
168
|
+
/>
|
|
169
|
+
)}
|
|
140
170
|
</div>
|
|
141
171
|
)
|
|
142
172
|
}
|