@cdc/dashboard 4.26.1 → 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/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 +59394 -65183
- package/examples/default.json +522 -133
- package/examples/nested-dropdown.json +6985 -0
- package/examples/private/abc.json +467 -0
- package/examples/private/dash.json +12696 -0
- package/examples/private/npcr.json +1 -0
- package/examples/private/test.json +125407 -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/tp5-gauges.json +196 -0
- package/examples/tp5-test.json +266 -0
- package/index.html +1 -29
- package/package.json +38 -40
- package/src/CdcDashboardComponent.tsx +0 -3
- package/src/_stories/Dashboard.DataSetup.stories.tsx +2 -1
- package/src/_stories/Dashboard.stories.tsx +27 -5
- package/src/_stories/_mock/dashboard-line-chart-angles.json +1030 -0
- package/src/_stories/_mock/tp5-test.json +267 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +3 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +5 -1
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +5 -1
- package/src/components/VisualizationRow.tsx +30 -25
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +0 -17
- package/src/helpers/addValuesToDashboardFilters.ts +17 -11
- package/src/helpers/apiFilterHelpers.ts +28 -32
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +141 -44
- package/src/helpers/tests/apiFilterHelpers.test.ts +523 -420
- package/src/scss/main.scss +8 -5
- package/vite.config.js +7 -1
- 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
|
@@ -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
|
+
|
|
@@ -52,6 +52,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
52
52
|
|
|
53
53
|
const openControls = useState({})
|
|
54
54
|
const [canAddExisting, setCanAddExisting] = useState(false)
|
|
55
|
+
const [isNestedDragHovered, setIsNestedDragHovered] = useState(false)
|
|
55
56
|
|
|
56
57
|
const updateFilterProp = (prop: string, index: number, value) => {
|
|
57
58
|
const newSharedFilters = _.cloneDeep(sharedFilters)
|
|
@@ -255,6 +256,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
255
256
|
key={filter.key + index}
|
|
256
257
|
draggableId={`filter-${filter.key}-${index}`}
|
|
257
258
|
index={filterIndex}
|
|
259
|
+
isDragDisabled={isNestedDragHovered}
|
|
258
260
|
>
|
|
259
261
|
{(provided, snapshot) => (
|
|
260
262
|
<div
|
|
@@ -296,6 +298,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
296
298
|
toggleNestedQueryParameters(index, checked)
|
|
297
299
|
}}
|
|
298
300
|
config={config}
|
|
301
|
+
onNestedDragAreaHover={setIsNestedDragHovered}
|
|
299
302
|
/>
|
|
300
303
|
</FieldSetWrapper>
|
|
301
304
|
</div>
|
|
@@ -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)
|
|
@@ -546,6 +548,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
546
548
|
values.splice(index2, 0, removed)
|
|
547
549
|
updateFilterProp('orderedValues', values)
|
|
548
550
|
}}
|
|
551
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
549
552
|
/>
|
|
550
553
|
)}
|
|
551
554
|
|
|
@@ -569,6 +572,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
569
572
|
}}
|
|
570
573
|
isDashboard={true}
|
|
571
574
|
config={config}
|
|
575
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
572
576
|
/>
|
|
573
577
|
<label>
|
|
574
578
|
<input
|
|
@@ -12,13 +12,15 @@ type NestedDropDownEditorDashboardProps = {
|
|
|
12
12
|
filter: SharedFilter
|
|
13
13
|
isDashboard: boolean
|
|
14
14
|
updateFilterProp: Function
|
|
15
|
+
onNestedDragAreaHover?: (isHovering: boolean) => void
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
18
19
|
filter,
|
|
19
20
|
config,
|
|
20
21
|
isDashboard = false,
|
|
21
|
-
updateFilterProp
|
|
22
|
+
updateFilterProp,
|
|
23
|
+
onNestedDragAreaHover
|
|
22
24
|
}) => {
|
|
23
25
|
const subGrouping = filter?.subGrouping
|
|
24
26
|
|
|
@@ -270,6 +272,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
270
272
|
<FilterOrder
|
|
271
273
|
orderedValues={filter.orderedValues || filter.values}
|
|
272
274
|
handleFilterOrder={handleGroupingCustomOrder}
|
|
275
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
273
276
|
/>
|
|
274
277
|
)}
|
|
275
278
|
</div>
|
|
@@ -298,6 +301,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
298
301
|
handleFilterOrder={(sourceIndex, destinationIndex) => {
|
|
299
302
|
handleSubGroupingCustomOrder(sourceIndex, destinationIndex, orderedSubGroupValues, groupName)
|
|
300
303
|
}}
|
|
304
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
301
305
|
/>
|
|
302
306
|
</div>
|
|
303
307
|
)
|
|
@@ -103,50 +103,55 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
103
103
|
}
|
|
104
104
|
}, [toggledRow, row.toggle])
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!rowElement) return
|
|
106
|
+
const setupTP5MinHeightEqualizer = (rowElement: Element, itemSelector: string) => {
|
|
107
|
+
const items = Array.from(rowElement.querySelectorAll(itemSelector)) as HTMLElement[]
|
|
108
|
+
if (items.length <= 1) return undefined
|
|
110
109
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const equalizeTP5Titles = () => {
|
|
115
|
-
// Reset heights first
|
|
116
|
-
tp5Titles.forEach((title: HTMLElement) => {
|
|
117
|
-
title.style.minHeight = ''
|
|
110
|
+
const equalizeHeights = () => {
|
|
111
|
+
items.forEach(item => {
|
|
112
|
+
item.style.minHeight = ''
|
|
118
113
|
})
|
|
119
114
|
|
|
120
|
-
// Calculate max height after reset
|
|
121
115
|
let maxHeight = 0
|
|
122
|
-
|
|
123
|
-
const height =
|
|
116
|
+
items.forEach(item => {
|
|
117
|
+
const height = item.offsetHeight
|
|
124
118
|
if (height > maxHeight) maxHeight = height
|
|
125
119
|
})
|
|
126
120
|
|
|
127
|
-
// Apply max height to all titles
|
|
128
121
|
if (maxHeight > 0) {
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
items.forEach(item => {
|
|
123
|
+
item.style.minHeight = `${maxHeight}px`
|
|
131
124
|
})
|
|
132
125
|
}
|
|
133
126
|
}
|
|
134
127
|
|
|
135
|
-
|
|
136
|
-
equalizeTP5Titles()
|
|
128
|
+
equalizeHeights()
|
|
137
129
|
|
|
138
|
-
// Use ResizeObserver to watch for size changes in any of the titles
|
|
139
130
|
const resizeObserver = new ResizeObserver(() => {
|
|
140
|
-
|
|
131
|
+
equalizeHeights()
|
|
141
132
|
})
|
|
142
133
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
resizeObserver.observe(title as Element)
|
|
134
|
+
items.forEach(item => {
|
|
135
|
+
resizeObserver.observe(item)
|
|
146
136
|
})
|
|
147
137
|
|
|
138
|
+
return () => resizeObserver.disconnect()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Equalize TP5 callout title heights and TP5 gauge message blocks for like visualizations in the same row
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
const rowElement = document.querySelector(`[data-row-index="${index}"]`)
|
|
144
|
+
if (!rowElement) return
|
|
145
|
+
|
|
146
|
+
const cleanups = [
|
|
147
|
+
setupTP5MinHeightEqualizer(rowElement, '.bite__style--tp5 .cdc-callout__heading'),
|
|
148
|
+
setupTP5MinHeightEqualizer(rowElement, '.waffle__style--tp5 .cdc-callout__heading'),
|
|
149
|
+
setupTP5MinHeightEqualizer(rowElement, '.gauge__style--tp5 .cdc-callout__heading'),
|
|
150
|
+
setupTP5MinHeightEqualizer(rowElement, '.gauge__style--tp5 .cove-gauge-chart__content')
|
|
151
|
+
].filter(Boolean) as Array<() => void>
|
|
152
|
+
|
|
148
153
|
return () => {
|
|
149
|
-
|
|
154
|
+
cleanups.forEach(cleanup => cleanup())
|
|
150
155
|
}
|
|
151
156
|
}, [index, row, config, filteredDataOverride])
|
|
152
157
|
|
|
@@ -46,24 +46,7 @@ const addVisualization = (type, subType) => {
|
|
|
46
46
|
newVisualizationConfig.visualizationType = type
|
|
47
47
|
break
|
|
48
48
|
case 'markup-include':
|
|
49
|
-
newVisualizationConfig.contentEditor = {
|
|
50
|
-
inlineHTML: '<h2>Inline HTML</h2>',
|
|
51
|
-
markupVariables: [],
|
|
52
|
-
showHeader: true,
|
|
53
|
-
srcUrl: '#example',
|
|
54
|
-
useInlineHTML: true
|
|
55
|
-
}
|
|
56
|
-
newVisualizationConfig.theme = 'theme-blue'
|
|
57
|
-
newVisualizationConfig.visual = {
|
|
58
|
-
border: false,
|
|
59
|
-
accent: false,
|
|
60
|
-
background: false,
|
|
61
|
-
hideBackgroundColor: false,
|
|
62
|
-
borderColorTheme: false
|
|
63
|
-
}
|
|
64
|
-
newVisualizationConfig.showEditorPanel = true
|
|
65
49
|
newVisualizationConfig.visualizationType = type
|
|
66
|
-
|
|
67
50
|
break
|
|
68
51
|
case 'dashboardFilters': {
|
|
69
52
|
newVisualizationConfig.sharedFilterIndexes = []
|
|
@@ -58,8 +58,7 @@ export const addValuesToDashboardFilters = (
|
|
|
58
58
|
const active: string[] = Array.isArray(filterCopy.active) ? filterCopy.active : [filterCopy.active]
|
|
59
59
|
filterCopy.active = active.filter(val => defaultValues.includes(val))
|
|
60
60
|
} else {
|
|
61
|
-
//
|
|
62
|
-
// OR if defaultValue exists, always use it (overrides stale active from saved config)
|
|
61
|
+
// Use defaultValue if set, otherwise keep existing active or use first value
|
|
63
62
|
if (filterCopy.defaultValue) {
|
|
64
63
|
filterCopy.active = filterCopy.defaultValue
|
|
65
64
|
} else if (!filterCopy.active) {
|
|
@@ -82,17 +81,24 @@ export const addValuesToDashboardFilters = (
|
|
|
82
81
|
}
|
|
83
82
|
const queryStringFilterValue = getQueryStringFilterValue(subGroupingFilter)
|
|
84
83
|
const groupActive = groupName || filterCopy.values[0]
|
|
85
|
-
const
|
|
84
|
+
const currentGroupValues = filterCopy.subGrouping.valuesLookup[groupActive as string]?.values || []
|
|
85
|
+
const defaultSubValue = currentGroupValues[0]
|
|
86
86
|
|
|
87
|
-
// Priority order: query string >
|
|
88
|
-
let activeValue
|
|
87
|
+
// Priority order: query string > configured default > existing active > first available value
|
|
88
|
+
let activeValue: string | undefined
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
if (queryStringFilterValue && currentGroupValues.includes(queryStringFilterValue)) {
|
|
91
|
+
// 1. Query string parameter takes highest priority (only if valid for the current group)
|
|
92
|
+
activeValue = queryStringFilterValue
|
|
93
|
+
} else if (
|
|
94
|
+
filterCopy.subGrouping.defaultValue &&
|
|
95
|
+
currentGroupValues.includes(filterCopy.subGrouping.defaultValue)
|
|
96
|
+
) {
|
|
97
|
+
// 2. Use configured defaultValue if it exists and is valid for the current group
|
|
98
|
+
activeValue = filterCopy.subGrouping.defaultValue
|
|
99
|
+
} else if (filterCopy.subGrouping.active && currentGroupValues.includes(filterCopy.subGrouping.active)) {
|
|
100
|
+
// 3. Keep existing active value if it's valid for the current group
|
|
101
|
+
activeValue = filterCopy.subGrouping.active
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
filterCopy.subGrouping.active = activeValue || defaultSubValue
|
|
@@ -119,35 +119,31 @@ export const getToFetch = (
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
export const setActiveNestedDropdown = (dropdownOptions, sharedFilter) => {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
}
|
|
122
|
+
const queryValue = getQueryParam(sharedFilter?.setByQueryParameter)
|
|
123
|
+
const subQueryValue = getQueryParam(sharedFilter?.subGrouping?.setByQueryParameter)
|
|
124
|
+
|
|
125
|
+
// Priority: query string > configured defaultValue > existing active (if valid) > first option
|
|
126
|
+
// Note: use loose equality here to match values across possible string/number differences
|
|
127
|
+
const validActive = dropdownOptions.find(option => option.value == sharedFilter.active)
|
|
128
|
+
sharedFilter.active =
|
|
129
|
+
queryValue || sharedFilter.defaultValue || (validActive ? sharedFilter.active : dropdownOptions[0]?.value)
|
|
130
|
+
|
|
131
|
+
const options = dropdownOptions.find(option => option.value == sharedFilter.active)?.subOptions || []
|
|
132
|
+
const validSubActive = options.find(o => o.value == sharedFilter.subGrouping?.active)
|
|
133
|
+
sharedFilter.subGrouping.active =
|
|
134
|
+
subQueryValue ||
|
|
135
|
+
sharedFilter.subGrouping?.defaultValue ||
|
|
136
|
+
(validSubActive ? sharedFilter.subGrouping.active : options[0]?.value)
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
export const setActiveMultiDropdown = (dropdownOptions, sharedFilter) => {
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
: defaultQueryParamValue?.split(',')
|
|
146
|
-
const multiDefaultValue = defaultQueryParamValue ? multiDefaultQueryParamValue : [dropdownOptions[0]?.value]
|
|
140
|
+
const queryValue = getQueryParam(sharedFilter?.setByQueryParameter)
|
|
141
|
+
const queryValues = Array.isArray(queryValue) ? queryValue : queryValue?.split(',')
|
|
142
|
+
const defaultValues = queryValue ? queryValues : [dropdownOptions[0]?.value]
|
|
147
143
|
const currentOption = (Array.isArray(sharedFilter.active) ? sharedFilter.active : []).filter(activeVal =>
|
|
148
144
|
dropdownOptions.find(option => option.value === activeVal)
|
|
149
145
|
)
|
|
150
|
-
sharedFilter.active = currentOption.length ? currentOption :
|
|
146
|
+
sharedFilter.active = currentOption.length ? currentOption : defaultValues
|
|
151
147
|
}
|
|
152
148
|
|
|
153
149
|
export const setAutoLoadDefaultValue = (
|
|
@@ -158,20 +154,20 @@ export const setAutoLoadDefaultValue = (
|
|
|
158
154
|
): SharedFilter => {
|
|
159
155
|
const sharedFiltersCopy = _.cloneDeep(sharedFilters)
|
|
160
156
|
const sharedFilter = _.cloneDeep(sharedFiltersCopy[sharedFilterIndex])
|
|
161
|
-
const
|
|
162
|
-
const
|
|
157
|
+
const queryValue = getQueryParam(sharedFilter?.setByQueryParameter)
|
|
158
|
+
const hasQuery = sharedFilter.setByQueryParameter ? queryValue !== undefined : false
|
|
163
159
|
if (!autoLoadFilterIndexes.length || !dropdownOptions?.length) {
|
|
164
|
-
if (
|
|
160
|
+
if (hasQuery && sharedFilter.apiFilter) {
|
|
165
161
|
const subQueryValue = getQueryParam(sharedFilter.subGrouping?.setByQueryParameter)
|
|
166
|
-
const
|
|
167
|
-
sharedFilter.queuedActive =
|
|
162
|
+
const isNested = subQueryValue !== undefined
|
|
163
|
+
sharedFilter.queuedActive = isNested ? [queryValue, subQueryValue] : queryValue
|
|
168
164
|
}
|
|
169
165
|
return sharedFilter // no autoLoading happening
|
|
170
166
|
}
|
|
171
|
-
if (autoLoadFilterIndexes.includes(sharedFilterIndex) ||
|
|
167
|
+
if (autoLoadFilterIndexes.includes(sharedFilterIndex) || hasQuery) {
|
|
172
168
|
const filterParents = sharedFiltersCopy.filter(f => sharedFilter.parents?.includes(f.key))
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
169
|
+
const missingParents = filterParents.some(p => !(p.active || p.queuedActive))
|
|
170
|
+
if (missingParents) return sharedFilter
|
|
175
171
|
if (sharedFilter.filterStyle === FILTER_STYLE.multiSelect) {
|
|
176
172
|
setActiveMultiDropdown(dropdownOptions, sharedFilter)
|
|
177
173
|
} else if (sharedFilter.filterStyle === FILTER_STYLE.nestedDropdown) {
|
|
@@ -179,7 +175,7 @@ export const setAutoLoadDefaultValue = (
|
|
|
179
175
|
} else {
|
|
180
176
|
const defaultValue = dropdownOptions[0]?.value
|
|
181
177
|
if (!sharedFilter.active) {
|
|
182
|
-
sharedFilter.active =
|
|
178
|
+
sharedFilter.active = queryValue ?? defaultValue
|
|
183
179
|
} else {
|
|
184
180
|
const currentOption = dropdownOptions.find(option => option.value == sharedFilter.active) // loose equality required: 2017 should equal '2017'
|
|
185
181
|
sharedFilter.active = currentOption ? currentOption.value : defaultValue
|