@cdc/chart 4.26.4 → 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.
- package/CONFIG.md +353 -29
- package/LICENSE +201 -0
- package/dist/{cdcchart-DQ00cQCm.es.js → cdcchart-BWVIUcAF.es.js} +5 -55
- package/dist/cdcchart.js +37885 -35117
- package/examples/feature/heatmap/average-age-categorical.json +313 -0
- package/examples/feature/heatmap/calendar-heatmap.json +134 -0
- package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
- package/examples/feature/heatmap/sparse-aggregation.json +158 -0
- package/examples/private/bad-y-axis.json +543 -0
- package/examples/private/bar-chart-negative-value.json +455 -0
- package/examples/private/bump-chart.json +1116 -0
- package/examples/private/test-123.json +624 -0
- package/package.json +4 -3
- package/src/CdcChart.tsx +5 -4
- package/src/CdcChartComponent.tsx +166 -62
- package/src/_stories/Chart.AltText.stories.tsx +136 -0
- package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
- package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
- package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +183 -0
- package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +29 -0
- package/src/_stories/Chart.YAxisAutoMaxStrategy.stories.tsx +52 -0
- package/src/_stories/Chart.YAxisFilterDomain.stories.tsx +90 -0
- package/src/_stories/ChartAnnotation.smoke.stories.tsx +10 -0
- package/src/_stories/ChartAxisTitles.smoke.stories.tsx +13 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +63 -0
- package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
- package/src/_stories/ChartBrush.smoke.stories.tsx +13 -4
- package/src/_stories/_mock/alt_text_metadata.json +38 -0
- package/src/_stories/_mock/annotation_event-line_mock.json +1064 -0
- package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
- package/src/_stories/_mock/heatmap.json +156 -0
- package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
- package/src/_stories/_mock/sankey_mock.json +78 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +22 -1
- package/src/components/Annotations/components/AnnotationDraggable.tsx +220 -120
- package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
- package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +198 -0
- package/src/components/Axis/LeftAxis.tsx +17 -11
- package/src/components/Axis/LeftAxisGridlines.tsx +22 -12
- package/src/components/Axis/RightAxis.tsx +3 -0
- package/src/components/Axis/tests/AxisTickValues.test.tsx +86 -0
- package/src/components/Brush/BrushSelector.tsx +88 -187
- package/src/components/Brush/brushInteraction.ts +22 -0
- package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
- package/src/components/EditorPanel/EditorPanel.tsx +677 -185
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +114 -52
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +1 -0
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +5 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.test.tsx +98 -0
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +10 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.test.tsx +484 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -1
- package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +262 -0
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +32 -0
- package/src/components/EditorPanel/components/panels.scss +10 -0
- package/src/components/EditorPanel/editor-panel.scss +47 -0
- package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +61 -0
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +74 -0
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +315 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +77 -21
- package/src/components/HeatMap/components/HeatMap.tsx +767 -0
- package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +128 -0
- package/src/components/HeatMap/heatmap.constants.ts +13 -0
- package/src/components/HeatMap/heatmap.css +85 -0
- package/src/components/HeatMap/helpers/index.ts +261 -0
- package/src/components/HeatMap/index.tsx +5 -0
- package/src/components/HeatMap/tests/HeatMap.test.tsx +1072 -0
- package/src/components/HeatMap/tests/helpers.test.ts +167 -0
- package/src/components/Legend/Legend.Component.tsx +2 -2
- package/src/components/Legend/helpers/createFormatLabels.test.ts +88 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +6 -1
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +19 -22
- package/src/components/LineChart/helpers.test.ts +574 -0
- package/src/components/LineChart/helpers.ts +39 -16
- package/src/components/LineChart/index.tsx +28 -5
- package/src/components/LinearChart/tests/LinearChart.test.tsx +137 -33
- package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
- package/src/components/LinearChart.tsx +103 -47
- package/src/components/PieChart/PieChart.tsx +4 -1
- package/src/components/RadarChart/RadarChart.tsx +3 -1
- package/src/components/Regions/components/Regions.tsx +195 -41
- package/src/components/Sankey/components/Sankey.tsx +7 -3
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
- package/src/components/ScatterPlot/ScatterPlot.jsx +3 -3
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +33 -3
- package/src/components/SmallMultiples/SmallMultiples.css +12 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +50 -15
- package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
- package/src/components/SmallMultiples/tests/SmallMultiples.test.tsx +170 -0
- package/src/components/Sparkline/components/SparkLine.tsx +4 -9
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
- package/src/data/initial-state.js +12 -0
- package/src/helpers/axisLabelFontSize.ts +8 -0
- package/src/helpers/bumpChartTooltip.ts +54 -0
- package/src/helpers/calculateLeftYAxisWidth.ts +13 -3
- package/src/helpers/colorAssignmentHelpers.ts +102 -0
- package/src/helpers/dataHelpers.ts +11 -1
- package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
- package/src/helpers/getAxisMaxOverride.ts +16 -0
- package/src/helpers/getCleanTopTickMax.ts +64 -0
- package/src/helpers/getColorScale.ts +18 -9
- package/src/helpers/getHasBoundarySuppression.ts +26 -0
- package/src/helpers/getMinMax.ts +26 -25
- package/src/helpers/getYAxisDomainData.ts +38 -0
- package/src/helpers/getYAxisFinalizationEligibility.ts +40 -0
- package/src/helpers/handleChartAriaLabels.ts +23 -10
- package/src/helpers/hasSpacedInlineLabel.ts +10 -0
- package/src/helpers/seriesColumnSettings.ts +5 -4
- package/src/helpers/seriesHighlight.ts +26 -0
- package/src/helpers/smallMultiplesHelpers.ts +5 -0
- package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
- package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
- package/src/helpers/tests/calculateLeftYAxisWidth.test.ts +38 -0
- package/src/helpers/tests/dataHelpers.test.ts +135 -0
- package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -0
- package/src/helpers/tests/getAxisMaxOverride.test.ts +36 -0
- package/src/helpers/tests/getCleanTopTickMax.test.ts +165 -0
- package/src/helpers/tests/getColorScale.test.ts +197 -0
- package/src/helpers/tests/getMinMax.test.ts +211 -0
- package/src/helpers/tests/getYAxisDomainData.test.ts +119 -0
- package/src/helpers/tests/getYAxisFinalizationEligibility.test.ts +185 -0
- package/src/helpers/tests/handleChartAriaLabels.test.ts +71 -0
- package/src/helpers/tests/hasSpacedInlineLabel.test.ts +75 -0
- package/src/helpers/tests/seriesColumnSettings.test.ts +16 -2
- package/src/helpers/tests/seriesHighlight.test.ts +61 -0
- package/src/helpers/tests/smallMultiplesHelpers.test.ts +99 -0
- package/src/hooks/tests/useRightAxis.test.tsx +127 -0
- package/src/hooks/tests/useScales.test.tsx +383 -0
- package/src/hooks/useRightAxis.ts +30 -13
- package/src/hooks/useScales.ts +48 -21
- package/src/hooks/useTooltip.tsx +3 -1
- package/src/index.jsx +5 -2
- package/src/scss/main.scss +32 -49
- package/src/test/CdcChart.dataTable.test.tsx +122 -0
- package/src/types/ChartConfig.ts +24 -1
- package/src/types/ChartContext.ts +5 -0
- package/src/helpers/getYAxisAutoPadding.ts +0 -53
|
@@ -848,6 +848,61 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
848
848
|
}
|
|
849
849
|
)
|
|
850
850
|
|
|
851
|
+
const getAxisTitlePlacementVisualization = () => {
|
|
852
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
853
|
+
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
854
|
+
const sideLabelCandidates = svg ? Array.from(svg.querySelectorAll('text.y-label')) : []
|
|
855
|
+
const sideLabel = sideLabelCandidates.find(el => (el.textContent?.trim() || '').length > 0) || null
|
|
856
|
+
const topLabel = chartContainer?.querySelector('.y-axis-top-title')
|
|
857
|
+
|
|
858
|
+
return {
|
|
859
|
+
hasSideLabel: !!sideLabel,
|
|
860
|
+
sideLabelTransform: sideLabel?.getAttribute('transform') || '',
|
|
861
|
+
sideLabelText: sideLabel?.textContent?.trim() || '',
|
|
862
|
+
hasTopLabel: !!topLabel,
|
|
863
|
+
topLabelText: topLabel?.textContent?.trim() || ''
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const titlePlacementSelect = canvas.getByLabelText(/label placement/i) as HTMLSelectElement
|
|
868
|
+
|
|
869
|
+
expect(yAxisLabelInput.getAttribute('maxlength')).toBe('35')
|
|
870
|
+
|
|
871
|
+
await performAndAssert(
|
|
872
|
+
'Switch Title Placement to Top',
|
|
873
|
+
getAxisTitlePlacementVisualization,
|
|
874
|
+
async () => {
|
|
875
|
+
await userEvent.selectOptions(titlePlacementSelect, 'top')
|
|
876
|
+
},
|
|
877
|
+
(before, after) => {
|
|
878
|
+
expect(titlePlacementSelect.value).toBe('top')
|
|
879
|
+
expect(yAxisLabelInput.getAttribute('maxlength')).toBeNull()
|
|
880
|
+
expect(after.hasTopLabel).toBe(true)
|
|
881
|
+
expect(after.topLabelText).toBe('Y-Axis')
|
|
882
|
+
expect(after.hasSideLabel).toBe(false)
|
|
883
|
+
|
|
884
|
+
return true
|
|
885
|
+
}
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
await performAndAssert(
|
|
889
|
+
'Switch Title Placement back to Side',
|
|
890
|
+
getAxisTitlePlacementVisualization,
|
|
891
|
+
async () => {
|
|
892
|
+
await userEvent.selectOptions(titlePlacementSelect, 'side')
|
|
893
|
+
},
|
|
894
|
+
(before, after) => {
|
|
895
|
+
expect(titlePlacementSelect.value).toBe('side')
|
|
896
|
+
expect(yAxisLabelInput.getAttribute('maxlength')).toBe('35')
|
|
897
|
+
expect(after.hasTopLabel).toBe(false)
|
|
898
|
+
expect(after.hasSideLabel).toBe(true)
|
|
899
|
+
expect(after.sideLabelText).toBe('Y-Axis')
|
|
900
|
+
expect(after.sideLabelTransform).toContain('rotate(-90)')
|
|
901
|
+
|
|
902
|
+
return true
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
|
|
851
906
|
// ============================================================================
|
|
852
907
|
// TEST: Inline Label Field
|
|
853
908
|
// Tests visualization output changes when entering custom inline label text
|
|
@@ -1529,9 +1584,13 @@ export const BarRegionsSectionTests: Story = {
|
|
|
1529
1584
|
const textColors = canvas.queryAllByLabelText(/text color/i)
|
|
1530
1585
|
const backgrounds = canvas.queryAllByLabelText(/background/i)
|
|
1531
1586
|
const minRegionTypes = canvas.queryAllByLabelText(/minimum region type/i)
|
|
1587
|
+
const textColor = textColors[0] as HTMLInputElement | undefined
|
|
1588
|
+
const background = backgrounds[0] as HTMLInputElement | undefined
|
|
1532
1589
|
|
|
1533
1590
|
return {
|
|
1534
1591
|
regionCount: regionLabels.length,
|
|
1592
|
+
textColor: textColor?.placeholder,
|
|
1593
|
+
background: background?.placeholder,
|
|
1535
1594
|
hasFields:
|
|
1536
1595
|
regionLabels.length > 0 && textColors.length > 0 && backgrounds.length > 0 && minRegionTypes.length > 0
|
|
1537
1596
|
}
|
|
@@ -1544,6 +1603,10 @@ export const BarRegionsSectionTests: Story = {
|
|
|
1544
1603
|
// Should now have 1 region with all fields
|
|
1545
1604
|
expect(after.regionCount).toBe(1)
|
|
1546
1605
|
expect(after.hasFields).toBe(true)
|
|
1606
|
+
expect(after.textColor).toBe(
|
|
1607
|
+
getComputedStyle(document.documentElement).getPropertyValue('--cool-gray-90').trim()
|
|
1608
|
+
)
|
|
1609
|
+
expect(after.background).toBe('#71767a')
|
|
1547
1610
|
|
|
1548
1611
|
return true
|
|
1549
1612
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { expect, waitFor } from 'storybook/test'
|
|
3
|
+
import Chart from '../CdcChartComponent'
|
|
4
|
+
import brushEnabledConfig from './_mock/brush_enabled.json'
|
|
5
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Chart> = {
|
|
8
|
+
title: 'Components/Templates/Chart/BrushSlider/Interactions',
|
|
9
|
+
component: Chart
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof Chart>
|
|
15
|
+
|
|
16
|
+
type BrushSelectionState = {
|
|
17
|
+
x: string | null
|
|
18
|
+
width: string | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const getBrushSelection = (canvasElement: HTMLElement) => {
|
|
22
|
+
const selection = canvasElement.querySelector('.visx-brush-selection') as SVGRectElement | null
|
|
23
|
+
expect(selection).toBeTruthy()
|
|
24
|
+
return selection!
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const readBrushSelectionState = (canvasElement: HTMLElement): BrushSelectionState => {
|
|
28
|
+
const selection = getBrushSelection(canvasElement)
|
|
29
|
+
return {
|
|
30
|
+
x: selection.getAttribute('x'),
|
|
31
|
+
width: selection.getAttribute('width')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const getBrushPoint = (canvasElement: HTMLElement, x: number) => {
|
|
36
|
+
const overlay = canvasElement.querySelector('.visx-brush-overlay') as SVGRectElement | null
|
|
37
|
+
expect(overlay).toBeTruthy()
|
|
38
|
+
|
|
39
|
+
const rect = overlay!.getBoundingClientRect()
|
|
40
|
+
return {
|
|
41
|
+
target: overlay!,
|
|
42
|
+
clientX: rect.left + x,
|
|
43
|
+
clientY: rect.top + rect.height / 2
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const dispatchPointer = (target: Element, type: string, clientX: number, clientY: number, buttons = 1) => {
|
|
48
|
+
target.dispatchEvent(
|
|
49
|
+
new PointerEvent(type, {
|
|
50
|
+
bubbles: true,
|
|
51
|
+
cancelable: true,
|
|
52
|
+
clientX,
|
|
53
|
+
clientY,
|
|
54
|
+
buttons,
|
|
55
|
+
pointerId: 1,
|
|
56
|
+
pointerType: 'mouse'
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const settleInteraction = async () => {
|
|
62
|
+
await new Promise<void>(resolve => {
|
|
63
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const OutsideSelectionInteractionsBlocked: Story = {
|
|
68
|
+
args: {
|
|
69
|
+
config: brushEnabledConfig,
|
|
70
|
+
isEditor: false
|
|
71
|
+
},
|
|
72
|
+
parameters: {
|
|
73
|
+
docs: {
|
|
74
|
+
description: {
|
|
75
|
+
story: 'Outside-selection clicks and drags are blocked before they reach the visx brush overlay.'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
play: async ({ canvasElement }) => {
|
|
80
|
+
await assertVisualizationRendered(canvasElement)
|
|
81
|
+
|
|
82
|
+
await waitFor(() => {
|
|
83
|
+
expect(canvasElement.querySelector('.visx-brush-selection')).toBeTruthy()
|
|
84
|
+
expect(canvasElement.querySelector('.visx-brush-overlay')).toBeTruthy()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const initial = readBrushSelectionState(canvasElement)
|
|
88
|
+
const selectionX = Number(initial.x)
|
|
89
|
+
const selectionWidth = Number(initial.width)
|
|
90
|
+
|
|
91
|
+
const outsideLeft = getBrushPoint(canvasElement, Math.max(1, selectionX - 40))
|
|
92
|
+
dispatchPointer(outsideLeft.target, 'pointerdown', outsideLeft.clientX, outsideLeft.clientY)
|
|
93
|
+
dispatchPointer(outsideLeft.target, 'pointerup', outsideLeft.clientX, outsideLeft.clientY, 0)
|
|
94
|
+
outsideLeft.target.dispatchEvent(
|
|
95
|
+
new MouseEvent('click', {
|
|
96
|
+
bubbles: true,
|
|
97
|
+
cancelable: true,
|
|
98
|
+
clientX: outsideLeft.clientX,
|
|
99
|
+
clientY: outsideLeft.clientY
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
await settleInteraction()
|
|
103
|
+
expect(readBrushSelectionState(canvasElement)).toEqual(initial)
|
|
104
|
+
|
|
105
|
+
const outsideRight = getBrushPoint(canvasElement, selectionX + selectionWidth + 40)
|
|
106
|
+
dispatchPointer(outsideRight.target, 'pointerdown', outsideRight.clientX, outsideRight.clientY)
|
|
107
|
+
dispatchPointer(outsideRight.target, 'pointermove', outsideRight.clientX + 80, outsideRight.clientY)
|
|
108
|
+
dispatchPointer(outsideRight.target, 'pointerup', outsideRight.clientX + 80, outsideRight.clientY, 0)
|
|
109
|
+
await settleInteraction()
|
|
110
|
+
expect(readBrushSelectionState(canvasElement)).toEqual(initial)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import Chart from '../CdcChartComponent'
|
|
3
3
|
import brushEnabledConfig from './_mock/brush_enabled.json'
|
|
4
|
+
import pertussisConfig from './_mock/reported_pertussis_cases.json'
|
|
4
5
|
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
5
6
|
|
|
6
7
|
const meta: Meta<typeof Chart> = {
|
|
@@ -36,16 +37,24 @@ export const BrushSliderEnabled: Story = {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
export const
|
|
40
|
+
export const BrushDynamicYAxis: Story = {
|
|
40
41
|
args: {
|
|
41
|
-
config:
|
|
42
|
-
|
|
42
|
+
config: {
|
|
43
|
+
...pertussisConfig,
|
|
44
|
+
xAxis: {
|
|
45
|
+
...pertussisConfig.xAxis,
|
|
46
|
+
brushActive: true,
|
|
47
|
+
brushDynamicYAxis: true,
|
|
48
|
+
brushDefaultRecentDateCount: undefined
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
isEditor: false
|
|
43
52
|
},
|
|
44
53
|
parameters: {
|
|
45
54
|
docs: {
|
|
46
55
|
description: {
|
|
47
56
|
story:
|
|
48
|
-
'
|
|
57
|
+
'Reported pertussis cases (1922-2025) with Dynamic Y-Axis enabled. The Y-axis rescales to fit only the data visible in the current brush selection. Drag the brush to the 1970s-1990s low period (~1K-7K cases) then to the 1920s-1940s peak (~100K-265K cases) to see the Y-axis adjust dramatically.'
|
|
49
58
|
}
|
|
50
59
|
}
|
|
51
60
|
},
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "4.26.4",
|
|
3
|
+
"type": "chart",
|
|
4
|
+
"visualizationType": "Line",
|
|
5
|
+
"title": "COVID-19 Test Positivity",
|
|
6
|
+
"showTitle": true,
|
|
7
|
+
"animate": false,
|
|
8
|
+
"orientation": "vertical",
|
|
9
|
+
"dataMetadata": {
|
|
10
|
+
"altDescription": "Line chart showing COVID-19 test positivity rising from 4% to 12% over the last eight weeks, which may indicate growing community spread.",
|
|
11
|
+
"lastUpdated": "2024-09-21",
|
|
12
|
+
"source": "CDC NREVSS"
|
|
13
|
+
},
|
|
14
|
+
"data": [
|
|
15
|
+
{ "week": "Aug 3", "positivity": 4.2 },
|
|
16
|
+
{ "week": "Aug 10", "positivity": 4.8 },
|
|
17
|
+
{ "week": "Aug 17", "positivity": 5.5 },
|
|
18
|
+
{ "week": "Aug 24", "positivity": 6.3 },
|
|
19
|
+
{ "week": "Aug 31", "positivity": 7.4 },
|
|
20
|
+
{ "week": "Sep 7", "positivity": 8.9 },
|
|
21
|
+
{ "week": "Sep 14", "positivity": 10.6 },
|
|
22
|
+
{ "week": "Sep 21", "positivity": 12.0 }
|
|
23
|
+
],
|
|
24
|
+
"xAxis": {
|
|
25
|
+
"dataKey": "week",
|
|
26
|
+
"type": "categorical",
|
|
27
|
+
"label": "Week Ending"
|
|
28
|
+
},
|
|
29
|
+
"yAxis": {
|
|
30
|
+
"label": "% Positive",
|
|
31
|
+
"numTicks": "5"
|
|
32
|
+
},
|
|
33
|
+
"series": [
|
|
34
|
+
{ "dataKey": "positivity", "type": "Line", "axis": "Left", "tooltip": true }
|
|
35
|
+
],
|
|
36
|
+
"legend": { "hide": true },
|
|
37
|
+
"table": { "show": true, "label": "Data Table" }
|
|
38
|
+
}
|