storybook/stories/mixed/6_fitting.story.tsx (235 lines of code) (raw):
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { select, boolean, color, number, text } from '@storybook/addon-knobs';
import React from 'react';
import type { RecursivePartial } from '@elastic/charts';
import {
AreaSeries,
Axis,
Chart,
LineSeries,
Position,
ScaleType,
Settings,
Fit,
SeriesType,
LegendValue,
} from '@elastic/charts';
import { ColorVariant } from '../../../packages/charts/src/utils/common';
import type { AreaFitStyle, LineFitStyle } from '../../../packages/charts/src/utils/themes/theme';
import { TextureShape } from '../../../packages/charts/src/utils/themes/theme';
import type { ChartsStory } from '../../types';
import { useBaseTheme } from '../../use_base_theme';
import { customKnobs } from '../utils/knobs';
interface MixedDatum {
x: number | string;
y: number | string | null;
}
export const Example: ChartsStory = (_, { title, description }) => {
const dataTypes: Record<string, MixedDatum[]> = {
isolated: [
{ x: 0, y: 3 },
{ x: 1, y: 5 },
{ x: 2, y: null },
{ x: 3, y: 4 },
{ x: 4, y: null },
{ x: 5, y: 5 },
{ x: 6, y: null },
{ x: 7, y: 12 },
{ x: 8, y: null },
{ x: 9, y: 10 },
{ x: 10, y: 7 },
],
successive: [
{ x: 0, y: 3 },
{ x: 1, y: 5 },
{ x: 2, y: null },
{ x: 4, y: null },
{ x: 6, y: null },
{ x: 8, y: null },
{ x: 9, y: 10 },
{ x: 10, y: 7 },
],
endPoints: [
{ x: 0, y: null },
{ x: 1, y: 5 },
{ x: 3, y: 4 },
{ x: 5, y: 5 },
{ x: 7, y: 12 },
{ x: 9, y: 10 },
{ x: 10, y: null },
],
ordinal: [
{ x: 'a', y: null },
{ x: 'b', y: 3 },
{ x: 'c', y: 5 },
{ x: 'd', y: null },
{ x: 'e', y: 4 },
{ x: 'f', y: null },
{ x: 'g', y: 5 },
{ x: 'h', y: 6 },
{ x: 'i', y: null },
{ x: 'j', y: null },
{ x: 'k', y: null },
{ x: 'l', y: 12 },
{ x: 'm', y: null },
],
all: [
{ x: 0, y: null },
{ x: 1, y: 3 },
{ x: 2, y: 5 },
{ x: 3, y: null },
{ x: 4, y: 4 },
{ x: 5, y: null },
{ x: 6, y: 5 },
{ x: 7, y: 6 },
{ x: 8, y: null },
{ x: 9, y: null },
{ x: 10, y: null },
{ x: 11, y: 12 },
{ x: 12, y: null },
],
};
const seriesType = select<string>(
'seriesType',
{
Area: SeriesType.Area,
Line: SeriesType.Line,
},
SeriesType.Area,
);
const dataKey = select<keyof typeof dataTypes>(
'dataset',
{
'Isolated Points': 'isolated',
'Successive null Points': 'successive',
'null end points': 'endPoints',
'Ordinal x values': 'ordinal',
'All edge cases': 'all',
},
'all',
);
const dataset = dataTypes[dataKey];
const fit = customKnobs.enum.fit();
const curve = customKnobs.enum.curve();
const endValue = select<number | 'none' | 'nearest'>(
'End value',
{
None: 'none',
nearest: 'nearest',
0: 0,
2: 2,
},
'none',
);
const parsedEndValue: number | 'nearest' = Number.isNaN(Number(endValue)) ? 'nearest' : Number(endValue);
const value = number('Explicit value (using Fit.Explicit)', 5);
const xScaleType = dataKey === 'ordinal' ? ScaleType.Ordinal : ScaleType.Linear;
const baseTheme = useBaseTheme();
const useSeriesColorLine = boolean('use series color for line', true, 'fit style');
const customLineColor = color('fit line color', 'rgba(0,0,0,1)', 'fit style');
const fitLineStyle: RecursivePartial<LineFitStyle> = {
opacity: number(
'fit line opacity',
seriesType === SeriesType.Area
? baseTheme.areaSeriesStyle.fit.line.opacity
: baseTheme.lineSeriesStyle.fit.line.opacity,
{
range: true,
min: 0,
max: 1,
step: 0.05,
},
'fit style',
),
stroke: useSeriesColorLine ? ColorVariant.Series : customLineColor,
dash: text(
'fit line dash array',
(seriesType === SeriesType.Area
? baseTheme.areaSeriesStyle.fit.line.dash
: baseTheme.lineSeriesStyle.fit.line.dash
).join(','),
'fit style',
)
.split(',')
.map(Number),
};
const useSeriesColor = boolean('use series color for area', true, 'fit style');
const fitAreaCustomColor = color('fit area color', 'rgba(0,0,0,1)', 'fit style');
const fitAreaOpacity = number(
'fit area opacity',
baseTheme.areaSeriesStyle.fit.area.opacity,
{
range: true,
min: 0,
max: baseTheme.areaSeriesStyle.area.opacity,
step: 0.01,
},
'fit style',
);
const fitAreaStyle: RecursivePartial<AreaFitStyle> = {
opacity: fitAreaOpacity,
fill: useSeriesColor ? ColorVariant.Series : fitAreaCustomColor,
texture: boolean('use texture on area', false, 'fit style')
? { shape: TextureShape.Line, rotation: -45, opacity: fitAreaOpacity }
: undefined,
};
return (
<Chart title={title} description={description}>
<Settings
showLegend
legendValues={[LegendValue.CurrentAndLastValue]}
theme={{
areaSeriesStyle: {
point: {
visible: 'always',
},
},
}}
baseTheme={baseTheme}
/>
<Axis id="bottom" position={Position.Bottom} title="Bottom axis" showOverlappingTicks />
<Axis id="left" title="Left axis" position={Position.Left} />
{seriesType === SeriesType.Area ? (
<AreaSeries
id="test"
xScaleType={xScaleType}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
curve={curve}
fit={{
type: fit,
value: fit === Fit.Explicit ? value : undefined,
endValue: endValue === 'none' ? undefined : parsedEndValue,
}}
areaSeriesStyle={{
fit: {
line: fitLineStyle,
area: fitAreaStyle,
},
}}
data={dataset}
/>
) : (
<LineSeries
id="test"
xScaleType={xScaleType}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
curve={curve}
fit={{
type: fit,
value: fit === Fit.Explicit ? value : undefined,
endValue: endValue === 'none' ? undefined : parsedEndValue,
}}
lineSeriesStyle={{
fit: {
line: fitLineStyle,
},
}}
data={dataset}
/>
)}
</Chart>
);
};