@cdc/dashboard 4.26.1 → 4.26.3
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-8NmHlKRI.es.js +15 -0
- package/dist/cdcdashboard-BPoPzKPz.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-vr9HZwRt.es.js +6 -0
- package/dist/cdcdashboard.js +80971 -83096
- package/examples/custom/css/respiratory.css +1 -1
- package/examples/data/data-with-metadata.json +18 -0
- package/examples/default.json +492 -132
- package/examples/nested-dropdown.json +6985 -0
- package/examples/private/abc.json +467 -0
- package/examples/private/dash.json +12696 -0
- package/examples/private/inline-markup.json +775 -0
- package/examples/private/npcr.json +1 -0
- package/examples/private/recent-update.json +1456 -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/private/toggle.json +10137 -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/CdcDashboard.tsx +2 -1
- package/src/CdcDashboardComponent.tsx +47 -30
- package/src/_stories/Dashboard.DataSetup.stories.tsx +8 -2
- package/src/_stories/Dashboard.Pages.stories.tsx +22 -0
- package/src/_stories/Dashboard.stories.tsx +4501 -80
- package/src/_stories/_mock/dashboard-line-chart-angles.json +1030 -0
- package/src/_stories/_mock/tab-simple-filter.json +153 -0
- package/src/_stories/_mock/tp5-test.json +267 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +19 -3
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +10 -4
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +1 -1
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +6 -3
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +13 -8
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +8 -8
- package/src/components/DashboardFilters/_stories/DashboardFilters.stories.tsx +1 -1
- package/src/components/DashboardFilters/dashboardfilter.styles.css +3 -3
- package/src/components/DataDesignerModal.tsx +2 -2
- package/src/components/Header/Header.tsx +27 -5
- package/src/components/Header/index.scss +1 -1
- package/src/components/MultiConfigTabs/multiconfigtabs.styles.css +6 -6
- package/src/components/Row.tsx +21 -0
- package/src/components/Toggle/toggle-style.css +7 -7
- package/src/components/VisualizationRow.tsx +42 -29
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +1 -71
- package/src/components/VisualizationsPanel/visualizations-panel-styles.css +2 -2
- package/src/components/Widget/Widget.tsx +2 -2
- package/src/components/Widget/widget.styles.css +12 -12
- package/src/data/initial-state.js +1 -1
- package/src/helpers/addValuesToDashboardFilters.ts +17 -11
- package/src/helpers/addVisualization.ts +71 -0
- package/src/helpers/apiFilterHelpers.ts +28 -32
- package/src/helpers/formatConfigBeforeSave.ts +1 -1
- package/src/helpers/getVizConfig.ts +13 -3
- package/src/helpers/iconHash.tsx +45 -36
- package/src/helpers/processDataLegacy.ts +19 -14
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +141 -44
- package/src/helpers/tests/addVisualization.test.ts +52 -0
- package/src/helpers/tests/apiFilterHelpers.test.ts +523 -420
- package/src/helpers/tests/formatConfigBeforeSave.test.ts +81 -1
- package/src/scss/editor-panel.scss +1 -1
- package/src/scss/main.scss +169 -41
- package/src/store/dashboard.reducer.ts +1 -1
- package/src/test/CdcDashboard.test.jsx +2 -2
- package/src/test/CdcDashboardComponent.test.tsx +74 -0
- package/src/types/FilterStyles.ts +2 -1
- package/tests/fixtures/dashboard-config-with-metadata.json +89 -0
- 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,153 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dashboard": {
|
|
3
|
+
"theme": "theme-blue",
|
|
4
|
+
"sharedFilters": [
|
|
5
|
+
{
|
|
6
|
+
"key": "category",
|
|
7
|
+
"showDropdown": true,
|
|
8
|
+
"filterStyle": "tab-simple",
|
|
9
|
+
"values": ["Category A", "Category B", "Category C"],
|
|
10
|
+
"orderedValues": ["Category A", "Category B", "Category C"],
|
|
11
|
+
"type": "datafilter",
|
|
12
|
+
"columnName": "category",
|
|
13
|
+
"tier": 1,
|
|
14
|
+
"order": "cust",
|
|
15
|
+
"defaultValue": "Category A"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"rows": [
|
|
20
|
+
{
|
|
21
|
+
"columns": [
|
|
22
|
+
{
|
|
23
|
+
"width": 12,
|
|
24
|
+
"widget": "dashboardFilters1"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"columns": [
|
|
30
|
+
{
|
|
31
|
+
"width": 12,
|
|
32
|
+
"widget": "chart1"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"visualizations": {
|
|
38
|
+
"dashboardFilters1": {
|
|
39
|
+
"filters": [],
|
|
40
|
+
"filterBehavior": "Filter Change",
|
|
41
|
+
"newViz": true,
|
|
42
|
+
"uid": "dashboardFilters1",
|
|
43
|
+
"type": "dashboardFilters",
|
|
44
|
+
"sharedFilterIndexes": [0],
|
|
45
|
+
"visualizationType": "dashboardFilters"
|
|
46
|
+
},
|
|
47
|
+
"chart1": {
|
|
48
|
+
"filters": [],
|
|
49
|
+
"filterBehavior": "Filter Change",
|
|
50
|
+
"uid": "chart1",
|
|
51
|
+
"type": "chart",
|
|
52
|
+
"visualizationType": "Bar",
|
|
53
|
+
"title": "Sales by Category",
|
|
54
|
+
"showTitle": true,
|
|
55
|
+
"theme": "theme-blue",
|
|
56
|
+
"animate": false,
|
|
57
|
+
"dataKey": "./tab-simple-data.json",
|
|
58
|
+
"dataDescription": {
|
|
59
|
+
"horizontal": false,
|
|
60
|
+
"series": false
|
|
61
|
+
},
|
|
62
|
+
"xAxis": {
|
|
63
|
+
"dataKey": "product",
|
|
64
|
+
"type": "categorical",
|
|
65
|
+
"hideAxis": false,
|
|
66
|
+
"hideLabel": false,
|
|
67
|
+
"hideTicks": false,
|
|
68
|
+
"size": 75,
|
|
69
|
+
"tickRotation": 0,
|
|
70
|
+
"labelColor": "#1c1d1f",
|
|
71
|
+
"tickLabelColor": "#1c1d1f",
|
|
72
|
+
"tickColor": "#1c1d1f",
|
|
73
|
+
"axisPadding": 200,
|
|
74
|
+
"padding": 5,
|
|
75
|
+
"sortDates": false,
|
|
76
|
+
"anchors": []
|
|
77
|
+
},
|
|
78
|
+
"yAxis": {
|
|
79
|
+
"hideAxis": false,
|
|
80
|
+
"hideLabel": false,
|
|
81
|
+
"hideTicks": false,
|
|
82
|
+
"size": 50,
|
|
83
|
+
"gridLines": false,
|
|
84
|
+
"labelColor": "#1c1d1f",
|
|
85
|
+
"tickLabelColor": "#1c1d1f",
|
|
86
|
+
"tickColor": "#1c1d1f",
|
|
87
|
+
"anchors": [],
|
|
88
|
+
"categories": []
|
|
89
|
+
},
|
|
90
|
+
"series": [
|
|
91
|
+
{
|
|
92
|
+
"dataKey": "sales",
|
|
93
|
+
"type": "Bar",
|
|
94
|
+
"axis": "Left",
|
|
95
|
+
"tooltip": true
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"orientation": "vertical",
|
|
99
|
+
"general": {
|
|
100
|
+
"showZeroValueData": true,
|
|
101
|
+
"palette": {
|
|
102
|
+
"name": "qualitative_bold",
|
|
103
|
+
"version": "1.0"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"columns": {},
|
|
107
|
+
"legend": {
|
|
108
|
+
"hide": true
|
|
109
|
+
},
|
|
110
|
+
"table": {
|
|
111
|
+
"label": "Data Table",
|
|
112
|
+
"show": false,
|
|
113
|
+
"sharedFilterColumns": ["category"]
|
|
114
|
+
},
|
|
115
|
+
"heights": {
|
|
116
|
+
"vertical": 300,
|
|
117
|
+
"horizontal": 750
|
|
118
|
+
},
|
|
119
|
+
"visual": {
|
|
120
|
+
"border": true,
|
|
121
|
+
"accent": true,
|
|
122
|
+
"background": true
|
|
123
|
+
},
|
|
124
|
+
"tooltips": {
|
|
125
|
+
"opacity": 90,
|
|
126
|
+
"singleSeries": false
|
|
127
|
+
},
|
|
128
|
+
"barThickness": 0.35
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"datasets": {
|
|
132
|
+
"./tab-simple-data.json": {
|
|
133
|
+
"data": [
|
|
134
|
+
{ "category": "Category A", "product": "Widget", "sales": "120" },
|
|
135
|
+
{ "category": "Category A", "product": "Gadget", "sales": "85" },
|
|
136
|
+
{ "category": "Category A", "product": "Doohickey", "sales": "210" },
|
|
137
|
+
{ "category": "Category B", "product": "Widget", "sales": "95" },
|
|
138
|
+
{ "category": "Category B", "product": "Gadget", "sales": "150" },
|
|
139
|
+
{ "category": "Category B", "product": "Doohickey", "sales": "60" },
|
|
140
|
+
{ "category": "Category C", "product": "Widget", "sales": "175" },
|
|
141
|
+
{ "category": "Category C", "product": "Gadget", "sales": "110" },
|
|
142
|
+
{ "category": "Category C", "product": "Doohickey", "sales": "140" }
|
|
143
|
+
],
|
|
144
|
+
"dataFileSize": 500,
|
|
145
|
+
"dataFileName": "./tab-simple-data.json",
|
|
146
|
+
"dataFileSourceType": "file",
|
|
147
|
+
"dataFileFormat": "JSON",
|
|
148
|
+
"preview": true
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"type": "dashboard",
|
|
152
|
+
"version": "4.26.1"
|
|
153
|
+
}
|
|
@@ -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
|
+
|
|
@@ -11,6 +11,7 @@ import { MouseEventHandler } from 'react'
|
|
|
11
11
|
import Loader from '@cdc/core/components/Loader'
|
|
12
12
|
import _ from 'lodash'
|
|
13
13
|
import { DROPDOWN_STYLES } from '@cdc/core/components/Filters/components/Dropdown'
|
|
14
|
+
import Tabs from '@cdc/core/components/Filters/components/Tabs'
|
|
14
15
|
|
|
15
16
|
type DashboardFilterProps = {
|
|
16
17
|
show: number[]
|
|
@@ -63,7 +64,12 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
63
64
|
const urlFilterType = filter.type === 'urlfilter'
|
|
64
65
|
const label = stripDuplicateLabelIncrement(filter.key || '')
|
|
65
66
|
|
|
66
|
-
if (
|
|
67
|
+
if (
|
|
68
|
+
!urlFilterType &&
|
|
69
|
+
!filter.showDropdown &&
|
|
70
|
+
filter.filterStyle !== FILTER_STYLE.nestedDropdown &&
|
|
71
|
+
filter.filterStyle !== FILTER_STYLE.tabSimple
|
|
72
|
+
)
|
|
67
73
|
return <React.Fragment key={`${filter.key}-filtersection-${filterIndex}-option`} />
|
|
68
74
|
const values: JSX.Element[] = []
|
|
69
75
|
|
|
@@ -125,7 +131,10 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
125
131
|
)
|
|
126
132
|
}
|
|
127
133
|
|
|
128
|
-
const
|
|
134
|
+
const isTabSimple = filter.filterStyle === FILTER_STYLE.tabSimple
|
|
135
|
+
const formGroupClass = `form-group${isTabSimple ? '' : ' me-4'} mb-1${loading ? ' loading-filter' : ''}${
|
|
136
|
+
isTabSimple ? ' w-100' : ''
|
|
137
|
+
}`
|
|
129
138
|
return (
|
|
130
139
|
<div className={formGroupClass} key={`${filter.key}-filtersection-${filterIndex}`}>
|
|
131
140
|
{label && (
|
|
@@ -133,7 +142,14 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
|
|
|
133
142
|
{label}
|
|
134
143
|
</label>
|
|
135
144
|
)}
|
|
136
|
-
{filter.filterStyle === FILTER_STYLE.
|
|
145
|
+
{filter.filterStyle === FILTER_STYLE.tabSimple ? (
|
|
146
|
+
<Tabs
|
|
147
|
+
filter={filter}
|
|
148
|
+
index={filterIndex}
|
|
149
|
+
changeFilterActive={(index, value) => handleOnChange(index, value)}
|
|
150
|
+
loading={loading}
|
|
151
|
+
/>
|
|
152
|
+
) : filter.filterStyle === FILTER_STYLE.multiSelect ? (
|
|
137
153
|
<MultiSelect
|
|
138
154
|
label={label}
|
|
139
155
|
options={multiValues}
|
|
@@ -12,7 +12,7 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
12
12
|
import FieldSetWrapper from '@cdc/core/components/EditorPanel/FieldSetWrapper'
|
|
13
13
|
import FilterEditor from './components/FilterEditor'
|
|
14
14
|
import { DashboardContext, DashboardDispatchContext } from '../../../DashboardContext'
|
|
15
|
-
import
|
|
15
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
16
16
|
import { DashboardFilters } from '../../../types/DashboardFilters'
|
|
17
17
|
import { SharedFilter } from '../../../types/SharedFilter'
|
|
18
18
|
import { useGlobalContext } from '@cdc/core/components/GlobalContext'
|
|
@@ -52,9 +52,10 @@ 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
|
-
const newSharedFilters =
|
|
58
|
+
const newSharedFilters = cloneDeep(sharedFilters)
|
|
58
59
|
const {
|
|
59
60
|
apiEndpoint: oldEndpoint,
|
|
60
61
|
valueSelector: oldValueSelector,
|
|
@@ -101,6 +102,9 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
101
102
|
// changing a api filter and want to load the api data into the preview.
|
|
102
103
|
// automatically dispatches SET_SHARED_FILTERS
|
|
103
104
|
loadAPIFilters(newSharedFilters, {})
|
|
105
|
+
} else if (prop === 'defaultValue') {
|
|
106
|
+
newSharedFilters[index].active = value
|
|
107
|
+
dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
|
|
104
108
|
} else {
|
|
105
109
|
handleSorting(newSharedFilters[index])
|
|
106
110
|
dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
|
|
@@ -108,7 +112,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
const toggleNestedQueryParameters = (index, checked: boolean) => {
|
|
111
|
-
const newSharedFilters =
|
|
115
|
+
const newSharedFilters = cloneDeep(sharedFilters)
|
|
112
116
|
const filter = newSharedFilters[index]
|
|
113
117
|
const isUrlFilter = filter.type === 'urlfilter'
|
|
114
118
|
const groupColumnName = isUrlFilter ? filter.apiFilter.valueSelector : filter.columnName
|
|
@@ -148,7 +152,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
const addNewFilter = () => {
|
|
151
|
-
const _sharedFilters =
|
|
155
|
+
const _sharedFilters = cloneDeep(sharedFilters) || []
|
|
152
156
|
const columnName = 'New Dashboard Filter ' + (_sharedFilters.length + 1)
|
|
153
157
|
const newFilter = { key: columnName, showDropdown: true, values: [] } as SharedFilter
|
|
154
158
|
dispatch({ type: 'SET_SHARED_FILTERS', payload: [..._sharedFilters, newFilter] })
|
|
@@ -255,6 +259,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
255
259
|
key={filter.key + index}
|
|
256
260
|
draggableId={`filter-${filter.key}-${index}`}
|
|
257
261
|
index={filterIndex}
|
|
262
|
+
isDragDisabled={isNestedDragHovered}
|
|
258
263
|
>
|
|
259
264
|
{(provided, snapshot) => (
|
|
260
265
|
<div
|
|
@@ -296,6 +301,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
296
301
|
toggleNestedQueryParameters(index, checked)
|
|
297
302
|
}}
|
|
298
303
|
config={config}
|
|
304
|
+
onNestedDragAreaHover={setIsNestedDragHovered}
|
|
299
305
|
/>
|
|
300
306
|
</FieldSetWrapper>
|
|
301
307
|
</div>
|
|
@@ -16,7 +16,7 @@ const APIModal: React.FC<APIModalProps> = ({ filter, isNestedDropdown, updateAPI
|
|
|
16
16
|
const [APISubGroupValueSelector, setAPISubGroupValueSelector] = useState(filter.apiFilter?.subgroupValueSelector)
|
|
17
17
|
const [APISubGroupTextSelector, setAPISubGroupTextSelector] = useState(filter.apiFilter?.subgroupTextSelector)
|
|
18
18
|
return (
|
|
19
|
-
<fieldset className='mb-1 px-3
|
|
19
|
+
<fieldset className='mb-1 px-3 cove-visualization'>
|
|
20
20
|
<label className='d-block'>
|
|
21
21
|
<span>API Endpoint: </span>
|
|
22
22
|
<textarea
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash'
|
|
2
1
|
import { getVizRowColumnLocator } from '../../../../helpers/getVizRowColumnLocator'
|
|
3
2
|
import { Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
|
|
4
3
|
import DataTransform from '@cdc/core/helpers/DataTransform'
|
|
@@ -27,6 +26,7 @@ type FilterEditorProps = {
|
|
|
27
26
|
filterIndex: number
|
|
28
27
|
updateFilterProp: (name: keyof SharedFilter, value: any) => void
|
|
29
28
|
toggleNestedQueryParameters: (checked: boolean) => void
|
|
29
|
+
onNestedDragAreaHover?: (isHovering: boolean) => void
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
@@ -34,7 +34,8 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
34
34
|
filterIndex,
|
|
35
35
|
config,
|
|
36
36
|
updateFilterProp,
|
|
37
|
-
toggleNestedQueryParameters
|
|
37
|
+
toggleNestedQueryParameters,
|
|
38
|
+
onNestedDragAreaHover
|
|
38
39
|
}) => {
|
|
39
40
|
const [columns, setColumns] = useState<string[]>([])
|
|
40
41
|
const [dataFiltersLoading, setDataFiltersLoading] = useState(false)
|
|
@@ -103,7 +104,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
103
104
|
let _dataSet = config.datasets[dataKey]
|
|
104
105
|
if (!_dataSet.data && _dataSet.dataUrl) {
|
|
105
106
|
setDataFiltersLoading(true)
|
|
106
|
-
let data = await fetchRemoteData(_dataSet.dataUrl)
|
|
107
|
+
let data = (await fetchRemoteData(_dataSet.dataUrl)).data
|
|
107
108
|
if (_dataSet.dataDescription && data && data.length > 0) {
|
|
108
109
|
try {
|
|
109
110
|
data = transform.autoStandardize(data)
|
|
@@ -546,6 +547,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
546
547
|
values.splice(index2, 0, removed)
|
|
547
548
|
updateFilterProp('orderedValues', values)
|
|
548
549
|
}}
|
|
550
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
549
551
|
/>
|
|
550
552
|
)}
|
|
551
553
|
|
|
@@ -569,6 +571,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
569
571
|
}}
|
|
570
572
|
isDashboard={true}
|
|
571
573
|
config={config}
|
|
574
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
572
575
|
/>
|
|
573
576
|
<label>
|
|
574
577
|
<input
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DashboardConfig } from '../../../../types/DashboardConfig'
|
|
2
2
|
import { SharedFilter } from '../../../../types/SharedFilter'
|
|
3
|
-
import
|
|
3
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
4
|
+
import uniq from 'lodash/uniq'
|
|
4
5
|
import { SubGrouping, OrderBy } from '@cdc/core/types/VizFilter'
|
|
5
6
|
import { TextField, Select } from '@cdc/core/components/EditorPanel/Inputs'
|
|
6
7
|
import { handleSorting } from '@cdc/core/components/Filters/helpers/handleSorting'
|
|
@@ -12,13 +13,15 @@ type NestedDropDownEditorDashboardProps = {
|
|
|
12
13
|
filter: SharedFilter
|
|
13
14
|
isDashboard: boolean
|
|
14
15
|
updateFilterProp: Function
|
|
16
|
+
onNestedDragAreaHover?: (isHovering: boolean) => void
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
18
20
|
filter,
|
|
19
21
|
config,
|
|
20
22
|
isDashboard = false,
|
|
21
|
-
updateFilterProp
|
|
23
|
+
updateFilterProp,
|
|
24
|
+
onNestedDragAreaHover
|
|
22
25
|
}) => {
|
|
23
26
|
const subGrouping = filter?.subGrouping
|
|
24
27
|
|
|
@@ -70,7 +73,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
70
73
|
const order = subGrouping?.order || 'asc'
|
|
71
74
|
|
|
72
75
|
const valuesLookup = filter.values.reduce((acc, groupName) => {
|
|
73
|
-
const rawValues: string[] =
|
|
76
|
+
const rawValues: string[] = uniq(
|
|
74
77
|
config.datasets[selectedOptionDatasetName].data
|
|
75
78
|
.map(d => {
|
|
76
79
|
return d[filter.columnName] === groupName ? d[newColumnName] : ''
|
|
@@ -102,7 +105,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
102
105
|
// Handle group order change (asc/desc/cust)
|
|
103
106
|
const handleGroupingOrderBy = (order: OrderBy) => {
|
|
104
107
|
const groupSortObject = {
|
|
105
|
-
values:
|
|
108
|
+
values: cloneDeep(filter.values),
|
|
106
109
|
order
|
|
107
110
|
}
|
|
108
111
|
const { values: newOrderedValues } = handleSorting(groupSortObject)
|
|
@@ -126,7 +129,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
126
129
|
const handleGroupingCustomOrder = (sourceIndex: number, destinationIndex: number) => {
|
|
127
130
|
if (sourceIndex === undefined || destinationIndex === undefined || sourceIndex === destinationIndex) return
|
|
128
131
|
|
|
129
|
-
const orderedValues =
|
|
132
|
+
const orderedValues = cloneDeep(filter.orderedValues || filter.values)
|
|
130
133
|
const [movedItem] = orderedValues.splice(sourceIndex, 1)
|
|
131
134
|
orderedValues.splice(destinationIndex, 0, movedItem)
|
|
132
135
|
|
|
@@ -141,7 +144,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
141
144
|
const handleSubGroupingOrderBy = (order: OrderBy) => {
|
|
142
145
|
const newValuesLookup = Object.keys(subGrouping.valuesLookup).reduce((acc, groupName) => {
|
|
143
146
|
const subGroup = subGrouping.valuesLookup[groupName]
|
|
144
|
-
const { values: sortedValues } = handleSorting({ values:
|
|
147
|
+
const { values: sortedValues } = handleSorting({ values: cloneDeep(subGroup.values), order })
|
|
145
148
|
|
|
146
149
|
acc[groupName] = {
|
|
147
150
|
values: sortedValues,
|
|
@@ -168,11 +171,11 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
168
171
|
) => {
|
|
169
172
|
if (sourceIndex === undefined || destinationIndex === undefined || sourceIndex === destinationIndex) return
|
|
170
173
|
|
|
171
|
-
const updatedGroupOrderedValues =
|
|
174
|
+
const updatedGroupOrderedValues = cloneDeep(currentOrderedValues)
|
|
172
175
|
const [movedItem] = updatedGroupOrderedValues.splice(sourceIndex, 1)
|
|
173
176
|
updatedGroupOrderedValues.splice(destinationIndex, 0, movedItem)
|
|
174
177
|
|
|
175
|
-
const newSubGrouping =
|
|
178
|
+
const newSubGrouping = cloneDeep(subGrouping)
|
|
176
179
|
newSubGrouping.valuesLookup[groupName].values = updatedGroupOrderedValues
|
|
177
180
|
newSubGrouping.valuesLookup[groupName].orderedValues = updatedGroupOrderedValues
|
|
178
181
|
newSubGrouping.order = 'cust'
|
|
@@ -270,6 +273,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
270
273
|
<FilterOrder
|
|
271
274
|
orderedValues={filter.orderedValues || filter.values}
|
|
272
275
|
handleFilterOrder={handleGroupingCustomOrder}
|
|
276
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
273
277
|
/>
|
|
274
278
|
)}
|
|
275
279
|
</div>
|
|
@@ -298,6 +302,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
298
302
|
handleFilterOrder={(sourceIndex, destinationIndex) => {
|
|
299
303
|
handleSubGroupingCustomOrder(sourceIndex, destinationIndex, orderedSubGroupValues, groupName)
|
|
300
304
|
}}
|
|
305
|
+
onNestedDragAreaHover={onNestedDragAreaHover}
|
|
301
306
|
/>
|
|
302
307
|
</div>
|
|
303
308
|
)
|