@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
File without changes
@@ -1,310 +0,0 @@
1
- import React, { useContext, useEffect } from 'react'
2
- import ConfigContext from '../ConfigContext'
3
- import parse from 'html-react-parser'
4
- import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
5
- import LegendCircle from '@cdc/core/components/LegendCircle'
6
-
7
- import useLegendClasses from './../hooks/useLegendClasses'
8
- import { useHighlightedBars } from '../hooks/useHighlightedBars'
9
- import { Line } from '@visx/shape'
10
- import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
11
- import { scaleOrdinal } from '@visx/scale'
12
- import { FaStar } from 'react-icons/fa'
13
-
14
- // * FILE REVIEW *
15
- // TODO: fix eslint-disable jsxa11y issues
16
-
17
- // * ADDITIONAL NOTES *
18
- // > recently removed dynamic legend items as they weren't used
19
- // > recently removed boxplots, they don't provide any legend settings
20
-
21
- /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
22
- const Legend = () => {
23
- // prettier-ignore
24
- const {
25
- config,
26
- legend,
27
- colorScale,
28
- seriesHighlight,
29
- highlight,
30
- twoColorPalette,
31
- tableData,
32
- highlightReset,
33
- transformedData: data,
34
- colorPalettes,
35
- currentViewport,
36
- handleLineType
37
- } = useContext(ConfigContext)
38
-
39
- const { innerClasses, containerClasses } = useLegendClasses(config)
40
- const { visualizationType, visualizationSubType, series, runtime, orientation } = config
41
- // create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
42
- const reverseLabels = labels => (config.legend.reverseLabelOrder && config.legend.position === 'bottom' ? labels.reverse() : labels)
43
- const displayScale = scaleOrdinal({
44
- domain: config.suppressedData?.map(d => d.label),
45
- range: ['none'],
46
- unknown: 'block'
47
- })
48
-
49
- const createLegendLabels = defaultLabels => {
50
- const colorCode = config.legend?.colorCode
51
- if (visualizationType === 'Deviation Bar') {
52
- const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
53
- const labelBelow = {
54
- datum: 'X',
55
- index: 0,
56
- text: `Below ${config.xAxis.targetLabel}`,
57
- value: belowColor
58
- }
59
- const labelAbove = {
60
- datum: 'X',
61
- index: 1,
62
- text: `Above ${config.xAxis.targetLabel}`,
63
- value: aboveColor
64
- }
65
-
66
- return reverseLabels([labelBelow, labelAbove])
67
- }
68
- if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
69
- let palette = colorPalettes[config.palette]
70
-
71
- while (tableData.length > palette.length) {
72
- palette = palette.concat(palette)
73
- }
74
- palette = palette.slice(0, data.length)
75
- //store unique values to Set by colorCode
76
- const set = new Set()
77
-
78
- tableData.forEach(d => set.add(d[colorCode]))
79
-
80
- // create labels with unique values
81
- const uniqueLabels = Array.from(set).map((val, i) => {
82
- const newLabel = {
83
- datum: val,
84
- index: i,
85
- text: val,
86
- value: palette[i]
87
- }
88
- return newLabel
89
- })
90
-
91
- return reverseLabels(uniqueLabels)
92
- }
93
-
94
- // get forecasting items inside of combo
95
- if (runtime?.forecastingSeriesKeys?.length > 0) {
96
- let seriesLabels = []
97
-
98
- //store unique values to Set by colorCode
99
-
100
- // loop through each stage/group/area on the chart and create a label
101
- config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
102
- return outerGroup?.stages?.map((stage, index) => {
103
- let colorValue = sequentialPalettes[stage.color]?.[2] ? sequentialPalettes[stage.color]?.[2] : colorPalettes[stage.color]?.[2] ? colorPalettes[stage.color]?.[2] : '#ccc'
104
-
105
- const newLabel = {
106
- datum: stage.key,
107
- index: index,
108
- text: stage.key,
109
- value: colorValue
110
- }
111
-
112
- seriesLabels.push(newLabel)
113
- })
114
- })
115
-
116
- // loop through bars for now to meet requirements.
117
- config.runtime.barSeriesKeys &&
118
- config.runtime.barSeriesKeys.forEach((bar, index) => {
119
- let colorValue = colorPalettes[config.palette][index] ? colorPalettes[config.palette][index] : '#ccc'
120
-
121
- const newLabel = {
122
- datum: bar,
123
- index: index,
124
- text: bar,
125
- value: colorValue
126
- }
127
-
128
- seriesLabels.push(newLabel)
129
- })
130
-
131
- return reverseLabels(seriesLabels)
132
- }
133
-
134
- // DEV-4161: replaceable series name in the legend
135
- const hasNewSeriesName = config.series.map(s => s.name).filter(item => item).length > 0
136
- if (hasNewSeriesName) {
137
- let palette = colorPalettes[config.palette]
138
-
139
- while (tableData.length > palette.length) {
140
- palette = palette.concat(palette)
141
- }
142
-
143
- palette = palette.slice(0, data.length)
144
- //store unique values to Set by colorCode
145
- const set = new Set()
146
-
147
- config.series.forEach(d => {
148
- set.add(d['name'] ? d['name'] : d['dataKey'])
149
- })
150
-
151
- // create labels with unique values
152
- const uniqueLabels = Array.from(set).map((val, i) => {
153
- const newLabel = {
154
- datum: val,
155
- index: i,
156
- text: val,
157
- value: colorScale(val)
158
- }
159
- return newLabel
160
- })
161
-
162
- return reverseLabels(uniqueLabels)
163
- }
164
-
165
- if ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && config.visualizationSubType === 'regular' && config.suppressedData) {
166
- const lastIndex = defaultLabels.length - 1
167
- let newLabels = []
168
-
169
- config.suppressedData?.forEach(({ label, icon, value }, index) => {
170
- const dataExists = data.some(d => {
171
- return runtime.seriesKeys.some(column => d[column] === value)
172
- })
173
-
174
- if (label && icon) {
175
- const newLabel = {
176
- datum: label,
177
- index: lastIndex + index,
178
- text: label,
179
- icon: <FaStar color='#000' size={15} />
180
- }
181
- newLabels.push(newLabel)
182
- }
183
- })
184
-
185
- return [...defaultLabels, ...newLabels]
186
- }
187
-
188
- return reverseLabels(defaultLabels)
189
- }
190
-
191
- const isBottomOrSmallViewport = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
192
-
193
- const legendClasses = {
194
- marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
195
- marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `${isBottomOrSmallViewport ? config.dynamicMarginTop + 15 : 0}px`
196
- }
197
-
198
- const { HighLightedBarUtils } = useHighlightedBars(config)
199
-
200
- let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
201
- if (!legend) return null
202
-
203
- return (
204
- config.visualizationType !== 'Box Plot' && (
205
- <aside style={legendClasses} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
206
- {legend.label && <h2>{parse(legend.label)}</h2>}
207
- {legend.description && <p>{parse(legend.description)}</p>}
208
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
209
- {labels => {
210
- return (
211
- <div className={innerClasses.join(' ')}>
212
- {createLegendLabels(labels).map((label, i) => {
213
- let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
214
- let itemName = label.datum
215
-
216
- // Filter excluded data keys from legend
217
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
218
- return null
219
- }
220
-
221
- if (runtime.seriesLabels) {
222
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
223
- itemName = config.runtime.seriesKeys[index]
224
-
225
- if (runtime?.forecastingSeriesKeys?.length > 0) {
226
- itemName = label.text
227
- }
228
- }
229
-
230
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
231
- className.push('inactive')
232
- }
233
-
234
- return (
235
- <LegendItem
236
- className={className.join(' ')}
237
- tabIndex={0}
238
- key={`legend-quantile-${i}`}
239
- onKeyPress={e => {
240
- if (e.key === 'Enter') {
241
- highlight(label)
242
- }
243
- }}
244
- onClick={() => {
245
- highlight(label)
246
- }}
247
- >
248
- {config.visualizationType === 'Line' && config.legend.lineMode ? (
249
- <svg width={40} height={20}>
250
- <Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={label.value} strokeWidth={2} strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')} />
251
- </svg>
252
- ) : (
253
- <div style={{ display: 'flex', flexDirection: 'column' }}>
254
- <LegendCircle margin='0' fill={label.value} display={displayScale(label.datum)} />
255
- <div style={{ marginTop: '2px', marginRight: '6px' }}>{label.icon}</div>
256
- </div>
257
- )}
258
-
259
- <LegendLabel align='left' margin='0 0 0 4px'>
260
- {label.text}
261
- </LegendLabel>
262
- </LegendItem>
263
- )
264
- })}
265
-
266
- {highLightedLegendItems.map((bar, i) => {
267
- // if duplicates only return first item
268
- let className = 'legend-item'
269
- let itemName = bar.legendLabel
270
-
271
- if (!itemName) return false
272
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
273
- className += ' inactive'
274
- }
275
- return (
276
- <LegendItem
277
- className={className}
278
- tabIndex={0}
279
- key={`legend-quantile-${i}`}
280
- onKeyPress={e => {
281
- if (e.key === 'Enter') {
282
- highlight(bar.legendLabel)
283
- }
284
- }}
285
- onClick={() => {
286
- highlight(bar.legendLabel)
287
- }}
288
- >
289
- <LegendCircle fill='transparent' borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`} />{' '}
290
- <LegendLabel align='left' margin='0 0 0 4px'>
291
- {bar.legendLabel ? bar.legendLabel : bar.value}
292
- </LegendLabel>
293
- </LegendItem>
294
- )
295
- })}
296
- {seriesHighlight.length > 0 && (
297
- <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
298
- Reset
299
- </button>
300
- )}
301
- </div>
302
- )
303
- }}
304
- </LegendOrdinal>
305
- </aside>
306
- )
307
- )
308
- }
309
-
310
- export default Legend
@@ -1,105 +0,0 @@
1
- import React from 'react'
2
- import chroma from 'chroma-js'
3
- import { type ChartConfig } from '../../types/ChartConfig'
4
-
5
- // todo: change this config obj to ChartConfig once its created
6
- type LineChartCircleProps = {
7
- config: ChartConfig
8
- data: object[]
9
- d?: Object
10
- displayArea: boolean
11
- seriesKey: string
12
- tooltipData: {
13
- data: []
14
- tooltipDataX: number
15
- tooltipDataY: number
16
- }
17
- xScale: any
18
- yScale: any
19
- yScaleRight: any
20
- colorScale: any
21
- parseDate: any
22
- seriesAxis: string
23
- }
24
-
25
- const LineChartCircle = (props: LineChartCircleProps) => {
26
- const { config, d, displayArea, seriesKey, tooltipData, xScale, yScale, colorScale, parseDate, yScaleRight, data } = props
27
- const { lineDatapointStyle } = config
28
- const filtered = config?.series.filter(s => s.dataKey === seriesKey)?.[0]
29
- // If we're not showing the circle, simply return
30
- if (lineDatapointStyle === 'hidden') return <></>
31
-
32
- const getIndex = seriesKey => config.runtime.seriesLabelsAll.indexOf(seriesKey)
33
-
34
- const getColor = (displayArea: boolean, colorScale: Function, config: ChartConfig, hoveredKey: string, seriesKey: string) => {
35
- const seriesLabels = config.runtime.seriesLabels || []
36
- let color
37
-
38
- if (displayArea) {
39
- color = colorScale(seriesLabels[hoveredKey] || seriesKey)
40
- } else {
41
- color = 'transparent'
42
- }
43
-
44
- if (config.lineDatapointColor === 'Lighter than Line' && color !== 'transparent' && color) {
45
- color = chroma(color).brighten(1)
46
- }
47
- return color
48
- }
49
-
50
- if (lineDatapointStyle === 'always show') {
51
- return (
52
- <circle
53
- cx={config.xAxis.type === 'categorical' ? xScale(d[config.xAxis.dataKey]) : xScale(parseDate(d[config.xAxis.dataKey]))}
54
- cy={filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])}
55
- r={4.5}
56
- opacity={d[seriesKey] ? 1 : 0}
57
- fillOpacity={1}
58
- fill={getColor(displayArea, colorScale, config, seriesKey, seriesKey)}
59
- style={{ filter: 'unset', opacity: 1 }}
60
- />
61
- )
62
- }
63
-
64
- if (lineDatapointStyle === 'hover') {
65
- if (!tooltipData) return
66
- if (!seriesKey) return
67
- if (!tooltipData.data) return
68
- let hoveredXValue = tooltipData?.data?.[0]?.[1]
69
- if (!hoveredXValue) return
70
-
71
- let hoveredSeriesValue
72
- let hoveredSeriesIndex
73
- let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
74
- let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
75
- let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
76
- if (!hoveredSeriesKey) return
77
- hoveredSeriesIndex = tooltipData?.data.indexOf(hoveredSeriesKey)
78
- hoveredSeriesValue = data?.find(d => {
79
- return d[config?.xAxis.dataKey] === hoveredXValue
80
- })?.[seriesKey]
81
-
82
- // hoveredSeriesValue = extractNumber(hoveredSeriesValue)
83
- return tooltipData?.data.map((tooltipItem, index) => {
84
- let seriesIndex = config.runtime.seriesLabelsAll.indexOf(hoveredXValue)
85
-
86
- if (isNaN(hoveredSeriesValue)) return <></>
87
- return (
88
- <circle
89
- cx={config.xAxis.type === 'categorical' ? xScale(hoveredXValue) : xScale(parseDate(hoveredXValue))}
90
- cy={hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)}
91
- r={4.5}
92
- opacity={1}
93
- fillOpacity={1}
94
- fill={getColor(displayArea, colorScale, config, hoveredSeriesKey, seriesKey)}
95
- style={{ filter: 'unset', opacity: 1 }}
96
- key={`line-chart-circle--${JSON.stringify(tooltipItem)}--${index}`}
97
- />
98
- )
99
- })
100
- }
101
-
102
- return null
103
- }
104
-
105
- export default LineChartCircle
File without changes