@cdc/core 4.24.12 → 4.25.1
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 +26 -36
- package/components/DataTable/DataTableStandAlone.tsx +3 -3
- package/components/DataTable/components/ChartHeader.tsx +3 -2
- package/components/DataTable/components/ExpandCollapse.tsx +1 -4
- package/components/DataTable/data-table.css +2 -7
- 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/VizFilterEditor/VizFilterEditor.tsx +15 -2
- package/components/Filters/Filters.tsx +42 -36
- package/components/Filters/helpers/handleSorting.ts +5 -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 +7 -12
- package/components/NestedDropdown/nesteddropdown.styles.css +11 -5
- package/components/Table/Table.tsx +0 -6
- package/components/Table/components/Row.tsx +2 -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 +77 -591
- package/dist/cove-main.css.map +1 -1
- package/helpers/DataTransform.ts +55 -63
- 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 +16 -1
- package/helpers/isOlderVersion.ts +20 -0
- package/helpers/missingRequiredSections.ts +20 -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.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 +5 -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/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 +2 -1
- package/styles/v2/utils/_spacers.scss +0 -31
|
@@ -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,13 +99,13 @@ 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
|
|
|
104
|
+
const newFilter = newFilters[index]
|
|
99
105
|
if (visualizationConfig.filterBehavior === 'Apply Button') {
|
|
100
|
-
|
|
106
|
+
newFilter.queuedActive = value
|
|
101
107
|
setShowApplyButton(true)
|
|
102
108
|
} else {
|
|
103
|
-
const newFilter = newFilters[index]
|
|
104
109
|
if (newFilter.filterStyle !== 'nested-dropdown') {
|
|
105
110
|
newFilter.active = value
|
|
106
111
|
} else {
|
|
@@ -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))
|
|
@@ -274,13 +274,12 @@ type FilterProps = {
|
|
|
274
274
|
setFilteredData: Function
|
|
275
275
|
// updating function for setting fitlerBehavior
|
|
276
276
|
setConfig: Function
|
|
277
|
-
|
|
278
|
-
exclusions: any[]
|
|
277
|
+
standaloneMap?: boolean
|
|
279
278
|
}
|
|
280
279
|
|
|
281
280
|
const Filters = (props: FilterProps) => {
|
|
282
|
-
const { config: visualizationConfig, filteredData, dimensions } = props
|
|
283
|
-
const { filters,
|
|
281
|
+
const { config: visualizationConfig, filteredData, dimensions, standaloneMap } = props
|
|
282
|
+
const { filters, general, theme, filterBehavior } = visualizationConfig
|
|
284
283
|
const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
|
|
285
284
|
const [selectedFilter, setSelectedFilter] = useState<EventTarget>(null)
|
|
286
285
|
const id = useId()
|
|
@@ -363,7 +362,7 @@ const Filters = (props: FilterProps) => {
|
|
|
363
362
|
|
|
364
363
|
const vizFiltersWithValues = useMemo(() => {
|
|
365
364
|
// Here charts is using config.filters where maps is using a runtime value
|
|
366
|
-
let vizfilters =
|
|
365
|
+
let vizfilters = standaloneMap ? filteredData : filters
|
|
367
366
|
if (!vizfilters) return []
|
|
368
367
|
if (vizfilters.fromHash) delete vizfilters.fromHash // support for Maps config
|
|
369
368
|
return addValuesToFilters(vizfilters as VizFilter[], visualizationConfig.data)
|
|
@@ -437,16 +436,23 @@ const Filters = (props: FilterProps) => {
|
|
|
437
436
|
|
|
438
437
|
const classList = [
|
|
439
438
|
'single-filters',
|
|
440
|
-
'form-group
|
|
439
|
+
'form-group',
|
|
441
440
|
mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`
|
|
442
441
|
]
|
|
443
442
|
const mobileExempt = ['nested-dropdown', 'multi-select'].includes(filterStyle)
|
|
444
443
|
const showDefaultDropdown = (filterStyle === 'dropdown' || mobileFilterStyle) && !mobileExempt
|
|
444
|
+
const [nestedActiveGroup, nestedActiveSubGroup] = useMemo<string[]>(() => {
|
|
445
|
+
if (filterStyle !== 'nested-dropdown') return []
|
|
446
|
+
return (singleFilter.queuedActive || [singleFilter.active, singleFilter.subGrouping?.active]) as [
|
|
447
|
+
string,
|
|
448
|
+
string
|
|
449
|
+
]
|
|
450
|
+
}, [singleFilter])
|
|
445
451
|
return (
|
|
446
452
|
<div className={classList.join(' ')} key={outerIndex}>
|
|
447
453
|
<>
|
|
448
454
|
{label && (
|
|
449
|
-
<label className='font-weight-bold
|
|
455
|
+
<label className='font-weight-bold mb-2' htmlFor={`filter-${outerIndex}`}>
|
|
450
456
|
{label}
|
|
451
457
|
</label>
|
|
452
458
|
)}
|
|
@@ -464,8 +470,8 @@ const Filters = (props: FilterProps) => {
|
|
|
464
470
|
)}
|
|
465
471
|
{filterStyle === 'nested-dropdown' && (
|
|
466
472
|
<NestedDropdown
|
|
467
|
-
activeGroup={
|
|
468
|
-
activeSubGroup={
|
|
473
|
+
activeGroup={nestedActiveGroup}
|
|
474
|
+
activeSubGroup={nestedActiveSubGroup}
|
|
469
475
|
filterIndex={outerIndex}
|
|
470
476
|
options={getNestedOptions(singleFilter)}
|
|
471
477
|
listLabel={label}
|
|
@@ -492,18 +498,18 @@ const Filters = (props: FilterProps) => {
|
|
|
492
498
|
const getClasses = () => {
|
|
493
499
|
const { visualizationType, legend } = visualizationConfig || {}
|
|
494
500
|
const baseClass = 'filters-section'
|
|
495
|
-
const conditionalClass =
|
|
501
|
+
const conditionalClass = standaloneMap ? general.headerColor : visualizationType === 'Spark Line' ? null : theme
|
|
496
502
|
const legendClass = legend && !legend.hide && legend.position === 'top' ? 'mb-0' : null
|
|
497
503
|
|
|
498
|
-
return [baseClass, conditionalClass, legendClass].filter(Boolean)
|
|
504
|
+
return [baseClass, conditionalClass, legendClass, 'w-100'].filter(Boolean)
|
|
499
505
|
}
|
|
500
506
|
|
|
501
507
|
return (
|
|
502
508
|
<section className={getClasses().join(' ')}>
|
|
503
509
|
{visualizationConfig.filterIntro && (
|
|
504
|
-
<p className='filters-section__intro-text'>{visualizationConfig.filterIntro}</p>
|
|
510
|
+
<p className='filters-section__intro-text mb-3'>{visualizationConfig.filterIntro}</p>
|
|
505
511
|
)}
|
|
506
|
-
<div className='d-flex flex-wrap w-100 filters-section__wrapper'>
|
|
512
|
+
<div className='d-flex flex-wrap w-100 mb-4 pb-2 filters-section__wrapper'>
|
|
507
513
|
{' '}
|
|
508
514
|
<>
|
|
509
515
|
<Style />
|
|
@@ -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 })
|
|
@@ -12,7 +12,7 @@ const Footnotes: React.FC<FootnotesProps> = ({ footnotes }) => {
|
|
|
12
12
|
{footnotes.map((note, i) => {
|
|
13
13
|
return (
|
|
14
14
|
<li key={note.symbol + i} className='mb-1'>
|
|
15
|
-
{note.symbol && <span className='
|
|
15
|
+
{note.symbol && <span className='me-1'>{note.symbol}</span>}
|
|
16
16
|
{note.text}
|
|
17
17
|
</li>
|
|
18
18
|
)
|
|
@@ -17,10 +17,17 @@ type VisualizationWrapper = {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const Visualization: React.FC<VisualizationWrapper> = forwardRef((props, ref) => {
|
|
20
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
config = {},
|
|
22
|
+
isEditor = false,
|
|
23
|
+
currentViewport = 'lg',
|
|
24
|
+
imageId = '',
|
|
25
|
+
showEditorPanel = true,
|
|
26
|
+
className
|
|
27
|
+
} = props
|
|
21
28
|
|
|
22
29
|
const getWrappingClasses = () => {
|
|
23
|
-
let classes = ['cdc-open-viz-module', `${currentViewport}`,
|
|
30
|
+
let classes = ['cdc-open-viz-module', `${currentViewport}`, `${config?.theme}`]
|
|
24
31
|
|
|
25
32
|
if (className) {
|
|
26
33
|
classes.push(className)
|
|
@@ -40,7 +47,7 @@ const Visualization: React.FC<VisualizationWrapper> = forwardRef((props, ref) =>
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
if (config.type === 'filtered-text') {
|
|
43
|
-
classes.push('type-filtered-text')
|
|
50
|
+
classes.push('type-filtered-text', `font-${config.fontSize}`)
|
|
44
51
|
classes = classes.filter(item => item !== 'cove-component__content')
|
|
45
52
|
return classes
|
|
46
53
|
}
|
|
@@ -67,7 +74,14 @@ const Visualization: React.FC<VisualizationWrapper> = forwardRef((props, ref) =>
|
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
if (config.type === 'waffle-chart') {
|
|
70
|
-
classes.push(
|
|
77
|
+
classes.push(
|
|
78
|
+
'cove',
|
|
79
|
+
'cdc-open-viz-module',
|
|
80
|
+
'type-waffle-chart',
|
|
81
|
+
currentViewport,
|
|
82
|
+
config.theme,
|
|
83
|
+
'font-' + config.overallFontSize
|
|
84
|
+
)
|
|
71
85
|
|
|
72
86
|
if (isEditor) {
|
|
73
87
|
classes.push('is-editor')
|
|
@@ -92,10 +92,7 @@ const LegendGradient = ({
|
|
|
92
92
|
|
|
93
93
|
if (style === 'gradient') {
|
|
94
94
|
return (
|
|
95
|
-
<svg
|
|
96
|
-
style={{ overflow: 'visible', width: '100%', marginTop: 10, marginBottom: hideBorder ? 10 : 0 }}
|
|
97
|
-
height={newHeight}
|
|
98
|
-
>
|
|
95
|
+
<svg className={'w-100 overflow-visible'} height={newHeight}>
|
|
99
96
|
{/* background border*/}
|
|
100
97
|
<rect x={0} y={0} width={legendWidth + MARGIN * 2} height={boxHeight + MARGIN * 2} fill='#d3d3d3' />
|
|
101
98
|
{/* Define the gradient */}
|
|
@@ -28,7 +28,7 @@ const ResetButton = props => {
|
|
|
28
28
|
if (config.runtime.disabledAmt === 0) return <></>
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
|
-
<button onClick={handleReset} className={legendClasses.
|
|
31
|
+
<button onClick={handleReset} className={legendClasses.showAllButton.join(' ') || ''}>
|
|
32
32
|
Reset
|
|
33
33
|
</button>
|
|
34
34
|
)
|
|
@@ -10,9 +10,8 @@ interface LegendShapeProps {
|
|
|
10
10
|
const LegendShape: React.FC<LegendShapeProps> = props => {
|
|
11
11
|
const { fill, borderColor, display = 'inline-block', shape = 'circle' } = props
|
|
12
12
|
const dimensions = { width: '1em', height: '1em' }
|
|
13
|
-
const
|
|
13
|
+
const isCircleOrSquare = ['circle', 'square'].includes(shape)
|
|
14
14
|
const styles = {
|
|
15
|
-
marginRight: marginRight,
|
|
16
15
|
borderRadius: shape === 'circle' ? '50%' : '0px',
|
|
17
16
|
verticalAlign: 'middle',
|
|
18
17
|
display: display,
|
|
@@ -22,7 +21,7 @@ const LegendShape: React.FC<LegendShapeProps> = props => {
|
|
|
22
21
|
backgroundColor: fill
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
return <span className=
|
|
24
|
+
return <span className={`legend-item ${isCircleOrSquare ? 'me-2' : ''}`} style={styles} />
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
export default LegendShape
|
|
@@ -42,13 +42,30 @@ const generateMedia = (state, type, elementToCapture) => {
|
|
|
42
42
|
// Apparently some packages use state.title where others use state.general.title
|
|
43
43
|
const handleFileName = state => {
|
|
44
44
|
// dashboard titles
|
|
45
|
-
if (state?.dashboard?.title)
|
|
45
|
+
if (state?.dashboard?.title)
|
|
46
|
+
return (
|
|
47
|
+
state.dashboard.title.replace(/\s+/g, '-').toLowerCase() +
|
|
48
|
+
'-' +
|
|
49
|
+
date.getDate() +
|
|
50
|
+
date.getMonth() +
|
|
51
|
+
date.getFullYear()
|
|
52
|
+
)
|
|
46
53
|
|
|
47
54
|
// map titles
|
|
48
|
-
if (state?.general?.title)
|
|
55
|
+
if (state?.general?.title)
|
|
56
|
+
return (
|
|
57
|
+
state.general.title.replace(/\s+/g, '-').toLowerCase() +
|
|
58
|
+
'-' +
|
|
59
|
+
date.getDate() +
|
|
60
|
+
date.getMonth() +
|
|
61
|
+
date.getFullYear()
|
|
62
|
+
)
|
|
49
63
|
|
|
50
64
|
// chart titles
|
|
51
|
-
if (state?.title)
|
|
65
|
+
if (state?.title)
|
|
66
|
+
return (
|
|
67
|
+
state.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
|
|
68
|
+
)
|
|
52
69
|
|
|
53
70
|
return 'no-title'
|
|
54
71
|
}
|
|
@@ -59,7 +76,10 @@ const generateMedia = (state, type, elementToCapture) => {
|
|
|
59
76
|
|
|
60
77
|
switch (type) {
|
|
61
78
|
case 'image':
|
|
62
|
-
html2canvas(baseSvg, {
|
|
79
|
+
html2canvas(baseSvg, {
|
|
80
|
+
ignoreElements: el =>
|
|
81
|
+
el.className?.indexOf && el.className.search(/download-buttons|download-links|data-table-container/) !== -1
|
|
82
|
+
}).then(canvas => {
|
|
63
83
|
saveImageAs(canvas.toDataURL(), filename + '.png')
|
|
64
84
|
})
|
|
65
85
|
return
|
|
@@ -98,9 +118,14 @@ const handleTheme = state => {
|
|
|
98
118
|
|
|
99
119
|
// Download CSV
|
|
100
120
|
const Button = ({ state, text, type, title, elementToCapture }) => {
|
|
101
|
-
const buttonClasses = ['btn', 'btn-
|
|
121
|
+
const buttonClasses = ['btn', 'btn-primary']
|
|
102
122
|
return (
|
|
103
|
-
<button
|
|
123
|
+
<button
|
|
124
|
+
className={buttonClasses.join(' ')}
|
|
125
|
+
title={title}
|
|
126
|
+
onClick={() => generateMedia(state, type, elementToCapture)}
|
|
127
|
+
style={{ lineHeight: '1.4em' }}
|
|
128
|
+
>
|
|
104
129
|
{buttonText[type]}
|
|
105
130
|
</button>
|
|
106
131
|
)
|
|
@@ -108,7 +133,7 @@ const Button = ({ state, text, type, title, elementToCapture }) => {
|
|
|
108
133
|
|
|
109
134
|
// Link to CSV/JSON data
|
|
110
135
|
const Link = ({ config, dashboardDataConfig }) => {
|
|
111
|
-
let dataConfig = dashboardDataConfig || config
|
|
136
|
+
let dataConfig = dashboardDataConfig || config
|
|
112
137
|
// Handles Maps & Charts
|
|
113
138
|
if (dataConfig.dataFileSourceType === 'url' && dataConfig.dataFileName && config.table.showDownloadUrl) {
|
|
114
139
|
return (
|
|
@@ -126,7 +151,6 @@ const Link = ({ config, dashboardDataConfig }) => {
|
|
|
126
151
|
) : null
|
|
127
152
|
}
|
|
128
153
|
|
|
129
|
-
// TODO: convert to standardized COVE section
|
|
130
154
|
const Section = ({ children, classes }) => {
|
|
131
155
|
return (
|
|
132
156
|
<section className={classes.join(' ')}>
|
|
@@ -124,25 +124,21 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
|
|
|
124
124
|
}) => {
|
|
125
125
|
const dropdownId = useId()
|
|
126
126
|
|
|
127
|
-
const [userSearchTerm, setUserSearchTerm] = useState(
|
|
128
|
-
const [inputValue, setInputValue] = useState('')
|
|
127
|
+
const [userSearchTerm, setUserSearchTerm] = useState(null)
|
|
129
128
|
|
|
130
|
-
const
|
|
129
|
+
const inputValue = useMemo(() => {
|
|
131
130
|
// value from props
|
|
132
131
|
return activeSubGroup ? `${activeGroup} - ${activeSubGroup}` : ''
|
|
133
|
-
}, [activeSubGroup])
|
|
132
|
+
}, [activeGroup, activeSubGroup])
|
|
134
133
|
const [inputHasFocus, setInputHasFocus] = useState(false)
|
|
135
134
|
const [isListOpened, setIsListOpened] = useState(false)
|
|
136
|
-
|
|
137
135
|
const searchInput = useRef(null)
|
|
138
136
|
const searchDropdown = useRef(null)
|
|
139
137
|
|
|
140
138
|
const chooseSelectedSubGroup = (tierOne: string | number, tierTwo: string | number) => {
|
|
141
139
|
searchInput.current.focus()
|
|
142
|
-
|
|
143
|
-
setUserSearchTerm('')
|
|
140
|
+
setUserSearchTerm(null)
|
|
144
141
|
setIsListOpened(false)
|
|
145
|
-
setInputValue(selectedItemValue)
|
|
146
142
|
handleSelectedItems([String(tierOne), String(tierTwo)])
|
|
147
143
|
}
|
|
148
144
|
|
|
@@ -220,14 +216,13 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
|
|
|
220
216
|
}
|
|
221
217
|
|
|
222
218
|
const filterOptions = useMemo(() => {
|
|
223
|
-
return filterSearchTerm(userSearchTerm, options)
|
|
219
|
+
return filterSearchTerm(userSearchTerm || '', options)
|
|
224
220
|
}, [userSearchTerm, options])
|
|
225
221
|
|
|
226
222
|
const handleSearchTermChange = e => {
|
|
227
223
|
const newSearchTerm = e.target.value
|
|
228
224
|
setIsListOpened(true)
|
|
229
225
|
setUserSearchTerm(newSearchTerm)
|
|
230
|
-
setInputValue(newSearchTerm)
|
|
231
226
|
}
|
|
232
227
|
|
|
233
228
|
const handleOnBlur = e => {
|
|
@@ -265,7 +260,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
|
|
|
265
260
|
aria-haspopup='true'
|
|
266
261
|
aria-hidden='false'
|
|
267
262
|
tabIndex={0}
|
|
268
|
-
value={
|
|
263
|
+
value={userSearchTerm !== null ? userSearchTerm : inputValue}
|
|
269
264
|
onChange={handleSearchTermChange}
|
|
270
265
|
placeholder={loading ? 'Loading...' : '- Select -'}
|
|
271
266
|
disabled={loading || !options.length}
|
|
@@ -303,7 +298,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
|
|
|
303
298
|
chooseSelectedSubGroup(groupValue, subGroupValue)
|
|
304
299
|
}}
|
|
305
300
|
userSelectedLabel={activeGroup + activeSubGroup}
|
|
306
|
-
userSearchTerm={userSearchTerm}
|
|
301
|
+
userSearchTerm={userSearchTerm || ''}
|
|
307
302
|
/>
|
|
308
303
|
)
|
|
309
304
|
})
|
|
@@ -10,23 +10,31 @@
|
|
|
10
10
|
list-style: none;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
* {
|
|
14
|
+
font-family: var(--app-font-secondary) !important;
|
|
15
|
+
font-size: var(--filter-select-font-size) !important;
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
.search-input {
|
|
19
|
+
color: var(--cool-gray-90);
|
|
20
|
+
font-weight: 300;
|
|
21
|
+
|
|
14
22
|
border: none;
|
|
15
23
|
position: relative;
|
|
16
24
|
display: inline-block;
|
|
17
25
|
width: 100%;
|
|
18
26
|
padding: 0;
|
|
19
27
|
&::placeholder {
|
|
20
|
-
color: var(--
|
|
28
|
+
color: var(--cool-gray-90);
|
|
21
29
|
}
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
.main-nested-dropdown-container,
|
|
25
33
|
.nested-dropdown-input-container {
|
|
26
|
-
border: 1px solid var(--
|
|
34
|
+
border: 1px solid var(--cool-gray-10);
|
|
27
35
|
min-width: 200px;
|
|
28
36
|
cursor: pointer;
|
|
29
|
-
padding: 0.
|
|
37
|
+
padding: 0.4rem 0.75rem;
|
|
30
38
|
font-size: 1em;
|
|
31
39
|
}
|
|
32
40
|
|
|
@@ -53,7 +61,6 @@
|
|
|
53
61
|
border-radius: 0.25rem;
|
|
54
62
|
position: relative;
|
|
55
63
|
& > span.list-arrow {
|
|
56
|
-
color: var(--mediumGray);
|
|
57
64
|
position: absolute;
|
|
58
65
|
top: 9px;
|
|
59
66
|
right: 1px;
|
|
@@ -63,7 +70,6 @@
|
|
|
63
70
|
&.disabled {
|
|
64
71
|
background-color: var(--lightestGray);
|
|
65
72
|
& > :is(input) {
|
|
66
|
-
color: var(--darkGray);
|
|
67
73
|
background-color: var(--lightestGray);
|
|
68
74
|
}
|
|
69
75
|
}
|
|
@@ -22,7 +22,6 @@ type TableProps = {
|
|
|
22
22
|
}
|
|
23
23
|
wrapColumns?: boolean
|
|
24
24
|
hasRowType?: boolean // if it has row type then the first column is the row type which will explain how to render the row
|
|
25
|
-
fontSize: 'small' | 'medium' | 'large'
|
|
26
25
|
viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
27
26
|
preliminaryData?: PreliminaryDataItem[]
|
|
28
27
|
}
|
|
@@ -39,7 +38,6 @@ const Table = ({
|
|
|
39
38
|
tableOptions,
|
|
40
39
|
wrapColumns,
|
|
41
40
|
hasRowType,
|
|
42
|
-
fontSize,
|
|
43
41
|
viewport,
|
|
44
42
|
preliminaryData
|
|
45
43
|
}: TableProps) => {
|
|
@@ -71,7 +69,6 @@ const Table = ({
|
|
|
71
69
|
childRow={row}
|
|
72
70
|
wrapColumns={wrapColumns}
|
|
73
71
|
cellMinWidth={tableOptions.cellMinWidth}
|
|
74
|
-
fontSize={fontSize}
|
|
75
72
|
viewport={viewport}
|
|
76
73
|
/>
|
|
77
74
|
)
|
|
@@ -92,7 +89,6 @@ const Table = ({
|
|
|
92
89
|
childRow={childRow}
|
|
93
90
|
wrapColumns={wrapColumns}
|
|
94
91
|
cellMinWidth={tableOptions.cellMinWidth}
|
|
95
|
-
fontSize={fontSize}
|
|
96
92
|
viewport={viewport}
|
|
97
93
|
/>
|
|
98
94
|
)
|
|
@@ -110,7 +106,6 @@ const Table = ({
|
|
|
110
106
|
isTotal={true}
|
|
111
107
|
wrapColumns={wrapColumns}
|
|
112
108
|
cellMinWidth={tableOptions.cellMinWidth}
|
|
113
|
-
fontSize={fontSize}
|
|
114
109
|
viewport={viewport}
|
|
115
110
|
/>
|
|
116
111
|
)
|
|
@@ -125,7 +120,6 @@ const Table = ({
|
|
|
125
120
|
childRow={childRowCopy}
|
|
126
121
|
wrapColumns={wrapColumns}
|
|
127
122
|
cellMinWidth={tableOptions.cellMinWidth}
|
|
128
|
-
fontSize={fontSize}
|
|
129
123
|
viewport={viewport}
|
|
130
124
|
/>
|
|
131
125
|
)
|
|
@@ -8,18 +8,15 @@ type RowProps = {
|
|
|
8
8
|
wrapColumns: boolean
|
|
9
9
|
isTotal?: boolean
|
|
10
10
|
cellMinWidth?: number
|
|
11
|
-
fontSize: 'small' | 'medium' | 'large'
|
|
12
11
|
viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
13
12
|
style?: object
|
|
14
13
|
preliminaryData?: PreliminaryDataItem[]
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
const Row: FC<RowProps> = props => {
|
|
18
|
-
const { childRow, rowKey, wrapColumns, cellMinWidth = 0, isTotal,
|
|
17
|
+
const { childRow, rowKey, wrapColumns, cellMinWidth = 0, isTotal, viewport, preliminaryData } = props
|
|
19
18
|
const whiteSpace = wrapColumns ? 'unset' : 'nowrap'
|
|
20
19
|
const minWidth = cellMinWidth + 'px'
|
|
21
|
-
const fontSizes = { small: 16, medium: 18, large: 20 }
|
|
22
|
-
const cellFontSize = ['xs', 'xxs'].includes(viewport) ? '12px' : `${fontSizes[fontSize]}px`
|
|
23
20
|
|
|
24
21
|
return (
|
|
25
22
|
<tr>
|
|
@@ -34,7 +31,7 @@ const Row: FC<RowProps> = props => {
|
|
|
34
31
|
<Cell
|
|
35
32
|
ariaLabel={style?.color ? 'suppressed data' : ''}
|
|
36
33
|
key={rowKey + '__' + i}
|
|
37
|
-
style={{ whiteSpace, minWidth,
|
|
34
|
+
style={{ whiteSpace, minWidth, ...style }}
|
|
38
35
|
isBold={isTotal}
|
|
39
36
|
>
|
|
40
37
|
{child}
|