@cdc/chart 4.23.10 → 4.23.11
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 +30989 -29057
- package/examples/feature/bar/example-bar-chart.json +1 -46
- package/examples/feature/bar/lollipop.json +156 -0
- package/examples/feature/combo/planet-combo-example-config.json +99 -9
- package/examples/feature/dev-4261.json +399 -0
- package/examples/feature/forest-plot/{broken.json → linear.json} +55 -50
- package/examples/feature/forest-plot/logarithmic.json +306 -0
- package/examples/feature/line/line-points.json +340 -0
- package/examples/feature/regions/index.json +462 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
- package/examples/sparkline-multilple.json +846 -0
- package/index.html +10 -6
- package/package.json +3 -3
- package/src/CdcChart.tsx +75 -63
- package/src/_stories/Chart.stories.tsx +188 -0
- package/src/_stories/Chart.tooltip.stories.tsx +305 -0
- package/src/_stories/ChartBrush.stories.tsx +19 -0
- package/src/_stories/ChartSuppress.stories.tsx +19 -0
- package/src/_stories/_mock/brush_mock.json +393 -0
- package/src/_stories/_mock/suppress_mock.json +911 -0
- package/src/components/AreaChart.Stacked.jsx +4 -5
- package/src/components/AreaChart.jsx +6 -35
- package/src/components/{BarChart.Horizontal.jsx → BarChart.Horizontal.tsx} +106 -29
- package/src/components/{BarChart.StackedHorizontal.jsx → BarChart.StackedHorizontal.tsx} +22 -17
- package/src/components/{BarChart.StackedVertical.jsx → BarChart.StackedVertical.tsx} +22 -26
- package/src/components/{BarChart.Vertical.jsx → BarChart.Vertical.tsx} +175 -31
- package/src/components/BarChart.jsx +1 -1
- package/src/components/DeviationBar.jsx +4 -3
- package/src/components/EditorPanel.jsx +176 -50
- package/src/components/ForestPlot/ForestPlotProps.ts +11 -0
- package/src/components/ForestPlot/Readme.md +0 -0
- package/src/components/ForestPlot/index.scss +1 -0
- package/src/components/{ForestPlot.jsx → ForestPlot/index.tsx} +51 -31
- package/src/components/ForestPlotSettings.jsx +162 -113
- package/src/components/Legend.jsx +36 -3
- package/src/components/{LineChart.Circle.tsx → LineChart/LineChart.Circle.tsx} +26 -29
- package/src/components/LineChart/LineChartProps.ts +17 -0
- package/src/components/LineChart/index.scss +1 -0
- package/src/components/{LineChart.tsx → LineChart/index.tsx} +64 -35
- package/src/components/LinearChart.jsx +120 -60
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/Series.jsx +22 -3
- package/src/components/ZoomBrush.tsx +168 -0
- package/src/data/initial-state.js +27 -12
- package/src/hooks/useBarChart.js +71 -7
- package/src/hooks/useColorScale.ts +50 -0
- package/src/hooks/useEditorPermissions.js +22 -4
- package/src/hooks/{useMinMax.js → useMinMax.ts} +75 -23
- package/src/hooks/{useRightAxis.js → useRightAxis.ts} +10 -2
- package/src/hooks/{useScales.js → useScales.ts} +64 -17
- package/src/hooks/useTooltip.jsx +68 -41
- package/src/scss/main.scss +3 -35
- package/src/types/ChartConfig.ts +43 -0
- package/src/types/ChartContext.ts +38 -0
- package/src/types/ChartProps.ts +7 -0
- package/src/types/ForestPlot.ts +60 -0
|
@@ -2,6 +2,8 @@ import React, { useContext, memo, useState, useEffect } from 'react'
|
|
|
2
2
|
import ConfigContext from '../ConfigContext'
|
|
3
3
|
import { useDebounce } from 'use-debounce'
|
|
4
4
|
import WarningImage from '../images/warning.svg'
|
|
5
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
6
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
5
7
|
|
|
6
8
|
import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
7
9
|
|
|
@@ -180,7 +182,7 @@ const ForestPlotSettings = () => {
|
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
if (section === 'forestPlot' && subsection) {
|
|
183
|
-
|
|
185
|
+
let newConfig = {
|
|
184
186
|
...config,
|
|
185
187
|
[section]: {
|
|
186
188
|
...config[section],
|
|
@@ -189,7 +191,9 @@ const ForestPlotSettings = () => {
|
|
|
189
191
|
[fieldName]: newValue
|
|
190
192
|
}
|
|
191
193
|
}
|
|
192
|
-
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
updateConfig(newConfig)
|
|
193
197
|
return
|
|
194
198
|
}
|
|
195
199
|
|
|
@@ -244,116 +248,99 @@ const ForestPlotSettings = () => {
|
|
|
244
248
|
</AccordionItemButton>
|
|
245
249
|
</AccordionItemHeading>
|
|
246
250
|
<AccordionItemPanel>
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
min={0}
|
|
255
|
-
max={100}
|
|
256
|
-
value={config.forestPlot.width || ''}
|
|
257
|
-
onChange={e => {
|
|
251
|
+
<Select
|
|
252
|
+
value={config.forestPlot.type}
|
|
253
|
+
label='Forest Plot Type'
|
|
254
|
+
initial={'Select'}
|
|
255
|
+
required={true}
|
|
256
|
+
onChange={e => {
|
|
257
|
+
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
258
258
|
updateConfig({
|
|
259
259
|
...config,
|
|
260
260
|
forestPlot: {
|
|
261
261
|
...config.forestPlot,
|
|
262
|
-
|
|
262
|
+
type: e.target.value
|
|
263
263
|
}
|
|
264
264
|
})
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
}
|
|
266
|
+
e.target.value = ''
|
|
267
|
+
}}
|
|
268
|
+
options={['Linear', 'Logarithmic']}
|
|
269
|
+
tooltip={
|
|
270
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
271
|
+
<Tooltip.Target>
|
|
272
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
273
|
+
</Tooltip.Target>
|
|
274
|
+
<Tooltip.Content>
|
|
275
|
+
<p>
|
|
276
|
+
Linear - Typically used for continuous outcomes. Line of no effect is positioned on 0 (zero) <br />
|
|
277
|
+
<br /> Logarithmic - Typically used for binary outcomes such as risk ratios and odds ratios. Line of no effect is positioned on 1.
|
|
278
|
+
</p>
|
|
279
|
+
</Tooltip.Content>
|
|
280
|
+
</Tooltip>
|
|
281
|
+
}
|
|
282
|
+
/>
|
|
268
283
|
|
|
269
|
-
<label
|
|
270
|
-
<span className='edit-label column-heading'>Chart Offset Left (%)</span>
|
|
271
|
-
<input
|
|
272
|
-
type='number'
|
|
273
|
-
min={0}
|
|
274
|
-
max={100}
|
|
275
|
-
value={config.forestPlot.leftWidthOffset || 0}
|
|
276
|
-
onChange={e => {
|
|
277
|
-
updateConfig({
|
|
278
|
-
...config,
|
|
279
|
-
forestPlot: {
|
|
280
|
-
...config.forestPlot,
|
|
281
|
-
leftWidthOffset: e.target.value
|
|
282
|
-
}
|
|
283
|
-
})
|
|
284
|
-
}}
|
|
285
|
-
/>
|
|
286
|
-
</label>
|
|
284
|
+
<TextField type='text' value={config.forestPlot?.title || ''} updateField={updateField} section='forestPlot' fieldName='title' label='Plot Title' />
|
|
287
285
|
|
|
288
|
-
<
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
min={0}
|
|
293
|
-
max={100}
|
|
294
|
-
value={config.forestPlot.leftWidthOffsetMobile || 0}
|
|
295
|
-
onChange={e => {
|
|
296
|
-
updateConfig({
|
|
297
|
-
...config,
|
|
298
|
-
forestPlot: {
|
|
299
|
-
...config.forestPlot,
|
|
300
|
-
leftWidthOffsetMobile: e.target.value
|
|
301
|
-
}
|
|
302
|
-
})
|
|
303
|
-
}}
|
|
304
|
-
/>
|
|
305
|
-
</label>
|
|
286
|
+
<br />
|
|
287
|
+
<hr />
|
|
288
|
+
<br />
|
|
289
|
+
<h4>Column Settings</h4>
|
|
306
290
|
|
|
307
|
-
<
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
value
|
|
314
|
-
onChange={e => {
|
|
291
|
+
<Select
|
|
292
|
+
value={config.forestPlot.estimateField}
|
|
293
|
+
label='Point Estimate Column'
|
|
294
|
+
initial={config.forestPlot.estimateField || 'Select'}
|
|
295
|
+
required={true}
|
|
296
|
+
onChange={e => {
|
|
297
|
+
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
315
298
|
updateConfig({
|
|
316
299
|
...config,
|
|
317
300
|
forestPlot: {
|
|
318
301
|
...config.forestPlot,
|
|
319
|
-
|
|
302
|
+
estimateField: e.target.value
|
|
320
303
|
}
|
|
321
304
|
})
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
305
|
+
}
|
|
306
|
+
e.target.value = ''
|
|
307
|
+
}}
|
|
308
|
+
options={getColumns(false)}
|
|
309
|
+
/>
|
|
325
310
|
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
value={config.forestPlot.rightWidthOffsetMobile || 0}
|
|
333
|
-
onChange={e => {
|
|
311
|
+
<Select
|
|
312
|
+
value={config.forestPlot.shape}
|
|
313
|
+
label='Point Estimate Shape'
|
|
314
|
+
initial={config.forestPlot.shape || 'Select'}
|
|
315
|
+
onChange={e => {
|
|
316
|
+
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
334
317
|
updateConfig({
|
|
335
318
|
...config,
|
|
336
319
|
forestPlot: {
|
|
337
320
|
...config.forestPlot,
|
|
338
|
-
|
|
321
|
+
shape: e.target.value
|
|
339
322
|
}
|
|
340
323
|
})
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
|
|
324
|
+
}
|
|
325
|
+
e.target.value = ''
|
|
326
|
+
}}
|
|
327
|
+
options={['text', 'circle', 'square']}
|
|
328
|
+
/>
|
|
344
329
|
|
|
345
330
|
<Select
|
|
346
|
-
value={config.forestPlot.
|
|
347
|
-
label='
|
|
331
|
+
value={config.forestPlot.radius.scalingColumn}
|
|
332
|
+
label='Scale Radius Column'
|
|
348
333
|
initial={'Select'}
|
|
349
|
-
required={true}
|
|
350
334
|
onChange={e => {
|
|
351
335
|
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
352
336
|
updateConfig({
|
|
353
337
|
...config,
|
|
354
338
|
forestPlot: {
|
|
355
339
|
...config.forestPlot,
|
|
356
|
-
|
|
340
|
+
radius: {
|
|
341
|
+
...config.forestPlot.radius,
|
|
342
|
+
scalingColumn: e.target.value
|
|
343
|
+
}
|
|
357
344
|
}
|
|
358
345
|
})
|
|
359
346
|
}
|
|
@@ -366,7 +353,7 @@ const ForestPlotSettings = () => {
|
|
|
366
353
|
value={config.forestPlot.lower}
|
|
367
354
|
label='Lower CI Column'
|
|
368
355
|
required={true}
|
|
369
|
-
initial={'Select'}
|
|
356
|
+
initial={config.forestPlot.lower || 'Select'}
|
|
370
357
|
onChange={e => {
|
|
371
358
|
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
372
359
|
updateConfig({
|
|
@@ -381,10 +368,11 @@ const ForestPlotSettings = () => {
|
|
|
381
368
|
}}
|
|
382
369
|
options={getColumns(false)}
|
|
383
370
|
/>
|
|
371
|
+
|
|
384
372
|
<Select
|
|
385
373
|
value={config.forestPlot.upper}
|
|
386
374
|
label='Upper CI Column'
|
|
387
|
-
initial={'Select'}
|
|
375
|
+
initial={config.forestPlot.upper || 'Select'}
|
|
388
376
|
required={true}
|
|
389
377
|
onChange={e => {
|
|
390
378
|
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
@@ -401,46 +389,113 @@ const ForestPlotSettings = () => {
|
|
|
401
389
|
options={getColumns(false)}
|
|
402
390
|
/>
|
|
403
391
|
|
|
404
|
-
<CheckBox value={config.forestPlot.showZeroLine} section='forestPlot' fieldName='showZeroLine' label='Show Line on Zero' updateField={updateField} />
|
|
405
392
|
<Select
|
|
406
|
-
value={config.forestPlot.
|
|
407
|
-
label='
|
|
408
|
-
initial={'Select'}
|
|
393
|
+
value={config.forestPlot.pooledResult.column}
|
|
394
|
+
label='Pooled Result Row'
|
|
395
|
+
initial={config.forestPlot.pooledResult.column || 'Select'}
|
|
396
|
+
required={false}
|
|
409
397
|
onChange={e => {
|
|
410
398
|
if (e.target.value !== '' && e.target.value !== 'Select') {
|
|
411
399
|
updateConfig({
|
|
412
400
|
...config,
|
|
413
401
|
forestPlot: {
|
|
414
402
|
...config.forestPlot,
|
|
415
|
-
|
|
403
|
+
pooledResult: {
|
|
404
|
+
...config.forestPlot.pooledResult,
|
|
405
|
+
column: e.target.value
|
|
406
|
+
}
|
|
416
407
|
}
|
|
417
408
|
})
|
|
418
409
|
}
|
|
419
410
|
e.target.value = ''
|
|
420
411
|
}}
|
|
421
|
-
options={['
|
|
412
|
+
options={['None', ...config.data.map(d => d[config.xAxis.dataKey])]}
|
|
422
413
|
/>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
414
|
+
|
|
415
|
+
<CheckBox value={config.forestPlot?.hideDateCategoryCol || false} section='forestPlot' fieldName='hideDateCategoryCol' label='Hide Date Category Column' updateField={updateField} />
|
|
416
|
+
<CheckBox value={config.forestPlot?.lineOfNoEffect?.show || false} section='forestPlot' subsection='lineOfNoEffect' fieldName='show' label='Show Line of No Effect' updateField={updateField} />
|
|
417
|
+
|
|
418
|
+
<br />
|
|
419
|
+
<hr />
|
|
420
|
+
<br />
|
|
421
|
+
<h4>Width Settings</h4>
|
|
422
|
+
|
|
423
|
+
<label>
|
|
424
|
+
<span className='edit-label column-heading'>Chart Offset Left (%)</span>
|
|
425
|
+
<input
|
|
426
|
+
type='number'
|
|
427
|
+
min={0}
|
|
428
|
+
max={100}
|
|
429
|
+
value={config.forestPlot.leftWidthOffset || 0}
|
|
430
|
+
onChange={e => {
|
|
429
431
|
updateConfig({
|
|
430
432
|
...config,
|
|
431
433
|
forestPlot: {
|
|
432
434
|
...config.forestPlot,
|
|
433
|
-
|
|
434
|
-
...config.forestPlot.radius,
|
|
435
|
-
scalingColumn: e.target.value
|
|
436
|
-
}
|
|
435
|
+
leftWidthOffset: e.target.value
|
|
437
436
|
}
|
|
438
437
|
})
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
438
|
+
}}
|
|
439
|
+
/>
|
|
440
|
+
</label>
|
|
441
|
+
|
|
442
|
+
<label>
|
|
443
|
+
<span className='edit-label column-heading'>Chart Offset Left Mobile(%)</span>
|
|
444
|
+
<input
|
|
445
|
+
type='number'
|
|
446
|
+
min={0}
|
|
447
|
+
max={100}
|
|
448
|
+
value={config.forestPlot.leftWidthOffsetMobile || 0}
|
|
449
|
+
onChange={e => {
|
|
450
|
+
updateConfig({
|
|
451
|
+
...config,
|
|
452
|
+
forestPlot: {
|
|
453
|
+
...config.forestPlot,
|
|
454
|
+
leftWidthOffsetMobile: e.target.value
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
}}
|
|
458
|
+
/>
|
|
459
|
+
</label>
|
|
460
|
+
|
|
461
|
+
<label>
|
|
462
|
+
<span className='edit-label column-heading'>Chart Offset Right (%)</span>
|
|
463
|
+
<input
|
|
464
|
+
type='number'
|
|
465
|
+
min={0}
|
|
466
|
+
max={100}
|
|
467
|
+
value={config.forestPlot.rightWidthOffset || 0}
|
|
468
|
+
onChange={e => {
|
|
469
|
+
updateConfig({
|
|
470
|
+
...config,
|
|
471
|
+
forestPlot: {
|
|
472
|
+
...config.forestPlot,
|
|
473
|
+
rightWidthOffset: e.target.value
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
}}
|
|
477
|
+
/>
|
|
478
|
+
</label>
|
|
479
|
+
|
|
480
|
+
<label>
|
|
481
|
+
<span className='edit-label column-heading'>Chart Offset Right Mobile(%)</span>
|
|
482
|
+
<input
|
|
483
|
+
type='number'
|
|
484
|
+
min={0}
|
|
485
|
+
max={100}
|
|
486
|
+
value={config.forestPlot.rightWidthOffsetMobile || 0}
|
|
487
|
+
onChange={e => {
|
|
488
|
+
updateConfig({
|
|
489
|
+
...config,
|
|
490
|
+
forestPlot: {
|
|
491
|
+
...config.forestPlot,
|
|
492
|
+
rightWidthOffsetMobile: e.target.value
|
|
493
|
+
}
|
|
494
|
+
})
|
|
495
|
+
}}
|
|
496
|
+
/>
|
|
497
|
+
</label>
|
|
498
|
+
|
|
444
499
|
<label>
|
|
445
500
|
<span className='edit-label column-heading'>Radius Minimum Size</span>
|
|
446
501
|
<input
|
|
@@ -491,15 +546,9 @@ const ForestPlotSettings = () => {
|
|
|
491
546
|
<br />
|
|
492
547
|
<hr />
|
|
493
548
|
<br />
|
|
494
|
-
<h4>
|
|
495
|
-
<TextField type='
|
|
496
|
-
<TextField type='
|
|
497
|
-
<TextField type='number' value={config.forestPlot?.regression?.estimateField || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='estimateField' label='Estimate Value' />
|
|
498
|
-
<TextField type='text' value={config.forestPlot?.regression?.baseLineColor || 'black'} updateField={updateField} section='forestPlot' subsection='regression' fieldName='baseLineColor' label='Base Color' />
|
|
499
|
-
<CheckBox value={config.forestPlot?.regression?.showBaseLine || false} section='forestPlot' subsection='regression' fieldName='showBaseLine' label='Show base line' updateField={updateField} />
|
|
500
|
-
<CheckBox value={config.forestPlot?.regression?.showDiamond || false} section='forestPlot' subsection='regression' fieldName='showDiamond' label='Show Diamond' updateField={updateField} />
|
|
501
|
-
<CheckBox value={config.forestPlot?.hideDateCategoryCol || false} section='forestPlot' fieldName='hideDateCategoryCol' label='Hide Date Category Column' updateField={updateField} />
|
|
502
|
-
<TextField type='text' value={config.forestPlot?.regression?.description || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='description' label='Description' />
|
|
549
|
+
<h4>Labels Settings</h4>
|
|
550
|
+
<TextField type='text' value={config.forestPlot?.leftLabel || ''} updateField={updateField} section='forestPlot' fieldName='leftLabel' label='Left Label' />
|
|
551
|
+
<TextField type='text' value={config.forestPlot?.rightLabel || ''} updateField={updateField} section='forestPlot' fieldName='rightLabel' label='Right Label' />
|
|
503
552
|
</AccordionItemPanel>
|
|
504
553
|
</AccordionItem>
|
|
505
554
|
)
|
|
@@ -8,6 +8,8 @@ import useLegendClasses from './../hooks/useLegendClasses'
|
|
|
8
8
|
import { useHighlightedBars } from '../hooks/useHighlightedBars'
|
|
9
9
|
import { Line } from '@visx/shape'
|
|
10
10
|
import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
|
|
11
|
+
import { scaleOrdinal } from '@visx/scale'
|
|
12
|
+
import { FaStar } from 'react-icons/fa'
|
|
11
13
|
|
|
12
14
|
// * FILE REVIEW *
|
|
13
15
|
// TODO: fix eslint-disable jsxa11y issues
|
|
@@ -38,6 +40,11 @@ const Legend = () => {
|
|
|
38
40
|
const { visualizationType, visualizationSubType, series, runtime, orientation } = config
|
|
39
41
|
// create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
|
|
40
42
|
const reverseLabels = labels => (config.legend.reverseLabelOrder && config.legend.position === 'bottom' ? labels.reverse() : labels)
|
|
43
|
+
const displayScale = scaleOrdinal({
|
|
44
|
+
domain: config.suppressedData?.map(d => d.label),
|
|
45
|
+
range: ['none'],
|
|
46
|
+
unknown: 'block'
|
|
47
|
+
})
|
|
41
48
|
|
|
42
49
|
const createLegendLabels = defaultLabels => {
|
|
43
50
|
const colorCode = config.legend?.colorCode
|
|
@@ -147,7 +154,7 @@ const Legend = () => {
|
|
|
147
154
|
datum: val,
|
|
148
155
|
index: i,
|
|
149
156
|
text: val,
|
|
150
|
-
value:
|
|
157
|
+
value: colorScale(val)
|
|
151
158
|
}
|
|
152
159
|
return newLabel
|
|
153
160
|
})
|
|
@@ -155,6 +162,29 @@ const Legend = () => {
|
|
|
155
162
|
return reverseLabels(uniqueLabels)
|
|
156
163
|
}
|
|
157
164
|
|
|
165
|
+
if ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && config.visualizationSubType === 'regular' && config.suppressedData) {
|
|
166
|
+
const lastIndex = defaultLabels.length - 1
|
|
167
|
+
let newLabels = []
|
|
168
|
+
|
|
169
|
+
config.suppressedData?.forEach(({ label, icon, value }, index) => {
|
|
170
|
+
const dataExists = data.some(d => {
|
|
171
|
+
return runtime.seriesKeys.some(column => d[column] === value)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
if (label && icon) {
|
|
175
|
+
const newLabel = {
|
|
176
|
+
datum: label,
|
|
177
|
+
index: lastIndex + index,
|
|
178
|
+
text: label,
|
|
179
|
+
icon: <FaStar color='#000' size={15} />
|
|
180
|
+
}
|
|
181
|
+
newLabels.push(newLabel)
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
return [...defaultLabels, ...newLabels]
|
|
186
|
+
}
|
|
187
|
+
|
|
158
188
|
return reverseLabels(defaultLabels)
|
|
159
189
|
}
|
|
160
190
|
|
|
@@ -162,7 +192,7 @@ const Legend = () => {
|
|
|
162
192
|
|
|
163
193
|
const legendClasses = {
|
|
164
194
|
marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
|
|
165
|
-
marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `${config.dynamicMarginTop + 15}px`
|
|
195
|
+
marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `${isBottomOrSmallViewport ? config.dynamicMarginTop + 15 : 0}px`
|
|
166
196
|
}
|
|
167
197
|
|
|
168
198
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
@@ -220,7 +250,10 @@ const Legend = () => {
|
|
|
220
250
|
<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 : '')} />
|
|
221
251
|
</svg>
|
|
222
252
|
) : (
|
|
223
|
-
<
|
|
253
|
+
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
254
|
+
<LegendCircle margin='0' fill={label.value} display={displayScale(label.datum)} />
|
|
255
|
+
<div style={{ marginTop: '2px', marginRight: '6px' }}>{label.icon}</div>
|
|
256
|
+
</div>
|
|
224
257
|
)}
|
|
225
258
|
|
|
226
259
|
<LegendLabel align='left' margin='0 0 0 4px'>
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
import chroma from 'chroma-js'
|
|
3
|
+
import { type ChartConfig } from '../../types/ChartConfig'
|
|
2
4
|
|
|
3
5
|
// todo: change this config obj to ChartConfig once its created
|
|
4
6
|
type LineChartCircleProps = {
|
|
5
|
-
config:
|
|
6
|
-
|
|
7
|
-
data: Object[]
|
|
8
|
-
lineDatapointStyle: string
|
|
9
|
-
runtime: Object
|
|
10
|
-
}
|
|
7
|
+
config: ChartConfig
|
|
8
|
+
data: object[]
|
|
11
9
|
d?: Object
|
|
12
10
|
displayArea: boolean
|
|
13
11
|
seriesKey: string
|
|
@@ -25,30 +23,27 @@ type LineChartCircleProps = {
|
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
const LineChartCircle = (props: LineChartCircleProps) => {
|
|
28
|
-
const { config, d, displayArea, seriesKey, tooltipData, xScale, yScale, colorScale, parseDate, yScaleRight } = props
|
|
26
|
+
const { config, d, displayArea, seriesKey, tooltipData, xScale, yScale, colorScale, parseDate, yScaleRight, data } = props
|
|
29
27
|
const { lineDatapointStyle } = config
|
|
30
28
|
const filtered = config?.series.filter(s => s.dataKey === seriesKey)?.[0]
|
|
29
|
+
// If we're not showing the circle, simply return
|
|
30
|
+
if (lineDatapointStyle === 'hidden') return <></>
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const getIndex = seriesKey => config.runtime.seriesLabelsAll.indexOf(seriesKey)
|
|
33
33
|
|
|
34
|
-
const getColor = (displayArea, colorScale, config
|
|
35
|
-
const customColors = config.customColors || []
|
|
34
|
+
const getColor = (displayArea: boolean, colorScale: Function, config: ChartConfig, hoveredKey: string, seriesKey: string) => {
|
|
36
35
|
const seriesLabels = config.runtime.seriesLabels || []
|
|
37
36
|
let color
|
|
38
37
|
|
|
39
|
-
const getIndex = seriesKey => config.runtime.seriesLabelsAll.indexOf(seriesKey)
|
|
40
|
-
|
|
41
38
|
if (displayArea) {
|
|
42
|
-
|
|
43
|
-
if (getIndex(hoveredKey) === false) return
|
|
44
|
-
color = customColors.length > 0 ? customColors[getIndex(hoveredKey)] : colorScale(seriesLabels[hoveredKey] || seriesKey)
|
|
45
|
-
} else if (customColors) {
|
|
46
|
-
color = customColors.length > 0 ? customColors[getIndex(hoveredKey)] : 'transparent'
|
|
47
|
-
}
|
|
39
|
+
color = colorScale(seriesLabels[hoveredKey] || seriesKey)
|
|
48
40
|
} else {
|
|
49
41
|
color = 'transparent'
|
|
50
42
|
}
|
|
51
|
-
|
|
43
|
+
|
|
44
|
+
if (config.lineDatapointColor === 'Lighter than Line' && color !== 'transparent' && color) {
|
|
45
|
+
color = chroma(color).brighten(1)
|
|
46
|
+
}
|
|
52
47
|
return color
|
|
53
48
|
}
|
|
54
49
|
|
|
@@ -60,7 +55,7 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
60
55
|
r={4.5}
|
|
61
56
|
opacity={d[seriesKey] ? 1 : 0}
|
|
62
57
|
fillOpacity={1}
|
|
63
|
-
fill={displayArea
|
|
58
|
+
fill={getColor(displayArea, colorScale, config, seriesKey, seriesKey)}
|
|
64
59
|
style={{ filter: 'unset', opacity: 1 }}
|
|
65
60
|
/>
|
|
66
61
|
)
|
|
@@ -68,23 +63,24 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
68
63
|
|
|
69
64
|
if (lineDatapointStyle === 'hover') {
|
|
70
65
|
if (!tooltipData) return
|
|
66
|
+
if (!seriesKey) return
|
|
71
67
|
if (!tooltipData.data) return
|
|
72
68
|
let hoveredXValue = tooltipData?.data?.[0]?.[1]
|
|
73
69
|
if (!hoveredXValue) return
|
|
70
|
+
|
|
74
71
|
let hoveredSeriesValue
|
|
75
72
|
let hoveredSeriesIndex
|
|
76
73
|
let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
|
|
77
74
|
let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
|
|
78
75
|
let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
console.log('hoveredSeriesValue', hoveredSeriesValue)
|
|
76
|
+
if (!hoveredSeriesKey) return
|
|
77
|
+
hoveredSeriesIndex = tooltipData?.data.indexOf(hoveredSeriesKey)
|
|
78
|
+
hoveredSeriesValue = data?.find(d => {
|
|
79
|
+
return d[config?.xAxis.dataKey] === hoveredXValue
|
|
80
|
+
})?.[seriesKey]
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
return tooltipData
|
|
82
|
+
// hoveredSeriesValue = extractNumber(hoveredSeriesValue)
|
|
83
|
+
return tooltipData?.data.map((tooltipItem, index) => {
|
|
88
84
|
let seriesIndex = config.runtime.seriesLabelsAll.indexOf(hoveredXValue)
|
|
89
85
|
|
|
90
86
|
if (isNaN(hoveredSeriesValue)) return <></>
|
|
@@ -95,8 +91,9 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
95
91
|
r={4.5}
|
|
96
92
|
opacity={1}
|
|
97
93
|
fillOpacity={1}
|
|
98
|
-
fill={getColor(displayArea, colorScale, config,
|
|
94
|
+
fill={getColor(displayArea, colorScale, config, hoveredSeriesKey, seriesKey)}
|
|
99
95
|
style={{ filter: 'unset', opacity: 1 }}
|
|
96
|
+
key={`line-chart-circle--${JSON.stringify(tooltipItem)}--${index}`}
|
|
100
97
|
/>
|
|
101
98
|
)
|
|
102
99
|
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// todo: review tooltipData type
|
|
2
|
+
// todo: review svgRef type
|
|
3
|
+
export type LineChartProps = {
|
|
4
|
+
xScale: Function
|
|
5
|
+
yScale: Function
|
|
6
|
+
getXAxisData: Function
|
|
7
|
+
getYAxisData: Function
|
|
8
|
+
xMax: number
|
|
9
|
+
yMax: number
|
|
10
|
+
handleTooltipMouseOver: Function
|
|
11
|
+
handleTooltipMouseOff: Function
|
|
12
|
+
showTooltip: boolean
|
|
13
|
+
seriesStyle: String
|
|
14
|
+
svgRef: any
|
|
15
|
+
handleTooltipClick: Function
|
|
16
|
+
tooltipData: any
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Line Chart Styles...
|