@cdc/chart 4.24.5 → 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 (68) hide show
  1. package/dist/cdcchart.js +39128 -35959
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +5 -4
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +104 -64
  7. package/src/_stories/Chart.stories.tsx +18 -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/line_chart_two_points_new_chart.json +128 -0
  13. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  14. package/src/_stories/_mock/lollipop.json +171 -0
  15. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  17. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  19. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  20. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  21. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  22. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  23. package/src/components/Annotations/index.tsx +13 -0
  24. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  25. package/src/components/AreaChart/components/AreaChart.jsx +1 -1
  26. package/src/components/BarChart/components/BarChart.Horizontal.tsx +44 -42
  27. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  28. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  29. package/src/components/BarChart/components/BarChart.Vertical.tsx +50 -22
  30. package/src/components/BarChart/helpers/index.ts +102 -0
  31. package/src/components/EditorPanel/EditorPanel.tsx +232 -98
  32. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  33. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +117 -6
  34. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  35. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  36. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  37. package/src/components/EditorPanel/components/panels.scss +4 -0
  38. package/src/components/EditorPanel/editor-panel.scss +19 -0
  39. package/src/components/EditorPanel/useEditorPermissions.js +19 -2
  40. package/src/components/Legend/Legend.Component.tsx +7 -8
  41. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  42. package/src/components/Legend/helpers/index.ts +5 -0
  43. package/src/components/LineChart/LineChartProps.ts +3 -0
  44. package/src/components/LineChart/helpers.ts +21 -7
  45. package/src/components/LineChart/index.tsx +7 -7
  46. package/src/components/LinearChart.jsx +179 -136
  47. package/src/components/PairedBarChart.jsx +9 -9
  48. package/src/components/PieChart/PieChart.tsx +4 -4
  49. package/src/components/Sankey/index.tsx +73 -20
  50. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  51. package/src/components/ZoomBrush.tsx +90 -44
  52. package/src/data/initial-state.js +14 -6
  53. package/src/helpers/handleChartTabbing.ts +8 -0
  54. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  55. package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
  56. package/src/hooks/useColorScale.ts +1 -1
  57. package/src/hooks/useMinMax.ts +8 -3
  58. package/src/hooks/useScales.ts +25 -3
  59. package/src/hooks/useTooltip.tsx +58 -11
  60. package/src/scss/main.scss +21 -14
  61. package/src/types/ChartConfig.ts +50 -3
  62. package/src/types/ChartContext.ts +9 -0
  63. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  64. package/LICENSE +0 -201
  65. package/src/helpers/filterData.ts +0 -18
  66. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  67. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  68. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -5,7 +5,7 @@ import Button from '@cdc/core/components/elements/Button'
5
5
  import useLegendClasses from '../../hooks/useLegendClasses'
6
6
  import { useHighlightedBars } from '../../hooks/useHighlightedBars'
7
7
  import { handleLineType } from '../../helpers/handleLineType'
8
- import { useBarChart } from '../../hooks/useBarChart'
8
+ import { getMarginTop } from './helpers/index'
9
9
  import { Line } from '@visx/shape'
10
10
  import { Label } from '../../types/Label'
11
11
  import { ChartConfig } from '../../types/ChartConfig'
