@cdc/core 4.24.12 → 4.25.2-25
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/components/DataTable/DataTable.tsx +58 -45
- package/components/DataTable/DataTableStandAlone.tsx +3 -3
- package/components/DataTable/components/ChartHeader.tsx +26 -6
- package/components/DataTable/components/ExpandCollapse.tsx +1 -4
- package/components/DataTable/components/MapHeader.tsx +5 -1
- package/components/DataTable/data-table.css +3 -8
- package/components/DataTable/helpers/chartCellMatrix.tsx +14 -5
- package/components/DataTable/helpers/customSort.ts +2 -2
- package/components/DataTable/helpers/mapCellMatrix.tsx +83 -60
- package/components/DataTable/types/TableConfig.ts +0 -1
- package/components/EditorPanel/FootnotesEditor.tsx +49 -7
- package/components/EditorPanel/Inputs.tsx +4 -0
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -3
- package/components/Filters/Filters.tsx +95 -51
- package/components/Filters/helpers/filterWrapping.ts +43 -0
- package/components/Filters/helpers/handleSorting.ts +6 -0
- package/components/Filters/helpers/tests/handleSorting.test.ts +26 -0
- package/components/Footnotes/Footnotes.tsx +1 -1
- package/components/Layout/components/Visualization/index.tsx +18 -4
- package/components/Layout/components/Visualization/visualizations.scss +1 -1
- package/components/Legend/Legend.Gradient.tsx +1 -4
- package/components/Legend/index.tsx +1 -1
- package/components/LegendShape.tsx +2 -3
- package/components/MediaControls.jsx +32 -8
- package/components/NestedDropdown/NestedDropdown.tsx +25 -17
- package/components/NestedDropdown/nesteddropdown.styles.css +13 -7
- package/components/Table/Table.tsx +11 -11
- package/components/Table/components/Row.tsx +14 -5
- package/components/_stories/DataTable.stories.tsx +1 -2
- package/components/elements/Button.jsx +38 -19
- package/components/elements/Confirm.tsx +45 -0
- package/components/elements/Error.tsx +24 -0
- package/components/managers/DataDesigner.tsx +198 -143
- package/components/ui/Title/Title.scss +12 -5
- package/components/ui/Title/index.tsx +1 -1
- package/dist/cove-main.css +260 -723
- package/dist/cove-main.css.map +1 -1
- package/helpers/DataTransform.ts +55 -61
- package/helpers/addValuesToFilters.ts +45 -16
- package/helpers/cove/accessibility.ts +24 -0
- package/helpers/cove/fontSettings.ts +1 -1
- package/helpers/cove/number.ts +1 -7
- package/helpers/coveUpdateWorker.ts +5 -1
- package/helpers/displayDataAsText.ts +64 -0
- package/helpers/filterVizData.ts +2 -2
- package/helpers/formatConfigBeforeSave.ts +17 -2
- package/helpers/isOlderVersion.ts +20 -0
- package/helpers/isRightAlignedTableValue.js +14 -0
- package/helpers/missingRequiredSections.ts +20 -0
- package/helpers/queryStringUtils.ts +7 -0
- package/helpers/tests/addValuesToFilters.test.ts +19 -1
- package/helpers/useDataVizClasses.ts +8 -4
- package/helpers/ver/4.24.10.ts +12 -0
- package/helpers/ver/4.24.11.ts +18 -0
- package/helpers/ver/4.24.7.ts +19 -1
- package/helpers/ver/4.25.1.ts +18 -0
- package/package.json +2 -2
- package/styles/_button-section.scss +2 -5
- package/styles/_global-variables.scss +17 -7
- package/styles/_global.scss +8 -12
- package/styles/_reset.scss +4 -5
- package/styles/_typography.scss +0 -20
- package/styles/_variables.scss +0 -3
- package/styles/base.scss +44 -5
- package/styles/cove-main.scss +1 -1
- package/styles/filters.scss +65 -6
- package/styles/v2/base/_general.scss +3 -2
- package/styles/v2/components/button.scss +0 -1
- package/styles/v2/main.scss +3 -4
- package/styles/v2/themes/_color-definitions.scss +4 -4
- package/styles/v2/utils/index.scss +0 -1
- package/types/BoxPlot.ts +1 -0
- package/types/Runtime.ts +1 -0
- package/types/Table.ts +0 -1
- package/types/Version.ts +1 -1
- package/types/VizFilter.ts +3 -1
- package/styles/v2/utils/_spacers.scss +0 -31
|
@@ -3,7 +3,12 @@ import { useEffect } from 'react'
|
|
|
3
3
|
import Button from '../elements/Button'
|
|
4
4
|
import Card from '../elements/Card'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
DATA_TABLE_VERTICAL,
|
|
8
|
+
DATA_TABLE_HORIZONTAL,
|
|
9
|
+
DATA_TABLE_SINGLE_ROW,
|
|
10
|
+
DATA_TABLE_MULTI_ROW
|
|
11
|
+
} from '../../templates/dataDesignerTables'
|
|
7
12
|
import '../../styles/v2/components/data-designer.scss'
|
|
8
13
|
import { ConfigureData } from '../../types/ConfigureData'
|
|
9
14
|
|
|
@@ -45,7 +50,12 @@ const DataDesigner = (props: DataDesignerProps) => {
|
|
|
45
50
|
<div className='grid grid-gap-2 mb-4'>
|
|
46
51
|
<div className='column'>
|
|
47
52
|
<button
|
|
48
|
-
className={
|
|
53
|
+
className={
|
|
54
|
+
'cove-data-designer__button' +
|
|
55
|
+
(configureData.dataDescription && configureData.dataDescription.horizontal === false
|
|
56
|
+
? ' active'
|
|
57
|
+
: '')
|
|
58
|
+
}
|
|
49
59
|
onClick={() => {
|
|
50
60
|
updateDescriptionProp('horizontal', false)
|
|
51
61
|
}}
|
|
@@ -61,7 +71,12 @@ const DataDesigner = (props: DataDesignerProps) => {
|
|
|
61
71
|
</div>
|
|
62
72
|
<div className='column'>
|
|
63
73
|
<button
|
|
64
|
-
className={
|
|
74
|
+
className={
|
|
75
|
+
'cove-data-designer__button' +
|
|
76
|
+
(configureData.dataDescription && configureData.dataDescription.horizontal === true
|
|
77
|
+
? ' active'
|
|
78
|
+
: '')
|
|
79
|
+
}
|
|
65
80
|
onClick={() => {
|
|
66
81
|
updateDescriptionProp('horizontal', true)
|
|
67
82
|
}}
|
|
@@ -88,7 +103,7 @@ const DataDesigner = (props: DataDesignerProps) => {
|
|
|
88
103
|
<Button
|
|
89
104
|
style={{ backgroundColor: '#00345d' }}
|
|
90
105
|
hoverStyle={{ backgroundColor: '#015daa' }}
|
|
91
|
-
className='
|
|
106
|
+
className='me-1'
|
|
92
107
|
onClick={() => {
|
|
93
108
|
updateDescriptionProp('series', true)
|
|
94
109
|
}}
|
|
@@ -127,154 +142,194 @@ const DataDesigner = (props: DataDesignerProps) => {
|
|
|
127
142
|
</select>
|
|
128
143
|
</div>
|
|
129
144
|
)}
|
|
130
|
-
{configureData.dataDescription.horizontal === false &&
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<div className='
|
|
135
|
-
<div className='
|
|
136
|
-
|
|
137
|
-
className={'cove-data-designer__button' + (configureData.dataDescription.singleRow === true ? ' active' : '')}
|
|
138
|
-
onClick={() => {
|
|
139
|
-
updateDescriptionProp('singleRow', true)
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
142
|
-
<Card>
|
|
143
|
-
<strong className='cove-heading--3'>Single Row</strong>
|
|
144
|
-
<p className='mb-1'>Each row contains the data for an individual series in itself.</p>
|
|
145
|
-
{DATA_TABLE_SINGLE_ROW}
|
|
146
|
-
</Card>
|
|
147
|
-
</button>
|
|
145
|
+
{configureData.dataDescription.horizontal === false &&
|
|
146
|
+
configureData.dataDescription.series === true &&
|
|
147
|
+
hasRowSelection && (
|
|
148
|
+
<>
|
|
149
|
+
<div className='mb-2'>
|
|
150
|
+
<div className='mb-1'>
|
|
151
|
+
Are the series values in your data represented in a single row, or across multiple rows?
|
|
148
152
|
</div>
|
|
149
|
-
<div className='
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
153
|
+
<div className='grid grid-gap-2 mb-4'>
|
|
154
|
+
<div className='column'>
|
|
155
|
+
<button
|
|
156
|
+
className={
|
|
157
|
+
'cove-data-designer__button' +
|
|
158
|
+
(configureData.dataDescription.singleRow === true ? ' active' : '')
|
|
159
|
+
}
|
|
160
|
+
onClick={() => {
|
|
161
|
+
updateDescriptionProp('singleRow', true)
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
<Card>
|
|
165
|
+
<strong className='cove-heading--3'>Single Row</strong>
|
|
166
|
+
<p className='mb-1'>Each row contains the data for an individual series in itself.</p>
|
|
167
|
+
{DATA_TABLE_SINGLE_ROW}
|
|
168
|
+
</Card>
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
<div className='column'>
|
|
172
|
+
<button
|
|
173
|
+
className={
|
|
174
|
+
'cove-data-designer__button' +
|
|
175
|
+
(configureData.dataDescription.singleRow === false ? ' active' : '')
|
|
176
|
+
}
|
|
177
|
+
onClick={() => {
|
|
178
|
+
updateDescriptionProp('singleRow', false)
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
<Card>
|
|
182
|
+
<strong className='cove-heading--3'>Multiple Rows</strong>
|
|
183
|
+
<p className='mb-1'>Each series data is broken out into multiple rows.</p>
|
|
184
|
+
{DATA_TABLE_MULTI_ROW}
|
|
185
|
+
</Card>
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
162
188
|
</div>
|
|
163
189
|
</div>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
{value}
|
|
179
|
-
</option>
|
|
180
|
-
))}
|
|
181
|
-
</select>
|
|
182
|
-
</div>
|
|
183
|
-
<div className='mb-2'>
|
|
184
|
-
<div className='mb-1'>Which property in the dataset represents the values for the category/date axis or map geography?</div>
|
|
185
|
-
<select
|
|
186
|
-
onChange={e => {
|
|
187
|
-
updateDescriptionProp('xKey', e.target.value)
|
|
188
|
-
}}
|
|
189
|
-
defaultValue={configureData.dataDescription.xKey}
|
|
190
|
-
>
|
|
191
|
-
<option value=''>Choose an option</option>
|
|
192
|
-
{Object.keys(configureData.data[0]).map((value, index) => (
|
|
193
|
-
<option value={value} key={index}>
|
|
194
|
-
{value}
|
|
195
|
-
</option>
|
|
196
|
-
))}
|
|
197
|
-
</select>
|
|
198
|
-
</div>
|
|
199
|
-
<div className='mb-2'>
|
|
200
|
-
<div className='mb-1'>Which properties in the dataset represent the numeric value? (all remaining properties will be treated as filters)</div>
|
|
201
|
-
{configureData.dataDescription.valueKeysTallSupport && configureData.dataDescription.valueKeysTallSupport.length > 0 && (
|
|
202
|
-
<ul className='value-list'>
|
|
203
|
-
{configureData.dataDescription.valueKeysTallSupport.map((valueKey, index) => (
|
|
204
|
-
<li key={`value-keys-list-${index}`}>
|
|
205
|
-
{valueKey}
|
|
206
|
-
<button
|
|
207
|
-
onClick={() => {
|
|
208
|
-
let newValueKeys = configureData.dataDescription.valueKeysTallSupport
|
|
209
|
-
newValueKeys.splice(index, 1)
|
|
210
|
-
updateDescriptionProp('valueKeysTallSupport', newValueKeys)
|
|
211
|
-
}}
|
|
212
|
-
>
|
|
213
|
-
X
|
|
214
|
-
</button>
|
|
215
|
-
</li>
|
|
216
|
-
))}
|
|
217
|
-
</ul>
|
|
218
|
-
)}
|
|
219
|
-
<select
|
|
220
|
-
onChange={e => {
|
|
221
|
-
if (e.target.value && (!configureData.dataDescription.valueKeysTallSupport || configureData.dataDescription.valueKeysTallSupport.indexOf(e.target.value) === -1)) {
|
|
222
|
-
updateDescriptionProp('valueKeysTallSupport', [...(configureData.dataDescription.valueKeysTallSupport || []), e.target.value])
|
|
223
|
-
}
|
|
224
|
-
}}
|
|
225
|
-
>
|
|
226
|
-
<option value=''>Choose an option</option>
|
|
227
|
-
{Object.keys(configureData.data[0])
|
|
228
|
-
.filter(value => !configureData.dataDescription.valueKeysTallSupport || configureData.dataDescription.valueKeysTallSupport.indexOf(value) === -1)
|
|
229
|
-
.map((value, index) => (
|
|
230
|
-
<option value={value} key={`value-keys-option-${index}`}>
|
|
190
|
+
{configureData.dataDescription.singleRow === false && (
|
|
191
|
+
<>
|
|
192
|
+
<div className='mb-2'>
|
|
193
|
+
<div className='mb-1'>
|
|
194
|
+
Which property in the dataset represents which series the row is describing?
|
|
195
|
+
</div>
|
|
196
|
+
<select
|
|
197
|
+
onChange={e => {
|
|
198
|
+
updateDescriptionProp('seriesKey', e.target.value)
|
|
199
|
+
}}
|
|
200
|
+
defaultValue={configureData.dataDescription.seriesKey}
|
|
201
|
+
>
|
|
202
|
+
<option value=''>Choose an option</option>
|
|
203
|
+
{Object.keys(configureData.data[0]).map((value, index) => (
|
|
204
|
+
<option value={value} key={index}>
|
|
231
205
|
{value}
|
|
232
206
|
</option>
|
|
233
207
|
))}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
>
|
|
250
|
-
X
|
|
251
|
-
</button>
|
|
252
|
-
</li>
|
|
253
|
-
))}
|
|
254
|
-
</ul>
|
|
255
|
-
)}
|
|
256
|
-
<select
|
|
257
|
-
onChange={e => {
|
|
258
|
-
if (e.target.value) {
|
|
259
|
-
updateDescriptionProp('ignoredKeys', [...(configureData.dataDescription.ignoredKeys || []), e.target.value])
|
|
260
|
-
}
|
|
261
|
-
e.target.value = ''
|
|
262
|
-
}}
|
|
263
|
-
>
|
|
264
|
-
<option value=''>Choose an option</option>
|
|
265
|
-
{Object.keys(configureData.data[0])
|
|
266
|
-
.filter(value => !configureData.dataDescription.ignoredKeys || configureData.dataDescription.ignoredKeys.indexOf(value) === -1)
|
|
267
|
-
.map((value, index) => (
|
|
268
|
-
<option value={value} key={`ignored-keys-option-${index}`}>
|
|
208
|
+
</select>
|
|
209
|
+
</div>
|
|
210
|
+
<div className='mb-2'>
|
|
211
|
+
<div className='mb-1'>
|
|
212
|
+
Which property in the dataset represents the values for the category/date axis or map geography?
|
|
213
|
+
</div>
|
|
214
|
+
<select
|
|
215
|
+
onChange={e => {
|
|
216
|
+
updateDescriptionProp('xKey', e.target.value)
|
|
217
|
+
}}
|
|
218
|
+
defaultValue={configureData.dataDescription.xKey}
|
|
219
|
+
>
|
|
220
|
+
<option value=''>Choose an option</option>
|
|
221
|
+
{Object.keys(configureData.data[0]).map((value, index) => (
|
|
222
|
+
<option value={value} key={index}>
|
|
269
223
|
{value}
|
|
270
224
|
</option>
|
|
271
225
|
))}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
226
|
+
</select>
|
|
227
|
+
</div>
|
|
228
|
+
<div className='mb-2'>
|
|
229
|
+
<div className='mb-1'>
|
|
230
|
+
Which properties in the dataset represent the numeric value? (all remaining properties will be
|
|
231
|
+
treated as filters)
|
|
232
|
+
</div>
|
|
233
|
+
{configureData.dataDescription.valueKeysTallSupport &&
|
|
234
|
+
configureData.dataDescription.valueKeysTallSupport.length > 0 && (
|
|
235
|
+
<ul className='value-list'>
|
|
236
|
+
{configureData.dataDescription.valueKeysTallSupport.map((valueKey, index) => (
|
|
237
|
+
<li key={`value-keys-list-${index}`}>
|
|
238
|
+
{valueKey}
|
|
239
|
+
<button
|
|
240
|
+
onClick={() => {
|
|
241
|
+
let newValueKeys = configureData.dataDescription.valueKeysTallSupport
|
|
242
|
+
newValueKeys.splice(index, 1)
|
|
243
|
+
updateDescriptionProp('valueKeysTallSupport', newValueKeys)
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
X
|
|
247
|
+
</button>
|
|
248
|
+
</li>
|
|
249
|
+
))}
|
|
250
|
+
</ul>
|
|
251
|
+
)}
|
|
252
|
+
<select
|
|
253
|
+
onChange={e => {
|
|
254
|
+
if (
|
|
255
|
+
e.target.value &&
|
|
256
|
+
(!configureData.dataDescription.valueKeysTallSupport ||
|
|
257
|
+
configureData.dataDescription.valueKeysTallSupport.indexOf(e.target.value) === -1)
|
|
258
|
+
) {
|
|
259
|
+
updateDescriptionProp('valueKeysTallSupport', [
|
|
260
|
+
...(configureData.dataDescription.valueKeysTallSupport || []),
|
|
261
|
+
e.target.value
|
|
262
|
+
])
|
|
263
|
+
}
|
|
264
|
+
}}
|
|
265
|
+
>
|
|
266
|
+
<option value=''>Choose an option</option>
|
|
267
|
+
{Object.keys(configureData.data[0])
|
|
268
|
+
.filter(
|
|
269
|
+
value =>
|
|
270
|
+
!configureData.dataDescription.valueKeysTallSupport ||
|
|
271
|
+
configureData.dataDescription.valueKeysTallSupport.indexOf(value) === -1
|
|
272
|
+
)
|
|
273
|
+
.map((value, index) => (
|
|
274
|
+
<option value={value} key={`value-keys-option-${index}`}>
|
|
275
|
+
{value}
|
|
276
|
+
</option>
|
|
277
|
+
))}
|
|
278
|
+
</select>
|
|
279
|
+
</div>
|
|
280
|
+
<div className='mb-2'>
|
|
281
|
+
<div className='mb-1'>
|
|
282
|
+
(Optional) Which properties in the dataset should be ignored? (will not be used or treated as
|
|
283
|
+
filters)
|
|
284
|
+
</div>
|
|
285
|
+
{configureData.dataDescription.ignoredKeys &&
|
|
286
|
+
configureData.dataDescription.ignoredKeys.length > 0 && (
|
|
287
|
+
<ul className='value-list'>
|
|
288
|
+
{configureData.dataDescription.ignoredKeys.map((ignoredKey, index) => (
|
|
289
|
+
<li key={`value-keys-list-${index}`}>
|
|
290
|
+
{ignoredKey}
|
|
291
|
+
<button
|
|
292
|
+
onClick={() => {
|
|
293
|
+
let newIgnoredKeys = configureData.dataDescription.ignoredKeys
|
|
294
|
+
newIgnoredKeys.splice(index, 1)
|
|
295
|
+
updateDescriptionProp('ignoredKeys', newIgnoredKeys)
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
X
|
|
299
|
+
</button>
|
|
300
|
+
</li>
|
|
301
|
+
))}
|
|
302
|
+
</ul>
|
|
303
|
+
)}
|
|
304
|
+
<select
|
|
305
|
+
onChange={e => {
|
|
306
|
+
if (e.target.value) {
|
|
307
|
+
updateDescriptionProp('ignoredKeys', [
|
|
308
|
+
...(configureData.dataDescription.ignoredKeys || []),
|
|
309
|
+
e.target.value
|
|
310
|
+
])
|
|
311
|
+
}
|
|
312
|
+
e.target.value = ''
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
<option value=''>Choose an option</option>
|
|
316
|
+
{Object.keys(configureData.data[0])
|
|
317
|
+
.filter(
|
|
318
|
+
value =>
|
|
319
|
+
!configureData.dataDescription.ignoredKeys ||
|
|
320
|
+
configureData.dataDescription.ignoredKeys.indexOf(value) === -1
|
|
321
|
+
)
|
|
322
|
+
.map((value, index) => (
|
|
323
|
+
<option value={value} key={`ignored-keys-option-${index}`}>
|
|
324
|
+
{value}
|
|
325
|
+
</option>
|
|
326
|
+
))}
|
|
327
|
+
</select>
|
|
328
|
+
</div>
|
|
329
|
+
</>
|
|
330
|
+
)}
|
|
331
|
+
</>
|
|
332
|
+
)}
|
|
278
333
|
|
|
279
334
|
{config?.visualizationType === 'Forest Plot' && (
|
|
280
335
|
<>
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
.cove-component__header {
|
|
1
|
+
.cdc-open-viz-module header.cove-component__header {
|
|
2
2
|
position: relative;
|
|
3
|
-
padding:
|
|
3
|
+
padding: 1rem;
|
|
4
4
|
margin: 0;
|
|
5
5
|
color: var(--white);
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
& > sup {
|
|
8
|
+
font-size: var(--superTitle-font-size);
|
|
9
|
+
font-family: var(--app-font-secondary);
|
|
10
|
+
font-weight: 500;
|
|
11
|
+
text-transform: uppercase;
|
|
12
|
+
top: 0;
|
|
13
|
+
}
|
|
7
14
|
|
|
8
15
|
h2 {
|
|
9
|
-
font-size:
|
|
16
|
+
font-size: var(--title-font-size);
|
|
17
|
+
font-family: var(--app-font-secondary);
|
|
10
18
|
color: var(--white);
|
|
11
19
|
margin: 0;
|
|
12
20
|
}
|
|
@@ -27,7 +35,6 @@
|
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
&:not(:empty) {
|
|
30
|
-
padding: 0.6em 0.8em;
|
|
31
38
|
border-bottom-width: 4px;
|
|
32
39
|
border-bottom-style: solid;
|
|
33
40
|
}
|
|
@@ -16,7 +16,7 @@ const Title = (props: HeaderProps) => {
|
|
|
16
16
|
const { isDashboard, title, superTitle, classes = [], showTitle = true, ariaLevel = 2 } = props
|
|
17
17
|
|
|
18
18
|
// standard classes every vis should have
|
|
19
|
-
const updatedClasses = ['cove-component__header', 'component__header', ...classes]
|
|
19
|
+
const updatedClasses = ['cove-component__header', 'component__header', 'mb-3', ...classes]
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
title &&
|