@cdc/core 4.24.11 → 4.24.12-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Alert/components/Alert.tsx +9 -4
- package/components/DataTable/DataTable.tsx +19 -8
- package/components/DataTable/DataTableStandAlone.tsx +3 -3
- package/components/DataTable/components/ExpandCollapse.tsx +1 -1
- package/components/DataTable/components/SortIcon/sort-icon.css +15 -0
- package/components/DataTable/helpers/boxplotCellMatrix.tsx +6 -2
- package/components/DataTable/helpers/getChartCellValue.ts +11 -11
- package/components/EditorPanel/DataTableEditor.tsx +29 -23
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +25 -1
- package/components/Filters/Filters.tsx +31 -35
- package/components/Filters/helpers/handleSorting.ts +5 -0
- package/components/Footnotes/FootnotesStandAlone.tsx +17 -4
- package/components/Layout/components/Visualization/visualizations.scss +1 -1
- package/components/Legend/Legend.Gradient.tsx +1 -1
- package/components/Loader/Loader.tsx +10 -5
- package/components/MultiSelect/MultiSelect.tsx +84 -84
- package/components/MultiSelect/multiselect.styles.css +10 -0
- package/components/NestedDropdown/NestedDropdown.tsx +21 -18
- package/components/NestedDropdown/nesteddropdown.styles.css +11 -0
- package/components/Table/components/Row.tsx +1 -1
- package/components/inputs/{InputToggle.jsx → InputToggle.tsx} +35 -29
- package/dist/cove-main.css +6 -3
- package/dist/cove-main.css.map +1 -1
- package/helpers/addValuesToFilters.ts +22 -8
- package/helpers/coveUpdateWorker.ts +1 -1
- package/helpers/filterVizData.ts +2 -2
- package/helpers/formatConfigBeforeSave.ts +15 -0
- package/helpers/gatherQueryParams.ts +2 -3
- package/helpers/queryStringUtils.ts +10 -1
- package/helpers/tests/addValuesToFilters.test.ts +6 -1
- package/helpers/useDataVizClasses.ts +2 -1
- package/helpers/ver/4.24.10.ts +12 -0
- package/helpers/ver/versionNeedsUpdate.ts +2 -0
- package/helpers/viewports.ts +8 -7
- package/package.json +2 -2
- package/styles/_global-variables.scss +8 -3
- package/styles/_reset.scss +0 -1
- package/types/Version.ts +1 -0
- package/types/VizFilter.ts +2 -1
|
@@ -20,6 +20,8 @@ type AlertProps = {
|
|
|
20
20
|
autoDismiss?: boolean
|
|
21
21
|
// set seconds until autoDismiss, default is 5 seconds
|
|
22
22
|
secondsBeforeDismiss?: number
|
|
23
|
+
// provide option for non dismissable alert
|
|
24
|
+
showCloseButton?: boolean
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
const Alert: React.FC<AlertProps> = ({
|
|
@@ -29,7 +31,8 @@ const Alert: React.FC<AlertProps> = ({
|
|
|
29
31
|
heading,
|
|
30
32
|
onDismiss,
|
|
31
33
|
autoDismiss,
|
|
32
|
-
secondsBeforeDismiss = 5
|
|
34
|
+
secondsBeforeDismiss = 5,
|
|
35
|
+
showCloseButton = true
|
|
33
36
|
}) => {
|
|
34
37
|
// sanitize the text for setting dangerouslySetInnerHTML
|
|
35
38
|
|
|
@@ -55,9 +58,11 @@ const Alert: React.FC<AlertProps> = ({
|
|
|
55
58
|
{type === 'info' && <Icon display='info' size={iconSize} />}
|
|
56
59
|
<span dangerouslySetInnerHTML={sanitizedData()} />
|
|
57
60
|
</div>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
{showCloseButton && (
|
|
62
|
+
<button type='button' className='close pl-5' aria-label='Close' onClick={() => onDismiss()}>
|
|
63
|
+
X
|
|
64
|
+
</button>
|
|
65
|
+
)}
|
|
61
66
|
</div>
|
|
62
67
|
)
|
|
63
68
|
}
|
|
@@ -202,7 +202,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
202
202
|
: config.visualizationType === 'Pie'
|
|
203
203
|
? [config.yAxis.dataKey]
|
|
204
204
|
: config.visualizationType === 'Box Plot'
|
|
205
|
-
? Object.entries(config.boxplot.
|
|
205
|
+
? config?.boxplot?.plots?.[0] ? Object.entries(config.boxplot.plots[0]) : []
|
|
206
206
|
: config.runtime?.seriesKeys),
|
|
207
207
|
[config.runtime?.seriesKeys]) // eslint-disable-line
|
|
208
208
|
|
|
@@ -244,17 +244,28 @@ const DataTable = (props: DataTableProps) => {
|
|
|
244
244
|
</MediaControls.Section>
|
|
245
245
|
)
|
|
246
246
|
}
|
|
247
|
+
const getClassNames = (): string => {
|
|
248
|
+
const classes = ['data-table-container']
|
|
249
|
+
const isBrushActive = config?.brush?.active && config.legend?.position !== 'bottom'
|
|
250
|
+
|
|
251
|
+
if (isBrushActive) {
|
|
252
|
+
classes.push('brush-active')
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
classes.push(viewport)
|
|
256
|
+
|
|
257
|
+
const downloadLinkClass = !config.table.showDownloadLinkBelow ? 'download-link-above' : ''
|
|
258
|
+
if (downloadLinkClass) {
|
|
259
|
+
classes.push(downloadLinkClass)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return classes.join(' ')
|
|
263
|
+
}
|
|
247
264
|
|
|
248
265
|
return (
|
|
249
266
|
<ErrorBoundary component='DataTable'>
|
|
250
267
|
{!config.table.showDownloadLinkBelow && <TableMediaControls />}
|
|
251
|
-
<section
|
|
252
|
-
id={tabbingId.replace('#', '')}
|
|
253
|
-
className={`data-table-container ${viewport} ${
|
|
254
|
-
!config.table.showDownloadLinkBelow ? 'download-link-above' : ''
|
|
255
|
-
}`}
|
|
256
|
-
aria-label={accessibilityLabel}
|
|
257
|
-
>
|
|
268
|
+
<section id={tabbingId.replace('#', '')} className={getClassNames()} aria-label={accessibilityLabel}>
|
|
258
269
|
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
259
270
|
{config.table.collapsible !== false && (
|
|
260
271
|
<ExpandCollapse
|
|
@@ -6,6 +6,7 @@ import DataTableEditorPanel from './components/DataTableEditorPanel'
|
|
|
6
6
|
import Filters from '../Filters'
|
|
7
7
|
import { TableConfig } from './types/TableConfig'
|
|
8
8
|
import { filterVizData } from '../../helpers/filterVizData'
|
|
9
|
+
import { addValuesToFilters } from '../../helpers/addValuesToFilters'
|
|
9
10
|
|
|
10
11
|
type StandAloneProps = {
|
|
11
12
|
visualizationKey: string
|
|
@@ -28,9 +29,8 @@ const DataTableStandAlone: React.FC<StandAloneProps> = ({
|
|
|
28
29
|
|
|
29
30
|
useEffect(() => {
|
|
30
31
|
// when using editor changes to filter should update the data
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
32
|
+
const filters = addValuesToFilters(config.filters, config.data)
|
|
33
|
+
setFilteredData(filterVizData(filters, config?.formattedData?.length > 0 ? config.formattedData : config.data))
|
|
34
34
|
}, [config.filters])
|
|
35
35
|
|
|
36
36
|
if (isEditor)
|
|
@@ -2,7 +2,7 @@ import Icon from '../../ui/Icon'
|
|
|
2
2
|
import { fontSizes } from '../../../helpers/cove/fontSettings'
|
|
3
3
|
|
|
4
4
|
const ExpandCollapse = ({ expanded, setExpanded, tableTitle, fontSize, viewport }) => {
|
|
5
|
-
const titleFontSize = ['
|
|
5
|
+
const titleFontSize = ['xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[fontSize]}px`
|
|
6
6
|
return (
|
|
7
7
|
<div
|
|
8
8
|
style={{ fontSize: titleFontSize }}
|
|
@@ -18,7 +18,7 @@ const boxplotCellMatrix = ({ rows, config }): CellMatrix => {
|
|
|
18
18
|
values: labels.values,
|
|
19
19
|
columnCount: labels.count,
|
|
20
20
|
columnSd: 'Standard Deviation',
|
|
21
|
-
|
|
21
|
+
columnNonOutliers: 'Non Outliers',
|
|
22
22
|
columnLowerBounds: labels.lowerBounds,
|
|
23
23
|
columnUpperBounds: labels.upperBounds
|
|
24
24
|
}
|
|
@@ -36,8 +36,12 @@ const boxplotCellMatrix = ({ rows, config }): CellMatrix => {
|
|
|
36
36
|
if (Number(rowid) === 5) return plot.columnCount
|
|
37
37
|
if (Number(rowid) === 6) return plot.columnSd
|
|
38
38
|
if (Number(rowid) === 7) return plot.columnMean
|
|
39
|
-
if (Number(rowid) === 8) return plot.
|
|
39
|
+
if (Number(rowid) === 8) return plot.columnIqr
|
|
40
40
|
if (Number(rowid) === 9) return plot.values.length > 0 ? plot.values.toString() : '-'
|
|
41
|
+
if (Number(rowid) === 10) return plot.columnLowerBounds
|
|
42
|
+
if (Number(rowid) === 11) return plot.columnUpperBounds
|
|
43
|
+
if (Number(rowid) === 12) return plot.columnOutliers.length > 0 ? plot.columnOutliers.toString() : '-'
|
|
44
|
+
if (Number(rowid) === 13) return plot.columnNonOutliers.length > 0 ? plot.columnNonOutliers.toString() : '-'
|
|
41
45
|
return <p>-</p>
|
|
42
46
|
}
|
|
43
47
|
// get list of data keys for each row
|
|
@@ -29,18 +29,18 @@ export const getChartCellValue = (row: string, column: string, config: TableConf
|
|
|
29
29
|
|
|
30
30
|
const rowObj = runtimeData[row]
|
|
31
31
|
let cellValue // placeholder for formatting below
|
|
32
|
-
|
|
32
|
+
const labelValue = rowObj[column] // just raw X axis string
|
|
33
33
|
if (column === config.xAxis?.dataKey) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const { type, dateDisplayFormat, dateParseFormat } = config.xAxis || {}
|
|
35
|
+
const dateFormat = config.table?.dateDisplayFormat || dateDisplayFormat
|
|
36
|
+
|
|
37
|
+
if (type === 'date' || type === 'date-time') {
|
|
38
|
+
cellValue = formatDate(dateFormat, parseDate(dateParseFormat, labelValue))
|
|
39
|
+
} else if (type === 'continuous') {
|
|
40
|
+
cellValue = formatNumber(runtimeData[row][column], 'bottom', false, config)
|
|
41
|
+
} else {
|
|
42
|
+
cellValue = labelValue
|
|
43
|
+
}
|
|
44
44
|
} else {
|
|
45
45
|
let resolvedAxis = 'left'
|
|
46
46
|
let leftAxisItems = config.series ? config.series.filter(item => item?.axis === 'Left') : []
|
|
@@ -205,15 +205,18 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
205
205
|
/>
|
|
206
206
|
)}
|
|
207
207
|
{config?.visualizationType !== 'Sankey' && (
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
208
|
+
<label>
|
|
209
|
+
<span className='edit-label column-heading mt-1'>Exclude Columns </span>
|
|
210
|
+
<MultiSelect
|
|
211
|
+
key={excludedColumns.join('') + 'excluded'}
|
|
212
|
+
options={dataColumns.map(c => ({ label: c, value: c }))}
|
|
213
|
+
selected={excludedColumns}
|
|
214
|
+
label={'Exclude Columns'}
|
|
215
|
+
fieldName='dataTable'
|
|
216
|
+
section='columns'
|
|
217
|
+
updateField={excludeColumns}
|
|
218
|
+
/>
|
|
219
|
+
</label>
|
|
217
220
|
)}
|
|
218
221
|
<CheckBox
|
|
219
222
|
value={config.table.collapsible}
|
|
@@ -323,18 +326,9 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
323
326
|
updateField={updateField}
|
|
324
327
|
/>
|
|
325
328
|
{config.table.pivot?.columnName && (
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
.filter(col => col !== config.table.pivot?.columnName && col !== config.table.groupBy)
|
|
330
|
-
.map(c => ({ label: c, value: c }))}
|
|
331
|
-
selected={config.table.pivot?.valueColumns}
|
|
332
|
-
label='Pivot Value Column(s) '
|
|
333
|
-
section='table'
|
|
334
|
-
subsection='pivot'
|
|
335
|
-
fieldName='valueColumns'
|
|
336
|
-
updateField={updateField}
|
|
337
|
-
tooltip={
|
|
329
|
+
<label>
|
|
330
|
+
<span className='edit-label column-heading mt-1'>
|
|
331
|
+
Pivot Value Column(s)
|
|
338
332
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
339
333
|
<Tooltip.Target>
|
|
340
334
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
@@ -343,8 +337,20 @@ const DataTableEditor: React.FC<DataTableProps> = ({ config, updateField, isDash
|
|
|
343
337
|
<p>The column(s) whos values will be pivoted under the column selected as the Filter.</p>
|
|
344
338
|
</Tooltip.Content>
|
|
345
339
|
</Tooltip>
|
|
346
|
-
|
|
347
|
-
|
|
340
|
+
</span>
|
|
341
|
+
<MultiSelect
|
|
342
|
+
key={config.table.pivot?.columnName}
|
|
343
|
+
options={groupPivotColumns
|
|
344
|
+
.filter(col => col !== config.table.pivot?.columnName && col !== config.table.groupBy)
|
|
345
|
+
.map(c => ({ label: c, value: c }))}
|
|
346
|
+
selected={config.table.pivot?.valueColumns}
|
|
347
|
+
label='Pivot Value Column(s) '
|
|
348
|
+
section='table'
|
|
349
|
+
subsection='pivot'
|
|
350
|
+
fieldName='valueColumns'
|
|
351
|
+
updateField={updateField}
|
|
352
|
+
/>
|
|
353
|
+
</label>
|
|
348
354
|
)}
|
|
349
355
|
</>
|
|
350
356
|
)
|
|
@@ -67,7 +67,12 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
67
67
|
|
|
68
68
|
const addNewFilter = () => {
|
|
69
69
|
const filters = config.filters ? [...config.filters] : []
|
|
70
|
-
const newVizFilter: VizFilter = {
|
|
70
|
+
const newVizFilter: VizFilter = {
|
|
71
|
+
values: [],
|
|
72
|
+
filterStyle: 'dropdown',
|
|
73
|
+
id: Date.now(),
|
|
74
|
+
showDropdown: true
|
|
75
|
+
} as VizFilter
|
|
71
76
|
filters.push(newVizFilter)
|
|
72
77
|
updateField(null, null, 'filters', filters)
|
|
73
78
|
}
|
|
@@ -125,6 +130,13 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
125
130
|
</Tooltip>
|
|
126
131
|
}
|
|
127
132
|
/>
|
|
133
|
+
<TextField
|
|
134
|
+
type='textarea'
|
|
135
|
+
label='Filter intro text'
|
|
136
|
+
value={config.filterIntro}
|
|
137
|
+
updateField={updateField}
|
|
138
|
+
fieldName='filterIntro'
|
|
139
|
+
/>
|
|
128
140
|
<br />
|
|
129
141
|
<ul className='filters-list'>
|
|
130
142
|
{/* Whether filters should apply onChange or Apply Button */}
|
|
@@ -229,6 +241,17 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
229
241
|
handleFilterOrder={(index1, index2) => handleFilterOrder(index1, index2, filterIndex)}
|
|
230
242
|
/>
|
|
231
243
|
)}
|
|
244
|
+
{filter.order === 'column' && (
|
|
245
|
+
<Select
|
|
246
|
+
value={filter.orderColumn}
|
|
247
|
+
fieldName='orderColumn'
|
|
248
|
+
label='Order Column'
|
|
249
|
+
updateField={(_section, _subSection, _field, value) =>
|
|
250
|
+
updateFilterProp('orderColumn', filterIndex, value)
|
|
251
|
+
}
|
|
252
|
+
options={dataColumns}
|
|
253
|
+
/>
|
|
254
|
+
)}
|
|
232
255
|
</>
|
|
233
256
|
) : (
|
|
234
257
|
<NestedDropdownEditor
|
|
@@ -262,6 +285,7 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
262
285
|
updateFilterProp('parents', filterIndex, value)
|
|
263
286
|
}}
|
|
264
287
|
options={getParentFilterOptions(filterIndex)}
|
|
288
|
+
selected={config.filters[filterIndex].parents}
|
|
265
289
|
/>
|
|
266
290
|
</label>
|
|
267
291
|
</FieldSetWrapper>
|
|
@@ -41,18 +41,25 @@ export const filterOrderOptions: { label: string; value: OrderBy }[] = [
|
|
|
41
41
|
{
|
|
42
42
|
label: 'Custom',
|
|
43
43
|
value: 'cust'
|
|
44
|
-
}
|
|
44
|
+
},
|
|
45
|
+
{ label: 'Order By Data Column', value: 'column' }
|
|
45
46
|
]
|
|
46
47
|
|
|
47
|
-
const hasStandardFilterBehavior = ['chart', 'table']
|
|
48
|
-
|
|
49
48
|
export const useFilters = props => {
|
|
50
49
|
const [showApplyButton, setShowApplyButton] = useState(false)
|
|
51
50
|
|
|
52
51
|
// Desconstructing: notice, adding more descriptive visualizationConfig name over config
|
|
53
52
|
// visualizationConfig feels more robust for all vis types so that its not confused with config/state/etc.
|
|
54
|
-
const {
|
|
55
|
-
|
|
53
|
+
const {
|
|
54
|
+
config: visualizationConfig,
|
|
55
|
+
setConfig,
|
|
56
|
+
filteredData,
|
|
57
|
+
setFilteredData,
|
|
58
|
+
excludedData,
|
|
59
|
+
getUniqueValues,
|
|
60
|
+
standaloneMap
|
|
61
|
+
} = props
|
|
62
|
+
const { data } = visualizationConfig
|
|
56
63
|
|
|
57
64
|
/**
|
|
58
65
|
* Re-orders a filter based on two indices and updates the runtime filters array and filters state
|
|
@@ -72,9 +79,7 @@ export const useFilters = props => {
|
|
|
72
79
|
const [movedItem] = updatedValues.splice(idx1, 1)
|
|
73
80
|
updatedValues.splice(idx2, 0, movedItem)
|
|
74
81
|
|
|
75
|
-
const filtersCopy =
|
|
76
|
-
? [...visualizationConfig.filters]
|
|
77
|
-
: [...filteredData]
|
|
82
|
+
const filtersCopy = !standaloneMap ? [...visualizationConfig.filters] : [...filteredData]
|
|
78
83
|
const filterItem = { ...filtersCopy[filterIndex] }
|
|
79
84
|
|
|
80
85
|
// Overwrite filterItem.values since thats what we map through in the editor panel
|
|
@@ -86,7 +91,7 @@ export const useFilters = props => {
|
|
|
86
91
|
// Update the filters
|
|
87
92
|
filtersCopy[filterIndex] = filterItem
|
|
88
93
|
|
|
89
|
-
if (
|
|
94
|
+
if (standaloneMap) {
|
|
90
95
|
setFilteredData(filtersCopy)
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -94,7 +99,7 @@ export const useFilters = props => {
|
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
const changeFilterActive = (index, value) => {
|
|
97
|
-
let newFilters =
|
|
102
|
+
let newFilters = standaloneMap ? [...filteredData] : [...visualizationConfig.filters]
|
|
98
103
|
|
|
99
104
|
if (visualizationConfig.filterBehavior === 'Apply Button') {
|
|
100
105
|
newFilters[index].queuedActive = value
|
|
@@ -120,7 +125,7 @@ export const useFilters = props => {
|
|
|
120
125
|
queryParams[newFilter?.subGrouping?.setByQueryParameter] = newFilter.subGrouping.active
|
|
121
126
|
updateQueryString(queryParams)
|
|
122
127
|
}
|
|
123
|
-
setFilteredData(newFilters
|
|
128
|
+
setFilteredData(newFilters)
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
if (!visualizationConfig.dynamicSeries) {
|
|
@@ -132,15 +137,12 @@ export const useFilters = props => {
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
// Used for setting active filter, fromHash breaks the filteredData functionality.
|
|
135
|
-
if (
|
|
140
|
+
if (standaloneMap && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
136
141
|
setFilteredData(newFilters)
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
// If we're on a chart and not using the apply button
|
|
140
|
-
if (
|
|
141
|
-
hasStandardFilterBehavior.includes(visualizationConfig.type) &&
|
|
142
|
-
visualizationConfig.filterBehavior === 'Filter Change'
|
|
143
|
-
) {
|
|
145
|
+
if (!standaloneMap && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
144
146
|
const newFilteredData = filterVizData(newFilters, excludedData)
|
|
145
147
|
setFilteredData(newFilteredData)
|
|
146
148
|
|
|
@@ -204,11 +206,9 @@ export const useFilters = props => {
|
|
|
204
206
|
|
|
205
207
|
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
206
208
|
|
|
207
|
-
if (
|
|
209
|
+
if (standaloneMap) {
|
|
208
210
|
setFilteredData(newFilters, excludedData)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (hasStandardFilterBehavior.includes(visualizationConfig.type)) {
|
|
211
|
+
} else {
|
|
212
212
|
setFilteredData(filterVizData(newFilters, excludedData))
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -240,7 +240,7 @@ export const useFilters = props => {
|
|
|
240
240
|
|
|
241
241
|
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
242
242
|
|
|
243
|
-
if (
|
|
243
|
+
if (standaloneMap) {
|
|
244
244
|
setFilteredData(newFilters, excludedData)
|
|
245
245
|
} else {
|
|
246
246
|
setFilteredData(filterVizData(newFilters, excludedData))
|
|
@@ -249,9 +249,7 @@ export const useFilters = props => {
|
|
|
249
249
|
|
|
250
250
|
const filterConstants = {
|
|
251
251
|
buttonText: 'Apply Filters',
|
|
252
|
-
resetText: 'Reset All'
|
|
253
|
-
introText: `Make a selection from the filters to change the visualization information.`,
|
|
254
|
-
applyText: 'Select the apply button to update the visualization information.'
|
|
252
|
+
resetText: 'Reset All'
|
|
255
253
|
}
|
|
256
254
|
|
|
257
255
|
// prettier-ignore
|
|
@@ -276,13 +274,12 @@ type FilterProps = {
|
|
|
276
274
|
setFilteredData: Function
|
|
277
275
|
// updating function for setting fitlerBehavior
|
|
278
276
|
setConfig: Function
|
|
279
|
-
|
|
280
|
-
exclusions: any[]
|
|
277
|
+
standaloneMap?: boolean
|
|
281
278
|
}
|
|
282
279
|
|
|
283
280
|
const Filters = (props: FilterProps) => {
|
|
284
|
-
const { config: visualizationConfig, filteredData, dimensions } = props
|
|
285
|
-
const { filters,
|
|
281
|
+
const { config: visualizationConfig, filteredData, dimensions, standaloneMap } = props
|
|
282
|
+
const { filters, general, theme, filterBehavior } = visualizationConfig
|
|
286
283
|
const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
|
|
287
284
|
const [selectedFilter, setSelectedFilter] = useState<EventTarget>(null)
|
|
288
285
|
const id = useId()
|
|
@@ -365,7 +362,7 @@ const Filters = (props: FilterProps) => {
|
|
|
365
362
|
|
|
366
363
|
const vizFiltersWithValues = useMemo(() => {
|
|
367
364
|
// Here charts is using config.filters where maps is using a runtime value
|
|
368
|
-
let vizfilters =
|
|
365
|
+
let vizfilters = standaloneMap ? filteredData : filters
|
|
369
366
|
if (!vizfilters) return []
|
|
370
367
|
if (vizfilters.fromHash) delete vizfilters.fromHash // support for Maps config
|
|
371
368
|
return addValuesToFilters(vizfilters as VizFilter[], visualizationConfig.data)
|
|
@@ -448,7 +445,7 @@ const Filters = (props: FilterProps) => {
|
|
|
448
445
|
<div className={classList.join(' ')} key={outerIndex}>
|
|
449
446
|
<>
|
|
450
447
|
{label && (
|
|
451
|
-
<label className='
|
|
448
|
+
<label className='font-weight-bold mt-1 mb-0' htmlFor={`filter-${outerIndex}`}>
|
|
452
449
|
{label}
|
|
453
450
|
</label>
|
|
454
451
|
)}
|
|
@@ -494,7 +491,7 @@ const Filters = (props: FilterProps) => {
|
|
|
494
491
|
const getClasses = () => {
|
|
495
492
|
const { visualizationType, legend } = visualizationConfig || {}
|
|
496
493
|
const baseClass = 'filters-section'
|
|
497
|
-
const conditionalClass =
|
|
494
|
+
const conditionalClass = standaloneMap ? general.headerColor : visualizationType === 'Spark Line' ? null : theme
|
|
498
495
|
const legendClass = legend && !legend.hide && legend.position === 'top' ? 'mb-0' : null
|
|
499
496
|
|
|
500
497
|
return [baseClass, conditionalClass, legendClass].filter(Boolean)
|
|
@@ -502,10 +499,9 @@ const Filters = (props: FilterProps) => {
|
|
|
502
499
|
|
|
503
500
|
return (
|
|
504
501
|
<section className={getClasses().join(' ')}>
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
</p>
|
|
502
|
+
{visualizationConfig.filterIntro && (
|
|
503
|
+
<p className='filters-section__intro-text'>{visualizationConfig.filterIntro}</p>
|
|
504
|
+
)}
|
|
509
505
|
<div className='d-flex flex-wrap w-100 filters-section__wrapper'>
|
|
510
506
|
{' '}
|
|
511
507
|
<>
|
|
@@ -7,6 +7,11 @@ export const handleSorting = singleFilter => {
|
|
|
7
7
|
return singleFilter
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
if (singleFilter.order === 'column') {
|
|
11
|
+
// sorting is done in the generateValuesForFilter function
|
|
12
|
+
return singleFilter
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
const sort = (a, b) => {
|
|
11
16
|
const asc = singleFilter.order !== 'desc'
|
|
12
17
|
return String(asc ? a : b).localeCompare(String(asc ? b : a), 'en', { numeric: true })
|
|
@@ -15,20 +15,33 @@ type StandAloneProps = {
|
|
|
15
15
|
viewport?: ViewPort
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const FootnotesStandAlone: React.FC<StandAloneProps> = ({
|
|
18
|
+
const FootnotesStandAlone: React.FC<StandAloneProps> = ({
|
|
19
|
+
visualizationKey,
|
|
20
|
+
config,
|
|
21
|
+
viewport,
|
|
22
|
+
isEditor,
|
|
23
|
+
updateConfig
|
|
24
|
+
}) => {
|
|
19
25
|
const updateField = updateFieldFactory<Footnote[]>(config, updateConfig)
|
|
20
26
|
if (isEditor)
|
|
21
27
|
return (
|
|
22
|
-
<EditorWrapper
|
|
28
|
+
<EditorWrapper
|
|
29
|
+
component={FootnotesStandAlone}
|
|
30
|
+
visualizationKey={visualizationKey}
|
|
31
|
+
visualizationConfig={config}
|
|
32
|
+
updateConfig={updateConfig}
|
|
33
|
+
type={'Footnotes'}
|
|
34
|
+
viewport={viewport}
|
|
35
|
+
>
|
|
23
36
|
<FootnotesEditor key={visualizationKey} config={config} updateField={updateField} />
|
|
24
37
|
</EditorWrapper>
|
|
25
38
|
)
|
|
26
39
|
|
|
27
40
|
// get the api footnotes from the config
|
|
28
41
|
const apiFootnotes = useMemo(() => {
|
|
29
|
-
|
|
42
|
+
const configData = config.formattedData || config.data
|
|
43
|
+
if (configData && config.dataKey && config.dynamicFootnotes) {
|
|
30
44
|
const { symbolColumn, textColumn, orderColumn } = config.dynamicFootnotes
|
|
31
|
-
const configData = config.formattedData || config.data
|
|
32
45
|
const _data = configData.map(row => _.pick(row, [symbolColumn, textColumn, orderColumn]))
|
|
33
46
|
_data.sort((a, b) => a[orderColumn] - b[orderColumn])
|
|
34
47
|
return _data.map(row => ({ symbol: row[symbolColumn], text: row[textColumn] }))
|
|
@@ -38,7 +38,7 @@ const LegendGradient = ({
|
|
|
38
38
|
|
|
39
39
|
const numTicks = colors?.length
|
|
40
40
|
|
|
41
|
-
const longestLabel = labels
|
|
41
|
+
const longestLabel = (labels || []).reduce((a: string, b) => (a.length > String(b).length ? a : b), '')
|
|
42
42
|
const boxHeight = 20
|
|
43
43
|
let height = 50
|
|
44
44
|
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react'
|
|
2
2
|
import './loader.styles.css'
|
|
3
3
|
|
|
4
|
+
// these coorespond to bootstrap classes
|
|
5
|
+
// https://getbootstrap.com/docs/4.2/components/spinners/
|
|
6
|
+
type SpinnerType = 'text-primary' | 'text-secondary'
|
|
7
|
+
|
|
4
8
|
type LoaderProps = {
|
|
5
9
|
fullScreen?: boolean
|
|
10
|
+
spinnerType?: SpinnerType
|
|
6
11
|
}
|
|
7
12
|
|
|
8
|
-
const Spinner = () => (
|
|
9
|
-
<div className=
|
|
13
|
+
const Spinner = ({ spinnerType }: { spinnerType: SpinnerType }) => (
|
|
14
|
+
<div className={`spinner-border ${spinnerType}`} role='status'>
|
|
10
15
|
<span className='sr-only'>Loading...</span>
|
|
11
16
|
</div>
|
|
12
17
|
)
|
|
13
18
|
|
|
14
|
-
const Loader: React.FC<LoaderProps> = ({ fullScreen = false }) => {
|
|
19
|
+
const Loader: React.FC<LoaderProps> = ({ fullScreen = false, spinnerType }) => {
|
|
15
20
|
const backgroundRef = useRef(null)
|
|
16
21
|
|
|
17
22
|
useEffect(() => {
|
|
@@ -23,10 +28,10 @@ const Loader: React.FC<LoaderProps> = ({ fullScreen = false }) => {
|
|
|
23
28
|
|
|
24
29
|
return fullScreen ? (
|
|
25
30
|
<div ref={backgroundRef} className='cove-loader fullscreen'>
|
|
26
|
-
<Spinner />
|
|
31
|
+
<Spinner spinnerType={spinnerType || 'text-primary'} />
|
|
27
32
|
</div>
|
|
28
33
|
) : (
|
|
29
|
-
<Spinner />
|
|
34
|
+
<Spinner spinnerType={spinnerType || 'text-primary'} />
|
|
30
35
|
)
|
|
31
36
|
}
|
|
32
37
|
|