@cdc/chart 4.26.4 → 4.26.5

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 (93) hide show
  1. package/CONFIG.md +349 -29
  2. package/LICENSE +201 -0
  3. package/dist/cdcchart.js +34247 -32805
  4. package/examples/feature/heatmap/average-age-categorical.json +313 -0
  5. package/examples/feature/heatmap/calendar-heatmap.json +134 -0
  6. package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
  7. package/examples/feature/heatmap/sparse-aggregation.json +158 -0
  8. package/examples/private/bump-chart.json +1116 -0
  9. package/examples/private/test-123.json +624 -0
  10. package/package.json +4 -3
  11. package/src/CdcChartComponent.tsx +135 -54
  12. package/src/_stories/Chart.AltText.stories.tsx +136 -0
  13. package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
  14. package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
  15. package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +29 -0
  16. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +13 -0
  17. package/src/_stories/ChartBar.Editor.stories.tsx +63 -0
  18. package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
  19. package/src/_stories/ChartBrush.smoke.stories.tsx +13 -4
  20. package/src/_stories/_mock/alt_text_metadata.json +38 -0
  21. package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
  22. package/src/_stories/_mock/heatmap.json +156 -0
  23. package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
  24. package/src/_stories/_mock/sankey_mock.json +78 -0
  25. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
  26. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
  27. package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
  28. package/src/components/Axis/LeftAxis.tsx +14 -11
  29. package/src/components/Axis/LeftAxisGridlines.tsx +14 -11
  30. package/src/components/Brush/BrushSelector.tsx +88 -187
  31. package/src/components/Brush/brushInteraction.ts +22 -0
  32. package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +310 -49
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +13 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
  36. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +1 -0
  37. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +5 -0
  38. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
  39. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -1
  40. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +57 -0
  41. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +56 -0
  42. package/src/components/EditorPanel/useEditorPermissions.test.tsx +70 -0
  43. package/src/components/EditorPanel/useEditorPermissions.ts +42 -22
  44. package/src/components/HeatMap/components/HeatMap.tsx +762 -0
  45. package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +114 -0
  46. package/src/components/HeatMap/heatmap.constants.ts +13 -0
  47. package/src/components/HeatMap/heatmap.css +71 -0
  48. package/src/components/HeatMap/helpers/index.ts +261 -0
  49. package/src/components/HeatMap/index.tsx +5 -0
  50. package/src/components/HeatMap/tests/HeatMap.test.tsx +1040 -0
  51. package/src/components/HeatMap/tests/helpers.test.ts +167 -0
  52. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +19 -22
  53. package/src/components/LineChart/helpers.test.ts +345 -0
  54. package/src/components/LineChart/helpers.ts +15 -5
  55. package/src/components/LineChart/index.tsx +7 -1
  56. package/src/components/LinearChart/tests/LinearChart.test.tsx +107 -33
  57. package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
  58. package/src/components/LinearChart.tsx +58 -37
  59. package/src/components/PieChart/PieChart.tsx +4 -1
  60. package/src/components/RadarChart/RadarChart.tsx +3 -1
  61. package/src/components/Regions/components/Regions.tsx +195 -41
  62. package/src/components/Sankey/components/Sankey.tsx +7 -3
  63. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
  64. package/src/components/SmallMultiples/SmallMultipleTile.tsx +30 -3
  65. package/src/components/SmallMultiples/SmallMultiples.css +12 -0
  66. package/src/components/SmallMultiples/SmallMultiples.tsx +15 -9
  67. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
  68. package/src/components/Sparkline/components/SparkLine.tsx +4 -9
  69. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
  70. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
  71. package/src/data/initial-state.js +11 -0
  72. package/src/helpers/axisLabelFontSize.ts +8 -0
  73. package/src/helpers/bumpChartTooltip.ts +54 -0
  74. package/src/helpers/calculateLeftYAxisWidth.ts +4 -0
  75. package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
  76. package/src/helpers/getMinMax.ts +2 -3
  77. package/src/helpers/getYAxisAutoPadding.ts +8 -6
  78. package/src/helpers/getYAxisAutoPaddingMode.ts +17 -0
  79. package/src/helpers/handleChartAriaLabels.ts +23 -10
  80. package/src/helpers/seriesHighlight.ts +26 -0
  81. package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
  82. package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
  83. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -0
  84. package/src/helpers/tests/getYAxisAutoPadding.test.ts +37 -0
  85. package/src/helpers/tests/getYAxisAutoPaddingMode.test.ts +90 -0
  86. package/src/helpers/tests/handleChartAriaLabels.test.ts +71 -0
  87. package/src/helpers/tests/seriesHighlight.test.ts +61 -0
  88. package/src/hooks/tests/useScales.test.tsx +105 -0
  89. package/src/hooks/useScales.ts +19 -9
  90. package/src/index.jsx +5 -2
  91. package/src/scss/main.scss +32 -49
  92. package/src/types/ChartConfig.ts +18 -0
  93. package/src/types/ChartContext.ts +2 -0
