@cdc/chart 4.23.11 → 4.24.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/cdcchart.js +35740 -35027
- 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 +393 -0
- package/examples/feature/regions/index.json +9 -5
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/index.html +10 -8
- package/package.json +2 -2
- package/src/CdcChart.tsx +70 -234
- 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 +192 -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} +2 -26
- 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 +108 -0
- package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
- 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} +10 -9
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
- package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
- package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +739 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -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/Legend.Component.tsx +199 -0
- package/src/components/Legend/Legend.tsx +28 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +29 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.tsx +111 -23
- package/src/components/LinearChart.jsx +55 -72
- package/src/components/PairedBarChart.jsx +4 -2
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +144 -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 +10 -8
- 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/useHighlightedBars.js +1 -1
- package/src/hooks/useMinMax.ts +3 -3
- package/src/hooks/useScales.ts +19 -6
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
- package/src/scss/main.scss +0 -3
- package/src/types/ChartConfig.ts +167 -23
- package/src/types/ChartContext.ts +34 -12
- package/src/types/ForestPlot.ts +7 -14
- package/src/types/Label.ts +7 -0
- 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/EditorPanel.jsx +0 -3356
- package/src/components/ForestPlot/Readme.md +0 -0
- package/src/components/Legend.jsx +0 -310
- package/src/components/LineChart/LineChart.Circle.tsx +0 -105
- 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,160 @@
|
|
|
1
|
+
import { useContext, FC } from 'react'
|
|
2
|
+
|
|
3
|
+
// external libraries
|
|
4
|
+
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
5
|
+
import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
|
|
6
|
+
|
|
7
|
+
// core
|
|
8
|
+
import { TextField, Select, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
|
|
9
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
10
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
11
|
+
|
|
12
|
+
// contexts
|
|
13
|
+
import { useEditorPermissions } from '../../useEditorPermissions.js'
|
|
14
|
+
import { useEditorPanelContext } from '../../EditorPanelContext.js'
|
|
15
|
+
import ConfigContext from '../../../../ConfigContext.js'
|
|
16
|
+
import { PanelProps } from '../PanelProps'
|
|
17
|
+
|
|
18
|
+
const PanelGeneral: FC<PanelProps> = props => {
|
|
19
|
+
const { config } = useContext(ConfigContext)
|
|
20
|
+
const { updateField } = useEditorPanelContext()
|
|
21
|
+
const { enabledChartTypes, visHasNumbersOnBars, visHasLabelOnData, visSupportsChartHeight, visSupportsSuperTitle, visSupportsFootnotes } = useEditorPermissions()
|
|
22
|
+
const { visualizationType, visualizationSubType, barStyle } = config
|
|
23
|
+
|
|
24
|
+
const showBarStyleOptions = () => {
|
|
25
|
+
if ((visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && visualizationSubType !== 'stacked' && (config.orientation === 'horizontal' || config.orientation === 'vertical')) {
|
|
26
|
+
return ['flat', 'rounded', 'lollipop']
|
|
27
|
+
} else {
|
|
28
|
+
return ['flat', 'rounded']
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<AccordionItem>
|
|
34
|
+
{' '}
|
|
35
|
+
{/* General */}
|
|
36
|
+
<AccordionItemHeading>
|
|
37
|
+
<AccordionItemButton>General</AccordionItemButton>
|
|
38
|
+
</AccordionItemHeading>
|
|
39
|
+
<AccordionItemPanel>
|
|
40
|
+
<Select value={visualizationType} fieldName='visualizationType' label='Chart Type' updateField={updateField} options={enabledChartTypes} />
|
|
41
|
+
{(visualizationType === 'Bar' || visualizationType === 'Combo' || visualizationType === 'Area Chart') && <Select value={visualizationSubType || 'Regular'} fieldName='visualizationSubType' label='Chart Subtype' updateField={updateField} options={['regular', 'stacked']} />}
|
|
42
|
+
{visualizationType === 'Area Chart' && visualizationSubType === 'stacked' && <Select value={config.stackedAreaChartLineType || 'Linear'} fieldName='stackedAreaChartLineType' label='Stacked Area Chart Line Type' updateField={updateField} options={Object.keys(approvedCurveTypes)} />}
|
|
43
|
+
{visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName='orientation' label='Orientation' updateField={updateField} options={['vertical', 'horizontal']} />}
|
|
44
|
+
{visualizationType === 'Deviation Bar' && <Select label='Orientation' options={['horizontal']} />}
|
|
45
|
+
{(visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && <Select value={config.isLollipopChart ? 'lollipop' : barStyle || 'flat'} fieldName='barStyle' label='bar style' updateField={updateField} options={showBarStyleOptions()} />}
|
|
46
|
+
{(visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && barStyle === 'rounded' && <Select value={config.tipRounding || 'top'} fieldName='tipRounding' label='tip rounding' updateField={updateField} options={['top', 'full']} />}
|
|
47
|
+
{(visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && barStyle === 'rounded' && <Select value={config.roundingStyle || 'standard'} fieldName='roundingStyle' label='rounding style' updateField={updateField} options={['standard', 'shallow', 'finger']} />}
|
|
48
|
+
{visualizationType === 'Bar' && config.orientation === 'horizontal' && <Select value={config.yAxis.labelPlacement || 'Below Bar'} section='yAxis' fieldName='labelPlacement' label='Label Placement' updateField={updateField} options={['Below Bar', 'On Date/Category Axis']} />}
|
|
49
|
+
{visHasNumbersOnBars() ? (
|
|
50
|
+
<CheckBox value={config.yAxis.displayNumbersOnBar} section='yAxis' fieldName='displayNumbersOnBar' label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField} />
|
|
51
|
+
) : (
|
|
52
|
+
visHasLabelOnData() && <CheckBox value={config.labels} fieldName='labels' label='Display label on data' updateField={updateField} />
|
|
53
|
+
)}
|
|
54
|
+
{visualizationType === 'Pie' && <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />}
|
|
55
|
+
|
|
56
|
+
<TextField
|
|
57
|
+
value={config.title || 'Chart Title'}
|
|
58
|
+
fieldName='title'
|
|
59
|
+
id='title'
|
|
60
|
+
label='Title'
|
|
61
|
+
placeholder='Chart Title'
|
|
62
|
+
//defaultValue='Chart Title'
|
|
63
|
+
updateField={updateField}
|
|
64
|
+
//onChange={handleTitleChange}
|
|
65
|
+
tooltip={
|
|
66
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
67
|
+
<Tooltip.Target>
|
|
68
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
69
|
+
</Tooltip.Target>
|
|
70
|
+
<Tooltip.Content>
|
|
71
|
+
<p>Title is required to set the name of the download file but can be hidden using the option below.</p>
|
|
72
|
+
</Tooltip.Content>
|
|
73
|
+
</Tooltip>
|
|
74
|
+
}
|
|
75
|
+
/>
|
|
76
|
+
<CheckBox value={config.showTitle} fieldName='showTitle' label='Show Title' updateField={updateField} />
|
|
77
|
+
|
|
78
|
+
{visSupportsSuperTitle() && (
|
|
79
|
+
<TextField
|
|
80
|
+
value={config.superTitle}
|
|
81
|
+
updateField={updateField}
|
|
82
|
+
fieldName='superTitle'
|
|
83
|
+
label='Super Title'
|
|
84
|
+
placeholder='Super Title'
|
|
85
|
+
tooltip={
|
|
86
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
87
|
+
<Tooltip.Target>
|
|
88
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
89
|
+
</Tooltip.Target>
|
|
90
|
+
<Tooltip.Content>
|
|
91
|
+
<p>Super Title</p>
|
|
92
|
+
</Tooltip.Content>
|
|
93
|
+
</Tooltip>
|
|
94
|
+
}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
<TextField
|
|
99
|
+
type='textarea'
|
|
100
|
+
value={config.introText}
|
|
101
|
+
updateField={updateField}
|
|
102
|
+
fieldName='introText'
|
|
103
|
+
label='Message'
|
|
104
|
+
tooltip={
|
|
105
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
106
|
+
<Tooltip.Target>
|
|
107
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
108
|
+
</Tooltip.Target>
|
|
109
|
+
<Tooltip.Content>
|
|
110
|
+
<p>Intro Text</p>
|
|
111
|
+
</Tooltip.Content>
|
|
112
|
+
</Tooltip>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
|
|
116
|
+
<TextField
|
|
117
|
+
type='textarea'
|
|
118
|
+
value={config.description}
|
|
119
|
+
fieldName='description'
|
|
120
|
+
label='Subtext/Citation'
|
|
121
|
+
updateField={updateField}
|
|
122
|
+
tooltip={
|
|
123
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
124
|
+
<Tooltip.Target>
|
|
125
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
126
|
+
</Tooltip.Target>
|
|
127
|
+
<Tooltip.Content>
|
|
128
|
+
<p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
|
|
129
|
+
</Tooltip.Content>
|
|
130
|
+
</Tooltip>
|
|
131
|
+
}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
{visSupportsFootnotes() && (
|
|
135
|
+
<TextField
|
|
136
|
+
type='textarea'
|
|
137
|
+
value={config.footnotes}
|
|
138
|
+
updateField={updateField}
|
|
139
|
+
fieldName='footnotes'
|
|
140
|
+
label='Footnotes'
|
|
141
|
+
tooltip={
|
|
142
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
143
|
+
<Tooltip.Target>
|
|
144
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
145
|
+
</Tooltip.Target>
|
|
146
|
+
<Tooltip.Content>
|
|
147
|
+
<p>Footnotes</p>
|
|
148
|
+
</Tooltip.Content>
|
|
149
|
+
</Tooltip>
|
|
150
|
+
}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{visSupportsChartHeight() && config.orientation === 'vertical' && <TextField type='number' value={config.heights.vertical} section='heights' fieldName='vertical' label='Chart Height' updateField={updateField} />}
|
|
155
|
+
</AccordionItemPanel>
|
|
156
|
+
</AccordionItem>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export default PanelGeneral
|
|
@@ -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.js'
|
|
9
|
+
import { type PanelProps } from '../PanelProps.js'
|
|
10
|
+
import ConfigContext from '../../../../ConfigContext.js'
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
|
-
import ConfigContext from '
|
|
2
|
+
import ConfigContext from '../../../../ConfigContext'
|
|
3
3
|
|
|
4
4
|
// Core
|
|
5
5
|
import InputSelect from '@cdc/core/components/inputs/InputSelect'
|
|
@@ -427,9 +427,7 @@ const SeriesDropdownConfidenceInterval = props => {
|
|
|
427
427
|
const SeriesInputName = props => {
|
|
428
428
|
const { series, index: i } = props
|
|
429
429
|
const { config, updateConfig } = useContext(ConfigContext)
|
|
430
|
-
const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart', 'dashed-sm', 'dashed-md', 'dashed-lg']
|
|
431
|
-
|
|
432
|
-
if (config.visualizationType === 'Combo') return
|
|
430
|
+
const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart', 'Combo', 'Deviation', 'Paired', 'Scatter', 'dashed-sm', 'dashed-md', 'dashed-lg']
|
|
433
431
|
|
|
434
432
|
if (!adjustableNameSeriesTypes.includes(series.type)) return
|
|
435
433
|
|
|
@@ -470,6 +468,8 @@ const SeriesDisplayInTooltip = props => {
|
|
|
470
468
|
const { series, index } = props
|
|
471
469
|
const { config, updateConfig } = useContext(ConfigContext)
|
|
472
470
|
|
|
471
|
+
if(['Paired Bar', 'Scatter Plot', 'Deviation Bar'].includes(config.visualizationType)) return
|
|
472
|
+
|
|
473
473
|
const toggleTooltip = seriesIndex => {
|
|
474
474
|
let copiedSeries = [...config.series]
|
|
475
475
|
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { useContext, FC } from 'react'
|
|
2
|
+
|
|
3
|
+
// external libraries
|
|
4
|
+
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
5
|
+
|
|
6
|
+
// core
|
|
7
|
+
import { TextField, Select, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
|
|
8
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
9
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
10
|
+
import InputToggle from '@cdc/core/components/inputs/InputToggle'
|
|
11
|
+
|
|
12
|
+
// contexts
|
|
13
|
+
import { useColorPalette } from '../../../../hooks/useColorPalette'
|
|
14
|
+
import { ChartContext } from './../../../../types/ChartContext.js'
|
|
15
|
+
|
|
16
|
+
import { useEditorPermissions } from '../../useEditorPermissions.js'
|
|
17
|
+
import { useEditorPanelContext } from '../../EditorPanelContext.js'
|
|
18
|
+
import ConfigContext from '../../../../ConfigContext.js'
|
|
19
|
+
import { PanelProps } from '../PanelProps'
|
|
20
|
+
|
|
21
|
+
const PanelVisual: FC<PanelProps> = props => {
|
|
22
|
+
const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
|
|
23
|
+
const { visual } = config
|
|
24
|
+
const { setLollipopShape, updateField } = useEditorPanelContext()
|
|
25
|
+
const { visHasBarBorders, visCanAnimate, visSupportsNonSequentialPallete, headerColors, visSupportsTooltipOpacity, visSupportsTooltipLines, visSupportsBarSpace, visSupportsBarThickness, visHasDataCutoff, visSupportsSequentialPallete, visSupportsReverseColorPalette } = useEditorPermissions()
|
|
26
|
+
const { twoColorPalettes, sequential, nonSequential } = useColorPalette(config, updateConfig)
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<AccordionItem>
|
|
30
|
+
<AccordionItemHeading>
|
|
31
|
+
<AccordionItemButton>Visual</AccordionItemButton>
|
|
32
|
+
</AccordionItemHeading>
|
|
33
|
+
<AccordionItemPanel>
|
|
34
|
+
{config.isLollipopChart && (
|
|
35
|
+
<>
|
|
36
|
+
<fieldset className='header'>
|
|
37
|
+
<legend className='edit-label'>Lollipop Shape</legend>
|
|
38
|
+
<div
|
|
39
|
+
onChange={e => {
|
|
40
|
+
setLollipopShape(e.target.value)
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<label className='radio-label'>
|
|
44
|
+
<input type='radio' name='lollipopShape' value='circle' checked={config.lollipopShape === 'circle'} />
|
|
45
|
+
Circle
|
|
46
|
+
</label>
|
|
47
|
+
<label className='radio-label'>
|
|
48
|
+
<input type='radio' name='lollipopShape' value='square' checked={config.lollipopShape === 'square'} />
|
|
49
|
+
Square
|
|
50
|
+
</label>
|
|
51
|
+
</div>
|
|
52
|
+
</fieldset>
|
|
53
|
+
<Select value={config.lollipopColorStyle ? config.lollipopColorStyle : 'two-tone'} fieldName='lollipopColorStyle' label='Lollipop Color Style' updateField={updateField} options={['regular', 'two-tone']} />
|
|
54
|
+
<Select value={config.lollipopSize ? config.lollipopSize : 'small'} fieldName='lollipopSize' label='Lollipop Size' updateField={updateField} options={['small', 'medium', 'large']} />
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{config.visualizationType === 'Box Plot' && (
|
|
59
|
+
<fieldset className='fieldset fieldset--boxplot'>
|
|
60
|
+
<legend className=''>Box Plot Settings</legend>
|
|
61
|
+
<Select value={config.boxplot.borders} fieldName='borders' section='boxplot' label='Box Plot Borders' updateField={updateField} options={['true', 'false']} />
|
|
62
|
+
<CheckBox value={config.boxplot.plotOutlierValues} fieldName='plotOutlierValues' section='boxplot' label='Plot Outliers' updateField={updateField} />
|
|
63
|
+
<CheckBox value={config.boxplot.plotNonOutlierValues} fieldName='plotNonOutlierValues' section='boxplot' label='Plot non-outlier values' updateField={updateField} />
|
|
64
|
+
</fieldset>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
<Select value={config.fontSize} fieldName='fontSize' label='Font Size' updateField={updateField} options={['small', 'medium', 'large']} />
|
|
68
|
+
{visHasBarBorders() && <Select value={config.barHasBorder} fieldName='barHasBorder' label='Bar Borders' updateField={updateField} options={['true', 'false']} />}
|
|
69
|
+
{visCanAnimate() && <CheckBox value={config.animate} fieldName='animate' label='Animate Visualization' updateField={updateField} />}
|
|
70
|
+
|
|
71
|
+
{/*<CheckBox value={config.animateReplay} fieldName="animateReplay" label="Replay Animation When Filters Are Changed" updateField={updateField} />*/}
|
|
72
|
+
|
|
73
|
+
{((config.series?.some(series => series.type === 'Line' || series.type === 'dashed-lg' || series.type === 'dashed-sm' || series.type === 'dashed-md') && config.visualizationType === 'Combo') || config.visualizationType === 'Line') && (
|
|
74
|
+
<>
|
|
75
|
+
<Select value={config.lineDatapointStyle} fieldName='lineDatapointStyle' label='Line Datapoint Style' updateField={updateField} options={['hidden', 'hover', 'always show']} />
|
|
76
|
+
<Select value={config.lineDatapointColor} fieldName='lineDatapointColor' label='Line Datapoint Color' updateField={updateField} options={['Same as Line', 'Lighter than Line']} />
|
|
77
|
+
</>
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
{/* eslint-disable */}
|
|
81
|
+
<label className='header'>
|
|
82
|
+
<span className='edit-label'>Header Theme</span>
|
|
83
|
+
<ul className='color-palette'>
|
|
84
|
+
{headerColors.map(palette => (
|
|
85
|
+
<button
|
|
86
|
+
title={palette}
|
|
87
|
+
key={palette}
|
|
88
|
+
onClick={e => {
|
|
89
|
+
e.preventDefault()
|
|
90
|
+
updateConfig({ ...config, theme: palette })
|
|
91
|
+
}}
|
|
92
|
+
className={config.theme === palette ? 'selected ' + palette : palette}
|
|
93
|
+
></button>
|
|
94
|
+
))}
|
|
95
|
+
</ul>
|
|
96
|
+
</label>
|
|
97
|
+
{/* eslint-enable */}
|
|
98
|
+
{(visSupportsNonSequentialPallete() || visSupportsNonSequentialPallete()) && (
|
|
99
|
+
<>
|
|
100
|
+
<label>
|
|
101
|
+
<span className='edit-label'>Chart Color Palette</span>
|
|
102
|
+
</label>
|
|
103
|
+
{visSupportsReverseColorPalette() && <InputToggle fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={config.isPaletteReversed} />}
|
|
104
|
+
{visSupportsSequentialPallete() && (
|
|
105
|
+
<>
|
|
106
|
+
<span>Sequential</span>
|
|
107
|
+
<ul className='color-palette'>
|
|
108
|
+
{sequential.map(palette => {
|
|
109
|
+
const colorOne = {
|
|
110
|
+
backgroundColor: colorPalettes[palette][2]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const colorTwo = {
|
|
114
|
+
backgroundColor: colorPalettes[palette][3]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const colorThree = {
|
|
118
|
+
backgroundColor: colorPalettes[palette][5]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<button
|
|
123
|
+
title={palette}
|
|
124
|
+
key={palette}
|
|
125
|
+
onClick={e => {
|
|
126
|
+
e.preventDefault()
|
|
127
|
+
updateConfig({ ...config, palette })
|
|
128
|
+
}}
|
|
129
|
+
className={config.palette === palette ? 'selected' : ''}
|
|
130
|
+
>
|
|
131
|
+
<span style={colorOne}></span>
|
|
132
|
+
<span style={colorTwo}></span>
|
|
133
|
+
<span style={colorThree}></span>
|
|
134
|
+
</button>
|
|
135
|
+
)
|
|
136
|
+
})}
|
|
137
|
+
</ul>
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
{visSupportsNonSequentialPallete() && (
|
|
141
|
+
<>
|
|
142
|
+
<span>Non-Sequential</span>
|
|
143
|
+
<ul className='color-palette'>
|
|
144
|
+
{nonSequential.map(palette => {
|
|
145
|
+
const colorOne = {
|
|
146
|
+
backgroundColor: colorPalettes[palette][2]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const colorTwo = {
|
|
150
|
+
backgroundColor: colorPalettes[palette][4]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const colorThree = {
|
|
154
|
+
backgroundColor: colorPalettes[palette][6]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<button
|
|
159
|
+
title={palette}
|
|
160
|
+
key={palette}
|
|
161
|
+
onClick={e => {
|
|
162
|
+
e.preventDefault()
|
|
163
|
+
updateConfig({ ...config, palette })
|
|
164
|
+
}}
|
|
165
|
+
className={config.palette === palette ? 'selected' : ''}
|
|
166
|
+
>
|
|
167
|
+
<span style={colorOne}></span>
|
|
168
|
+
<span style={colorTwo}></span>
|
|
169
|
+
<span style={colorThree}></span>
|
|
170
|
+
</button>
|
|
171
|
+
)
|
|
172
|
+
})}
|
|
173
|
+
</ul>
|
|
174
|
+
</>
|
|
175
|
+
)}
|
|
176
|
+
</>
|
|
177
|
+
)}
|
|
178
|
+
{(config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar') && (
|
|
179
|
+
<>
|
|
180
|
+
<InputToggle section='twoColor' fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={config.twoColor.isPaletteReversed} />
|
|
181
|
+
<ul className='color-palette'>
|
|
182
|
+
{twoColorPalettes.map(palette => {
|
|
183
|
+
const colorOne = {
|
|
184
|
+
backgroundColor: twoColorPalette[palette][0]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const colorTwo = {
|
|
188
|
+
backgroundColor: twoColorPalette[palette][1]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<button
|
|
193
|
+
title={palette}
|
|
194
|
+
key={palette}
|
|
195
|
+
onClick={e => {
|
|
196
|
+
e.preventDefault()
|
|
197
|
+
updateConfig({ ...config, twoColor: { ...config.twoColor, palette } })
|
|
198
|
+
}}
|
|
199
|
+
className={config.twoColor.palette === palette ? 'selected' : ''}
|
|
200
|
+
>
|
|
201
|
+
<span className='two-color' style={colorOne}></span>
|
|
202
|
+
<span className='two-color' style={colorTwo}></span>
|
|
203
|
+
</button>
|
|
204
|
+
)
|
|
205
|
+
})}
|
|
206
|
+
</ul>
|
|
207
|
+
</>
|
|
208
|
+
)}
|
|
209
|
+
|
|
210
|
+
{visHasDataCutoff() && (
|
|
211
|
+
<>
|
|
212
|
+
<TextField
|
|
213
|
+
value={config.dataCutoff}
|
|
214
|
+
type='number'
|
|
215
|
+
fieldName='dataCutoff'
|
|
216
|
+
className='number-narrow'
|
|
217
|
+
label='Data Cutoff'
|
|
218
|
+
updateField={updateField}
|
|
219
|
+
tooltip={
|
|
220
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
221
|
+
<Tooltip.Target>
|
|
222
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
223
|
+
</Tooltip.Target>
|
|
224
|
+
<Tooltip.Content>
|
|
225
|
+
<p>Any value below the cut-off value is included in a special "less than" category. This option supports special conditions like suppressed data.</p>
|
|
226
|
+
</Tooltip.Content>
|
|
227
|
+
</Tooltip>
|
|
228
|
+
}
|
|
229
|
+
/>
|
|
230
|
+
</>
|
|
231
|
+
)}
|
|
232
|
+
{visSupportsBarThickness() && config.orientation === 'horizontal' && !config.isLollipopChart && config.yAxis.labelPlacement !== 'On Bar' && <TextField type='number' value={config.barHeight || '25'} fieldName='barHeight' label=' Bar Thickness' updateField={updateField} min={15} />}
|
|
233
|
+
{((config.visualizationType === 'Bar' && config.orientation !== 'horizontal') || config.visualizationType === 'Combo') && <TextField value={config.barThickness} type='number' fieldName='barThickness' label='Bar Thickness' updateField={updateField} />}
|
|
234
|
+
{visSupportsBarSpace() && <TextField type='number' value={config.barSpace || '15'} fieldName='barSpace' label='Bar Space' updateField={updateField} min={0} />}
|
|
235
|
+
{(config.visualizationType === 'Bar' || config.visualizationType === 'Line' || config.visualizationType === 'Combo') && <CheckBox value={config.topAxis.hasLine} section='topAxis' fieldName='hasLine' label='Add Top Axis Line' updateField={updateField} />}
|
|
236
|
+
|
|
237
|
+
{config.visualizationType === 'Spark Line' && (
|
|
238
|
+
<div className='cove-accordion__panel-section checkbox-group'>
|
|
239
|
+
<CheckBox value={visual?.border} section='visual' fieldName='border' label='Show Border' updateField={updateField} />
|
|
240
|
+
<CheckBox value={visual?.borderColorTheme} section='visual' fieldName='borderColorTheme' label='Use Border Color Theme' updateField={updateField} />
|
|
241
|
+
<CheckBox value={visual?.accent} section='visual' fieldName='accent' label='Use Accent Style' updateField={updateField} />
|
|
242
|
+
<CheckBox value={visual?.background} section='visual' fieldName='background' label='Use Theme Background Color' updateField={updateField} />
|
|
243
|
+
<CheckBox value={visual?.hideBackgroundColor} section='visual' fieldName='hideBackgroundColor' label='Hide Background Color' updateField={updateField} />
|
|
244
|
+
</div>
|
|
245
|
+
)}
|
|
246
|
+
|
|
247
|
+
{(config.visualizationType === 'Line' || config.visualizationType === 'Combo') && <CheckBox value={config.showLineSeriesLabels} fieldName='showLineSeriesLabels' label='Append Series Name to End of Line Charts' updateField={updateField} />}
|
|
248
|
+
{(config.visualizationType === 'Line' || config.visualizationType === 'Combo') && config.showLineSeriesLabels && <CheckBox value={config.colorMatchLineSeriesLabels} fieldName='colorMatchLineSeriesLabels' label='Match Series Color to Name at End of Line Charts' updateField={updateField} />}
|
|
249
|
+
|
|
250
|
+
{visSupportsTooltipLines() && (
|
|
251
|
+
<>
|
|
252
|
+
<CheckBox value={visual.verticalHoverLine} fieldName='verticalHoverLine' section='visual' label='Vertical Hover Line' updateField={updateField} />
|
|
253
|
+
<CheckBox value={visual.horizontalHoverLine} fieldName='horizontalHoverLine' section='visual' label='Horizontal Hover Line' updateField={updateField} />
|
|
254
|
+
</>
|
|
255
|
+
)}
|
|
256
|
+
{visSupportsTooltipOpacity() && (
|
|
257
|
+
<label>
|
|
258
|
+
<span className='edit-label column-heading'>Tooltip Opacity</span>
|
|
259
|
+
<input
|
|
260
|
+
type='number'
|
|
261
|
+
value={config.tooltips.opacity ? config.tooltips.opacity : 100}
|
|
262
|
+
onChange={e =>
|
|
263
|
+
updateConfig({
|
|
264
|
+
...config,
|
|
265
|
+
tooltips: {
|
|
266
|
+
...config.tooltips,
|
|
267
|
+
opacity: e.target.value
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
/>
|
|
272
|
+
</label>
|
|
273
|
+
)}
|
|
274
|
+
{config.visualizationType === 'Bar' && <CheckBox value={config.tooltips.singleSeries} fieldName='singleSeries' section='tooltips' label='SHOW HOVER FOR SINGLE DATA SERIES' updateField={updateField} />}
|
|
275
|
+
|
|
276
|
+
<label>
|
|
277
|
+
<span className='edit-label column-heading'>No Data Message</span>
|
|
278
|
+
<input
|
|
279
|
+
type='text'
|
|
280
|
+
value={config.chartMessage.noData ? config.chartMessage.noData : ''}
|
|
281
|
+
onChange={e =>
|
|
282
|
+
updateConfig({
|
|
283
|
+
...config,
|
|
284
|
+
chartMessage: {
|
|
285
|
+
...config.chartMessage,
|
|
286
|
+
noData: e.target.value
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
/>
|
|
291
|
+
</label>
|
|
292
|
+
</AccordionItemPanel>
|
|
293
|
+
</AccordionItem>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export default PanelVisual
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ForestPlotSettings from './Panel.ForestPlotSettings.js'
|
|
2
|
+
import Series from './Panel.Series.js'
|
|
3
|
+
import Regions from './Panel.Regions.js'
|
|
4
|
+
import General from './Panel.General.js'
|
|
5
|
+
import BoxPlot from './Panel.BoxPlot.js'
|
|
6
|
+
import Visual from './Panel.Visual.js'
|
|
7
|
+
|
|
8
|
+
const Panels = {
|
|
9
|
+
ForestPlot: ForestPlotSettings,
|
|
10
|
+
Series: Series,
|
|
11
|
+
Regions,
|
|
12
|
+
General,
|
|
13
|
+
BoxPlot,
|
|
14
|
+
Visual
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default Panels
|