@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.
Files changed (104) hide show
  1. package/dist/cdcchart.js +35740 -35027
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/tall-data.json +98 -0
  4. package/examples/feature/forest-plot/forest-plot.json +63 -19
  5. package/examples/feature/forest-plot/linear.json +52 -3
  6. package/examples/feature/forest-plot/log.json +26 -0
  7. package/examples/feature/forest-plot/logarithmic.json +0 -35
  8. package/examples/feature/line/line-chart-preliminary.json +393 -0
  9. package/examples/feature/regions/index.json +9 -5
  10. package/examples/feature/scatterplot/scatterplot.json +272 -33
  11. package/index.html +10 -8
  12. package/package.json +2 -2
  13. package/src/CdcChart.tsx +70 -234
  14. package/src/ConfigContext.tsx +6 -0
  15. package/src/_stories/ChartEditor.stories.tsx +22 -0
  16. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  17. package/src/_stories/_mock/pie_config.json +192 -0
  18. package/src/_stories/_mock/pie_data.json +218 -0
  19. package/src/_stories/_mock/preliminary_mock.json +346 -0
  20. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
  21. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +2 -26
  22. package/src/components/AreaChart/index.tsx +4 -0
  23. package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
  24. package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
  25. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +108 -0
  26. package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
  27. package/src/components/BarChart/components/BarChart.jsx +39 -0
  28. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  29. package/src/components/BarChart/components/context.tsx +13 -0
  30. package/src/components/BarChart/index.tsx +3 -0
  31. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +10 -9
  32. package/src/components/BoxPlot/index.tsx +3 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
  34. package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
  35. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  36. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
  37. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
  38. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
  39. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
  40. package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
  41. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
  42. package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
  43. package/src/components/EditorPanel/components/panels.scss +72 -0
  44. package/src/components/EditorPanel/editor-panel.scss +739 -0
  45. package/src/components/EditorPanel/index.tsx +3 -0
  46. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -2
  47. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  48. package/src/components/Forecasting/index.tsx +3 -0
  49. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  50. package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
  51. package/src/components/ForestPlot/index.tsx +1 -209
  52. package/src/components/Legend/Legend.Component.tsx +199 -0
  53. package/src/components/Legend/Legend.tsx +28 -0
  54. package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
  55. package/src/components/Legend/index.tsx +3 -0
  56. package/src/components/LineChart/LineChartProps.ts +29 -0
  57. package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
  58. package/src/components/LineChart/helpers.ts +45 -0
  59. package/src/components/LineChart/index.tsx +111 -23
  60. package/src/components/LinearChart.jsx +55 -72
  61. package/src/components/PairedBarChart.jsx +4 -2
  62. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
  63. package/src/components/PieChart/index.tsx +3 -0
  64. package/src/components/Regions/components/Regions.tsx +144 -0
  65. package/src/components/Regions/index.tsx +3 -0
  66. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  67. package/src/components/ScatterPlot/index.tsx +3 -0
  68. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  69. package/src/components/Sparkline/index.tsx +3 -0
  70. package/src/data/initial-state.js +10 -8
  71. package/src/helpers/abbreviateNumber.ts +17 -0
  72. package/src/helpers/computeMarginBottom.ts +55 -0
  73. package/src/helpers/filterData.ts +18 -0
  74. package/src/helpers/generateColorsArray.ts +8 -0
  75. package/src/helpers/getQuartiles.ts +30 -0
  76. package/src/helpers/handleChartAriaLabels.ts +19 -0
  77. package/src/helpers/handleLineType.ts +18 -0
  78. package/src/helpers/lineOptions.ts +18 -0
  79. package/src/helpers/sort.ts +7 -0
  80. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  81. package/src/hooks/useBarChart.js +7 -6
  82. package/src/hooks/useHighlightedBars.js +1 -1
  83. package/src/hooks/useMinMax.ts +3 -3
  84. package/src/hooks/useScales.ts +19 -6
  85. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
  86. package/src/scss/main.scss +0 -3
  87. package/src/types/ChartConfig.ts +167 -23
  88. package/src/types/ChartContext.ts +34 -12
  89. package/src/types/ForestPlot.ts +7 -14
  90. package/src/types/Label.ts +7 -0
  91. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  92. package/src/ConfigContext.jsx +0 -5
  93. package/src/components/BarChart.StackedVertical.tsx +0 -91
  94. package/src/components/BarChart.jsx +0 -30
  95. package/src/components/EditorPanel.jsx +0 -3356
  96. package/src/components/ForestPlot/Readme.md +0 -0
  97. package/src/components/Legend.jsx +0 -310
  98. package/src/components/LineChart/LineChart.Circle.tsx +0 -105
  99. package/src/scss/LinearChart.scss +0 -0
  100. package/src/scss/editor-panel.scss +0 -745
  101. package/src/scss/legend.scss +0 -206
  102. package/src/scss/mixins.scss +0 -0
  103. package/src/scss/variables.scss +0 -1
  104. 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 '../ConfigContext'
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