@cdc/dashboard 4.25.10 → 4.25.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/{cdcdashboard-fce76882.es.js → cdcdashboard-BnB1QM5d.es.js} +6 -13
  2. package/dist/{cdcdashboard-c55ac1ea.es.js → cdcdashboard-D6CG2-Hb.es.js} +5 -12
  3. package/dist/{cdcdashboard-31a33da1.es.js → cdcdashboard-MXgURbdZ.es.js} +6 -13
  4. package/dist/{cdcdashboard-1a1724a1.es.js → cdcdashboard-dgT_1dIT.es.js} +136 -151
  5. package/dist/cdcdashboard.js +48574 -46414
  6. package/examples/api-test/categories.json +18 -0
  7. package/examples/api-test/chart-data.json +602 -0
  8. package/examples/api-test/topics.json +47 -0
  9. package/examples/api-test/years.json +22 -0
  10. package/examples/markup-axis-label.json +4167 -0
  11. package/examples/private/DEV-10538.json +407 -0
  12. package/examples/private/DEV-11405.json +39112 -0
  13. package/examples/private/big-dashboard.json +39095 -39077
  14. package/examples/private/clade-2.json +430 -0
  15. package/examples/private/delete.json +32919 -0
  16. package/examples/private/diabetes.json +546 -196
  17. package/examples/private/markup-footer/mortality-deaths-footnotes-age.csv +3 -0
  18. package/examples/private/mpox.json +38128 -0
  19. package/examples/private/reset.json +32920 -0
  20. package/examples/test-api-filter-reset.json +132 -0
  21. package/index.html +2 -2
  22. package/package.json +9 -10
  23. package/src/CdcDashboardComponent.tsx +17 -8
  24. package/src/DashboardContext.tsx +3 -1
  25. package/src/_stories/Dashboard.stories.tsx +17 -0
  26. package/src/_stories/_mock/custom-order-new-values.json +116 -0
  27. package/src/components/DashboardFilters/DashboardFilters.tsx +34 -20
  28. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +29 -12
  29. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +77 -111
  30. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +51 -51
  31. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +120 -24
  32. package/src/components/DashboardFilters/_stories/DashboardFilters.stories.tsx +62 -3
  33. package/src/components/DataDesignerModal.tsx +12 -5
  34. package/src/components/Header/Header.tsx +10 -9
  35. package/src/components/Toggle/Toggle.tsx +48 -48
  36. package/src/components/VisualizationRow.tsx +4 -3
  37. package/src/helpers/addValuesToDashboardFilters.ts +29 -4
  38. package/src/helpers/apiFilterHelpers.ts +26 -2
  39. package/src/helpers/filterData.ts +52 -7
  40. package/src/helpers/filterResetHelpers.ts +102 -0
  41. package/src/helpers/getVizConfig.ts +2 -2
  42. package/src/helpers/loadAPIFilters.ts +109 -99
  43. package/src/helpers/tests/filterResetHelpers.test.ts +532 -0
  44. package/src/index.tsx +1 -0
  45. package/src/scss/editor-panel.scss +3 -431
  46. package/src/scss/main.scss +1 -24
  47. package/src/store/errorMessage/errorMessage.reducer.ts +1 -1
  48. package/src/types/DashboardFilters.ts +9 -8
  49. package/examples/private/burden_toolkit_mortality_diabetes_attributable_deaths_data.csv +0 -14041
  50. package/examples/private/burden_toolkit_mortality_diabetes_attributable_deaths_per_100000_data.csv +0 -14041
  51. package/examples/private/burden_toolkit_mortality_qaly_data.csv +0 -18721
  52. package/examples/private/burden_toolkit_mortality_yll_data.csv +0 -18721
  53. package/src/helpers/getAutoLoadVisualization.ts +0 -11
  54. package/src/scss/mixins.scss +0 -47
  55. package/src/scss/variables.scss +0 -5
  56. /package/dist/{cdcdashboard-548642e6.es.js → cdcdashboard-Ct2SB0vL.es.js} +0 -0