@@ -27,25 +27,24 @@ export interface LegendProps {
27
27
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
28
28
  const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHighlight, highlight, highlightReset, currentViewport, formatLabels, skipId = 'legend' }, ref) => {
29
29
  const { innerClasses, containerClasses } = useLegendClasses(config)
30
- const { runtime, orientation, legend } = config
30
+ const { runtime, legend } = config
31
31
 
32
32
  if (!legend) return null
33
- const isBottomOrSmallViewport = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
33
+ const isBottomOrSmallViewport = legend?.position === 'bottom' || (['sm', 'xs', 'xxs'].includes(currentViewport) && !legend.hide)
34
34
 
35
35
  const legendClasses = {
36
36
  marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
37
- marginTop: isBottomOrSmallViewport ? '15px' : '0px'
37
+ marginTop: isBottomOrSmallViewport && config.orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : getMarginTop(isBottomOrSmallViewport, config.brush.active)
38
38
  }
39
39
 
40
40
  const { HighLightedBarUtils } = useHighlightedBars(config)
41
41
 
42
42
  let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
43
- const fontSize = ['sm', 'xs', 'xxs'].includes(currentViewport) ? { fontSize: '11px' } : null
44
43
 
45
44
  return (
46
45
  <aside ref={ref} style={legendClasses} id={skipId || 'legend'} className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
47
46
  {legend.label && <h3>{parse(legend.label)}</h3>}
48
- {legend.description && <p style={fontSize}>{parse(legend.description)}</p>}
47
+ {legend.description && <p>{parse(legend.description)}</p>}
49
48
 
50
49
  <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
51
50
  {labels => {
@@ -103,7 +102,7 @@ const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHi
103
102
  )}
104
103
  </div>
105
104
 
106
- <LegendLabel style={fontSize} align='left' margin='0 0 0 4px'>
105
+ <LegendLabel align='left' margin='0 0 0 4px'>
107
106
  {label.text}
108
107
  </LegendLabel>
109
108
  </LegendItem>
@@ -145,7 +144,7 @@ const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHi
145
144
  </div>
146
145
 
147
146
  <>
148
- {config?.preliminaryData?.some(pd => pd.label && pd.type === 'effect' && pd.style) && ['Line', 'Combo'].includes(config.visualizationType) && (
147
+ {config?.preliminaryData?.some(pd => pd.label && pd.type === 'effect' && pd.style === 'Open Circles') && ['Line', 'Combo'].includes(config.visualizationType) && (
149
148
  <>
150
149
  <hr></hr>
151
150
  <div className={config.legend.singleRow && isBottomOrSmallViewport ? 'legend-container__inner bottom single-row' : ''}>
@@ -9,7 +9,7 @@ export const createFormatLabels =
9
9
  (defaultLabels: Label[]): Label[] => {
10
10
  const { visualizationType, visualizationSubType, series, runtime } = config
11
11
 
12
- const reverseLabels = labels => (config.legend.reverseLabelOrder && config.legend.position === 'bottom' ? labels.reverse() : labels)
12
+ const reverseLabels = labels => (config.legend.reverseLabelOrder && config.legend?.position === 'bottom' ? labels.reverse() : labels)
13
13
  const colorCode = config.legend?.colorCode
14
14
  if (visualizationType === 'Deviation Bar') {
15
15
  const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
@@ -0,0 +1,5 @@
1
+ export const getMarginTop = (isBottomOrSmallViewport, isBrushActive) => {
2
+ if (!isBottomOrSmallViewport) return '0px'
3
+ if (isBrushActive) return '35px'
4
+ return '15px'
5
+ }
@@ -29,6 +29,9 @@ export interface PreliminaryDataItem {
29
29
  symbol: string
30
30
  type: 'effect' | 'suppression'
31
31
  value: string
32
+ hideBarSymbol: boolean
33
+ hideLineStyle: boolean
34
+ circleSize: number
32
35
  }
33
36
 
34
37
  export interface DataItem {
@@ -29,16 +29,30 @@ export const createStyles = (props: StyleProps): Style[] => {
29
29
 
30
30
  export const filterCircles = (preliminaryData: PreliminaryDataItem[], data: DataItem[], seriesKey: string): DataItem[] => {
31
31
  // Filter and map preliminaryData to get circlesFiltered
32
- const circlesFiltered = preliminaryData?.filter(item => item.style === 'Open Circles' && item.type === 'effect').map(item => ({ column: item.column, value: item.value, seriesKey: item.seriesKey }))
33
- const filteredData: DataItem[] = []
32
+ const circlesFiltered = preliminaryData?.filter(item => item.style.includes('Circles') && item.type === 'effect').map(item => ({ column: item.column, value: item.value, seriesKey: item.seriesKey, circleSize: item.circleSize, style: item.style }))
33
+ const filteredData = []
34
34
  // Process data to find matching items
35
35
  data.forEach(item => {
36
36
  circlesFiltered.forEach(fc => {
37
- if (item[fc.column] === fc.value && fc.seriesKey === seriesKey) {
38
- filteredData.push(item)
37
+ if (item[fc.column] === fc.value && fc.seriesKey === seriesKey && item[seriesKey] && fc.style === 'Open Circles') {
38
+ const result = {
39
+ data: item,
40
+ size: fc.circleSize,
41
+ isFilled: false
42
+ }
43
+ filteredData.push(result)
44
+ }
45
+ if ((!fc.value || item[fc.column] === fc.value) && fc.seriesKey === seriesKey && item[seriesKey] && fc.style === 'Filled Circles') {
46
+ const result = {
47
+ data: item,
48
+ size: fc.circleSize,
49
+ isFilled: true
50
+ }
51
+ filteredData.push(result)
39
52
  }
40
53
  })
41
54
  })
55
+
42
56
  return filteredData
43
57
  }
44
58
 
@@ -56,7 +70,7 @@ const handleFirstIndex = (data, seriesKey, preliminaryData) => {
56
70
 
57
71
  // Function to check if a data item matches the suppression criteria
58
72
  const isSuppressed = pd => {
59
- if (pd.type === 'effect') return
73
+ if (pd.type === 'effect' || pd.hideLineStyle) return
60
74
  return pd.type == 'suppression' && pd.value === firstIndexDataItem[seriesKey] && (!pd.column || pd.column === seriesKey)
61
75
  }
62
76
 
@@ -93,7 +107,7 @@ const handleLastIndex = (data, seriesKey, preliminaryData) => {
93
107
  let lastAddedIndex = -1 // Tracks the last index added to the result
94
108
  preliminaryData?.forEach(pd => {
95
109
  if (pd.type === 'effect') return
96
- if (data[data.length - 1][seriesKey] === pd.value && pd.style && (!pd.column || pd.column === seriesKey) && pd.type == 'suppression') {
110
+ if (data[data.length - 1][seriesKey] === pd.value && pd.style && (!pd.column || pd.column === seriesKey) && pd.type == 'suppression' && !pd.hideLineStyle) {
97
111
  const lastIndex = data.length - 1
98
112
  const modifiedItem = { ...data[lastIndex], [seriesKey]: 0 }
99
113
  result.data.push(modifiedItem)
@@ -123,7 +137,7 @@ function handleMiddleIndices(data, seriesKey, dataKey, preliminaryData) {
123
137
  const isValidMiddleIndex = index => index > 0 && index < data.length - 1
124
138
 
125
139
  preliminaryData?.forEach(pd => {
126
- if (pd.type === 'effect') return
140
+ if (pd.type === 'effect' || pd.hideLineStyle) return
127
141
  const targetValue = pd.value
128
142
 
129
143
  // Find all indices
@@ -46,7 +46,7 @@ const LineChart = (props: LineChartProps) => {
46
46
  let data = transformedData
47
47
  let tableD = tableData
48
48
  // if brush on use brush data and clean
49
- if (brushConfig.data.length) {
49
+ if (brushConfig.data.length > 0 && config.brush?.active) {
50
50
  data = clean(brushConfig.data)
51
51
  tableD = clean(brushConfig.data)
52
52
  }
@@ -207,7 +207,7 @@ const LineChart = (props: LineChartProps) => {
207
207
  <LinePath
208
208
  curve={allCurves[seriesData[0].lineType]}
209
209
  data={
210
- config.xAxis.type === 'date-time'
210
+ config.xAxis.type === 'date-time' || config.xAxis.type === 'date'
211
211
  ? data.sort((d1, d2) => {
212
212
  let x1 = getXAxisData(d1)
213
213
  let x2 = getXAxisData(d2)
@@ -232,16 +232,16 @@ const LineChart = (props: LineChartProps) => {
232
232
  )}
233
233
 
234
234
  {/* circles for preliminaryData data */}
235
- {circleData.map((d, i) => {
235
+ {circleData.map((item, i) => {
236
236
  return (
237
237
  <circle
238
238
  key={i}
239
- cx={xPos(d)}
240
- cy={seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(Number(getYAxisData(d, seriesKey)))}
241
- r={6}
239
+ cx={xPos(item.data)}
240
+ cy={seriesAxis === 'Right' ? yScaleRight(getYAxisData(item.data, seriesKey)) : yScale(Number(getYAxisData(item.data, seriesKey)))}
241
+ r={item.size}
242
242
  strokeWidth={seriesData[0].weight || 2}
243
243
  stroke={colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000'}
244
- fill='#fff'
244
+ fill={item.isFilled ? (colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000') : '#fff'}
245
245
  />
246
246
  )
247
247
  })}