@cdc/chart 4.24.4 → 4.24.7

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 (76) hide show
  1. package/dist/cdcchart.js +39611 -36038
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +9 -8
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +115 -71
  7. package/src/_stories/Chart.stories.tsx +26 -171
  8. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  9. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  10. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  11. package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
  12. package/src/_stories/_mock/bar-chart-suppressed.json +474 -0
  13. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  14. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  15. package/src/_stories/_mock/lollipop.json +171 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  17. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  19. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  20. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  21. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  22. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  23. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  24. package/src/components/Annotations/index.tsx +13 -0
  25. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  26. package/src/components/AreaChart/components/AreaChart.jsx +2 -2
  27. package/src/components/BarChart/components/BarChart.Horizontal.tsx +78 -71
  28. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  29. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  30. package/src/components/BarChart/components/BarChart.Vertical.tsx +100 -87
  31. package/src/components/BarChart/helpers/index.ts +102 -0
  32. package/src/components/DeviationBar.jsx +4 -2
  33. package/src/components/EditorPanel/EditorPanel.tsx +435 -613
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +135 -7
  36. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  37. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -5
  38. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  39. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  40. package/src/components/EditorPanel/components/panels.scss +4 -0
  41. package/src/components/EditorPanel/editor-panel.scss +19 -0
  42. package/src/components/EditorPanel/useEditorPermissions.js +23 -3
  43. package/src/components/Legend/Legend.Component.tsx +66 -15
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  45. package/src/components/Legend/helpers/index.ts +5 -0
  46. package/src/components/LineChart/LineChartProps.ts +16 -6
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +22 -11
  48. package/src/components/LineChart/helpers.ts +148 -10
  49. package/src/components/LineChart/index.tsx +71 -44
  50. package/src/components/LinearChart.jsx +184 -125
  51. package/src/components/PairedBarChart.jsx +9 -9
  52. package/src/components/PieChart/PieChart.tsx +4 -4
  53. package/src/components/Sankey/index.tsx +73 -20
  54. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  55. package/src/components/ZoomBrush.tsx +120 -55
  56. package/src/data/initial-state.js +14 -6
  57. package/src/helpers/handleChartTabbing.ts +8 -0
  58. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  59. package/src/hooks/{useBarChart.js → useBarChart.ts} +9 -22
  60. package/src/hooks/useColorScale.ts +1 -1
  61. package/src/hooks/useMinMax.ts +29 -5
  62. package/src/hooks/useScales.ts +48 -26
  63. package/src/hooks/useTooltip.tsx +62 -15
  64. package/src/scss/main.scss +69 -12
  65. package/src/types/ChartConfig.ts +53 -16
  66. package/src/types/ChartContext.ts +13 -0
  67. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  68. package/LICENSE +0 -201
  69. package/src/_stories/ChartLine.preliminary.tsx +0 -19
  70. package/src/_stories/ChartSuppress.stories.tsx +0 -19
  71. package/src/_stories/_mock/suppress_mock.json +0 -911
  72. package/src/helpers/computeMarginBottom.ts +0 -56
  73. package/src/helpers/filterData.ts +0 -18
  74. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  75. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  76. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -0,0 +1,306 @@
