@cdc/chart 4.23.4 → 4.23.6
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/cdcchart.js +54845 -51755
- package/examples/feature/__data__/planet-example-data.json +14 -32
- package/examples/feature/__data__/planet-logaritmic-data.json +56 -0
- package/examples/feature/area/area-chart-category.json +240 -0
- package/examples/feature/bar/example-bar-chart.json +544 -22
- package/examples/feature/bar/new.json +561 -0
- package/examples/feature/bar/planet-chart-logaritmic-config.json +170 -0
- package/examples/feature/boxplot/valid-boxplot.csv +17 -0
- package/examples/feature/combo/right-issues.json +190 -0
- package/examples/feature/filters/filter-testing.json +37 -3
- package/examples/feature/forecasting/combo-forecasting.json +245 -0
- package/examples/feature/forecasting/forecasting.json +5325 -0
- package/examples/feature/forecasting/index.json +203 -0
- package/examples/feature/forecasting/random_data.csv +366 -0
- package/examples/feature/line/line-chart.json +3 -3
- package/examples/feature/test-highlight/test-highlight-2.json +789 -0
- package/examples/feature/test-highlight/test-highlight-vertical.json +561 -0
- package/examples/feature/test-highlight/test-highlight.json +100 -0
- package/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json +1 -2
- package/examples/gallery/bar-chart-horizontal/horizontal-highlight.json +345 -0
- package/examples/gallery/line/line.json +173 -1
- package/index.html +14 -8
- package/package.json +2 -2
- package/src/CdcChart.jsx +342 -25
- package/src/components/AreaChart.jsx +32 -40
- package/src/components/BarChart.jsx +147 -25
- package/src/components/DataTable.jsx +30 -12
- package/src/components/DeviationBar.jsx +32 -32
- package/src/components/EditorPanel.jsx +1902 -1126
- package/src/components/Forecasting.jsx +147 -0
- package/src/components/Legend.jsx +193 -243
- package/src/components/LineChart.jsx +4 -9
- package/src/components/LinearChart.jsx +263 -285
- package/src/components/Series.jsx +518 -0
- package/src/components/SparkLine.jsx +3 -3
- package/src/data/initial-state.js +24 -5
- package/src/hooks/useHighlightedBars.js +154 -0
- package/src/hooks/useMinMax.js +128 -0
- package/src/hooks/useReduceData.js +31 -57
- package/src/hooks/useRightAxis.js +8 -2
- package/src/hooks/useScales.js +196 -0
- /package/examples/feature/area/{area-chart.json → area-chart-date.json} +0 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../ConfigContext'
|
|
3
|
+
|
|
4
|
+
// Core
|
|
5
|
+
import InputSelect from '@cdc/core/components/inputs/InputSelect'
|
|
6
|
+
import Check from '@cdc/core/assets/icon-check.svg'
|
|
7
|
+
|
|
8
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
9
|
+
|
|
10
|
+
// Third Party
|
|
11
|
+
import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
12
|
+
import { Draggable } from '@hello-pangea/dnd'
|
|
13
|
+
import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
14
|
+
|
|
15
|
+
const SeriesContext = React.createContext()
|
|
16
|
+
|
|
17
|
+
const SeriesWrapper = props => {
|
|
18
|
+
const { updateConfig, config } = useContext(ConfigContext)
|
|
19
|
+
|
|
20
|
+
const { getColumns, selectComponent } = props
|
|
21
|
+
|
|
22
|
+
const supportedRightAxisTypes = ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg']
|
|
23
|
+
|
|
24
|
+
const updateSeries = (index, value, property) => {
|
|
25
|
+
let series = [...config.series]
|
|
26
|
+
series[index][property] = value
|
|
27
|
+
|
|
28
|
+
updateConfig({ ...config, series })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return <SeriesContext.Provider value={{ updateSeries, supportedRightAxisTypes, getColumns, selectComponent }}>{props.children}</SeriesContext.Provider>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SeriesDropdownLineType = props => {
|
|
35
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
36
|
+
const { series, index } = props
|
|
37
|
+
|
|
38
|
+
// Run a quick test to determine if we should even show this.
|
|
39
|
+
const supportsLineType = ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg', 'Area Chart'].some(item => item.includes(series.type))
|
|
40
|
+
|
|
41
|
+
if (!supportsLineType) return
|
|
42
|
+
|
|
43
|
+
const changeLineType = (i, value) => {
|
|
44
|
+
let series = [...config.series]
|
|
45
|
+
series[i].lineType = value
|
|
46
|
+
updateConfig({ ...config, series })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const approvedCurveTypes = {
|
|
50
|
+
Linear: 'curveLinear',
|
|
51
|
+
Cardinal: 'curveCardinal',
|
|
52
|
+
Natural: 'curveNatural',
|
|
53
|
+
'Monotone X': 'curveMonotoneX',
|
|
54
|
+
Step: 'curveStep'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let options = []
|
|
58
|
+
|
|
59
|
+
Object.keys(approvedCurveTypes).map(curveName => {
|
|
60
|
+
return options.push(approvedCurveTypes[curveName])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<InputSelect
|
|
65
|
+
initial='Select an option'
|
|
66
|
+
value={series.lineType ? series.lineType : 'curveLinear'}
|
|
67
|
+
label='Series Line Type'
|
|
68
|
+
onChange={event => {
|
|
69
|
+
changeLineType(index, event.target.value)
|
|
70
|
+
}}
|
|
71
|
+
options={options}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const SeriesDropdownSeriesType = props => {
|
|
77
|
+
const { config } = useContext(ConfigContext)
|
|
78
|
+
const { updateSeries } = useContext(SeriesContext)
|
|
79
|
+
|
|
80
|
+
const { index, series } = props
|
|
81
|
+
|
|
82
|
+
const getOptions = () => {
|
|
83
|
+
if (config.visualizationType === 'Combo') {
|
|
84
|
+
return {
|
|
85
|
+
Bar: 'Bar',
|
|
86
|
+
Line: 'Line',
|
|
87
|
+
'dashed-sm': 'Small Dashed',
|
|
88
|
+
'dashed-md': 'Medium Dashed',
|
|
89
|
+
'dashed-lg': 'Large Dashed',
|
|
90
|
+
'Area Chart': 'Area Chart',
|
|
91
|
+
Forecasting: 'Forecasting'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (config.visualizationType === 'Line') {
|
|
95
|
+
return {
|
|
96
|
+
Line: 'Line',
|
|
97
|
+
'dashed-sm': 'Small Dashed',
|
|
98
|
+
'dashed-md': 'Medium Dashed',
|
|
99
|
+
'dashed-lg': 'Large Dashed'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Allowable changes
|
|
105
|
+
if (!['Line', 'Combo'].includes(config.visualizationType)) return
|
|
106
|
+
return (
|
|
107
|
+
<InputSelect
|
|
108
|
+
initial='Select an option'
|
|
109
|
+
value={series.type}
|
|
110
|
+
label='Series Type'
|
|
111
|
+
onChange={event => {
|
|
112
|
+
updateSeries(index, event.target.value, 'type')
|
|
113
|
+
}}
|
|
114
|
+
options={getOptions()}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const SeriesDropdownForecastingStage = props => {
|
|
120
|
+
const { config, updateConfig, rawData } = useContext(ConfigContext)
|
|
121
|
+
const { updateSeries, getColumns } = useContext(SeriesContext)
|
|
122
|
+
|
|
123
|
+
const { index, series } = props
|
|
124
|
+
|
|
125
|
+
// Only combo charts are allowed to have different options
|
|
126
|
+
if (series.type !== 'Forecasting') return
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<InputSelect
|
|
130
|
+
initial='Select an option'
|
|
131
|
+
value={series.stageColumn}
|
|
132
|
+
label='Add Forecasting Stages'
|
|
133
|
+
onChange={e => {
|
|
134
|
+
let stageObjects = []
|
|
135
|
+
let tempGroups = new Set(rawData?.map(item => item[e.target.value])) // [estimate, forecast, etc.]
|
|
136
|
+
tempGroups = Array.from(tempGroups) // convert set to array
|
|
137
|
+
|
|
138
|
+
tempGroups = tempGroups.filter(group => group !== undefined) // removes undefined
|
|
139
|
+
|
|
140
|
+
tempGroups.forEach(group => stageObjects.push({ key: group }))
|
|
141
|
+
|
|
142
|
+
const copyOfSeries = [...config.series] // copy the entire series array
|
|
143
|
+
copyOfSeries[index] = { ...copyOfSeries[index], stages: stageObjects, stageColumn: e.target.value }
|
|
144
|
+
|
|
145
|
+
updateConfig({
|
|
146
|
+
...config,
|
|
147
|
+
series: copyOfSeries
|
|
148
|
+
})
|
|
149
|
+
}}
|
|
150
|
+
options={getColumns(false)}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const SeriesDropdownForecastingColumn = props => {
|
|
156
|
+
const { config, rawData } = useContext(ConfigContext)
|
|
157
|
+
const { updateSeries } = useContext(SeriesContext)
|
|
158
|
+
|
|
159
|
+
const { index, series } = props
|
|
160
|
+
|
|
161
|
+
// Only combo charts are allowed to have different options
|
|
162
|
+
if (series.type !== 'Forecasting') return
|
|
163
|
+
if (!rawData) return
|
|
164
|
+
if (!series.stageColumn) return
|
|
165
|
+
|
|
166
|
+
let tempGroups = new Set(rawData.map(item => item[series.stageColumn])) // [estimate, forecast, etc.]
|
|
167
|
+
tempGroups = Array.from(tempGroups) // convert set to array
|
|
168
|
+
|
|
169
|
+
tempGroups = tempGroups.filter(group => group !== undefined) // removes undefined
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<InputSelect
|
|
173
|
+
initial='Select an option'
|
|
174
|
+
value={series.stageItem}
|
|
175
|
+
label='Forecasting Item Column'
|
|
176
|
+
onChange={event => {
|
|
177
|
+
updateSeries(index, event.target.value, 'stageItem')
|
|
178
|
+
}}
|
|
179
|
+
options={tempGroups}
|
|
180
|
+
/>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const SeriesDropdownAxisPosition = props => {
|
|
185
|
+
const { config } = useContext(ConfigContext)
|
|
186
|
+
const { updateSeries, supportedRightAxisTypes } = useContext(SeriesContext)
|
|
187
|
+
|
|
188
|
+
const { index, series } = props
|
|
189
|
+
|
|
190
|
+
// Hide AxisPositionDropdown in certain cases.
|
|
191
|
+
if (config.visualizationType !== 'Combo' || !series) return
|
|
192
|
+
if (!supportedRightAxisTypes.includes(series.type)) {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
return (
|
|
196
|
+
<InputSelect
|
|
197
|
+
initial='Select an option'
|
|
198
|
+
value={series.axis ? series.axis : 'Left'}
|
|
199
|
+
label='Series Axis'
|
|
200
|
+
onChange={event => {
|
|
201
|
+
updateSeries(index, event.target.value, 'axis')
|
|
202
|
+
}}
|
|
203
|
+
options={{
|
|
204
|
+
['Left']: 'Left',
|
|
205
|
+
['Right']: 'Right'
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const SeriesDropdownForecastColor = props => {
|
|
212
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
213
|
+
|
|
214
|
+
const { index, series } = props
|
|
215
|
+
|
|
216
|
+
if (series.type !== 'Forecasting') return
|
|
217
|
+
|
|
218
|
+
// Hide AxisPositionDropdown in certain cases.
|
|
219
|
+
if (!series) return
|
|
220
|
+
|
|
221
|
+
return series?.stages?.map((stage, stageIndex) => (
|
|
222
|
+
<InputSelect
|
|
223
|
+
key={`${stage}--${stageIndex}`}
|
|
224
|
+
initial='Select an option'
|
|
225
|
+
value={config.series?.[index].stages?.[stageIndex].color ? config.series?.[index].stages?.[stageIndex].color : 'Select'}
|
|
226
|
+
label={`${stage.key} Series Color`}
|
|
227
|
+
onChange={event => {
|
|
228
|
+
const copyOfSeries = [...config.series] // copy the entire series array
|
|
229
|
+
const copyOfStages = copyOfSeries[index].stages
|
|
230
|
+
copyOfStages[stageIndex].color = event.target.value
|
|
231
|
+
copyOfSeries[index] = { ...copyOfSeries[index], stages: copyOfStages }
|
|
232
|
+
|
|
233
|
+
updateConfig({
|
|
234
|
+
...config,
|
|
235
|
+
series: copyOfSeries
|
|
236
|
+
})
|
|
237
|
+
}}
|
|
238
|
+
options={Object.keys(colorPalettesChart)}
|
|
239
|
+
/>
|
|
240
|
+
))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const SeriesDropdownConfidenceInterval = props => {
|
|
244
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
245
|
+
const { series, index } = props
|
|
246
|
+
const { getColumns } = useContext(SeriesContext)
|
|
247
|
+
if (series.type !== 'Forecasting') return
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<div className='edit-block'>
|
|
251
|
+
<h3>Confidence Interval Groups</h3>
|
|
252
|
+
<fieldset>
|
|
253
|
+
<Accordion allowZeroExpanded>
|
|
254
|
+
{series?.confidenceIntervals?.map((ciGroup, ciIndex) => {
|
|
255
|
+
const showInTooltip = ciGroup.showInTooltip ? ciGroup.showInTooltip : false
|
|
256
|
+
|
|
257
|
+
const updateShowInTooltip = (e, seriesIndex, ciIndex) => {
|
|
258
|
+
e.preventDefault()
|
|
259
|
+
let copiedSeries = [...config.series]
|
|
260
|
+
copiedSeries[seriesIndex].confidenceIntervals[ciIndex].showInTooltip = !showInTooltip
|
|
261
|
+
updateConfig({
|
|
262
|
+
...config,
|
|
263
|
+
series: copiedSeries
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<AccordionItem className='series-item series-item--chart' key={`${ciIndex}`}>
|
|
269
|
+
<AccordionItemHeading className='series-item__title'>
|
|
270
|
+
<>
|
|
271
|
+
<AccordionItemButton className={'accordion__button accordion__button'}>
|
|
272
|
+
Group {ciIndex + 1}
|
|
273
|
+
<button
|
|
274
|
+
className='series-list__remove'
|
|
275
|
+
onClick={e => {
|
|
276
|
+
e.preventDefault()
|
|
277
|
+
const copiedIndex = [...config.series[index].confidenceIntervals]
|
|
278
|
+
copiedIndex.splice(ciIndex, 1)
|
|
279
|
+
const copyOfSeries = [...config.series] // copy the entire series array
|
|
280
|
+
copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: [...copiedIndex] }
|
|
281
|
+
updateConfig({
|
|
282
|
+
...config,
|
|
283
|
+
series: copyOfSeries
|
|
284
|
+
})
|
|
285
|
+
}}
|
|
286
|
+
>
|
|
287
|
+
Remove
|
|
288
|
+
</button>
|
|
289
|
+
</AccordionItemButton>
|
|
290
|
+
</>
|
|
291
|
+
</AccordionItemHeading>
|
|
292
|
+
<AccordionItemPanel>
|
|
293
|
+
<div className='input-group'>
|
|
294
|
+
<label htmlFor='showInTooltip'>Show In Tooltip</label>
|
|
295
|
+
<div className={'cove-input__checkbox--small'} onClick={e => updateShowInTooltip(e, index, ciIndex)}>
|
|
296
|
+
<div className={`cove-input__checkbox-box${'blue' ? ' custom-color' : ''}`} style={{ backgroundColor: '' }}>
|
|
297
|
+
{showInTooltip && <Check className='' style={{ fill: '#025eaa' }} />}
|
|
298
|
+
</div>
|
|
299
|
+
<input className='cove-input--hidden' type='checkbox' name={'showInTooltip'} checked={showInTooltip ? showInTooltip : false} readOnly />
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
<InputSelect
|
|
304
|
+
initial='Select an option'
|
|
305
|
+
value={config.series[index].confidenceIntervals[ciIndex].low ? config.series[index].confidenceIntervals[ciIndex].low : 'Select'}
|
|
306
|
+
label='Low Confidence Interval'
|
|
307
|
+
onChange={e => {
|
|
308
|
+
const copiedConfidenceArray = [...config.series[index].confidenceIntervals]
|
|
309
|
+
copiedConfidenceArray[ciIndex].low = e.target.value
|
|
310
|
+
const copyOfSeries = [...config.series] // copy the entire series array
|
|
311
|
+
copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: copiedConfidenceArray }
|
|
312
|
+
updateConfig({
|
|
313
|
+
...config,
|
|
314
|
+
series: copyOfSeries
|
|
315
|
+
})
|
|
316
|
+
}}
|
|
317
|
+
options={getColumns()}
|
|
318
|
+
/>
|
|
319
|
+
<InputSelect
|
|
320
|
+
initial='Select an option'
|
|
321
|
+
value={config.series[index].confidenceIntervals[ciIndex].high ? config.series[index].confidenceIntervals[ciIndex].high : 'Select'}
|
|
322
|
+
label='High Confidence Interval'
|
|
323
|
+
onChange={e => {
|
|
324
|
+
const copiedConfidenceArray = [...config.series[index].confidenceIntervals]
|
|
325
|
+
copiedConfidenceArray[ciIndex].high = e.target.value
|
|
326
|
+
const copyOfSeries = [...config.series] // copy the entire series array
|
|
327
|
+
copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: copiedConfidenceArray }
|
|
328
|
+
updateConfig({
|
|
329
|
+
...config,
|
|
330
|
+
series: copyOfSeries
|
|
331
|
+
})
|
|
332
|
+
}}
|
|
333
|
+
options={getColumns()}
|
|
334
|
+
/>
|
|
335
|
+
</AccordionItemPanel>
|
|
336
|
+
</AccordionItem>
|
|
337
|
+
)
|
|
338
|
+
})}
|
|
339
|
+
</Accordion>
|
|
340
|
+
<button
|
|
341
|
+
className='btn full-width'
|
|
342
|
+
onClick={e => {
|
|
343
|
+
e.preventDefault()
|
|
344
|
+
let copiedIndex = null
|
|
345
|
+
if (config.series[index].confidenceIntervals) {
|
|
346
|
+
copiedIndex = [...config.series[index].confidenceIntervals]
|
|
347
|
+
} else {
|
|
348
|
+
copiedIndex = []
|
|
349
|
+
}
|
|
350
|
+
const copyOfSeries = [...config.series] // copy the entire series array
|
|
351
|
+
copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: [...copiedIndex, { high: '', low: '' }] } // update the nested array
|
|
352
|
+
updateConfig({
|
|
353
|
+
...config,
|
|
354
|
+
series: copyOfSeries
|
|
355
|
+
})
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
Add Confidence Interval Group
|
|
359
|
+
</button>
|
|
360
|
+
</fieldset>
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const SeriesInputName = props => {
|
|
366
|
+
const { series, index: i } = props
|
|
367
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
368
|
+
const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart']
|
|
369
|
+
|
|
370
|
+
if (config.visualizationType === 'Combo') return
|
|
371
|
+
|
|
372
|
+
if (!adjustableNameSeriesTypes.includes(series.type)) return
|
|
373
|
+
|
|
374
|
+
let changeSeriesName = (i, value) => {
|
|
375
|
+
let series = [...config.series]
|
|
376
|
+
let seriesLabelsCopy = { ...config.runtime.seriesLabels }
|
|
377
|
+
series[i].name = value
|
|
378
|
+
seriesLabelsCopy[series[i].dataKey] = series[i].name
|
|
379
|
+
|
|
380
|
+
let newConfig = {
|
|
381
|
+
...config,
|
|
382
|
+
series
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
updateConfig(newConfig)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<>
|
|
390
|
+
<label htmlFor='series-name'>Series Name</label>
|
|
391
|
+
<input
|
|
392
|
+
type='text'
|
|
393
|
+
key={`series-name-${i}`}
|
|
394
|
+
value={series.name ? series.name : ''}
|
|
395
|
+
onChange={event => {
|
|
396
|
+
changeSeriesName(i, event.target.value)
|
|
397
|
+
}}
|
|
398
|
+
/>
|
|
399
|
+
</>
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const SeriesButtonRemove = props => {
|
|
404
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
405
|
+
const { series, index } = props
|
|
406
|
+
|
|
407
|
+
const removeSeries = seriesKey => {
|
|
408
|
+
let series = [...config.series]
|
|
409
|
+
let seriesIndex = -1
|
|
410
|
+
|
|
411
|
+
for (let i = 0; i < series.length; i++) {
|
|
412
|
+
if (series[i].dataKey === seriesKey) {
|
|
413
|
+
seriesIndex = i
|
|
414
|
+
break
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (seriesIndex !== -1) {
|
|
419
|
+
series.splice(seriesIndex, 1)
|
|
420
|
+
|
|
421
|
+
let newConfig = { ...config, series }
|
|
422
|
+
|
|
423
|
+
if (series.length === 0) {
|
|
424
|
+
delete newConfig.series
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
updateConfig(newConfig)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (config.visualizationType === 'Paired Bar') {
|
|
431
|
+
updateConfig({
|
|
432
|
+
...config,
|
|
433
|
+
series: []
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const handleRemoveSeries = (e, series, index) => {
|
|
439
|
+
e.preventDefault()
|
|
440
|
+
removeSeries(series.dataKey)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return (
|
|
444
|
+
config.series &&
|
|
445
|
+
config.series.length > 1 && (
|
|
446
|
+
<button className='series-list__remove' onClick={e => handleRemoveSeries(e, series, index)}>
|
|
447
|
+
Remove
|
|
448
|
+
</button>
|
|
449
|
+
)
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const SeriesItem = props => {
|
|
454
|
+
const { config } = useContext(ConfigContext)
|
|
455
|
+
|
|
456
|
+
const { series, getItemStyle, sortableItemStyles, chartsWithOptions, index: i } = props
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<Draggable key={series.dataKey} draggableId={`draggableFilter-${series.dataKey}`} index={i}>
|
|
460
|
+
{(provided, snapshot) => (
|
|
461
|
+
<div key={i} className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
|
|
462
|
+
<Accordion allowZeroExpanded>
|
|
463
|
+
<AccordionItem className='series-item series-item--chart'>
|
|
464
|
+
<AccordionItemHeading className='series-item__title'>
|
|
465
|
+
<AccordionItemButton className={chartsWithOptions.includes(config.visualizationType) ? 'accordion__button' : 'accordion__button hide-arrow'}>
|
|
466
|
+
<Icon display='move' size={15} style={{ cursor: 'default' }} />
|
|
467
|
+
{series.dataKey}
|
|
468
|
+
<Series.Button.Remove series={series} index={i} />
|
|
469
|
+
</AccordionItemButton>
|
|
470
|
+
</AccordionItemHeading>
|
|
471
|
+
{chartsWithOptions.includes(config.visualizationType) && (
|
|
472
|
+
<AccordionItemPanel>
|
|
473
|
+
<Series.Input.Name series={series} index={i} />
|
|
474
|
+
<Series.Dropdown.SeriesType series={series} index={i} />
|
|
475
|
+
<Series.Dropdown.AxisPosition series={series} index={i} />
|
|
476
|
+
<Series.Dropdown.LineType series={series} index={i} />
|
|
477
|
+
<Series.Dropdown.ForecastingStage series={series} index={i} />
|
|
478
|
+
<Series.Dropdown.ForecastingColor series={series} index={i} />
|
|
479
|
+
<Series.Dropdown.ConfidenceInterval series={series} index={i} />
|
|
480
|
+
</AccordionItemPanel>
|
|
481
|
+
)}
|
|
482
|
+
</AccordionItem>
|
|
483
|
+
</Accordion>
|
|
484
|
+
</div>
|
|
485
|
+
)}
|
|
486
|
+
</Draggable>
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const SeriesList = props => {
|
|
491
|
+
const { series, getItemStyle, sortableItemStyles, chartsWithOptions } = props
|
|
492
|
+
return series.map((series, i) => {
|
|
493
|
+
return <SeriesItem getItemStyle={getItemStyle} sortableItemStyles={sortableItemStyles} chartsWithOptions={chartsWithOptions} series={series} index={i} key={`series-list-${i}`} />
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const Series = {
|
|
498
|
+
Wrapper: SeriesWrapper,
|
|
499
|
+
Dropdown: {
|
|
500
|
+
SeriesType: SeriesDropdownSeriesType,
|
|
501
|
+
AxisPosition: SeriesDropdownAxisPosition,
|
|
502
|
+
ConfidenceInterval: SeriesDropdownConfidenceInterval,
|
|
503
|
+
LineType: SeriesDropdownLineType,
|
|
504
|
+
ForecastingStage: SeriesDropdownForecastingStage,
|
|
505
|
+
ForecastingColumn: SeriesDropdownForecastingColumn,
|
|
506
|
+
ForecastingColor: SeriesDropdownForecastColor
|
|
507
|
+
},
|
|
508
|
+
Input: {
|
|
509
|
+
Name: SeriesInputName
|
|
510
|
+
},
|
|
511
|
+
Button: {
|
|
512
|
+
Remove: SeriesButtonRemove
|
|
513
|
+
},
|
|
514
|
+
Item: SeriesItem,
|
|
515
|
+
List: SeriesList
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export default Series
|
|
@@ -121,8 +121,8 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
121
121
|
<Text display={config.labels ? 'block' : 'none'} x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
|
|
122
122
|
{formatNumber(d[seriesKey])}
|
|
123
123
|
</Text>
|
|
124
|
-
|
|
125
|
-
{dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
|
|
124
|
+
{/* hide data point circles */}
|
|
125
|
+
{/* dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
|
|
126
126
|
<circle
|
|
127
127
|
key={`${seriesKey}-${dataIndex}`}
|
|
128
128
|
r={circleRadii}
|
|
@@ -133,7 +133,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
133
133
|
data-tooltip-html={tooltip}
|
|
134
134
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
135
135
|
/>
|
|
136
|
-
)}
|
|
136
|
+
)*/}
|
|
137
137
|
</Group>
|
|
138
138
|
)
|
|
139
139
|
})}
|
|
@@ -2,6 +2,7 @@ export default {
|
|
|
2
2
|
type: 'chart',
|
|
3
3
|
title: '',
|
|
4
4
|
showTitle: true,
|
|
5
|
+
showDownloadMediaButton: false,
|
|
5
6
|
theme: 'theme-blue',
|
|
6
7
|
animate: false,
|
|
7
8
|
fontSize: 'medium',
|
|
@@ -14,6 +15,9 @@ export default {
|
|
|
14
15
|
barStyle: '',
|
|
15
16
|
roundingStyle: 'standard',
|
|
16
17
|
tipRounding: 'top',
|
|
18
|
+
general: {
|
|
19
|
+
showDownloadButton: false
|
|
20
|
+
},
|
|
17
21
|
padding: {
|
|
18
22
|
left: 5,
|
|
19
23
|
right: 5
|
|
@@ -40,7 +44,8 @@ export default {
|
|
|
40
44
|
rightAxisTickColor: '#333',
|
|
41
45
|
numTicks: '',
|
|
42
46
|
axisPadding: 0,
|
|
43
|
-
tickRotation: 0
|
|
47
|
+
tickRotation: 0,
|
|
48
|
+
anchors: []
|
|
44
49
|
},
|
|
45
50
|
boxplot: {
|
|
46
51
|
plots: [],
|
|
@@ -84,6 +89,7 @@ export default {
|
|
|
84
89
|
horizontal: 750
|
|
85
90
|
},
|
|
86
91
|
xAxis: {
|
|
92
|
+
anchors: [],
|
|
87
93
|
type: 'categorical',
|
|
88
94
|
showTargetLabel: true,
|
|
89
95
|
targetLabel: 'Target',
|
|
@@ -100,7 +106,8 @@ export default {
|
|
|
100
106
|
numTicks: '',
|
|
101
107
|
labelOffset: 65,
|
|
102
108
|
axisPadding: 0,
|
|
103
|
-
target: 0
|
|
109
|
+
target: 0,
|
|
110
|
+
anchors: []
|
|
104
111
|
},
|
|
105
112
|
table: {
|
|
106
113
|
label: 'Data Table',
|
|
@@ -109,9 +116,16 @@ export default {
|
|
|
109
116
|
height: '',
|
|
110
117
|
caption: '',
|
|
111
118
|
showDownloadUrl: false,
|
|
112
|
-
showDataTableLink: true
|
|
119
|
+
showDataTableLink: true,
|
|
120
|
+
indexLabel: '',
|
|
121
|
+
download: false,
|
|
122
|
+
showVertical: true
|
|
113
123
|
},
|
|
114
124
|
orientation: 'vertical',
|
|
125
|
+
color: 'pinkpurple',
|
|
126
|
+
columns: {
|
|
127
|
+
// start with a blank list
|
|
128
|
+
},
|
|
115
129
|
legend: {
|
|
116
130
|
behavior: 'isolate',
|
|
117
131
|
singleRow: false,
|
|
@@ -148,7 +162,12 @@ export default {
|
|
|
148
162
|
visual: {
|
|
149
163
|
border: true,
|
|
150
164
|
accent: true,
|
|
151
|
-
background: true
|
|
165
|
+
background: true,
|
|
166
|
+
verticalHoverLine: false,
|
|
167
|
+
horizontalHoverLine: false
|
|
152
168
|
},
|
|
153
|
-
|
|
169
|
+
useLogScale: false,
|
|
170
|
+
filterBehavior: 'Filter Change',
|
|
171
|
+
highlightedBarValues: [],
|
|
172
|
+
series: []
|
|
154
173
|
}
|