diff --git a/packages/ez-core/src/utils/types.ts b/packages/ez-core/src/utils/types.ts
index b2c76da..5e8f9d2 100644
--- a/packages/ez-core/src/utils/types.ts
+++ b/packages/ez-core/src/utils/types.ts
@@ -120,6 +120,7 @@ type CurveConfig = {
export type AreaConfig = CurveConfig & {
curve?: AreaCurve;
fill?: string;
+ opacity?: number;
};
export type LineConfig = CurveConfig & {
diff --git a/packages/ez-dev/jest/snapshots/recipes/area/MultiAreaChart.spec.tsx.snap b/packages/ez-dev/jest/snapshots/recipes/area/MultiAreaChart.spec.tsx.snap
new file mode 100644
index 0000000..2f5e8de
--- /dev/null
+++ b/packages/ez-dev/jest/snapshots/recipes/area/MultiAreaChart.spec.tsx.snap
@@ -0,0 +1,655 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MultiAreaChart renders a multiarea chart 1`] = `
+
+`;
diff --git a/packages/ez-dev/storybook/data.ts b/packages/ez-dev/storybook/data.ts
index a51f0f3..4b07c4d 100644
--- a/packages/ez-dev/storybook/data.ts
+++ b/packages/ez-dev/storybook/data.ts
@@ -9,30 +9,35 @@ export const dimensions = {
export const rawData: RawData = [
{
value: 9,
+ value1: 20,
name: 'Alpha',
id: '1',
v: 2,
},
{
value: 45,
+ value1: 20,
name: 'Beta',
id: '2',
v: 5,
},
{
value: 29,
+ value1: 20,
name: 'Gamma',
id: '3',
v: 10,
},
{
value: 30,
+ value1: 20,
name: 'Delta',
id: '4',
v: 4,
},
{
value: 50,
+ value1: 20,
name: 'Epsilon',
id: '5',
v: 8,
diff --git a/packages/ez-react/src/components/Area.tsx b/packages/ez-react/src/components/Area.tsx
new file mode 100644
index 0000000..7d942b6
--- /dev/null
+++ b/packages/ez-react/src/components/Area.tsx
@@ -0,0 +1,84 @@
+import React, { FC, SVGAttributes, useMemo } from 'react';
+import { AreaConfig, AreaData, MarkerConfig } from 'eazychart-core/src/types';
+import { Point } from '@/components/shapes/Point';
+import { Points } from '@/components/Points';
+import { useColorScale } from '@/components/scales/ColorScale';
+import { AreaPath } from './shapes/Area';
+import { LinePath } from './shapes/LinePath';
+
+export interface AreaProps extends SVGAttributes {
+ xDomainKey: string;
+ yDomainKey: string;
+ area?: AreaConfig;
+ marker?: MarkerConfig;
+}
+
+export const Area: FC = ({
+ xDomainKey,
+ yDomainKey,
+ area = {
+ stroke: '#339999',
+ strokeWidth: 2,
+ curve: 'curveLinear',
+ },
+ marker = {
+ hidden: true,
+ radius: 5,
+ color: '#FFF',
+ },
+}) => {
+ const { colorScale } = useColorScale();
+
+ const color = useMemo(
+ () => (colorScale.isDefined() ? colorScale.scale(yDomainKey) : area.fill),
+ [area.fill, colorScale, yDomainKey]
+ );
+ return (
+ {
+ const lineAreaData: AreaData = shapeData.map((d) => {
+ return {
+ x: d.x,
+ y0: chartDimensions.height,
+ y1: d.y,
+ };
+ });
+ return (
+
+
+
+ {!marker.hidden &&
+ shapeData.map((shapeDatum) => {
+ return (
+
+ );
+ })}
+
+ );
+ },
+ }}
+ />
+ );
+};
diff --git a/packages/ez-react/src/components/StackedBars.tsx b/packages/ez-react/src/components/StackedBars.tsx
new file mode 100644
index 0000000..e693a84
--- /dev/null
+++ b/packages/ez-react/src/components/StackedBars.tsx
@@ -0,0 +1,87 @@
+import React, { FC, SVGAttributes, useMemo } from 'react';
+import { scaleRectangleData } from 'eazychart-core/src';
+import { Bar } from '@/components/shapes/Bar';
+import { useChart } from '@/lib/use-chart';
+import { useCartesianScales } from '@/components/scales/CartesianScale';
+import { useColorScale } from './scales/ColorScale';
+import { RectangleDatum } from 'eazychart-core/src/types';
+
+export interface StackedBarsProps extends SVGAttributes {
+ xDomainKey: string;
+ yDomainKeys: string[];
+}
+
+export const StackedBars: FC = ({
+ xDomainKey,
+ yDomainKeys,
+ ...rest
+}) => {
+ const { data, dimensions, isRTL } = useChart();
+ const { xScale, yScale } = useCartesianScales();
+ const { colorScale } = useColorScale();
+
+ const scaledData = useMemo(() => {
+ return yDomainKeys.reduce((acc, yDomainKey, index) => {
+ if (index === 0) {
+ // @ts-ignore
+ acc[yDomainKey] = scaleRectangleData(
+ data,
+ xDomainKey,
+ yDomainKey,
+ xScale,
+ yScale,
+ colorScale,
+ dimensions,
+ isRTL
+ );
+ return acc;
+ } else {
+ // @ts-ignore
+ acc[yDomainKey] = scaleRectangleData(
+ data,
+ xDomainKey,
+ yDomainKey,
+ xScale,
+ yScale,
+ colorScale,
+ dimensions,
+ isRTL
+ ).map((datum, id) => {
+ // @ts-ignore
+ const height0 = acc[yDomainKeys[index - 1]][id].height;
+ return datum.height > height0
+ ? { ...datum, height: datum.height - height0 }
+ : datum;
+ });
+ return acc;
+ }
+ }, {});
+ }, [
+ data,
+ yDomainKeys,
+ xDomainKey,
+ xScale,
+ yScale,
+ colorScale,
+ dimensions,
+ isRTL,
+ ]);
+
+ return (
+
+ {yDomainKeys.map((yDomainKey) => {
+ // @ts-ignore
+ const shapeData = scaledData[yDomainKey] as RectangleDatum[];
+ const color = colorScale.scale(yDomainKey);
+ return shapeData.map((shapeDatum: RectangleDatum, index: number) => {
+ return (
+
+ );
+ });
+ })}
+
+ );
+};
diff --git a/packages/ez-react/src/components/shapes/Area.tsx b/packages/ez-react/src/components/shapes/Area.tsx
index b92ada2..d92faa9 100644
--- a/packages/ez-react/src/components/shapes/Area.tsx
+++ b/packages/ez-react/src/components/shapes/Area.tsx
@@ -4,13 +4,13 @@ import { defaultColor, generateAreaPath } from 'eazychart-core/src';
import { useAnimation } from '../../lib/use-animation';
import { useChart } from '@/lib/use-chart';
-export interface AreaProps extends SVGAttributes {
+export interface AreaPathProps extends SVGAttributes {
shapeData?: AreaData;
curve?: AreaCurve;
beta?: number;
}
-export const Area: FC = ({
+export const AreaPath: FC = ({
shapeData = [],
curve = 'curveLinear',
beta,
diff --git a/packages/ez-react/src/lib/useToggableDomainKey.ts b/packages/ez-react/src/lib/useToggableDomainKey.ts
index 57e41dc..851e67b 100644
--- a/packages/ez-react/src/lib/useToggableDomainKey.ts
+++ b/packages/ez-react/src/lib/useToggableDomainKey.ts
@@ -1,11 +1,12 @@
import { useCallback, useMemo, useState } from 'react';
import { RawData } from 'eazychart-core/src/types';
-import { getDomainByKeys } from 'eazychart-core/src';
+import { getDomainByKeys } from 'eazychart-core';
export const useToggableDomainKey = (data: RawData, domainKeys: string[]) => {
// Setup a state for the domain keys to make them toggable
const [activeDomainKeys, setActiveDomainKeys] =
useState(domainKeys);
+
// Toggle Y axis domain keys whenever a legend key is clicked
const toggleDomainKey = useCallback(
(key: string, isActive: boolean, _color: string) => {
@@ -19,6 +20,7 @@ export const useToggableDomainKey = (data: RawData, domainKeys: string[]) => {
},
[activeDomainKeys, setActiveDomainKeys]
);
+
// Re-scale the Y axis
const activeDomain = useMemo(
() => getDomainByKeys(activeDomainKeys, data),
diff --git a/packages/ez-react/src/recipes/area/AreaChart.stories.tsx b/packages/ez-react/src/recipes/area/AreaChart.stories.tsx
index 7ac0dd4..a898d6d 100644
--- a/packages/ez-react/src/recipes/area/AreaChart.stories.tsx
+++ b/packages/ez-react/src/recipes/area/AreaChart.stories.tsx
@@ -3,6 +3,7 @@ import { Meta, Story } from '@storybook/react';
import { AreaChart, AreaChartProps } from '@/recipes/area/AreaChart';
import { baseChartArgTypes, ChartWrapper } from '../../lib/storybook-utils';
import { colors, evolutionData } from 'eazychart-dev/storybook/data';
+import { MultiAreaChart, MultiAreaChartProps } from './MultiAreaChart';
const meta: Meta = {
id: '2',
@@ -24,11 +25,19 @@ const Template: Story = (args) => {
);
};
+const MultiAreaTemplate: Story = (args) => {
+ return (
+
+
+
+ );
+};
+
// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test
// https://storybook.js.org/docs/react/workflows/unit-testing
export const Default = Template.bind({});
-Default.args = {
+const defaultArguments = {
area: {
stroke: colors[0],
strokeWidth: 2,
@@ -52,3 +61,16 @@ Default.args = {
},
data: evolutionData,
};
+
+Default.args = defaultArguments;
+
+export const MultiArea = MultiAreaTemplate.bind({});
+
+MultiArea.args = {
+ ...defaultArguments,
+ yAxis: {
+ domainKeys: ['yValue', 'yValue1', 'yValue2'],
+ title: 'Temperature',
+ tickFormat: (d: number) => `${d}°`,
+ },
+};
diff --git a/packages/ez-react/src/recipes/area/AreaChart.tsx b/packages/ez-react/src/recipes/area/AreaChart.tsx
index f0b7416..e0bba73 100644
--- a/packages/ez-react/src/recipes/area/AreaChart.tsx
+++ b/packages/ez-react/src/recipes/area/AreaChart.tsx
@@ -11,17 +11,13 @@ import {
Dimensions,
AreaConfig,
MarkerConfig,
- AreaData,
} from 'eazychart-core/src/types';
import { TooltipProps, Tooltip } from '@/components/addons/tooltip/Tooltip';
import { Chart } from '@/components/Chart';
-import { Points } from '@/components/Points';
import { Axis } from '@/components/scales/Axis';
import { Grid } from '@/components/scales/grid/Grid';
-import { LinePath } from '@/components/shapes/LinePath';
-import { Point } from '@/components/shapes/Point';
-import { Area } from '@/components/shapes/Area';
import { CartesianScale } from '@/components/scales/CartesianScale';
+import { Area } from '@/components/Area';
export interface AreaChartProps extends SVGAttributes {
data: RawData;
@@ -107,50 +103,11 @@ export const AreaChart: FC = ({
}}
>
- {
- const lineAreaData: AreaData = shapeData.map((d) => {
- return {
- x: d.x,
- y0: chartDimensions.height,
- y1: d.y,
- };
- });
- return (
-
-
-
- {!marker.hidden &&
- shapeData.map((shapeDatum) => {
- return (
-
- );
- })}
-
- );
- },
- }}
+ area={area}
+ marker={marker}
/>
{
+ data: RawData;
+ colors?: string[];
+ area?: AreaConfig;
+ marker?: MarkerConfig;
+ animationOptions?: AnimationOptions;
+ padding?: ChartPadding;
+ grid?: GridConfig;
+ isRTL?: boolean;
+ xAxis?: AxisConfig;
+ yAxis?: AxisConfigMulti;
+ dimensions?: Partial;
+ scopedSlots?: {
+ TooltipComponent: FC;
+ LegendComponent: FC;
+ };
+ onResize?: (dimensions: Dimensions) => void;
+}
+
+export const MultiAreaChart: FC = ({
+ data,
+ colors = ['#339999', '#993399', '#333399'],
+ area = {
+ stroke: '#339999',
+ strokeWidth: 2,
+ curve: 'curveLinear',
+ },
+ marker = {
+ hidden: true,
+ radius: 5,
+ color: '#FFF',
+ },
+ animationOptions = {
+ easing: 'easeBack',
+ duration: 400,
+ delay: 0,
+ },
+ padding = {
+ left: 100,
+ bottom: 100,
+ right: 100,
+ top: 100,
+ },
+ grid = {
+ directions: [Direction.HORIZONTAL, Direction.VERTICAL],
+ color: '#a8a8a8',
+ },
+ xAxis = {
+ domainKey: 'xValue',
+ },
+ yAxis = {
+ domainKeys: ['yValue', 'yValue1', 'yValue2'],
+ },
+ isRTL = false,
+ dimensions = {},
+ scopedSlots = {
+ TooltipComponent: Tooltip,
+ LegendComponent: Legend,
+ },
+}) => {
+ const { activeDomainKeys, activeDomain, toggleDomainKey } =
+ useToggableDomainKey(data, yAxis.domainKeys);
+ return (
+
+
+
+
+ {activeDomainKeys.map((yDomainKey) => {
+ return (
+
+ );
+ })}
+
+
+
+
+
+ );
+};
diff --git a/packages/ez-react/src/recipes/column/ColumnChart.stories.tsx b/packages/ez-react/src/recipes/column/ColumnChart.stories.tsx
index 3cc8395..8da296e 100644
--- a/packages/ez-react/src/recipes/column/ColumnChart.stories.tsx
+++ b/packages/ez-react/src/recipes/column/ColumnChart.stories.tsx
@@ -7,6 +7,10 @@ import {
} from '@/recipes/column/LineColumnChart';
import { baseChartArgTypes, ChartWrapper } from '@/lib/storybook-utils';
import { colors, rawData } from 'eazychart-dev/storybook/data';
+import {
+ StackedColumnChart,
+ StackedColumnChartProps,
+} from './StackedColumnChart';
const meta: Meta = {
id: '4',
@@ -36,6 +40,14 @@ const LineColumnTemplate: Story = (args) => {
);
};
+const StackedColumnTemplate: Story = (args) => {
+ return (
+
+
+
+ );
+};
+
// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test
// https://storybook.js.org/docs/react/workflows/unit-testing
export const Default = DefaultTemplate.bind({});
@@ -78,3 +90,16 @@ const lineColumnArguments = {
};
LineColumn.args = lineColumnArguments;
+
+export const StackedColumn = StackedColumnTemplate.bind({});
+
+const StackedColumnArguments = {
+ ...defaultArguments,
+ yAxis: {
+ domainKeys: ['value', 'value1'],
+ title: 'Temperature',
+ tickFormat: (d: number) => `${d}°`,
+ },
+};
+
+StackedColumn.args = StackedColumnArguments;
diff --git a/packages/ez-react/src/recipes/column/StackedColumnChart.tsx b/packages/ez-react/src/recipes/column/StackedColumnChart.tsx
new file mode 100644
index 0000000..8967f48
--- /dev/null
+++ b/packages/ez-react/src/recipes/column/StackedColumnChart.tsx
@@ -0,0 +1,122 @@
+import { Legend, LegendProps } from '@/components/addons/legend/Legend';
+import { Tooltip, TooltipProps } from '@/components/addons/tooltip/Tooltip';
+import { Chart } from '@/components/Chart';
+import { Axis } from '@/components/scales/Axis';
+import { CartesianScale } from '@/components/scales/CartesianScale';
+import { ColorScale } from '@/components/scales/ColorScale';
+import { StackedBars } from '@/components/StackedBars';
+import { useToggableDomainKey } from '@/lib/useToggableDomainKey';
+import { ScaleBand, ScaleLinear } from 'eazychart-core/src/scales';
+import {
+ RawData,
+ AnimationOptions,
+ ChartPadding,
+ AxisConfig,
+ Position,
+ Dimensions,
+ Direction,
+ AxisConfigMulti,
+} from 'eazychart-core/src/types';
+import React, { FC, SVGAttributes } from 'react';
+
+export interface StackedColumnChartProps extends SVGAttributes {
+ data: RawData;
+ colors?: string[];
+ animationOptions?: AnimationOptions;
+ padding?: ChartPadding;
+ isRTL?: boolean;
+ xAxis?: AxisConfig;
+ yAxis?: AxisConfigMulti;
+ dimensions?: Partial;
+ scopedSlots?: {
+ LegendComponent: React.FC;
+ TooltipComponent: React.FC;
+ };
+}
+
+export const StackedColumnChart: FC = ({
+ data,
+ colors = ['#339999', '#993399', '#333399'],
+ animationOptions = {
+ easing: 'easeBack',
+ duration: 400,
+ delay: 0,
+ },
+ padding = {
+ left: 150,
+ bottom: 100,
+ right: 150,
+ top: 100,
+ },
+ xAxis = {
+ domainKey: 'name',
+ position: Position.BOTTOM,
+ },
+ yAxis = {
+ domainKeys: ['value', 'value1'],
+ position: Position.LEFT,
+ },
+ isRTL = false,
+ dimensions = {},
+ scopedSlots = {
+ LegendComponent: Legend,
+ TooltipComponent: Tooltip,
+ },
+}) => {
+ const { activeDomainKeys, activeDomain, toggleDomainKey } =
+ useToggableDomainKey(data, yAxis.domainKeys);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/ez-react/src/recipes/line/LineErrorMarginChart.tsx b/packages/ez-react/src/recipes/line/LineErrorMarginChart.tsx
index c940b71..9dd6f13 100644
--- a/packages/ez-react/src/recipes/line/LineErrorMarginChart.tsx
+++ b/packages/ez-react/src/recipes/line/LineErrorMarginChart.tsx
@@ -23,7 +23,7 @@ import { Axis } from '@/components/scales/Axis';
import { Grid } from '@/components/scales/grid/Grid';
import { LinePath } from '@/components/shapes/LinePath';
import { Point } from '@/components/shapes/Point';
-import { Area } from '@/components/shapes/Area';
+import { AreaPath } from '@/components/shapes/Area';
import { CartesianScale } from '@/components/scales/CartesianScale';
export interface LineErrorMarginChartProps extends SVGAttributes {
@@ -153,7 +153,7 @@ export const LineErrorMarginChart: FC = ({
});
return (
- {
+ it('renders a multiarea chart', async () => {
+ let wrapper: RenderResult;
+ act(() => {
+ wrapper = render(
+
+ );
+ });
+
+ await waitFor(() => {
+ expect(wrapper.container.innerHTML).toMatchSnapshot();
+ });
+ });
+});