@cdc/chart 4.26.5 → 4.26.6

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 (91) hide show
  1. package/CONFIG.md +12 -8
  2. package/dist/{cdcchart-DQ00cQCm.es.js → cdcchart-BWVIUcAF.es.js} +5 -55
  3. package/dist/cdcchart.js +34093 -32767
  4. package/examples/feature/heatmap/average-age-categorical.json +1 -1
  5. package/examples/feature/heatmap/calendar-heatmap.json +1 -1
  6. package/examples/feature/heatmap/sparse-aggregation.json +1 -1
  7. package/examples/private/bad-y-axis.json +543 -0
  8. package/examples/private/bar-chart-negative-value.json +455 -0
  9. package/package.json +3 -3
  10. package/src/CdcChart.tsx +5 -4
  11. package/src/CdcChartComponent.tsx +32 -9
  12. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +183 -0
  13. package/src/_stories/Chart.YAxisAutoMaxStrategy.stories.tsx +52 -0
  14. package/src/_stories/Chart.YAxisFilterDomain.stories.tsx +90 -0
  15. package/src/_stories/ChartAnnotation.smoke.stories.tsx +10 -0
  16. package/src/_stories/_mock/annotation_event-line_mock.json +1064 -0
  17. package/src/_stories/_mock/heatmap-average-age-categorical.json +1 -1
  18. package/src/_stories/_mock/heatmap.json +1 -1
  19. package/src/components/Annotations/components/AnnotationDraggable.styles.css +22 -1
  20. package/src/components/Annotations/components/AnnotationDraggable.tsx +219 -119
  21. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +198 -0
  22. package/src/components/Axis/LeftAxis.tsx +3 -0
  23. package/src/components/Axis/LeftAxisGridlines.tsx +8 -1
  24. package/src/components/Axis/RightAxis.tsx +3 -0
  25. package/src/components/Axis/tests/AxisTickValues.test.tsx +86 -0
  26. package/src/components/EditorPanel/EditorPanel.tsx +367 -136
  27. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +101 -52
  28. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.test.tsx +98 -0
  29. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +10 -0
  30. package/src/components/EditorPanel/components/Panels/Panel.Visual.test.tsx +484 -0
  31. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -0
  32. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +262 -0
  33. package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +32 -0
  34. package/src/components/EditorPanel/components/panels.scss +10 -0
  35. package/src/components/EditorPanel/editor-panel.scss +47 -0
  36. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +6 -2
  37. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +20 -2
  38. package/src/components/EditorPanel/useEditorPermissions.test.tsx +256 -11
  39. package/src/components/EditorPanel/useEditorPermissions.ts +41 -5
  40. package/src/components/HeatMap/components/HeatMap.tsx +22 -17
  41. package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +29 -15
  42. package/src/components/HeatMap/heatmap.css +15 -1
  43. package/src/components/HeatMap/tests/HeatMap.test.tsx +39 -7
  44. package/src/components/Legend/Legend.Component.tsx +2 -2
  45. package/src/components/Legend/helpers/createFormatLabels.test.ts +88 -0
  46. package/src/components/Legend/helpers/createFormatLabels.tsx +6 -1
  47. package/src/components/LineChart/helpers.test.ts +230 -1
  48. package/src/components/LineChart/helpers.ts +26 -13
  49. package/src/components/LineChart/index.tsx +21 -4
  50. package/src/components/LinearChart/tests/LinearChart.test.tsx +30 -0
  51. package/src/components/LinearChart.tsx +50 -15
  52. package/src/components/ScatterPlot/ScatterPlot.jsx +3 -3
  53. package/src/components/SmallMultiples/SmallMultipleTile.tsx +3 -0
  54. package/src/components/SmallMultiples/SmallMultiples.tsx +38 -9
  55. package/src/components/SmallMultiples/tests/SmallMultiples.test.tsx +170 -0
  56. package/src/data/initial-state.js +1 -0
  57. package/src/helpers/calculateLeftYAxisWidth.ts +9 -3
  58. package/src/helpers/colorAssignmentHelpers.ts +102 -0
  59. package/src/helpers/dataHelpers.ts +11 -1
  60. package/src/helpers/getAxisMaxOverride.ts +16 -0
  61. package/src/helpers/getCleanTopTickMax.ts +64 -0
  62. package/src/helpers/getColorScale.ts +18 -9
  63. package/src/helpers/getHasBoundarySuppression.ts +26 -0
  64. package/src/helpers/getMinMax.ts +24 -22
  65. package/src/helpers/getYAxisDomainData.ts +38 -0
  66. package/src/helpers/getYAxisFinalizationEligibility.ts +40 -0
  67. package/src/helpers/hasSpacedInlineLabel.ts +10 -0
  68. package/src/helpers/seriesColumnSettings.ts +5 -4
  69. package/src/helpers/smallMultiplesHelpers.ts +5 -0
  70. package/src/helpers/tests/calculateLeftYAxisWidth.test.ts +38 -0
  71. package/src/helpers/tests/dataHelpers.test.ts +135 -0
  72. package/src/helpers/tests/getAxisMaxOverride.test.ts +36 -0
  73. package/src/helpers/tests/getCleanTopTickMax.test.ts +165 -0
  74. package/src/helpers/tests/getColorScale.test.ts +197 -0
  75. package/src/helpers/tests/getMinMax.test.ts +211 -0
  76. package/src/helpers/tests/getYAxisDomainData.test.ts +119 -0
  77. package/src/helpers/tests/getYAxisFinalizationEligibility.test.ts +185 -0
  78. package/src/helpers/tests/{getYAxisAutoPaddingMode.test.ts → hasSpacedInlineLabel.test.ts} +17 -32
  79. package/src/helpers/tests/seriesColumnSettings.test.ts +16 -2
  80. package/src/helpers/tests/smallMultiplesHelpers.test.ts +99 -0
  81. package/src/hooks/tests/useRightAxis.test.tsx +127 -0
  82. package/src/hooks/tests/useScales.test.tsx +307 -29
  83. package/src/hooks/useRightAxis.ts +30 -13
  84. package/src/hooks/useScales.ts +45 -28
  85. package/src/hooks/useTooltip.tsx +3 -1
  86. package/src/test/CdcChart.dataTable.test.tsx +122 -0
  87. package/src/types/ChartConfig.ts +6 -1
  88. package/src/types/ChartContext.ts +3 -0
  89. package/src/helpers/getYAxisAutoPadding.ts +0 -55
  90. package/src/helpers/getYAxisAutoPaddingMode.ts +0 -17
  91. package/src/helpers/tests/getYAxisAutoPadding.test.ts +0 -37
