@cdc/dashboard 4.26.2 → 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-vr9HZwRt.es.js +6 -0
- package/dist/cdcdashboard.js +53345 -49681
- package/examples/custom/css/respiratory.css +1 -1
- package/examples/data/data-with-metadata.json +18 -0
- package/examples/default.json +7 -36
- package/examples/private/inline-markup.json +775 -0
- package/examples/private/recent-update.json +1456 -0
- package/examples/private/toggle.json +10137 -0
- package/package.json +9 -9
- package/src/CdcDashboard.tsx +2 -1
- package/src/CdcDashboardComponent.tsx +47 -27
- package/src/_stories/Dashboard.DataSetup.stories.tsx +6 -1
- package/src/_stories/Dashboard.Pages.stories.tsx +22 -0
- package/src/_stories/Dashboard.stories.tsx +4406 -7
- package/src/_stories/_mock/tab-simple-filter.json +153 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +19 -3
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +7 -4
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +1 -1
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +1 -2
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +8 -7
- 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 +12 -4
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +1 -54
- 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/addVisualization.ts +71 -0
- 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/addVisualization.test.ts +52 -0
- package/src/helpers/tests/formatConfigBeforeSave.test.ts +81 -1
- package/src/scss/editor-panel.scss +1 -1
- package/src/scss/main.scss +164 -39
- 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 +2 -2
- package/dist/cdcdashboard-Cf9_fbQf.es.js +0 -6
|
@@ -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
|
+
}
|
|
@@ -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'
|
|
@@ -55,7 +55,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
55
55
|
const [isNestedDragHovered, setIsNestedDragHovered] = useState(false)
|
|
56
56
|
|
|
57
57
|
const updateFilterProp = (prop: string, index: number, value) => {
|
|
58
|
-
const newSharedFilters =
|
|
58
|
+
const newSharedFilters = cloneDeep(sharedFilters)
|
|
59
59
|
const {
|
|
60
60
|
apiEndpoint: oldEndpoint,
|
|
61
61
|
valueSelector: oldValueSelector,
|
|
@@ -102,6 +102,9 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
102
102
|
// changing a api filter and want to load the api data into the preview.
|
|
103
103
|
// automatically dispatches SET_SHARED_FILTERS
|
|
104
104
|
loadAPIFilters(newSharedFilters, {})
|
|
105
|
+
} else if (prop === 'defaultValue') {
|
|
106
|
+
newSharedFilters[index].active = value
|
|
107
|
+
dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
|
|
105
108
|
} else {
|
|
106
109
|
handleSorting(newSharedFilters[index])
|
|
107
110
|
dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
|
|
@@ -109,7 +112,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
const toggleNestedQueryParameters = (index, checked: boolean) => {
|
|
112
|
-
const newSharedFilters =
|
|
115
|
+
const newSharedFilters = cloneDeep(sharedFilters)
|
|
113
116
|
const filter = newSharedFilters[index]
|
|
114
117
|
const isUrlFilter = filter.type === 'urlfilter'
|
|
115
118
|
const groupColumnName = isUrlFilter ? filter.apiFilter.valueSelector : filter.columnName
|
|
@@ -149,7 +152,7 @@ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConf
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
const addNewFilter = () => {
|
|
152
|
-
const _sharedFilters =
|
|
155
|
+
const _sharedFilters = cloneDeep(sharedFilters) || []
|
|
153
156
|
const columnName = 'New Dashboard Filter ' + (_sharedFilters.length + 1)
|
|
154
157
|
const newFilter = { key: columnName, showDropdown: true, values: [] } as SharedFilter
|
|
155
158
|
dispatch({ type: 'SET_SHARED_FILTERS', payload: [..._sharedFilters, newFilter] })
|
|
@@ -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'
|
|
@@ -105,7 +104,7 @@ const FilterEditor: React.FC<FilterEditorProps> = ({
|
|
|
105
104
|
let _dataSet = config.datasets[dataKey]
|
|
106
105
|
if (!_dataSet.data && _dataSet.dataUrl) {
|
|
107
106
|
setDataFiltersLoading(true)
|
|
108
|
-
let data = await fetchRemoteData(_dataSet.dataUrl)
|
|
107
|
+
let data = (await fetchRemoteData(_dataSet.dataUrl)).data
|
|
109
108
|
if (_dataSet.dataDescription && data && data.length > 0) {
|
|
110
109
|
try {
|
|
111
110
|
data = transform.autoStandardize(data)
|
|
@@ -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'
|
|
@@ -72,7 +73,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
72
73
|
const order = subGrouping?.order || 'asc'
|
|
73
74
|
|
|
74
75
|
const valuesLookup = filter.values.reduce((acc, groupName) => {
|
|
75
|
-
const rawValues: string[] =
|
|
76
|
+
const rawValues: string[] = uniq(
|
|
76
77
|
config.datasets[selectedOptionDatasetName].data
|
|
77
78
|
.map(d => {
|
|
78
79
|
return d[filter.columnName] === groupName ? d[newColumnName] : ''
|
|
@@ -104,7 +105,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
104
105
|
// Handle group order change (asc/desc/cust)
|
|
105
106
|
const handleGroupingOrderBy = (order: OrderBy) => {
|
|
106
107
|
const groupSortObject = {
|
|
107
|
-
values:
|
|
108
|
+
values: cloneDeep(filter.values),
|
|
108
109
|
order
|
|
109
110
|
}
|
|
110
111
|
const { values: newOrderedValues } = handleSorting(groupSortObject)
|
|
@@ -128,7 +129,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
128
129
|
const handleGroupingCustomOrder = (sourceIndex: number, destinationIndex: number) => {
|
|
129
130
|
if (sourceIndex === undefined || destinationIndex === undefined || sourceIndex === destinationIndex) return
|
|
130
131
|
|
|
131
|
-
const orderedValues =
|
|
132
|
+
const orderedValues = cloneDeep(filter.orderedValues || filter.values)
|
|
132
133
|
const [movedItem] = orderedValues.splice(sourceIndex, 1)
|
|
133
134
|
orderedValues.splice(destinationIndex, 0, movedItem)
|
|
134
135
|
|
|
@@ -143,7 +144,7 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
143
144
|
const handleSubGroupingOrderBy = (order: OrderBy) => {
|
|
144
145
|
const newValuesLookup = Object.keys(subGrouping.valuesLookup).reduce((acc, groupName) => {
|
|
145
146
|
const subGroup = subGrouping.valuesLookup[groupName]
|
|
146
|
-
const { values: sortedValues } = handleSorting({ values:
|
|
147
|
+
const { values: sortedValues } = handleSorting({ values: cloneDeep(subGroup.values), order })
|
|
147
148
|
|
|
148
149
|
acc[groupName] = {
|
|
149
150
|
values: sortedValues,
|
|
@@ -170,11 +171,11 @@ const NestedDropDownDashboard: React.FC<NestedDropDownEditorDashboardProps> = ({
|
|
|
170
171
|
) => {
|
|
171
172
|
if (sourceIndex === undefined || destinationIndex === undefined || sourceIndex === destinationIndex) return
|
|
172
173
|
|
|
173
|
-
const updatedGroupOrderedValues =
|
|
174
|
+
const updatedGroupOrderedValues = cloneDeep(currentOrderedValues)
|
|
174
175
|
const [movedItem] = updatedGroupOrderedValues.splice(sourceIndex, 1)
|
|
175
176
|
updatedGroupOrderedValues.splice(destinationIndex, 0, movedItem)
|
|
176
177
|
|
|
177
|
-
const newSubGrouping =
|
|
178
|
+
const newSubGrouping = cloneDeep(subGrouping)
|
|
178
179
|
newSubGrouping.valuesLookup[groupName].values = updatedGroupOrderedValues
|
|
179
180
|
newSubGrouping.valuesLookup[groupName].orderedValues = updatedGroupOrderedValues
|
|
180
181
|
newSubGrouping.order = 'cust'
|
|
@@ -7,7 +7,7 @@ import { FilterBehavior } from '../../helpers/FilterBehavior'
|
|
|
7
7
|
import { getFilteredData } from '../../helpers/getFilteredData'
|
|
8
8
|
import { DashboardFilters } from '../../types/DashboardFilters'
|
|
9
9
|
import { getQueryParams, updateQueryString } from '@cdc/core/helpers/queryStringUtils'
|
|
10
|
-
import
|
|
10
|
+
import { VisualizationWrapper, Sidebar, Responsive } from '@cdc/core/components/Layout'
|
|
11
11
|
import DashboardFiltersEditor from './DashboardFiltersEditor'
|
|
12
12
|
import { ViewPort } from '@cdc/core/types/ViewPort'
|
|
13
13
|
import { hasDashboardApplyBehavior } from '../../helpers/hasDashboardApplyBehavior'
|
|
@@ -302,24 +302,24 @@ const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({
|
|
|
302
302
|
const displayNone = filters?.length ? filters.every(filter => filter.showDropdown === false) : false
|
|
303
303
|
if (displayNone && !isEditor) return <></>
|
|
304
304
|
return (
|
|
305
|
-
<
|
|
305
|
+
<VisualizationWrapper config={visualizationConfig} isEditor={isEditor} currentViewport={currentViewport}>
|
|
306
306
|
{isEditor && (
|
|
307
|
-
<
|
|
307
|
+
<Sidebar
|
|
308
308
|
displayPanel={displayPanel}
|
|
309
309
|
isDashboard={true}
|
|
310
310
|
title={'Configure Dashboard Filters'}
|
|
311
311
|
onBackClick={onBackClick}
|
|
312
312
|
>
|
|
313
313
|
<DashboardFiltersEditor updateConfig={updateConfig} vizConfig={visualizationConfig} />
|
|
314
|
-
</
|
|
314
|
+
</Sidebar>
|
|
315
315
|
)}
|
|
316
316
|
|
|
317
317
|
{!displayNone && (
|
|
318
|
-
<
|
|
318
|
+
<Responsive isEditor={isEditor}>
|
|
319
319
|
<div
|
|
320
320
|
className={`${
|
|
321
321
|
isEditor ? ' is-editor' : ''
|
|
322
|
-
} cove-
|
|
322
|
+
} cove-visualization__inner cove-visualization__body col-12 cove-dashboard-filters-container`}
|
|
323
323
|
>
|
|
324
324
|
<Filters
|
|
325
325
|
show={visualizationConfig?.sharedFilterIndexes?.map(Number)}
|
|
@@ -337,9 +337,9 @@ const DashboardFiltersWrapper: React.FC<DashboardFiltersProps> = ({
|
|
|
337
337
|
}
|
|
338
338
|
/>
|
|
339
339
|
</div>
|
|
340
|
-
</
|
|
340
|
+
</Responsive>
|
|
341
341
|
)}
|
|
342
|
-
</
|
|
342
|
+
</VisualizationWrapper>
|
|
343
343
|
)
|
|
344
344
|
}
|
|
345
345
|
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
font-weight: 700;
|
|
5
5
|
}
|
|
6
6
|
.btn {
|
|
7
|
+
align-self: flex-end;
|
|
7
8
|
/* this is the height that is defined for the .form-control class in _forms.scss in bootstrap. */
|
|
8
9
|
height: calc(1.5em + 0.75rem + 2px);
|
|
9
|
-
align-self: flex-end;
|
|
10
10
|
}
|
|
11
11
|
.loading-filter {
|
|
12
12
|
position: relative;
|
|
13
13
|
.spinner-border {
|
|
14
|
+
height: 1.5rem;
|
|
14
15
|
position: absolute;
|
|
15
|
-
top: 55%;
|
|
16
16
|
right: 10%;
|
|
17
|
+
top: 55%;
|
|
17
18
|
width: 1.5rem;
|
|
18
|
-
height: 1.5rem;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
:is(select):disabled {
|
|
@@ -59,8 +59,8 @@ export const DataDesignerModal: React.FC<DataDesignerModalProps> = ({ vizKey, ro
|
|
|
59
59
|
if (dataSetChanged || noCachedData) {
|
|
60
60
|
setLoadingAPIData(true)
|
|
61
61
|
try {
|
|
62
|
-
|
|
63
|
-
newData = transform.autoStandardize(
|
|
62
|
+
const result = await fetchRemoteData(dataUrl)
|
|
63
|
+
newData = transform.autoStandardize(result.data)
|
|
64
64
|
} catch (e) {
|
|
65
65
|
setErrorMessage('There was an issue loading the data source. Please check the datasource URL and try again.')
|
|
66
66
|
}
|
|
@@ -176,7 +176,6 @@ const Header = (props: HeaderProps) => {
|
|
|
176
176
|
Show Data Table(s)
|
|
177
177
|
</label>
|
|
178
178
|
<br />
|
|
179
|
-
|
|
180
179
|
<label>
|
|
181
180
|
<input
|
|
182
181
|
type='checkbox'
|
|
@@ -185,7 +184,6 @@ const Header = (props: HeaderProps) => {
|
|
|
185
184
|
/>
|
|
186
185
|
Expanded by Default
|
|
187
186
|
</label>
|
|
188
|
-
<br />
|
|
189
187
|
</div>
|
|
190
188
|
|
|
191
189
|
<div className='wrap'>
|
|
@@ -206,9 +204,6 @@ const Header = (props: HeaderProps) => {
|
|
|
206
204
|
onChange={e => changeConfigValue('table', 'height', e.target.value)}
|
|
207
205
|
/>
|
|
208
206
|
)}
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
<div className='wrap'>
|
|
212
207
|
<label>
|
|
213
208
|
<input
|
|
214
209
|
type='checkbox'
|
|
@@ -217,6 +212,17 @@ const Header = (props: HeaderProps) => {
|
|
|
217
212
|
/>
|
|
218
213
|
Show Download CSV Link
|
|
219
214
|
</label>
|
|
215
|
+
{config.table.download && (
|
|
216
|
+
<input
|
|
217
|
+
type='text'
|
|
218
|
+
placeholder='Customize label'
|
|
219
|
+
defaultValue={config.table.downloadDataLabel}
|
|
220
|
+
onChange={e => changeConfigValue('table', 'downloadDataLabel', e.target.value)}
|
|
221
|
+
/>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<div className='wrap'>
|
|
220
226
|
<label>
|
|
221
227
|
<input
|
|
222
228
|
type='checkbox'
|
|
@@ -225,6 +231,22 @@ const Header = (props: HeaderProps) => {
|
|
|
225
231
|
/>
|
|
226
232
|
Show URL to Automatically Updated Data
|
|
227
233
|
</label>
|
|
234
|
+
<label>
|
|
235
|
+
<input
|
|
236
|
+
type='checkbox'
|
|
237
|
+
defaultChecked={config.table.downloadImageButton}
|
|
238
|
+
onChange={e => changeConfigValue('table', 'downloadImageButton', e.target.checked)}
|
|
239
|
+
/>
|
|
240
|
+
Show Download Image Button
|
|
241
|
+
</label>
|
|
242
|
+
{config.table.downloadImageButton && (
|
|
243
|
+
<input
|
|
244
|
+
type='text'
|
|
245
|
+
placeholder='Customize label'
|
|
246
|
+
defaultValue={config.table.downloadImageLabel}
|
|
247
|
+
onChange={e => changeConfigValue('table', 'downloadImageLabel', e.target.value)}
|
|
248
|
+
/>
|
|
249
|
+
)}
|
|
228
250
|
</div>
|
|
229
251
|
</>
|
|
230
252
|
)}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
.multi-config-tabs {
|
|
2
2
|
.nav-link {
|
|
3
|
+
border-color: var(--lightGray);
|
|
3
4
|
border-radius: 6px 6px 0 0;
|
|
4
|
-
|
|
5
|
+
color: var(--primary);
|
|
5
6
|
display: block;
|
|
7
|
+
font-weight: 400;
|
|
6
8
|
padding: 0.5rem 1rem;
|
|
7
9
|
@media (max-width: 480px) {
|
|
8
10
|
padding: 0.5rem 0.8rem;
|
|
9
11
|
}
|
|
10
|
-
color: var(--primary);
|
|
11
|
-
border-color: var(--lightGray);
|
|
12
12
|
:is(button) {
|
|
13
|
-
display: none;
|
|
14
13
|
background: none;
|
|
14
|
+
display: none;
|
|
15
15
|
}
|
|
16
16
|
&:hover {
|
|
17
17
|
:is(button) {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
font-weight: bold;
|
|
32
32
|
}
|
|
33
33
|
.btn-danger {
|
|
34
|
-
text-decoration: none;
|
|
35
|
-
padding: 0px 5px;
|
|
36
34
|
font-size: inherit;
|
|
35
|
+
padding: 0px 5px;
|
|
36
|
+
text-decoration: none;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
.add {
|
package/src/components/Row.tsx
CHANGED
|
@@ -70,6 +70,12 @@ const RowMenu: React.FC<RowMenuProps> = ({ rowIdx }) => {
|
|
|
70
70
|
updateConfig({ ...config, rows: newRows })
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
const toggleEqualHeight = () => {
|
|
74
|
+
const newRows = _.cloneDeep(rows)
|
|
75
|
+
newRows[rowIdx].equalHeight = !newRows[rowIdx].equalHeight
|
|
76
|
+
updateConfig({ ...config, rows: newRows })
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
const moveRow = (dir = 'down') => {
|
|
74
80
|
if (rowIdx === rows.length - 1 && dir === 'down') return
|
|
75
81
|
|
|
@@ -179,9 +185,24 @@ const RowMenu: React.FC<RowMenuProps> = ({ rowIdx }) => {
|
|
|
179
185
|
</li>
|
|
180
186
|
]
|
|
181
187
|
|
|
188
|
+
const isMultiColumn = curr !== '12' && curr !== 'toggle'
|
|
189
|
+
|
|
182
190
|
return (
|
|
183
191
|
<nav className='row-menu'>
|
|
184
192
|
<ul className='row-menu__flyout'>{layoutList}</ul>
|
|
193
|
+
{isMultiColumn && (
|
|
194
|
+
<button
|
|
195
|
+
className={`btn row-menu__btn border-0${row.equalHeight ? ' btn-primary' : ''}`}
|
|
196
|
+
title={row.equalHeight ? 'Disable Equal Height Rows' : 'Enable Equal Height Rows'}
|
|
197
|
+
onClick={toggleEqualHeight}
|
|
198
|
+
>
|
|
199
|
+
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='25' height='20' fill='#fff'>
|
|
200
|
+
<rect x='1' y='2' width='9' height='14' rx='1' />
|
|
201
|
+
<rect x='14' y='2' width='9' height='14' rx='1' />
|
|
202
|
+
<line x1='0' y1='19' x2='24' y2='19' stroke='#fff' strokeWidth='2' strokeDasharray='3 2' />
|
|
203
|
+
</svg>
|
|
204
|
+
</button>
|
|
205
|
+
)}
|
|
185
206
|
<div className='spacer'></div>
|
|
186
207
|
<button
|
|
187
208
|
className={`btn btn-primary row-menu__btn border-0`}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
.
|
|
1
|
+
.cove-visualization {
|
|
2
2
|
--border: 1px solid var(--lightGray);
|
|
3
3
|
.toggle-component {
|
|
4
4
|
display: flex;
|
|
5
5
|
justify-content: right;
|
|
6
|
-
width: 100%;
|
|
7
6
|
margin-bottom: 15px;
|
|
7
|
+
width: 100%;
|
|
8
8
|
:first-child:is(div) {
|
|
9
9
|
border: var(--border);
|
|
10
10
|
border-radius: 5px 0 0 5px;
|
|
@@ -14,18 +14,18 @@
|
|
|
14
14
|
border-radius: 0 5px 5px 0;
|
|
15
15
|
}
|
|
16
16
|
:is(div) {
|
|
17
|
-
|
|
17
|
+
background-color: var(--white);
|
|
18
18
|
border-bottom: var(--border);
|
|
19
|
-
|
|
19
|
+
border-top: var(--border);
|
|
20
|
+
color: var(--primary);
|
|
21
|
+
cursor: pointer;
|
|
20
22
|
display: inline;
|
|
21
23
|
float: right;
|
|
22
|
-
|
|
24
|
+
padding: 7px 15px;
|
|
23
25
|
&.selected {
|
|
24
26
|
background-color: var(--primary);
|
|
25
27
|
color: white;
|
|
26
28
|
}
|
|
27
|
-
background-color: var(--white);
|
|
28
|
-
color: var(--primary);
|
|
29
29
|
:is(svg) {
|
|
30
30
|
height: 25px;
|
|
31
31
|
}
|