Skip to content

Commit

Permalink
feat(D3 plugin): Stacked percentage charts (bar-x, bar-y, area) (#391)
Browse files Browse the repository at this point in the history
* feat(D3 plugin): percent stacking

* fix review(1)
  • Loading branch information
kuzmadom authored Jan 24, 2024
1 parent da86362 commit 590f342
Show file tree
Hide file tree
Showing 19 changed files with 343 additions and 18 deletions.
3 changes: 2 additions & 1 deletion src/i18n/keysets/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"label_invalid-axis-datetime-data-point": "It seems you are trying to use inappropriate data type for \"{{key}}\" value in series \"{{seriesName}}\" for axis with type \"datetime\". Only numbers are allowed.",
"label_invalid-axis-linear-data-point": "It seems you are trying to use inappropriate data type for \"{{key}}\" value in series \"{{seriesName}}\" for axis with type \"linear\". Numbers and nulls are allowed.",
"label_invalid-pie-data-value": "It seems you are trying to use inappropriate data type for \"value\" value. Only numbers are allowed.",
"label_invalid-series-type": "It seems you haven't defined \"series.type\" property, or defined it incorrectly. Available values: [{{types}}]."
"label_invalid-series-type": "It seems you haven't defined \"series.type\" property, or defined it incorrectly. Available values: [{{types}}].",
"label_invalid-series-property": "It seems you are trying to use inappropriate value for \"{{key}}\", or defined it incorrectly. Available values: [{{values}}]."
},
"highcharts": {
"reset-zoom-title": "Reset zoom",
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/keysets/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"label_invalid-axis-datetime-data-point": "Похоже, что вы пытаетесь использовать недопустимый тип данных для значения \"{{key}}\" в серии \"{{seriesName}}\" для оси с типом \"datetime\". Допускается только использование чисел.",
"label_invalid-axis-linear-data-point": "Похоже, что вы пытаетесь использовать недопустимый тип данных для значения \"{{key}}\" в серии \"{{seriesName}}\" для оси с типом \"linear\". Допускается использование чисел и значений null.",
"label_invalid-pie-data-value": "Похоже, что вы пытаетесь использовать недопустимый тип данных для значения \"value\". Допускается только использование чисел.",
"label_invalid-series-type": "Похоже, что вы не указали значение \"series.type\" или указали его неверно. Доступные значения: [{{types}}]."
"label_invalid-series-type": "Похоже, что вы не указали значение \"series.type\" или указали его неверно. Доступные значения: [{{types}}].",
"label_invalid-series-property": "Похоже, что вы пытаетесь использовать недопустимое значение для \"{{key}}\", или указали его неверно. Доступные значения: [{{values}}]."
},
"highcharts": {
"reset-zoom-title": "Сбросить увеличение",
Expand Down
19 changes: 17 additions & 2 deletions src/plugins/d3/__stories__/Showcase.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {D3Plugin} from '../index';
import {BasicBarXChart} from '../examples/bar-x/Basic';
import {GroupedColumns} from '../examples/bar-x/GroupedColumns';
import {StackedColumns} from '../examples/bar-x/StackedColumns';
import {PercentStackColumns} from '../examples/bar-x/PercentStack';
import {DataLabels as BarXDataLabels} from '../examples/bar-x/DataLabels';
import {Basic as BasicBarY} from '../examples/bar-y/Basic';
import {GroupedColumns as GroupedColumnsBarY} from '../examples/bar-y/GroupedColumns';
Expand All @@ -23,6 +24,8 @@ import {Donut} from '../examples/pie/Donut';
import {LineAndBarXCombinedChart} from '../examples/combined/LineAndBarX';
import {LineWithMarkers} from '../examples/line/LineWithMarkers';
import {StackedArea} from '../examples/area/StackedArea';
import {PercentStackingBars} from '../examples/bar-y/PercentStacking';
import {PercentStackingArea} from '../examples/area/PercentStacking';

const ShowcaseStory = () => {
const [loading, setLoading] = React.useState(true);
Expand Down Expand Up @@ -71,6 +74,10 @@ const ShowcaseStory = () => {
<Text variant="subheader-1">Stacked area</Text>
<StackedArea />
</Col>
<Col s={12} m={6}>
<Text variant="subheader-1">Stacked percentage areas</Text>
<PercentStackingArea />
</Col>
</Row>
<Row space={1}>
<Text variant="header-2">Bar-x charts</Text>
Expand All @@ -85,9 +92,13 @@ const ShowcaseStory = () => {
<GroupedColumns />
</Col>
<Col s={12} m={6}>
<Text variant="subheader-1">Stacked columns</Text>
<Text variant="subheader-1">Stacked columns(normal)</Text>
<StackedColumns />
</Col>
<Col s={12} m={6}>
<Text variant="subheader-1">Stacked percentage column</Text>
<PercentStackColumns />
</Col>
<Col s={12} m={6}>
<Text variant="subheader-1">Bar-x chart with data labels</Text>
<BarXDataLabels />
Expand All @@ -105,10 +116,14 @@ const ShowcaseStory = () => {
<Text variant="subheader-1">Grouped bars</Text>
<GroupedColumnsBarY />
</Col>
<Col s={12} m={12}>
<Col s={12} m={6}>
<Text variant="subheader-1">Stacked bars</Text>
<StackedColumnsBarY />
</Col>
<Col s={12} m={6}>
<Text variant="subheader-1">Stacked percentage bars</Text>
<PercentStackingBars />
</Col>
</Row>
<Row space={1}>
<Text variant="header-2">Pie charts</Text>
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/d3/__stories__/area/Basic.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {settings} from '../../../../libs';
import {D3Plugin} from '../..';
import {Basic} from '../../examples/area/Basic';
import {StackedArea} from '../../examples/area/StackedArea';
import {PercentStackingArea} from '../../examples/area/PercentStacking';

const ChartStory = ({Chart}: {Chart: React.FC}) => {
const [shown, setShown] = React.useState(false);
Expand Down Expand Up @@ -41,6 +42,13 @@ export const StackedAreaChartStory: StoryObj<typeof ChartStory> = {
},
};

export const PercentStackingAreaChartStory: StoryObj<typeof ChartStory> = {
name: 'Stacked percentage areas',
args: {
Chart: PercentStackingArea,
},
};

export default {
title: 'Plugins/D3/Area',
decorators: [withKnobs],
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/d3/__stories__/bar-x/BarX.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '../../examples/bar-x/Basic';
import {GroupedColumns} from '../../examples/bar-x/GroupedColumns';
import {StackedColumns} from '../../examples/bar-x/StackedColumns';
import {PercentStackColumns} from '../../examples/bar-x/PercentStack';

const ChartStory = ({Chart}: {Chart: React.FC}) => {
const [shown, setShown] = React.useState(false);
Expand Down Expand Up @@ -67,6 +68,13 @@ export const StackedBarXChartStory: StoryObj<typeof ChartStory> = {
},
};

export const PercentStackBarXChartStory: StoryObj<typeof ChartStory> = {
name: 'Stacked percentage columns',
args: {
Chart: PercentStackColumns,
},
};

export default {
title: 'Plugins/D3/Bar-X',
decorators: [withKnobs],
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/d3/__stories__/bar-y/BarY.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {D3Plugin} from '../..';
import {Basic} from '../../examples/bar-y/Basic';
import {GroupedColumns} from '../../examples/bar-y/GroupedColumns';
import {StackedColumns} from '../../examples/bar-y/StackedColumns';
import {PercentStackingBars} from '../../examples/bar-y/PercentStacking';

const ChartStory = ({Chart}: {Chart: React.FC}) => {
const [shown, setShown] = React.useState(false);
Expand Down Expand Up @@ -48,6 +49,13 @@ export const StackedBarYChartStory: StoryObj<typeof ChartStory> = {
},
};

export const PercentStackingBarYChartStory: StoryObj<typeof ChartStory> = {
name: 'Stacked percentage bars',
args: {
Chart: PercentStackingBars,
},
};

export default {
title: 'Plugins/D3/Bar-Y',
component: ChartStory,
Expand Down
69 changes: 69 additions & 0 deletions src/plugins/d3/examples/area/PercentStacking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {groups} from 'd3';
import React from 'react';
import {ChartKit} from '../../../../components/ChartKit';
import type {ChartKitWidgetData, AreaSeries, AreaSeriesData} from '../../../../types';
import {ExampleWrapper} from '../ExampleWrapper';
import nintendoGames from '../nintendoGames';

const years = Array.from(
new Set(
nintendoGames.map((d) =>
d.date ? String(new Date(d.date as number).getFullYear()) : 'unknown',
),
),
).sort();

function prepareData() {
const grouped = groups(
nintendoGames,
(d) => d.platform,
(d) => (d.date ? String(new Date(d.date as number).getFullYear()) : 'unknown'),
);
const series = grouped.map(([platform, gamesByYear]) => {
const platformGames = Object.fromEntries(gamesByYear) || {};
return {
name: platform,
data: years.reduce<AreaSeriesData[]>((acc, year) => {
if (year in platformGames) {
acc.push({
x: year,
y: platformGames[year].length,
});
}

return acc;
}, []),
};
});

return {series};
}

export const PercentStackingArea = () => {
const {series} = prepareData();

const data = series.map((s) => {
return {
type: 'area',
stacking: 'percent',
name: s.name,
data: s.data,
} as AreaSeries;
});

const widgetData: ChartKitWidgetData = {
series: {
data: data,
},
xAxis: {
type: 'category',
categories: years,
},
};

return (
<ExampleWrapper>
<ChartKit type="d3" data={widgetData} />
</ExampleWrapper>
);
};
61 changes: 61 additions & 0 deletions src/plugins/d3/examples/bar-x/PercentStack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import {groups} from 'd3';
import {ChartKit} from '../../../../components/ChartKit';
import type {BarXSeries, ChartKitWidgetData} from '../../../../types';
import {ExampleWrapper} from '../ExampleWrapper';
import nintendoGames from '../nintendoGames';

function prepareData() {
const grouped = groups(
nintendoGames,
(d) => d.platform,
(d) => (d.date ? new Date(d.date as number).getFullYear() : 'unknown'),
);
const categories: string[] = [];
const series = grouped.map(([platform, years]) => {
return {
name: platform,
data: years.map(([year, list]) => {
categories.push(String(year));

return {
x: String(year),
y: list.length,
};
}),
};
});

return {categories, series};
}

export const PercentStackColumns = () => {
const {series, categories} = prepareData();
const data = series.map((s) => {
return {
type: 'bar-x',
stacking: 'percent',
name: s.name,
data: s.data,
} as BarXSeries;
});

const widgetData: ChartKitWidgetData = {
series: {
data: data,
},
xAxis: {
type: 'category',
categories: categories.sort(),
title: {
text: 'Release year',
},
},
};

return (
<ExampleWrapper>
<ChartKit type="d3" data={widgetData} />
</ExampleWrapper>
);
};
63 changes: 63 additions & 0 deletions src/plugins/d3/examples/bar-y/PercentStacking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import {groups} from 'd3';
import {ChartKit} from '../../../../components/ChartKit';
import type {BarYSeries, ChartKitWidgetData} from '../../../../types';
import {ExampleWrapper} from '../ExampleWrapper';
import nintendoGames from '../nintendoGames';

function prepareData() {
const grouped = groups(
nintendoGames,
(d) => d.platform,
(d) => (d.date ? new Date(d.date as number).getFullYear() : 'unknown'),
);
const categories: string[] = [];
const series = grouped.map(([platform, years]) => {
return {
name: platform,
data: years.map(([year, list]) => {
categories.push(String(year));

return {
y: String(year),
x: list.length,
};
}),
};
});

return {categories, series};
}

export const PercentStackingBars = () => {
const {series, categories} = prepareData();
const data = series.map((s) => {
return {
type: 'bar-y',
stacking: 'percent',
name: s.name,
data: s.data,
} as BarYSeries;
});

const widgetData: ChartKitWidgetData = {
series: {
data: data,
},
yAxis: [
{
type: 'category',
categories: categories.sort(),
title: {
text: 'Release year',
},
},
],
};

return (
<ExampleWrapper>
<ChartKit type="d3" data={widgetData} />
</ExampleWrapper>
);
};
2 changes: 2 additions & 0 deletions src/plugins/d3/renderer/hooks/useSeries/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export type PreparedBarXSeries = {
type: BarXSeries['type'];
data: BarXSeriesData[];
stackId: string;
stacking: BarXSeries['stacking'];
dataLabels: {
enabled: boolean;
inside: boolean;
Expand All @@ -105,6 +106,7 @@ export type PreparedBarYSeries = {
type: BarYSeries['type'];
data: BarYSeriesData[];
stackId: string;
stacking: BarYSeries['stacking'];
dataLabels: {
enabled: boolean;
inside: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/d3/renderer/hooks/useSeries/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function getSeriesStackId(series: StackedSeries) {
let stackId = series.stackId;

if (!stackId) {
stackId = series.stacking === 'normal' ? getCommonStackId() : getRandomCKId();
stackId = series.stacking ? getCommonStackId() : getRandomCKId();
}

return stackId;
Expand Down
Loading

0 comments on commit 590f342

Please sign in to comment.