@@ -2,6 +2,7 @@ import React, { useContext } from 'react'
2
2
  import { Draggable } from '@hello-pangea/dnd'
3
3
  import ConfigContext from '../../../../ConfigContext.js'
4
4
  import { useEditorPermissions } from '../../useEditorPermissions'
5
+ import { EVENT_LINE_LABEL_OFFSET } from '../../../Annotations/components/AnnotationDraggable'
5
6
 
6
7
  // CDC Core
7
8
  import Accordion from '@cdc/core/components/ui/Accordion'
@@ -63,6 +64,7 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
63
64
  const handleAddAnnotation = () => {
64
65
  const newAnnotation = {
65
66
  text: 'New annotation',
67
+ style: 'callout',
66
68
  anchorMode: 'fixed',
67
69
  fontSize: 16,
68
70
  bezier: 10,
@@ -213,7 +215,48 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
213
215
  />
214
216
  </label>
215
217
 
216
- {visSupportsDataAnnotations() && (
218
+ <Select
219
+ label='Annotation Style:'
220
+ value={annotation.style || 'callout'}
221
+ options={[
222
+ { value: 'callout', label: 'Callout' },
223
+ {
224
+ value: 'event-line',
225
+ label: visSupportsDataAnnotations()
226
+ ? 'Event Line'
227
+ : 'Event Line (requires Line/Bar/Combo/Area/Forecasting, vertical)'
228
+ }
229
+ ]}
230
+ section='annotations'
231
+ subsection={null}
232
+ fieldName='style'
233
+ updateField={(section, subsection, fieldName, value) => {
234
+ const updatedAnnotations = cloneDeep(config?.annotations)
235
+ const target = updatedAnnotations[index]
236
+ target.style = value
237
+
238
+ if (value === 'event-line') {
239
+ target.anchorMode = 'data'
240
+ if (!target.dataX) {
241
+ target.dataX = transformedData?.[0]?.[config.xAxis.dataKey] || ''
242
+ }
243
+ target.seriesKey = ''
244
+ target.edit = { subject: false, label: true }
245
+ target.dx =
246
+ typeof target.dx === 'number' && target.dx < 0
247
+ ? -EVENT_LINE_LABEL_OFFSET
248
+ : EVENT_LINE_LABEL_OFFSET
249
+ if (target.dy === undefined || target.dy === null) target.dy = -120
250
+ }
251
+
252
+ updateConfig({
253
+ ...config,
254
+ annotations: updatedAnnotations
255
+ })
256
+ }}
257
+ />
258
+
259
+ {visSupportsDataAnnotations() && annotation.style !== 'event-line' && (
217
260
  <Select
218
261
  label='Position Mode:'
219
262
  value={annotation.anchorMode || 'fixed'}
@@ -246,24 +289,26 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
246
289
  />
247
290
  )}
248
291
 
249
- {visSupportsDataAnnotations() && annotation.anchorMode === 'data' && (
250
- <Select
251
- label='Series:'
252
- value={annotation.seriesKey || config.series?.[0]?.dataKey || ''}
253
- options={config.series.map(s => s.dataKey)}
254
- section='annotations'
255
- subsection={null}
256
- fieldName='seriesKey'
257
- updateField={(section, subsection, fieldName, value) => {
258
- const updatedAnnotations = cloneDeep(config?.annotations)
259
- updatedAnnotations[index].seriesKey = value || config.series?.[0]?.dataKey
260
- updateConfig({
261
- ...config,
262
- annotations: updatedAnnotations
263
- })
264
- }}
265
- />
266
- )}
292
+ {visSupportsDataAnnotations() &&
293
+ annotation.style !== 'event-line' &&
294
+ annotation.anchorMode === 'data' && (
295
+ <Select
296
+ label='Series:'
297
+ value={annotation.seriesKey || config.series?.[0]?.dataKey || ''}
298
+ options={config.series.map(s => s.dataKey)}
299
+ section='annotations'
300
+ subsection={null}
301
+ fieldName='seriesKey'
302
+ updateField={(section, subsection, fieldName, value) => {
303
+ const updatedAnnotations = cloneDeep(config?.annotations)
304
+ updatedAnnotations[index].seriesKey = value || config.series?.[0]?.dataKey
305
+ updateConfig({
306
+ ...config,
307
+ annotations: updatedAnnotations
308
+ })
309
+ }}
310
+ />
311
+ )}
267
312
 
268
313
  <label>
269
314
  Opacity
@@ -313,24 +358,26 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
313
358
  }}
314
359
  />
315
360
 
316
- <Select
317
- label='Connection Type:'
318
- value={config?.annotations[index]?.connectionType}
319
- options={['Select', 'curve', 'line', 'elbow', 'none']}
320
- section='annotations'
321
- subsection={null}
322
- fieldName='connectionType'
323
- updateField={(section, subsection, fieldName, value) => {
324
- const updatedAnnotations = cloneDeep(config?.annotations)
325
- updatedAnnotations[index].connectionType = value
326
- updateConfig({
327
- ...config,
328
- annotations: updatedAnnotations
329
- })
330
- }}
331
- />
361
+ {annotation.style !== 'event-line' && (
362
+ <Select
363
+ label='Connection Type:'
364
+ value={config?.annotations[index]?.connectionType}
365
+ options={['Select', 'curve', 'line', 'elbow', 'none']}
366
+ section='annotations'
367
+ subsection={null}
368
+ fieldName='connectionType'
369
+ updateField={(section, subsection, fieldName, value) => {
370
+ const updatedAnnotations = cloneDeep(config?.annotations)
371
+ updatedAnnotations[index].connectionType = value
372
+ updateConfig({
373
+ ...config,
374
+ annotations: updatedAnnotations
375
+ })
376
+ }}
377
+ />
378
+ )}
332
379
 
333
- {annotation.connectionType === 'curve' && (
380
+ {annotation.style !== 'event-line' && annotation.connectionType === 'curve' && (
334
381
  <>
335
382
  <label>
336
383
  Curve Control
@@ -353,22 +400,24 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
353
400
  </>
354
401
  )}
355
402
 
356
- <Select
357
- label='Marker'
358
- value={annotation.marker}
359
- options={['arrow', 'circle']}
360
- section='annotations'
361
- subsection={null}
362
- fieldName='marker'
363
- updateField={(section, subsection, fieldName, value) => {
364
- const updatedAnnotations = cloneDeep(config?.annotations)
365
- updatedAnnotations[index].marker = value
366
- updateConfig({
367
- ...config,
368
- annotations: updatedAnnotations
369
- })
370
- }}
371
- />
403
+ {annotation.style !== 'event-line' && (
404
+ <Select
405
+ label='Marker'
406
+ value={annotation.marker}
407
+ options={['arrow', 'circle']}
408
+ section='annotations'
409
+ subsection={null}
410
+ fieldName='marker'
411
+ updateField={(section, subsection, fieldName, value) => {
412
+ const updatedAnnotations = cloneDeep(config?.annotations)
413
+ updatedAnnotations[index].marker = value
414
+ updateConfig({
415
+ ...config,
416
+ annotations: updatedAnnotations
417
+ })
418
+ }}
419
+ />
420
+ )}
372
421
  </AccordionItemPanel>
373
422
  </AccordionItem>
374
423
  </AccessibleAccordion>
@@ -0,0 +1,98 @@
1
+ import React from 'react'
2
+ import { Accordion } from 'react-accessible-accordion'
3
+ import { render, screen, fireEvent } from '@testing-library/react'
4
+ import { describe, expect, it, vi } from 'vitest'
5
+ import ConfigContext from '../../../../ConfigContext'
6
+ import EditorPanelContext from '../../EditorPanelContext'
7
+ import PanelSmallMultiples from './Panel.SmallMultiples'
8
+ import { createMockChartContext, createMockConfig } from '../../../LinearChart/tests/mockConfigContext'
9
+
10
+ vi.mock('@cdc/core/components/ui/Icon', () => ({
11
+ default: () => <span data-testid='icon' />
12
+ }))
13
+
14
+ const renderPanel = config => {
15
+ const context = createMockChartContext(config, {
16
+ rawData: [{ Date: '2024-01-01', series_a: 1, series_b: 2 }]
17
+ } as any)
18
+
19
+ render(
20
+ <ConfigContext.Provider value={context}>
21
+ <EditorPanelContext.Provider value={{ updateField: vi.fn() }}>
22
+ <Accordion allowZeroExpanded>
23
+ <PanelSmallMultiples name='Small Multiples' />
24
+ </Accordion>
25
+ </EditorPanelContext.Provider>
26
+ </ConfigContext.Provider>
27
+ )
28
+
29
+ fireEvent.click(screen.getByRole('button', { name: 'Small Multiples' }))
30
+ }
31
+
32
+ describe('PanelSmallMultiples color assignments warning', () => {
33
+ it('shows the by-series same-color override warning only when assignments are active', () => {
34
+ const config = createMockConfig({
35
+ visualizationType: 'Line',
36
+ general: {
37
+ palette: {
38
+ colorAssignmentMode: 'by-value',
39
+ colorAssignments: [{ key: 'series_a', color: '#123456' }]
40
+ }
41
+ } as any,
42
+ smallMultiples: {
43
+ mode: 'by-series',
44
+ colorMode: 'same'
45
+ },
46
+ series: [
47
+ { dataKey: 'series_a', name: 'Alpha', type: 'Line' },
48
+ { dataKey: 'series_b', name: 'Beta', type: 'Line' }
49
+ ] as any
50
+ })
51
+
52
+ renderPanel(config)
53
+
54
+ expect(screen.queryByText(/Color assignments are enabled/)).toBeInTheDocument()
55
+ })
56
+
57
+ it('does not show the override warning when assignment mode is ordered', () => {
58
+ const config = createMockConfig({
59
+ visualizationType: 'Line',
60
+ general: {
61
+ palette: {
62
+ colorAssignmentMode: 'ordered',
63
+ colorAssignments: [{ key: 'series_a', color: '#123456' }]
64
+ }
65
+ } as any,
66
+ smallMultiples: {
67
+ mode: 'by-series',
68
+ colorMode: 'same'
69
+ },
70
+ series: [{ dataKey: 'series_a', name: 'Alpha', type: 'Line' }] as any
71
+ })
72
+
73
+ renderPanel(config)
74
+
75
+ expect(screen.queryByText(/Color assignments are enabled/)).not.toBeInTheDocument()
76
+ })
77
+
78
+ it('does not show the override warning when assignments only contain stale keys', () => {
79
+ const config = createMockConfig({
80
+ visualizationType: 'Line',
81
+ general: {
82
+ palette: {
83
+ colorAssignmentMode: 'by-value',
84
+ colorAssignments: [{ key: 'removed_series', color: '#123456' }]
85
+ }
86
+ } as any,
87
+ smallMultiples: {
88
+ mode: 'by-series',
89
+ colorMode: 'same'
90
+ },
91
+ series: [{ dataKey: 'series_a', name: 'Alpha', type: 'Line' }] as any
92
+ })
93
+
94
+ renderPanel(config)
95
+
96
+ expect(screen.queryByText(/Color assignments are enabled/)).not.toBeInTheDocument()
97
+ })
98
+ })
@@ -20,6 +20,7 @@ import { useEditorPanelContext } from '../../EditorPanelContext.js'
20
20
  import ConfigContext from '../../../../ConfigContext.js'
21
21
  import { PanelProps } from '../PanelProps'
22
22
  import { getTileKeys } from '../../../../helpers/smallMultiplesHelpers'
23
+ import { hasSeriesColorAssignmentOverrides } from '../../../../helpers/colorAssignmentHelpers'
23
24
 
24
25
  const PanelSmallMultiples: FC<PanelProps> = props => {
25
26
  const { config, rawData, updateConfig } = useContext<ChartContext>(ConfigContext)
@@ -273,6 +274,15 @@ const PanelSmallMultiples: FC<PanelProps> = props => {
273
274
  }
274
275
  />
275
276
 
277
+ {config.smallMultiples?.mode === 'by-series' &&
278
+ config.smallMultiples?.colorMode === 'same' &&
279
+ hasSeriesColorAssignmentOverrides(config) && (
280
+ <p className='small-multiples-color-assignment-warning mt-1'>
281
+ Color assignments are enabled in the Visual section, so tiles will use their assigned series
282
+ colors instead of the &lsquo;Same Color&rsquo; setting.
283
+ </p>
284
+ )}
285
+
276
286
  {/* Custom Tile Titles - only show for by-column mode */}
277
287
  {config.smallMultiples?.mode === 'by-column' && (
278
288
  <div>