@cdc/waffle-chart 1.0.0 → 1.0.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/dist/cdcwafflechart.js +2 -2
- package/package.json +12 -14
- package/src/CdcWaffleChart.tsx +127 -125
- package/src/{context.js → ConfigContext.js} +0 -0
- package/src/components/EditorPanel.js +281 -350
- package/src/data/initial-state.js +8 -1
- package/src/scss/main.scss +1 -4
- package/src/scss/waffle-chart.scss +42 -69
- package/LICENSE +0 -201
- package/src/scss/editor-panel.scss +0 -710
- package/src/scss/responsive.scss +0 -1
- package/src/scss/variables.scss +0 -29
|
@@ -1,120 +1,31 @@
|
|
|
1
1
|
import React, { useState, useEffect, memo, useContext } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
Accordion,
|
|
5
|
-
AccordionItem,
|
|
6
|
-
AccordionItemHeading,
|
|
7
|
-
AccordionItemPanel,
|
|
8
|
-
AccordionItemButton,
|
|
9
|
-
} from 'react-accessible-accordion'
|
|
10
|
-
|
|
11
|
-
import { useDebounce } from 'use-debounce'
|
|
12
|
-
import Context from '../context'
|
|
13
3
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
14
|
-
import { DATA_OPERATORS, DATA_FUNCTIONS } from '../CdcWaffleChart'
|
|
15
|
-
|
|
16
|
-
const TextField = memo((
|
|
17
|
-
{
|
|
18
|
-
label,
|
|
19
|
-
section = null,
|
|
20
|
-
subsection = null,
|
|
21
|
-
fieldName,
|
|
22
|
-
updateField,
|
|
23
|
-
value: stateValue,
|
|
24
|
-
type = 'input',
|
|
25
|
-
i = null, min = null, max = null,
|
|
26
|
-
...attributes
|
|
27
|
-
}
|
|
28
|
-
) => {
|
|
29
|
-
|
|
30
|
-
const [ value, setValue ] = useState(stateValue)
|
|
31
|
-
const [ debouncedValue ] = useDebounce(value, 500)
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
35
|
-
updateField(section, subsection, fieldName, debouncedValue, i)
|
|
36
|
-
}
|
|
37
|
-
}, [ debouncedValue, section, subsection, fieldName, i, stateValue, updateField ])
|
|
38
|
-
|
|
39
|
-
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
|
|
40
|
-
|
|
41
|
-
const onChange = (e) => {
|
|
42
|
-
if ('number' !== type || min === null) {
|
|
43
|
-
setValue(e.target.value)
|
|
44
|
-
} else {
|
|
45
|
-
if (!e.target.value || (parseFloat(min) <= parseFloat(e.target.value) & parseFloat(max) >= parseFloat(e.target.value))) {
|
|
46
|
-
setValue(e.target.value)
|
|
47
|
-
} else {
|
|
48
|
-
setValue(min.toString())
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
4
|
|
|
53
|
-
|
|
5
|
+
import ConfigContext from '../ConfigContext'
|
|
54
6
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
7
|
+
import Accordion from '@cdc/core/components/ui/Accordion'
|
|
8
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
9
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
10
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
11
|
+
import InputText from '@cdc/core/components/inputs/InputText'
|
|
12
|
+
import InputSelect from '@cdc/core/components/inputs/InputSelect'
|
|
13
|
+
import InputCheckbox from '@cdc/core/components/inputs/InputCheckbox'
|
|
60
14
|
|
|
61
|
-
|
|
62
|
-
formElement = <input type="number" name={name} onChange={onChange} {...attributes} value={value}/>
|
|
63
|
-
}
|
|
15
|
+
import '@cdc/core/styles/v2/components/editor.scss'
|
|
64
16
|
|
|
65
|
-
|
|
66
|
-
<label>
|
|
67
|
-
{label && <span className="edit-label column-heading">{label}</span>}
|
|
68
|
-
{formElement}
|
|
69
|
-
</label>
|
|
70
|
-
)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
const Select = memo((
|
|
74
|
-
{
|
|
75
|
-
label,
|
|
76
|
-
value,
|
|
77
|
-
options,
|
|
78
|
-
fieldName,
|
|
79
|
-
section = null,
|
|
80
|
-
subsection = null,
|
|
81
|
-
required = false,
|
|
82
|
-
updateField,
|
|
83
|
-
initial: initialValue,
|
|
84
|
-
...attributes
|
|
85
|
-
}
|
|
86
|
-
) => {
|
|
87
|
-
|
|
88
|
-
let optionsJsx = ''
|
|
89
|
-
|
|
90
|
-
if (Array.isArray(options)) { //Handle basic array
|
|
91
|
-
optionsJsx = options.map(optionName => <option value={optionName} key={optionName}>{optionName}</option>)
|
|
92
|
-
} else { //Handle object with value/name pairs
|
|
93
|
-
optionsJsx = []
|
|
94
|
-
for (const [ optionValue, optionName ] of Object.entries(options)) {
|
|
95
|
-
optionsJsx.push(<option value={optionValue} key={optionValue}>{optionName}</option>)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (initialValue) {
|
|
100
|
-
optionsJsx.unshift(<option value="" key="initial">{initialValue}</option>)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<label>
|
|
105
|
-
{label && <span className="edit-label">{label}</span>}
|
|
106
|
-
<select className={required && !value ? 'warning' : ''} name={fieldName} value={value} onChange={(event) => {
|
|
107
|
-
updateField(section, subsection, fieldName, event.target.value)
|
|
108
|
-
}} {...attributes}>
|
|
109
|
-
{optionsJsx}
|
|
110
|
-
</select>
|
|
111
|
-
</label>
|
|
112
|
-
)
|
|
113
|
-
})
|
|
17
|
+
import { DATA_OPERATORS, DATA_FUNCTIONS } from '../CdcWaffleChart'
|
|
114
18
|
|
|
115
19
|
const headerColors = [ 'theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber' ]
|
|
116
20
|
|
|
117
|
-
const
|
|
21
|
+
const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
|
|
22
|
+
<label className="checkbox">
|
|
23
|
+
<input type="checkbox" name={fieldName} checked={value} onChange={() => { updateField(section, subsection, fieldName, !value) }} {...attributes} />
|
|
24
|
+
<span className="edit-label column-heading">{label}</span><span className="cove-icon">{tooltip}</span>
|
|
25
|
+
</label>
|
|
26
|
+
))
|
|
27
|
+
|
|
28
|
+
const EditorPanel = memo((props) => {
|
|
118
29
|
const {
|
|
119
30
|
config,
|
|
120
31
|
updateConfig,
|
|
@@ -122,13 +33,10 @@ const EditorPanel = memo(() => {
|
|
|
122
33
|
data,
|
|
123
34
|
setParentConfig,
|
|
124
35
|
isDashboard
|
|
125
|
-
} = useContext(
|
|
36
|
+
} = useContext(ConfigContext)
|
|
126
37
|
|
|
127
38
|
const [ displayPanel, setDisplayPanel ] = useState(true)
|
|
128
|
-
|
|
129
|
-
const enforceRestrictions = (updatedConfig) => {
|
|
130
|
-
//If there are any dependencies between fields, etc../
|
|
131
|
-
}
|
|
39
|
+
const [ showConfigConfirm, setShowConfigConfirm ] = useState(false)
|
|
132
40
|
|
|
133
41
|
const updateField = (section, subsection, fieldName, newValue) => {
|
|
134
42
|
// Top level
|
|
@@ -139,8 +47,6 @@ const EditorPanel = memo(() => {
|
|
|
139
47
|
updatedConfig.filterValue = ''
|
|
140
48
|
}
|
|
141
49
|
|
|
142
|
-
enforceRestrictions(updatedConfig)
|
|
143
|
-
|
|
144
50
|
updateConfig(updatedConfig)
|
|
145
51
|
return
|
|
146
52
|
}
|
|
@@ -159,29 +65,34 @@ const EditorPanel = memo(() => {
|
|
|
159
65
|
sectionValue = { ...config[section], [subsection]: { ...config[section][subsection], [fieldName]: newValue } }
|
|
160
66
|
}
|
|
161
67
|
}
|
|
162
|
-
|
|
163
68
|
let updatedConfig = { ...config, [section]: sectionValue }
|
|
164
69
|
|
|
165
|
-
enforceRestrictions(updatedConfig)
|
|
166
|
-
|
|
167
70
|
updateConfig(updatedConfig)
|
|
168
71
|
}
|
|
169
72
|
|
|
170
|
-
const missingRequiredSections = () => {
|
|
171
|
-
//Whether to show error message if something is required to show a data-bite and isn't filled in
|
|
172
|
-
return false
|
|
173
|
-
}
|
|
174
|
-
|
|
175
73
|
useEffect(() => {
|
|
74
|
+
|
|
75
|
+
console.log('updating parent')
|
|
76
|
+
console.log(setParentConfig)
|
|
176
77
|
// Pass up to Editor if needed
|
|
177
78
|
if (setParentConfig) {
|
|
178
79
|
const newConfig = convertStateToConfig()
|
|
179
80
|
|
|
81
|
+
console.log('newConfig', newConfig)
|
|
82
|
+
|
|
180
83
|
setParentConfig(newConfig)
|
|
181
84
|
}
|
|
182
85
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
183
86
|
}, [ config ])
|
|
184
87
|
|
|
88
|
+
useEffect(()=> {
|
|
89
|
+
if (!showConfigConfirm) {
|
|
90
|
+
let newConfig = { ...config }
|
|
91
|
+
delete newConfig.newViz
|
|
92
|
+
updateConfig(newConfig)
|
|
93
|
+
}
|
|
94
|
+
}, [])
|
|
95
|
+
|
|
185
96
|
useEffect(() => {
|
|
186
97
|
//Verify comparate data type
|
|
187
98
|
let operators = [ '<', '>', '<=', '>=' ]
|
|
@@ -199,11 +110,13 @@ const EditorPanel = memo(() => {
|
|
|
199
110
|
}, [ config.dataConditionalOperator, config.dataConditionalComparate ])
|
|
200
111
|
|
|
201
112
|
const onBackClick = () => {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
113
|
+
setDisplayPanel(!displayPanel)
|
|
114
|
+
|
|
115
|
+
// if (isDashboard) {
|
|
116
|
+
// updateConfig({ ...config, editing: false })
|
|
117
|
+
// } else {
|
|
118
|
+
// setDisplayPanel(!displayPanel)
|
|
119
|
+
// }
|
|
207
120
|
}
|
|
208
121
|
|
|
209
122
|
const Error = () => {
|
|
@@ -218,13 +131,10 @@ const EditorPanel = memo(() => {
|
|
|
218
131
|
}
|
|
219
132
|
|
|
220
133
|
const Confirm = () => {
|
|
221
|
-
|
|
222
134
|
const confirmDone = (e) => {
|
|
223
135
|
e.preventDefault()
|
|
224
|
-
|
|
225
|
-
let newConfig = {...config}
|
|
136
|
+
let newConfig = { ...config }
|
|
226
137
|
delete newConfig.newViz
|
|
227
|
-
|
|
228
138
|
updateConfig(newConfig)
|
|
229
139
|
}
|
|
230
140
|
|
|
@@ -233,7 +143,7 @@ const EditorPanel = memo(() => {
|
|
|
233
143
|
<section className="waiting-container">
|
|
234
144
|
<h3>Finish Configuring</h3>
|
|
235
145
|
<p>Set all required options to the left and confirm below to display a preview of the chart.</p>
|
|
236
|
-
<button className="btn" style={{ margin: '1em auto' }}
|
|
146
|
+
<button className="btn" style={{ margin: '1em auto' }} onClick={confirmDone}>I'm Done</button>
|
|
237
147
|
</section>
|
|
238
148
|
</section>
|
|
239
149
|
)
|
|
@@ -241,35 +151,27 @@ const EditorPanel = memo(() => {
|
|
|
241
151
|
|
|
242
152
|
const convertStateToConfig = () => {
|
|
243
153
|
let strippedState = JSON.parse(JSON.stringify(config))
|
|
244
|
-
|
|
245
|
-
delete strippedState.newViz
|
|
246
|
-
}
|
|
154
|
+
delete strippedState.newViz
|
|
247
155
|
delete strippedState.runtime
|
|
248
156
|
|
|
249
157
|
return strippedState
|
|
250
158
|
}
|
|
251
159
|
|
|
160
|
+
const addNewFilter = () => {
|
|
161
|
+
let filters = config.filters ? [ ...config.filters ] : []
|
|
162
|
+
filters.push({ values: [] })
|
|
163
|
+
updateConfig({ ...config, filters })
|
|
164
|
+
}
|
|
165
|
+
|
|
252
166
|
const removeFilter = (index) => {
|
|
253
167
|
let filters = [ ...config.filters ]
|
|
254
|
-
|
|
255
168
|
filters.splice(index, 1)
|
|
256
|
-
|
|
257
169
|
updateConfig({ ...config, filters })
|
|
258
170
|
}
|
|
259
171
|
|
|
260
172
|
const updateFilterProp = (name, index, value) => {
|
|
261
173
|
let filters = [ ...config.filters ]
|
|
262
|
-
|
|
263
174
|
filters[index][name] = value
|
|
264
|
-
|
|
265
|
-
updateConfig({ ...config, filters })
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const addNewFilter = () => {
|
|
269
|
-
let filters = config.filters ? [ ...config.filters ] : []
|
|
270
|
-
|
|
271
|
-
filters.push({ values: [] })
|
|
272
|
-
|
|
273
175
|
updateConfig({ ...config, filters })
|
|
274
176
|
}
|
|
275
177
|
|
|
@@ -295,214 +197,243 @@ const EditorPanel = memo(() => {
|
|
|
295
197
|
return filterDataOptions
|
|
296
198
|
}
|
|
297
199
|
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
200
|
+
const editorContent = (
|
|
201
|
+
|
|
202
|
+
<Accordion>
|
|
203
|
+
<Accordion.Section title="General">
|
|
204
|
+
<InputText value={config.title} fieldName="title" label="Title" placeholder="Waffle Chart Title"
|
|
205
|
+
updateField={updateField}/>
|
|
206
|
+
<InputText type="textarea" value={config.content} fieldName="content" label="Message" updateField={updateField} tooltip={
|
|
207
|
+
<Tooltip style={{textTransform: 'none'}}>
|
|
208
|
+
<Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
|
|
209
|
+
<Tooltip.Content>
|
|
210
|
+
<p>Enter the message text for the visualization. The following HTML tags are supported: strong, em, sup, and sub.</p>
|
|
211
|
+
</Tooltip.Content>
|
|
212
|
+
</Tooltip>
|
|
213
|
+
}/>
|
|
214
|
+
<InputText value={config.subtext} fieldName="subtext" label="Subtext/Citation"
|
|
215
|
+
placeholder="Waffle Chart Subtext or Citation" updateField={updateField} tooltip={
|
|
216
|
+
<Tooltip style={{textTransform: 'none'}}>
|
|
217
|
+
<Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
|
|
218
|
+
<Tooltip.Content>
|
|
219
|
+
<p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
|
|
220
|
+
</Tooltip.Content>
|
|
221
|
+
</Tooltip>
|
|
222
|
+
}/>
|
|
223
|
+
</Accordion.Section>
|
|
224
|
+
<Accordion.Section title="Data">
|
|
225
|
+
<h4 style={{ fontWeight: '600' }}>Numerator</h4>
|
|
226
|
+
<div className="cove-accordion__panel-section">
|
|
227
|
+
<div className="cove-input-group">
|
|
228
|
+
<InputSelect value={config.dataColumn || ''} fieldName="dataColumn" label="Data Column"
|
|
229
|
+
updateField={updateField} initial="Select" options={getColumns()}/>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div className="cove-input-group">
|
|
233
|
+
<InputSelect value={config.dataFunction || ''} fieldName="dataFunction" label="Data Function"
|
|
234
|
+
updateField={updateField} initial="Select" options={DATA_FUNCTIONS}/>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div className="cove-input-group">
|
|
238
|
+
<label><span className="edit-label">Data Conditional</span></label>
|
|
239
|
+
<div className="cove-accordion__panel-row cove-accordion__small-inputs">
|
|
240
|
+
<div className="cove-accordion__panel-col">
|
|
241
|
+
<InputSelect value={config.dataConditionalColumn || ''} fieldName="dataConditionalColumn"
|
|
242
|
+
updateField={updateField} initial="Select" options={getColumns()}/>
|
|
243
|
+
</div>
|
|
244
|
+
<div className="cove-accordion__panel-col">
|
|
245
|
+
<InputSelect value={config.dataConditionalOperator || ''} fieldName="dataConditionalOperator"
|
|
246
|
+
updateField={updateField} initial="Select" options={DATA_OPERATORS}/>
|
|
247
|
+
</div>
|
|
248
|
+
<div className="cove-accordion__panel-col">
|
|
249
|
+
<InputText value={config.dataConditionalComparate} fieldName={'dataConditionalComparate'}
|
|
250
|
+
updateField={updateField}
|
|
251
|
+
className={config.invalidComparate ? 'cove-accordion__input-error' : ''}
|
|
252
|
+
style={{minHeight: '2rem'}}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
{config.invalidComparate &&
|
|
259
|
+
<div className="cove-accordion__panel-error">
|
|
260
|
+
Non-numerical comparate values can only be used with = or ≠.
|
|
261
|
+
</div>
|
|
262
|
+
}
|
|
263
|
+
</div>
|
|
264
|
+
<div className="cove-accordion__panel-row align-center">
|
|
265
|
+
<div className="cove-accordion__panel-col">
|
|
266
|
+
<h4 style={{ fontWeight: '600' }}>Denominator</h4>
|
|
267
|
+
</div>
|
|
268
|
+
<div className="cove-accordion__panel-col">
|
|
269
|
+
<div style={{display: 'flex', justifyContent: 'flex-end'}}>
|
|
270
|
+
<label className="cove-accordion__panel-label--inline">Select from data</label>
|
|
271
|
+
<InputCheckbox size='small' value={config.customDenom} fieldName='customDenom' updateField={updateField}/>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
<div className="cove-accordion__panel-section">
|
|
276
|
+
{!config.customDenom &&
|
|
277
|
+
<div className="cove-accordion__panel-row align-center">
|
|
278
|
+
<div className="cove-accordion__panel-col">
|
|
279
|
+
<InputText value={config.dataDenom} fieldName="dataDenom" updateField={updateField}/>
|
|
280
|
+
</div>
|
|
281
|
+
<div className="cove-accordion__panel-col" style={{display: 'flex', alignItems: 'center'}}>
|
|
282
|
+
<label className="cove-accordion__panel-label--muted">default (100)</label>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
}
|
|
286
|
+
{config.customDenom &&
|
|
287
|
+
<>
|
|
288
|
+
<InputSelect value={config.dataDenomColumn || ''} fieldName="dataDenomColumn" label="Data Column"
|
|
289
|
+
updateField={updateField} initial="Select" options={getColumns()}/>
|
|
290
|
+
<InputSelect value={config.dataDenomFunction || ''} fieldName="dataDenomFunction" label="Data Function"
|
|
291
|
+
updateField={updateField} initial="Select" options={DATA_FUNCTIONS}/>
|
|
292
|
+
</>
|
|
293
|
+
}
|
|
294
|
+
</div>
|
|
295
|
+
<ul className="column-edit">
|
|
296
|
+
<li className="three-col">
|
|
297
|
+
<div style={{marginRight: '1rem'}}>
|
|
298
|
+
<InputText value={config.prefix} fieldName="prefix" label="Prefix" updateField={updateField}/>
|
|
299
|
+
</div>
|
|
300
|
+
<div style={{marginRight: '1rem'}}>
|
|
301
|
+
<InputText value={config.suffix} fieldName="suffix" label="Suffix" updateField={updateField}/>
|
|
302
|
+
</div>
|
|
303
|
+
<div>
|
|
304
|
+
<InputText type="number" value={config.roundToPlace} fieldName="roundToPlace" label="Round" updateField={updateField}/>
|
|
305
|
+
</div>
|
|
306
|
+
</li>
|
|
307
|
+
</ul>
|
|
308
|
+
|
|
309
|
+
<hr className="cove-accordion__divider" />
|
|
310
|
+
|
|
311
|
+
<label style={{marginBottom: '1rem'}}>
|
|
312
|
+
<span className="edit-label">Data Point Filters</span>
|
|
313
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
314
|
+
<Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
|
|
315
|
+
<Tooltip.Content>
|
|
316
|
+
<p>To refine the highlighted data point, specify one or more filters (e.g., "Male" and
|
|
317
|
+
"Female" for a column called "Sex").</p>
|
|
318
|
+
</Tooltip.Content>
|
|
319
|
+
</Tooltip>
|
|
320
|
+
</label>
|
|
321
|
+
{config.filters &&
|
|
322
|
+
<ul className="filters-list" style={{paddingLeft: 0, marginBottom: '1rem'}}>
|
|
323
|
+
{config.filters.map((filter, index) => (
|
|
324
|
+
<fieldset className="edit-block" key={index}>
|
|
325
|
+
<button type="button" className="remove-column" onClick={() => {
|
|
326
|
+
removeFilter(index)
|
|
327
|
+
}}>Remove
|
|
328
|
+
</button>
|
|
329
|
+
<label>
|
|
330
|
+
<span className="edit-label column-heading">Column</span>
|
|
331
|
+
<select value={filter.columnName} onChange={(e) => {
|
|
332
|
+
updateFilterProp('columnName', index, e.target.value)
|
|
333
|
+
}}>
|
|
334
|
+
<option value="">- Select Option -</option>
|
|
335
|
+
{getColumns().map((dataKey, index) => (
|
|
336
|
+
<option value={dataKey} key={index}>{dataKey}</option>
|
|
337
|
+
))}
|
|
338
|
+
</select>
|
|
339
|
+
</label>
|
|
340
|
+
<label>
|
|
341
|
+
<span className="edit-label column-heading">Column Value</span>
|
|
342
|
+
<select value={filter.columnValue} onChange={(e) => {
|
|
343
|
+
updateFilterProp('columnValue', index, e.target.value)
|
|
344
|
+
}}>
|
|
345
|
+
<option value="">- Select Option -</option>
|
|
346
|
+
{getFilterColumnValues(index).map((dataKey, index) => (
|
|
347
|
+
<option value={dataKey} key={index}>{dataKey}</option>
|
|
348
|
+
))}
|
|
349
|
+
</select>
|
|
350
|
+
</label>
|
|
351
|
+
</fieldset>
|
|
352
|
+
))}
|
|
353
|
+
</ul>
|
|
354
|
+
}
|
|
355
|
+
<Button onClick={addNewFilter} fluid>Add Filter</Button>
|
|
356
|
+
</Accordion.Section>
|
|
357
|
+
<Accordion.Section title="Visual">
|
|
358
|
+
<InputSelect value={config.shape} fieldName="shape" label="Shape"
|
|
359
|
+
updateField={updateField} options={[ 'circle', 'square', 'person' ]}/>
|
|
360
|
+
|
|
361
|
+
<div className="cove-accordion__panel-row cove-accordion__small-inputs" style={{marginTop: '1rem', marginBottom: '1rem'}}>
|
|
362
|
+
<div className="cove-accordion__panel-col">
|
|
363
|
+
<InputText type="number" value={config.nodeWidth} fieldName="nodeWidth" label="Width" updateField={updateField}/>
|
|
364
|
+
</div>
|
|
365
|
+
<div className="cove-accordion__panel-col">
|
|
366
|
+
<InputText type="number" value={config.nodeSpacer} fieldName="nodeSpacer" label="Spacer" updateField={updateField}/>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<div className="cove-input-group">
|
|
371
|
+
<InputSelect value={config.orientation} fieldName="orientation" label="Layout"
|
|
372
|
+
updateField={updateField} options={[ 'horizontal', 'vertical' ]}/>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
<div className="cove-input-group">
|
|
376
|
+
<label><span className="edit-label column-heading">Data Point Font Size</span></label>
|
|
377
|
+
<div className="cove-accordion__panel-row cove-accordion__small-inputs align-center">
|
|
378
|
+
<div className="cove-accordion__panel-col">
|
|
379
|
+
<InputText type="number" value={config.fontSize} fieldName="fontSize" updateField={updateField}/>
|
|
380
|
+
</div>
|
|
381
|
+
<div className="cove-accordion__panel-col" style={{display: 'flex', alignItems: 'center'}}>
|
|
382
|
+
<label className="accordion__panel-label--muted">default (50px)</label>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<InputSelect value={config.overallFontSize} fieldName="overallFontSize" label="Overall Font Size"
|
|
388
|
+
updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
|
|
389
|
+
|
|
390
|
+
<label>
|
|
391
|
+
<span className="edit-label">Theme</span>
|
|
392
|
+
<ul className="color-palette">
|
|
393
|
+
{headerColors.map((palette) => (
|
|
394
|
+
<li title={palette} key={palette} onClick={() => {
|
|
395
|
+
updateConfig({ ...config, theme: palette })
|
|
396
|
+
}} className={config.theme === palette ? 'selected ' + palette : palette}>
|
|
397
|
+
</li>
|
|
398
|
+
))}
|
|
399
|
+
</ul>
|
|
400
|
+
|
|
401
|
+
{/* <div className="cove-accordion__panel-section">
|
|
402
|
+
<CheckBox value={config.visual.border} section="visual" fieldName="border" label="Display Borders" updateField={updateField} />
|
|
403
|
+
<CheckBox value={config.visual.borderColorTheme} section="visual" fieldName="borderColorTheme" label="Use theme border color" updateField={updateField} />
|
|
404
|
+
<CheckBox value={config.visual.accent} section="visual" fieldName="accent" label="Use Accent Style" updateField={updateField} />
|
|
405
|
+
<CheckBox value={config.visual.background} section="visual" fieldName="background" label="Use theme background color" updateField={updateField} />
|
|
406
|
+
<CheckBox value={config.visual.hideBackgroundColor} section="visual" fieldName="hideBackgroundColor" label="Hide Background Color" updateField={updateField} />
|
|
407
|
+
</div> */}
|
|
408
|
+
|
|
409
|
+
</label>
|
|
410
|
+
</Accordion.Section>
|
|
411
|
+
</Accordion>
|
|
412
|
+
)
|
|
302
413
|
|
|
303
|
-
if (loading)
|
|
304
|
-
return null
|
|
305
|
-
}
|
|
414
|
+
if (loading) return null
|
|
306
415
|
|
|
307
416
|
return (
|
|
308
417
|
<ErrorBoundary component="EditorPanel">
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
<
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
General
|
|
322
|
-
</AccordionItemButton>
|
|
323
|
-
</AccordionItemHeading>
|
|
324
|
-
<AccordionItemPanel>
|
|
325
|
-
<TextField value={config.title} fieldName="title" label="Title" placeholder="Waffle Chart Title"
|
|
326
|
-
updateField={updateField}/>
|
|
327
|
-
<TextField type="textarea" value={config.content} fieldName="content" label="Message"
|
|
328
|
-
updateField={updateField}/>
|
|
329
|
-
<TextField value={config.subtext} fieldName="subtext" label="Subtext/Citation"
|
|
330
|
-
placeholder="Waffle Chart Subtext or Citation" updateField={updateField}/>
|
|
331
|
-
</AccordionItemPanel>
|
|
332
|
-
</AccordionItem>
|
|
333
|
-
<AccordionItem>
|
|
334
|
-
<AccordionItemHeading>
|
|
335
|
-
<AccordionItemButton>
|
|
336
|
-
Data
|
|
337
|
-
</AccordionItemButton>
|
|
338
|
-
</AccordionItemHeading>
|
|
339
|
-
<AccordionItemPanel>
|
|
340
|
-
<h4 style={{ fontWeight: '600' }}>Numerator</h4>
|
|
341
|
-
<div className="accordion__panel-section">
|
|
342
|
-
<Select value={config.dataColumn || ''} fieldName="dataColumn" label="Data Column"
|
|
343
|
-
updateField={updateField} initial="Select" options={getColumns()}/>
|
|
344
|
-
<Select value={config.dataFunction || ''} fieldName="dataFunction" label="Data Function"
|
|
345
|
-
updateField={updateField} initial="Select" options={DATA_FUNCTIONS}/>
|
|
346
|
-
<label><span className="edit-label">Data Conditional</span></label>
|
|
347
|
-
<div className="accordion__panel-row accordion__small-inputs">
|
|
348
|
-
<div className="accordion__panel-col">
|
|
349
|
-
<Select value={config.dataConditionalColumn || ''} fieldName="dataConditionalColumn"
|
|
350
|
-
updateField={updateField} initial="Select" options={getColumns()}/>
|
|
351
|
-
</div>
|
|
352
|
-
<div className="accordion__panel-col">
|
|
353
|
-
<Select value={config.dataConditionalOperator || ''} fieldName="dataConditionalOperator"
|
|
354
|
-
updateField={updateField} initial="Select" options={DATA_OPERATORS}/>
|
|
355
|
-
</div>
|
|
356
|
-
<div className="accordion__panel-col">
|
|
357
|
-
<TextField value={config.dataConditionalComparate} fieldName={'dataConditionalComparate'}
|
|
358
|
-
updateField={updateField}
|
|
359
|
-
className={config.invalidComparate ? 'accordion__input-error' : ''}/>
|
|
360
|
-
</div>
|
|
361
|
-
</div>
|
|
362
|
-
{config.invalidComparate &&
|
|
363
|
-
<div className="accordion__panel-error">Non-numerical comparate values can only be used with = or
|
|
364
|
-
≠.</div>
|
|
365
|
-
}
|
|
366
|
-
</div>
|
|
367
|
-
|
|
368
|
-
<div className="accordion__panel-row align-center">
|
|
369
|
-
<div className="accordion__panel-col">
|
|
370
|
-
<h4 style={{ fontWeight: '600' }}>Denominator</h4>
|
|
371
|
-
</div>
|
|
372
|
-
<div className="accordion__panel-col">
|
|
373
|
-
<div className="d-flex justify-end">
|
|
374
|
-
<label className={'accordion__panel-label--inline'}>Select from data</label>
|
|
375
|
-
<div className={`accordion__panel-checkbox${config.customDenom ? ' checked' : ''}`}
|
|
376
|
-
onClick={() => toggleCustomDenom()}/>
|
|
377
|
-
</div>
|
|
378
|
-
</div>
|
|
379
|
-
</div>
|
|
380
|
-
<div className="accordion__panel-section">
|
|
381
|
-
{!config.customDenom &&
|
|
382
|
-
<div className="accordion__panel-row align-center">
|
|
383
|
-
<div className="accordion__panel-col">
|
|
384
|
-
<TextField value={config.dataDenom} fieldName="dataDenom" updateField={updateField}/>
|
|
385
|
-
</div>
|
|
386
|
-
<div className="accordion__panel-col">
|
|
387
|
-
<label className={'accordion__panel-label--muted'}>default (100)</label>
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
}
|
|
391
|
-
{config.customDenom &&
|
|
392
|
-
<>
|
|
393
|
-
<Select value={config.dataDenomColumn || ''} fieldName="dataDenomColumn" label="Data Column"
|
|
394
|
-
updateField={updateField} initial="Select" options={getColumns()}/>
|
|
395
|
-
<Select value={config.dataDenomFunction || ''} fieldName="dataDenomFunction" label="Data Function"
|
|
396
|
-
updateField={updateField} initial="Select" options={DATA_FUNCTIONS}/>
|
|
397
|
-
</>
|
|
398
|
-
}
|
|
399
|
-
</div>
|
|
400
|
-
<ul className="column-edit">
|
|
401
|
-
<li className="three-col">
|
|
402
|
-
<TextField value={config.prefix} fieldName="prefix" label="Prefix" updateField={updateField}/>
|
|
403
|
-
<TextField value={config.suffix} fieldName="suffix" label="Suffix" updateField={updateField}/>
|
|
404
|
-
<TextField type="number" value={config.roundToPlace} fieldName="roundToPlace" label="Round"
|
|
405
|
-
updateField={updateField}/>
|
|
406
|
-
</li>
|
|
407
|
-
</ul>
|
|
408
|
-
</AccordionItemPanel>
|
|
409
|
-
</AccordionItem>
|
|
410
|
-
<AccordionItem>
|
|
411
|
-
<AccordionItemHeading>
|
|
412
|
-
<AccordionItemButton>
|
|
413
|
-
Filters
|
|
414
|
-
</AccordionItemButton>
|
|
415
|
-
</AccordionItemHeading>
|
|
416
|
-
<AccordionItemPanel>
|
|
417
|
-
<ul className="filters-list">
|
|
418
|
-
{config.filters && config.filters.map((filter, index) => (
|
|
419
|
-
<fieldset className="edit-block" key={index}>
|
|
420
|
-
<button type="button" className="remove-column" onClick={() => {
|
|
421
|
-
removeFilter(index)
|
|
422
|
-
}}>Remove
|
|
423
|
-
</button>
|
|
424
|
-
<label>
|
|
425
|
-
<span className="edit-label column-heading">Column</span>
|
|
426
|
-
<select value={filter.columnName} onChange={(e) => {
|
|
427
|
-
updateFilterProp('columnName', index, e.target.value)
|
|
428
|
-
}}>
|
|
429
|
-
<option value="">- Select Option -</option>
|
|
430
|
-
{getColumns().map((dataKey, index) => (
|
|
431
|
-
<option value={dataKey} key={index}>{dataKey}</option>
|
|
432
|
-
))}
|
|
433
|
-
</select>
|
|
434
|
-
</label>
|
|
435
|
-
<label>
|
|
436
|
-
<span className="edit-label column-heading">Column Value</span>
|
|
437
|
-
<select value={filter.columnValue} onChange={(e) => {
|
|
438
|
-
updateFilterProp('columnValue', index, e.target.value)
|
|
439
|
-
}}>
|
|
440
|
-
<option value="">- Select Option -</option>
|
|
441
|
-
{getFilterColumnValues(index).map((dataKey, index) => (
|
|
442
|
-
<option value={dataKey} key={index}>{dataKey}</option>
|
|
443
|
-
))}
|
|
444
|
-
</select>
|
|
445
|
-
</label>
|
|
446
|
-
</fieldset>
|
|
447
|
-
)
|
|
448
|
-
)}
|
|
449
|
-
</ul>
|
|
450
|
-
|
|
451
|
-
<button type="button" onClick={addNewFilter} className="btn btn-primary">Add Filter</button>
|
|
452
|
-
</AccordionItemPanel>
|
|
453
|
-
</AccordionItem>
|
|
454
|
-
<AccordionItem>
|
|
455
|
-
<AccordionItemHeading>
|
|
456
|
-
<AccordionItemButton>
|
|
457
|
-
Visual
|
|
458
|
-
</AccordionItemButton>
|
|
459
|
-
</AccordionItemHeading>
|
|
460
|
-
<AccordionItemPanel>
|
|
461
|
-
<Select value={config.shape} fieldName="shape" label="Shape"
|
|
462
|
-
updateField={updateField} options={[ 'circle', 'square', 'person' ]}/>
|
|
463
|
-
|
|
464
|
-
<div className="accordion__panel-row accordion__small-inputs" style={{marginTop: '1em'}}>
|
|
465
|
-
<div className="accordion__panel-col">
|
|
466
|
-
<TextField type="number" value={config.nodeWidth} fieldName="nodeWidth" label="Width" updateField={updateField}/>
|
|
467
|
-
</div>
|
|
468
|
-
<div className="accordion__panel-col">
|
|
469
|
-
<TextField type="number" value={config.nodeSpacer} fieldName="nodeSpacer" label="Spacer" updateField={updateField}/>
|
|
470
|
-
</div>
|
|
471
|
-
</div>
|
|
472
|
-
|
|
473
|
-
<Select value={config.orientation} fieldName="orientation" label="Layout"
|
|
474
|
-
updateField={updateField} options={[ 'horizontal', 'vertical' ]}/>
|
|
475
|
-
|
|
476
|
-
<label><span className="edit-label column-heading">Data Point Font Size</span></label>
|
|
477
|
-
<div className="accordion__panel-row accordion__small-inputs align-center">
|
|
478
|
-
<div className="accordion__panel-col">
|
|
479
|
-
<TextField type="number" value={config.fontSize} fieldName="fontSize" updateField={updateField}/>
|
|
480
|
-
</div>
|
|
481
|
-
<div className="accordion__panel-col">
|
|
482
|
-
<label className={'accordion__panel-label--muted'}>default (50px)</label>
|
|
483
|
-
</div>
|
|
484
|
-
</div>
|
|
485
|
-
|
|
486
|
-
<Select value={config.overallFontSize} fieldName="overallFontSize" label="Overall Font Size"
|
|
487
|
-
updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
|
|
488
|
-
|
|
489
|
-
<label className="header">
|
|
490
|
-
<span className="edit-label">Theme</span>
|
|
491
|
-
<ul className="color-palette">
|
|
492
|
-
{headerColors.map((palette) => (
|
|
493
|
-
<li title={palette} key={palette} onClick={() => {
|
|
494
|
-
updateConfig({ ...config, theme: palette })
|
|
495
|
-
}} className={config.theme === palette ? 'selected ' + palette : palette}>
|
|
496
|
-
</li>
|
|
497
|
-
))}
|
|
498
|
-
</ul>
|
|
499
|
-
</label>
|
|
500
|
-
</AccordionItemPanel>
|
|
501
|
-
</AccordionItem>
|
|
502
|
-
</Accordion>
|
|
503
|
-
</form>
|
|
418
|
+
<div className="cove-editor">
|
|
419
|
+
{!config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error/>}
|
|
420
|
+
{config.newViz && showConfigConfirm && <Confirm/>}
|
|
421
|
+
<button className={`cove-editor--toggle` + (!displayPanel ? ` collapsed` : ``)}
|
|
422
|
+
title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick}/>
|
|
423
|
+
<section className={`cove-editor__panel` + (displayPanel ? `` : ' hidden')}>
|
|
424
|
+
<div className="cove-editor__panel-container">
|
|
425
|
+
<h2 className="cove-editor__heading">Configure Waffle Chart</h2>
|
|
426
|
+
<section className="cove-editor__content">
|
|
427
|
+
{editorContent}
|
|
428
|
+
</section>
|
|
429
|
+
</div>
|
|
504
430
|
</section>
|
|
505
|
-
|
|
431
|
+
<div className="cove-editor__content">
|
|
432
|
+
<div className="cove-editor__content-wrap">
|
|
433
|
+
{props.children}
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
506
437
|
</ErrorBoundary>
|
|
507
438
|
)
|
|
508
439
|
})
|