@@ -0,0 +1,90 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getYAxisAutoPaddingMode } from '../getYAxisAutoPaddingMode'
3
+ import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
4
+
5
+ describe('getYAxisAutoPaddingMode', () => {
6
+ it('returns inline-label for multi-word inline labels', () => {
7
+ expect(
8
+ getYAxisAutoPaddingMode(
9
+ createMockConfig({
10
+ yAxis: {
11
+ ...createMockConfig().yAxis,
12
+ titlePlacement: 'side',
13
+ inlineLabel: 'per 100k'
14
+ }
15
+ })
16
+ )
17
+ ).toBe('inline-label')
18
+ })
19
+
20
+ it('returns inline-label for inline labels with leading space', () => {
21
+ expect(
22
+ getYAxisAutoPaddingMode(
23
+ createMockConfig({
24
+ yAxis: {
25
+ ...createMockConfig().yAxis,
26
+ titlePlacement: 'side',
27
+ inlineLabel: ' cases'
28
+ }
29
+ })
30
+ )
31
+ ).toBe('inline-label')
32
+ })
33
+
34
+ it('returns top-title for top y-axis titles with label content', () => {
35
+ expect(
36
+ getYAxisAutoPaddingMode(
37
+ createMockConfig({
38
+ yAxis: {
39
+ ...createMockConfig().yAxis,
40
+ titlePlacement: 'top',
41
+ inlineLabel: ''
42
+ }
43
+ })
44
+ )
45
+ ).toBe('top-title')
46
+ })
47
+
48
+ it('returns none for hidden top y-axis titles', () => {
49
+ expect(
50
+ getYAxisAutoPaddingMode(
51
+ createMockConfig({
52
+ hideYAxisLabel: true,
53
+ yAxis: {
54
+ ...createMockConfig().yAxis,
55
+ titlePlacement: 'top',
56
+ inlineLabel: ''
57
+ }
58
+ })
59
+ )
60
+ ).toBe('none')
61
+ })
62
+
63
+ it('returns none when neither trigger applies', () => {
64
+ expect(
65
+ getYAxisAutoPaddingMode(
66
+ createMockConfig({
67
+ yAxis: {
68
+ ...createMockConfig().yAxis,
69
+ titlePlacement: 'side',
70
+ inlineLabel: 'Rate'
71
+ }
72
+ })
73
+ )
74
+ ).toBe('none')
75
+ })
76
+
77
+ it('returns inline-label when both triggers are present', () => {
78
+ expect(
79
+ getYAxisAutoPaddingMode(
80
+ createMockConfig({
81
+ yAxis: {
82
+ ...createMockConfig().yAxis,
83
+ titlePlacement: 'top',
84
+ inlineLabel: 'per 100k'
85
+ }
86
+ })
87
+ )
88
+ ).toBe('inline-label')
89
+ })
90
+ })
@@ -0,0 +1,71 @@
1
+ import { handleChartAriaLabels } from '../handleChartAriaLabels'
2
+
3
+ describe('handleChartAriaLabels', () => {
4
+ it('returns auto-generated title from visualizationType and title', () => {
5
+ const result = handleChartAriaLabels({
6
+ visualizationType: 'Line',
7
+ title: 'COVID-19 Cases'
8
+ })
9
+ expect(result).toBe('Line chart with the title: COVID-19 Cases')
10
+ })
11
+
12
+ it('returns visualizationType-only title when chart title is absent', () => {
13
+ const result = handleChartAriaLabels({
14
+ visualizationType: 'Bar'
15
+ })
16
+ expect(result).toBe('Bar chart')
17
+ })
18
+
19
+ it('returns fallback when visualizationType is missing', () => {
20
+ const result = handleChartAriaLabels({})
21
+ expect(result).toBe('Data visualization container')
22
+ })
23
+
24
+ describe('description', () => {
25
+ it('concatenates static description after title', () => {
26
+ const result = handleChartAriaLabels({
27
+ visualizationType: 'Line',
28
+ title: 'Trends',
29
+ altText: { type: 'static', value: 'Cases rose steadily over 8 weeks.' }
30
+ })
31
+ expect(result).toBe('Line chart with the title: Trends. Cases rose steadily over 8 weeks.')
32
+ })
33
+
34
+ it('concatenates description from metadata after title', () => {
35
+ const result = handleChartAriaLabels({
36
+ visualizationType: 'Line',
37
+ title: 'Trends',
38
+ altText: { type: 'metadata', metadataKey: 'altDescription' },
39
+ dataMetadata: { altDescription: 'Positivity increased from 4% to 12%.' }
40
+ })
41
+ expect(result).toBe('Line chart with the title: Trends. Positivity increased from 4% to 12%.')
42
+ })
43
+
44
+ it('returns title only when metadata key is missing', () => {
45
+ const result = handleChartAriaLabels({
46
+ visualizationType: 'Line',
47
+ title: 'Trends',
48
+ altText: { type: 'metadata', metadataKey: 'missing' },
49
+ dataMetadata: { other: 'value' }
50
+ })
51
+ expect(result).toBe('Line chart with the title: Trends')
52
+ })
53
+
54
+ it('returns title only when not configured', () => {
55
+ const result = handleChartAriaLabels({
56
+ visualizationType: 'Line',
57
+ title: 'Trends'
58
+ })
59
+ expect(result).toBe('Line chart with the title: Trends')
60
+ })
61
+
62
+ it('returns title only when altText is empty object', () => {
63
+ const result = handleChartAriaLabels({
64
+ visualizationType: 'Line',
65
+ title: 'Trends',
66
+ altText: {}
67
+ })
68
+ expect(result).toBe('Line chart with the title: Trends')
69
+ })
70
+ })
71
+ })
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getLegendHighlightKey, shouldResetSeriesHighlight } from '../seriesHighlight'
3
+
4
+ describe('seriesHighlight helpers', () => {
5
+ const seriesLabels = {
6
+ nonfatal_rate_alldrugs: 'All drugs',
7
+ opioid_nonfatal_rate: 'All opioids',
8
+ stimulant_nonfatal_rate: 'All stimulants'
9
+ }
10
+
11
+ it('normalizes a legend label to the stored series highlight key', () => {
12
+ expect(getLegendHighlightKey(seriesLabels, 'All opioids')).toBe('opioid_nonfatal_rate')
13
+ expect(getLegendHighlightKey(seriesLabels, 'unmapped_series')).toBe('unmapped_series')
14
+ })
15
+
16
+ it('accepts legend label objects and legacy raw string callers', () => {
17
+ expect(
18
+ getLegendHighlightKey(seriesLabels, {
19
+ datum: 'All stimulants',
20
+ index: 2,
21
+ text: 'All stimulants',
22
+ value: '#075290'
23
+ })
24
+ ).toBe('stimulant_nonfatal_rate')
25
+
26
+ expect(getLegendHighlightKey(seriesLabels, 'Highlighted bar')).toBe('Highlighted bar')
27
+ })
28
+
29
+ it('does not reset when removing one selected item from a three-series legend', () => {
30
+ expect(
31
+ shouldResetSeriesHighlight(
32
+ ['nonfatal_rate_alldrugs', 'opioid_nonfatal_rate'],
33
+ Object.keys(seriesLabels),
34
+ 'opioid_nonfatal_rate',
35
+ 'Line'
36
+ )
37
+ ).toBe(false)
38
+ })
39
+
40
+ it('resets when selecting the only remaining unselected item', () => {
41
+ expect(
42
+ shouldResetSeriesHighlight(
43
+ ['nonfatal_rate_alldrugs', 'opioid_nonfatal_rate'],
44
+ Object.keys(seriesLabels),
45
+ 'stimulant_nonfatal_rate',
46
+ 'Line'
47
+ )
48
+ ).toBe(true)
49
+ })
50
+
51
+ it('does not reset when the clicked item is not a known series key', () => {
52
+ expect(
53
+ shouldResetSeriesHighlight(
54
+ ['nonfatal_rate_alldrugs', 'opioid_nonfatal_rate'],
55
+ Object.keys(seriesLabels),
56
+ 'suppressed-data-note',
57
+ 'Line'
58
+ )
59
+ ).toBe(false)
60
+ })
61
+ })
@@ -0,0 +1,105 @@
1
+ import React from 'react'
2
+ import { renderHook } from '@testing-library/react'
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
4
+ import ConfigContext from '../../ConfigContext'
5
+ import useScales from '../useScales'
6
+ import { createMockChartContext, createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
7
+
8
+ const mockGetMinMax = vi.fn(() => ({
9
+ min: 0,
10
+ max: 100,
11
+ leftMax: 100,
12
+ rightMax: 0
13
+ }))
14
+
15
+ const mockGetYAxisAutoPadding = vi.fn(() => 10)
16
+
17
+ vi.mock('../../helpers/getMinMax', () => ({
18
+ default: (...args: any[]) => mockGetMinMax(...args)
19
+ }))
20
+
21
+ vi.mock('../../helpers/getYAxisAutoPadding', () => ({
22
+ getYAxisAutoPadding: (...args: any[]) => mockGetYAxisAutoPadding(...args)
23
+ }))
24
+
25
+ const config = createMockConfig({
26
+ runtime: {
27
+ ...createMockConfig().runtime,
28
+ yAxis: {
29
+ ...createMockConfig().runtime.yAxis,
30
+ numTicks: 5
31
+ }
32
+ } as any
33
+ })
34
+
35
+ const createWrapper = () => {
36
+ const context = createMockChartContext(config)
37
+
38
+ return ({ children }: React.PropsWithChildren) => (
39
+ <ConfigContext.Provider value={context}>{children}</ConfigContext.Provider>
40
+ )
41
+ }
42
+
43
+ const baseProps = {
44
+ config,
45
+ data: [],
46
+ tableData: [],
47
+ minValue: 0,
48
+ maxValue: 100,
49
+ existPositiveValue: true,
50
+ isAllLine: true,
51
+ xAxisDataMapped: [1, 2, 3],
52
+ xMax: 300,
53
+ yMax: 200,
54
+ currentViewport: 'lg' as const
55
+ }
56
+
57
+ describe('useScales', () => {
58
+ beforeEach(() => {
59
+ mockGetMinMax.mockClear()
60
+ mockGetYAxisAutoPadding.mockClear()
61
+ })
62
+
63
+ it('allows repeated padding passes for inline-label mode', () => {
64
+ renderHook(() => useScales({ ...baseProps, yAxisAutoPaddingMode: 'inline-label' }), {
65
+ wrapper: createWrapper()
66
+ })
67
+
68
+ expect(mockGetYAxisAutoPadding).toHaveBeenCalledTimes(3)
69
+ })
70
+
71
+ it('runs a single padding pass for top-title mode', () => {
72
+ renderHook(() => useScales({ ...baseProps, yAxisAutoPaddingMode: 'top-title' }), {
73
+ wrapper: createWrapper()
74
+ })
75
+
76
+ expect(mockGetYAxisAutoPadding).toHaveBeenCalledTimes(1)
77
+ })
78
+
79
+ it('ignores manual y-axis padding while calculating auto-padding', () => {
80
+ const configWithManualPadding = createMockConfig({
81
+ yAxis: {
82
+ ...createMockConfig().yAxis,
83
+ enablePadding: true,
84
+ scalePadding: 90
85
+ }
86
+ })
87
+
88
+ renderHook(() => useScales({ ...baseProps, config: configWithManualPadding, yAxisAutoPaddingMode: 'top-title' }), {
89
+ wrapper: createWrapper()
90
+ })
91
+
92
+ expect(mockGetMinMax.mock.calls[0][0].config.yAxis).toMatchObject({
93
+ enablePadding: false,
94
+ scalePadding: 0
95
+ })
96
+ expect(mockGetYAxisAutoPadding.mock.calls[0][4].yAxis).toMatchObject({
97
+ enablePadding: false,
98
+ scalePadding: 0
99
+ })
100
+ expect(mockGetMinMax.mock.calls[1][0].config.yAxis).toMatchObject({
101
+ enablePadding: true,
102
+ scalePadding: 10
103
+ })
104
+ })
105
+ })
@@ -15,6 +15,7 @@ import { ChartConfig } from '../types/ChartConfig'
15
15
  import { ChartContext } from '../types/ChartContext'
16
16
  import _ from 'lodash'
17
17
  import { getYAxisAutoPadding } from '../helpers/getYAxisAutoPadding'
18
+ import { YAxisAutoPaddingMode } from '../helpers/getYAxisAutoPaddingMode'
18
19
  import getMinMax from '../helpers/getMinMax'
19
20
  import { countNumOfTicks } from '../helpers/countNumOfTicks'
20
21
 
@@ -39,7 +40,7 @@ type useScaleProps = {
39
40
  xAxisDataMapped: Object[] // array of x axis date/category items
40
41
  xMax: number // chart svg width
41
42
  yMax: number // chart svg height
42
- needsYAxisAutoPadding?: boolean // whether Y-axis needs auto padding for label overflow
43
+ yAxisAutoPaddingMode?: YAxisAutoPaddingMode // why Y-axis auto padding should run
43
44
  currentViewport?: string // current viewport for tick calculation
44
45
  }
45
46
 
@@ -55,7 +56,7 @@ const useScales = (properties: useScaleProps) => {
55
56
  maxValue,
56
57
  existPositiveValue,
57
58
  isAllLine,
58
- needsYAxisAutoPadding,
59
+ yAxisAutoPaddingMode = 'none',
59
60
  currentViewport
60
61
  } = properties
61
62
 
@@ -65,9 +66,13 @@ const useScales = (properties: useScaleProps) => {
65
66
  const isHorizontal = config.orientation === 'horizontal'
66
67
  const { visualizationType, xAxis, forestPlot, runtime } = config
67
68
  const isForestPlot = visualizationType === 'Forest Plot'
69
+ const shouldApplyYAxisAutoPadding = yAxisAutoPaddingMode !== 'none' && !isHorizontal
70
+ const minMaxConfig = shouldApplyYAxisAutoPadding
71
+ ? { ...config, yAxis: { ...config.yAxis, enablePadding: false, scalePadding: 0 } }
72
+ : config
68
73
 
69
74
  const minMaxProps = {
70
- config,
75
+ config: minMaxConfig,
71
76
  minValue,
72
77
  maxValue,
73
78
  existPositiveValue,
@@ -91,12 +96,17 @@ const useScales = (properties: useScaleProps) => {
91
96
  const handleNumTicks = isForestPlot ? config.data.length : yTickCount
92
97
 
93
98
  // Apply auto-padding if needed
94
- if (needsYAxisAutoPadding && !isHorizontal) {
95
- for (let i = 0; i < 3; i++) {
96
- const scale = composeYScale({ min, max, yMax, config, leftMax })
97
- const padding = getYAxisAutoPadding(scale, handleNumTicks, maxValue, minValue, config)
98
- if (i === 0 || padding > 0) {
99
- const adjustedConfig = { ...config, yAxis: { ...config.yAxis, scalePadding: padding, enablePadding: true } }
99
+ if (shouldApplyYAxisAutoPadding) {
100
+ const maxPasses = yAxisAutoPaddingMode === 'inline-label' ? 3 : 1
101
+
102
+ for (let i = 0; i < maxPasses; i++) {
103
+ const scale = composeYScale({ min, max, yMax, config: minMaxConfig, leftMax })
104
+ const padding = getYAxisAutoPadding(scale, handleNumTicks, maxValue, minValue, minMaxConfig, yAxisAutoPaddingMode)
105
+ if (padding > 0 || (maxPasses > 1 && i === 0)) {
106
+ const adjustedConfig = {
107
+ ...minMaxConfig,
108
+ yAxis: { ...minMaxConfig.yAxis, scalePadding: padding, enablePadding: true }
109
+ }
100
110
  const result = getMinMax({ ...minMaxProps, config: adjustedConfig })
101
111
  min = result.min
102
112
  max = result.max
package/src/index.jsx CHANGED
@@ -9,12 +9,15 @@ let isEditor = window.location.href.includes('editor=true')
9
9
  let isDebug = window.location.href.includes('debug=true')
10
10
 
11
11
  let domContainer = document.getElementsByClassName('react-container')[0]
12
+ let configUrl = domContainer.dataset.configUrl
13
+ let injectedConfig = domContainer.coveConfig
12
14
 
13
15
  ReactDOM.createRoot(domContainer).render(
14
16
  <React.StrictMode>
15
17
  <CdcChart
16
- interactionLabel={domContainer.attributes['data-config']?.value}
17
- configUrl={domContainer.attributes['data-config'].value}
18
+ interactionLabel={configUrl}
19
+ config={injectedConfig}
20
+ configUrl={injectedConfig ? undefined : configUrl}
18
21
  isEditor={isEditor}
19
22
  isDebug={isDebug}
20
23
  />
@@ -1,5 +1,6 @@
1
1
  @import '@cdc/core/styles/utils/breakpoints';
2
2
  @import '@cdc/core/styles/layout/wrapper-padding';
3
+ @import '@cdc/core/styles/layout/callout';
3
4
 
4
5
  .form-container {
5
6
  overflow-y: auto;
@@ -24,33 +25,17 @@
24
25
  background: transparent;
25
26
 
26
27
  .cdc-callout {
27
- box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
28
- border: 1px solid #dff2f6;
29
- margin: 0;
30
- padding: 1.25rem;
31
- border-radius: 0.25rem;
32
- position: relative;
33
- background: transparent;
34
-
35
- .cdc-callout__flag {
36
- position: absolute;
37
- top: -0.36rem;
38
- right: 1.08rem;
39
- width: 1.84rem;
40
- height: auto;
41
- }
28
+ --cdc-callout-background: transparent;
42
29
 
43
30
  .cove-visualization__title,
44
31
  .cove-visualization__header {
45
32
  background: transparent;
46
33
  border: 0;
47
- color: var(--cool-gray-90, #1f2937);
48
34
  margin: 0 0 1rem;
49
35
  padding: 0;
50
36
  border-radius: 0;
51
37
 
52
38
  h2 {
53
- color: var(--cool-gray-90, #1f2937);
54
39
  font-family: var(--fonts-nunito, var(--app-font-secondary));
55
40
  font-size: 1.1rem;
56
41
  font-weight: 700;
@@ -64,11 +49,17 @@
64
49
  margin-bottom: 0 !important;
65
50
  }
66
51
 
52
+ .y-axis-top-title {
53
+ font-family: var(--fonts-nunito, var(--app-font-secondary));
54
+ font-weight: 700;
55
+ line-height: 1.4;
56
+ margin: 0 0 0.5rem;
57
+ }
58
+
67
59
  .legend-top {
68
60
  .legend-wrapper .tooltip-boundary {
69
61
  display: flex;
70
62
  flex-wrap: wrap;
71
- row-gap: var(--cove-visualization-section-gap, 1.5rem);
72
63
  order: 2;
73
64
 
74
65
  aside {
@@ -188,7 +179,7 @@
188
179
  white-space: nowrap;
189
180
  }
190
181
 
191
- .legend-item>.legend-item {
182
+ .legend-item > .legend-item {
192
183
  display: inline-block;
193
184
  flex: 0 0 auto;
194
185
  }
@@ -222,11 +213,11 @@
222
213
  left: 0%;
223
214
  top: 108%;
224
215
 
225
- &>* {
216
+ & > * {
226
217
  margin: 0;
227
218
  }
228
219
 
229
- &>p,
220
+ & > p,
230
221
  a {
231
222
  margin-left: 5px;
232
223
  }
@@ -235,7 +226,6 @@
235
226
  }
236
227
 
237
228
  .dynamic-legend-list {
238
-
239
229
  // overide traditional legend item that uses !important
240
230
  .legend-item {
241
231
  align-items: flex-end !important;
@@ -268,7 +258,7 @@
268
258
  font-size: 1em;
269
259
  vertical-align: middle;
270
260
 
271
- &>span {
261
+ & > span {
272
262
  display: flex;
273
263
  justify-items: center;
274
264
  align-items: center;
@@ -278,25 +268,25 @@
278
268
  max-height: 1px;
279
269
  }
280
270
 
281
- &>span[class*='Asterisk'] {
271
+ & > span[class*='Asterisk'] {
282
272
  transform: scale(1.8);
283
273
  margin-top: 15px;
284
274
  }
285
275
 
286
- &>span[class*='Dagger'] {
276
+ & > span[class*='Dagger'] {
287
277
  margin-top: 2px;
288
278
  }
289
279
 
290
- &>span[class*='Sign'] {
280
+ & > span[class*='Sign'] {
291
281
  margin-top: 1px;
292
282
  }
293
283
 
294
- &>svg {
284
+ & > svg {
295
285
  width: 50px;
296
286
  height: 23px;
297
287
  }
298
288
 
299
- &>p {
289
+ & > p {
300
290
  margin: 0;
301
291
  }
302
292
  }
@@ -325,8 +315,8 @@
325
315
  min-width: 0;
326
316
  max-width: 100%;
327
317
 
328
- >div,
329
- >aside {
318
+ > div,
319
+ > aside {
330
320
  min-width: 0;
331
321
  max-width: 100%;
332
322
  }
@@ -339,7 +329,7 @@
339
329
 
340
330
  &.legend-top:not(.legend-hidden) {
341
331
  .legend-wrapper {
342
- &>div {
332
+ & > div {
343
333
  order: 2;
344
334
  width: 100%;
345
335
  }
@@ -354,7 +344,7 @@
354
344
 
355
345
  &.legend-left:not(.legend-hidden) {
356
346
  .legend-wrapper {
357
- &>div {
347
+ & > div {
358
348
  order: 2;
359
349
  width: 75%;
360
350
  }
@@ -369,7 +359,7 @@
369
359
 
370
360
  &.legend-right:not(.legend-hidden) {
371
361
  .legend-wrapper {
372
- &>div {
362
+ & > div {
373
363
  order: 1;
374
364
  width: 75%;
375
365
  }
@@ -391,7 +381,7 @@
391
381
  flex-wrap: nowrap;
392
382
  flex-direction: column-reverse;
393
383
 
394
- &>div.tooltip-boundary {
384
+ & > div.tooltip-boundary {
395
385
  margin-top: 1.5em;
396
386
  }
397
387
  }
@@ -401,14 +391,10 @@
401
391
  flex-direction: column;
402
392
  }
403
393
 
404
- &.legend-hidden>svg {
394
+ &.legend-hidden > svg {
405
395
  width: 100% !important;
406
396
  }
407
397
 
408
- &.dashboard-brush {
409
- margin-bottom: 2.5em;
410
- }
411
-
412
398
  // Brush touch support
413
399
  .brush-overlay {
414
400
  touch-action: none;
@@ -434,7 +420,7 @@
434
420
  pointer-events: none;
435
421
  }
436
422
 
437
- >svg {
423
+ > svg {
438
424
  overflow: visible;
439
425
  font-size: 14px;
440
426
  margin: 1rem 0 2rem;
@@ -449,7 +435,7 @@
449
435
  }
450
436
 
451
437
  &.chart-line--hover {
452
- >svg circle {
438
+ > svg circle {
453
439
  opacity: 0;
454
440
 
455
441
  &:hover {
@@ -459,12 +445,12 @@
459
445
  }
460
446
 
461
447
  &.chart-line--always {
462
- >svg circle {
448
+ > svg circle {
463
449
  opacity: 1;
464
450
  }
465
451
 
466
452
  // Animations for line chart and data points
467
- >svg.animated {
453
+ > svg.animated {
468
454
  circle {
469
455
  opacity: 0;
470
456
  animation: revealLolly 0.25s linear forwards;
@@ -525,7 +511,7 @@
525
511
 
526
512
  @include breakpointClass(md) {
527
513
  .visualization-container {
528
- >svg {
514
+ > svg {
529
515
  font-size: 16px;
530
516
  width: 75%;
531
517
  order: 1;
@@ -608,7 +594,6 @@
608
594
  }
609
595
 
610
596
  &.animated {
611
-
612
597
  .vertical path,
613
598
  .vertical rect,
614
599
  .vertical foreignObject div {
@@ -626,7 +611,6 @@
626
611
  }
627
612
 
628
613
  &.animate {
629
-
630
614
  path,
631
615
  rect,
632
616
  foreignObject div {
@@ -685,7 +669,7 @@
685
669
  text-align: center;
686
670
  transform: scale(1);
687
671
 
688
- >.visx-group[style] {
672
+ > .visx-group[style] {
689
673
  transform: scale(1);
690
674
  }
691
675
  }
@@ -707,7 +691,7 @@
707
691
  }
708
692
 
709
693
  .cove-visualization.type-chart.type-sparkline {
710
- &.lg .visualization-container>svg {
694
+ &.lg .visualization-container > svg {
711
695
  width: 100%;
712
696
  }
713
697
 
@@ -738,7 +722,6 @@
738
722
  transform: translate(-20px, 0);
739
723
  }
740
724
  }
741
-
742
725
  }
743
726
 
744
727
  .subtext,