@cdc/dashboard 4.25.11 → 4.26.2
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/Dynamic_Data.md +66 -0
- package/dist/cdcdashboard-8NmHlKRI.es.js +15 -0
- package/dist/cdcdashboard-BPoPzKPz.es.js +6 -0
- package/dist/cdcdashboard-Cf9_fbQf.es.js +6 -0
- package/dist/{cdcdashboard-dgT_1dIT.es.js → cdcdashboard-DQ00cQCm.es.js} +1 -20
- package/dist/cdcdashboard-jiQQPkty.es.js +6 -0
- package/dist/cdcdashboard.js +83537 -86913
- package/examples/api-dashboard-data.json +272 -0
- package/examples/api-dashboard-years.json +11 -0
- package/examples/api-geographies-data.json +11 -0
- package/examples/default.json +522 -133
- package/examples/nested-dropdown.json +6985 -0
- package/examples/private/abc.json +467 -0
- package/examples/private/cat-y.json +1235 -0
- package/examples/private/chronic-dash.json +1584 -0
- package/examples/private/dash.json +12696 -0
- package/examples/private/map-issue.json +2260 -0
- package/examples/private/mpinc-state-reports.json +2260 -0
- package/examples/private/npcr.json +1 -0
- package/examples/private/nwss/rsv.json +1240 -0
- package/examples/private/simple-dash.json +490 -0
- package/examples/private/test-dash.json +0 -0
- package/examples/private/test.json +125407 -0
- package/examples/private/test123.json +491 -0
- package/examples/private/timeline-data.json +4994 -0
- package/examples/private/timeline.json +1708 -0
- package/examples/test-api-filter-reset.json +8 -4
- package/examples/test-dashboard-simple.json +503 -0
- package/examples/tp5-gauges.json +196 -0
- package/examples/tp5-test.json +266 -0
- package/index.html +1 -30
- package/package.json +39 -40
- package/src/CdcDashboardComponent.tsx +18 -5
- package/src/_stories/Dashboard.DataSetup.stories.tsx +204 -0
- package/src/_stories/Dashboard.stories.tsx +407 -1
- package/src/_stories/_mock/dashboard-line-chart-angles.json +1030 -0
- package/src/_stories/_mock/filter-cascade.json +3350 -0
- package/src/_stories/_mock/gallery-data-bite-dashboard.json +3500 -0
- package/src/_stories/_mock/nested-parent-child-filters.json +392 -0
- package/src/_stories/_mock/parent-child-filters.json +233 -0
- package/src/_stories/_mock/tp5-test.json +267 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +20 -11
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +92 -38
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +56 -30
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +151 -10
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +11 -7
- package/src/components/DataDesignerModal.tsx +6 -1
- package/src/components/Header/Header.tsx +51 -20
- package/src/components/VisualizationRow.tsx +76 -5
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +2 -20
- package/src/components/Widget/Widget.tsx +1 -1
- package/src/data/initial-state.js +1 -0
- package/src/helpers/addValuesToDashboardFilters.ts +30 -31
- package/src/helpers/apiFilterHelpers.ts +28 -32
- package/src/helpers/changeFilterActive.ts +67 -65
- package/src/helpers/formatConfigBeforeSave.ts +6 -5
- package/src/helpers/getUpdateConfig.ts +91 -91
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +141 -44
- package/src/helpers/tests/apiFilterHelpers.test.ts +523 -420
- package/src/helpers/tests/updatesChildFilters.test.ts +53 -22
- package/src/helpers/updateChildFilters.ts +50 -27
- package/src/scss/main.scss +144 -1
- package/src/test/CdcDashboard.test.jsx +9 -4
- package/src/types/Dashboard.ts +1 -0
- package/src/types/FilterStyles.ts +8 -7
- package/src/types/SharedFilter.ts +13 -0
- package/vite.config.js +7 -1
- package/LICENSE +0 -201
- package/dist/cdcdashboard-BnB1QM5d.es.js +0 -361528
- package/dist/cdcdashboard-Ct2SB0vL.es.js +0 -231049
- package/dist/cdcdashboard-D6CG2-Hb.es.js +0 -39377
- package/dist/cdcdashboard-MXgURbdZ.es.js +0 -39194
- package/examples/private/DEV-10538.json +0 -407
- package/examples/private/DEV-11072.json +0 -7591
- package/examples/private/DEV-11405.json +0 -39112
- package/examples/private/delete.json +0 -32919
- package/examples/private/pedro.json +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "dashboard",
|
|
3
|
+
"title": "TP5 Alignment Test Dashboard",
|
|
4
|
+
"description": "Testing alignment of TP5 Waffles, Data Bites, and Gauges",
|
|
5
|
+
"data": [
|
|
6
|
+
{
|
|
7
|
+
"Category": "Adults",
|
|
8
|
+
"Vaccination Rate": "68.5",
|
|
9
|
+
"Insured Rate": "87.2",
|
|
10
|
+
"Screening Rate": "72.8"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"Category": "Seniors",
|
|
14
|
+
"Vaccination Rate": "82.3",
|
|
15
|
+
"Insured Rate": "95.1",
|
|
16
|
+
"Screening Rate": "84.6"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"Category": "Youth",
|
|
20
|
+
"Vaccination Rate": "54.2",
|
|
21
|
+
"Insured Rate": "92.4",
|
|
22
|
+
"Screening Rate": "65.3"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"dashboard": {
|
|
26
|
+
"filters": []
|
|
27
|
+
},
|
|
28
|
+
"rows": [
|
|
29
|
+
[
|
|
30
|
+
{ "width": 4, "widget": "waffle1" },
|
|
31
|
+
{ "width": 4, "widget": "waffle2" },
|
|
32
|
+
{ "width": 4, "widget": "waffle3" }
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
{ "width": 4, "widget": "bite1" },
|
|
36
|
+
{ "width": 4, "widget": "bite2" },
|
|
37
|
+
{ "width": 4, "widget": "bite3" }
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
{ "width": 4, "widget": "gauge1" },
|
|
41
|
+
{ "width": 4, "widget": "gauge2" },
|
|
42
|
+
{ "width": 4, "widget": "gauge3" }
|
|
43
|
+
]
|
|
44
|
+
],
|
|
45
|
+
"visualizations": {
|
|
46
|
+
"waffle1": {
|
|
47
|
+
"uid": "waffle1",
|
|
48
|
+
"type": "waffle-chart",
|
|
49
|
+
"title": "Vaccination Coverage",
|
|
50
|
+
"visualizationType": "TP5 Waffle",
|
|
51
|
+
"visualizationSubType": "linear",
|
|
52
|
+
"showPercent": true,
|
|
53
|
+
"showDenominator": false,
|
|
54
|
+
"valueDescription": "",
|
|
55
|
+
"content": "of the population is vaccinated against seasonal flu",
|
|
56
|
+
"subtext": "Based on 2024 CDC surveillance data across all age groups",
|
|
57
|
+
"dataColumn": "Vaccination Rate",
|
|
58
|
+
"dataFunction": "Mean (Average)",
|
|
59
|
+
"customDenom": false,
|
|
60
|
+
"dataDenom": "100",
|
|
61
|
+
"suffix": "%",
|
|
62
|
+
"roundToPlace": "1",
|
|
63
|
+
"theme": "theme-blue",
|
|
64
|
+
"shape": "square",
|
|
65
|
+
"visual": {
|
|
66
|
+
"whiteBackground": false
|
|
67
|
+
},
|
|
68
|
+
"showTitle": true,
|
|
69
|
+
"overallFontSize": "medium"
|
|
70
|
+
},
|
|
71
|
+
"waffle2": {
|
|
72
|
+
"uid": "waffle2",
|
|
73
|
+
"type": "waffle-chart",
|
|
74
|
+
"title": "Health Insurance Coverage Rate",
|
|
75
|
+
"visualizationType": "TP5 Waffle",
|
|
76
|
+
"visualizationSubType": "linear",
|
|
77
|
+
"showPercent": true,
|
|
78
|
+
"showDenominator": false,
|
|
79
|
+
"valueDescription": "",
|
|
80
|
+
"content": "completed recommended cancer screenings including mammography, colonoscopy, and cervical cancer screening",
|
|
81
|
+
"subtext": "",
|
|
82
|
+
"dataColumn": "Insured Rate",
|
|
83
|
+
"dataFunction": "Mean (Average)",
|
|
84
|
+
"customDenom": false,
|
|
85
|
+
"dataDenom": "100",
|
|
86
|
+
"suffix": "%",
|
|
87
|
+
"roundToPlace": "1",
|
|
88
|
+
"theme": "theme-teal",
|
|
89
|
+
"shape": "person",
|
|
90
|
+
"visual": {
|
|
91
|
+
"whiteBackground": false
|
|
92
|
+
},
|
|
93
|
+
"showTitle": true,
|
|
94
|
+
"overallFontSize": "medium"
|
|
95
|
+
},
|
|
96
|
+
"waffle3": {
|
|
97
|
+
"uid": "waffle3",
|
|
98
|
+
"type": "waffle-chart",
|
|
99
|
+
"title": "Cancer Screening Completion",
|
|
100
|
+
"visualizationType": "TP5 Waffle",
|
|
101
|
+
"visualizationSubType": "linear",
|
|
102
|
+
"showPercent": true,
|
|
103
|
+
"showDenominator": false,
|
|
104
|
+
"valueDescription": "",
|
|
105
|
+
"content": "completed recommended cancer screenings including mammography, colonoscopy, and cervical cancer screening",
|
|
106
|
+
"subtext": "Data from National Health Interview Survey 2024",
|
|
107
|
+
"dataColumn": "Screening Rate",
|
|
108
|
+
"dataFunction": "Mean (Average)",
|
|
109
|
+
"customDenom": false,
|
|
110
|
+
"dataDenom": "100",
|
|
111
|
+
"suffix": "%",
|
|
112
|
+
"roundToPlace": "1",
|
|
113
|
+
"theme": "theme-purple",
|
|
114
|
+
"shape": "circle",
|
|
115
|
+
"visual": {
|
|
116
|
+
"whiteBackground": false
|
|
117
|
+
},
|
|
118
|
+
"showTitle": true,
|
|
119
|
+
"overallFontSize": "medium"
|
|
120
|
+
},
|
|
121
|
+
"gauge1": {
|
|
122
|
+
"uid": "gauge1",
|
|
123
|
+
"type": "waffle-chart",
|
|
124
|
+
"title": "Vaccination Coverage",
|
|
125
|
+
"visualizationType": "TP5 Gauge",
|
|
126
|
+
"visualizationSubType": "linear",
|
|
127
|
+
"showPercent": true,
|
|
128
|
+
"showDenominator": false,
|
|
129
|
+
"valueDescription": "",
|
|
130
|
+
"content": "of the population is vaccinated against seasonal flu",
|
|
131
|
+
"subtext": "Based on 2024 CDC surveillance data across all age groups",
|
|
132
|
+
"dataColumn": "Vaccination Rate",
|
|
133
|
+
"dataFunction": "Mean (Average)",
|
|
134
|
+
"customDenom": false,
|
|
135
|
+
"dataDenom": "100",
|
|
136
|
+
"suffix": "%",
|
|
137
|
+
"roundToPlace": "1",
|
|
138
|
+
"theme": "theme-blue",
|
|
139
|
+
"gauge": {
|
|
140
|
+
"height": 35,
|
|
141
|
+
"width": "100%"
|
|
142
|
+
},
|
|
143
|
+
"visual": {
|
|
144
|
+
"whiteBackground": false
|
|
145
|
+
},
|
|
146
|
+
"showTitle": true,
|
|
147
|
+
"overallFontSize": "medium"
|
|
148
|
+
},
|
|
149
|
+
"gauge2": {
|
|
150
|
+
"uid": "gauge2",
|
|
151
|
+
"type": "waffle-chart",
|
|
152
|
+
"title": "Health Insurance Coverage Rate",
|
|
153
|
+
"visualizationType": "TP5 Gauge",
|
|
154
|
+
"visualizationSubType": "linear",
|
|
155
|
+
"showPercent": true,
|
|
156
|
+
"showDenominator": false,
|
|
157
|
+
"valueDescription": "",
|
|
158
|
+
"content": "",
|
|
159
|
+
"subtext": "",
|
|
160
|
+
"dataColumn": "Insured Rate",
|
|
161
|
+
"dataFunction": "Mean (Average)",
|
|
162
|
+
"customDenom": false,
|
|
163
|
+
"dataDenom": "100",
|
|
164
|
+
"suffix": "%",
|
|
165
|
+
"roundToPlace": "1",
|
|
166
|
+
"theme": "theme-teal",
|
|
167
|
+
"gauge": {
|
|
168
|
+
"height": 35,
|
|
169
|
+
"width": "100%"
|
|
170
|
+
},
|
|
171
|
+
"visual": {
|
|
172
|
+
"whiteBackground": false
|
|
173
|
+
},
|
|
174
|
+
"showTitle": true,
|
|
175
|
+
"overallFontSize": "medium"
|
|
176
|
+
},
|
|
177
|
+
"gauge3": {
|
|
178
|
+
"uid": "gauge3",
|
|
179
|
+
"type": "waffle-chart",
|
|
180
|
+
"title": "Cancer Screening Completion",
|
|
181
|
+
"visualizationType": "TP5 Gauge",
|
|
182
|
+
"visualizationSubType": "linear",
|
|
183
|
+
"showPercent": true,
|
|
184
|
+
"showDenominator": false,
|
|
185
|
+
"valueDescription": "",
|
|
186
|
+
"content": "completed recommended cancer screenings including mammography, colonoscopy, and cervical cancer screening",
|
|
187
|
+
"subtext": "Data from National Health Interview Survey 2024",
|
|
188
|
+
"dataColumn": "Screening Rate",
|
|
189
|
+
"dataFunction": "Mean (Average)",
|
|
190
|
+
"customDenom": false,
|
|
191
|
+
"dataDenom": "100",
|
|
192
|
+
"suffix": "%",
|
|
193
|
+
"roundToPlace": "1",
|
|
194
|
+
"theme": "theme-purple",
|
|
195
|
+
"gauge": {
|
|
196
|
+
"height": 35,
|
|
197
|
+
"width": "100%"
|
|
198
|
+
},
|
|
199
|
+
"visual": {
|
|
200
|
+
"whiteBackground": false
|
|
201
|
+
},
|
|
202
|
+
"showTitle": true,
|
|
203
|
+
"overallFontSize": "medium"
|
|
204
|
+
},
|
|
205
|
+
"bite1": {
|
|
206
|
+
"uid": "bite1",
|
|
207
|
+
"type": "data-bite",
|
|
208
|
+
"title": "Vaccination Coverage",
|
|
209
|
+
"biteStyle": "tp5",
|
|
210
|
+
"dataColumn": "Vaccination Rate",
|
|
211
|
+
"dataFunction": "Mean (Average)",
|
|
212
|
+
"biteBody": "of the population is vaccinated against seasonal flu",
|
|
213
|
+
"subtext": "Based on 2024 CDC surveillance data across all age groups",
|
|
214
|
+
"dataFormat": {
|
|
215
|
+
"roundToPlace": 1,
|
|
216
|
+
"commas": true,
|
|
217
|
+
"prefix": "",
|
|
218
|
+
"suffix": "%"
|
|
219
|
+
},
|
|
220
|
+
"theme": "theme-blue",
|
|
221
|
+
"visual": {
|
|
222
|
+
"hideBackgroundColor": false
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"bite2": {
|
|
226
|
+
"uid": "bite2",
|
|
227
|
+
"type": "data-bite",
|
|
228
|
+
"title": "Health Insurance Coverage Rate",
|
|
229
|
+
"biteStyle": "tp5",
|
|
230
|
+
"dataColumn": "Insured Rate",
|
|
231
|
+
"dataFunction": "Mean (Average)",
|
|
232
|
+
"biteBody": "",
|
|
233
|
+
"subtext": "",
|
|
234
|
+
"dataFormat": {
|
|
235
|
+
"roundToPlace": 1,
|
|
236
|
+
"commas": true,
|
|
237
|
+
"prefix": "",
|
|
238
|
+
"suffix": "%"
|
|
239
|
+
},
|
|
240
|
+
"theme": "theme-teal",
|
|
241
|
+
"visual": {
|
|
242
|
+
"hideBackgroundColor": false
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"bite3": {
|
|
246
|
+
"uid": "bite3",
|
|
247
|
+
"type": "data-bite",
|
|
248
|
+
"title": "Cancer Screening Completion",
|
|
249
|
+
"biteStyle": "tp5",
|
|
250
|
+
"dataColumn": "Screening Rate",
|
|
251
|
+
"dataFunction": "Mean (Average)",
|
|
252
|
+
"biteBody": "completed recommended cancer screenings including mammography, colonoscopy, and cervical cancer screening",
|
|
253
|
+
"subtext": "Data from National Health Interview Survey 2024",
|
|
254
|
+
"dataFormat": {
|
|
255
|
+
"roundToPlace": 1,
|
|
256
|
+
"commas": true,
|
|
257
|
+
"prefix": "",
|
|
258
|
+
"suffix": "%"
|
|
259
|
+
},
|
|
260
|
+
"theme": "theme-purple",
|
|
261
|
+
"visual": {
|
|
262
|
+
"hideBackgroundColor": false
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import MultiSelect from '@cdc/core/components/MultiSelect'
|
|
3
|
+
import ComboBox from '@cdc/core/components/ComboBox'
|
|
3
4
|
import { SharedFilter } from '../../types/SharedFilter'
|
|
4
5
|
import { APIFilterDropdowns, DropdownOptions } from './DashboardFiltersWrapper'
|
|
5
6
|
import { FILTER_STYLE } from '../../types/FilterStyles'
|
|
6
7
|
import { NestedOptions, ValueTextPair } from '@cdc/core/components/NestedDropdown/nestedDropdownHelpers'
|
|
7
8
|
import NestedDropdown from '@cdc/core/components/NestedDropdown'
|
|
9
|
+
import { getNestedOptions } from '@cdc/core/components/Filters/helpers/getNestedOptions'
|
|
8
10
|
import { MouseEventHandler } from 'react'
|
|
9
11
|
import Loader from '@cdc/core/components/Loader'
|
|
10
12
|
import _ from 'lodash'
|
|
@@ -56,14 +58,12 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
56
58
|
|
|
57
59
|
return (
|
|
58
60
|
<form className='d-flex flex-wrap'>
|
|
59
|
-
{
|
|
61
|
+
{show.map(filterIndex => {
|
|
62
|
+
const filter = sharedFilters[filterIndex]
|
|
60
63
|
const urlFilterType = filter.type === 'urlfilter'
|
|
61
64
|
const label = stripDuplicateLabelIncrement(filter.key || '')
|
|
62
65
|
|
|
63
|
-
if (
|
|
64
|
-
(!urlFilterType && !filter.showDropdown && filter.filterStyle !== FILTER_STYLE.nestedDropdown) ||
|
|
65
|
-
(show && !show.includes(filterIndex))
|
|
66
|
-
)
|
|
66
|
+
if (!urlFilterType && !filter.showDropdown && filter.filterStyle !== FILTER_STYLE.nestedDropdown)
|
|
67
67
|
return <React.Fragment key={`${filter.key}-filtersection-${filterIndex}-option`} />
|
|
68
68
|
const values: JSX.Element[] = []
|
|
69
69
|
|
|
@@ -71,12 +71,11 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
71
71
|
const loading = apiFilterDropdowns[_key] === null
|
|
72
72
|
|
|
73
73
|
const multiValues: { value; label }[] = []
|
|
74
|
-
const nestedOptions: NestedOptions =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
74
|
+
const nestedOptions: NestedOptions = getNestedOptions({
|
|
75
|
+
orderedValues: filter.orderedValues,
|
|
76
|
+
values: filter.values,
|
|
77
|
+
subGrouping: filter.subGrouping
|
|
78
|
+
})
|
|
80
79
|
|
|
81
80
|
if (_key && apiFilterDropdowns[_key]) {
|
|
82
81
|
// URL Filter
|
|
@@ -154,6 +153,16 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
154
153
|
handleSelectedItems={value => updateField(null, null, filterIndex, value)}
|
|
155
154
|
loading={loading}
|
|
156
155
|
/>
|
|
156
|
+
) : filter.filterStyle === FILTER_STYLE.combobox ? (
|
|
157
|
+
<ComboBox
|
|
158
|
+
options={multiValues}
|
|
159
|
+
fieldName={filterIndex}
|
|
160
|
+
updateField={updateField}
|
|
161
|
+
selected={(filter.queuedActive || filter.active) as string}
|
|
162
|
+
label={label}
|
|
163
|
+
loading={loading}
|
|
164
|
+
placeholder={filter.resetLabel || '- Select -'}
|
|
165
|
+
/>
|
|
157
166
|
) : (
|
|
158
167
|
<>
|
|
159
168
|
<select
|
|
@@ -21,6 +21,7 @@ import { addValuesToDashboardFilters } from '../../../helpers/addValuesToDashboa
|
|
|
21
21
|
import { FILTER_STYLE } from '../../../types/FilterStyles'
|
|
22
22
|
import { handleSorting } from '@cdc/core/components/Filters'
|
|
23
23
|
import { removeDashboardFilter } from '../../../helpers/removeDashboardFilter'
|
|
24
|
+
import { DragDropContext, Droppable, Draggable, DropResult } from '@hello-pangea/dnd'
|
|
24
25
|
|
|
25
26
|
type DashboardFitlersEditorProps = {
|
|
26
27
|
vizConfig: DashboardFilters
|
|
@@ -51,6 +52,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
51
52
|
|
|
52
53
|
const openControls = useState({})
|
|
53
54
|
const [canAddExisting, setCanAddExisting] = useState(false)
|
|
55
|
+
const [isNestedDragHovered, setIsNestedDragHovered] = useState(false)
|
|
54
56
|
|
|
55
57
|
const updateFilterProp = (prop: string, index: number, value) => {
|
|
56
58
|
const newSharedFilters = _.cloneDeep(sharedFilters)
|
|
@@ -71,6 +73,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
71
73
|
newSharedFilters[index][prop] = value
|
|
72
74
|
if (prop === 'columnName') {
|
|
73
75
|
if (newSharedFilters[index].subGrouping) delete newSharedFilters[index].subGrouping
|
|
76
|
+
newSharedFilters[index].defaultValue = ''
|
|
74
77
|
// changing a data column and want to load the data into the preview options
|
|
75
78
|
const sharedFiltersWithValues = addValuesToDashboardFilters(newSharedFilters, data)
|
|
76
79
|
dispatch({ type: 'SET_SHARED_FILTERS', payload: sharedFiltersWithValues })
|
|
@@ -122,6 +125,29 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
122
125
|
dispatch({ type: 'SET_CONFIG', payload: { dashboard, visualizations: newVisualizations } })
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
const handleFilterReorder = (result: DropResult) => {
|
|
129
|
+
const { source, destination } = result
|
|
130
|
+
if (!destination || source.index === destination.index) return
|
|
131
|
+
|
|
132
|
+
const newIndexes = [...vizConfig.sharedFilterIndexes]
|
|
133
|
+
const [movedIndex] = newIndexes.splice(source.index, 1)
|
|
134
|
+
newIndexes.splice(destination.index, 0, movedIndex)
|
|
135
|
+
|
|
136
|
+
updateConfig({
|
|
137
|
+
...vizConfig,
|
|
138
|
+
sharedFilterIndexes: newIndexes
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const getItemStyle = (isDragging, draggableStyle) => ({
|
|
143
|
+
...draggableStyle,
|
|
144
|
+
...(isDragging && sortableItemStyles)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const sortableItemStyles = {
|
|
148
|
+
background: 'rgba(0, 0, 0, 0.1)'
|
|
149
|
+
}
|
|
150
|
+
|
|
125
151
|
const addNewFilter = () => {
|
|
126
152
|
const _sharedFilters = _.cloneDeep(sharedFilters) || []
|
|
127
153
|
const columnName = 'New Dashboard Filter ' + (_sharedFilters.length + 1)
|
|
@@ -219,44 +245,72 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
219
245
|
<AccordionItemButton>Filters</AccordionItemButton>
|
|
220
246
|
</AccordionItemHeading>
|
|
221
247
|
<AccordionItemPanel>
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
248
|
+
<DragDropContext onDragEnd={handleFilterReorder}>
|
|
249
|
+
<Droppable droppableId='dashboard_filters_list'>
|
|
250
|
+
{provided => (
|
|
251
|
+
<ul {...provided.droppableProps} ref={provided.innerRef} className='draggable-field-list'>
|
|
252
|
+
{vizConfig.sharedFilterIndexes.map((index, filterIndex) => {
|
|
253
|
+
const filter = sharedFilters[index]
|
|
254
|
+
return (
|
|
255
|
+
<Draggable
|
|
256
|
+
key={filter.key + index}
|
|
257
|
+
draggableId={`filter-${filter.key}-${index}`}
|
|
258
|
+
index={filterIndex}
|
|
259
|
+
isDragDisabled={isNestedDragHovered}
|
|
260
|
+
>
|
|
261
|
+
{(provided, snapshot) => (
|
|
262
|
+
<div
|
|
263
|
+
ref={provided.innerRef}
|
|
264
|
+
{...provided.draggableProps}
|
|
265
|
+
{...provided.dragHandleProps}
|
|
266
|
+
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
|
|
267
|
+
className={snapshot.isDragging ? 'currently-dragging' : ''}
|
|
268
|
+
>
|
|
269
|
+
<FieldSetWrapper
|
|
270
|
+
key={filter.key + index}
|
|
271
|
+
fieldName={filter.key}
|
|
272
|
+
fieldKey={index}
|
|
273
|
+
fieldType='Dashboard Filter'
|
|
274
|
+
controls={openControls}
|
|
275
|
+
draggable={true}
|
|
276
|
+
deleteField={() => {
|
|
277
|
+
overlay?.actions.openOverlay(
|
|
278
|
+
<DeleteFilterModal
|
|
279
|
+
removeFilterCompletely={removeFilter}
|
|
280
|
+
removeFilterFromViz={index => {
|
|
281
|
+
updateConfig({
|
|
282
|
+
...vizConfig,
|
|
283
|
+
sharedFilterIndexes: vizConfig.sharedFilterIndexes.filter(i => i !== index)
|
|
284
|
+
})
|
|
285
|
+
}}
|
|
286
|
+
filterIndex={index}
|
|
287
|
+
/>
|
|
288
|
+
)
|
|
289
|
+
}}
|
|
290
|
+
>
|
|
291
|
+
<FilterEditor
|
|
292
|
+
filter={filter}
|
|
293
|
+
filterIndex={index}
|
|
294
|
+
updateFilterProp={(name, value) => {
|
|
295
|
+
updateFilterProp(name, index, value)
|
|
296
|
+
}}
|
|
297
|
+
toggleNestedQueryParameters={checked => {
|
|
298
|
+
toggleNestedQueryParameters(index, checked)
|
|
299
|
+
}}
|
|
300
|
+
config={config}
|
|
301
|
+
onNestedDragAreaHover={setIsNestedDragHovered}
|
|
302
|
+
/>
|
|
303
|
+
</FieldSetWrapper>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
</Draggable>
|
|
307
|
+
)
|
|
308
|
+
})}
|
|
309
|
+
{provided.placeholder}
|
|
310
|
+
</ul>
|
|
311
|
+
)}
|
|
312
|
+
</Droppable>
|
|
313
|
+
</DragDropContext>
|
|
260
314
|
<button onClick={addNewFilter} className='btn btn-primary full-width'>
|
|
261
315
|
Add Filter
|
|
262
316
|
</button>
|
|
@@ -27,6 +27,7 @@ type FilterEditorProps = {
|
|
|
27
27
|
filterIndex: number
|
|
28
28
|
updateFilterProp: (name: keyof SharedFilter, value: any) => void
|
|
29
29
|
toggleNestedQueryParameters: (checked: boolean) => void
|
|
30
|
+
onNestedDragAreaHover?: (isHovering: boolean) => void
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
@@ -34,7 +35,8 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
34
35
|
filterIndex,
|
|
35
36
|
config,
|
|
36
37
|
updateFilterProp,
|
|
37
|
-
toggleNestedQueryParameters
|
|
38
|
+
toggleNestedQueryParameters,
|
|
39
|
+
onNestedDragAreaHover
|
|
38
40
|
}) => {
|
|
39
41
|
const [columns, setColumns] = useState<string[]>([])
|
|
40
42
|
const [dataFiltersLoading, setDataFiltersLoading] = useState(false)
|
|
@@ -43,7 +45,11 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
43
45
|
const filterStyles = Object.values(FILTER_STYLE)
|
|
44
46
|
|
|
45
47
|
const parentFilters: string[] = (config.dashboard.sharedFilters || [])
|
|
46
|
-
.filter(({ key
|
|
48
|
+
.filter(({ key }) => key !== filter.key)
|
|
49
|
+
.map(({ key }) => key)
|
|
50
|
+
|
|
51
|
+
const dataFilterParents: string[] = (config.dashboard.sharedFilters || [])
|
|
52
|
+
.filter(({ key }) => key !== filter.key)
|
|
47
53
|
.map(({ key }) => key)
|
|
48
54
|
|
|
49
55
|
const vizRowColumnLocator = getVizRowColumnLocator(config.rows)
|
|
@@ -246,21 +252,6 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
246
252
|
<>
|
|
247
253
|
{!hasDashboardApplyBehavior(config.visualizations) && (
|
|
248
254
|
<>
|
|
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
|
-
/>
|
|
263
|
-
|
|
264
255
|
<Select
|
|
265
256
|
label='Filter By'
|
|
266
257
|
value={filter.filterBy || ''}
|
|
@@ -271,6 +262,40 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
271
262
|
]}
|
|
272
263
|
onChange={e => updateFilterProp('filterBy', e.target.value)}
|
|
273
264
|
/>
|
|
265
|
+
|
|
266
|
+
{filter.filterBy === 'File Name' && (
|
|
267
|
+
<Select
|
|
268
|
+
label='URL to Filter'
|
|
269
|
+
value={filter.datasetKey || ''}
|
|
270
|
+
options={[
|
|
271
|
+
{ value: '', label: '- Select Option -' },
|
|
272
|
+
...Object.keys(config.datasets)
|
|
273
|
+
.filter(datasetKey => config.datasets[datasetKey].dataUrl)
|
|
274
|
+
.map(datasetKey => ({
|
|
275
|
+
value: datasetKey,
|
|
276
|
+
label: config.datasets[datasetKey].dataUrl
|
|
277
|
+
}))
|
|
278
|
+
]}
|
|
279
|
+
onChange={e => updateFilterProp('datasetKey', e.target.value)}
|
|
280
|
+
tooltip={
|
|
281
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
282
|
+
<Tooltip.Target>
|
|
283
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
284
|
+
</Tooltip.Target>
|
|
285
|
+
<Tooltip.Content>
|
|
286
|
+
<p>Select which dataset URL's filename should be modified by this filter.</p>
|
|
287
|
+
</Tooltip.Content>
|
|
288
|
+
</Tooltip>
|
|
289
|
+
}
|
|
290
|
+
/>
|
|
291
|
+
)}
|
|
292
|
+
|
|
293
|
+
{filter.filterBy === 'Query String' && filter.usedBy && filter.usedBy.length > 0 && (
|
|
294
|
+
<div className='bg-info-subtle p-2 my-2' style={{ fontSize: '0.9em' }}>
|
|
295
|
+
<Icon display='info' style={{ marginRight: '0.5rem' }} />
|
|
296
|
+
Will apply to datasets used by selected widgets
|
|
297
|
+
</div>
|
|
298
|
+
)}
|
|
274
299
|
{filter.filterBy === 'File Name' && (
|
|
275
300
|
<>
|
|
276
301
|
<TextField
|
|
@@ -523,6 +548,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
523
548
|
values.splice(index2, 0, removed)
|
|
524
549
|
updateFilterProp('orderedValues', values)
|
|
525
550
|
}}
|
|
551
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
526
552
|
/>
|
|
527
553
|
)}
|
|
528
554
|
|
|
@@ -546,6 +572,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
546
572
|
}}
|
|
547
573
|
isDashboard={true}
|
|
548
574
|
config={config}
|
|
575
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
549
576
|
/>
|
|
550
577
|
<label>
|
|
551
578
|
<input
|
|
@@ -617,19 +644,18 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
617
644
|
updateField={(_section, _subSection, _key, value) => updateFilterProp('resetLabel', value)}
|
|
618
645
|
/>
|
|
619
646
|
|
|
620
|
-
<
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
{ value:
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
/>
|
|
647
|
+
<label>
|
|
648
|
+
<span className='edit-label column-heading mt-1'>Parent Filter(s): </span>
|
|
649
|
+
<MultiSelect
|
|
650
|
+
label='Parent Filter(s): '
|
|
651
|
+
options={dataFilterParents.map(key => ({ value: key, label: key }))}
|
|
652
|
+
fieldName='parents'
|
|
653
|
+
selected={filter.parents}
|
|
654
|
+
updateField={(_section, _subsection, _fieldname, newItems) => {
|
|
655
|
+
updateFilterProp('parents', newItems)
|
|
656
|
+
}}
|
|
657
|
+
/>
|
|
658
|
+
</label>
|
|
633
659
|
|
|
634
660
|
{!isNestedDropdown && (
|
|
635
661
|
<TextField
|