@cdc/chart 4.24.1 → 4.24.3
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 +48948 -37923
- package/examples/{private/combo.json → chart-regression-1.json} +40 -31
- package/examples/chart-regression-2.json +2360 -0
- package/examples/feature/filters/url-filter.json +1076 -0
- package/examples/feature/line/line-chart-preliminary.json +84 -37
- package/examples/feature/line/line-chart.json +2 -1
- package/examples/feature/regions/index.json +55 -5
- package/examples/feature/sankey/sankey-example-data.json +1364 -0
- package/examples/feature/sankey/sankey_chart_data.csv +20 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
- package/examples/sparkline.json +868 -0
- package/index.html +128 -121
- package/package.json +4 -2
- package/src/CdcChart.tsx +73 -38
- package/src/_stories/ChartEditor.stories.tsx +15 -4
- package/src/_stories/_mock/pie_config.json +4 -3
- package/src/_stories/_mock/url_filter.json +1076 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
- package/src/components/AreaChart/components/AreaChart.jsx +2 -25
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -49
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +36 -41
- package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -64
- package/src/components/BoxPlot/BoxPlot.jsx +11 -9
- package/src/components/DeviationBar.jsx +3 -3
- package/src/components/EditorPanel/EditorPanel.tsx +1717 -1961
- package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
- package/src/components/EditorPanel/components/{Panel.ForestPlotSettings.tsx → Panels/Panel.ForestPlotSettings.tsx} +16 -7
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
- package/src/components/EditorPanel/components/{Panel.Regions.tsx → Panels/Panel.Regions.tsx} +6 -6
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +108 -0
- package/src/components/EditorPanel/components/{Panel.Series.tsx → Panels/Panel.Series.tsx} +50 -6
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +338 -0
- package/src/components/EditorPanel/components/Panels/index.tsx +19 -0
- package/src/components/EditorPanel/components/panels.scss +11 -0
- package/src/components/EditorPanel/editor-panel.scss +1 -13
- package/src/components/EditorPanel/useEditorPermissions.js +44 -13
- package/src/components/Legend/Legend.Component.tsx +207 -0
- package/src/components/Legend/Legend.tsx +8 -327
- package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
- package/src/components/LineChart/LineChartProps.ts +2 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +85 -52
- package/src/components/LineChart/helpers.ts +3 -3
- package/src/components/LineChart/index.tsx +99 -23
- package/src/components/LinearChart.jsx +12 -33
- package/src/components/PairedBarChart.jsx +10 -12
- package/src/components/PieChart/PieChart.tsx +80 -27
- package/src/components/Regions/components/Regions.tsx +120 -69
- package/src/components/Sankey/index.tsx +434 -0
- package/src/components/Sankey/sankey.scss +153 -0
- package/src/components/Sankey/types/index.ts +16 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
- package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
- package/src/components/Sparkline/index.scss +3 -0
- package/src/components/Sparkline/index.tsx +1 -1
- package/src/components/ZoomBrush.tsx +2 -1
- package/src/data/initial-state.js +51 -4
- package/src/helpers/computeMarginBottom.ts +4 -3
- package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
- package/src/hooks/useBarChart.js +5 -2
- package/src/hooks/useHighlightedBars.js +1 -1
- package/src/hooks/useMinMax.ts +3 -3
- package/src/hooks/useScales.ts +28 -18
- package/src/hooks/useTooltip.tsx +19 -14
- package/src/scss/main.scss +8 -96
- package/src/types/ChartConfig.ts +47 -20
- package/src/types/ChartContext.ts +17 -4
- package/src/types/Label.ts +7 -0
- package/examples/private/chart-t.json +0 -3740
- package/examples/private/epi-data.csv +0 -13
- package/examples/private/epi-data.json +0 -62
- package/examples/private/epi.json +0 -403
- package/examples/private/occupancy.json +0 -109283
- package/examples/private/prod-line-config.json +0 -401
- package/examples/private/region-data.json +0 -822
- package/examples/private/region-testing.json +0 -312
- package/examples/private/scaling.json +0 -45325
- package/examples/private/testing-data.json +0 -1739
- package/examples/private/testing.json +0 -816
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +0 -109
- package/src/components/EditorPanel/components/Panels.tsx +0 -13
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
export type EditorPanelContext = {
|
|
4
|
+
addNewExclusion?: Function
|
|
5
|
+
updateField?: Function
|
|
6
|
+
// warning messages updated within EditorPanel
|
|
7
|
+
warningMsg?: { maxMsg?: string; minMsg?: string; rightMaxMessage?: string; minMsgRight?: string }
|
|
8
|
+
// current enabled chart types in useEditorPermissions file
|
|
9
|
+
enabledChartTypes?: string[]
|
|
10
|
+
showBarStyleOptions?: boolean
|
|
11
|
+
getDataValueOptions?: Function
|
|
12
|
+
data?: object[]
|
|
13
|
+
// function used on inputs, selects, etc. to update config values.
|
|
14
|
+
getColumns?: Function
|
|
15
|
+
getDataValues?: Function
|
|
16
|
+
handleAddNewHighlightedBar?: Function
|
|
17
|
+
handleHighlightedBarLegendLabel?: Function
|
|
18
|
+
handleUpdateHighlightedBar?: Function
|
|
19
|
+
handleUpdateHighlightedBarColor?: Function
|
|
20
|
+
handleUpdateHighlightedBorderWidth?: Function
|
|
21
|
+
highlightedBarValues?: Function
|
|
22
|
+
highlightedSeriesValues?: Function
|
|
23
|
+
isPaletteReversed?: boolean
|
|
24
|
+
handleRemoveHighlightedBar?: Function
|
|
25
|
+
setLollipopShape?: Function
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const EditorPanelContext = createContext<EditorPanelContext>(null)
|
|
29
|
+
|
|
30
|
+
export const useEditorPanelContext = () => {
|
|
31
|
+
const editorPanelCtx = useContext(EditorPanelContext)
|
|
32
|
+
|
|
33
|
+
if (editorPanelCtx === null) {
|
|
34
|
+
throw new Error('COVE: editor panel context is null.')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return editorPanelCtx
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default EditorPanelContext
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { useContext, FC } from 'react'
|
|
2
|
+
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
3
|
+
import { TextField } from '@cdc/core/components/EditorPanel/Inputs'
|
|
4
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
5
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
6
|
+
import ConfigContext from '../../../../ConfigContext'
|
|
7
|
+
import { useEditorPanelContext } from '../../EditorPanelContext'
|
|
8
|
+
|
|
9
|
+
// types
|
|
10
|
+
import { type PanelProps } from './../PanelProps'
|
|
11
|
+
|
|
12
|
+
const PanelBoxPlot: FC<PanelProps> = props => {
|
|
13
|
+
const { config } = useContext(ConfigContext)
|
|
14
|
+
const { boxplot } = config
|
|
15
|
+
if (config.visualizationType !== 'Box Plot') return
|
|
16
|
+
const { updateField } = useEditorPanelContext()
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<AccordionItem>
|
|
20
|
+
<AccordionItemHeading>
|
|
21
|
+
<AccordionItemButton>{props.name}</AccordionItemButton>
|
|
22
|
+
</AccordionItemHeading>
|
|
23
|
+
<AccordionItemPanel>
|
|
24
|
+
<h4 style={{ fontSize: '18px' }}>Labels for 5-Number Summary</h4>
|
|
25
|
+
|
|
26
|
+
{/* max */}
|
|
27
|
+
<TextField
|
|
28
|
+
type='text'
|
|
29
|
+
value={boxplot.labels.maximum}
|
|
30
|
+
fieldName='maximum'
|
|
31
|
+
section='boxplot'
|
|
32
|
+
subsection='labels'
|
|
33
|
+
label='Maximum'
|
|
34
|
+
updateField={updateField}
|
|
35
|
+
tooltip={
|
|
36
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
37
|
+
<Tooltip.Target>
|
|
38
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
39
|
+
</Tooltip.Target>
|
|
40
|
+
<Tooltip.Content>
|
|
41
|
+
<p>Highest value, excluding outliers</p>
|
|
42
|
+
</Tooltip.Content>
|
|
43
|
+
</Tooltip>
|
|
44
|
+
}
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
{/* Q3 */}
|
|
48
|
+
<TextField
|
|
49
|
+
type='text'
|
|
50
|
+
value={boxplot.labels.q3}
|
|
51
|
+
fieldName='q3'
|
|
52
|
+
section='boxplot'
|
|
53
|
+
subsection='labels'
|
|
54
|
+
label='Upper Quartile'
|
|
55
|
+
updateField={updateField}
|
|
56
|
+
tooltip={
|
|
57
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
58
|
+
<Tooltip.Target>
|
|
59
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
60
|
+
</Tooltip.Target>
|
|
61
|
+
<Tooltip.Content>
|
|
62
|
+
<p>Represented by top line of box. 25% of data are higher.</p>
|
|
63
|
+
</Tooltip.Content>
|
|
64
|
+
</Tooltip>
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
{/* median */}
|
|
69
|
+
<TextField
|
|
70
|
+
type='text'
|
|
71
|
+
value={boxplot.labels.median}
|
|
72
|
+
fieldName='median'
|
|
73
|
+
section='boxplot'
|
|
74
|
+
subsection='labels'
|
|
75
|
+
label='Median'
|
|
76
|
+
updateField={updateField}
|
|
77
|
+
tooltip={
|
|
78
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
79
|
+
<Tooltip.Target>
|
|
80
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
81
|
+
</Tooltip.Target>
|
|
82
|
+
<Tooltip.Content>
|
|
83
|
+
<p>Middle data point. Half of data are higher value.</p>
|
|
84
|
+
</Tooltip.Content>
|
|
85
|
+
</Tooltip>
|
|
86
|
+
}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
{/* q1 */}
|
|
90
|
+
<TextField
|
|
91
|
+
type='text'
|
|
92
|
+
value={boxplot.labels.q1}
|
|
93
|
+
fieldName='q1'
|
|
94
|
+
section='boxplot'
|
|
95
|
+
subsection='labels'
|
|
96
|
+
label='Lower Quartile'
|
|
97
|
+
updateField={updateField}
|
|
98
|
+
tooltip={
|
|
99
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
100
|
+
<Tooltip.Target>
|
|
101
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
102
|
+
</Tooltip.Target>
|
|
103
|
+
<Tooltip.Content>
|
|
104
|
+
<p>Represented by bottom line of box. 25% of data are lower.</p>
|
|
105
|
+
</Tooltip.Content>
|
|
106
|
+
</Tooltip>
|
|
107
|
+
}
|
|
108
|
+
/>
|
|
109
|
+
|
|
110
|
+
{/* minimum */}
|
|
111
|
+
<TextField
|
|
112
|
+
type='text'
|
|
113
|
+
value={boxplot.labels.minimum}
|
|
114
|
+
fieldName='minimum'
|
|
115
|
+
section='boxplot'
|
|
116
|
+
subsection='labels'
|
|
117
|
+
label='Minimum'
|
|
118
|
+
updateField={updateField}
|
|
119
|
+
tooltip={
|
|
120
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
121
|
+
<Tooltip.Target>
|
|
122
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
123
|
+
</Tooltip.Target>
|
|
124
|
+
<Tooltip.Content>
|
|
125
|
+
<p>Lowest value, excluding outliers</p>
|
|
126
|
+
</Tooltip.Content>
|
|
127
|
+
</Tooltip>
|
|
128
|
+
}
|
|
129
|
+
/>
|
|
130
|
+
<br />
|
|
131
|
+
<h4 style={{ fontSize: '18px' }}>Labels for Additional Measures</h4>
|
|
132
|
+
|
|
133
|
+
{/* iqr */}
|
|
134
|
+
<TextField type='text' value={boxplot.labels.iqr} fieldName='iqr' section='boxplot' subsection='labels' label='Interquartile Range' updateField={updateField} />
|
|
135
|
+
{/* count */}
|
|
136
|
+
<TextField type='text' value={boxplot.labels.total} fieldName='total' section='boxplot' subsection='labels' label='Total' updateField={updateField} />
|
|
137
|
+
{/* mean */}
|
|
138
|
+
<TextField type='text' value={boxplot.labels.mean} fieldName='mean' section='boxplot' subsection='labels' label='Mean' updateField={updateField} />
|
|
139
|
+
{/* outliers */}
|
|
140
|
+
<TextField type='text' value={boxplot.labels.outliers} fieldName='outliers' section='boxplot' subsection='labels' label='Outliers' updateField={updateField} />
|
|
141
|
+
{/* values */}
|
|
142
|
+
<TextField type='text' value={boxplot.labels.values} fieldName='values' section='boxplot' subsection='labels' label='Values' updateField={updateField} />
|
|
143
|
+
</AccordionItemPanel>
|
|
144
|
+
</AccordionItem>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export default PanelBoxPlot
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import WarningImage from '../../../images/warning.svg'
|
|
1
|
+
import { useContext, FC } from 'react'
|
|
2
|
+
|
|
3
|
+
// cdc
|
|
5
4
|
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
6
5
|
import Icon from '@cdc/core/components/ui/Icon'
|
|
7
|
-
import { type ChartContext } from '../../../types/ChartContext'
|
|
8
6
|
import { Select, CheckBox, TextField } from '@cdc/core/components/EditorPanel/Inputs'
|
|
9
|
-
import
|
|
7
|
+
import WarningImage from '../../../../images/warning.svg'
|
|
8
|
+
|
|
9
|
+
// contexts
|
|
10
|
+
import ConfigContext from '../../../../ConfigContext'
|
|
11
|
+
|
|
12
|
+
// types
|
|
13
|
+
import { type ChartContext } from '../../../../types/ChartContext'
|
|
14
|
+
import { type PanelProps } from '../PanelProps'
|
|
10
15
|
|
|
11
16
|
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
12
17
|
|
|
13
|
-
const ForestPlotSettings = ({ name }
|
|
18
|
+
const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
|
|
14
19
|
const { config, rawData: unfilteredData, updateConfig } = useContext<ChartContext>(ConfigContext)
|
|
20
|
+
if (config.visualizationType !== 'Forest Plot') return
|
|
15
21
|
|
|
22
|
+
// todo: get from editor context?
|
|
16
23
|
const enforceRestrictions = updatedConfig => {
|
|
17
24
|
if (updatedConfig.orientation === 'horizontal') {
|
|
18
25
|
updatedConfig.labels = false
|
|
@@ -26,6 +33,7 @@ const ForestPlotSettings = ({ name }: PanelProps) => {
|
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
// todo: get from editor context?
|
|
29
37
|
const getColumns = (filter = true) => {
|
|
30
38
|
let columns = {}
|
|
31
39
|
unfilteredData.forEach(row => {
|
|
@@ -50,6 +58,7 @@ const ForestPlotSettings = ({ name }: PanelProps) => {
|
|
|
50
58
|
return Object.keys(columns)
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
// todo: editor context?
|
|
53
62
|
const updateField = (section, subsection, fieldName, newValue) => {
|
|
54
63
|
if (section === 'boxplot' && subsection === 'legend') {
|
|
55
64
|
updateConfig({
|
|
@@ -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
|
package/src/components/EditorPanel/components/{Panel.Regions.tsx → Panels/Panel.Regions.tsx}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { memo, useContext } from 'react'
|
|
2
|
-
import { useEditorPermissions } from '
|
|
2
|
+
import { useEditorPermissions } from '../../useEditorPermissions.js'
|
|
3
3
|
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
4
|
-
import { type ChartConfig } from '
|
|
4
|
+
import { type ChartConfig } from '../../../../types/ChartConfig.js'
|
|
5
5
|
import { TextField, Select } from '@cdc/core/components/EditorPanel/Inputs'
|
|
6
6
|
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
7
7
|
import Icon from '@cdc/core/components/ui/Icon'
|
|
8
|
-
import { type ChartContext } from '
|
|
9
|
-
import { type PanelProps } from '
|
|
10
|
-
import ConfigContext from '
|
|
8
|
+
import { type ChartContext } from '../../../../types/ChartContext.js'
|
|
9
|
+
import { type PanelProps } from '../PanelProps.js'
|
|
10
|
+
import ConfigContext from '../../../../ConfigContext.js'
|
|
11
11
|
|
|
12
12
|
const RegionSettings = memo(({ config, updateConfig }: { config: ChartConfig; updateConfig: Function }) => {
|
|
13
13
|
let regionUpdate = (fieldName, value, i) => {
|
|
@@ -104,7 +104,7 @@ const RegionSettings = memo(({ config, updateConfig }: { config: ChartConfig; up
|
|
|
104
104
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
105
105
|
</Tooltip.Target>
|
|
106
106
|
<Tooltip.Content>
|
|
107
|
-
<p>
|
|
107
|
+
<p>When using categorical (linear scale) match the data set value. When using date (linear / date time scale) match the x-axis value.</p>
|
|
108
108
|
</Tooltip.Content>
|
|
109
109
|
</Tooltip>
|
|
110
110
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../../../../ConfigContext'
|
|
3
|
+
import { CheckBox, TextField } from '@cdc/core/components/EditorPanel/Inputs'
|
|
4
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
5
|
+
|
|
6
|
+
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
7
|
+
import EditorPanelContext, { type EditorPanelContext as EPContext } from '../../EditorPanelContext'
|
|
8
|
+
|
|
9
|
+
const SankeySettings = () => {
|
|
10
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
11
|
+
const data = config.data?.[0]
|
|
12
|
+
const { updateField } = useContext<EPContext>(EditorPanelContext)
|
|
13
|
+
|
|
14
|
+
if (config.visualizationType !== 'Sankey') return
|
|
15
|
+
|
|
16
|
+
const updateStoryNode = (fieldName, value, i) => {
|
|
17
|
+
let storyNodes = []
|
|
18
|
+
|
|
19
|
+
if (data?.storyNodeText) {
|
|
20
|
+
storyNodes = [...data?.storyNodeText]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
storyNodes[i][fieldName] = value
|
|
24
|
+
console.log('story nodes', storyNodes[i])
|
|
25
|
+
updateConfig({
|
|
26
|
+
...config,
|
|
27
|
+
sankey: {
|
|
28
|
+
...config.sankey,
|
|
29
|
+
data: {
|
|
30
|
+
...config.sankey.data,
|
|
31
|
+
storyNodeText: storyNodes
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const addStoryNode = () => {
|
|
38
|
+
const newData = data
|
|
39
|
+
|
|
40
|
+
newData.storyNodeText.push({
|
|
41
|
+
StoryNode: '',
|
|
42
|
+
segmentTextBefore: '',
|
|
43
|
+
segmentTextAfter: ''
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
updateConfig({
|
|
47
|
+
...config,
|
|
48
|
+
sankey: {
|
|
49
|
+
...config.sankey,
|
|
50
|
+
data: [{ ...newData }]
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const removeStoryNode = index => {
|
|
56
|
+
const newData = data
|
|
57
|
+
newData.storyNodeText.splice(index, 1)
|
|
58
|
+
|
|
59
|
+
updateConfig({ ...config, sankey: { ...config.sankey, data: { ...newData } } })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<AccordionItem>
|
|
64
|
+
<AccordionItemHeading>
|
|
65
|
+
<AccordionItemButton>Sankey Settings</AccordionItemButton>
|
|
66
|
+
</AccordionItemHeading>
|
|
67
|
+
<AccordionItemPanel>
|
|
68
|
+
{data?.storyNodeText &&
|
|
69
|
+
data?.storyNodeText.map(({ StoryNode, segmentTextBefore, segmentTextAfter }, i) => (
|
|
70
|
+
<div key={i} style={{ border: '1px solid black', margin: '15px auto', padding: '15px', borderRadius: '10px' }}>
|
|
71
|
+
<label>
|
|
72
|
+
Story Node Text
|
|
73
|
+
<input type='text' value={StoryNode} fieldName='StoryNode' label='StoryNode' onChange={e => updateStoryNode('StoryNode', e.target.value, i)} />
|
|
74
|
+
</label>
|
|
75
|
+
<label>
|
|
76
|
+
Story Text Before
|
|
77
|
+
<input type='text' value={segmentTextBefore} fieldName='segmentTextBefore' label='Segment Text Before' onChange={e => updateStoryNode('segmentTextBefore', e.target.value, i)} />
|
|
78
|
+
</label>
|
|
79
|
+
<label>
|
|
80
|
+
Story Text After
|
|
81
|
+
<input type='text' value={segmentTextAfter} fieldName='segmentTextAfter' label='Segment Text After' onChange={e => updateStoryNode('segmentTextAfter', e.target.value, i)} />
|
|
82
|
+
</label>
|
|
83
|
+
<Button onClick={e => removeStoryNode(i)} className='btn' style={{ background: 'tomato' }}>
|
|
84
|
+
Remove Story Node
|
|
85
|
+
</Button>
|
|
86
|
+
</div>
|
|
87
|
+
))}
|
|
88
|
+
{`Total Story Nodes: ${data?.storyNodeText?.length}`}
|
|
89
|
+
{data?.storyNodeText?.length < 3 && (
|
|
90
|
+
<button
|
|
91
|
+
type='button'
|
|
92
|
+
className='btn full-width'
|
|
93
|
+
onClick={e => {
|
|
94
|
+
e.preventDefault()
|
|
95
|
+
addStoryNode()
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
Add StoryNode
|
|
99
|
+
</button>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
<CheckBox value={config.enableTooltips} fieldName='enableTooltips' label='Enable Tooltips' updateField={updateField} />
|
|
103
|
+
</AccordionItemPanel>
|
|
104
|
+
</AccordionItem>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default SankeySettings
|
|
@@ -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'
|
|
@@ -424,16 +424,56 @@ const SeriesDropdownConfidenceInterval = props => {
|
|
|
424
424
|
)
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
const
|
|
427
|
+
const SeriesInputWeight = props => {
|
|
428
428
|
const { series, index: i } = props
|
|
429
429
|
const { config, updateConfig } = useContext(ConfigContext)
|
|
430
|
-
const
|
|
430
|
+
const adjustableWeightSeriesTypes = ['Line', 'Combo', 'dashed-sm', 'dashed-md', 'dashed-lg']
|
|
431
|
+
|
|
432
|
+
if (!adjustableWeightSeriesTypes.includes(series.type)) return
|
|
433
|
+
|
|
434
|
+
const changeSeriesWeight = (i, value, min, max) => {
|
|
435
|
+
let series = [...config.series]
|
|
436
|
+
let seriesLabelsCopy = { ...config.runtime.seriesLabels }
|
|
437
|
+
series[i].weight = !value ? value : Math.max(Number(min), Math.min(Number(max), Number(value)))
|
|
438
|
+
seriesLabelsCopy[series[i].dataKey] = series[i].weight ? series[i].weight : series[i].dataKey
|
|
439
|
+
|
|
440
|
+
const newConfig = {
|
|
441
|
+
...config,
|
|
442
|
+
series,
|
|
443
|
+
runtime: {
|
|
444
|
+
...config.runtime,
|
|
445
|
+
seriesLabels: seriesLabelsCopy
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
updateConfig(newConfig)
|
|
450
|
+
}
|
|
431
451
|
|
|
432
|
-
|
|
452
|
+
return (
|
|
453
|
+
<>
|
|
454
|
+
<label htmlFor='series-weight'>Line Weight</label>
|
|
455
|
+
<input
|
|
456
|
+
type='number'
|
|
457
|
+
key={`series-weight-${i}`}
|
|
458
|
+
value={series.weight ? series.weight : ''}
|
|
459
|
+
min="1"
|
|
460
|
+
max="9"
|
|
461
|
+
onChange={event => {
|
|
462
|
+
changeSeriesWeight(i, event.target.value, event.target.min, event.target.max)
|
|
463
|
+
}}
|
|
464
|
+
/>
|
|
465
|
+
</>
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const SeriesInputName = props => {
|
|
470
|
+
const { series, index: i } = props
|
|
471
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
472
|
+
const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart', 'Combo', 'Deviation Bar', 'Paired Bar', 'Scatter Plot', 'dashed-sm', 'dashed-md', 'dashed-lg']
|
|
433
473
|
|
|
434
474
|
if (!adjustableNameSeriesTypes.includes(series.type)) return
|
|
435
475
|
|
|
436
|
-
|
|
476
|
+
const changeSeriesName = (i, value) => {
|
|
437
477
|
let series = [...config.series]
|
|
438
478
|
let seriesLabelsCopy = { ...config.runtime.seriesLabels }
|
|
439
479
|
series[i].name = value
|
|
@@ -470,6 +510,8 @@ const SeriesDisplayInTooltip = props => {
|
|
|
470
510
|
const { series, index } = props
|
|
471
511
|
const { config, updateConfig } = useContext(ConfigContext)
|
|
472
512
|
|
|
513
|
+
if (['Paired Bar', 'Scatter Plot', 'Deviation Bar'].includes(config.visualizationType)) return
|
|
514
|
+
|
|
473
515
|
const toggleTooltip = seriesIndex => {
|
|
474
516
|
let copiedSeries = [...config.series]
|
|
475
517
|
|
|
@@ -569,6 +611,7 @@ const SeriesItem = props => {
|
|
|
569
611
|
{chartsWithOptions.includes(config.visualizationType) && (
|
|
570
612
|
<AccordionItemPanel>
|
|
571
613
|
<Series.Input.Name series={series} index={i} />
|
|
614
|
+
<Series.Input.Weight series={series} index={i} />
|
|
572
615
|
<Series.Dropdown.SeriesType series={series} index={i} />
|
|
573
616
|
<Series.Dropdown.AxisPosition series={series} index={i} />
|
|
574
617
|
<Series.Dropdown.LineType series={series} index={i} />
|
|
@@ -604,7 +647,8 @@ const Series = {
|
|
|
604
647
|
ForecastingColor: SeriesDropdownForecastColor
|
|
605
648
|
},
|
|
606
649
|
Input: {
|
|
607
|
-
Name: SeriesInputName
|
|
650
|
+
Name: SeriesInputName,
|
|
651
|
+
Weight: SeriesInputWeight
|
|
608
652
|
},
|
|
609
653
|
Checkbox: {
|
|
610
654
|
DisplayInTooltip: SeriesDisplayInTooltip
|