@cdc/core 4.26.1 → 4.26.3
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/.claude/agents/qa-test-developer.md +126 -0
- package/CLAUDE.local.md +67 -0
- package/LICENSE +201 -0
- package/_stories/Gallery.Charts.stories.tsx +35 -42
- package/_stories/Gallery.DataBite.stories.tsx +15 -8
- package/_stories/Gallery.Maps.stories.tsx +37 -28
- package/_stories/Gallery.WaffleChart.stories.tsx +1 -1
- package/_stories/PageART.stories.tsx +5 -4
- package/_stories/PageBRFSS.stories.tsx +21 -16
- package/_stories/PageCancerRegistries.stories.tsx +15 -15
- package/_stories/PageEasternEquineEncephalitis.stories.tsx +33 -19
- package/_stories/PageExcessiveAlcoholUse.stories.tsx +148 -143
- package/_stories/PageMaternalMortality.stories.tsx +5 -4
- package/_stories/PageOralHealth.stories.tsx +15 -10
- package/_stories/PageRespiratory.stories.tsx +4 -4
- package/_stories/PageSmokingTobacco.stories.tsx +15 -10
- package/_stories/PageStateDiabetesProfiles.stories.tsx +15 -10
- package/_stories/PageWastewater.stories.tsx +44 -30
- package/_stories/VegaImport.stories.tsx +401 -0
- package/_stories/vega-fixtures/bars-with-line.json +444 -0
- package/_stories/vega-fixtures/bars.json +58 -0
- package/_stories/vega-fixtures/combo-bar-rolling-mean.json +88 -0
- package/_stories/vega-fixtures/combo.json +68 -0
- package/_stories/vega-fixtures/grouped-horizontal-bars.json +83 -0
- package/_stories/vega-fixtures/grouped-horizontal-bars2.json +231 -0
- package/_stories/vega-fixtures/horizontal-bar.json +427 -0
- package/_stories/vega-fixtures/horizontal-bars-with-bad-colors.json +197 -0
- package/_stories/vega-fixtures/horizontal-bars2.json +58 -0
- package/_stories/vega-fixtures/lines.json +227 -0
- package/_stories/vega-fixtures/measles-bars.json +348 -0
- package/_stories/vega-fixtures/measles-map.json +11101 -0
- package/_stories/vega-fixtures/measles-stacked-bars.json +2147 -0
- package/_stories/vega-fixtures/multi-dataset.json +255 -0
- package/_stories/vega-fixtures/no-data.json +14 -0
- package/_stories/vega-fixtures/pie-chart.json +94 -0
- package/_stories/vega-fixtures/repeat-spec.json +47 -0
- package/_stories/vega-fixtures/stacked-area.json +222 -0
- package/_stories/vega-fixtures/stacked-bar-with-rect.json +3412 -0
- package/_stories/vega-fixtures/stacked-bars-with-line.json +364 -0
- package/_stories/vega-fixtures/stacked-bars.json +212 -0
- package/_stories/vega-fixtures/stacked-horizontal-bars.json +140 -0
- package/_stories/vega-fixtures/warning-combo.json +59 -0
- package/_stories/vega-fixtures/warning-scatter-and-line.json +1182 -0
- package/assets/callout-flag.svg +7 -0
- package/assets/icon-chart-area.svg +1 -0
- package/assets/icon-chart-radar.svg +23 -0
- package/assets/logo2.svg +31 -0
- package/components/AdvancedEditor/EmbedEditor.tsx +270 -38
- package/components/Alert/components/Alert.styles.css +2 -2
- package/components/ComboBox/combobox.styles.css +48 -48
- package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
- package/components/CustomColorsEditor/CustomColorsEditor.tsx +3 -10
- package/components/DataTable/DataTable.tsx +46 -18
- package/components/DataTable/DataTableStandAlone.tsx +1 -0
- package/components/DataTable/components/ChartHeader.tsx +21 -12
- package/components/DataTable/components/MapHeader.tsx +34 -28
- package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
- package/components/DataTable/data-table.css +50 -52
- package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
- package/components/DataTable/helpers/getChartCellValue.ts +10 -7
- package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
- package/components/DataTable/helpers/getSeriesName.ts +6 -0
- package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
- package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
- package/components/DownloadButton.tsx +14 -6
- package/components/EditorPanel/ColumnsEditor.tsx +38 -31
- package/components/EditorPanel/CustomSortOrder.tsx +94 -0
- package/components/EditorPanel/DataTableEditor.tsx +139 -23
- package/components/EditorPanel/EditorPanel.styles.css +71 -71
- package/components/EditorPanel/EditorPanel.tsx +3 -8
- package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
- package/components/EditorPanel/FootnotesEditor.tsx +2 -2
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +21 -12
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -10
- package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +33 -29
- package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
- package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
- package/{styles/v2/components → components/EditorPanel}/editor.scss +76 -22
- package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
- package/components/EditorPanel/sections/VisualSection.tsx +11 -0
- package/components/EditorWrapper/editor-wrapper.style.css +1 -1
- package/components/Filters/Filters.tsx +3 -5
- package/components/Filters/components/Tabs.tsx +19 -7
- package/{styles → components/Filters}/filters.scss +3 -3
- package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
- package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
- package/components/Layout/components/Responsive.tsx +14 -6
- package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +14 -20
- package/components/Layout/components/Visualization/index.tsx +50 -38
- package/components/Layout/components/Visualization/visualizations.scss +232 -15
- package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
- package/components/Layout/components/VisualizationContainer.tsx +37 -0
- package/components/Layout/components/VisualizationContent.test.tsx +182 -0
- package/components/Layout/components/VisualizationContent.tsx +75 -0
- package/components/Layout/index.tsx +5 -5
- package/components/Layout/styles/editor-utils.scss +3 -3
- package/components/Layout/styles/editor.scss +4 -4
- package/components/Legend/Legend.Gradient.tsx +7 -1
- package/components/Loader/loader.styles.css +2 -2
- package/components/Loading.jsx +1 -1
- package/components/MediaControls.tsx +10 -3
- package/components/MultiSelect/multiselect.styles.css +19 -19
- package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
- package/components/PaletteSelector/PaletteSelector.css +15 -15
- package/components/RichTooltip/richTooltip.css +6 -6
- package/components/Table/table.styles.css +2 -2
- package/components/Waiting.tsx +1 -1
- package/components/_stories/CustomColorsEditor.stories.tsx +37 -0
- package/components/_stories/DataTable.stories.tsx +1 -0
- package/components/_stories/Filters.stories.tsx +1 -1
- package/components/_stories/styles.scss +0 -1
- package/components/elements/Button.jsx +1 -1
- package/components/elements/Card.jsx +1 -1
- package/{styles/v2/components → components/elements}/button.scss +9 -8
- package/components/inputs/InputCheckbox.jsx +1 -1
- package/components/inputs/InputSelect.tsx +1 -1
- package/components/inputs/InputText.jsx +1 -1
- package/components/inputs/InputToggle.tsx +1 -1
- package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
- package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
- package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
- package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
- package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
- package/{styles → components}/loading.scss +1 -1
- package/components/managers/DataDesigner.tsx +1 -1
- package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
- package/components/ui/Accordion.jsx +1 -1
- package/components/ui/Icon.tsx +1 -1
- package/components/ui/LoadSpin.jsx +1 -1
- package/components/ui/Modal.jsx +1 -1
- package/components/ui/Overlay.jsx +1 -1
- package/components/ui/Title/index.test.tsx +34 -0
- package/components/ui/Title/index.tsx +24 -7
- package/components/ui/Title/title.styles.css +119 -25
- package/components/ui/Tooltip.tsx +1 -1
- package/components/ui/_stories/Title.stories.tsx +1 -1
- package/{styles/v2/components → components/ui}/accordion.scss +3 -3
- package/components/ui/accordion.styles.css +11 -11
- package/{styles/v2/components → components/ui}/modal.scss +2 -2
- package/{styles/v2/components → components/ui}/overlay.scss +6 -6
- package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
- package/{styles → components}/waiting.scss +9 -3
- package/data/colorPalettes.ts +18 -5
- package/data/mapColorPalettes.ts +10 -0
- package/devTemplate/dev.js +285 -0
- package/devTemplate/index.html +30 -0
- package/devTemplate/preview.html +1503 -0
- package/devTemplate/sidebar.css +151 -0
- package/dist/cove-main.css +2530 -3901
- package/dist/cove-main.css.map +1 -1
- package/generateViteConfig.js +111 -2
- package/helpers/DataTransform.ts +1 -5
- package/helpers/backfillDefaults.ts +35 -0
- package/helpers/constants.ts +12 -0
- package/helpers/cove/date.ts +64 -3
- package/helpers/cove/number.ts +29 -15
- package/helpers/cove/string.ts +29 -0
- package/helpers/coveUpdateWorker.ts +14 -8
- package/helpers/displayDataAsText.ts +1 -1
- package/helpers/embed/embedCodeGenerator.ts +80 -0
- package/helpers/embed/embedHelper.js +169 -0
- package/helpers/embed/filterUtils.ts +121 -0
- package/helpers/embed/index.ts +17 -0
- package/helpers/embed/urlValidation.ts +119 -0
- package/helpers/extractDataAndMetadata.ts +20 -0
- package/helpers/fetchRemoteData.ts +14 -8
- package/helpers/filterVizData.ts +6 -1
- package/helpers/getFileExtension.ts +0 -6
- package/helpers/labelHash.ts +9 -0
- package/helpers/markupProcessor.ts +56 -38
- package/helpers/metrics/types.ts +3 -0
- package/helpers/palettes/colorDistributions.ts +1 -1
- package/helpers/palettes/utils.ts +12 -12
- package/helpers/parseCsvWithQuotes.ts +15 -14
- package/helpers/prepareScreenshot.ts +33 -10
- package/helpers/testing.ts +44 -0
- package/helpers/tests/DataTransform.test.ts +125 -0
- package/helpers/tests/abbreviateNumber.test.ts +59 -0
- package/helpers/tests/backfillDefaults.test.ts +253 -0
- package/helpers/tests/date.test.ts +110 -0
- package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
- package/helpers/tests/markupProcessor.test.ts +315 -124
- package/helpers/tests/number.test.ts +42 -0
- package/helpers/tests/prepareScreenshot.test.ts +28 -28
- package/helpers/tests/testStandaloneBuild.ts +36 -26
- package/helpers/tests/useDataVizClasses.test.ts +66 -0
- package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
- package/helpers/useDataVizClasses.ts +13 -7
- package/helpers/vegaConfig.ts +1 -1
- package/helpers/vegaConfigImport.ts +160 -0
- package/helpers/ver/4.24.4.ts +24 -0
- package/helpers/ver/4.26.1.ts +1 -1
- package/helpers/ver/4.26.2.ts +84 -0
- package/helpers/ver/4.26.3.ts +44 -0
- package/helpers/ver/4.26.4.ts +31 -0
- package/helpers/ver/tests/4.26.1.test.ts +105 -0
- package/helpers/ver/tests/4.26.2.test.ts +298 -0
- package/helpers/ver/tests/4.26.3.test.ts +168 -0
- package/helpers/ver/tests/4.26.4.test.ts +88 -0
- package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
- package/helpers/viewports.ts +2 -0
- package/package.json +27 -32
- package/styles/_global.scss +7 -7
- package/styles/_reset.scss +2 -2
- package/styles/{v2/base → base}/_file-selector.scss +4 -4
- package/styles/{v2/base → base}/_general.scss +2 -4
- package/styles/{v2/base → base}/index.scss +1 -1
- package/styles/base.scss +107 -165
- package/styles/cove-main.scss +3 -6
- package/styles/layout/_component.scss +110 -0
- package/styles/{v2/layout → layout}/_data-table.scss +7 -7
- package/styles/layout/_wrapper-padding.scss +27 -0
- package/styles/{v2/main.scss → main.scss} +3 -1
- package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
- package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
- package/styles/{v2/utils → utils}/_grid.scss +8 -3
- package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
- package/styles/{v2/utils → utils}/index.scss +2 -1
- package/types/Annotation.ts +10 -11
- package/types/Axis.ts +2 -0
- package/types/ComponentStyles.ts +1 -0
- package/types/ConfigureData.ts +1 -0
- package/types/General.ts +2 -0
- package/types/MarkupInclude.ts +1 -0
- package/types/MarkupVariable.ts +2 -1
- package/types/Palette.ts +22 -0
- package/types/Table.ts +9 -0
- package/types/Visualization.ts +7 -0
- package/_stories/StoryRenderingTests.stories.tsx +0 -164
- package/helpers/embedCodeGenerator.ts +0 -109
- package/styles/_common-components.css +0 -73
- package/styles/_variables.scss +0 -63
- package/styles/v2/layout/_component.scss +0 -21
- package/styles/v2/utils/_variables.scss +0 -9
- package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
- /package/{styles/v2/components → components/ui}/icon.scss +0 -0
- /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
- /package/styles/{v2/base → base}/_heading.scss +0 -0
- /package/styles/{v2/base → base}/_reset.scss +0 -0
- /package/styles/{v2/layout → layout}/_alert.scss +0 -0
- /package/styles/{v2/layout → layout}/_progression.scss +0 -0
- /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
- /package/styles/{v2/layout → layout}/index.scss +0 -0
- /package/styles/{v2/themes → themes}/index.scss +0 -0
- /package/styles/{v2/utils → utils}/_align.scss +0 -0
- /package/styles/{v2/utils → utils}/_animations.scss +0 -0
- /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
- /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { formatNumber } from '../cove/number'
|
|
3
|
+
|
|
4
|
+
const baseConfig = {
|
|
5
|
+
locale: 'en-US',
|
|
6
|
+
visualizationType: 'Bar',
|
|
7
|
+
yAxis: {},
|
|
8
|
+
dataFormat: {
|
|
9
|
+
abbreviated: false,
|
|
10
|
+
bottomAbbreviated: false,
|
|
11
|
+
bottomPrefix: '',
|
|
12
|
+
bottomRoundTo: 0,
|
|
13
|
+
bottomSuffix: '',
|
|
14
|
+
bottomComas: false,
|
|
15
|
+
commas: true,
|
|
16
|
+
prefix: '$',
|
|
17
|
+
preserveOriginalDecimals: false,
|
|
18
|
+
rightPrefix: 'R$',
|
|
19
|
+
rightRoundTo: 2,
|
|
20
|
+
rightSuffix: '%',
|
|
21
|
+
rightCommas: true,
|
|
22
|
+
roundTo: 1,
|
|
23
|
+
suffix: ''
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('formatNumber', () => {
|
|
28
|
+
it('applies explicit right-axis column formatting overrides', () => {
|
|
29
|
+
expect(
|
|
30
|
+
formatNumber(1234.5, 'right', false, baseConfig as any, {
|
|
31
|
+
addColPrefix: '',
|
|
32
|
+
addColSuffix: ' units',
|
|
33
|
+
addColRoundTo: 0,
|
|
34
|
+
addColCommas: false
|
|
35
|
+
})
|
|
36
|
+
).toBe('1235 units')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('falls back to configured right-axis formatting when no override is present', () => {
|
|
40
|
+
expect(formatNumber(1234.5, 'right', false, baseConfig as any)).toBe('R$1,234.50%')
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -21,7 +21,7 @@ describe('prepareClonedElements', () => {
|
|
|
21
21
|
<div class="dfe-section">
|
|
22
22
|
<h2>Title</h2>
|
|
23
23
|
<p>Text</p>
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="cove-visualization" data-download-id="viz1">Viz Content</div>
|
|
25
25
|
</div>
|
|
26
26
|
`)
|
|
27
27
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -39,7 +39,7 @@ describe('prepareClonedElements', () => {
|
|
|
39
39
|
<div class="dfe-section">
|
|
40
40
|
<h2>Perfect Title</h2>
|
|
41
41
|
<p>Perfect description</p>
|
|
42
|
-
<div class="
|
|
42
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
43
43
|
</div>
|
|
44
44
|
`)
|
|
45
45
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -59,7 +59,7 @@ describe('prepareClonedElements', () => {
|
|
|
59
59
|
<div class="dfe-section">
|
|
60
60
|
<h2>Emergency Department Visits</h2>
|
|
61
61
|
<p>Weekly percent of total visits.</p>
|
|
62
|
-
<div class="
|
|
62
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
63
63
|
</div>
|
|
64
64
|
`)
|
|
65
65
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -78,7 +78,7 @@ describe('prepareClonedElements', () => {
|
|
|
78
78
|
<div class="dfe-section">
|
|
79
79
|
<h3>Emergency Department Visits by Age</h3>
|
|
80
80
|
<p>Weekly percent of total visits.</p>
|
|
81
|
-
<div class="
|
|
81
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
82
82
|
</div>
|
|
83
83
|
`)
|
|
84
84
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -98,7 +98,7 @@ describe('prepareClonedElements', () => {
|
|
|
98
98
|
<h2>By Age</h2>
|
|
99
99
|
<h3>Emergency Department Visits by Age</h3>
|
|
100
100
|
<p>Weekly percent of total visits.</p>
|
|
101
|
-
<div class="
|
|
101
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
102
102
|
</div>
|
|
103
103
|
`)
|
|
104
104
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -118,7 +118,7 @@ describe('prepareClonedElements', () => {
|
|
|
118
118
|
<p>First paragraph.</p>
|
|
119
119
|
<p>Second paragraph.</p>
|
|
120
120
|
<p>Third paragraph.</p>
|
|
121
|
-
<div class="
|
|
121
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
122
122
|
</div>
|
|
123
123
|
`)
|
|
124
124
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -138,8 +138,8 @@ describe('prepareClonedElements', () => {
|
|
|
138
138
|
const container = createDOM(`
|
|
139
139
|
<div class="dfe-section">
|
|
140
140
|
<h2>Title</h2>
|
|
141
|
-
<div class="
|
|
142
|
-
<div class="
|
|
141
|
+
<div class="cove-visualization" data-download-id="viz1">Chart 1</div>
|
|
142
|
+
<div class="cove-visualization" data-download-id="viz2">Chart 2</div>
|
|
143
143
|
</div>
|
|
144
144
|
`)
|
|
145
145
|
const viz2 = container.querySelector('[data-download-id="viz2"]') as HTMLElement
|
|
@@ -154,9 +154,9 @@ describe('prepareClonedElements', () => {
|
|
|
154
154
|
const container = createDOM(`
|
|
155
155
|
<div class="dfe-section">
|
|
156
156
|
<h2>First Chart</h2>
|
|
157
|
-
<div class="
|
|
157
|
+
<div class="cove-visualization" data-download-id="viz1">Chart 1</div>
|
|
158
158
|
<h2>Second Chart</h2>
|
|
159
|
-
<div class="
|
|
159
|
+
<div class="cove-visualization" data-download-id="viz2">Chart 2</div>
|
|
160
160
|
</div>
|
|
161
161
|
`)
|
|
162
162
|
const viz2 = container.querySelector('[data-download-id="viz2"]') as HTMLElement
|
|
@@ -172,11 +172,11 @@ describe('prepareClonedElements', () => {
|
|
|
172
172
|
it('should ignore heading inside another viz', () => {
|
|
173
173
|
const container = createDOM(`
|
|
174
174
|
<div class="dfe-section">
|
|
175
|
-
<div class="
|
|
175
|
+
<div class="cove-visualization" data-download-id="viz1">
|
|
176
176
|
<h2>Title Inside Viz1</h2>
|
|
177
177
|
<div>Chart 1</div>
|
|
178
178
|
</div>
|
|
179
|
-
<div class="
|
|
179
|
+
<div class="cove-visualization" data-download-id="viz2">Chart 2</div>
|
|
180
180
|
</div>
|
|
181
181
|
`)
|
|
182
182
|
const viz2 = container.querySelector('[data-download-id="viz2"]') as HTMLElement
|
|
@@ -191,9 +191,9 @@ describe('prepareClonedElements', () => {
|
|
|
191
191
|
const container = createDOM(`
|
|
192
192
|
<div class="dfe-section">
|
|
193
193
|
<h2>Title</h2>
|
|
194
|
-
<div class="
|
|
195
|
-
<div class="
|
|
196
|
-
<div class="
|
|
194
|
+
<div class="cove-visualization" data-download-id="viz1">Chart 1</div>
|
|
195
|
+
<div class="cove-visualization" data-download-id="viz2">Chart 2</div>
|
|
196
|
+
<div class="cove-visualization" data-download-id="viz3">Chart 3</div>
|
|
197
197
|
</div>
|
|
198
198
|
`)
|
|
199
199
|
const viz3 = container.querySelector('[data-download-id="viz3"]') as HTMLElement
|
|
@@ -207,11 +207,11 @@ describe('prepareClonedElements', () => {
|
|
|
207
207
|
it('should prefer H3 over H2 between two vizs', () => {
|
|
208
208
|
const container = createDOM(`
|
|
209
209
|
<div class="dfe-section">
|
|
210
|
-
<div class="
|
|
210
|
+
<div class="cove-visualization" data-download-id="viz1">Chart 1</div>
|
|
211
211
|
<h2>Section</h2>
|
|
212
212
|
<h3>Subsection</h3>
|
|
213
213
|
<p>Description</p>
|
|
214
|
-
<div class="
|
|
214
|
+
<div class="cove-visualization" data-download-id="viz2">Chart 2</div>
|
|
215
215
|
</div>
|
|
216
216
|
`)
|
|
217
217
|
const viz2 = container.querySelector('[data-download-id="viz2"]') as HTMLElement
|
|
@@ -231,10 +231,10 @@ describe('prepareClonedElements', () => {
|
|
|
231
231
|
<div class="dfe-section">
|
|
232
232
|
<h2>Previous Section</h2>
|
|
233
233
|
<p>Previous content</p>
|
|
234
|
-
<div class="
|
|
234
|
+
<div class="cove-visualization" data-download-id="viz0">Other viz</div>
|
|
235
235
|
</div>
|
|
236
236
|
<div class="dfe-section">
|
|
237
|
-
<div class="
|
|
237
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
238
238
|
</div>
|
|
239
239
|
</div>
|
|
240
240
|
`)
|
|
@@ -253,7 +253,7 @@ describe('prepareClonedElements', () => {
|
|
|
253
253
|
<div class="some-container">
|
|
254
254
|
<h2>Title</h2>
|
|
255
255
|
<p>Description</p>
|
|
256
|
-
<div class="
|
|
256
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
257
257
|
</div>
|
|
258
258
|
`)
|
|
259
259
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -272,7 +272,7 @@ describe('prepareClonedElements', () => {
|
|
|
272
272
|
<div class="outer-container">
|
|
273
273
|
<h2>Title Above Section</h2>
|
|
274
274
|
<section>
|
|
275
|
-
<div class="
|
|
275
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
276
276
|
</section>
|
|
277
277
|
</div>
|
|
278
278
|
`)
|
|
@@ -289,7 +289,7 @@ describe('prepareClonedElements', () => {
|
|
|
289
289
|
<div class="dfe-section">
|
|
290
290
|
<h2>Outer Title</h2>
|
|
291
291
|
<section>
|
|
292
|
-
<div class="
|
|
292
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
293
293
|
</section>
|
|
294
294
|
</div>
|
|
295
295
|
`)
|
|
@@ -307,7 +307,7 @@ describe('prepareClonedElements', () => {
|
|
|
307
307
|
<section>
|
|
308
308
|
<h2>Section Title</h2>
|
|
309
309
|
<p>Description</p>
|
|
310
|
-
<div class="
|
|
310
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
311
311
|
</section>
|
|
312
312
|
`)
|
|
313
313
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -325,7 +325,7 @@ describe('prepareClonedElements', () => {
|
|
|
325
325
|
const container = createDOM(`
|
|
326
326
|
<div class="dfe-section">
|
|
327
327
|
<p>Some paragraph without heading</p>
|
|
328
|
-
<div class="
|
|
328
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
329
329
|
</div>
|
|
330
330
|
`)
|
|
331
331
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -341,7 +341,7 @@ describe('prepareClonedElements', () => {
|
|
|
341
341
|
const container = createDOM(`
|
|
342
342
|
<div class="dfe-section">
|
|
343
343
|
<h2>Title</h2>
|
|
344
|
-
<div class="
|
|
344
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
345
345
|
<p>Content after viz should not be included</p>
|
|
346
346
|
</div>
|
|
347
347
|
`)
|
|
@@ -358,7 +358,7 @@ describe('prepareClonedElements', () => {
|
|
|
358
358
|
const container = createDOM(`
|
|
359
359
|
<div class="dfe-section">
|
|
360
360
|
<h2>Direct H2 Child</h2>
|
|
361
|
-
<div class="
|
|
361
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
362
362
|
</div>
|
|
363
363
|
`)
|
|
364
364
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -375,7 +375,7 @@ describe('prepareClonedElements', () => {
|
|
|
375
375
|
<div class="heading-wrapper">
|
|
376
376
|
<h2>Nested H2</h2>
|
|
377
377
|
</div>
|
|
378
|
-
<div class="
|
|
378
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
379
379
|
</div>
|
|
380
380
|
`)
|
|
381
381
|
const viz = container.querySelector('[data-download-id="viz1"]') as HTMLElement
|
|
@@ -394,7 +394,7 @@ describe('prepareClonedElements', () => {
|
|
|
394
394
|
<p>Description</p>
|
|
395
395
|
<div class="outer-wrapper">
|
|
396
396
|
<div class="inner-wrapper">
|
|
397
|
-
<div class="
|
|
397
|
+
<div class="cove-visualization" data-download-id="viz1">Chart</div>
|
|
398
398
|
</div>
|
|
399
399
|
</div>
|
|
400
400
|
</div>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import {
|
|
4
|
-
import os from 'os'
|
|
5
|
-
import {
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { exec } from 'node:child_process'
|
|
4
|
+
import os from 'node:os'
|
|
5
|
+
import { promisify } from 'node:util'
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec)
|
|
6
8
|
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
-
const __dirname = path.dirname(__filename)
|
|
9
|
-
const packagesDir = path.join(__dirname, '..', 'packages')
|
|
10
9
|
|
|
11
10
|
function copyDirSync(src, dest) {
|
|
12
11
|
fs.mkdirSync(dest, { recursive: true })
|
|
@@ -23,39 +22,50 @@ function copyDirSync(src, dest) {
|
|
|
23
22
|
|
|
24
23
|
// Tests if a package can be built in isolation
|
|
25
24
|
// See DOCS/PACKAGE_DEPENDENCIES.md for more details
|
|
26
|
-
export function testStandaloneBuild(pkgDir) {
|
|
25
|
+
export async function testStandaloneBuild(pkgDir) {
|
|
27
26
|
pkgDir = pkgDir.replace('/src', '')
|
|
28
27
|
const pkgName = pkgDir.split('/')[pkgDir.split('/').length - 1]
|
|
29
28
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `cdc-open-viz-${pkgName}-`))
|
|
30
29
|
copyDirSync(pkgDir, tmpDir)
|
|
31
30
|
|
|
32
|
-
let
|
|
31
|
+
let tarballDir = null
|
|
33
32
|
|
|
34
33
|
try {
|
|
35
|
-
|
|
34
|
+
await execAsync('npm install --include=dev', { cwd: tmpDir })
|
|
36
35
|
|
|
37
|
-
// Pack
|
|
38
|
-
|
|
36
|
+
// Pack all local @cdc/* packages and install them to ensure version consistency.
|
|
37
|
+
// This prevents mismatches between npm-published packages and the current local source
|
|
38
|
+
// (e.g. a published @cdc/map referencing a path in @cdc/core that has since changed).
|
|
39
|
+
const packagesDir = path.join(pkgDir, '..')
|
|
39
40
|
const uniqueId = `${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
tarballDir = fs.mkdtempSync(path.join(os.tmpdir(), `cdc-pack-${uniqueId}-`))
|
|
42
|
+
|
|
43
|
+
const tarballs: string[] = []
|
|
44
|
+
for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
45
|
+
if (!entry.isDirectory()) continue
|
|
46
|
+
const localPkgDir = path.join(packagesDir, entry.name)
|
|
47
|
+
const pkgJsonPath = path.join(localPkgDir, 'package.json')
|
|
48
|
+
if (!fs.existsSync(pkgJsonPath)) continue
|
|
49
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
|
|
50
|
+
if (!pkgJson.name?.startsWith('@cdc/')) continue
|
|
51
|
+
const { stdout: packOutput } = await execAsync(`npm pack --pack-destination="${tarballDir}"`, {
|
|
52
|
+
cwd: localPkgDir
|
|
53
|
+
})
|
|
54
|
+
const tarballName = packOutput.trim().split('\n').pop()
|
|
55
|
+
tarballs.push(path.join(tarballDir, tarballName))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await execAsync(`npm install ${tarballs.map(t => `"${t}"`).join(' ')}`, { cwd: tmpDir })
|
|
59
|
+
|
|
60
|
+
await execAsync('npm run build', { cwd: tmpDir })
|
|
50
61
|
return true
|
|
51
62
|
} catch (err) {
|
|
52
63
|
console.error(`❌ Isolated build for ${pkgName} package failed`)
|
|
53
64
|
console.error(err.message)
|
|
54
65
|
return false
|
|
55
66
|
} finally {
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
fs.rmSync(uniqueTarballDir, { recursive: true, force: true })
|
|
67
|
+
if (tarballDir && fs.existsSync(tarballDir)) {
|
|
68
|
+
fs.rmSync(tarballDir, { recursive: true, force: true })
|
|
59
69
|
}
|
|
60
70
|
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
61
71
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import useDataVizClasses from '../useDataVizClasses'
|
|
3
|
+
|
|
4
|
+
describe('useDataVizClasses', () => {
|
|
5
|
+
it('maps supported visual settings to wrapper classes for non-chart visualizations', () => {
|
|
6
|
+
const { contentClasses } = useDataVizClasses({
|
|
7
|
+
type: 'data-bite',
|
|
8
|
+
showTitle: true,
|
|
9
|
+
title: 'Example',
|
|
10
|
+
visualizationType: 'Bar',
|
|
11
|
+
visual: {
|
|
12
|
+
border: false,
|
|
13
|
+
borderColorTheme: true,
|
|
14
|
+
accent: true,
|
|
15
|
+
background: true,
|
|
16
|
+
hideBackgroundColor: true,
|
|
17
|
+
tp5Treatment: true
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
expect(contentClasses).not.toContain('component--has-legacy-border')
|
|
22
|
+
expect(contentClasses).toContain('component--has-border-color-theme')
|
|
23
|
+
expect(contentClasses).toContain('component--has-accent')
|
|
24
|
+
expect(contentClasses).toContain('component--has-background')
|
|
25
|
+
expect(contentClasses).toContain('component--hide-background-color')
|
|
26
|
+
expect(contentClasses).toContain('component--tp5-treatment')
|
|
27
|
+
expect(contentClasses).not.toContain('component--tp5-treatment-background')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('does not add background wrapper classes for chart and map visualizations', () => {
|
|
31
|
+
const { contentClasses: chartClasses } = useDataVizClasses({
|
|
32
|
+
type: 'chart',
|
|
33
|
+
visualizationType: 'Bar',
|
|
34
|
+
visual: {
|
|
35
|
+
background: true,
|
|
36
|
+
hideBackgroundColor: true
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const { contentClasses: mapClasses } = useDataVizClasses({
|
|
41
|
+
type: 'map',
|
|
42
|
+
visual: {
|
|
43
|
+
background: true,
|
|
44
|
+
hideBackgroundColor: true
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
expect(chartClasses).not.toContain('component--has-background')
|
|
49
|
+
expect(chartClasses).not.toContain('component--hide-background-color')
|
|
50
|
+
expect(mapClasses).not.toContain('component--has-background')
|
|
51
|
+
expect(mapClasses).not.toContain('component--hide-background-color')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('adds an explicit class when the legacy border checkbox is enabled', () => {
|
|
55
|
+
const { contentClasses } = useDataVizClasses({
|
|
56
|
+
type: 'chart',
|
|
57
|
+
visualizationType: 'Bar',
|
|
58
|
+
visual: {
|
|
59
|
+
border: true
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(contentClasses).toContain('component--has-legacy-border')
|
|
64
|
+
expect(contentClasses).not.toContain('no-borders')
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
|
|
5
|
+
const repoRoot = path.resolve(__dirname, '../../../..')
|
|
6
|
+
|
|
7
|
+
const wrapperEntrypoints = [
|
|
8
|
+
'packages/chart/src/CdcChartComponent.tsx',
|
|
9
|
+
'packages/map/src/CdcMapComponent.tsx',
|
|
10
|
+
'packages/data-bite/src/CdcDataBite.tsx',
|
|
11
|
+
'packages/data-table/src/CdcDataTable.tsx',
|
|
12
|
+
'packages/filtered-text/src/CdcFilteredText.jsx',
|
|
13
|
+
'packages/waffle-chart/src/CdcWaffleChart.tsx',
|
|
14
|
+
'packages/markup-include/src/CdcMarkupInclude.tsx',
|
|
15
|
+
'packages/dashboard/src/CdcDashboardComponent.tsx'
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
const visualizationContentEntrypoints = [
|
|
19
|
+
'packages/data-table/src/CdcDataTable.tsx',
|
|
20
|
+
'packages/filtered-text/src/CdcFilteredText.jsx',
|
|
21
|
+
'packages/markup-include/src/CdcMarkupInclude.tsx',
|
|
22
|
+
'packages/data-bite/src/CdcDataBite.tsx',
|
|
23
|
+
'packages/waffle-chart/src/CdcWaffleChart.tsx',
|
|
24
|
+
'packages/chart/src/CdcChartComponent.tsx',
|
|
25
|
+
'packages/map/src/CdcMapComponent.tsx'
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
describe('visualization wrapper usage', () => {
|
|
29
|
+
it.each(wrapperEntrypoints)('%s uses the shared VisualizationContainer shell', filePath => {
|
|
30
|
+
const source = fs.readFileSync(path.join(repoRoot, filePath), 'utf8')
|
|
31
|
+
|
|
32
|
+
expect(source).toContain('VisualizationContainer')
|
|
33
|
+
expect(source).toContain('<VisualizationContainer')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it.each(visualizationContentEntrypoints)('%s uses the shared VisualizationContent inner shell', filePath => {
|
|
37
|
+
const source = fs.readFileSync(path.join(repoRoot, filePath), 'utf8')
|
|
38
|
+
|
|
39
|
+
expect(source).toContain('VisualizationContent')
|
|
40
|
+
expect(source).toContain('<VisualizationContent')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it.each(visualizationContentEntrypoints)('%s does not opt into legacy wrapper merge flags', filePath => {
|
|
44
|
+
const source = fs.readFileSync(path.join(repoRoot, filePath), 'utf8')
|
|
45
|
+
|
|
46
|
+
expect(source).not.toContain('mergeInnerBody')
|
|
47
|
+
expect(source).not.toContain('mergeBodyWrap')
|
|
48
|
+
expect(source).not.toContain('wrapBody=')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('packages/map/src/CdcMapComponent.tsx uses the shared VisualizationContent header slot for the title', () => {
|
|
52
|
+
const source = fs.readFileSync(path.join(repoRoot, 'packages/map/src/CdcMapComponent.tsx'), 'utf8')
|
|
53
|
+
|
|
54
|
+
expect(source).toContain('header={')
|
|
55
|
+
expect(source).toContain("<Title")
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -2,6 +2,7 @@ import { isBelowBreakpoint } from './viewports'
|
|
|
2
2
|
|
|
3
3
|
export default function useDataVizClasses(config, viewport = null) {
|
|
4
4
|
const {
|
|
5
|
+
type,
|
|
5
6
|
legend,
|
|
6
7
|
lineDatapointStyle,
|
|
7
8
|
showTitle,
|
|
@@ -23,8 +24,8 @@ export default function useDataVizClasses(config, viewport = null) {
|
|
|
23
24
|
lineDatapointClass = ' chart-line--always'
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
let innerContainerClasses = [
|
|
27
|
-
let contentClasses = [
|
|
27
|
+
let innerContainerClasses = []
|
|
28
|
+
let contentClasses = []
|
|
28
29
|
|
|
29
30
|
if (visualizationType === 'Spark Line' || visualizationType === 'chart') {
|
|
30
31
|
if (title && showTitle) contentClasses.push('component--has-title')
|
|
@@ -32,22 +33,27 @@ export default function useDataVizClasses(config, viewport = null) {
|
|
|
32
33
|
|
|
33
34
|
showTitle && contentClasses.push('component--has-title')
|
|
34
35
|
title &&
|
|
36
|
+
showTitle &&
|
|
35
37
|
visualizationType !== 'chart' &&
|
|
36
38
|
visualizationType !== 'Spark Line' &&
|
|
37
39
|
contentClasses.push('component--has-title')
|
|
38
40
|
subtext && innerContainerClasses.push('component--has-subtext')
|
|
39
41
|
biteStyle && innerContainerClasses.push(`bite__style--${biteStyle}`)
|
|
40
|
-
general?.isCompactStyle && innerContainerClasses.push(`component--
|
|
42
|
+
general?.isCompactStyle && innerContainerClasses.push(`component--is-compact-style`)
|
|
41
43
|
|
|
44
|
+
visual?.border && contentClasses.push('component--has-legacy-border')
|
|
42
45
|
!visual?.border && contentClasses.push('no-borders')
|
|
43
46
|
visualizationType === 'Spark Line' && contentClasses.push('sparkline')
|
|
44
|
-
visual?.borderColorTheme && contentClasses.push('component--has-
|
|
47
|
+
visual?.borderColorTheme && contentClasses.push('component--has-border-color-theme')
|
|
45
48
|
visual?.accent && contentClasses.push('component--has-accent')
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
if (type !== 'chart' && type !== 'map') {
|
|
50
|
+
visual?.background && contentClasses.push('component--has-background')
|
|
51
|
+
visual?.hideBackgroundColor && contentClasses.push('component--hide-background-color')
|
|
52
|
+
}
|
|
53
|
+
visual?.tp5Treatment && contentClasses.push('component--tp5-treatment')
|
|
48
54
|
|
|
49
55
|
// ! these two will be retired.
|
|
50
|
-
shadow && innerContainerClasses.push('shadow')
|
|
56
|
+
shadow && type !== 'data-bite' && innerContainerClasses.push('shadow')
|
|
51
57
|
config?.visual?.roundedBorders && innerContainerClasses.push('bite--has-rounded-borders')
|
|
52
58
|
|
|
53
59
|
let sparkLineStyles = {
|
package/helpers/vegaConfig.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
2
2
|
import { formatDate } from '@cdc/core/helpers/cove/date.js'
|
|
3
|
-
import
|
|
3
|
+
import _ from 'lodash'
|
|
4
4
|
import { compile as vegaLiteCompile } from 'vega-lite'
|
|
5
5
|
import { parse as vegaParse, View as vegaView } from 'vega'
|
|
6
6
|
|