@cdc/chart 4.23.11 → 4.24.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/dist/cdcchart.js +30220 -29764
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/tall-data.json +98 -0
- package/examples/feature/forest-plot/forest-plot.json +63 -19
- package/examples/feature/forest-plot/linear.json +52 -3
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +0 -35
- package/examples/feature/line/line-chart-preliminary.json +346 -0
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/examples/private/chart-t.json +3740 -0
- package/examples/private/combo.json +369 -0
- package/examples/private/epi-data.csv +13 -0
- package/examples/private/epi-data.json +62 -0
- package/examples/private/epi.json +403 -0
- package/examples/private/occupancy.json +109283 -0
- package/examples/private/prod-line-config.json +401 -0
- package/examples/private/region-data.json +822 -0
- package/examples/private/region-testing.json +312 -0
- package/examples/private/scaling.json +45325 -0
- package/examples/private/testing-data.json +1739 -0
- package/examples/private/testing.json +816 -0
- package/index.html +7 -7
- package/package.json +2 -2
- package/src/CdcChart.tsx +29 -210
- package/src/ConfigContext.tsx +6 -0
- package/src/_stories/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -0
- package/src/_stories/_mock/pie_config.json +191 -0
- package/src/_stories/_mock/pie_data.json +218 -0
- package/src/_stories/_mock/preliminary_mock.json +346 -0
- package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +1 -1
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
- package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
- package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +41 -57
- package/src/components/BarChart/components/BarChart.jsx +39 -0
- package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
- package/src/components/BarChart/components/context.tsx +13 -0
- package/src/components/BarChart/index.tsx +3 -0
- package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +667 -851
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +87 -166
- package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +1 -1
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels.tsx +13 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +751 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +29 -2
- package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
- package/src/components/Forecasting/index.tsx +3 -0
- package/src/components/ForestPlot/ForestPlot.tsx +254 -0
- package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
- package/src/components/ForestPlot/index.tsx +1 -209
- package/src/components/{Legend.jsx → Legend/Legend.tsx} +150 -113
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +29 -0
- package/src/components/LineChart/{LineChart.Circle.tsx → components/LineChart.Circle.tsx} +12 -3
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.tsx +20 -8
- package/src/components/LinearChart.jsx +52 -69
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +135 -0
- package/src/components/Regions/index.tsx +3 -0
- package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
- package/src/components/ScatterPlot/index.tsx +3 -0
- package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
- package/src/components/Sparkline/index.tsx +3 -0
- package/src/data/initial-state.js +5 -6
- package/src/helpers/abbreviateNumber.ts +17 -0
- package/src/helpers/computeMarginBottom.ts +55 -0
- package/src/helpers/filterData.ts +18 -0
- package/src/helpers/generateColorsArray.ts +8 -0
- package/src/helpers/getQuartiles.ts +30 -0
- package/src/helpers/handleChartAriaLabels.ts +19 -0
- package/src/helpers/handleLineType.ts +18 -0
- package/src/helpers/lineOptions.ts +18 -0
- package/src/helpers/sort.ts +7 -0
- package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
- package/src/hooks/useBarChart.js +7 -6
- package/src/hooks/useScales.ts +1 -1
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +23 -21
- package/src/scss/main.scss +67 -3
- package/src/types/ChartConfig.ts +158 -23
- package/src/types/ChartContext.ts +26 -10
- package/src/types/ForestPlot.ts +7 -14
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.tsx +0 -91
- package/src/components/BarChart.jsx +0 -30
- package/src/components/ForestPlot/Readme.md +0 -0
- package/src/scss/LinearChart.scss +0 -0
- package/src/scss/editor-panel.scss +0 -745
- package/src/scss/legend.scss +0 -206
- package/src/scss/mixins.scss +0 -0
- package/src/scss/variables.scss +0 -1
- package/src/types/ChartProps.ts +0 -7
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react'
|
|
2
|
+
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
3
|
+
import { type PanelProps } from './PanelProps'
|
|
4
|
+
|
|
5
|
+
const days = [
|
|
6
|
+
{ day: 'Sunday', abbr: 'S', status: 'inactive' },
|
|
7
|
+
{ day: 'Monday', abbr: 'M', status: 'inactive' },
|
|
8
|
+
{ day: 'Tuesday', abbr: 'T', status: 'inactive' },
|
|
9
|
+
{ day: 'Wednesday', abbr: 'W', status: 'inactive' },
|
|
10
|
+
{ day: 'Thursday', abbr: 'T', status: 'inactive' },
|
|
11
|
+
{ day: 'Friday', abbr: 'F', status: 'inactive' },
|
|
12
|
+
{ day: 'Saturday', abbr: 'S', status: 'inactive' }
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const DateHighlighting = ({ name }: PanelProps) => {
|
|
16
|
+
const [interval, setInterval] = useState(0)
|
|
17
|
+
const [isPlural, setIsPlural] = useState(false)
|
|
18
|
+
const [recurrance, setRecurrance] = useState('Days')
|
|
19
|
+
const [firstRecurranceUpdate, setFirstRecurranceUpdate] = useState(false)
|
|
20
|
+
const [daySelections, setDaySelections] = useState(days)
|
|
21
|
+
|
|
22
|
+
const handleDaySelections = e => {
|
|
23
|
+
const incomingDay = e.target.value
|
|
24
|
+
const prev = [...daySelections]
|
|
25
|
+
|
|
26
|
+
const updatedDays = prev.map(day => {
|
|
27
|
+
if (day.day === incomingDay) {
|
|
28
|
+
return { ...day, status: day.status === 'active' ? 'inactive' : 'active' }
|
|
29
|
+
}
|
|
30
|
+
return day
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
setDaySelections(updatedDays)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (interval > 7 && !firstRecurranceUpdate) {
|
|
38
|
+
setRecurrance('Weeks')
|
|
39
|
+
}
|
|
40
|
+
}, [interval])
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (interval > 1) {
|
|
44
|
+
setIsPlural(true)
|
|
45
|
+
} else {
|
|
46
|
+
setIsPlural(false)
|
|
47
|
+
}
|
|
48
|
+
}, [interval])
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<AccordionItem>
|
|
52
|
+
<AccordionItemHeading>
|
|
53
|
+
<AccordionItemButton>{name}</AccordionItemButton>
|
|
54
|
+
</AccordionItemHeading>
|
|
55
|
+
<AccordionItemPanel>
|
|
56
|
+
<ul className='date-highlight'>
|
|
57
|
+
<div className='date-highlight__occurance'>
|
|
58
|
+
<label htmlFor=''>Repeat Every</label>
|
|
59
|
+
<input type='number' value={interval} onChange={e => setInterval(Number(e.target.value))} />
|
|
60
|
+
<select value={recurrance} onChange={e => setRecurrance(e.target.value)}>
|
|
61
|
+
<option value='Day'>{isPlural ? 'Days' : 'Day'}</option>
|
|
62
|
+
<option value='Week'>{isPlural ? 'Weeks' : 'Week'}</option>
|
|
63
|
+
<option value='Month'>{isPlural ? 'Months' : 'Month'}</option>
|
|
64
|
+
<option value='Year'>{isPlural ? 'Years' : 'Year'}</option>
|
|
65
|
+
</select>
|
|
66
|
+
</div>
|
|
67
|
+
{recurrance !== 'Days' && (
|
|
68
|
+
<div className='date-highlight__day-selection'>
|
|
69
|
+
<label htmlFor='' style={{ display: 'block', width: '100%' }}>
|
|
70
|
+
Repeat On
|
|
71
|
+
</label>
|
|
72
|
+
{daySelections.map(d => (
|
|
73
|
+
<button className={`week-button week-button--${d.status} ${d.day}`} value={d.day} onClick={handleDaySelections}>
|
|
74
|
+
{d.abbr}
|
|
75
|
+
</button>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
<div className='date-highlight__end-date'>
|
|
80
|
+
<label htmlFor=''>Ends</label>
|
|
81
|
+
<div className='radio-group'>
|
|
82
|
+
<div className='group'>
|
|
83
|
+
<input type='radio' name='ending' value='Never' id='Never' />
|
|
84
|
+
<label for='Never'>Never</label>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div className='radio-group'>
|
|
88
|
+
<div className='group'>
|
|
89
|
+
<input type='radio' name='ending' value='After' id='After' />
|
|
90
|
+
<label for='After'>After</label>
|
|
91
|
+
</div>
|
|
92
|
+
<input type='number' className='date-highlight__end-date--on' />
|
|
93
|
+
<p>occurances</p>
|
|
94
|
+
</div>
|
|
95
|
+
<div className='radio-group'>
|
|
96
|
+
<div className='group'>
|
|
97
|
+
<input type='radio' name='ending' value='On' id='On' />
|
|
98
|
+
<label for='On'>On</label>
|
|
99
|
+
</div>
|
|
100
|
+
<input type='date' className='date-highlight__end-date--after' />
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</ul>
|
|
104
|
+
</AccordionItemPanel>
|
|
105
|
+
</AccordionItem>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default DateHighlighting
|
|
@@ -1,119 +1,17 @@
|
|
|
1
1
|
import React, { useContext, memo, useState, useEffect } from 'react'
|
|
2
|
-
import ConfigContext from '
|
|
2
|
+
import ConfigContext from '../../../ConfigContext'
|
|
3
3
|
import { useDebounce } from 'use-debounce'
|
|
4
|
-
import WarningImage from '
|
|
4
|
+
import WarningImage from '../../../images/warning.svg'
|
|
5
5
|
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
6
6
|
import Icon from '@cdc/core/components/ui/Icon'
|
|
7
|
+
import { type ChartContext } from '../../../types/ChartContext'
|
|
8
|
+
import { Select, CheckBox, TextField } from '@cdc/core/components/EditorPanel/Inputs'
|
|
9
|
+
import { type PanelProps } from './PanelProps'
|
|
7
10
|
|
|
8
11
|
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
9
12
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
<option value={optionName} key={index}>
|
|
13
|
-
{optionName}
|
|
14
|
-
</option>
|
|
15
|
-
))
|
|
16
|
-
|
|
17
|
-
if (initialValue) {
|
|
18
|
-
optionsJsx.unshift(
|
|
19
|
-
<option value='' key='initial'>
|
|
20
|
-
{initialValue}
|
|
21
|
-
</option>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<label>
|
|
27
|
-
<span className='edit-label'>
|
|
28
|
-
{label}
|
|
29
|
-
{tooltip}
|
|
30
|
-
</span>
|
|
31
|
-
<select
|
|
32
|
-
className={required && !value ? 'warning' : ''}
|
|
33
|
-
name={fieldName}
|
|
34
|
-
value={value}
|
|
35
|
-
onChange={event => {
|
|
36
|
-
updateField(section, subsection, fieldName, event.target.value)
|
|
37
|
-
}}
|
|
38
|
-
{...attributes}
|
|
39
|
-
>
|
|
40
|
-
{optionsJsx}
|
|
41
|
-
</select>
|
|
42
|
-
</label>
|
|
43
|
-
)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
|
|
47
|
-
<label className='checkbox column-heading'>
|
|
48
|
-
<input
|
|
49
|
-
type='checkbox'
|
|
50
|
-
name={fieldName}
|
|
51
|
-
checked={value}
|
|
52
|
-
onChange={e => {
|
|
53
|
-
updateField(section, subsection, fieldName, !value)
|
|
54
|
-
}}
|
|
55
|
-
{...attributes}
|
|
56
|
-
/>
|
|
57
|
-
<span className='edit-label'>
|
|
58
|
-
{label}
|
|
59
|
-
{tooltip}
|
|
60
|
-
</span>
|
|
61
|
-
</label>
|
|
62
|
-
))
|
|
63
|
-
|
|
64
|
-
/* eslint-disable react-hooks/rules-of-hooks */
|
|
65
|
-
const TextField = memo(({ label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'input', i = null, min = null, ...attributes }) => {
|
|
66
|
-
const [value, setValue] = useState(stateValue)
|
|
67
|
-
|
|
68
|
-
const [debouncedValue] = useDebounce(value, 500)
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
72
|
-
updateField(section, subsection, fieldName, debouncedValue, i)
|
|
73
|
-
}
|
|
74
|
-
}, [debouncedValue]) // eslint-disable-line
|
|
75
|
-
|
|
76
|
-
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
|
|
77
|
-
|
|
78
|
-
const onChange = e => {
|
|
79
|
-
if ('number' !== type || min === null) {
|
|
80
|
-
setValue(e.target.value)
|
|
81
|
-
} else {
|
|
82
|
-
if (!e.target.value || min <= parseFloat(e.target.value)) {
|
|
83
|
-
setValue(e.target.value)
|
|
84
|
-
} else {
|
|
85
|
-
setValue(min.toString())
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
|
|
91
|
-
|
|
92
|
-
if ('textarea' === type) {
|
|
93
|
-
formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if ('number' === type) {
|
|
97
|
-
formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if ('date' === type) {
|
|
101
|
-
formElement = <input type='date' name={name} onChange={onChange} {...attributes} value={value} />
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<label>
|
|
106
|
-
<span className='edit-label column-heading'>
|
|
107
|
-
{label}
|
|
108
|
-
{tooltip}
|
|
109
|
-
</span>
|
|
110
|
-
{formElement}
|
|
111
|
-
</label>
|
|
112
|
-
)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
const ForestPlotSettings = () => {
|
|
116
|
-
const { config, rawData: unfilteredData, updateConfig, isDebug } = useContext(ConfigContext)
|
|
13
|
+
const ForestPlotSettings = ({ name }: PanelProps) => {
|
|
14
|
+
const { config, rawData: unfilteredData, updateConfig } = useContext<ChartContext>(ConfigContext)
|
|
117
15
|
|
|
118
16
|
const enforceRestrictions = updatedConfig => {
|
|
119
17
|
if (updatedConfig.orientation === 'horizontal') {
|
|
@@ -243,11 +141,31 @@ const ForestPlotSettings = () => {
|
|
|
243
141
|
<AccordionItem>
|
|
244
142
|
<AccordionItemHeading>
|
|
245
143
|
<AccordionItemButton>
|
|
246
|
-
|
|
144
|
+
{name}
|
|
247
145
|
{(!config.forestPlot.estimateField || !config.forestPlot.upper || !config.forestPlot.lower) && <WarningImage width='25' className='warning-icon' />}
|
|
248
146
|
</AccordionItemButton>
|
|
249
147
|
</AccordionItemHeading>
|
|
250
148
|
<AccordionItemPanel>
|
|
149
|
+
<Select
|
|
150
|
+
value={config.xAxis.dataKey || ''}
|
|
151
|
+
section='xAxis'
|
|
152
|
+
fieldName='dataKey'
|
|
153
|
+
label='Study Column'
|
|
154
|
+
initial='Select'
|
|
155
|
+
required={true}
|
|
156
|
+
updateField={updateField}
|
|
157
|
+
options={getColumns(false)}
|
|
158
|
+
tooltip={
|
|
159
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
160
|
+
<Tooltip.Target>
|
|
161
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
162
|
+
</Tooltip.Target>
|
|
163
|
+
<Tooltip.Content>
|
|
164
|
+
<p>Select the column or row containing the categories or dates for this axis. </p>
|
|
165
|
+
</Tooltip.Content>
|
|
166
|
+
</Tooltip>
|
|
167
|
+
}
|
|
168
|
+
/>
|
|
251
169
|
<Select
|
|
252
170
|
value={config.forestPlot.type}
|
|
253
171
|
label='Forest Plot Type'
|
|
@@ -311,7 +229,6 @@ const ForestPlotSettings = () => {
|
|
|
311
229
|
<Select
|
|
312
230
|
value={config.forestPlot.shape}
|
|
313
231
|
label='Point Estimate Shape'
|
|
314
|
-
initial={config.forestPlot.shape || 'Select'}
|
|
315
232
|
onChange={e => {
|
|
316
233
|
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
317
234
|
updateConfig({
|
|
@@ -327,28 +244,6 @@ const ForestPlotSettings = () => {
|
|
|
327
244
|
options={['text', 'circle', 'square']}
|
|
328
245
|
/>
|
|
329
246
|
|
|
330
|
-
<Select
|
|
331
|
-
value={config.forestPlot.radius.scalingColumn}
|
|
332
|
-
label='Scale Radius Column'
|
|
333
|
-
initial={'Select'}
|
|
334
|
-
onChange={e => {
|
|
335
|
-
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
336
|
-
updateConfig({
|
|
337
|
-
...config,
|
|
338
|
-
forestPlot: {
|
|
339
|
-
...config.forestPlot,
|
|
340
|
-
radius: {
|
|
341
|
-
...config.forestPlot.radius,
|
|
342
|
-
scalingColumn: e.target.value
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
}
|
|
347
|
-
e.target.value = ''
|
|
348
|
-
}}
|
|
349
|
-
options={getColumns(false)}
|
|
350
|
-
/>
|
|
351
|
-
|
|
352
247
|
<Select
|
|
353
248
|
value={config.forestPlot.lower}
|
|
354
249
|
label='Lower CI Column'
|
|
@@ -389,30 +284,30 @@ const ForestPlotSettings = () => {
|
|
|
389
284
|
options={getColumns(false)}
|
|
390
285
|
/>
|
|
391
286
|
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
287
|
+
<label>
|
|
288
|
+
<span className='edit-label column-heading'>
|
|
289
|
+
Pooled Result Column
|
|
290
|
+
<input
|
|
291
|
+
type='text'
|
|
292
|
+
value={config.forestPlot.pooledResult.column || ''}
|
|
293
|
+
label='Pooled Result Row'
|
|
294
|
+
onChange={e => {
|
|
295
|
+
updateConfig({
|
|
296
|
+
...config,
|
|
297
|
+
forestPlot: {
|
|
298
|
+
...config.forestPlot,
|
|
299
|
+
pooledResult: {
|
|
300
|
+
...config.forestPlot.pooledResult,
|
|
301
|
+
column: e.target.value
|
|
302
|
+
}
|
|
406
303
|
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
/>
|
|
304
|
+
})
|
|
305
|
+
e.target.value = ''
|
|
306
|
+
}}
|
|
307
|
+
/>
|
|
308
|
+
</span>
|
|
309
|
+
</label>
|
|
414
310
|
|
|
415
|
-
<CheckBox value={config.forestPlot?.hideDateCategoryCol || false} section='forestPlot' fieldName='hideDateCategoryCol' label='Hide Date Category Column' updateField={updateField} />
|
|
416
311
|
<CheckBox value={config.forestPlot?.lineOfNoEffect?.show || false} section='forestPlot' subsection='lineOfNoEffect' fieldName='show' label='Show Line of No Effect' updateField={updateField} />
|
|
417
312
|
|
|
418
313
|
<br />
|
|
@@ -496,12 +391,45 @@ const ForestPlotSettings = () => {
|
|
|
496
391
|
/>
|
|
497
392
|
</label>
|
|
498
393
|
|
|
394
|
+
<TextField type='number' min={20} max={45} value={config.forestPlot.rowHeight ? config.forestPlot.rowHeight : 10} updateField={updateField} section='forestPlot' fieldName='rowHeight' label='Row Height' placeholder='10' />
|
|
395
|
+
<br />
|
|
396
|
+
<hr />
|
|
397
|
+
<br />
|
|
398
|
+
<h4>Labels Settings</h4>
|
|
399
|
+
<TextField type='text' value={config.forestPlot?.leftLabel || ''} updateField={updateField} section='forestPlot' fieldName='leftLabel' label='Left Label' />
|
|
400
|
+
<TextField type='text' value={config.forestPlot?.rightLabel || ''} updateField={updateField} section='forestPlot' fieldName='rightLabel' label='Right Label' />
|
|
401
|
+
|
|
402
|
+
<br />
|
|
403
|
+
<hr />
|
|
404
|
+
<br />
|
|
405
|
+
<Select
|
|
406
|
+
value={config.forestPlot.radius.scalingColumn}
|
|
407
|
+
label='Weight Column'
|
|
408
|
+
initial={'Select'}
|
|
409
|
+
onChange={e => {
|
|
410
|
+
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
411
|
+
updateConfig({
|
|
412
|
+
...config,
|
|
413
|
+
forestPlot: {
|
|
414
|
+
...config.forestPlot,
|
|
415
|
+
radius: {
|
|
416
|
+
...config.forestPlot.radius,
|
|
417
|
+
scalingColumn: e.target.value
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
})
|
|
421
|
+
}
|
|
422
|
+
e.target.value = ''
|
|
423
|
+
}}
|
|
424
|
+
options={getColumns(false)}
|
|
425
|
+
/>
|
|
426
|
+
|
|
499
427
|
<label>
|
|
500
428
|
<span className='edit-label column-heading'>Radius Minimum Size</span>
|
|
501
429
|
<input
|
|
502
|
-
min={
|
|
503
|
-
max={
|
|
504
|
-
value={config.forestPlot.radius.min}
|
|
430
|
+
min={3}
|
|
431
|
+
max={6}
|
|
432
|
+
value={config.forestPlot.radius.min || 3}
|
|
505
433
|
onChange={e => {
|
|
506
434
|
updateConfig({
|
|
507
435
|
...config,
|
|
@@ -522,7 +450,7 @@ const ForestPlotSettings = () => {
|
|
|
522
450
|
<label>
|
|
523
451
|
<span className='edit-label column-heading'>Radius Maximum Size</span>
|
|
524
452
|
<input
|
|
525
|
-
min={
|
|
453
|
+
min={7}
|
|
526
454
|
max={10}
|
|
527
455
|
value={config.forestPlot.radius.max}
|
|
528
456
|
onChange={e => {
|
|
@@ -542,13 +470,6 @@ const ForestPlotSettings = () => {
|
|
|
542
470
|
placeholder=' 1'
|
|
543
471
|
/>
|
|
544
472
|
</label>
|
|
545
|
-
<TextField type='number' min={20} max={45} value={config.forestPlot.rowHeight ? config.forestPlot.rowHeight : 10} updateField={updateField} section='forestPlot' fieldName='rowHeight' label='Row Height' placeholder='10' />
|
|
546
|
-
<br />
|
|
547
|
-
<hr />
|
|
548
|
-
<br />
|
|
549
|
-
<h4>Labels Settings</h4>
|
|
550
|
-
<TextField type='text' value={config.forestPlot?.leftLabel || ''} updateField={updateField} section='forestPlot' fieldName='leftLabel' label='Left Label' />
|
|
551
|
-
<TextField type='text' value={config.forestPlot?.rightLabel || ''} updateField={updateField} section='forestPlot' fieldName='rightLabel' label='Right Label' />
|
|
552
473
|
</AccordionItemPanel>
|
|
553
474
|
</AccordionItem>
|
|
554
475
|
)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { memo, useContext } from 'react'
|
|
2
|
+
import { useEditorPermissions } from './../useEditorPermissions.js'
|
|
3
|
+
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
4
|
+
import { type ChartConfig } from './../../../types/ChartConfig.js'
|
|
5
|
+
import { TextField, Select } from '@cdc/core/components/EditorPanel/Inputs'
|
|
6
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
7
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
8
|
+
import { type ChartContext } from '../../../types/ChartContext'
|
|
9
|
+
import { type PanelProps } from './PanelProps'
|
|
10
|
+
import ConfigContext from '../../../ConfigContext'
|
|
11
|
+
|
|
12
|
+
const RegionSettings = memo(({ config, updateConfig }: { config: ChartConfig; updateConfig: Function }) => {
|
|
13
|
+
let regionUpdate = (fieldName, value, i) => {
|
|
14
|
+
let regions = []
|
|
15
|
+
|
|
16
|
+
if (config.regions) {
|
|
17
|
+
regions = [...config.regions]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
regions[i][fieldName] = value
|
|
21
|
+
updateConfig({ ...config, regions })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// only for Regions
|
|
25
|
+
let updateField = (section, subsection, fieldName, value, i) => regionUpdate(fieldName, value, i)
|
|
26
|
+
|
|
27
|
+
let removeColumn = i => {
|
|
28
|
+
let regions = []
|
|
29
|
+
|
|
30
|
+
if (config.regions) {
|
|
31
|
+
regions = [...config.regions]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
regions.splice(i, 1)
|
|
35
|
+
|
|
36
|
+
updateConfig({ ...config, regions })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let addColumn = () => {
|
|
40
|
+
let regions = []
|
|
41
|
+
|
|
42
|
+
if (config.regions) {
|
|
43
|
+
regions = [...config.regions]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
regions.push({})
|
|
47
|
+
|
|
48
|
+
updateConfig({ ...config, regions })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const fromOptions = ['Fixed', 'Previous Days']
|
|
52
|
+
const toOptions = ['Last Date', 'Fixed']
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
{config.regions &&
|
|
57
|
+
config.regions.map(({ label, color, from, to, background, range = 'Custom' }, i) => (
|
|
58
|
+
<div className='edit-block' key={`region-${i}`}>
|
|
59
|
+
<button
|
|
60
|
+
type='button'
|
|
61
|
+
className='remove-column'
|
|
62
|
+
onClick={event => {
|
|
63
|
+
event.preventDefault()
|
|
64
|
+
removeColumn(i)
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
Remove
|
|
68
|
+
</button>
|
|
69
|
+
<TextField value={label} label='Region Label' fieldName='label' i={i} updateField={updateField} />
|
|
70
|
+
<div className='two-col-inputs'>
|
|
71
|
+
<TextField value={color} label='Text Color' fieldName='color' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
|
|
72
|
+
<TextField value={background} label='Background' fieldName='background' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<Select
|
|
76
|
+
value={config.regions[i].fromType ?? 'Fixed'}
|
|
77
|
+
label='Minimum Region Type'
|
|
78
|
+
initial={'Select'}
|
|
79
|
+
required={true}
|
|
80
|
+
onChange={e => {
|
|
81
|
+
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
82
|
+
const newRegions = [...config.regions]
|
|
83
|
+
newRegions[i].fromType = e.target.value
|
|
84
|
+
updateConfig({
|
|
85
|
+
...config,
|
|
86
|
+
regions: newRegions
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
e.target.value = ''
|
|
90
|
+
}}
|
|
91
|
+
options={fromOptions}
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
{(config.regions[i].fromType === 'Fixed' || config.regions[i].fromType === 'Previous Days' || !config.regions[i].fromType) && (
|
|
95
|
+
<>
|
|
96
|
+
<TextField
|
|
97
|
+
value={from}
|
|
98
|
+
label={config.regions[i].fromType === 'Fixed' || !config.regions[i]?.fromType ? 'From Value' : 'Previous Number of Days'}
|
|
99
|
+
fieldName='from'
|
|
100
|
+
updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}
|
|
101
|
+
tooltip={
|
|
102
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
103
|
+
<Tooltip.Target>
|
|
104
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
105
|
+
</Tooltip.Target>
|
|
106
|
+
<Tooltip.Content>
|
|
107
|
+
<p>The date needs to be in the original format of the data. Not the displayed format of the data.</p>
|
|
108
|
+
</Tooltip.Content>
|
|
109
|
+
</Tooltip>
|
|
110
|
+
}
|
|
111
|
+
/>
|
|
112
|
+
</>
|
|
113
|
+
)}
|
|
114
|
+
|
|
115
|
+
<Select
|
|
116
|
+
value={config.regions[i].toType ?? 'Fixed'}
|
|
117
|
+
label='Maximum Region Type'
|
|
118
|
+
initial={'Select'}
|
|
119
|
+
required={true}
|
|
120
|
+
onChange={e => {
|
|
121
|
+
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
122
|
+
const newRegions = [...config.regions]
|
|
123
|
+
newRegions[i].toType = e.target.value
|
|
124
|
+
updateConfig({
|
|
125
|
+
...config,
|
|
126
|
+
regions: newRegions
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
e.target.value = ''
|
|
130
|
+
}}
|
|
131
|
+
options={toOptions}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
{(config.regions[i].toType === 'Fixed' || !config.regions[i].toType) && <TextField value={to} label='To Value' fieldName='to' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />}
|
|
135
|
+
</div>
|
|
136
|
+
))}
|
|
137
|
+
{!config.regions && <p style={{ textAlign: 'center' }}>There are currently no regions.</p>}
|
|
138
|
+
<button
|
|
139
|
+
type='button'
|
|
140
|
+
className='btn full-width'
|
|
141
|
+
onClick={e => {
|
|
142
|
+
e.preventDefault()
|
|
143
|
+
addColumn()
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
Add Region
|
|
147
|
+
</button>
|
|
148
|
+
</>
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const RegionsPanel = ({ name }: PanelProps) => {
|
|
153
|
+
const { visSupportsRegions } = useEditorPermissions()
|
|
154
|
+
const { config, updateConfig } = useContext<ChartContext>(ConfigContext)
|
|
155
|
+
|
|
156
|
+
return visSupportsRegions() ? (
|
|
157
|
+
<AccordionItem>
|
|
158
|
+
<AccordionItemHeading>
|
|
159
|
+
<AccordionItemButton>{name}</AccordionItemButton>
|
|
160
|
+
</AccordionItemHeading>
|
|
161
|
+
<AccordionItemPanel>
|
|
162
|
+
<RegionSettings config={config} updateConfig={updateConfig} />
|
|
163
|
+
</AccordionItemPanel>
|
|
164
|
+
</AccordionItem>
|
|
165
|
+
) : null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default RegionsPanel
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ForestPlotSettings from './Panel.ForestPlotSettings'
|
|
2
|
+
import Series from './Panel.Series.jsx'
|
|
3
|
+
import DateHighlighting from './Panel.DateHighlighting'
|
|
4
|
+
import Regions from './Panel.Regions'
|
|
5
|
+
|
|
6
|
+
const Panels = {
|
|
7
|
+
ForestPlot: ForestPlotSettings,
|
|
8
|
+
Series: Series,
|
|
9
|
+
DateHighlighting,
|
|
10
|
+
Regions
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default Panels
|