@@ -0,0 +1,132 @@
1
+ {
2
+ "dashboard": {
3
+ "theme": "theme-blue",
4
+ "sharedFilters": [
5
+ {
6
+ "key": "Category",
7
+ "type": "urlfilter",
8
+ "columnName": "category",
9
+ "apiFilter": {
10
+ "apiEndpoint": "http://localhost:8080/examples/api-test/categories.json",
11
+ "valueSelector": "category",
12
+ "textSelector": "category"
13
+ },
14
+ "queryParameter": "category",
15
+ "resetLabel": "- Select Category -",
16
+ "showDropdown": true,
17
+ "filterStyle": "dropdown",
18
+ "usedBy": ["chart1"]
19
+ },
20
+ {
21
+ "key": "Topic",
22
+ "type": "urlfilter",
23
+ "columnName": "topic",
24
+ "apiFilter": {
25
+ "apiEndpoint": "http://localhost:8080/examples/api-test/topics.json",
26
+ "valueSelector": "topic",
27
+ "textSelector": "topic"
28
+ },
29
+ "queryParameter": "topic",
30
+ "resetLabel": "- Select Topic -",
31
+ "showDropdown": true,
32
+ "parents": [
33
+ "Category"
34
+ ],
35
+ "filterStyle": "dropdown",
36
+ "usedBy": ["chart1"]
37
+ },
38
+ {
39
+ "key": "Year",
40
+ "type": "urlfilter",
41
+ "columnName": "year",
42
+ "apiFilter": {
43
+ "apiEndpoint": "http://localhost:8080/examples/api-test/years.json",
44
+ "valueSelector": "year",
45
+ "textSelector": "year"
46
+ },
47
+ "queryParameter": "year",
48
+ "resetLabel": "- Select -",
49
+ "showDropdown": true,
50
+ "parents": [
51
+ "Category",
52
+ "Topic"
53
+ ],
54
+ "filterStyle": "dropdown",
55
+ "usedBy": ["chart1"]
56
+ }
57
+ ]
58
+ },
59
+ "rows": [
60
+ [
61
+ {
62
+ "width": 12,
63
+ "widget": "filters"
64
+ }
65
+ ],
66
+ [
67
+ {
68
+ "width": 12,
69
+ "widget": "header"
70
+ }
71
+ ],
72
+ [
73
+ {
74
+ "width": 12,
75
+ "widget": "chart1"
76
+ }
77
+ ]
78
+ ],
79
+ "visualizations": {
80
+ "filters": {
81
+ "type": "dashboardFilters",
82
+ "visualizationType": "dashboardFilters",
83
+ "sharedFilterIndexes": [
84
+ 0,
85
+ 1,
86
+ 2
87
+ ],
88
+ "filterBehavior": "Apply Button",
89
+ "applyFiltersButtonText": "Apply Filters"
90
+ },
91
+ "header": {
92
+ "type": "markup-include",
93
+ "title": "Test API Filter Reset",
94
+ "contentEditor": {
95
+ "inlineHTML": "<h2>API Filter Reset Test Dashboard</h2><p>This dashboard tests the 'Clear Filters' button functionality with API filters.</p><ul><li><strong>Category</strong>: Has resetLabel = '- Select Category -'</li><li><strong>Topic</strong>: Has resetLabel = '- Select Topic -' (depends on Category)</li><li><strong>Year</strong>: Has defaultValue = '2023' (depends on Category and Topic)</li></ul><h3>Testing Instructions:</h3><ol><li>Select values for all three filters</li><li>Click 'Apply Filters'</li><li>Click 'Clear Filters'</li><li>Expected behavior:<ul><li>Category and Topic should reset to '- Select Category -' and '- Select Topic -'</li><li>Year should reset to '2023' (its defaultValue)</li></ul></li></ol>",
96
+ "useInlineHTML": true
97
+ },
98
+ "theme": "theme-blue"
99
+ },
100
+ "chart1": {
101
+ "type": "chart",
102
+ "title": "Sample Chart",
103
+ "showTitle": true,
104
+ "theme": "theme-blue",
105
+ "visualizationType": "Line",
106
+ "dataKey": "chartData",
107
+ "xAxis": {
108
+ "dataKey": "state",
109
+ "type": "categorical"
110
+ },
111
+ "series": [
112
+ {
113
+ "dataKey": "value",
114
+ "type": "Line"
115
+ }
116
+ ],
117
+ "general": {
118
+ "showDownloadButton": false
119
+ },
120
+ "table": {
121
+ "showTableButton": false
122
+ }
123
+ }
124
+ },
125
+ "datasets": {
126
+ "chartData": {
127
+ "dataUrl": "http://localhost:8080/examples/api-test/chart-data.json"
128
+ }
129
+ },
130
+ "filterBehavior": "Apply Button",
131
+ "type": "dashboard"
132
+ }
package/index.html CHANGED
@@ -22,9 +22,9 @@
22
22
  </head>
