Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions integration/tests/stylings_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,45 @@ describe('Stylings stories', () => {
});
});
});
describe('Fit function styling', () => {
it('can style the line', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/mixed-charts--fitting-functions-non-stacked-series&globals=theme:light&knob-Curve=0&knob-End value=none&knob-Explicit value (using Fit.Explicit)=5&knob-dataset=all&knob-fit area color_fit style=rgba(0,0,0,1)&knob-fit area opacity_fit style=0.15&knob-fit line color_fit style=rgba(5, 5, 5, 1)&knob-fit line dash array_fit style=5,10,1,10&knob-fit line opacity_fit style=0.4&knob-fitting function=average&knob-seriesType=line&knob-use series color for area_fit style=true&knob-use series color for line_fit style=&knob-use texture on area_fit style=',
);
});
it('can style the area', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/mixed-charts--fitting-functions-non-stacked-series&globals=theme:light&knob-Curve=0&knob-End value=none&knob-Explicit value (using Fit.Explicit)=5&knob-dataset=all&knob-fit area color_fit style=rgba(0,0,0,1)&knob-fit area opacity_fit style=0.08&knob-fit line color_fit style=rgba(5, 5, 5, 1)&knob-fit line dash array_fit style=5,10,1,10&knob-fit line opacity_fit style=0&knob-fitting function=average&knob-seriesType=area&knob-use series color for area_fit style=&knob-use series color for line_fit style=&knob-use texture on area_fit style=true',
);
});

it('should render interpolated line as non-interpolated lines', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/mixed-charts--fitting-functions-non-stacked-series&globals=theme:light&knob-Curve=0&knob-End value=nearest&knob-Explicit value (using Fit.Explicit)=5&knob-dataset=all&knob-fit area color_fit style=rgba(0,0,0,1)&knob-fit area opacity_fit style=0.3&knob-fit line color_fit style=rgba(5, 5, 5, 1)&knob-fit line dash array_fit style=&knob-fit line opacity_fit style=1&knob-fitting function=linear&knob-seriesType=line&knob-use series color for area_fit style=true&knob-use series color for line_fit style=true&knob-use texture on area_fit style=',
);
});
it('should render interpolated area as non-interpolated areas', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/mixed-charts--fitting-functions-non-stacked-series&globals=theme:light&knob-Curve=0&knob-End value=nearest&knob-Explicit value (using Fit.Explicit)=5&knob-dataset=all&knob-fit area color_fit style=rgba(0,0,0,1)&knob-fit area opacity_fit style=0.3&knob-fit line color_fit style=rgba(5, 5, 5, 1)&knob-fit line dash array_fit style=&knob-fit line opacity_fit style=1&knob-fitting function=linear&knob-seriesType=area&knob-use series color for area_fit style=true&knob-use series color for line_fit style=true&knob-use texture on area_fit style=',
);
});
it('should render style on stacked areas', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/mixed-charts--fitting-functions-stacked-series&globals=theme:light&knob-stackMode=none&knob-dataset=all&knob-fitting function=linear&knob-Curve=0&knob-End value=none&knob-Explicit value (using Fit.Explicit)=5&knob-apply custom fit style=true',
);
});

it('should apply opacity on styled fit series', async () => {
const action = async () => {
await common.moveMouseRelativeToDOMElement({ left: 0, top: 0 }, '.echLegendItem');
};
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/mixed-charts--fitting-functions-stacked-series&globals=theme:light&knob-stackMode=none&knob-dataset=all&knob-fitting function=linear&knob-Curve=0&knob-End value=none&knob-Explicit value (using Fit.Explicit)=5&knob-apply custom fit style=true',
{
action,
waitSelector: common.chartWaitSelector,
},
);
});
});
});
29 changes: 29 additions & 0 deletions packages/charts/api/charts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { default as React_2 } from 'react';
import { ReactChild } from 'react';
import { ReactNode } from 'react';

// @public (undocumented)
export type A = number;

// @public
export type Accessor = AccessorObjectKey | AccessorArrayIndex;

Expand Down Expand Up @@ -111,6 +114,12 @@ export interface ArcStyle {
visible: boolean;
}

