@cdc/chart 4.24.3 → 4.24.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdcchart.js +28061 -27814
- package/examples/feature/line/line-chart.json +361 -37
- package/examples/region-issue.json +2065 -0
- package/examples/test.json +5409 -0
- package/index.html +14 -12
- package/package.json +2 -2
- package/src/CdcChart.tsx +149 -83
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +9 -16
- package/src/components/BarChart/components/BarChart.Vertical.tsx +5 -5
- package/src/components/EditorPanel/EditorPanel.tsx +1631 -1663
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +0 -1
- package/src/components/EditorPanel/editor-panel.scss +0 -724
- package/src/components/Legend/Legend.Component.tsx +22 -50
- package/src/components/Legend/Legend.tsx +5 -1
- package/src/components/LinearChart.jsx +2 -1
- package/src/hooks/useScales.ts +32 -0
- package/src/scss/main.scss +25 -0
|
@@ -2,16 +2,13 @@ import parse from 'html-react-parser'
|
|
|
2
2
|
import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
|
|
3
3
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
4
4
|
import Button from '@cdc/core/components/elements/Button'
|
|
5
|
-
|
|
6
5
|
import useLegendClasses from '../../hooks/useLegendClasses'
|
|
7
6
|
import { useHighlightedBars } from '../../hooks/useHighlightedBars'
|
|
8
7
|
import { handleLineType } from '../../helpers/handleLineType'
|
|
9
8
|
import { Line } from '@visx/shape'
|
|
10
|
-
import { scaleOrdinal } from '@visx/scale'
|
|
11
9
|
import { Label } from '../../types/Label'
|
|
12
10
|
import { ChartConfig } from '../../types/ChartConfig'
|
|
13
11
|
import { ColorScale } from '../../types/ChartContext'
|
|
14
|
-
import { Group } from '@visx/group'
|
|
15
12
|
import { forwardRef } from 'react'
|
|
16
13
|
|
|
17
14
|
interface LegendProps {
|
|
@@ -20,43 +17,17 @@ interface LegendProps {
|
|
|
20
17
|
seriesHighlight: string[]
|
|
21
18
|
highlight: Function
|
|
22
19
|
highlightReset: Function
|
|
23
|
-
currentViewport:
|
|
20
|
+
currentViewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
24
21
|
formatLabels: (labels: Label[]) => Label[]
|
|
25
22
|
ref: React.Ref<() => void>
|
|
23
|
+
skipId: string
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
29
|
-
const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHighlight, highlight, highlightReset, currentViewport, formatLabels }, ref) => {
|
|
27
|
+
const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHighlight, highlight, highlightReset, currentViewport, formatLabels, skipId = 'legend' }, ref) => {
|
|
30
28
|
const { innerClasses, containerClasses } = useLegendClasses(config)
|
|
31
29
|
const { runtime, orientation, legend } = config
|
|
32
30
|
if (!legend) return null
|
|
33
|
-
// create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
|
|
34
|
-
const displayScale = scaleOrdinal({
|
|
35
|
-
domain: config.suppressedData?.map(d => d.label),
|
|
36
|
-
range: ['none'],
|
|
37
|
-
unknown: 'block'
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
const renderDashes = style => {
|
|
41
|
-
const dashCount = style === 'Dashed Small' ? 3 : 2
|
|
42
|
-
const dashClass = `dashes ${style.toLowerCase().replace(' ', '-')}`
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<div className={dashClass}>
|
|
46
|
-
{Array.from({ length: dashCount }, (_, i) => (
|
|
47
|
-
<span key={i}>-</span>
|
|
48
|
-
))}
|
|
49
|
-
</div>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
const renderDashesOrCircle = style => {
|
|
53
|
-
if (['Dashed Small', 'Dashed Medium', 'Dashed Large'].includes(style)) {
|
|
54
|
-
return renderDashes(style)
|
|
55
|
-
} else if (style === 'Open Circles') {
|
|
56
|
-
return <div className='dashes open-circles'></div>
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
31
|
const isBottomOrSmallViewport = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
61
32
|
|
|
62
33
|
const legendClasses = {
|
|
@@ -67,11 +38,13 @@ const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHi
|
|
|
67
38
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
68
39
|
|
|
69
40
|
let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
|
|
41
|
+
const fontSize = ['sm', 'xs', 'xxs'].includes(currentViewport) ? { fontSize: '11px' } : null
|
|
70
42
|
|
|
71
43
|
return (
|
|
72
|
-
<aside ref={ref} style={legendClasses} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
|
|
44
|
+
<aside ref={ref} style={legendClasses} id={skipId || 'legend'} className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
|
|
73
45
|
{legend.label && <h3>{parse(legend.label)}</h3>}
|
|
74
|
-
{legend.description && <p>{parse(legend.description)}</p>}
|
|
46
|
+
{legend.description && <p style={fontSize}>{parse(legend.description)}</p>}
|
|
47
|
+
|
|
75
48
|
<LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
|
|
76
49
|
{labels => {
|
|
77
50
|
return (
|
|
@@ -116,18 +89,19 @@ const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHi
|
|
|
116
89
|
}}
|
|
117
90
|
role='button'
|
|
118
91
|
>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
92
|
+
<div>
|
|
93
|
+
{config.visualizationType === 'Line' && config.legend.lineMode ? (
|
|
94
|
+
<svg width={40} height={20}>
|
|
95
|
+
<Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={label.value} strokeWidth={2} strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')} />
|
|
96
|
+
</svg>
|
|
97
|
+
) : (
|
|
98
|
+
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
99
|
+
<LegendCircle viewport={currentViewport} margin='0' fill={label.value} display={true} />
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<LegendLabel style={fontSize} align='left' margin='0 0 0 4px'>
|
|
131
105
|
{label.text}
|
|
132
106
|
</LegendLabel>
|
|
133
107
|
</LegendItem>
|
|
@@ -177,10 +151,8 @@ const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHi
|
|
|
177
151
|
return (
|
|
178
152
|
<>
|
|
179
153
|
{pd.label && (
|
|
180
|
-
<div
|
|
181
|
-
<svg
|
|
182
|
-
{pd.style.includes('Dashed') ? <Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={'#000'} strokeWidth={2} strokeDasharray={handleLineType(pd.style)} /> : <circle r={6} strokeWidth={2} stroke={'#000'} cx={22} cy={10} fill='transparent' />}
|
|
183
|
-
</svg>
|
|
154
|
+
<div key={index} className='legend-preliminary'>
|
|
155
|
+
<svg>{pd.style.includes('Dashed') ? <Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={'#000'} strokeWidth={2} strokeDasharray={handleLineType(pd.style)} /> : <circle r={6} strokeWidth={2} stroke={'#000'} cx={22} cy={10} fill='transparent' />}</svg>
|
|
184
156
|
<span> {pd.label}</span>
|
|
185
157
|
</div>
|
|
186
158
|
)}
|
|
@@ -22,7 +22,11 @@ const Legend = forwardRef((props, ref) => {
|
|
|
22
22
|
|
|
23
23
|
const createLegendLabels = createFormatLabels(config, tableData, data, colorScale)
|
|
24
24
|
|
|
25
|
-
return
|
|
25
|
+
return (
|
|
26
|
+
!['Box Plot', 'Pie'].includes(config.visualizationType) && (
|
|
27
|
+
<LegendComponent ref={ref} skipId={props.skipId || 'legend'} config={config} colorScale={colorScale} seriesHighlight={seriesHighlight} highlight={highlight} highlightReset={highlightReset} currentViewport={currentViewport} formatLabels={createLegendLabels} />
|
|
28
|
+
)
|
|
29
|
+
)
|
|
26
30
|
})
|
|
27
31
|
|
|
28
32
|
export default Legend
|
|
@@ -28,7 +28,7 @@ import Regions from './Regions'
|
|
|
28
28
|
import useMinMax from '../hooks/useMinMax'
|
|
29
29
|
import useReduceData from '../hooks/useReduceData'
|
|
30
30
|
import useRightAxis from '../hooks/useRightAxis'
|
|
31
|
-
import useScales from '../hooks/useScales'
|
|
31
|
+
import useScales, { getTickValues } from '../hooks/useScales'
|
|
32
32
|
import useTopAxis from '../hooks/useTopAxis'
|
|
33
33
|
import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
|
|
34
34
|
import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
|
|
@@ -375,6 +375,7 @@ const LinearChart = props => {
|
|
|
375
375
|
stroke='#333'
|
|
376
376
|
numTicks={countNumOfTicks('xAxis')}
|
|
377
377
|
tickStroke='#333'
|
|
378
|
+
tickValues={config.xAxis.manual ? getTickValues(xAxisDataMapped, xScale, config.xAxis.type === 'date-time' ? countNumOfTicks('xAxis') : config.xAxis.manualStep) : undefined}
|
|
378
379
|
>
|
|
379
380
|
{props => {
|
|
380
381
|
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : dimensions[0] / 2
|
package/src/hooks/useScales.ts
CHANGED
|
@@ -238,6 +238,38 @@ const useScales = (properties: useScaleProps) => {
|
|
|
238
238
|
|
|
239
239
|
export default useScales
|
|
240
240
|
|
|
241
|
+
export const getTickValues = (xAxisDataMapped, xScale, num) => {
|
|
242
|
+
const xDomain = xScale.domain();
|
|
243
|
+
|
|
244
|
+
if(xScale.type === 'time'){
|
|
245
|
+
const xDomainMax = xAxisDataMapped[xAxisDataMapped.length - 1];
|
|
246
|
+
const xDomainMin = xAxisDataMapped[0];
|
|
247
|
+
const step = (xDomainMax - xDomainMin) / (num - 1);
|
|
248
|
+
const tickValues = [];
|
|
249
|
+
for(let i = xDomainMax; i >= xDomainMin; i -= step){
|
|
250
|
+
tickValues.push(i);
|
|
251
|
+
}
|
|
252
|
+
if(tickValues[tickValues.length - 1] !== xDomainMin){
|
|
253
|
+
tickValues.push(xDomainMin);
|
|
254
|
+
}
|
|
255
|
+
tickValues.reverse();
|
|
256
|
+
|
|
257
|
+
return tickValues;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if(xDomain.length > 2){
|
|
261
|
+
const step = num || 1;
|
|
262
|
+
const tickValues = [];
|
|
263
|
+
for(let i = xDomain.length; i > 0; i -= step){
|
|
264
|
+
const adjustedIndex = Math.max(Math.round(i) - 1, 0);
|
|
265
|
+
tickValues.push(xDomain[adjustedIndex]);
|
|
266
|
+
}
|
|
267
|
+
tickValues.reverse();
|
|
268
|
+
|
|
269
|
+
return tickValues;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
241
273
|
/// helper functions
|
|
242
274
|
const composeXScale = ({ min, max, xMax, config }) => {
|
|
243
275
|
// Adjust min value if using logarithmic scale
|
package/src/scss/main.scss
CHANGED
|
@@ -130,6 +130,9 @@
|
|
|
130
130
|
word-wrap: break-word;
|
|
131
131
|
white-space: pre-wrap;
|
|
132
132
|
word-break: break-word;
|
|
133
|
+
@include breakpoint(xs) {
|
|
134
|
+
font-size: $font-small;
|
|
135
|
+
}
|
|
133
136
|
}
|
|
134
137
|
}
|
|
135
138
|
|
|
@@ -150,6 +153,9 @@
|
|
|
150
153
|
h3,
|
|
151
154
|
p {
|
|
152
155
|
margin-bottom: 0.8em;
|
|
156
|
+
@include breakpoint(xs) {
|
|
157
|
+
font-size: $font-small + 0.2em;
|
|
158
|
+
}
|
|
153
159
|
}
|
|
154
160
|
& div.legend-item {
|
|
155
161
|
margin-bottom: 0.5em !important;
|
|
@@ -192,6 +198,20 @@
|
|
|
192
198
|
}
|
|
193
199
|
}
|
|
194
200
|
|
|
201
|
+
.legend-preliminary {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: row;
|
|
204
|
+
align-items: center;
|
|
205
|
+
@include breakpoint(xs) {
|
|
206
|
+
font-size: $font-small;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
& > svg {
|
|
210
|
+
width: 50px;
|
|
211
|
+
height: 23px;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
195
215
|
.visx-tooltip {
|
|
196
216
|
z-index: 100000;
|
|
197
217
|
}
|
|
@@ -599,6 +619,11 @@
|
|
|
599
619
|
}
|
|
600
620
|
}
|
|
601
621
|
|
|
622
|
+
.cdc-open-viz-module .is-editor .cdc-chart-inner-container {
|
|
623
|
+
overflow: hidden;
|
|
624
|
+
background-color: var(--white);
|
|
625
|
+
}
|
|
626
|
+
|
|
602
627
|
.isEditor.type-sparkline .cdc-chart-inner-container {
|
|
603
628
|
margin: 3em auto 0;
|
|
604
629
|
max-width: 60%;
|