@cdc/dashboard 4.24.7 → 4.24.9-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/LICENSE +201 -0
- package/dist/cdcdashboard.js +131733 -123191
- package/examples/single-state-dashboard-filters.json +421 -0
- package/examples/state-level.json +90136 -0
- package/examples/state-points.json +10474 -0
- package/examples/test-file.json +147 -0
- package/examples/testing.json +94456 -0
- package/index.html +18 -6
- package/package.json +9 -9
- package/src/CdcDashboardComponent.tsx +154 -90
- package/src/DashboardContext.tsx +7 -1
- package/src/_stories/Dashboard.stories.tsx +124 -10
- package/src/_stories/_mock/api-filter-map.json +1 -1
- package/src/_stories/_mock/bump-chart.json +3554 -0
- package/src/_stories/_mock/methodology.json +412 -0
- package/src/_stories/_mock/methodologyAPI.ts +90 -0
- package/src/_stories/_mock/multi-viz.json +1 -1
- package/src/_stories/_mock/single-state-dashboard-filters.json +390 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +39 -17
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +2 -2
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +141 -31
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +66 -18
- package/src/components/Header/Header.tsx +0 -5
- package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +20 -8
- package/src/components/Row.tsx +1 -1
- package/src/components/VisualizationRow.tsx +98 -17
- package/src/components/Widget.tsx +1 -0
- package/src/helpers/FilterBehavior.ts +4 -0
- package/src/helpers/addValuesToDashboardFilters.ts +49 -0
- package/src/helpers/apiFilterHelpers.ts +70 -18
- package/src/helpers/changeFilterActive.ts +17 -8
- package/src/helpers/getFilteredData.ts +4 -4
- package/src/helpers/iconHash.tsx +2 -0
- package/src/helpers/loadAPIFilters.ts +74 -0
- package/src/helpers/reloadURLHelpers.ts +41 -7
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +44 -0
- package/src/helpers/tests/apiFilterHelpers.test.ts +155 -0
- package/src/helpers/tests/getFilteredData.test.ts +86 -0
- package/src/helpers/tests/loadAPIFiltersWrapper.test.ts +220 -0
- package/src/helpers/tests/reloadURLHelpers.test.ts +232 -0
- package/src/types/SharedFilter.ts +2 -1
|
@@ -23,30 +23,27 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
23
23
|
const [columns, setColumns] = useState<string[]>([])
|
|
24
24
|
const transform = new DataTransform()
|
|
25
25
|
|
|
26
|
-
const parentFilters: string[] = (config.dashboard.sharedFilters || [])
|
|
26
|
+
const parentFilters: string[] = (config.dashboard.sharedFilters || [])
|
|
27
|
+
.filter(({ key, type }) => key !== filter.key && type !== 'datafilter')
|
|
28
|
+
.map(({ key }) => key)
|
|
27
29
|
|
|
28
30
|
const vizRowColumnLocator = getVizRowColumnLocator(config.rows)
|
|
29
31
|
|
|
30
32
|
const [usedByNameLookup, usedByOptions] = useMemo(() => {
|
|
31
33
|
const nameLookup = {}
|
|
32
|
-
const vizOptions = Object.keys(config.visualizations)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const viz = config.visualizations[vizKey] as Visualization
|
|
46
|
-
const vizName = viz.general?.title || viz.title || vizKey
|
|
47
|
-
nameLookup[vizKey] = vizName
|
|
48
|
-
return vizKey
|
|
49
|
-
})
|
|
34
|
+
const vizOptions = Object.keys(config.visualizations).filter(vizKey => {
|
|
35
|
+
const vizLookup = vizRowColumnLocator[vizKey]
|
|
36
|
+
if (!vizLookup) return false
|
|
37
|
+
const viz = config.visualizations[vizKey] as Visualization
|
|
38
|
+
if (viz.type === 'dashboardFilters') return false
|
|
39
|
+
const vizName = viz.general?.title || viz.title || vizKey
|
|
40
|
+
nameLookup[vizKey] = vizName
|
|
41
|
+
const notAdded = !filter.usedBy || filter.usedBy.indexOf(vizKey) === -1
|
|
42
|
+
const usesSharedFilter = viz.usesSharedFilter
|
|
43
|
+
const rowIndex = vizLookup.row
|
|
44
|
+
const dataConfiguredOnRow = config.rows[rowIndex].dataKey
|
|
45
|
+
return filter.setBy !== vizKey && notAdded && !usesSharedFilter && !dataConfiguredOnRow
|
|
46
|
+
})
|
|
50
47
|
const rowOptions: number[] = []
|
|
51
48
|
|
|
52
49
|
config.rows.forEach((row, rowIndex) => {
|
|
@@ -121,7 +118,11 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
121
118
|
<>
|
|
122
119
|
<label>
|
|
123
120
|
<span className='edit-label column-heading'>Filter Type: </span>
|
|
124
|
-
<select
|
|
121
|
+
<select
|
|
122
|
+
defaultValue={filter.type || ''}
|
|
123
|
+
onChange={e => updateFilterProp('type', e.target.value)}
|
|
124
|
+
disabled={!!filter.type}
|
|
125
|
+
>
|
|
125
126
|
<option value=''>- Select Option -</option>
|
|
126
127
|
<option value='urlfilter'>URL</option>
|
|
127
128
|
<option value='datafilter'>Data</option>
|
|
@@ -129,12 +130,19 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
129
130
|
</label>
|
|
130
131
|
{filter.type === 'urlfilter' && (
|
|
131
132
|
<>
|
|
132
|
-
<TextField
|
|
133
|
+
<TextField
|
|
134
|
+
label='Label'
|
|
135
|
+
value={filter.key}
|
|
136
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('key', value)}
|
|
137
|
+
/>
|
|
133
138
|
{!hasDashboardApplyBehavior(config.visualizations) && (
|
|
134
139
|
<>
|
|
135
140
|
<label>
|
|
136
141
|
<span className='edit-label column-heading'>URL to Filter: </span>
|
|
137
|
-
<select
|
|
142
|
+
<select
|
|
143
|
+
defaultValue={filter.datasetKey || ''}
|
|
144
|
+
onChange={e => updateFilterProp('datasetKey', e.target.value)}
|
|
145
|
+
>
|
|
138
146
|
<option value=''>- Select Option -</option>
|
|
139
147
|
{Object.keys(config.datasets).map(datasetKey => {
|
|
140
148
|
if (config.datasets[datasetKey].dataUrl) {
|
|
@@ -150,7 +158,10 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
150
158
|
</label>
|
|
151
159
|
<label>
|
|
152
160
|
<span className='edit-label column-heading'>Filter By: </span>
|
|
153
|
-
<select
|
|
161
|
+
<select
|
|
162
|
+
defaultValue={filter.filterBy || ''}
|
|
163
|
+
onChange={e => updateFilterProp('filterBy', e.target.value)}
|
|
164
|
+
>
|
|
154
165
|
<option value=''>- Select Option -</option>
|
|
155
166
|
<option key={'query-string'} value={'Query String'}>
|
|
156
167
|
Query String
|
|
@@ -190,7 +201,10 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
190
201
|
</Tooltip.Content>
|
|
191
202
|
</Tooltip>
|
|
192
203
|
</span>
|
|
193
|
-
<select
|
|
204
|
+
<select
|
|
205
|
+
defaultValue={filter.whitespaceReplacement || 'Keep Spaces'}
|
|
206
|
+
onChange={e => updateFilterProp('whitespaceReplacement', e.target.value)}
|
|
207
|
+
>
|
|
194
208
|
<option key={'remove-spaces'} value={'Remove Spaces'}>
|
|
195
209
|
Remove Spaces
|
|
196
210
|
</option>
|
|
@@ -206,8 +220,18 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
206
220
|
)}
|
|
207
221
|
</>
|
|
208
222
|
)}
|
|
209
|
-
{filter.filterBy === 'Query String' &&
|
|
210
|
-
|
|
223
|
+
{filter.filterBy === 'Query String' && (
|
|
224
|
+
<TextField
|
|
225
|
+
label='Query string parameter'
|
|
226
|
+
value={filter.queryParameter}
|
|
227
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('queryParameter', value)}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
230
|
+
<TextField
|
|
231
|
+
label='Filter API Endpoint: '
|
|
232
|
+
value={filter.apiFilter?.apiEndpoint}
|
|
233
|
+
updateField={(_section, _subSection, _key, value) => updateAPIFilter('apiEndpoint', value)}
|
|
234
|
+
/>
|
|
211
235
|
<TextField
|
|
212
236
|
label='Option Text Selector:'
|
|
213
237
|
value={filter.apiFilter?.textSelector}
|
|
@@ -257,9 +281,43 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
257
281
|
/>
|
|
258
282
|
)}
|
|
259
283
|
|
|
260
|
-
<
|
|
284
|
+
<MultiSelect
|
|
285
|
+
label='Used By: (optional)'
|
|
286
|
+
tooltip={
|
|
287
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
288
|
+
<Tooltip.Target>
|
|
289
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
290
|
+
</Tooltip.Target>
|
|
291
|
+
<Tooltip.Content>
|
|
292
|
+
<p>
|
|
293
|
+
Select if you would like specific visualizations or rows to use this filter. Otherwise the filter
|
|
294
|
+
will be added to all api requests.
|
|
295
|
+
</p>
|
|
296
|
+
</Tooltip.Content>
|
|
297
|
+
</Tooltip>
|
|
298
|
+
}
|
|
299
|
+
options={[...usedByOptions, ...(filter.usedBy || [])].map(opt => ({
|
|
300
|
+
value: opt,
|
|
301
|
+
label: usedByNameLookup[opt]
|
|
302
|
+
}))}
|
|
303
|
+
fieldName='usedBy'
|
|
304
|
+
selected={filter.usedBy}
|
|
305
|
+
updateField={(_section, _subsection, _fieldname, newItems) => {
|
|
306
|
+
updateFilterProp('usedBy', newItems)
|
|
307
|
+
}}
|
|
308
|
+
/>
|
|
261
309
|
|
|
262
|
-
<TextField
|
|
310
|
+
<TextField
|
|
311
|
+
label='Reset Label: '
|
|
312
|
+
value={filter.resetLabel || ''}
|
|
313
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('resetLabel', value)}
|
|
314
|
+
/>
|
|
315
|
+
|
|
316
|
+
<TextField
|
|
317
|
+
label='Default Value Set By Query String Parameter: '
|
|
318
|
+
value={filter.setByQueryParameter || ''}
|
|
319
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('setByQueryParameter', value)}
|
|
320
|
+
/>
|
|
263
321
|
</>
|
|
264
322
|
)}
|
|
265
323
|
{filter.type === 'datafilter' && (
|
|
@@ -281,7 +339,11 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
281
339
|
</select>
|
|
282
340
|
</label>
|
|
283
341
|
|
|
284
|
-
<TextField
|
|
342
|
+
<TextField
|
|
343
|
+
label='Label'
|
|
344
|
+
value={filter.key}
|
|
345
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('key', value)}
|
|
346
|
+
/>
|
|
285
347
|
|
|
286
348
|
<label>
|
|
287
349
|
<span className='edit-label column-heading'>Show Dropdown</span>
|
|
@@ -337,7 +399,11 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
337
399
|
))}
|
|
338
400
|
</select>
|
|
339
401
|
</label>
|
|
340
|
-
<TextField
|
|
402
|
+
<TextField
|
|
403
|
+
label='Reset Label: '
|
|
404
|
+
value={filter.resetLabel || ''}
|
|
405
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('resetLabel', value)}
|
|
406
|
+
/>
|
|
341
407
|
|
|
342
408
|
<label>
|
|
343
409
|
<span className='edit-label column-heading'>Parent Filter: </span>
|
|
@@ -357,9 +423,53 @@ const FilterEditor: React.FC<FilterEditorProps> = ({ filter, config, updateFilte
|
|
|
357
423
|
</select>
|
|
358
424
|
</label>
|
|
359
425
|
|
|
360
|
-
<TextField
|
|
426
|
+
<TextField
|
|
427
|
+
label='Default Value Set By Query String Parameter: '
|
|
428
|
+
value={filter.setByQueryParameter || ''}
|
|
429
|
+
updateField={(_section, _subSection, _key, value) => updateFilterProp('setByQueryParameter', value)}
|
|
430
|
+
/>
|
|
361
431
|
</>
|
|
362
432
|
)}
|
|
433
|
+
<label>
|
|
434
|
+
<span className='mr-1'>Multi Select</span>
|
|
435
|
+
<input
|
|
436
|
+
type='checkbox'
|
|
437
|
+
checked={filter.multiSelect}
|
|
438
|
+
onChange={e => {
|
|
439
|
+
updateFilterProp('multiSelect', !filter.multiSelect)
|
|
440
|
+
}}
|
|
441
|
+
/>
|
|
442
|
+
</label>
|
|
443
|
+
|
|
444
|
+
{filter.multiSelect && (
|
|
445
|
+
<TextField
|
|
446
|
+
label='Select Limit'
|
|
447
|
+
value={filter.selectLimit}
|
|
448
|
+
updateField={(_section, _subSection, _field, value) => updateFilterProp('selectLimit', value)}
|
|
449
|
+
type='number'
|
|
450
|
+
tooltip={
|
|
451
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
452
|
+
<Tooltip.Target>
|
|
453
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
454
|
+
</Tooltip.Target>
|
|
455
|
+
<Tooltip.Content>
|
|
456
|
+
<p>The maximum number of items that can be selected.</p>
|
|
457
|
+
</Tooltip.Content>
|
|
458
|
+
</Tooltip>
|
|
459
|
+
}
|
|
460
|
+
/>
|
|
461
|
+
)}
|
|
462
|
+
|
|
463
|
+
<label>
|
|
464
|
+
<span className='mr-1'>Show Dropdown</span>
|
|
465
|
+
<input
|
|
466
|
+
type='checkbox'
|
|
467
|
+
checked={filter.showDropdown}
|
|
468
|
+
onChange={e => {
|
|
469
|
+
updateFilterProp('showDropdown', !filter.showDropdown)
|
|
470
|
+
}}
|
|
471
|
+
/>
|
|
472
|
+
</label>
|
|
363
473
|
</>
|
|
364
474
|
)
|
|
365
475
|
}
|
|
@@ -3,7 +3,7 @@ import { DashboardContext, DashboardDispatchContext } from '../../DashboardConte
|
|
|
3
3
|
import Filters from './DashboardFilters'
|
|
4
4
|
import { changeFilterActive } from '../../helpers/changeFilterActive'
|
|
5
5
|
import _ from 'lodash'
|
|
6
|
-
import { FilterBehavior } from '
|
|
6
|
+
import { FilterBehavior } from '../../helpers/FilterBehavior'
|
|
7
7
|
import { getFilteredData } from '../../helpers/getFilteredData'
|
|
8
8
|
import { DashboardFilters } from '../../types/DashboardFilters'
|
|
9
9
|
import { getQueryParams, updateQueryString } from '@cdc/core/helpers/queryStringUtils'
|
|
@@ -11,12 +11,14 @@ import Layout from '@cdc/core/components/Layout'
|
|
|
11
11
|
import DashboardFiltersEditor from './DashboardFiltersEditor'
|
|
12
12
|
import { ViewPort } from '@cdc/core/types/ViewPort'
|
|
13
13
|
import { hasDashboardApplyBehavior } from '../../helpers/hasDashboardApplyBehavior'
|
|
14
|
+
import * as apiFilterHelpers from '../../helpers/apiFilterHelpers'
|
|
14
15
|
|
|
15
16
|
export type DropdownOptions = Record<'value' | 'text', string>[]
|
|
16
17
|
|
|
18
|
+
/** the cached dropdown options for each filter */
|
|
17
19
|
export type APIFilterDropdowns = {
|
|
18
20
|
// null means still loading
|
|
19
|
-
[
|
|
21
|
+
[dropdownsKey: string]: null | DropdownOptions
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
type DashboardFiltersProps = {
|
|
@@ -27,9 +29,15 @@ type DashboardFiltersProps = {
|
|
|
27
29
|
currentViewport?: ViewPort
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({
|
|
32
|
+
const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({
|
|
33
|
+
apiFilterDropdowns,
|
|
34
|
+
visualizationConfig,
|
|
35
|
+
setConfig: updateConfig,
|
|
36
|
+
currentViewport,
|
|
37
|
+
isEditor = false
|
|
38
|
+
}) => {
|
|
31
39
|
const state = useContext(DashboardContext)
|
|
32
|
-
const { config: dashboardConfig, reloadURLData, loadAPIFilters } = state
|
|
40
|
+
const { config: dashboardConfig, reloadURLData, loadAPIFilters, setAPIFilterDropdowns } = state
|
|
33
41
|
const dispatch = useContext(DashboardDispatchContext)
|
|
34
42
|
|
|
35
43
|
const applyFilters = () => {
|
|
@@ -54,8 +62,13 @@ const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({ apiFilterDro
|
|
|
54
62
|
dashboardConfig.sharedFilters[index].active = sharedFilter.queuedActive
|
|
55
63
|
delete dashboardConfig.sharedFilters[index].queuedActive
|
|
56
64
|
|
|
57
|
-
if (
|
|
58
|
-
|
|
65
|
+
if (
|
|
66
|
+
sharedFilter.setByQueryParameter &&
|
|
67
|
+
queryParams[sharedFilter.setByQueryParameter] !== sharedFilter.active
|
|
68
|
+
) {
|
|
69
|
+
queryParams[sharedFilter.setByQueryParameter] = Array.isArray(sharedFilter.active)
|
|
70
|
+
? sharedFilter.active.join(',')
|
|
71
|
+
: sharedFilter.active
|
|
59
72
|
needsQueryUpdate = true
|
|
60
73
|
}
|
|
61
74
|
}
|
|
@@ -68,7 +81,7 @@ const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({ apiFilterDro
|
|
|
68
81
|
|
|
69
82
|
dispatch({ type: 'SET_SHARED_FILTERS', payload: dashboardConfig.sharedFilters })
|
|
70
83
|
dispatch({ type: 'SET_FILTERED_DATA', payload: getFilteredData(_.cloneDeep(state)) })
|
|
71
|
-
loadAPIFilters(dashboardConfig.sharedFilters)
|
|
84
|
+
loadAPIFilters(dashboardConfig.sharedFilters, apiFilterDropdowns)
|
|
72
85
|
.then(newFilters => {
|
|
73
86
|
reloadURLData(newFilters)
|
|
74
87
|
})
|
|
@@ -82,24 +95,36 @@ const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({ apiFilterDro
|
|
|
82
95
|
|
|
83
96
|
const handleOnChange = (index: number, value: string | string[]) => {
|
|
84
97
|
const newConfig = _.cloneDeep(dashboardConfig)
|
|
85
|
-
let newSharedFilters = changeFilterActive(
|
|
98
|
+
let [newSharedFilters, changedFilterIndexes] = changeFilterActive(
|
|
99
|
+
index,
|
|
100
|
+
value,
|
|
101
|
+
newConfig.dashboard.sharedFilters,
|
|
102
|
+
visualizationConfig
|
|
103
|
+
)
|
|
86
104
|
|
|
87
105
|
if (hasDashboardApplyBehavior(dashboardConfig.visualizations)) {
|
|
88
106
|
const isAutoSelectFilter = visualizationConfig.autoLoad
|
|
89
107
|
const missingFilterSelections = newConfig.dashboard.sharedFilters.some(f => !f.active)
|
|
108
|
+
const apiEndpoints = newSharedFilters.filter(f => f.apiFilter).map(f => f.apiFilter.apiEndpoint)
|
|
109
|
+
const loadingFilterMemo = apiFilterHelpers.getLoadingFilterMemo(
|
|
110
|
+
apiEndpoints,
|
|
111
|
+
apiFilterDropdowns,
|
|
112
|
+
changedFilterIndexes
|
|
113
|
+
)
|
|
90
114
|
if (isAutoSelectFilter && !missingFilterSelections) {
|
|
91
115
|
// a dropdown has been selected that doesn't
|
|
92
116
|
// require the Go Button
|
|
93
|
-
|
|
117
|
+
setAPIFilterDropdowns(loadingFilterMemo)
|
|
118
|
+
loadAPIFilters(newSharedFilters, loadingFilterMemo).then(filters => {
|
|
94
119
|
reloadURLData(filters)
|
|
95
120
|
})
|
|
96
121
|
} else {
|
|
97
|
-
if (Array.isArray(value)) throw Error(`Cannot set active values on urlfilters. expected: ${JSON.stringify(value)} to be a single value.`)
|
|
98
122
|
newSharedFilters[index].queuedActive = value
|
|
99
123
|
// setData to empty object because we no longer have a data state.
|
|
100
124
|
dispatch({ type: 'SET_DATA', payload: {} })
|
|
101
125
|
dispatch({ type: 'SET_FILTERED_DATA', payload: {} })
|
|
102
|
-
|
|
126
|
+
setAPIFilterDropdowns(loadingFilterMemo)
|
|
127
|
+
loadAPIFilters(newSharedFilters, loadingFilterMemo)
|
|
103
128
|
}
|
|
104
129
|
} else {
|
|
105
130
|
if (newSharedFilters[index].apiFilter) {
|
|
@@ -122,20 +147,43 @@ const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({ apiFilterDro
|
|
|
122
147
|
})
|
|
123
148
|
}
|
|
124
149
|
|
|
150
|
+
// if all of the filters are hidden filters don't display the VisualizationWrapper
|
|
151
|
+
const filters = visualizationConfig?.sharedFilterIndexes
|
|
152
|
+
?.map(Number)
|
|
153
|
+
.map(filterIndex => dashboardConfig.dashboard.sharedFilters[filterIndex])
|
|
154
|
+
|
|
155
|
+
const displayNone = filters.length ? filters.every(filter => filter.showDropdown === false) : false
|
|
156
|
+
if (displayNone && !isEditor) return <></>
|
|
125
157
|
return (
|
|
126
158
|
<Layout.VisualizationWrapper config={visualizationConfig} isEditor={isEditor} currentViewport={currentViewport}>
|
|
127
159
|
{isEditor && (
|
|
128
|
-
<Layout.Sidebar
|
|
160
|
+
<Layout.Sidebar
|
|
161
|
+
displayPanel={displayPanel}
|
|
162
|
+
isDashboard={true}
|
|
163
|
+
title={'Configure Dashboard Filters'}
|
|
164
|
+
onBackClick={onBackClick}
|
|
165
|
+
>
|
|
129
166
|
<DashboardFiltersEditor updateConfig={updateConfig} vizConfig={visualizationConfig} />
|
|
130
167
|
</Layout.Sidebar>
|
|
131
168
|
)}
|
|
132
169
|
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
170
|
+
{!displayNone && (
|
|
171
|
+
<Layout.Responsive isEditor={isEditor}>
|
|
172
|
+
<div
|
|
173
|
+
className={`cdc-dashboard-inner-container${isEditor ? ' is-editor' : ''} cove-component__content col-12`}
|
|
174
|
+
>
|
|
175
|
+
<Filters
|
|
176
|
+
show={visualizationConfig?.sharedFilterIndexes?.map(Number)}
|
|
177
|
+
filters={dashboardConfig.dashboard.sharedFilters || []}
|
|
178
|
+
apiFilterDropdowns={apiFilterDropdowns}
|
|
179
|
+
handleOnChange={handleOnChange}
|
|
180
|
+
/>
|
|
181
|
+
{visualizationConfig.filterBehavior === FilterBehavior.Apply && !visualizationConfig.autoLoad && (
|
|
182
|
+
<button onClick={applyFilters}>GO!</button>
|
|
183
|
+
)}
|
|
184
|
+
</div>
|
|
185
|
+
</Layout.Responsive>
|
|
186
|
+
)}
|
|
139
187
|
</Layout.VisualizationWrapper>
|
|
140
188
|
)
|
|
141
189
|
}
|
|
@@ -13,11 +13,6 @@ type HeaderProps = {
|
|
|
13
13
|
visualizationKey?: string
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export const FilterBehavior = {
|
|
17
|
-
Apply: 'Apply Button',
|
|
18
|
-
OnChange: 'Filter Change'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
16
|
const Header = (props: HeaderProps) => {
|
|
22
17
|
const tabs: Tab[] = ['Dashboard Description', 'Data Table Settings', 'Dashboard Preview']
|
|
23
18
|
const { visualizationKey, subEditor } = props
|
|
@@ -23,7 +23,8 @@ const Tab = ({ name, handleClick, tabs, index, active }) => {
|
|
|
23
23
|
const { overlay } = useGlobalContext()
|
|
24
24
|
const inputRef = createRef<HTMLInputElement>()
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const saveName = e => {
|
|
27
|
+
e.stopPropagation()
|
|
27
28
|
const newVal = inputRef.current.value
|
|
28
29
|
const sameName = newVal === name
|
|
29
30
|
const blankName = !newVal
|
|
@@ -64,15 +65,26 @@ const Tab = ({ name, handleClick, tabs, index, active }) => {
|
|
|
64
65
|
const canMoveRight = index <= tabs.length - 2
|
|
65
66
|
|
|
66
67
|
return (
|
|
67
|
-
<li className='nav-item'>
|
|
68
|
+
<li className='nav-item d-flex mt-0'>
|
|
69
|
+
{canMoveLeft && editing && <button onClick={() => handleReorder(index, -1)}>{'<'}</button>}
|
|
68
70
|
<div className={`edit nav-link${active ? ' active' : ''}`} aria-current={active ? 'page' : null} onClick={onClick}>
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
{editing ? (
|
|
72
|
+
<div className='d-flex'>
|
|
73
|
+
<input type='text' defaultValue={name} onBlur={saveName} ref={inputRef} />
|
|
74
|
+
<button className='btn btn-link save' onClick={saveName}>
|
|
75
|
+
save
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
) : (
|
|
79
|
+
<>
|
|
80
|
+
{name}
|
|
81
|
+
<button className='remove' onClick={handleRemove}>
|
|
82
|
+
X
|
|
83
|
+
</button>
|
|
84
|
+
</>
|
|
85
|
+
)}
|
|
75
86
|
</div>
|
|
87
|
+
{canMoveRight && editing && <button onClick={() => handleReorder(index, 1)}>{'>'}</button>}
|
|
76
88
|
</li>
|
|
77
89
|
)
|
|
78
90
|
}
|
package/src/components/Row.tsx
CHANGED
|
@@ -193,7 +193,7 @@ const Row: React.FC<RowProps> = ({ row, idx: rowIdx, uuid }) => {
|
|
|
193
193
|
overlay?.actions.openOverlay(<DataDesignerModal rowIndex={rowIdx} />)
|
|
194
194
|
}}
|
|
195
195
|
>
|
|
196
|
-
{iconHash['
|
|
196
|
+
{iconHash['gearMulti']}
|
|
197
197
|
</button>
|
|
198
198
|
<div className='column-container'>
|
|
199
199
|
{row.columns
|