@cdc/chart 4.26.2 → 4.26.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.
- package/CONFIG.md +169 -0
- package/README.md +42 -17
- package/dist/cdcchart.js +39419 -35180
- package/examples/__data__/data-with-metadata.json +10 -0
- package/examples/feature/bar/additional-column-tooltip.json +3 -3
- package/examples/feature/deviation/planet-deviation-config.json +2 -2
- package/examples/feature/paired-bar/paired-bar-example.json +2 -2
- package/examples/feature/pie/planet-pie-example-config.json +2 -1
- package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
- package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
- package/examples/feature/tests-covid/covid-example-config.json +2 -2
- package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
- package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
- package/examples/metadata-variables.json +58 -0
- package/examples/minimal-example.json +24 -0
- package/examples/private/test2.json +35012 -0
- package/package.json +4 -4
- package/src/CdcChart.tsx +8 -4
- package/src/CdcChartComponent.tsx +326 -290
- package/src/_stories/Chart.CustomColors.smoke.stories.tsx +97 -0
- package/src/_stories/Chart.Defaults.smoke.stories.tsx +95 -0
- package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
- package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
- package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
- package/src/_stories/Chart.SmallestLeftAxisMax.smoke.stories.tsx +64 -0
- package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +48 -2
- package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +177 -115
- package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
- package/src/_stories/ChartEditor.Editor.stories.tsx +24 -1
- package/src/_stories/{Chart.CustomColors.stories.tsx → ChartLine.DashedDates.smoke.stories.tsx} +4 -4
- package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
- package/src/_stories/_mock/paired-bar-abbr.json +421 -0
- package/src/_stories/_mock/paired-bar.json +1 -1
- package/src/_stories/_mock/pie_custom_colors.json +268 -0
- package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
- package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
- package/src/components/Axis/BottomAxis.tsx +10 -3
- package/src/components/Axis/LeftAxis.tsx +2 -4
- package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
- package/src/components/Axis/PairedBarAxis.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +21 -33
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +21 -35
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +21 -36
- package/src/components/BarChart/components/BarChart.Vertical.tsx +21 -34
- package/src/components/BarChart/components/BarChart.tsx +2 -6
- package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
- package/src/components/BarChart/helpers/useBarChart.ts +4 -1
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
- package/src/components/Brush/BrushSelector.tsx +2 -1
- package/src/components/Brush/MiniChartPreview.tsx +21 -26
- package/src/components/EditorPanel/EditorPanel.tsx +692 -954
- package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +218 -167
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -135
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +95 -28
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
- package/src/components/EditorPanel/editor-panel.scss +1 -1
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
- package/src/components/Forecasting/Forecasting.tsx +2 -2
- package/src/components/ForestPlot/ForestPlot.tsx +26 -22
- package/src/components/HorizonChart/HorizonChart.tsx +10 -2
- package/src/components/HoverLine/HoverLine.tsx +6 -8
- package/src/components/Legend/Legend.Component.tsx +4 -4
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +9 -4
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
- package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
- package/src/components/Legend/helpers/index.ts +0 -2
- package/src/components/LineChart/LineChartProps.ts +1 -0
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +3 -2
- package/src/components/LineChart/index.tsx +11 -4
- package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
- package/src/components/LinearChart/tests/LinearChart.test.tsx +224 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
- package/src/components/LinearChart.tsx +124 -47
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/PieChart/PieChart.tsx +30 -13
- package/src/components/RadarChart/RadarChart.tsx +1 -1
- package/src/components/Regions/components/Regions.tsx +36 -26
- package/src/components/Sankey/components/Sankey.tsx +3 -3
- package/src/components/Sankey/sankey.scss +1 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
- package/src/components/SmallMultiples/SmallMultiples.css +5 -5
- package/src/components/Sparkline/components/SparkLine.tsx +1 -1
- package/src/components/Sparkline/index.scss +4 -2
- package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
- package/src/data/initial-state.js +24 -14
- package/src/data/legacy-defaults.ts +18 -0
- package/src/helpers/abbreviateNumber.ts +24 -17
- package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
- package/src/helpers/getChartPatternId.ts +17 -0
- package/src/helpers/getMinMax.ts +16 -2
- package/src/helpers/seriesColumnSettings.ts +114 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
- package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
- package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
- package/src/helpers/tooltipHelpers.ts +176 -0
- package/src/hooks/useHighlightedBars.ts +9 -16
- package/src/hooks/useProgrammaticTooltip.ts +16 -2
- package/src/hooks/useRightAxis.ts +14 -0
- package/src/hooks/useScales.ts +92 -56
- package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
- package/src/hooks/useTooltip.tsx +186 -40
- package/src/scss/main.scss +152 -79
- package/src/test/CdcChart.test.jsx +26 -2
- package/src/types/ChartConfig.ts +5 -0
- package/tests/fixtures/chart-config-with-metadata.json +29 -0
- package/tests/fixtures/data-with-metadata.json +10 -0
- package/examples/chart-regression-2.json +0 -2360
- package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
- package/examples/feature/__data__/city-temperature.json +0 -2198
- package/examples/feature/bar/Barchart_with_negative.json +0 -34
- package/examples/feature/boxplot/box-plot-data.json +0 -71
- package/examples/feature/boxplot/boxplot-data.json +0 -156
- package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
- package/examples/feature/forest-plot/log.json +0 -26
- package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
- package/examples/region-issue.json +0 -2065
- package/examples/xaxis.json +0 -493
- /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
- /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
- /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
- /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
- /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
- /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
- /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
- /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
- /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
- /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
- /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import Chart from '../CdcChartComponent'
|
|
3
|
+
import scatterPlotCustomColorConfig from './_mock/scatterplot_mock.json'
|
|
4
|
+
import pieCustomColorConfig from './_mock/pie_custom_colors.json'
|
|
5
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
|
+
import { expect, userEvent, waitFor } from 'storybook/test'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Chart> = {
|
|
9
|
+
title: 'Components/Templates/Chart/Custom Colors',
|
|
10
|
+
component: Chart
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Chart>
|
|
14
|
+
|
|
15
|
+
export const ScatterPlot: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
config: scatterPlotCustomColorConfig,
|
|
18
|
+
isEditor: false
|
|
19
|
+
},
|
|
20
|
+
play: async ({ canvasElement }) => {
|
|
21
|
+
await assertVisualizationRendered(canvasElement)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const pieCustomColors = ['#0B6E4F', '#C75000', '#7C4DFF', '#0077B6', '#D7263D']
|
|
26
|
+
const pieFallbackCustomColors = ['#004E89', '#1A659E', '#47A8BD', '#D1495B', '#EDAE49']
|
|
27
|
+
|
|
28
|
+
const normalizeColor = (color: string) => {
|
|
29
|
+
const swatch = document.createElement('div')
|
|
30
|
+
swatch.style.color = color
|
|
31
|
+
document.body.appendChild(swatch)
|
|
32
|
+
const normalized = getComputedStyle(swatch).color
|
|
33
|
+
swatch.remove()
|
|
34
|
+
return normalized
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const assertPieCustomColorStory = async (canvasElement: HTMLElement, expectedColors: string[]) => {
|
|
38
|
+
await assertVisualizationRendered(canvasElement)
|
|
39
|
+
|
|
40
|
+
const chart = canvasElement.querySelector('svg.animated-pie')
|
|
41
|
+
const legend = canvasElement.querySelector('.legend-container')
|
|
42
|
+
expect(chart).toBeTruthy()
|
|
43
|
+
expect(legend).toBeTruthy()
|
|
44
|
+
|
|
45
|
+
await waitFor(() => {
|
|
46
|
+
expect(chart?.querySelectorAll('path')).toHaveLength(expectedColors.length)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const legendItems = Array.from(legend!.querySelectorAll('[role="button"]')) as HTMLElement[]
|
|
50
|
+
expect(legendItems).toHaveLength(expectedColors.length)
|
|
51
|
+
|
|
52
|
+
const sliceColors = Array.from(chart!.querySelectorAll('path')).map(path =>
|
|
53
|
+
normalizeColor(path.getAttribute('fill') || '')
|
|
54
|
+
)
|
|
55
|
+
expect(sliceColors).toEqual(expectedColors.map(normalizeColor))
|
|
56
|
+
|
|
57
|
+
const legendColors = legendItems.map(item => {
|
|
58
|
+
const swatch = item.querySelector('span.legend-item') as HTMLElement | null
|
|
59
|
+
return normalizeColor(swatch ? getComputedStyle(swatch).backgroundColor : '')
|
|
60
|
+
})
|
|
61
|
+
expect(legendColors).toEqual(expectedColors.map(normalizeColor))
|
|
62
|
+
|
|
63
|
+
await userEvent.click(legendItems[0])
|
|
64
|
+
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(chart?.querySelectorAll('path')).toHaveLength(1)
|
|
67
|
+
expect(legendItems[0].className).toContain('highlighted')
|
|
68
|
+
expect(legendItems[1].className).toContain('inactive')
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const PieChart: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
config: pieCustomColorConfig,
|
|
75
|
+
isEditor: false
|
|
76
|
+
},
|
|
77
|
+
play: async ({ canvasElement }) => {
|
|
78
|
+
await assertPieCustomColorStory(canvasElement, pieCustomColors)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const pieChartCustomColorsFallbackConfig = JSON.parse(JSON.stringify(pieCustomColorConfig))
|
|
83
|
+
pieChartCustomColorsFallbackConfig.title = 'Pie Chart Custom Colors Fallback'
|
|
84
|
+
pieChartCustomColorsFallbackConfig.general.palette.customColors = pieFallbackCustomColors
|
|
85
|
+
delete pieChartCustomColorsFallbackConfig.general.palette.customColorsOrdered
|
|
86
|
+
|
|
87
|
+
export const PieChartCustomColorsFallback: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
config: pieChartCustomColorsFallbackConfig,
|
|
90
|
+
isEditor: false
|
|
91
|
+
},
|
|
92
|
+
play: async ({ canvasElement }) => {
|
|
93
|
+
await assertPieCustomColorStory(canvasElement, pieFallbackCustomColors)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default meta
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { expect } from 'storybook/test'
|
|
3
|
+
import Chart from '../CdcChart'
|
|
4
|
+
import simplifiedLine from './_mock/simplified_line.json'
|
|
5
|
+
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
6
|
+
import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Chart> = {
|
|
9
|
+
title: 'Components/Templates/Chart/Defaults',
|
|
10
|
+
component: Chart
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Chart>
|
|
14
|
+
|
|
15
|
+
const oldConfig = editConfigKeys(simplifiedLine, [
|
|
16
|
+
{ path: ['yAxis', 'hideAxis'], value: false },
|
|
17
|
+
{ path: ['yAxis', 'hideTicks'], value: false },
|
|
18
|
+
{ path: ['yAxis', 'gridLines'], value: false },
|
|
19
|
+
{ path: ['yAxis', 'numTicks'], value: '' },
|
|
20
|
+
{ path: ['xAxis', 'numTicks'], value: '' },
|
|
21
|
+
{ path: ['table', 'expanded'], value: true },
|
|
22
|
+
{ path: ['legend', 'position'], value: 'right' },
|
|
23
|
+
{ path: ['dataFormat', 'commas'], value: false },
|
|
24
|
+
{ path: ['tooltips', 'dateDisplayFormat'], value: '' }
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
const newConfig = editConfigKeys(simplifiedLine, [
|
|
28
|
+
{ path: ['yAxis', 'hideAxis'], value: true },
|
|
29
|
+
{ path: ['yAxis', 'hideTicks'], value: true },
|
|
30
|
+
{ path: ['yAxis', 'gridLines'], value: true },
|
|
31
|
+
{ path: ['yAxis', 'numTicks'], value: 4 },
|
|
32
|
+
{ path: ['xAxis', 'numTicks'], value: 6 },
|
|
33
|
+
{ path: ['table', 'expanded'], value: false },
|
|
34
|
+
{ path: ['legend', 'position'], value: 'top' },
|
|
35
|
+
{ path: ['dataFormat', 'commas'], value: true },
|
|
36
|
+
{ path: ['tooltips', 'dateDisplayFormat'], value: '%B %-d, %Y' }
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
export const OldConfig_Preserves_Legacy_Defaults: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
config: oldConfig,
|
|
42
|
+
isEditor: false
|
|
43
|
+
},
|
|
44
|
+
play: async ({ canvasElement }) => {
|
|
45
|
+
await assertVisualizationRendered(canvasElement)
|
|
46
|
+
|
|
47
|
+
await waitForPresence('.legend-container', canvasElement)
|
|
48
|
+
const legend = canvasElement.querySelector('.legend-container')
|
|
49
|
+
expect(legend).toBeInTheDocument()
|
|
50
|
+
expect(legend?.classList.contains('right')).toBe(true)
|
|
51
|
+
|
|
52
|
+
const axisLine = canvasElement.querySelector('.left-axis line[stroke="#000"]')
|
|
53
|
+
expect(axisLine).toBeInTheDocument()
|
|
54
|
+
|
|
55
|
+
const gridlines = canvasElement.querySelectorAll('.left-axis line[stroke="#d6d6d6"]')
|
|
56
|
+
expect(gridlines.length).toBe(0)
|
|
57
|
+
|
|
58
|
+
const dataTable = canvasElement.querySelector('.data-table')
|
|
59
|
+
expect(dataTable).toBeInTheDocument()
|
|
60
|
+
expect(dataTable?.getAttribute('hidden')).toBeNull()
|
|
61
|
+
|
|
62
|
+
const tickTexts = canvasElement.querySelectorAll('.left-axis .vx-axis-tick text')
|
|
63
|
+
for (const tick of tickTexts) {
|
|
64
|
+
expect(tick.textContent).not.toMatch(/,/)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const NewConfig_Gets_New_Defaults: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
config: newConfig,
|
|
72
|
+
isEditor: false
|
|
73
|
+
},
|
|
74
|
+
play: async ({ canvasElement }) => {
|
|
75
|
+
await assertVisualizationRendered(canvasElement)
|
|
76
|
+
|
|
77
|
+
await waitForPresence('.legend-container', canvasElement)
|
|
78
|
+
const legend = canvasElement.querySelector('.legend-container')
|
|
79
|
+
expect(legend).toBeInTheDocument()
|
|
80
|
+
expect(legend?.classList.contains('top')).toBe(true)
|
|
81
|
+
|
|
82
|
+
const axisLine = canvasElement.querySelector('.left-axis line[stroke="#000"]')
|
|
83
|
+
expect(axisLine).toBeNull()
|
|
84
|
+
|
|
85
|
+
await waitForPresence('.left-axis line[stroke="#d6d6d6"]', canvasElement)
|
|
86
|
+
const gridlines = canvasElement.querySelectorAll('.left-axis line[stroke="#d6d6d6"]')
|
|
87
|
+
expect(gridlines.length).toBeGreaterThan(0)
|
|
88
|
+
|
|
89
|
+
const dataTable = canvasElement.querySelector('.data-table')
|
|
90
|
+
expect(dataTable).toBeInTheDocument()
|
|
91
|
+
expect(dataTable?.getAttribute('hidden')).not.toBeNull()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default meta
|
package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx}
RENAMED
|
@@ -493,6 +493,16 @@ export const PieChartWithHTMLFootnotes: Story = {
|
|
|
493
493
|
Footnote: '<strong>7 states</strong> with 60-79% coverage<br>Target: Move to <em>high category</em>'
|
|
494
494
|
}
|
|
495
495
|
],
|
|
496
|
+
xAxis: {
|
|
497
|
+
label: 'Coverage Category',
|
|
498
|
+
dataKey: 'Category',
|
|
499
|
+
type: 'categorical'
|
|
500
|
+
},
|
|
501
|
+
yAxis: {
|
|
502
|
+
label: 'Percentage',
|
|
503
|
+
dataKey: 'Percentage',
|
|
504
|
+
type: 'linear'
|
|
505
|
+
},
|
|
496
506
|
series: [
|
|
497
507
|
{
|
|
498
508
|
dataKey: 'Percentage',
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import Chart from '../CdcChart'
|
|
3
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
4
|
+
import MinimalExampleConfig from '../../examples/minimal-example.json'
|
|
5
|
+
import { expect } from 'storybook/test'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Chart> = {
|
|
8
|
+
title: 'Components/Templates/Chart/Minimal Config',
|
|
9
|
+
component: Chart
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
type Story = StoryObj<typeof Chart>
|
|
14
|
+
|
|
15
|
+
export const Minimal_Bar_Chart: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
config: MinimalExampleConfig,
|
|
18
|
+
isEditor: false
|
|
19
|
+
},
|
|
20
|
+
play: async ({ canvasElement }) => {
|
|
21
|
+
await assertVisualizationRendered(canvasElement)
|
|
22
|
+
expect(canvasElement.textContent).toContain('Minimal Chart')
|
|
23
|
+
expect(canvasElement.textContent).toContain('A')
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx}
RENAMED
|
@@ -5,7 +5,8 @@ import smallMultiplesBigDataBars from './_mock/small_multiples/small_multiples_b
|
|
|
5
5
|
import smallMultiplesLinesColors from './_mock/small_multiples/small_multiples_lines_colors.json'
|
|
6
6
|
import smallMultiplesLines from './_mock/small_multiples/small_multiples_lines.json'
|
|
7
7
|
import smallMultiplesStackedBars from './_mock/small_multiples/small_multiples_stacked_bars.json'
|
|
8
|
-
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
8
|
+
import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
|
|
9
|
+
import { expect } from 'storybook/test'
|
|
9
10
|
|
|
10
11
|
const meta: Meta<typeof Chart> = {
|
|
11
12
|
title: 'Components/Templates/Chart/Small Multiples',
|
|
@@ -14,6 +15,11 @@ const meta: Meta<typeof Chart> = {
|
|
|
14
15
|
|
|
15
16
|
type Story = StoryObj<typeof Chart>
|
|
16
17
|
|
|
18
|
+
const getTooltipHtmlValues = (canvasElement: HTMLElement) =>
|
|
19
|
+
Array.from(canvasElement.querySelectorAll('[data-tooltip-html]')).map(
|
|
20
|
+
element => element.getAttribute('data-tooltip-html') || ''
|
|
21
|
+
)
|
|
22
|
+
|
|
17
23
|
export const smallMultiples_Bars: Story = {
|
|
18
24
|
args: {
|
|
19
25
|
config: smallMultiplesBars,
|
|
@@ -21,6 +27,11 @@ export const smallMultiples_Bars: Story = {
|
|
|
21
27
|
},
|
|
22
28
|
play: async ({ canvasElement }) => {
|
|
23
29
|
await assertVisualizationRendered(canvasElement)
|
|
30
|
+
await waitForPresence('[data-tooltip-html]', canvasElement)
|
|
31
|
+
|
|
32
|
+
const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
|
|
33
|
+
expect(tooltipHtmlValues.length).toBeGreaterThan(0)
|
|
34
|
+
expect(tooltipHtmlValues.some(html => html.includes('tooltip-marker-swatch'))).toBe(false)
|
|
24
35
|
}
|
|
25
36
|
}
|
|
26
37
|
|
|
@@ -58,6 +69,11 @@ export const smallMultiples_StackedBars: Story = {
|
|
|
58
69
|
},
|
|
59
70
|
play: async ({ canvasElement }) => {
|
|
60
71
|
await assertVisualizationRendered(canvasElement)
|
|
72
|
+
await waitForPresence('[data-tooltip-html]', canvasElement)
|
|
73
|
+
|
|
74
|
+
const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
|
|
75
|
+
expect(tooltipHtmlValues.length).toBeGreaterThan(0)
|
|
76
|
+
expect(tooltipHtmlValues.some(html => html.includes('tooltip-marker-swatch'))).toBe(true)
|
|
61
77
|
}
|
|
62
78
|
}
|
|
63
79
|
export default meta
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import Chart from '../CdcChartComponent'
|
|
3
|
+
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
4
|
+
import smallestLeftAxisMaxConfig from './_mock/smallest_left_axis_max.json'
|
|
5
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Chart> = {
|
|
8
|
+
title: 'Components/Templates/Chart/Smallest Left Axis Maximum',
|
|
9
|
+
component: Chart
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof Chart>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Region B is the default filter — data only goes up to 1.
|
|
16
|
+
* Without smallestLeftAxisMax, the Y axis would show decimal ticks (0, 0.2, 0.4…).
|
|
17
|
+
* With smallestLeftAxisMax: 5, the axis extends to at least 5, producing clean integer ticks.
|
|
18
|
+
*/
|
|
19
|
+
export const WithSmallestLeftAxisMax: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
config: smallestLeftAxisMaxConfig
|
|
22
|
+
},
|
|
23
|
+
play: async ({ canvasElement }) => {
|
|
24
|
+
await assertVisualizationRendered(canvasElement)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Same chart without the smallestLeftAxisMax setting.
|
|
30
|
+
* Region B data (max 1) causes decimal ticks on the Y axis — the problem this feature solves.
|
|
31
|
+
*/
|
|
32
|
+
export const WithoutSmallestLeftAxisMax: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
config: editConfigKeys(smallestLeftAxisMaxConfig, [{ path: ['yAxis', 'smallestLeftAxisMax'], value: undefined }])
|
|
35
|
+
},
|
|
36
|
+
play: async ({ canvasElement }) => {
|
|
37
|
+
await assertVisualizationRendered(canvasElement)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Region A has data up to 28, well above the smallestLeftAxisMax of 5.
|
|
43
|
+
* The axis naturally scales to the data — the setting has no effect here.
|
|
44
|
+
*/
|
|
45
|
+
export const LargerDataUnaffected: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
config: editConfigKeys(smallestLeftAxisMaxConfig, [{ path: ['filters', '0', 'active'], value: 'Region A' }])
|
|
48
|
+
},
|
|
49
|
+
play: async ({ canvasElement }) => {
|
|
50
|
+
await assertVisualizationRendered(canvasElement)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Editor view so you can see the Smallest Left Axis Maximum field in the Left Value Axis section.
|
|
56
|
+
*/
|
|
57
|
+
export const Editor: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
config: smallestLeftAxisMaxConfig,
|
|
60
|
+
isEditor: true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default meta
|
|
@@ -6,6 +6,7 @@ import lineChartTwoPointsNewChart from './_mock/line_chart_two_points_new_chart.
|
|
|
6
6
|
import lollipop from './_mock/lollipop.json'
|
|
7
7
|
import forestPlot from '../../examples/feature/forest-plot/forest-plot.json'
|
|
8
8
|
import pairedBar from './_mock/paired-bar.json'
|
|
9
|
+
import pairedBarAbbreviated from './_mock/paired-bar-abbr.json'
|
|
9
10
|
import horizontalBarConfig from './_mock/horizontal_bar.json'
|
|
10
11
|
import horizontalBarsDynamicYAxis from './_mock/horizontal-bars-dynamic-y-axis.json'
|
|
11
12
|
import barChartLabels from './_mock/barchart_labels.mock.json'
|
|
@@ -13,8 +14,10 @@ import pieConfig from './_mock/pie_with_data.json'
|
|
|
13
14
|
import pieCalculatedArea from './_mock/pie_calculated_area.json'
|
|
14
15
|
import areaChartStacked from './_mock/area_chart_stacked.json'
|
|
15
16
|
import multipleLines from './_mock/short_dates.json'
|
|
17
|
+
import MinimalExampleConfig from '../../examples/minimal-example.json'
|
|
16
18
|
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
17
|
-
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
19
|
+
import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
|
|
20
|
+
import { expect } from 'storybook/test'
|
|
18
21
|
|
|
19
22
|
const meta: Meta<typeof Chart> = {
|
|
20
23
|
title: 'Components/Templates/Chart',
|
|
@@ -63,7 +66,8 @@ export const Lollipop: Story = {
|
|
|
63
66
|
|
|
64
67
|
export const Forest_Plot: Story = {
|
|
65
68
|
args: {
|
|
66
|
-
config: forestPlot
|
|
69
|
+
config: forestPlot,
|
|
70
|
+
isEditor: true
|
|
67
71
|
},
|
|
68
72
|
play: async ({ canvasElement }) => {
|
|
69
73
|
await assertVisualizationRendered(canvasElement)
|
|
@@ -127,6 +131,16 @@ export const Paired_Bar: Story = {
|
|
|
127
131
|
}
|
|
128
132
|
}
|
|
129
133
|
|
|
134
|
+
export const Paired_Bar_Year_Tick_Format_Regression: Story = {
|
|
135
|
+
args: {
|
|
136
|
+
config: pairedBarAbbreviated,
|
|
137
|
+
isEditor: true
|
|
138
|
+
},
|
|
139
|
+
play: async ({ canvasElement }) => {
|
|
140
|
+
await assertVisualizationRendered(canvasElement)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
130
144
|
export const Area_Chart_stacked: Story = {
|
|
131
145
|
args: {
|
|
132
146
|
config: areaChartStacked,
|
|
@@ -137,4 +151,36 @@ export const Area_Chart_stacked: Story = {
|
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
|
|
154
|
+
export const Metadata_In_Description: Story = {
|
|
155
|
+
args: {
|
|
156
|
+
configUrl: '/packages/chart/tests/fixtures/chart-config-with-metadata.json'
|
|
157
|
+
},
|
|
158
|
+
play: async ({ canvasElement }) => {
|
|
159
|
+
await assertVisualizationRendered(canvasElement)
|
|
160
|
+
const subtext = await waitForPresence('.subtext', canvasElement)
|
|
161
|
+
expect(subtext?.textContent).toContain('January 15, 2026')
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const Metadata_Backward_Compat_Plain_Array: Story = {
|
|
166
|
+
args: {
|
|
167
|
+
config: lineChartTwoPointsRegressionTest,
|
|
168
|
+
isEditor: false
|
|
169
|
+
},
|
|
170
|
+
play: async ({ canvasElement }) => {
|
|
171
|
+
await assertVisualizationRendered(canvasElement)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const Minimal_Config: Story = {
|
|
176
|
+
args: {
|
|
177
|
+
config: MinimalExampleConfig,
|
|
178
|
+
isEditor: false
|
|
179
|
+
},
|
|
180
|
+
play: async ({ canvasElement }) => {
|
|
181
|
+
await assertVisualizationRendered(canvasElement)
|
|
182
|
+
expect(canvasElement.textContent).toContain('Minimal Chart')
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
140
186
|
export default meta
|
|
@@ -3,6 +3,10 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
3
3
|
import Chart from '../CdcChartComponent'
|
|
4
4
|
import barChartStacked from './_mock/barchart_labels.mock.json'
|
|
5
5
|
import barChartSuppressed from './_mock/bar-chart-suppressed.json'
|
|
6
|
+
import simplifiedLine from './_mock/simplified_line.json'
|
|
7
|
+
import scatterPlot from './_mock/scatterplot_mock.json'
|
|
8
|
+
import gradientLegend from './_mock/legend.gradient_mock.json'
|
|
9
|
+
import patternLegend from './_mock/stacked-pattern-test.json'
|
|
6
10
|
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
7
11
|
const meta: Meta<typeof Chart> = {
|
|
8
12
|
title: 'Components/Templates/Chart/Tooltip',
|
|
@@ -68,4 +72,68 @@ export const Additional_Tooltip: Story = {
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
75
|
+
export const Line_Tooltip_Markers: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
config: {
|
|
78
|
+
...simplifiedLine,
|
|
79
|
+
title: 'Line Tooltip Markers'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
play: async ({ canvasElement }) => {
|
|
83
|
+
await assertVisualizationRendered(canvasElement)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const Scatter_Tooltip_Markers: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
config: {
|
|
90
|
+
...scatterPlot,
|
|
91
|
+
title: 'Scatter Tooltip Markers'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
play: async ({ canvasElement }) => {
|
|
95
|
+
await assertVisualizationRendered(canvasElement)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const No_Markers_When_Legend_Hidden: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
config: {
|
|
102
|
+
...simplifiedLine,
|
|
103
|
+
title: 'Tooltip Without Markers When Legend Hidden',
|
|
104
|
+
legend: {
|
|
105
|
+
...simplifiedLine.legend,
|
|
106
|
+
hide: true
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
play: async ({ canvasElement }) => {
|
|
111
|
+
await assertVisualizationRendered(canvasElement)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const No_Markers_For_Gradient_Legend: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
config: {
|
|
118
|
+
...gradientLegend,
|
|
119
|
+
title: 'Tooltip Without Markers For Gradient Legend'
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
play: async ({ canvasElement }) => {
|
|
123
|
+
await assertVisualizationRendered(canvasElement)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const Pattern_Legend_Uses_Color_Markers: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
config: {
|
|
130
|
+
...patternLegend,
|
|
131
|
+
title: 'Tooltip Color Markers For Pattern Legend'
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
play: async ({ canvasElement }) => {
|
|
135
|
+
await assertVisualizationRendered(canvasElement)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
71
139
|
export default meta
|