@cdc/chart 4.24.12 → 4.25.1
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/dist/cdcchart.js +79611 -78971
- package/examples/feature/boxplot/boxplot.json +2 -157
- package/examples/feature/boxplot/testing.csv +23 -38
- package/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json +394 -30
- package/examples/private/ehdi.json +29939 -0
- package/examples/private/not-loading.json +360 -0
- package/index.html +7 -14
- package/package.json +2 -2
- package/src/CdcChart.tsx +92 -1512
- package/src/CdcChartComponent.tsx +1105 -0
- package/src/_stories/Chart.Anchors.stories.tsx +1 -1
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +1 -1
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.ScatterPlot.stories.tsx +19 -0
- package/src/_stories/Chart.tooltip.stories.tsx +1 -2
- package/src/_stories/ChartAnnotation.stories.tsx +1 -1
- package/src/_stories/ChartAxisLabels.stories.tsx +1 -1
- package/src/_stories/ChartAxisTitles.stories.tsx +1 -1
- package/src/_stories/ChartEditor.stories.tsx +1 -1
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +18 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +1 -1
- package/src/_stories/_mock/line_chart_symbols.json +437 -0
- package/src/_stories/_mock/scatterplot-image-download.json +1244 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +3 -11
- package/src/components/Annotations/components/AnnotationDropdown.tsx +3 -3
- package/src/components/Axis/Categorical.Axis.tsx +3 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +14 -5
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Vertical.tsx +2 -2
- package/src/components/BoxPlot/BoxPlot.tsx +34 -32
- package/src/components/BoxPlot/helpers/index.ts +108 -18
- package/src/components/DeviationBar.jsx +2 -6
- package/src/components/EditorPanel/EditorPanel.tsx +62 -6
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +4 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +44 -7
- package/src/components/ForestPlot/ForestPlot.tsx +176 -26
- package/src/components/Legend/Legend.Component.tsx +29 -38
- package/src/components/Legend/Legend.Suppression.tsx +3 -5
- package/src/components/Legend/Legend.tsx +2 -2
- package/src/components/Legend/LegendLine.Shape.tsx +51 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +29 -26
- package/src/components/Legend/helpers/getLegendClasses.ts +20 -38
- package/src/components/Legend/helpers/index.ts +14 -7
- package/src/components/Legend/tests/getLegendClasses.test.ts +3 -20
- package/src/components/LineChart/components/LineChart.Circle.tsx +90 -88
- package/src/components/LineChart/index.tsx +4 -0
- package/src/components/LinearChart.tsx +65 -31
- package/src/components/PairedBarChart.jsx +2 -9
- package/src/components/ZoomBrush.tsx +5 -7
- package/src/data/initial-state.js +6 -3
- package/src/helpers/getBoxPlotConfig.ts +68 -0
- package/src/helpers/getColorScale.ts +28 -0
- package/src/helpers/getComboChartConfig.ts +42 -0
- package/src/helpers/getExcludedData.ts +37 -0
- package/src/helpers/getTopAxis.ts +7 -0
- package/src/hooks/useBarChart.ts +28 -9
- package/src/hooks/{useHighlightedBars.js → useHighlightedBars.ts} +2 -1
- package/src/hooks/useIntersectionObserver.ts +37 -0
- package/src/hooks/useMinMax.ts +4 -0
- package/src/hooks/useReduceData.ts +1 -1
- package/src/hooks/useTooltip.tsx +9 -1
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +0 -5
- package/src/scss/main.scss +30 -115
- package/src/types/ChartConfig.ts +6 -3
- package/src/types/ChartContext.ts +1 -3
- package/src/helpers/getQuartiles.ts +0 -27
- package/src/hooks/useColorScale.ts +0 -50
- package/src/hooks/useIntersectionObserver.jsx +0 -29
- package/src/hooks/useTopAxis.js +0 -6
|
@@ -610,7 +610,7 @@ const EditorPanel = () => {
|
|
|
610
610
|
lineOptions,
|
|
611
611
|
rawData,
|
|
612
612
|
highlight,
|
|
613
|
-
|
|
613
|
+
handleShowAll,
|
|
614
614
|
dimensions
|
|
615
615
|
} = useContext<ChartContext>(ConfigContext)
|
|
616
616
|
|
|
@@ -906,7 +906,7 @@ const EditorPanel = () => {
|
|
|
906
906
|
return Object.keys(columns)
|
|
907
907
|
}
|
|
908
908
|
|
|
909
|
-
const getLegendStyleOptions = (option: 'style' | 'subStyle'): string[] => {
|
|
909
|
+
const getLegendStyleOptions = (option: 'style' | 'subStyle' | 'shapes'): string[] => {
|
|
910
910
|
const options: string[] = []
|
|
911
911
|
|
|
912
912
|
switch (option) {
|
|
@@ -963,7 +963,7 @@ const EditorPanel = () => {
|
|
|
963
963
|
|
|
964
964
|
const convertStateToConfig = () => {
|
|
965
965
|
let strippedState = JSON.parse(JSON.stringify(config))
|
|
966
|
-
if (false === missingRequiredSections()) {
|
|
966
|
+
if (false === missingRequiredSections(config)) {
|
|
967
967
|
delete strippedState.newViz
|
|
968
968
|
}
|
|
969
969
|
delete strippedState.runtime
|
|
@@ -1631,8 +1631,19 @@ const EditorPanel = () => {
|
|
|
1631
1631
|
value={config.yAxis.label}
|
|
1632
1632
|
section='yAxis'
|
|
1633
1633
|
fieldName='label'
|
|
1634
|
-
label='Label
|
|
1634
|
+
label='Label'
|
|
1635
1635
|
updateField={updateField}
|
|
1636
|
+
maxLength={35}
|
|
1637
|
+
tooltip={
|
|
1638
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1639
|
+
<Tooltip.Target>
|
|
1640
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1641
|
+
</Tooltip.Target>
|
|
1642
|
+
<Tooltip.Content>
|
|
1643
|
+
<p>35 character limit</p>
|
|
1644
|
+
</Tooltip.Content>
|
|
1645
|
+
</Tooltip>
|
|
1646
|
+
}
|
|
1636
1647
|
/>
|
|
1637
1648
|
{config.runtime.seriesKeys &&
|
|
1638
1649
|
config.runtime.seriesKeys.length === 1 &&
|
|
@@ -2306,6 +2317,17 @@ const EditorPanel = () => {
|
|
|
2306
2317
|
fieldName='rightLabel'
|
|
2307
2318
|
label='Label'
|
|
2308
2319
|
updateField={updateField}
|
|
2320
|
+
maxLength={35}
|
|
2321
|
+
tooltip={
|
|
2322
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2323
|
+
<Tooltip.Target>
|
|
2324
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2325
|
+
</Tooltip.Target>
|
|
2326
|
+
<Tooltip.Content>
|
|
2327
|
+
<p>35 character limit</p>
|
|
2328
|
+
</Tooltip.Content>
|
|
2329
|
+
</Tooltip>
|
|
2330
|
+
}
|
|
2309
2331
|
/>
|
|
2310
2332
|
<TextField
|
|
2311
2333
|
value={config.yAxis.rightNumTicks}
|
|
@@ -2596,6 +2618,17 @@ const EditorPanel = () => {
|
|
|
2596
2618
|
fieldName='label'
|
|
2597
2619
|
label='Label'
|
|
2598
2620
|
updateField={updateField}
|
|
2621
|
+
maxLength={35}
|
|
2622
|
+
tooltip={
|
|
2623
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2624
|
+
<Tooltip.Target>
|
|
2625
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2626
|
+
</Tooltip.Target>
|
|
2627
|
+
<Tooltip.Content>
|
|
2628
|
+
<p>35 character limit</p>
|
|
2629
|
+
</Tooltip.Content>
|
|
2630
|
+
</Tooltip>
|
|
2631
|
+
}
|
|
2599
2632
|
/>
|
|
2600
2633
|
|
|
2601
2634
|
{config.xAxis.type === 'continuous' && (
|
|
@@ -3658,6 +3691,27 @@ const EditorPanel = () => {
|
|
|
3658
3691
|
updateField={updateField}
|
|
3659
3692
|
options={getLegendStyleOptions('style')}
|
|
3660
3693
|
/>
|
|
3694
|
+
<CheckBox
|
|
3695
|
+
tooltip={
|
|
3696
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
3697
|
+
<Tooltip.Target>
|
|
3698
|
+
<Icon
|
|
3699
|
+
display='question'
|
|
3700
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
3701
|
+
/>
|
|
3702
|
+
</Tooltip.Target>
|
|
3703
|
+
<Tooltip.Content>
|
|
3704
|
+
<p>Choose option Shapes in Line Datapoint Symbols to display.</p>
|
|
3705
|
+
</Tooltip.Content>
|
|
3706
|
+
</Tooltip>
|
|
3707
|
+
}
|
|
3708
|
+
display={!config.legend.hide && config.legend.style === 'lines'}
|
|
3709
|
+
value={config.legend.hasShape}
|
|
3710
|
+
section='legend'
|
|
3711
|
+
fieldName='hasShape'
|
|
3712
|
+
label='Shapes'
|
|
3713
|
+
updateField={updateField}
|
|
3714
|
+
/>
|
|
3661
3715
|
|
|
3662
3716
|
<Select
|
|
3663
3717
|
display={!config.legend.hide && config.legend.style === 'gradient'}
|
|
@@ -3797,7 +3851,7 @@ const EditorPanel = () => {
|
|
|
3797
3851
|
updatedSeriesHighlight.splice(i, 1)
|
|
3798
3852
|
updateField('legend', null, 'seriesHighlight', updatedSeriesHighlight)
|
|
3799
3853
|
if (!updatedSeriesHighlight.length) {
|
|
3800
|
-
|
|
3854
|
+
handleShowAll()
|
|
3801
3855
|
}
|
|
3802
3856
|
}}
|
|
3803
3857
|
>
|
|
@@ -3887,7 +3941,9 @@ const EditorPanel = () => {
|
|
|
3887
3941
|
display={
|
|
3888
3942
|
['bottom', 'top'].includes(config.legend.position) &&
|
|
3889
3943
|
!config.legend.hide &&
|
|
3890
|
-
config.legend.style !== 'gradient'
|
|
3944
|
+
config.legend.style !== 'gradient' &&
|
|
3945
|
+
!config.legend.singleRow &&
|
|
3946
|
+
!config.legend.singleRow
|
|
3891
3947
|
}
|
|
3892
3948
|
value={config.legend.verticalSorted}
|
|
3893
3949
|
section='legend'
|
|
@@ -209,6 +209,10 @@ const PanelGeneral: FC<PanelProps> = props => {
|
|
|
209
209
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
210
210
|
</Tooltip.Target>
|
|
211
211
|
<Tooltip.Content>
|
|
212
|
+
<p>
|
|
213
|
+
Recommended set to display for Section 508 compliance.
|
|
214
|
+
</p>
|
|
215
|
+
<hr/>
|
|
212
216
|
<p>
|
|
213
217
|
Selecting this option will <i> not </i> hide the display of "zero value", "suppressed data", or
|
|
214
218
|
"missing data" indicators on the chart (if applicable).
|
|
@@ -136,15 +136,23 @@ const PanelVisual: FC<PanelProps> = props => {
|
|
|
136
136
|
/>
|
|
137
137
|
</fieldset>
|
|
138
138
|
)}
|
|
139
|
-
<Select
|
|
140
|
-
value={config.fontSize}
|
|
141
|
-
fieldName='fontSize'
|
|
142
|
-
label='Font Size'
|
|
143
|
-
updateField={updateField}
|
|
144
|
-
options={['small', 'medium', 'large']}
|
|
145
|
-
/>
|
|
146
139
|
{visHasBarBorders() && (
|
|
147
140
|
<Select
|
|
141
|
+
tooltip={
|
|
142
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
143
|
+
<Tooltip.Target>
|
|
144
|
+
<Icon
|
|
145
|
+
display='question'
|
|
146
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
147
|
+
/>
|
|
148
|
+
</Tooltip.Target>
|
|
149
|
+
<Tooltip.Content>
|
|
150
|
+
<p>
|
|
151
|
+
Recommended set to display for Section 508 compliance.
|
|
152
|
+
</p>
|
|
153
|
+
</Tooltip.Content>
|
|
154
|
+
</Tooltip>
|
|
155
|
+
}
|
|
148
156
|
value={config.barHasBorder}
|
|
149
157
|
fieldName='barHasBorder'
|
|
150
158
|
label='Bar Borders'
|
|
@@ -171,6 +179,35 @@ const PanelVisual: FC<PanelProps> = props => {
|
|
|
171
179
|
config.visualizationType === 'Combo') ||
|
|
172
180
|
config.visualizationType === 'Line') && (
|
|
173
181
|
<>
|
|
182
|
+
<Select
|
|
183
|
+
tooltip={
|
|
184
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
185
|
+
<Tooltip.Target>
|
|
186
|
+
<Icon
|
|
187
|
+
display='question'
|
|
188
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
189
|
+
/>
|
|
190
|
+
</Tooltip.Target>
|
|
191
|
+
<Tooltip.Content>
|
|
192
|
+
<p>
|
|
193
|
+
Shapes will appear in the following order: circle, square, triangle, diamond, and inverted
|
|
194
|
+
triangle. Use with a maximum of 5 data points.
|
|
195
|
+
</p>
|
|
196
|
+
</Tooltip.Content>
|
|
197
|
+
</Tooltip>
|
|
198
|
+
}
|
|
199
|
+
value={config.visual.lineDatapointSymbol}
|
|
200
|
+
section='visual'
|
|
201
|
+
fieldName='lineDatapointSymbol'
|
|
202
|
+
label='Line Datapoint Symbols'
|
|
203
|
+
updateField={updateField}
|
|
204
|
+
options={['none', 'standard']}
|
|
205
|
+
/>
|
|
206
|
+
{config.series.length > config.visual.maximumShapeAmount &&
|
|
207
|
+
config.visual.lineDatapointSymbol === 'standard' && (
|
|
208
|
+
<small className='text-danger'>Standard only supports up to 7 data points</small>
|
|
209
|
+
)}
|
|
210
|
+
|
|
174
211
|
<Select
|
|
175
212
|
value={config.lineDatapointStyle}
|
|
176
213
|
fieldName='lineDatapointStyle'
|
|
@@ -14,9 +14,18 @@ import { type ChartContext } from '@cdc/chart/src/types/ChartContext'
|
|
|
14
14
|
|
|
15
15
|
// cdc
|
|
16
16
|
import ConfigContext from '../../ConfigContext'
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
const ForestPlot = ({
|
|
17
|
+
import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
|
|
18
|
+
|
|
19
|
+
const ForestPlot = ({
|
|
20
|
+
xScale,
|
|
21
|
+
yScale,
|
|
22
|
+
config,
|
|
23
|
+
height,
|
|
24
|
+
width,
|
|
25
|
+
handleTooltipMouseOff,
|
|
26
|
+
handleTooltipMouseOver,
|
|
27
|
+
forestPlotRightLabelRef
|
|
28
|
+
}: ForestPlotProps) => {
|
|
20
29
|
const { rawData: data, updateConfig } = useContext<ChartContext>(ConfigContext)
|
|
21
30
|
const { forestPlot } = config as ChartConfig
|
|
22
31
|
const labelPosition = config.xAxis.tickWidthMax + 10
|
|
@@ -33,7 +42,10 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
33
42
|
const colsToCheck = 10
|
|
34
43
|
for (let i = 0; i < colsToCheck; i++) {
|
|
35
44
|
defaultColumns.forEach(col => {
|
|
36
|
-
if (
|
|
45
|
+
if (
|
|
46
|
+
config.forestPlot[col] &&
|
|
47
|
+
config.forestPlot[col] !== newConfig.columns[config.forestPlot[`additionalColumn${i}`]]?.name
|
|
48
|
+
) {
|
|
37
49
|
delete newConfig.columns[`additionalColumn${i}`] // Remove old value if found to prevent duplicates
|
|
38
50
|
newConfig.columns[config.forestPlot[col]] = {}
|
|
39
51
|
newConfig.columns[config.forestPlot[col]].dataKey = newConfig.forestPlot[col]
|
|
@@ -83,9 +95,15 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
83
95
|
const regressionPoints = pooledData
|
|
84
96
|
? [
|
|
85
97
|
{ x: xScale(pooledData[config.forestPlot.lower]), y: height - Number(config.forestPlot.rowHeight) },
|
|
86
|
-
{
|
|
98
|
+
{
|
|
99
|
+
x: xScale(pooledData[config.forestPlot.estimateField]),
|
|
100
|
+
y: height - forestPlot.pooledResult.diamondHeight - Number(config.forestPlot.rowHeight)
|
|
101
|
+
},
|
|
87
102
|
{ x: xScale(pooledData[config.forestPlot.upper]), y: height - Number(config.forestPlot.rowHeight) },
|
|
88
|
-
{
|
|
103
|
+
{
|
|
104
|
+
x: xScale(pooledData[config.forestPlot.estimateField]),
|
|
105
|
+
y: height + forestPlot.pooledResult.diamondHeight - Number(config.forestPlot.rowHeight)
|
|
106
|
+
},
|
|
89
107
|
{ x: xScale(pooledData[config.forestPlot.lower]), y: height - Number(config.forestPlot.rowHeight) }
|
|
90
108
|
]
|
|
91
109
|
: []
|
|
@@ -114,16 +132,37 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
114
132
|
<>
|
|
115
133
|
<Group width={width}>
|
|
116
134
|
{forestPlot.title && (
|
|
117
|
-
<Text
|
|
135
|
+
<Text
|
|
136
|
+
className={`forest-plot--title`}
|
|
137
|
+
x={forestPlot.type === 'Linear' ? xScale(0) : xScale(1)}
|
|
138
|
+
y={0}
|
|
139
|
+
textAnchor='middle'
|
|
140
|
+
verticalAnchor='start'
|
|
141
|
+
fill={'black'}
|
|
142
|
+
>
|
|
118
143
|
{forestPlot.title}
|
|
119
144
|
</Text>
|
|
120
145
|
)}
|
|
121
146
|
|
|
122
147
|
{/* Line of no effect on Continuous Scale. */}
|
|
123
|
-
{forestPlot.lineOfNoEffect.show && forestPlot.type === 'Linear' &&
|
|
148
|
+
{forestPlot.lineOfNoEffect.show && forestPlot.type === 'Linear' && (
|
|
149
|
+
<Line
|
|
150
|
+
from={{ x: xScale(0), y: 0 + topMarginOffset }}
|
|
151
|
+
to={{ x: xScale(0), y: height }}
|
|
152
|
+
className='forestplot__line-of-no-effect'
|
|
153
|
+
stroke={forestPlot.regression.baseLineColor || 'black'}
|
|
154
|
+
/>
|
|
155
|
+
)}
|
|
124
156
|
|
|
125
157
|
{/* Line of no effect on Logarithmic Scale. */}
|
|
126
|
-
{forestPlot.lineOfNoEffect.show && forestPlot.type === 'Logarithmic' &&
|
|
158
|
+
{forestPlot.lineOfNoEffect.show && forestPlot.type === 'Logarithmic' && (
|
|
159
|
+
<Line
|
|
160
|
+
from={{ x: xScale(1), y: 0 + topMarginOffset }}
|
|
161
|
+
to={{ x: xScale(1), y: height }}
|
|
162
|
+
className='forestplot__line-of-no-effect'
|
|
163
|
+
stroke={forestPlot.regression.baseLineColor || 'black'}
|
|
164
|
+
/>
|
|
165
|
+
)}
|
|
127
166
|
|
|
128
167
|
{data.map((d, i) => {
|
|
129
168
|
// calculate both square and circle size based on radius.min and radius.max
|
|
@@ -133,7 +172,8 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
133
172
|
})
|
|
134
173
|
|
|
135
174
|
// glyph settings
|
|
136
|
-
const rectSize =
|
|
175
|
+
const rectSize =
|
|
176
|
+
forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.radius.scalingColumn]) : 4
|
|
137
177
|
const shapeColor = forestPlot.colors.shape ? forestPlot.colors.shape : 'black'
|
|
138
178
|
const lineColor = forestPlot.colors.line ? forestPlot.colors.line : 'black'
|
|
139
179
|
|
|
@@ -167,41 +207,124 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
167
207
|
/>
|
|
168
208
|
|
|
169
209
|
{/* main line */}
|
|
170
|
-
<line
|
|
210
|
+
<line
|
|
211
|
+
stroke={lineColor}
|
|
212
|
+
className={`line-${d[config.yAxis.dataKey]}`}
|
|
213
|
+
key={i}
|
|
214
|
+
x1={xScale(d[forestPlot.lower])}
|
|
215
|
+
x2={xScale(d[forestPlot.upper])}
|
|
216
|
+
y1={yScale(i)}
|
|
217
|
+
y2={yScale(i)}
|
|
218
|
+
/>
|
|
171
219
|
{forestPlot.shape === 'circle' && (
|
|
172
|
-
<Circle
|
|
220
|
+
<Circle
|
|
221
|
+
className='forest-plot--circle'
|
|
222
|
+
cx={xScale(Number(d[forestPlot.estimateField]))}
|
|
223
|
+
cy={yScale(i)}
|
|
224
|
+
r={forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.radius.scalingColumn]) : 4}
|
|
225
|
+
fill={shapeColor}
|
|
226
|
+
style={{ opacity: 1, filter: 'unset' }}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
{forestPlot.shape === 'square' && (
|
|
230
|
+
<rect
|
|
231
|
+
className='forest-plot--square'
|
|
232
|
+
x={xScale(Number(d[forestPlot.estimateField]))}
|
|
233
|
+
y={yScale(i) - rectSize / 2}
|
|
234
|
+
width={rectSize}
|
|
235
|
+
height={rectSize}
|
|
236
|
+
fill={shapeColor}
|
|
237
|
+
style={{ opacity: 1, filter: 'unset' }}
|
|
238
|
+
/>
|
|
173
239
|
)}
|
|
174
|
-
{forestPlot.shape === 'square' && <rect className='forest-plot--square' x={xScale(Number(d[forestPlot.estimateField]))} y={yScale(i) - rectSize / 2} width={rectSize} height={rectSize} fill={shapeColor} style={{ opacity: 1, filter: 'unset' }} />}
|
|
175
240
|
{forestPlot.shape === 'text' && (
|
|
176
|
-
<Text
|
|
241
|
+
<Text
|
|
242
|
+
className='forest-plot--text'
|
|
243
|
+
x={xScale(Number(d[forestPlot.estimateField]))}
|
|
244
|
+
y={yScale(i)}
|
|
245
|
+
textAnchor='middle'
|
|
246
|
+
verticalAnchor='middle'
|
|
247
|
+
fill={shapeColor}
|
|
248
|
+
>
|
|
177
249
|
{d[forestPlot.estimateField]}
|
|
178
250
|
</Text>
|
|
179
251
|
)}
|
|
180
252
|
</Group>
|
|
181
253
|
) : (
|
|
182
|
-
<LinePath
|
|
254
|
+
<LinePath
|
|
255
|
+
data={regressionPoints}
|
|
256
|
+
x={d => d.x}
|
|
257
|
+
y={d => d.y - appFontSize / 2}
|
|
258
|
+
stroke='black'
|
|
259
|
+
strokeWidth={2}
|
|
260
|
+
fill={'black'}
|
|
261
|
+
curve={curveLinearClosed}
|
|
262
|
+
/>
|
|
183
263
|
)
|
|
184
264
|
})}
|
|
185
265
|
|
|
186
266
|
{/* regression diamond */}
|
|
187
|
-
{regressionPoints && forestPlot.regression.showDiamond &&
|
|
267
|
+
{regressionPoints && forestPlot.regression.showDiamond && (
|
|
268
|
+
<LinePath
|
|
269
|
+
data={regressionPoints}
|
|
270
|
+
x={d => d.x}
|
|
271
|
+
y={d => d.y}
|
|
272
|
+
stroke='black'
|
|
273
|
+
strokeWidth={2}
|
|
274
|
+
fill={forestPlot.regression.baseLineColor}
|
|
275
|
+
curve={curveLinearClosed}
|
|
276
|
+
/>
|
|
277
|
+
)}
|
|
188
278
|
{/* regression text */}
|
|
189
279
|
{forestPlot.regression.description && (
|
|
190
|
-
<Text
|
|
280
|
+
<Text
|
|
281
|
+
x={0 - Number(config.xAxis.size)}
|
|
282
|
+
width={width}
|
|
283
|
+
y={height - config.forestPlot.rowHeight - Number(forestPlot.rowHeight) / 3}
|
|
284
|
+
verticalAnchor='start'
|
|
285
|
+
textAnchor='start'
|
|
286
|
+
style={{ fontWeight: 'bold', fontSize: 12 }}
|
|
287
|
+
>
|
|
191
288
|
{forestPlot.regression.description}
|
|
192
289
|
</Text>
|
|
193
290
|
)}
|
|
194
291
|
|
|
195
|
-
<Bar
|
|
292
|
+
<Bar
|
|
293
|
+
key='forest-plot-tooltip-area'
|
|
294
|
+
className='forest-plot-tooltip-area'
|
|
295
|
+
width={width}
|
|
296
|
+
height={height}
|
|
297
|
+
fill={false ? 'red' : 'transparent'}
|
|
298
|
+
fillOpacity={0.5}
|
|
299
|
+
onMouseMove={e => handleTooltipMouseOver(e, data)}
|
|
300
|
+
onMouseOut={handleTooltipMouseOff}
|
|
301
|
+
/>
|
|
196
302
|
</Group>
|
|
197
|
-
<Line
|
|
198
|
-
|
|
303
|
+
<Line
|
|
304
|
+
from={topLine[0]}
|
|
305
|
+
to={topLine[1]}
|
|
306
|
+
style={{ stroke: 'black', strokeWidth: 2 }}
|
|
307
|
+
className='forestplot__top-line'
|
|
308
|
+
/>
|
|
309
|
+
<Line
|
|
310
|
+
from={bottomLine[0]}
|
|
311
|
+
to={bottomLine[1]}
|
|
312
|
+
style={{ stroke: 'black', strokeWidth: 2 }}
|
|
313
|
+
className='forestplot__bottom-line'
|
|
314
|
+
/>
|
|
199
315
|
|
|
200
316
|
{/* column data */}
|
|
201
317
|
{columnsOnChart.map(column => {
|
|
202
318
|
return data.map((d, i) => {
|
|
203
319
|
return (
|
|
204
|
-
<Text
|
|
320
|
+
<Text
|
|
321
|
+
className={`${d[column.name]}`}
|
|
322
|
+
x={column.forestPlotAlignRight ? width : column.forestPlotStartingPoint}
|
|
323
|
+
y={yScale(i)}
|
|
324
|
+
textAnchor={column.forestPlotAlignRight ? 'end' : 'start'}
|
|
325
|
+
verticalAnchor='middle'
|
|
326
|
+
fill={'black'}
|
|
327
|
+
>
|
|
205
328
|
{d[column.name]}
|
|
206
329
|
</Text>
|
|
207
330
|
)
|
|
@@ -212,7 +335,14 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
212
335
|
{!forestPlot.hideDateCategoryCol &&
|
|
213
336
|
data.map((d, i) => {
|
|
214
337
|
return (
|
|
215
|
-
<Text
|
|
338
|
+
<Text
|
|
339
|
+
className={`${d[config.xAxis.dataKey]}`}
|
|
340
|
+
x={0}
|
|
341
|
+
y={yScale(i)}
|
|
342
|
+
textAnchor={'start'}
|
|
343
|
+
verticalAnchor='middle'
|
|
344
|
+
fill={'black'}
|
|
345
|
+
>
|
|
216
346
|
{d[config.xAxis.dataKey]}
|
|
217
347
|
</Text>
|
|
218
348
|
)
|
|
@@ -220,7 +350,7 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
220
350
|
|
|
221
351
|
{/* X Axis Datakey Header */}
|
|
222
352
|
{!forestPlot.hideDateCategoryCol && config.xAxis.dataKey && (
|
|
223
|
-
<Text className={config.xAxis.dataKey} x={0} y={0} textAnchor={'start'} verticalAnchor='start'
|
|
353
|
+
<Text className={config.xAxis.dataKey} x={0} y={0} textAnchor={'start'} verticalAnchor='start' fill={'black'}>
|
|
224
354
|
{config.xAxis.dataKey}
|
|
225
355
|
</Text>
|
|
226
356
|
)}
|
|
@@ -228,7 +358,14 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
228
358
|
{/* column headers */}
|
|
229
359
|
{columnsOnChart.map(column => {
|
|
230
360
|
return (
|
|
231
|
-
<Text
|
|
361
|
+
<Text
|
|
362
|
+
className={`${column.label}`}
|
|
363
|
+
x={column.forestPlotAlignRight ? width : column.forestPlotStartingPoint}
|
|
364
|
+
y={0}
|
|
365
|
+
textAnchor={column.forestPlotAlignRight ? 'end' : 'start'}
|
|
366
|
+
verticalAnchor='start'
|
|
367
|
+
fill={'black'}
|
|
368
|
+
>
|
|
232
369
|
{column.label}
|
|
233
370
|
</Text>
|
|
234
371
|
)
|
|
@@ -236,13 +373,26 @@ const ForestPlot = ({ xScale, yScale, config, height, width, handleTooltipMouseO
|
|
|
236
373
|
|
|
237
374
|
{/* left bottom label */}
|
|
238
375
|
{forestPlot.leftLabel && (
|
|
239
|
-
<Text
|
|
376
|
+
<Text
|
|
377
|
+
className='forest-plot__left-label'
|
|
378
|
+
x={forestPlot.type === 'Linear' ? xScale(0) - 25 : xScale(1) - 25}
|
|
379
|
+
y={height + labelPosition}
|
|
380
|
+
textAnchor='end'
|
|
381
|
+
verticalAnchor='start'
|
|
382
|
+
>
|
|
240
383
|
{forestPlot.leftLabel}
|
|
241
384
|
</Text>
|
|
242
385
|
)}
|
|
243
386
|
|
|
244
387
|
{forestPlot.rightLabel && (
|
|
245
|
-
<Text
|
|
388
|
+
<Text
|
|
389
|
+
innerRef={forestPlotRightLabelRef}
|
|
390
|
+
className='forest-plot__right-label'
|
|
391
|
+
x={forestPlot.type === 'Linear' ? xScale(0) + 25 : xScale(1) + 25}
|
|
392
|
+
y={height + labelPosition}
|
|
393
|
+
textAnchor='start'
|
|
394
|
+
verticalAnchor='start'
|
|
395
|
+
>
|
|
246
396
|
{forestPlot.rightLabel}
|
|
247
397
|
</Text>
|
|
248
398
|
)}
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import parse from 'html-react-parser'
|
|
2
|
+
import React from 'react'
|
|
2
3
|
import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
|
|
3
4
|
import LegendShape from '@cdc/core/components/LegendShape'
|
|
4
5
|
import Button from '@cdc/core/components/elements/Button'
|
|
5
6
|
import { getLegendClasses } from './helpers/getLegendClasses'
|
|
6
7
|
import { useHighlightedBars } from '../../hooks/useHighlightedBars'
|
|
7
|
-
import { handleLineType } from '../../helpers/handleLineType'
|
|
8
|
-
|
|
9
8
|
import { getMarginTop, getGradientConfig, getMarginBottom } from './helpers/index'
|
|
10
|
-
import { Line } from '@visx/shape'
|
|
11
9
|
import { Label } from '../../types/Label'
|
|
12
10
|
import { ChartConfig, ViewportSize } from '../../types/ChartConfig'
|
|
13
11
|
import { ColorScale } from '../../types/ChartContext'
|
|
14
|
-
import { forwardRef
|
|
12
|
+
import { forwardRef } from 'react'
|
|
15
13
|
import LegendSuppression from './Legend.Suppression'
|
|
16
14
|
import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
|
|
17
15
|
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
18
16
|
import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
|
|
17
|
+
import LegendLineShape from './LegendLine.Shape'
|
|
19
18
|
|
|
20
|
-
const LEGEND_PADDING =
|
|
19
|
+
const LEGEND_PADDING = 36
|
|
21
20
|
|
|
22
21
|
export interface LegendProps {
|
|
23
22
|
colorScale: ColorScale
|
|
@@ -25,7 +24,7 @@ export interface LegendProps {
|
|
|
25
24
|
currentViewport: ViewportSize
|
|
26
25
|
formatLabels: (labels: Label[]) => Label[]
|
|
27
26
|
highlight: Function
|
|
28
|
-
|
|
27
|
+
handleShowAll: Function
|
|
29
28
|
ref: React.Ref<() => void>
|
|
30
29
|
seriesHighlight: string[]
|
|
31
30
|
skipId: string
|
|
@@ -40,7 +39,7 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
40
39
|
colorScale,
|
|
41
40
|
seriesHighlight,
|
|
42
41
|
highlight,
|
|
43
|
-
|
|
42
|
+
handleShowAll,
|
|
44
43
|
currentViewport,
|
|
45
44
|
formatLabels,
|
|
46
45
|
skipId = 'legend',
|
|
@@ -51,14 +50,13 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
51
50
|
const { innerClasses, containerClasses } = getLegendClasses(config)
|
|
52
51
|
const { runtime, legend } = config
|
|
53
52
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
legend?.position === 'bottom' || (isLegendWrapViewport(currentViewport) && !legend.hide)
|
|
53
|
+
const isLegendBottom =
|
|
54
|
+
legend?.position === 'bottom' ||
|
|
55
|
+
(isLegendWrapViewport(currentViewport) && !legend.hide && legend?.position !== 'top')
|
|
58
56
|
|
|
59
57
|
const legendClasses = {
|
|
60
|
-
marginBottom: getMarginBottom(
|
|
61
|
-
marginTop: getMarginTop(
|
|
58
|
+
marginBottom: getMarginBottom(isLegendBottom, config),
|
|
59
|
+
marginTop: getMarginTop(isLegendBottom, config)
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
@@ -74,8 +72,12 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
74
72
|
aria-label='legend'
|
|
75
73
|
tabIndex={0}
|
|
76
74
|
>
|
|
77
|
-
{legend.label
|
|
78
|
-
|
|
75
|
+
{(legend.label || legend.description) && (
|
|
76
|
+
<div className={legend.description ? 'mb-3' : 'mb-2'}>
|
|
77
|
+
{legend.label && <h3 className='fw-bold'>{parse(legend.label)}</h3>}
|
|
78
|
+
{legend.description && <p className='mt-2'>{parse(legend.description)}</p>}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
79
81
|
<LegendGradient
|
|
80
82
|
config={config}
|
|
81
83
|
{...getGradientConfig(config, formatLabels, colorScale)}
|
|
@@ -106,8 +108,10 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
if (seriesHighlight.length
|
|
110
|
-
|
|
111
|
+
if (seriesHighlight.length) {
|
|
112
|
+
if (!seriesHighlight.includes(itemName)) {
|
|
113
|
+
className.push('inactive')
|
|
114
|
+
} else className.push('highlighted')
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
if (config.legend.style === 'gradient') {
|
|
@@ -133,15 +137,9 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
133
137
|
>
|
|
134
138
|
<>
|
|
135
139
|
{config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
|
|
136
|
-
<
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
to={{ x: 40, y: 10 }}
|
|
140
|
-
stroke={label.value}
|
|
141
|
-
strokeWidth={2}
|
|
142
|
-
strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')}
|
|
143
|
-
/>
|
|
144
|
-
</svg>
|
|
140
|
+
<React.Fragment>
|
|
141
|
+
<LegendLineShape index={i} label={label} config={config} />
|
|
142
|
+
</React.Fragment>
|
|
145
143
|
) : (
|
|
146
144
|
<>
|
|
147
145
|
<LegendShape
|
|
@@ -151,8 +149,7 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
151
149
|
</>
|
|
152
150
|
)}
|
|
153
151
|
</>
|
|
154
|
-
|
|
155
|
-
<LegendLabel align='left' margin='0 0 0 4px'>
|
|
152
|
+
<LegendLabel align='left' className='m-0'>
|
|
156
153
|
{label.text}
|
|
157
154
|
</LegendLabel>
|
|
158
155
|
</LegendItem>
|
|
@@ -191,26 +188,20 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
191
188
|
fill='transparent'
|
|
192
189
|
borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`}
|
|
193
190
|
/>{' '}
|
|
194
|
-
<LegendLabel align='left'
|
|
195
|
-
{bar.legendLabel ? bar.legendLabel : bar.value}
|
|
196
|
-
</LegendLabel>
|
|
191
|
+
<LegendLabel align='left'>{bar.legendLabel ? bar.legendLabel : bar.value}</LegendLabel>
|
|
197
192
|
</LegendItem>
|
|
198
193
|
)
|
|
199
194
|
})}
|
|
200
195
|
</div>
|
|
201
196
|
|
|
202
|
-
<LegendSuppression
|
|
203
|
-
config={config}
|
|
204
|
-
isBottomOrSmallViewport={isBottomOrSmallViewport}
|
|
205
|
-
setHasSuppression={setHasSuppression}
|
|
206
|
-
/>
|
|
197
|
+
<LegendSuppression config={config} isLegendBottom={isLegendBottom} />
|
|
207
198
|
</>
|
|
208
199
|
)
|
|
209
200
|
}}
|
|
210
201
|
</LegendOrdinal>
|
|
211
202
|
{seriesHighlight.length > 0 && (
|
|
212
|
-
<Button onClick={labels =>
|
|
213
|
-
|
|
203
|
+
<Button onClick={labels => handleShowAll(labels)} style={{ marginTop: '1rem' }}>
|
|
204
|
+
Show All
|
|
214
205
|
</Button>
|
|
215
206
|
)}
|
|
216
207
|
</aside>
|