23
23
 
24
24
  <body>
25
- <div class="react-container" data-config="/examples/private/oral-health.json"></div>
25
+ <div class="react-container" data-config="/examples/markup-axis-label.json"></div>
26
26
  <script src="https://www.cdc.gov/TemplatePackage/contrib/libs/jquery/latest/jquery.min.js?_=91329"></script>
27
27
  <script type="module" src="./src/index.tsx"></script>
28
28
  </body>
29
29
 
30
- </html>
30
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/dashboard",
3
- "version": "4.25.10",
3
+ "version": "4.25.11",
4
4
  "description": "React component for combining multiple visualizations into a single dashboard",
5
5
  "moduleName": "CdcDashboard",
6
6
  "main": "dist/cdcdashboard",
@@ -27,13 +27,13 @@
27
27
  },
28
28
  "license": "Apache-2.0",
29
29
  "dependencies": {
30
- "@cdc/chart": "^4.25.10",
31
- "@cdc/core": "^4.25.10",
32
- "@cdc/data-bite": "^4.25.10",
33
- "@cdc/filtered-text": "^4.25.10",
34
- "@cdc/map": "^4.25.10",
35
- "@cdc/markup-include": "^4.25.10",
36
- "@cdc/waffle-chart": "^4.25.10",
30
+ "@cdc/chart": "^4.25.11",
31
+ "@cdc/core": "^4.25.11",
32
+ "@cdc/data-bite": "^4.25.11",
33
+ "@cdc/filtered-text": "^4.25.11",
34
+ "@cdc/map": "^4.25.11",
35
+ "@cdc/markup-include": "^4.25.11",
36
+ "@cdc/waffle-chart": "^4.25.11",
37
37
  "js-base64": "^2.5.2",
38
38
  "react-accessible-accordion": "^5.0.1",
39
39
  "react-dnd": "^14.0.2",
@@ -45,11 +45,10 @@
45
45
  "react-dom": "^18.2.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@rollup/plugin-dsv": "^3.0.2",
49
48
  "@vitejs/plugin-react": "^4.3.4",
50
49
  "vite": "^4.4.11",
51
50
  "vite-plugin-css-injected-by-js": "^2.4.0",
52
51
  "vite-plugin-svgr": "^2.4.0"
53
52
  },
54
- "gitHead": "c2db758e74ab9b9ca1667a6f9cb41dd0dccf985d"
53
+ "gitHead": "5f09a137c22f454111ab5f4cd7fdf1d2d58e31bd"
55
54
  }
@@ -23,7 +23,7 @@ import { DataTransform } from '@cdc/core/helpers/DataTransform'
23
23
  import getViewport from '@cdc/core/helpers/getViewport'
24
24
 
25
25
  import Grid from './components/Grid'
26
- import Header from './components/Header/Header'
26
+ import Header from './components/Header'
27
27
  import DataTable from '@cdc/core/components/DataTable'
28
28
  import MediaControls from '@cdc/core/components/MediaControls'
29
29
 
@@ -32,7 +32,7 @@ import './scss/main.scss'
32
32
  import VisualizationsPanel from './components/VisualizationsPanel'
33
33
  import dashboardReducer from './store/dashboard.reducer'
34
34
  import errorMessagesReducer from './store/errorMessage/errorMessage.reducer'