// @public (undocumented)
export type AreaFitStyle = Visible & Opacity & {
fill: Color | typeof ColorVariant.Series;
texture?: TexturedStyles;
};

// Warning: (ae-forgotten-export) The symbol "SpecRequiredProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "SpecOptionalProps" needs to be exported by the entry point index.d.ts
//
Expand All @@ -132,6 +141,11 @@ export interface AreaSeriesStyle {
// (undocumented)
area: AreaStyle;
// (undocumented)
fit: {
line: LineFitStyle;
area: AreaFitStyle;
};
// (undocumented)
line: LineStyle;
// (undocumented)
point: PointStyle;
Expand Down Expand Up @@ -1445,6 +1459,11 @@ export interface LinearScale {
type: typeof ScaleType.Linear;
}

// @public (undocumented)
export type LineFitStyle = Visible & Opacity & StrokeDashArray & {
stroke: Color | typeof ColorVariant.Series;
};

// Warning: (ae-forgotten-export) The symbol "SpecRequiredProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "SpecOptionalProps" needs to be exported by the entry point index.d.ts
//
Expand All @@ -1462,6 +1481,10 @@ export type LineSeriesSpec = BasicSeriesSpec & HistogramConfig & {

// @public (undocumented)
export interface LineSeriesStyle {
// (undocumented)
fit: {
line: LineFitStyle;
};
// (undocumented)
line: LineStyle;
// (undocumented)
Expand Down Expand Up @@ -1853,6 +1876,12 @@ export type RenderChangeListener = (isRendered: boolean) => void;
// @public (undocumented)
export type Rendering = 'canvas' | 'svg';

// @public (undocumented)
export type RGB = number;

// @public (undocumented)
export type RgbaTuple = [r: RGB, g: RGB, b: RGB, alpha: A];

// @public (undocumented)
export type Rotation = 0 | 90 | -90 | 180;

Expand Down
68 changes: 53 additions & 15 deletions packages/charts/src/chart_types/xy_chart/renderer/canvas/areas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
* Side Public License, v 1.
*/

import { colorToRgba, overrideOpacity } from '../../../../common/color_library_wrappers';
import { LegendItem } from '../../../../common/legend';
import { Rect } from '../../../../geoms/types';
import { Fill, Rect, Stroke } from '../../../../geoms/types';
import { withContext } from '../../../../renderers/canvas';
import { Rotation } from '../../../../utils/common';
import { ColorVariant, Rotation } from '../../../../utils/common';
import { Dimensions } from '../../../../utils/dimensions';
import { AreaGeometry, PerPanel } from '../../../../utils/geometry';
import { SharedGeometryStateStyle } from '../../../../utils/themes/theme';
import { getGeometryStateStyle } from '../../rendering/utils';
import { getTextureStyles } from '../../utils/texture';
import { renderPoints } from './points';
import { renderLinePaths, renderAreaPath } from './primitives/path';
import { buildAreaStyles } from './styles/area';
Expand All @@ -35,8 +37,8 @@ export function renderAreas(ctx: CanvasRenderingContext2D, imgCanvas: HTMLCanvas

withContext(ctx, () => {
areas.forEach(({ panel, value: area }) => {
const { seriesAreaLineStyle, seriesAreaStyle } = area;
if (seriesAreaStyle.visible) {
const { style } = area;
if (style.area.visible) {
withPanelTransform(
ctx,
panel,
Expand All @@ -46,7 +48,7 @@ export function renderAreas(ctx: CanvasRenderingContext2D, imgCanvas: HTMLCanvas
{ area: clippings, shouldClip: true },
);
}
if (seriesAreaLineStyle.visible) {
if (style.line.visible) {
withPanelTransform(
ctx,
panel,
Expand All @@ -59,8 +61,8 @@ export function renderAreas(ctx: CanvasRenderingContext2D, imgCanvas: HTMLCanvas
});

areas.forEach(({ panel, value: area }) => {
const { seriesPointStyle, seriesIdentifier, points } = area;
const visiblePoints = seriesPointStyle.visible ? points : points.filter(({ orphan }) => orphan);
const { style, seriesIdentifier, points } = area;
const visiblePoints = style.point.visible ? points : points.filter(({ orphan }) => orphan);
if (visiblePoints.length === 0) {
return;
}
Expand All @@ -80,28 +82,64 @@ export function renderAreas(ctx: CanvasRenderingContext2D, imgCanvas: HTMLCanvas
function renderArea(
ctx: CanvasRenderingContext2D,
imgCanvas: HTMLCanvasElement,
glyph: AreaGeometry,
geometry: AreaGeometry,
sharedStyle: SharedGeometryStateStyle,
clippings: Rect,
highlightedLegendItem?: LegendItem,
) {
const { area, color, transform, seriesIdentifier, seriesAreaStyle, clippedRanges, hideClippedRanges } = glyph;
const { area, color, transform, seriesIdentifier, style, clippedRanges, shouldClip } = geometry;
const geometryStateStyle = getGeometryStateStyle(seriesIdentifier, sharedStyle, highlightedLegendItem);
const styles = buildAreaStyles(ctx, imgCanvas, color, seriesAreaStyle, geometryStateStyle);
const areaFill = buildAreaStyles(ctx, imgCanvas, color, style.area, geometryStateStyle);

renderAreaPath(ctx, transform, area, styles, clippedRanges, clippings, hideClippedRanges);
const fitAreaFillColor = style.fit.area.fill === ColorVariant.Series ? color : style.fit.area.fill;
const fitAreaFill: Fill = {
texture: getTextureStyles(ctx, imgCanvas, fitAreaFillColor, geometryStateStyle.opacity, style.fit.area.texture),
color: overrideOpacity(
colorToRgba(fitAreaFillColor),
(opacity) => opacity * geometryStateStyle.opacity * style.fit.area.opacity,
),
};

renderAreaPath(
ctx,
transform,
area,
areaFill,
fitAreaFill,
clippedRanges,
clippings,
shouldClip && style.fit.area.visible,
);
}

function renderAreaLines(
ctx: CanvasRenderingContext2D,
glyph: AreaGeometry,
geometry: AreaGeometry,
sharedStyle: SharedGeometryStateStyle,
clippings: Rect,
highlightedLegendItem?: LegendItem,
) {
const { lines, color, seriesIdentifier, transform, seriesAreaLineStyle, clippedRanges, hideClippedRanges } = glyph;
const { lines, color, seriesIdentifier, transform, style, clippedRanges, shouldClip } = geometry;
const geometryStateStyle = getGeometryStateStyle(seriesIdentifier, sharedStyle, highlightedLegendItem);
const styles = buildLineStyles(color, seriesAreaLineStyle, geometryStateStyle);
const lineStyle = buildLineStyles(color, style.line, geometryStateStyle);

const fitLineStroke: Stroke = {
dash: style.fit.line.dash,
width: style.line.strokeWidth,
color: overrideOpacity(
colorToRgba(style.fit.line.stroke === ColorVariant.Series ? color : style.fit.line.stroke),
(opacity) => opacity * geometryStateStyle.opacity * style.fit.line.opacity,
),
};

renderLinePaths(ctx, transform, lines, styles, clippedRanges, clippings, hideClippedRanges);
renderLinePaths(
ctx,
transform,
lines,
lineStyle,
fitLineStroke,
clippedRanges,
clippings,
shouldClip && style.fit.line.visible,
);
}
37 changes: 29 additions & 8 deletions packages/charts/src/chart_types/xy_chart/renderer/canvas/lines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
* Side Public License, v 1.
*/

import { colorToRgba, overrideOpacity } from '../../../../common/color_library_wrappers';
import { LegendItem } from '../../../../common/legend';
import { Rect } from '../../../../geoms/types';
import { Rect, Stroke } from '../../../../geoms/types';
import { withContext } from '../../../../renderers/canvas';
import { Rotation } from '../../../../utils/common';
import { ColorVariant, Rotation } from '../../../../utils/common';
import { Dimensions } from '../../../../utils/dimensions';
import { LineGeometry, PerPanel } from '../../../../utils/geometry';
import { SharedGeometryStateStyle } from '../../../../utils/themes/theme';
Expand All @@ -35,9 +36,9 @@ export function renderLines(ctx: CanvasRenderingContext2D, props: LineGeometries
const { lines, sharedStyle, highlightedLegendItem, clippings, renderingArea, rotation } = props;

lines.forEach(({ panel, value: line }) => {
const { seriesLineStyle, seriesPointStyle, points } = line;
const { style, points } = line;

if (seriesLineStyle.visible) {
if (style.line.visible) {
withPanelTransform(
ctx,
panel,
Expand All @@ -48,7 +49,7 @@ export function renderLines(ctx: CanvasRenderingContext2D, props: LineGeometries
);
}

const visiblePoints = seriesPointStyle.visible ? points : points.filter(({ orphan }) => orphan);
const visiblePoints = style.point.visible ? points : points.filter(({ orphan }) => orphan);
if (visiblePoints.length === 0) {
return;
}
Expand All @@ -73,8 +74,28 @@ function renderLine(
clippings: Rect,
highlightedLegendItem?: LegendItem,
) {
const { color, transform, seriesIdentifier, seriesLineStyle, clippedRanges, hideClippedRanges } = line;
const { color, transform, seriesIdentifier, style, clippedRanges, shouldClip } = line;
const geometryStyle = getGeometryStateStyle(seriesIdentifier, sharedStyle, highlightedLegendItem);
const stroke = buildLineStyles(color, seriesLineStyle, geometryStyle);
renderLinePaths(ctx, transform, [line.line], stroke, clippedRanges, clippings, hideClippedRanges);

const lineStroke = buildLineStyles(color, style.line, geometryStyle);
const fitLineStrokeColor = style.fit.line.stroke === ColorVariant.Series ? color : style.fit.line.stroke;
const fitLineStroke: Stroke = {
dash: style.fit.line.dash,
width: style.line.strokeWidth,
color: overrideOpacity(
colorToRgba(fitLineStrokeColor),
(opacity) => opacity * geometryStyle.opacity * style.fit.line.opacity,
),
};

renderLinePaths(
ctx,
transform,
[line.line],
lineStroke,
fitLineStroke,
clippedRanges,
clippings,
shouldClip && style.fit.line.visible,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { overrideOpacity, RGBATupleToString } from '../../../../../common/color_library_wrappers';
import { RGBATupleToString } from '../../../../../common/color_library_wrappers';
import { Fill, Rect, Stroke } from '../../../../../geoms/types';
import { withClipRanges } from '../../../../../renderers/canvas';
import { ClippedRanges } from '../../../../../utils/geometry';
Expand All @@ -19,18 +19,19 @@ export function renderLinePaths(
transform: Point,
linePaths: Array<string>,
stroke: Stroke,
fitStroke: Stroke,
clippedRanges: ClippedRanges,
clippings: Rect,
hideClippedRanges = false,
shouldClip: boolean,
) {
withClipRanges(ctx, clippedRanges, clippings, false, () => {
ctx.translate(transform.x, transform.y);
renderMultiLine(ctx, linePaths, stroke);
});
if (clippedRanges.length > 0 && !hideClippedRanges) {
if (clippedRanges.length > 0 && shouldClip) {
withClipRanges(ctx, clippedRanges, clippings, true, () => {
ctx.translate(transform.x, transform.y);
renderMultiLine(ctx, linePaths, { ...stroke, dash: [5, 5] });
renderMultiLine(ctx, linePaths, fitStroke);
});
}
}
Expand All @@ -41,15 +42,14 @@ export function renderAreaPath(
transform: Point,
area: string,
fill: Fill,
fitFill: Fill,
clippedRanges: ClippedRanges,
clippings: Rect,
hideClippedRanges = false,
shouldClip: boolean,
) {
withClipRanges(ctx, clippedRanges, clippings, false, () => renderPathFill(ctx, area, fill, transform));
if (clippedRanges.length > 0 && !hideClippedRanges) {
withClipRanges(ctx, clippedRanges, clippings, true, () =>
renderPathFill(ctx, area, { ...fill, color: overrideOpacity(fill.color, fill.color[3] / 2) }, transform),
);
if (clippedRanges.length > 0 && shouldClip) {
withClipRanges(ctx, clippedRanges, clippings, true, () => renderPathFill(ctx, area, fitFill, transform));
}
}

Expand Down
Loading