@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
|
@@ -5,25 +5,41 @@ import LoadSpin from '../ui/LoadSpin'
|
|
|
5
5
|
|
|
6
6
|
import '../../styles/v2/components/button.scss'
|
|
7
7
|
|
|
8
|
-
const Button = ({
|
|
8
|
+
const Button = ({
|
|
9
|
+
style,
|
|
10
|
+
role,
|
|
11
|
+
hoverStyle = {},
|
|
12
|
+
fluid = false,
|
|
13
|
+
loading = false,
|
|
14
|
+
loadingText = 'Loading...',
|
|
15
|
+
flexCenter,
|
|
16
|
+
active = false,
|
|
17
|
+
onClick,
|
|
18
|
+
children,
|
|
19
|
+
...attributes
|
|
20
|
+
}) => {
|
|
9
21
|
const buttonRef = useRef(null)
|
|
10
22
|
|
|
11
|
-
const [
|
|
12
|
-
const [
|
|
13
|
-
const [
|
|
14
|
-
const [
|
|
23
|
+
const [buttonState, setButtonState] = useState('out')
|
|
24
|
+
const [customStyles, setCustomStyles] = useState({ ...style })
|
|
25
|
+
const [childrenWidth, setChildrenWidth] = useState()
|
|
26
|
+
const [loadtextWidth, setLoadtextWidth] = useState()
|
|
15
27
|
|
|
16
28
|
const attributesObj = {
|
|
17
29
|
...attributes,
|
|
18
30
|
style: customStyles,
|
|
19
|
-
className:
|
|
31
|
+
className:
|
|
32
|
+
'cove-button' +
|
|
33
|
+
(flexCenter || 'loader' === role ? ' cove-button--flex-center' : '') +
|
|
34
|
+
(fluid ? ' fluid' : '') +
|
|
35
|
+
(loading ? ' loading' : '') +
|
|
36
|
+
(attributes.className ? ' ' + attributes.className : ''),
|
|
20
37
|
onMouseOver: () => setButtonState('in'),
|
|
21
38
|
onMouseOut: () => setButtonState('out'),
|
|
22
39
|
onFocus: () => setButtonState('in'),
|
|
23
40
|
onBlur: () => setButtonState('out')
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
|
|
27
43
|
useEffect(() => {
|
|
28
44
|
if ('loader' === role && buttonRef.current) {
|
|
29
45
|
//Create ghost object and text nodes for children
|
|
@@ -54,9 +70,8 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
|
|
|
54
70
|
buttonRef.current.parentNode.removeChild(ghostSpan)
|
|
55
71
|
buttonRef.current.parentNode.removeChild(ghostLoaderSpan)
|
|
56
72
|
}
|
|
57
|
-
return () => {
|
|
58
|
-
|
|
59
|
-
}, [ buttonRef, children, loadingText, role ])
|
|
73
|
+
return () => {}
|
|
74
|
+
}, [buttonRef, children, loadingText, role])
|
|
60
75
|
|
|
61
76
|
useEffect(() => {
|
|
62
77
|
//Adjust button styles depending on cursor, focus, and active, states
|
|
@@ -64,9 +79,10 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
|
|
|
64
79
|
|
|
65
80
|
// If button state is out, check if its 'active'; we want to keep hover styles applied to 'active' buttons
|
|
66
81
|
if (buttonState === 'out')
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
if (!active)
|
|
83
|
+
// If button state is out, and not 'active', reset display styles back to default
|
|
84
|
+
setCustomStyles({ ...style })
|
|
85
|
+
}, [buttonState, active, style])
|
|
70
86
|
|
|
71
87
|
return (
|
|
72
88
|
<button
|
|
@@ -82,16 +98,19 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
|
|
|
82
98
|
<>
|
|
83
99
|
{'loader' === role && (
|
|
84
100
|
<>
|
|
85
|
-
<span
|
|
86
|
-
|
|
101
|
+
<span
|
|
102
|
+
className='cove-button__text'
|
|
103
|
+
style={loading ? { width: loadtextWidth + 'px' } : { width: childrenWidth + 'px' }}
|
|
104
|
+
>
|
|
105
|
+
<div className='cove-button__text--loading' style={loading ? { opacity: 1 } : null}>
|
|
87
106
|
{loadingText}
|
|
88
107
|
</div>
|
|
89
|
-
<div className=
|
|
108
|
+
<div className='cove-button__text--children' style={loading ? { opacity: 0 } : null}>
|
|
90
109
|
{children}
|
|
91
110
|
</div>
|
|
92
111
|
</span>
|
|
93
|
-
<div className=
|
|
94
|
-
<LoadSpin className=
|
|
112
|
+
<div className='cove-button__load-spin' style={loading ? { width: '28px', opacity: 1 } : null}>
|
|
113
|
+
<LoadSpin className='ms-1' size={20} />
|
|
95
114
|
</div>
|
|
96
115
|
</>
|
|
97
116
|
)}
|
|
@@ -104,7 +123,7 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
|
|
|
104
123
|
|
|
105
124
|
Button.propTypes = {
|
|
106
125
|
/** Specify special role type for button */
|
|
107
|
-
role: PropTypes.oneOf([
|
|
126
|
+
role: PropTypes.oneOf(['loader']),
|
|
108
127
|
/** Provide object with styles that overwrite base styles when hovered */
|
|
109
128
|
hoverStyle: PropTypes.object,
|
|
110
129
|
/** Enables button to stretch to the full width of the content */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Button from './Button.jsx'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { missingRequiredSections } from '../../helpers/missingRequiredSections.js'
|
|
4
|
+
const Confirm = props => {
|
|
5
|
+
const { updateConfig, config } = props
|
|
6
|
+
const confirmDone = e => {
|
|
7
|
+
if (e) {
|
|
8
|
+
e.preventDefault()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let newConfig = { ...config }
|
|
12
|
+
delete newConfig.newViz
|
|
13
|
+
|
|
14
|
+
updateConfig(newConfig)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const styles: React.CSSProperties = {
|
|
18
|
+
position: 'relative',
|
|
19
|
+
height: '100vh',
|
|
20
|
+
width: '100%',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
justifyContent: 'center',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
gridArea: 'content'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<section className='waiting' style={styles}>
|
|
29
|
+
<section className='waiting-container'>
|
|
30
|
+
<h3>Finish Configuring</h3>
|
|
31
|
+
<p>Set all required options to the left and confirm below to display a preview of the chart.</p>
|
|
32
|
+
<Button
|
|
33
|
+
className='btn btn-primary'
|
|
34
|
+
style={{ margin: '1em auto' }}
|
|
35
|
+
disabled={missingRequiredSections(config)}
|
|
36
|
+
onClick={e => confirmDone(e)}
|
|
37
|
+
>
|
|
38
|
+
I'm Done
|
|
39
|
+
</Button>
|
|
40
|
+
</section>
|
|
41
|
+
</section>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default Confirm
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
const Error = ({ errorMessage }) => {
|
|
3
|
+
const styles: React.CSSProperties = {
|
|
4
|
+
position: 'absolute',
|
|
5
|
+
background: 'white',
|
|
6
|
+
zIndex: '999',
|
|
7
|
+
height: '100vh',
|
|
8
|
+
width: '100%',
|
|
9
|
+
display: 'flex',
|
|
10
|
+
justifyContent: 'center',
|
|
11
|
+
alignItems: 'center',
|
|
12
|
+
gridArea: 'content'
|
|
13
|
+
}
|
|
14
|
+
return (
|
|
15
|
+
<section className='waiting' style={styles}>
|
|
16
|
+
<section className='waiting-container'>
|
|
17
|
+
<h3>Error With Configuration</h3>
|
|
18
|
+
<p>{errorMessage}</p>
|
|
19
|
+
</section>
|
|
20
|
+
</section>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default Error
|
|
@@ -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 &&
|