35
- import { filterData } from './helpers/filterData'
35
+ import { filterData, isFilterAtResetState } from './helpers/filterData'
36
36
  import { getVizKeys } from './helpers/getVizKeys'
37
37
  import Title from '@cdc/core/components/ui/Title'
38
38
  import { type TableConfig } from '@cdc/core/components/DataTable/types/TableConfig'
@@ -89,10 +89,17 @@ export default function CdcDashboard({
89
89
  return true
90
90
  }
91
91
 
92
+ // Check if any filters are at their reset state (incomplete)
93
+ const sharedFilters = state.config.dashboard?.sharedFilters || []
94
+ const hasResetFilters = sharedFilters.some(isFilterAtResetState)
95
+ if (hasResetFilters) {
96
+ return true
97
+ }
98
+
92
99
  const vals = reloadURLHelpers.getDatasetKeys(state.config).map(key => state.data[key])
93
100
  if (!vals.length) return true
94
101
  return vals.some(val => val === undefined)
95
- }, [state.data, state.config.visualizations, state.filtersApplied])
102
+ }, [state.data, state.config.visualizations, state.config.dashboard?.sharedFilters, state.filtersApplied])
96
103
 
97
104
  const vizRowColumnLocator = getVizRowColumnLocator(state.config.rows)
98
105
 
