@cdc/chart 4.24.2 → 4.24.4

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 (60) hide show
  1. package/dist/cdcchart.js +47933 -36918
  2. package/examples/chart-regression-1.json +378 -0
  3. package/examples/chart-regression-2.json +2360 -0
  4. package/examples/feature/filters/url-filter.json +1076 -0
  5. package/examples/feature/line/line-chart.json +362 -37
  6. package/examples/feature/regions/index.json +50 -4
  7. package/examples/feature/sankey/sankey-example-data.json +1364 -0
  8. package/examples/feature/sankey/sankey_chart_data.csv +20 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
  10. package/examples/region-issue.json +2065 -0
  11. package/examples/sparkline.json +868 -0
  12. package/examples/test.json +5409 -0
  13. package/index.html +130 -123
  14. package/package.json +4 -2
  15. package/src/CdcChart.tsx +178 -94
  16. package/src/_stories/ChartEditor.stories.tsx +14 -3
  17. package/src/_stories/_mock/url_filter.json +1076 -0
  18. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
  19. package/src/components/AreaChart/components/AreaChart.jsx +2 -1
  20. package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -63
  21. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
  22. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +32 -39
  23. package/src/components/BarChart/components/BarChart.Vertical.tsx +44 -59
  24. package/src/components/BoxPlot/BoxPlot.jsx +2 -1
  25. package/src/components/DeviationBar.jsx +3 -3
  26. package/src/components/EditorPanel/EditorPanel.tsx +1684 -1564
  27. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
  28. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +107 -0
  29. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +48 -4
  30. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +41 -0
  31. package/src/components/EditorPanel/components/Panels/index.tsx +9 -7
  32. package/src/components/EditorPanel/components/panels.scss +11 -0
  33. package/src/components/EditorPanel/editor-panel.scss +0 -724
  34. package/src/components/EditorPanel/useEditorPermissions.js +40 -14
  35. package/src/components/Legend/Legend.Component.tsx +43 -63
  36. package/src/components/Legend/Legend.tsx +8 -4
  37. package/src/components/LineChart/LineChartProps.ts +1 -0
  38. package/src/components/LineChart/helpers.ts +2 -2
  39. package/src/components/LineChart/index.tsx +7 -7
  40. package/src/components/LinearChart.jsx +11 -31
  41. package/src/components/PairedBarChart.jsx +6 -10
  42. package/src/components/PieChart/PieChart.tsx +3 -3
  43. package/src/components/Regions/components/Regions.tsx +120 -78
  44. package/src/components/Sankey/index.tsx +434 -0
  45. package/src/components/Sankey/sankey.scss +153 -0
  46. package/src/components/Sankey/types/index.ts +16 -0
  47. package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
  48. package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
  49. package/src/components/Sparkline/index.scss +3 -0
  50. package/src/components/Sparkline/index.tsx +1 -1
  51. package/src/components/ZoomBrush.tsx +2 -1
  52. package/src/data/initial-state.js +46 -2
  53. package/src/helpers/computeMarginBottom.ts +2 -1
  54. package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
  55. package/src/hooks/useBarChart.js +5 -2
  56. package/src/hooks/useScales.ts +47 -18
  57. package/src/hooks/useTooltip.tsx +9 -8
  58. package/src/scss/main.scss +33 -29
  59. package/src/types/ChartConfig.ts +32 -14
  60. package/src/types/ChartContext.ts +7 -0
