From 630ce15eb84f27e43a5b80412fdd840817e756db Mon Sep 17 00:00:00 2001 From: mufazalov Date: Thu, 30 Jan 2025 14:59:16 +0300 Subject: [PATCH 1/2] feat: add legendColorKey property to series options --- demo/examples/legend.html | 39 +++++++++++++++++++ docs/en/api/series.md | 4 +- src/YagrCore/index.ts | 21 ++++++++++ src/YagrCore/plugins/legend/legend.ts | 2 +- src/YagrCore/plugins/tooltip/tooltip.ts | 2 +- src/YagrCore/types.ts | 9 +++++ tests/plugins/legend.test.ts | 52 ++++++++++++++++++++++++- tests/plugins/tooltip.test.ts | 44 ++++++++++++++++++++- 8 files changed, 167 insertions(+), 6 deletions(-) diff --git a/demo/examples/legend.html b/demo/examples/legend.html index 235ab632..709117c0 100644 --- a/demo/examples/legend.html +++ b/demo/examples/legend.html @@ -39,6 +39,13 @@

Legend

+

Charts with semi-transparent colors

+
To prevent transparent colors in legend and tooltip, + you can set 'lineColor' to them with legendColorKey serie option
+
+
+
+ diff --git a/docs/en/api/series.md b/docs/en/api/series.md index d7afccfa..3cf3f0b8 100644 --- a/docs/en/api/series.md +++ b/docs/en/api/series.md @@ -24,7 +24,9 @@ Series type adds extra features to [uPlot series](https://github.com/leeoniya/up - `series.width?: number` - line width (line type charts) -- `series.lineColor? string` - line color (area type charts) +- `series.lineColor?: string` - line color (area type charts) + +- `series.legendColorKey?: 'color' | 'lineColor'` - determines which color field to use for serie in legend and tooltip - `series.lineWidth?: number` - line width over area (area type charts) diff --git a/src/YagrCore/index.ts b/src/YagrCore/index.ts index 5d180580..62aa8c85 100644 --- a/src/YagrCore/index.ts +++ b/src/YagrCore/index.ts @@ -225,6 +225,27 @@ class Yagr { return this.uplot.series[this.state.y2uIdx[id]]; } + getSerieLegendColor(serie: Series) { + const {legendColorKey, color, lineColor} = serie; + + let serieColor = color; + + switch (legendColorKey) { + case 'lineColor': { + if (lineColor) { + serieColor = lineColor; + } + break; + } + case 'color': + default: { + serieColor = color; + } + } + + return serieColor; + } + dispose() { this.resizeOb && this.resizeOb.unobserve(this.root); this.unsubscribe(); diff --git a/src/YagrCore/plugins/legend/legend.ts b/src/YagrCore/plugins/legend/legend.ts index a1e508d2..dc548aff 100644 --- a/src/YagrCore/plugins/legend/legend.ts +++ b/src/YagrCore/plugins/legend/legend.ts @@ -420,7 +420,7 @@ export default class LegendPlugin { private createIconLineElement(serie: Series) { const iconLineElement = html('span', { class: `yagr-legend__icon yagr-legend__icon_${serie.type}`, - style: {'background-color': serie.color}, + style: {'background-color': this.yagr.getSerieLegendColor(serie)}, }); return iconLineElement; diff --git a/src/YagrCore/plugins/tooltip/tooltip.ts b/src/YagrCore/plugins/tooltip/tooltip.ts index df1973bf..ebfb508f 100644 --- a/src/YagrCore/plugins/tooltip/tooltip.ts +++ b/src/YagrCore/plugins/tooltip/tooltip.ts @@ -384,7 +384,7 @@ class YagrTooltip { value: displayValue, y: yValue, displayY: realY, - color: serie.color, + color: this.yagr.getSerieLegendColor(serie), seriesIdx, rowIdx: section.rows.length ? section.rows[section.rows.length - 1].rowIdx + 1 : 0, }; diff --git a/src/YagrCore/types.ts b/src/YagrCore/types.ts index 7c27a082..43035df4 100644 --- a/src/YagrCore/types.ts +++ b/src/YagrCore/types.ts @@ -252,6 +252,15 @@ export interface CommonSeriesOptions { * Use at your own risk **/ postProcess?: (data: (number | null)[], idx: number, y: Yagr) => (number | null)[]; + + /** + * Determines what data value should be used to get a color for legend and tooltip. + * - `lineColor` indicates that lineColor property should be used + * - `color` indicates that color property should be used + * + * @default 'color' + */ + legendColorKey?: 'color' | 'lineColor'; } export interface LineSeriesOptions extends CommonSeriesOptions { diff --git a/tests/plugins/legend.test.ts b/tests/plugins/legend.test.ts index 7347a730..77e22671 100644 --- a/tests/plugins/legend.test.ts +++ b/tests/plugins/legend.test.ts @@ -1,5 +1,5 @@ -import { Series } from 'uplot'; -import {MinimalValidConfig} from '../../src'; +import {Series} from 'uplot'; +import {AreaSeriesOptions, ExtendedSeriesOptions, MinimalValidConfig} from '../../src'; import Yagr from '../../src/YagrCore'; import {DEFAULT_X_SERIE_NAME} from '../../src/YagrCore/defaults'; import {hasOneVisibleLine} from '../../src/YagrCore/plugins/legend/legend'; @@ -69,6 +69,54 @@ describe('legend', () => { }); }); + describe('color', () => { + afterEach(() => { + el.innerHTML = ''; + }); + + const serie: ExtendedSeriesOptions & AreaSeriesOptions = { + type: 'area', + data: [1, 2, 3, 4], + id: '1', + color: 'rgba(255, 0, 0, 0.1)', + lineColor: 'rgb(255, 0, 0)', + }; + + const colorTestConfig: MinimalValidConfig = { + series: [serie], + timeline: [1, 2, 3, 4], + legend: { + show: true, + position: 'top', + }, + }; + + it('should use color for legend if there is no legendColorKey', () => { + const y = new Yagr(el, { + ...colorTestConfig, + }); + + expect(y.root.querySelector('.yagr-legend')).toBeTruthy(); + + const legendIcon = y.root.querySelector('.yagr-legend .yagr-legend__icon') as HTMLSpanElement; + + expect(legendIcon.style.background).toBe('rgba(255, 0, 0, 0.1)'); + }); + + it('should use lineColor for legend if legendColorKey is lineColor', () => { + const y = new Yagr(el, { + ...colorTestConfig, + series: [{...colorTestConfig.series[0], legendColorKey: 'lineColor'}], + }); + + expect(y.root.querySelector('.yagr-legend')).toBeTruthy(); + + const legendIcon = y.root.querySelector('.yagr-legend .yagr-legend__icon') as HTMLSpanElement; + + expect(legendIcon.style.background).toBe('rgb(255, 0, 0)'); + }); + }); + describe('basic interaction', () => { it('should toggle series on click', async () => { const y = new Yagr(el, { diff --git a/tests/plugins/tooltip.test.ts b/tests/plugins/tooltip.test.ts index c5ec192f..3915e2d6 100644 --- a/tests/plugins/tooltip.test.ts +++ b/tests/plugins/tooltip.test.ts @@ -1,4 +1,4 @@ -import {MinimalValidConfig, TooltipHandler} from '../../src'; +import {AreaSeriesOptions, ExtendedSeriesOptions, MinimalValidConfig, TooltipHandler} from '../../src'; import Yagr from '../../src/YagrCore'; const gen = (cfg: MinimalValidConfig) => { @@ -49,6 +49,48 @@ describe('tooltip', () => { }); }); + describe('color', () => { + const serie: ExtendedSeriesOptions & AreaSeriesOptions = { + type: 'area', + data: [1, 2, 3, 4], + id: '1', + color: 'rgba(255, 0, 0, 0.1)', + lineColor: 'rgb(255, 0, 0)', + }; + + const colorTestConfig: MinimalValidConfig = { + series: [serie], + timeline: [1, 2, 3, 4], + }; + + it('should use color for tooltip if there is no legendColorKey', () => { + const yagr = gen({...colorTestConfig}); + + yagr.uplot.setCursor({left: 10, top: 10}); + + const tooltipElem = window.document.querySelector(`#${yagr.id}_tooltip`) as HTMLElement; + + const legendIcon = tooltipElem.querySelector('.yagr-tooltip__mark') as HTMLSpanElement; + + expect(legendIcon.style.background).toBe('rgba(255, 0, 0, 0.1)'); + }); + + it('should use lineColor for tooltip if legendColorKey is lineColor', () => { + const yagr = gen({ + ...colorTestConfig, + series: [{...colorTestConfig.series[0], legendColorKey: 'lineColor'}], + }); + + yagr.uplot.setCursor({left: 10, top: 10}); + + const tooltipElem = window.document.querySelector(`#${yagr.id}_tooltip`) as HTMLElement; + + const legendIcon = tooltipElem.querySelector('.yagr-tooltip__mark') as HTMLSpanElement; + + expect(legendIcon.style.background).toBe('rgb(255, 0, 0)'); + }); + }); + describe('perScale', () => { const y = gen({ timeline: [1, 2, 3, 4], From 0cebc5c4785f86648dcd7e726c48f808798fb1e0 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Thu, 30 Jan 2025 17:26:50 +0300 Subject: [PATCH 2/2] fix(getSerieLegendColor): remove default case --- src/YagrCore/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/YagrCore/index.ts b/src/YagrCore/index.ts index 62aa8c85..a4434371 100644 --- a/src/YagrCore/index.ts +++ b/src/YagrCore/index.ts @@ -237,8 +237,7 @@ class Yagr { } break; } - case 'color': - default: { + case 'color': { serieColor = color; } }