@@ -165,9 +172,13 @@ export default function CdcDashboard({
165
172
  }
166
173
 
167
174
  if (filter.apiFilter && filter.active) {
168
- updatedQSParams[filter.apiFilter.valueSelector] = filter.active
169
- if (filter.apiFilter.subgroupValueSelector && filter.subGrouping.active) {
170
- updatedQSParams[filter.apiFilter.subgroupValueSelector] = filter.subGrouping.active
175
+ // Don't add filter to query params if it's set to its resetLabel
176
+ const isResetLabel = filter.resetLabel && filter.active === filter.resetLabel
177
+ if (!isResetLabel) {
178
+ updatedQSParams[filter.apiFilter.valueSelector] = filter.active
179
+ if (filter.apiFilter.subgroupValueSelector && filter.subGrouping.active) {
180
+ updatedQSParams[filter.apiFilter.subgroupValueSelector] = filter.subGrouping.active
181
+ }
171
182
  }
172
183
  }
173
184
  }
@@ -292,8 +303,6 @@ export default function CdcDashboard({
292
303
  }
293
304
 
294
305
  const setEventData = ({ detail }, data, filteredData) => {
295
- // eslint-disable-next-line no-console
296
- console.log('Event: cove_set_data', detail)
297
306
  try {
298
307
  const newDatasets = Object.keys(detail).reduce((acc, key) => {
299
308
  if (data[key] !== undefined) {
@@ -15,7 +15,9 @@ type ConfigCTX = DashboardState & {
15
15
  loadAPIFilters: (
16
16
  sharedFilters: SharedFilter[],
17
17
  dropdowns: APIFilterDropdowns,
18
- recursiveLimit?: number
18
+ loadAll?: boolean,
19
+ recursiveLimit?: number,
20
+ isStale?: () => boolean
19
21
  ) => Promise<SharedFilter[]>
20
22
  setAPIFilterDropdowns: (dropdowns: APIFilterDropdowns) => void
21
23
  setAPILoading: (loading: boolean) => void
@@ -28,6 +28,8 @@ import TopSpacing_1 from './_mock/data-bite-dash-test.json'
28
28
  import TopSpacing_2 from './_mock/data-bite-dash-test_1.json'
29
29
  import TopSpacing_3 from './_mock/data-bite-dash-test_1_1.json'
30
30
  import TopSpacing_4 from './_mock/data-bite-dash-test_1_1_1.json'
31
+ import CustomOrderNewValues from './_mock/custom-order-new-values.json'
32
+ import APIFilterResetConfig from '../../examples/test-api-filter-reset.json'
31
33
 
32
34
  // Dashboard Filter Updates for Ascending, Descending, and Custom Order
33
35
  import DashboardFilterAsc from './_mock/dashboard-filter-asc.json'
@@ -38,6 +40,7 @@ DashboardFilterCust.dashboard.sharedFilters[0].order = 'cust'
38
40
 
39
41
  // On DashboardFilterCust change the sharedFilters[0].values and orderedValues to be in a custom order
40
42
  const customOrder = ['American Samoa', 'Alaska', 'Alabama', 'Arizona', 'Arkansas']
43
+ DashboardFilterCust.dashboard.sharedFilters[0].orderedValues = customOrder
41
44
 
42
45
  const meta: Meta<typeof Dashboard> = {
43
46
  title: 'Components/Pages/Dashboard',
@@ -67,6 +70,13 @@ export const Dashboard_Filter_Cust: Story = {
67
70
  }
68
71
  }
69
72
 
73
+ export const CustomOrder_NewValues_AutoAppend: Story = {
74
+ args: {
75
+ config: CustomOrderNewValues,
76
+ isEditor: false
77
+ }
78
+ }
79
+
70
80
  export const Example_1: Story = {
71
81
  args: {
72
82
  config: ExampleConfig_1,
@@ -472,4 +482,11 @@ export const Top_Spacing_4: Story = {
472
482
  }
473
483
  }
474
484
 
485
+ export const Clear_Filters_Button: Story = {
486
+ args: {
487
+ config: APIFilterResetConfig as unknown as Config,
488
+ isEditor: false
489
+ }
490
+ }
491
+
475
492
  export default meta
@@ -0,0 +1,116 @@
1
+ {
2
+ "dashboard": {
3
+ "theme": "theme-blue",
4
+ "title": "Custom Order Filter - New Values Auto-Append Demo",
5
+ "sharedFilters": [
6
+ {
7
+ "key": "Location",
8
+ "columnName": "Location",
9
+ "showDropdown": true,
10
+ "type": "datafilter",
11
+ "order": "cust",
12
+ "orderedValues": [
13
+ "California",
14
+ "Texas",
15
+ "Florida"
16
+ ],
17
+ "usedBy": [
18
+ "data-bite-demo",
19
+ "chart-demo"
20
+ ],
21
+ "tier": 1
22
+ }
23
+ ]
24
+ },
25
+ "datasets": {
26
+ "custom-order-demo-data.csv": {
27
+ "dataUrl": "http://localhost:6006/custom-order-demo-data.csv"
28
+ }
29
+ },
30
+ "rows": [
31
+ {
32
+ "columns": [
33
+ {
34
+ "width": 12,
35
+ "widget": "legacySharedFilters"
36
+ }
37
+ ]
38
+ },
39
+ {
40
+ "columns": [
41
+ {
42
+ "width": 6,
43
+ "widget": "data-bite-demo"
44
+ },
45
+ {
46
+ "width": 6,
47
+ "widget": "chart-demo"
48
+ }
49
+ ]
50
+ }
51
+ ],
52
+ "visualizations": {
53
+ "data-bite-demo": {
54
+ "type": "data-bite",
55
+ "dataKey": "custom-order-demo-data.csv",
56
+ "dataFunction": "Sum",
57
+ "dataColumn": "Amount",
58
+ "bitePosition": "Left",
59
+ "biteFontSize": 36,
60
+ "fontSize": "large",
61
+ "biteBody": "<h3>Total Amount for Selected Location</h3><p><strong>Custom Order Demo:</strong> The filter has custom order: <em>California, Texas, Florida</em>. But the CSV data includes <strong>New York</strong> and <strong>Illinois</strong> which should auto-append to the end of the dropdown.</p><p>Select different locations from the dropdown above to see the total amount update.</p>",
62
+ "dataFormat": {
63
+ "roundToPlace": 0,
64
+ "commas": true,
65
+ "prefix": "$",
66
+ "suffix": ""
67
+ },
68
+ "biteStyle": "graphic",
69
+ "theme": "theme-blue",
70
+ "shadow": true,
71
+ "uid": "data-bite-demo",
72
+ "visualizationType": "data-bite"
73
+ },
74
+ "chart-demo": {
75
+ "type": "chart",
76
+ "title": "Amount by Year",
77
+ "theme": "theme-blue",
78
+ "fontSize": "medium",
79
+ "height": "250",
80
+ "yAxis": {
81
+ "label": "Amount",
82
+ "gridLines": true
83
+ },
84
+ "xAxis": {
85
+ "dataKey": "Year",
86
+ "label": "Year"
87
+ },
88
+ "table": {
89
+ "show": true,
90
+ "label": "Data Table"
91
+ },
92
+ "legend": {
93
+ "hide": true
94
+ },
95
+ "visualizationType": "Bar",
96
+ "series": [
97
+ {
98
+ "dataKey": "Amount",
99
+ "type": "Bar"
100
+ }
101
+ ],
102
+ "dataKey": "custom-order-demo-data.csv",
103
+ "uid": "chart-demo"
104
+ },
105
+ "legacySharedFilters": {
106
+ "type": "dashboardFilters",
107
+ "visualizationType": "dashboardFilters",
108
+ "sharedFilterIndexes": [
109
+ 0
110
+ ],
111
+ "filterBehavior": "Filter Change",
112
+ "uid": "legacySharedFilters"
113
+ }
114
+ },
115
+ "type": "dashboard"
116
+ }
@@ -18,6 +18,7 @@ type DashboardFilterProps = {
18
18
  showSubmit: boolean
19
19
  applyFilters: MouseEventHandler<HTMLButtonElement>
20
20
  applyFiltersButtonText?: string
21
+ handleReset?: MouseEventHandler<HTMLButtonElement>
21
22
  }
22
23
 
23
24
  const DashboardFilters: React.FC<DashboardFilterProps> = ({
@@ -27,7 +28,8 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
27
28
  handleOnChange,
28
29
  showSubmit,
29
30
  applyFilters,
30
- applyFiltersButtonText
31
+ applyFiltersButtonText,
32
+ handleReset
31
33
  }) => {
32
34
  const nullVal = (filter: SharedFilter) => {
33
35
  const val = filter.queuedActive || filter.active
@@ -115,8 +117,8 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
115
117
  }
116
118
 
117
119
  const isDisabled = !values.length
118
- // push reset label only if it does not includes in filter values options
119
- if (filter.resetLabel && !filter.values.includes(filter.resetLabel)) {
120
+ // push reset label only if it does not includes in filter values options
121
+ if (filter.resetLabel && !filter.values.includes(filter.resetLabel) && !_key) {
120
122
  values.unshift(
121
123
  <option key={`${filter.resetLabel}-option`} value={filter.resetLabel}>
122
124
  {filter.resetLabel}
@@ -145,9 +147,7 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
145
147
  ) : filter.filterStyle === FILTER_STYLE.nestedDropdown ? (
146
148
  <NestedDropdown
147
149
  activeGroup={(filter.queuedActive?.[0] || filter.active) as string}
148
- activeSubGroup={
149
- _key ? filter.queuedActive?.[1] || filter.subGrouping?.active : filter.subGrouping?.active
150
- }
150
+ activeSubGroup={(filter.queuedActive?.[1] || filter.subGrouping?.active) as string}
151
151
  filterIndex={filterIndex}
152
152
  options={_key ? getNestedDropdownOptions(apiFilterDropdowns[_key]) : nestedOptions}
153
153
  listLabel={label}
@@ -167,7 +167,14 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
167
167
  disabled={loading || isDisabled}
168
168
  >
169
169
  {loading && <option value='Loading...'>Loading...</option>}
170
- {nullVal(filter) && (
170
+ {/* For API filters, show placeholder when no value is selected */}
171
+ {_key && nullVal(filter) && (
172
+ <option key={`reset-label`} value=''>
173
+ {filter.resetLabel || '- Select One -'}
174
+ </option>
175
+ )}
176
+ {/* For non-API filters or when no value is selected, show empty option */}
177
+ {!_key && nullVal(filter) && (
171
178
  <option key={`select`} value=''>
172
179
  {filter.resetLabel || '- Select -'}
173
180
  </option>
@@ -181,19 +188,26 @@ const DashboardFilters: React.FC<DashboardFilterProps> = ({
181
188
  )
182
189
  })}
183
190
  {showSubmit && (
184
- <button
185
- className='btn btn-primary mb-1'
186
- onClick={applyFilters}
187
- disabled={show.some(filterIndex => {
188
- const emptyFilterValues = [undefined, '', '- Select -']
189
- return (
190
- emptyFilterValues.includes(sharedFilters[filterIndex].queuedActive) &&
191
- emptyFilterValues.includes(sharedFilters[filterIndex].active)
192
- )
193
- })}
194
- >
195
- {applyFiltersButtonText || 'GO!'}
196
- </button>
191
+ <>
192
+ <button
193
+ className='btn btn-primary mb-1 me-2'
194
+ onClick={applyFilters}
195
+ disabled={show.some(filterIndex => {
196
+ const emptyFilterValues = [undefined, '', '- Select -']
197
+ return (
198
+ emptyFilterValues.includes(sharedFilters[filterIndex].queuedActive) &&
199
+ emptyFilterValues.includes(sharedFilters[filterIndex].active)
200
+ )
201
+ })}
202
+ >
203
+ {applyFiltersButtonText || 'GO!'}
204
+ </button>
205
+ {handleReset && (
206
+ <button className='btn btn-link mb-1' onClick={handleReset}>
207
+ Clear Filters
208
+ </button>
209
+ )}
210
+ </>
197
211
  )}
198
212
  </form>
199
213
  )
@@ -43,9 +43,10 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
43
43
  return config.dashboard.sharedFilters
44
44
  ?.map<[number, string]>(({ key }, i) => [i, key])
45
45
  .filter(([filterIndex]) => !sharedFilterIndexes.includes(filterIndex)) // filter out already added filters
46
- .map(([filterIndex, filterName]) => (
47
- <option key={filterIndex} value={filterIndex}>{`${filterIndex} - ${filterName}`}</option>
48
- ))
46
+ .map(([filterIndex, filterName]) => ({
47
+ value: String(filterIndex),
48
+ label: `${filterIndex} - ${filterName}`
49
+ }))
49
50
  }, [config.visualizations, vizConfig.uid])
50
51
 
51
52
  const openControls = useState({})
@@ -189,6 +190,27 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
189
190
  }
190
191
  />
191
192
  )}
193
+ {vizConfig.filterBehavior === 'Apply Button' && (
194
+ <CheckBox
195
+ label='Show Clear Filters Button'
196
+ value={vizConfig.showClearButton ?? true}
197
+ updateField={(_section, _subsection, _key, value) => {
198
+ updateConfig({ ...vizConfig, showClearButton: value })
199
+ }}
200
+ tooltip={
201
+ <Tooltip style={{ textTransform: 'none' }}>
202
+ <Tooltip.Target>
203
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
204
+ </Tooltip.Target>
205
+ <Tooltip.Content>
206
+ <p>
207
+ When enabled, displays a "Clear Filters" button that allows users to reset all filter selections.
208
+ </p>
209
+ </Tooltip.Content>
210
+ </Tooltip>
211
+ }
212
+ />
213
+ )}
192
214
  </AccordionItemPanel>
193
215
  </AccordionItem>
194
216
 
@@ -254,8 +276,10 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
254
276
  </Tooltip.Content>
255
277
  </Tooltip>
256
278
  </span>
257
- <select
279
+ <Select
280
+ label=''
258
281
  value={''}
282
+ options={[{ value: '', label: 'Select' }, ...(existingOptions || [])]}
259
283
  onChange={e => {
260
284
  updateConfig({
261
285
  ...vizConfig,
@@ -263,14 +287,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
263
287
  })
264
288
  setCanAddExisting(false)
265
289
  }}
266
- >
267
- {[
268
- <option key='select' value=''>
269
- Select
270
- </option>,
271
- ...existingOptions
272
- ]}
273
- </select>
290
+ />
274
291
  </label>
275
292
  ) : (
276
293
  <button onClick={() => setCanAddExisting(true)} className='btn btn-primary full-width mt-2'>