@@ -104,7 +104,7 @@ const RegionSettings = memo(({ config, updateConfig }: { config: ChartConfig; up
104
104
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
105
105
  </Tooltip.Target>
106
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>
107
+ <p>When using categorical (linear scale) match the data set value. When using date (linear / date time scale) match the x-axis value.</p>
108
108
  </Tooltip.Content>
109
109
  </Tooltip>
110
110
  }
@@ -0,0 +1,107 @@
1
+ import { useContext } from 'react'
2
+ import ConfigContext from '../../../../ConfigContext'
3
+ import { CheckBox, TextField } from '@cdc/core/components/EditorPanel/Inputs'
4
+ import Button from '@cdc/core/components/elements/Button'
5
+
6
+ import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
7
+ import EditorPanelContext, { type EditorPanelContext as EPContext } from '../../EditorPanelContext'
8
+
9
+ const SankeySettings = () => {
10
+ const { config, updateConfig } = useContext(ConfigContext)
11
+ const data = config.data?.[0]
12
+ const { updateField } = useContext<EPContext>(EditorPanelContext)
13
+
14
+ if (config.visualizationType !== 'Sankey') return
15
+
16
+ const updateStoryNode = (fieldName, value, i) => {
17
+ let storyNodes = []
18
+
19
+ if (data?.storyNodeText) {
20
+ storyNodes = [...data?.storyNodeText]
21
+ }
22
+
23
+ storyNodes[i][fieldName] = value
24
+ updateConfig({
25
+ ...config,
26
+ sankey: {
27
+ ...config.sankey,
28
+ data: {
29
+ ...config.sankey.data,
30
+ storyNodeText: storyNodes
31
+ }
32
+ }
33
+ })
34
+ }
35
+
36
+ const addStoryNode = () => {
37
+ const newData = data
38
+
39
+ newData.storyNodeText.push({
40
+ StoryNode: '',
41
+ segmentTextBefore: '',
42
+ segmentTextAfter: ''
43
+ })
44
+
45
+ updateConfig({
46
+ ...config,
47
+ sankey: {
48
+ ...config.sankey,
49
+ data: [{ ...newData }]
50
+ }
51
+ })
52
+ }
53
+
54
+ const removeStoryNode = index => {
55
+ const newData = data
56
+ newData.storyNodeText.splice(index, 1)
57
+
58
+ updateConfig({ ...config, sankey: { ...config.sankey, data: { ...newData } } })
59
+ }
60
+
61
+ return (
62
+ <AccordionItem>
63
+ <AccordionItemHeading>
64
+ <AccordionItemButton>Sankey Settings</AccordionItemButton>
65
+ </AccordionItemHeading>
66
+ <AccordionItemPanel>
67
+ {data?.storyNodeText &&
68
+ data?.storyNodeText.map(({ StoryNode, segmentTextBefore, segmentTextAfter }, i) => (
69
+ <div key={i} style={{ border: '1px solid black', margin: '15px auto', padding: '15px', borderRadius: '10px' }}>
70
+ <label>
71
+ Story Node Text
72
+ <input type='text' value={StoryNode} fieldName='StoryNode' label='StoryNode' onChange={e => updateStoryNode('StoryNode', e.target.value, i)} />
73
+ </label>
74
+ <label>
75
+ Story Text Before
76
+ <input type='text' value={segmentTextBefore} fieldName='segmentTextBefore' label='Segment Text Before' onChange={e => updateStoryNode('segmentTextBefore', e.target.value, i)} />
77
+ </label>
78
+ <label>
79
+ Story Text After
80
+ <input type='text' value={segmentTextAfter} fieldName='segmentTextAfter' label='Segment Text After' onChange={e => updateStoryNode('segmentTextAfter', e.target.value, i)} />
81
+ </label>
82
+ <Button onClick={e => removeStoryNode(i)} className='btn' style={{ background: 'tomato' }}>
83
+ Remove Story Node
84
+ </Button>
85
+ </div>
86
+ ))}
87
+ {`Total Story Nodes: ${data?.storyNodeText?.length}`}
88
+ {data?.storyNodeText?.length < 3 && (
89
+ <button
90
+ type='button'
91
+ className='btn full-width'
92
+ onClick={e => {
93
+ e.preventDefault()
94
+ addStoryNode()
95
+ }}
96
+ >
97
+ Add StoryNode
98
+ </button>
99
+ )}
100
+
101
+ <CheckBox value={config.enableTooltips} fieldName='enableTooltips' label='Enable Tooltips' updateField={updateField} />
102
+ </AccordionItemPanel>
103
+ </AccordionItem>
104
+ )
105
+ }
106
+
107
+ export default SankeySettings
@@ -424,14 +424,56 @@ const SeriesDropdownConfidenceInterval = props => {
424
424
  )
425
425
  }
426
426
 
427
+ const SeriesInputWeight = props => {
428
+ const { series, index: i } = props
429
+ const { config, updateConfig } = useContext(ConfigContext)
430
+ const adjustableWeightSeriesTypes = ['Line', 'Combo', 'dashed-sm', 'dashed-md', 'dashed-lg']
431
+
432
+ if (!adjustableWeightSeriesTypes.includes(series.type)) return
433
+
434
+ const changeSeriesWeight = (i, value, min, max) => {
435
+ let series = [...config.series]
436
+ let seriesLabelsCopy = { ...config.runtime.seriesLabels }
437
+ series[i].weight = !value ? value : Math.max(Number(min), Math.min(Number(max), Number(value)))
438
+ seriesLabelsCopy[series[i].dataKey] = series[i].weight ? series[i].weight : series[i].dataKey
439
+
440
+ const newConfig = {
441
+ ...config,
442
+ series,
443
+ runtime: {
444
+ ...config.runtime,
445
+ seriesLabels: seriesLabelsCopy
446
+ }
447
+ }
448
+
449
+ updateConfig(newConfig)
450
+ }
451
+
452
+ return (
453
+ <>
454
+ <label htmlFor='series-weight'>Line Weight</label>
455
+ <input
456
+ type='number'
457
+ key={`series-weight-${i}`}
458
+ value={series.weight ? series.weight : ''}
459
+ min="1"
460
+ max="9"
461
+ onChange={event => {
462
+ changeSeriesWeight(i, event.target.value, event.target.min, event.target.max)
463
+ }}
464
+ />
465
+ </>
466
+ )
467
+ }
468
+
427
469
  const SeriesInputName = props => {
428
470
  const { series, index: i } = props
429
471
  const { config, updateConfig } = useContext(ConfigContext)
430
- const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart', 'Combo', 'Deviation', 'Paired', 'Scatter', 'dashed-sm', 'dashed-md', 'dashed-lg']
472
+ const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart', 'Combo', 'Deviation Bar', 'Paired Bar', 'Scatter Plot', 'dashed-sm', 'dashed-md', 'dashed-lg']
431
473
 
432
474
  if (!adjustableNameSeriesTypes.includes(series.type)) return
433
475
 
434
- let changeSeriesName = (i, value) => {
476
+ const changeSeriesName = (i, value) => {
435
477
  let series = [...config.series]
436
478
  let seriesLabelsCopy = { ...config.runtime.seriesLabels }
437
479
  series[i].name = value
@@ -468,7 +510,7 @@ const SeriesDisplayInTooltip = props => {
468
510
  const { series, index } = props
469
511
  const { config, updateConfig } = useContext(ConfigContext)
470
512
 
471
- if(['Paired Bar', 'Scatter Plot', 'Deviation Bar'].includes(config.visualizationType)) return
513
+ if (['Paired Bar', 'Scatter Plot', 'Deviation Bar'].includes(config.visualizationType)) return
472
514
 
473
515
  const toggleTooltip = seriesIndex => {
474
516
  let copiedSeries = [...config.series]
@@ -569,6 +611,7 @@ const SeriesItem = props => {
569
611
  {chartsWithOptions.includes(config.visualizationType) && (
570
612
  <AccordionItemPanel>
571
613
  <Series.Input.Name series={series} index={i} />
614
+ <Series.Input.Weight series={series} index={i} />
572
615
  <Series.Dropdown.SeriesType series={series} index={i} />
573
616
  <Series.Dropdown.AxisPosition series={series} index={i} />
574
617
  <Series.Dropdown.LineType series={series} index={i} />
@@ -604,7 +647,8 @@ const Series = {
604
647
  ForecastingColor: SeriesDropdownForecastColor
605
648
  },
606
649
  Input: {
607
- Name: SeriesInputName
650
+ Name: SeriesInputName,
651
+ Weight: SeriesInputWeight
608
652
  },
609
653
  Checkbox: {
610
654
  DisplayInTooltip: SeriesDisplayInTooltip
@@ -25,6 +25,31 @@ const PanelVisual: FC<PanelProps> = props => {
25
25
  const { visHasBarBorders, visCanAnimate, visSupportsNonSequentialPallete, headerColors, visSupportsTooltipOpacity, visSupportsTooltipLines, visSupportsBarSpace, visSupportsBarThickness, visHasDataCutoff, visSupportsSequentialPallete, visSupportsReverseColorPalette } = useEditorPermissions()
26
26
  const { twoColorPalettes, sequential, nonSequential } = useColorPalette(config, updateConfig)
27
27
 
28
+ const updateColor = (property, _value) => {
29
+ console.log('value', _value)
30
+ if (property === 'storyNodeFontColor') {
31
+ updateConfig({
32
+ ...config,
33
+ sankey: {
34
+ ...config.sankey,
35
+ storyNodeFontColor: _value
36
+ }
37
+ })
38
+ return
39
+ } else {
40
+ updateConfig({
41
+ ...config,
42
+ sankey: {
43
+ ...config.sankey,
44
+ [property]: {
45
+ ...config.sankey[property],
46
+ default: _value
47
+ }
48
+ }
49
+ })
50
+ }
51
+ }
52
+
28
53
  return (
29
54
  <AccordionItem>
30
55
  <AccordionItemHeading>
@@ -175,6 +200,22 @@ const PanelVisual: FC<PanelProps> = props => {
175
200
  )}
176
201
  </>
177
202
  )}
203
+ {config.visualizationType === 'Sankey' && (
204
+ <>
205
+ <span className='sankey__color-input'>
206
+ <input type='color' value={config.sankey.nodeColor.default} id='storyNodeColor' name='storyNodeColor' onChange={e => updateColor('nodeColor', e.target.value)} />
207
+ <label htmlFor='storyNodeColor'>Story Node Color</label>
208
+ </span>
209
+ <span className='sankey__color-input'>
210
+ <input type='color' value={config.sankey.storyNodeFontColor || 'red'} id='storyNodeFontColor' name='storyNodeFontColor' onChange={e => updateColor('storyNodeFontColor', e.target.value)} />
211
+ <label htmlFor='storyNodeFontColor'>Story Node Font Color</label>
212
+ </span>
213
+ <span className='sankey__color-input'>
214
+ <input type='color' value={config.sankey.linkColor.default} id='linkColor' name='linkColor' onChange={e => updateColor('linkColor', e.target.value)} />
215
+ <label htmlFor='linkColor'>Link Color</label>
216
+ </span>
217
+ </>
218
+ )}
178
219
  {(config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar') && (
179
220
  <>
180
221
  <InputToggle section='twoColor' fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={config.twoColor.isPaletteReversed} />
@@ -1,9 +1,10 @@
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'
1
+ import ForestPlotSettings from './Panel.ForestPlotSettings'
2
+ import Series from './Panel.Series'
3
+ import Regions from './Panel.Regions'
4
+ import General from './Panel.General'
5
+ import BoxPlot from './Panel.BoxPlot'
6
+ import Visual from './Panel.Visual'
7
+ import Sankey from './Panel.Sankey'
7
8
 
8
9
  const Panels = {
9
10
  ForestPlot: ForestPlotSettings,
@@ -11,7 +12,8 @@ const Panels = {
11
12
  Regions,
12
13
  General,
13
14
  BoxPlot,
14
- Visual
15
+ Visual,
16
+ Sankey
15
17
  }
16
18
 
17
19
  export default Panels
@@ -70,3 +70,14 @@
70
70
  }
71
71
  }
72
72
  }
73
+
74
+ .sankey__color-input {
75
+ display: flex;
76
+ align-items: center;
77
+ margin: 10px auto;
78
+ label {
79
+ align-items: center;
80
+ padding-left: 5px;
81
+ margin-top: 0 !important;
82
+ }
83
+ }