1
+ import React, { useContext } from 'react'
2
+ import ConfigContext from '../../../../ConfigContext.js'
3
+
4
+ // CDC Core
5
+ import Accordion from '@cdc/core/components/ui/Accordion'
6
+ import Button from '@cdc/core/components/elements/Button'
7
+ import _ from 'lodash'
8
+
9
+ // types
10
+ import { type PanelProps } from './../PanelProps'
11
+ // styles
12
+ import './../panels.scss'
13
+
14
+ const PanelAnnotate: React.FC<PanelProps> = props => {
15
+ const { updateConfig, config, unfilteredData, dimensions, isDraggingAnnotation } = useContext(ConfigContext)
16
+
17
+ const getColumns = (filter = true) => {
18
+ const columns = {}
19
+ unfilteredData.forEach(row => {
20
+ Object.keys(row).forEach(columnName => (columns[columnName] = true))
21
+ })
22
+
23
+ if (filter) {
24
+ Object.keys(columns).forEach(key => {
25
+ if ((config.series && config.series.filter(series => series.dataKey === key).length > 0) || (config.confidenceKeys && Object.keys(config.confidenceKeys).includes(key))) {
26
+ delete columns[key]
27
+ }
28
+ })
29
+ }
30
+
31
+ return Object.keys(columns)
32
+ }
33
+
34
+ const handleAnnotationUpdate = (value, property, index) => {
35
+ const svgContainer = document.querySelector('.chart-container > div > svg')?.getBoundingClientRect()
36
+ const newSvgDims = [svgContainer.width, svgContainer.height]
37
+ const annotations = [...config?.annotations]
38
+ annotations[index][property] = value
39
+ annotations[index].savedDimensions = newSvgDims
40
+
41
+ updateConfig({
42
+ ...config,
43
+ annotations
44
+ })
45
+ }
46
+
47
+ const handleAddAnnotation = () => {
48
+ const svgContainer = document.querySelector('.chart-container > div > svg')?.getBoundingClientRect()
49
+ const newSvgDims = [svgContainer.width, svgContainer.height]
50
+
51
+ const newAnnotation = {
52
+ text: 'New Annotation',
53
+ snapToNearestPoint: false,
54
+ fontSize: 16,
55
+ bezier: 10,
56
+ show: {
57
+ desktop: true,
58
+ tablet: true,
59
+ mobile: true
60
+ },
61
+ connectorType: 'line',
62
+ colors: {
63
+ label: 'black',
64
+ connector: 'black',
65
+ marker: 'black'
66
+ },
67
+ selected: true,
68
+ anchor: {
69
+ vertical: false,
70
+ horizontal: false
71
+ },
72
+ marker: 'arrow',
73
+ edit: {
74
+ subject: true,
75
+ label: true
76
+ },
77
+ seriesKey: '',
78
+ x: 50,
79
+ y: Number(newSvgDims?.[1] / 2),
80
+ xKey: config.xAxis.type === 'date' ? new Date(config?.data?.[0]?.[config.xAxis.dataKey]).getTime() : config.xAxis.type === 'categorical' ? '1/15/2016' : '',
81
+ yKey: '',
82
+ dx: 20,
83
+ dy: -20,
84
+ opacity: '100',
85
+ savedDimensions: newSvgDims,
86
+ connectionType: 'line'
87
+ }
88
+
89
+ const annotations = Array.isArray(config.annotations) ? config.annotations : []
90
+
91
+ updateConfig({
92
+ ...config,
93
+ annotations: [...annotations, newAnnotation]
94
+ })
95
+ }
96
+
97
+ const handleRemoveAnnotation = (annotationIndex: number) => {
98
+ const updated = config.annotations.filter((_, index) => index !== annotationIndex)
99
+ updateConfig({
100
+ ...config,
101
+ annotations: updated
102
+ })
103
+ }
104
+
105
+ return (
106
+ <Accordion key={props.name}>
107
+ <Accordion.Section title={props.name} key={props.name}>
108
+ <label key={`key-1`}>
109
+ Show Annotation Dropdown
110
+ <input
111
+ type='checkbox'
112
+ checked={config?.general?.showAnnotationDropdown || false}
113
+ onChange={e => {
114
+ updateConfig({
115
+ ...config,
116
+ general: {
117
+ ...config.general,
118
+ showAnnotationDropdown: e.target.checked
119
+ }
120
+ })
121
+ }}
122
+ />
123
+ </label>
124
+
125
+ {config.general.showAnnotationDropdown && (
126
+ <label key={`key-2`}>
127
+ Annotation Dropdown Title:
128
+ <input
129
+ type='text'
130
+ style={{ marginBottom: '10px' }}
131
+ value={config?.general?.annotationDropdownText}
132
+ onChange={e => {
133
+ updateConfig({
134
+ ...config,
135
+ general: {
136
+ ...config.general,
137
+ annotationDropdownText: e.target.value
138
+ }
139
+ })
140
+ }}
141
+ />
142
+ </label>
143
+ )}
144
+ {config?.annotations &&
145
+ config?.annotations.map((annotation, index) => (
146
+ <Accordion key={index}>
147
+ <Accordion.Section title={annotation.text ? annotation.text.substring(0, 15) + '...' : `Annotation ${index + 1}`}>
148
+ <div className='annotation-group'>
149
+ <label>
150
+ Annotation Text:
151
+ <textarea rows={5} value={annotation.text} onChange={e => handleAnnotationUpdate(e.target.value, 'text', index)} />
152
+ </label>
153
+ <label>
154
+ Opacity
155
+ <br />
156
+ <input
157
+ type='range'
158
+ onChange={e => {
159
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
160
+ updatedAnnotations[index].opacity = e.target.value
161
+ updateConfig({
162
+ ...config,
163
+ annotations: updatedAnnotations
164
+ })
165
+ }}
166
+ value={config?.annotations?.[index]?.opacity || '100'}
167
+ />
168
+ </label>
169
+
170
+ <label>
171
+ Edit Subject
172
+ <input
173
+ type='checkbox'
174
+ checked={config?.annotations[index]?.edit?.subject || false}
175
+ onChange={e => {
176
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
177
+ updatedAnnotations[index].edit.subject = e.target.checked
178
+ updateConfig({
179
+ ...config,
180
+ annotations: updatedAnnotations
181
+ })
182
+ }}
183
+ />
184
+ </label>
185
+ <label>
186
+ Edit Label
187
+ <input
188
+ type='checkbox'
189
+ checked={config?.annotations[index]?.edit?.label || false}
190
+ onChange={e => {
191
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
192
+ updatedAnnotations[index].edit.label = e.target.checked
193
+ updateConfig({
194
+ ...config,
195
+ annotations: updatedAnnotations
196
+ })
197
+ }}
198
+ />
199
+ </label>
200
+
201
+ <label>
202
+ Connection Type:
203
+ <select
204
+ key='annotation-connection-type'
205
+ onChange={e => {
206
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
207
+ updatedAnnotations[index].connectionType = e.target.value
208
+ updateConfig({
209
+ ...config,
210
+ annotations: updatedAnnotations
211
+ })
212
+ }}
213
+ value={config?.annotations[index]?.connectionType}
214
+ >
215
+ <option key='select' value='select'>
216
+ Select
217
+ </option>
218
+ {['curve', 'line', 'elbow', 'none'].map((side, index) => (
219
+ <option key={side} value={side}>
220
+ {side}
221
+ </option>
222
+ ))}
223
+ </select>
224
+ </label>
225
+
226
+ {annotation.connectionType === 'curve' && (
227
+ <>
228
+ <label>
229
+ Curve Control
230
+ {/* create a range input */}
231
+ <input
232
+ type='range'
233
+ min='-20'
234
+ max='20'
235
+ value={config?.annotations[index]?.bezier || 0}
236
+ onChange={e => {
237
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
238
+ updatedAnnotations[index].bezier = e.target.value
239
+ updateConfig({
240
+ ...config,
241
+ annotations: updatedAnnotations
242
+ })
243
+ }}
244
+ />
245
+ </label>
246
+ </>
247
+ )}
248
+
249
+ {/* <label>
250
+ Connection Location:
251
+ <select
252
+ onChange={e => {
253
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
254
+ updatedAnnotations[index].connectionLocation = e.target.value
255
+ updateConfig({
256
+ ...config,
257
+ annotations: updatedAnnotations
258
+ })
259
+ }}
260
+ >
261
+ {['auto', 'left', 'top', 'bottom', 'right'].map((side, index) => (
262
+ <option key={side} value={side}>
263
+ {side}
264
+ </option>
265
+ ))}
266
+ </select>
267
+ </label> */}
268
+
269
+ <label>
270
+ Marker
271
+ <select
272
+ key='annotation-marker'
273
+ value={annotation.marker}
274
+ onChange={e => {
275
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
276
+ updatedAnnotations[index].marker = e.target.value
277
+ updateConfig({
278
+ ...config,
279
+ annotations: updatedAnnotations
280
+ })
281
+ }}
282
+ >
283
+ {['arrow', 'circle'].map((column, columnIndex) => {
284
+ return <option key={`col-${columnIndex}`}>{column}</option>
285
+ })}
286
+ </select>
287
+ </label>
288
+
289
+ <Button className='warn btn-warn btn btn-remove delete' onClick={() => handleRemoveAnnotation(index)}>
290
+ Delete Annotation
291
+ </Button>
292
+ </div>
293
+ </Accordion.Section>
294
+ </Accordion>
295
+ ))}
296
+ {config?.annotations?.length < 3 && (
297
+ <Button onClick={handleAddAnnotation} className='mt-2'>
298
+ Add Annotation
299
+ </Button>
300
+ )}
301
+ </Accordion.Section>
302
+ </Accordion>
303
+ )
304
+ }
305
+
306
+ export default PanelAnnotate
@@ -18,7 +18,7 @@ import { PanelProps } from '../PanelProps'
18
18
  const PanelGeneral: FC<PanelProps> = props => {
19
19
  const { config } = useContext(ConfigContext)
20
20
  const { updateField } = useEditorPanelContext()
21
- const { enabledChartTypes, visHasNumbersOnBars, visHasLabelOnData, visSupportsChartHeight, visSupportsSuperTitle, visSupportsFootnotes } = useEditorPermissions()
21
+ const { enabledChartTypes, visHasNumbersOnBars, visHasaAdditionalLabelsOnBars, visHasLabelOnData, visSupportsChartHeight, visSupportsSuperTitle, visSupportsFootnotes } = useEditorPermissions()
22
22
  const { visualizationType, visualizationSubType, barStyle } = config
23
23
 
24
24
  const showBarStyleOptions = () => {
@@ -37,21 +37,151 @@ const PanelGeneral: FC<PanelProps> = props => {
37
37
  <AccordionItemButton>General</AccordionItemButton>
38
38
  </AccordionItemHeading>
39
39
  <AccordionItemPanel>
40
- <Select value={visualizationType} fieldName='visualizationType' label='Chart Type' updateField={updateField} options={enabledChartTypes} />
40
+ {config?.visualizationType !== 'Sankey' && <Select value={visualizationType} fieldName='visualizationType' label='Chart Type' updateField={updateField} options={enabledChartTypes} />}
41
+ {visSupportsChartHeight() && config.orientation === 'vertical' && (
42
+ <TextField
43
+ type='number'
44
+ value={config.heights.vertical}
45
+ section='heights'
46
+ fieldName='vertical'
47
+ label='Chart Height'
48
+ updateField={updateField}
49
+ tooltip={
50
+ <Tooltip style={{ textTransform: 'none' }}>
51
+ <Tooltip.Target>
52
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
53
+ </Tooltip.Target>
54
+ <Tooltip.Content>
55
+ <p>For some visualization types, such as the sankey diagram, it may be necessary to adjust to chart height for optimal display.</p>
56
+ </Tooltip.Content>
57
+ </Tooltip>
58
+ }
59
+ />
60
+ )}
41
61
  {(visualizationType === 'Bar' || visualizationType === 'Combo' || visualizationType === 'Area Chart') && <Select value={visualizationSubType || 'Regular'} fieldName='visualizationSubType' label='Chart Subtype' updateField={updateField} options={['regular', 'stacked']} />}
42
62
  {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
63
  {visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName='orientation' label='Orientation' updateField={updateField} options={['vertical', 'horizontal']} />}
44
64
  {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()} />}
65
+ {(visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && (
66
+ <Select
67
+ value={config.isLollipopChart ? 'lollipop' : barStyle || 'flat'}
68
+ fieldName='barStyle'
69
+ label='bar style'
70
+ updateField={updateField}
71
+ options={showBarStyleOptions()}
72
+ tooltip={
73
+ <Tooltip style={{ textTransform: 'none' }}>
74
+ <Tooltip.Target>
75
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
76
+ </Tooltip.Target>
77
+ <Tooltip.Content>
78
+ <p>Consider using the 'Flat' bar style when presenting data that includes '0' values.</p>
79
+ </Tooltip.Content>
80
+ </Tooltip>
81
+ }
82
+ />
83
+ )}
46
84
  {(visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && barStyle === 'rounded' && <Select value={config.tipRounding || 'top'} fieldName='tipRounding' label='tip rounding' updateField={updateField} options={['top', 'full']} />}
47
85
  {(visualizationType === 'Bar' || visualizationType === 'Deviation Bar') && barStyle === 'rounded' && <Select value={config.roundingStyle || 'standard'} fieldName='roundingStyle' label='rounding style' updateField={updateField} options={['standard', 'shallow', 'finger']} />}
48
86
  {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
87
  {visHasNumbersOnBars() ? (
50
88
  <CheckBox value={config.yAxis.displayNumbersOnBar} section='yAxis' fieldName='displayNumbersOnBar' label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField} />
51
89
  ) : (
52
- visHasLabelOnData() && <CheckBox value={config.labels} fieldName='labels' label='Display label on data' updateField={updateField} />
90
+ visHasLabelOnData() && (
91
+ <CheckBox
92
+ value={config.labels}
93
+ fieldName='labels'
94
+ label='Display label on data'
95
+ updateField={updateField}
96
+ tooltip={
97
+ <Tooltip style={{ textTransform: 'none' }}>
98
+ <Tooltip.Target>
99
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
100
+ </Tooltip.Target>
101
+ <Tooltip.Content>
102
+ <p>Selecting this option will not hide the display of "zero value", "suppressed data", or "missing data" indicators on the chart (if applicable).</p>
103
+ </Tooltip.Content>
104
+ </Tooltip>
105
+ }
106
+ />
107
+ )
108
+ )}
109
+ {visHasaAdditionalLabelsOnBars() && (
110
+ <>
111
+ <CheckBox
112
+ tooltip={
113
+ <Tooltip style={{ textTransform: 'none' }}>
114
+ <Tooltip.Target>
115
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
116
+ </Tooltip.Target>
117
+ <Tooltip.Content>
118
+ <p>Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in the data table to indicate missing or undefined data values.</p>
119
+ </Tooltip.Content>
120
+ </Tooltip>
121
+ }
122
+ value={config.general.showMissingDataLabel}
123
+ section='general'
124
+ fieldName='showMissingDataLabel'
125
+ label='Display "Missing Data" Label'
126
+ updateField={updateField}
127
+ />
128
+ <CheckBox
129
+ tooltip={
130
+ <Tooltip style={{ textTransform: 'none' }}>
131
+ <Tooltip.Target>
132
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
133
+ </Tooltip.Target>
134
+ <Tooltip.Content>
135
+ <p> Selecting this option will display a thin line slightly above the Date/Category Axis to indicate "zero value" where zero values are indicated in the Data Series.</p>
136
+ </Tooltip.Content>
137
+ </Tooltip>
138
+ }
139
+ value={config.general.showZeroValueDataLabel}
140
+ section='general'
141
+ fieldName='showZeroValueDataLabel'
142
+ label='Display "Zero Value Data" Label'
143
+ updateField={updateField}
144
+ />
145
+
146
+ <CheckBox
147
+ tooltip={
148
+ <Tooltip style={{ textTransform: 'none' }}>
149
+ <Tooltip.Target>
150
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
151
+ </Tooltip.Target>
152
+ <Tooltip.Content>
153
+ <p>Selecting this option will display the 'suppressed data symbol' on the Date/Category Axis, in the tooltip hover, and in the data table where suppressed data values are indicated in the Data Series</p>
154
+ </Tooltip.Content>
155
+ </Tooltip>
156
+ }
157
+ value={config.general.showSuppressedSymbol}
158
+ section='general'
159
+ fieldName='showSuppressedSymbol'
160
+ label='Display "suppressed data" label'
161
+ updateField={updateField}
162
+ />
163
+ </>
53
164
  )}
165
+
54
166
  {visualizationType === 'Pie' && <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />}
167
+ {visualizationType === 'Line' && (
168
+ <CheckBox
169
+ value={config.allowLineToBarGraph}
170
+ fieldName='allowLineToBarGraph'
171
+ label='Convert to Bar Graph'
172
+ updateField={updateField}
173
+ tooltip={
174
+ <Tooltip style={{ textTransform: 'none' }}>
175
+ <Tooltip.Target>
176
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
177
+ </Tooltip.Target>
178
+ <Tooltip.Content>
179
+ <p>Switch to bar graph when less than 3 data points available.</p>
180
+ </Tooltip.Content>
181
+ </Tooltip>
182
+ }
183
+ />
184
+ )}
55
185
 
56
186
  <TextField
57
187
  value={config.title || 'Chart Title'}
@@ -144,14 +274,12 @@ const PanelGeneral: FC<PanelProps> = props => {
144
274
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
145
275
  </Tooltip.Target>
146
276
  <Tooltip.Content>
147
- <p>Footnotes</p>
277
+ <p>Consider adding footnotes when displaying 'suppressed,' 'no data,' and 'zero values' to ensure accurate interpretation of the data.</p>
148
278
  </Tooltip.Content>
149
279
  </Tooltip>
150
280
  }
151
281
  />
152
282
  )}
153
-
154
- {visSupportsChartHeight() && config.orientation === 'vertical' && <TextField type='number' value={config.heights.vertical} section='heights' fieldName='vertical' label='Chart Height' updateField={updateField} />}
155
283
  </AccordionItemPanel>
156
284
  </AccordionItem>
157
285
  )
@@ -64,6 +64,7 @@ const SankeySettings = () => {
64
64
  <AccordionItemButton>Sankey Settings</AccordionItemButton>
65
65
  </AccordionItemHeading>
66
66
  <AccordionItemPanel>
67
+ <p>Node stories can provide additional details to support public health messaging. COVE can display a maximum of 3 node stories.</p>
67
68
  {data?.storyNodeText &&
68
69
  data?.storyNodeText.map(({ StoryNode, segmentTextBefore, segmentTextAfter }, i) => (
69
70
  <div key={i} style={{ border: '1px solid black', margin: '15px auto', padding: '15px', borderRadius: '10px' }}>
@@ -84,7 +85,6 @@ const SankeySettings = () => {
84
85
  </Button>
85
86
  </div>
86
87
  ))}
87
- {`Total Story Nodes: ${data?.storyNodeText?.length}`}
88
88
  {data?.storyNodeText?.length < 3 && (
89
89
  <button
90
90
  type='button'
@@ -97,8 +97,7 @@ const SankeySettings = () => {
97
97
  Add StoryNode
98
98
  </button>
99
99
  )}
100
-
101
- <CheckBox value={config.enableTooltips} fieldName='enableTooltips' label='Enable Tooltips' updateField={updateField} />
100
+ {config.enableTooltips && config.data?.tooltips?.length > 0 && <CheckBox value={config.enableTooltips} fieldName='enableTooltips' label='Enable Tooltips' updateField={updateField} />}
102
101
  </AccordionItemPanel>
103
102
  </AccordionItem>
104
103
  )
@@ -5,15 +5,14 @@ import ConfigContext from '../../../../ConfigContext'
5
5
  import InputSelect from '@cdc/core/components/inputs/InputSelect'
6
6
  import Check from '@cdc/core/assets/icon-check.svg'
7
7
  import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
8
-
8
+ import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
9
9
  import Icon from '@cdc/core/components/ui/Icon'
10
10
 
11
11
  // Third Party
12
12
  import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
13
13
  import { Draggable } from '@hello-pangea/dnd'
14
- import { colorPalettesChart, sequentialPalettes } from '@cdc/core/data/colorPalettes'
15
14
 
16
- const SeriesContext = React.createContext()
15
+ const SeriesContext = React.createContext({})
17
16
 
18
17
  const SeriesWrapper = props => {
19
18
  const { updateConfig, config, rawData } = useContext(ConfigContext)
@@ -456,8 +455,8 @@ const SeriesInputWeight = props => {
456
455
  type='number'
457
456
  key={`series-weight-${i}`}
458
457
  value={series.weight ? series.weight : ''}
459
- min="1"
460
- max="9"
458
+ min='1'
459
+ max='9'
461
460
  onChange={event => {
462
461
  changeSeriesWeight(i, event.target.value, event.target.min, event.target.max)
463
462
  }}
@@ -22,7 +22,8 @@ const PanelVisual: FC<PanelProps> = props => {
22
22
  const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
23
23
  const { visual } = config
24
24
  const { setLollipopShape, updateField } = useEditorPanelContext()
25
- const { visHasBarBorders, visCanAnimate, visSupportsNonSequentialPallete, headerColors, visSupportsTooltipOpacity, visSupportsTooltipLines, visSupportsBarSpace, visSupportsBarThickness, visHasDataCutoff, visSupportsSequentialPallete, visSupportsReverseColorPalette } = useEditorPermissions()
25
+ const { visHasBarBorders, visCanAnimate, visSupportsNonSequentialPallete, headerColors, visSupportsTooltipOpacity, visSupportsTooltipLines, visSupportsBarSpace, visSupportsBarThickness, visHasDataCutoff, visSupportsSequentialPallete, visSupportsReverseColorPalette, visHasSingleSeriesTooltip } =
26
+ useEditorPermissions()
26
27
  const { twoColorPalettes, sequential, nonSequential } = useColorPalette(config, updateConfig)
27
28
 
28
29
  const updateColor = (property, _value) => {
@@ -312,7 +313,7 @@ const PanelVisual: FC<PanelProps> = props => {
312
313
  />
313
314
  </label>
314
315
  )}
315
- {config.visualizationType === 'Bar' && <CheckBox value={config.tooltips.singleSeries} fieldName='singleSeries' section='tooltips' label='SHOW HOVER FOR SINGLE DATA SERIES' updateField={updateField} />}
316
+ {visHasSingleSeriesTooltip() && <CheckBox value={config.tooltips.singleSeries} fieldName='singleSeries' section='tooltips' label='SHOW HOVER FOR SINGLE DATA SERIES' updateField={updateField} />}
316
317
 
317
318
  <label>
318
319
  <span className='edit-label column-heading'>No Data Message</span>
@@ -5,6 +5,7 @@ import General from './Panel.General'
5
5
  import BoxPlot from './Panel.BoxPlot'
6
6
  import Visual from './Panel.Visual'
7
7
  import Sankey from './Panel.Sankey'
8
+ import Annotate from './Panel.Annotate'
8
9
 
9
10
  const Panels = {
10
11
  ForestPlot: ForestPlotSettings,
@@ -13,7 +14,8 @@ const Panels = {
13
14
  General,
14
15
  BoxPlot,
15
16
  Visual,
16
- Sankey
17
+ Sankey,
18
+ Annotate
17
19
  }
18
20
 
19
21
  export default Panels
@@ -81,3 +81,7 @@
81
81
  margin-top: 0 !important;
82
82
  }
83
83
  }
84
+
85
+ .annotation-group {
86
+ border: 1px solid black;
87
+ }
@@ -12,4 +12,23 @@
12
12
  padding-left: 350px;
13
13
  }
14
14
  }
15
+
16
+ .viewport-overrides {
17
+ button {
18
+ width: 100%;
19
+ padding: 1em;
20
+ margin-top: 1em;
21
+ text-align: left;
22
+
23
+ span {
24
+ display: inline-block;
25
+ float: right;
26
+ }
27
+ }
28
+
29
+ .edit-block {
30
+ margin-top: 0;
31
+ padding: 1em;
32
+ }
33
+ }
15
34
  }
@@ -76,6 +76,11 @@ export const useEditorPermissions = () => {
76
76
  return false
77
77
  }
78
78
 
79
+ const visHasaAdditionalLabelsOnBars = () => {
80
+ if (['Bar', 'Combo', 'Line'].includes(config.visualizationType)) return true
81
+ return false
82
+ }
83
+
79
84
  const visHasAnchors = () => {
80
85
  switch (visualizationType) {
81
86
  case 'Area Chart':
@@ -103,7 +108,7 @@ export const useEditorPermissions = () => {
103
108
  }
104
109
 
105
110
  const visHasBarBorders = () => {
106
- const disabledCharts = ['Box Plot', 'Scatter Plot', 'Pie']
111
+ const disabledCharts = ['Box Plot', 'Scatter Plot', 'Pie', 'Line']
107
112
  if (disabledCharts.includes(visualizationType)) return false
108
113
  return series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar' || series.type === 'Deviation Bar')
109
114
  }
@@ -301,8 +306,21 @@ export const useEditorPermissions = () => {
301
306
  if (visualizationType === 'Line') {
302
307
  return true
303
308
  }
309
+ if (visualizationType === 'Bar' && visualizationSubType === 'regular') {
310
+ return true
311
+ }
312
+
313
+ if (visualizationType === 'Combo') {
314
+ return true
315
+ }
316
+ return false
317
+ }
304
318
 
305
- if (visualizationType === 'Combo' && lineExist) {
319
+ const visHasSingleSeriesTooltip = () => {
320
+ if (visualizationType === 'Bar' || visualizationType === 'Line') {
321
+ return true
322
+ }
323
+ if (visualizationType === 'Area Chart' && visualizationSubType === 'stacked') {
306
324
  return true
307
325
  }
308
326
  return false
@@ -321,6 +339,7 @@ export const useEditorPermissions = () => {
321
339
  visHasLegendAxisAlign,
322
340
  visHasBrushChart,
323
341
  visHasNumbersOnBars,
342
+ visHasaAdditionalLabelsOnBars,
324
343
  visSupportsBarSpace,
325
344
  visSupportsBarThickness,
326
345
  visSupportsChartHeight,
@@ -352,6 +371,7 @@ export const useEditorPermissions = () => {
352
371
  visSupportsValueAxisTicks,
353
372
  visSupportsReactTooltip,
354
373
  visSupportsValueAxisMax,
355
- visSupportsValueAxisMin
374
+ visSupportsValueAxisMin,
375
+ visHasSingleSeriesTooltip
356
376
  }
357
377
  }