Skip to content

Commit

Permalink
Merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
artemipanchuk committed Dec 20, 2023
2 parents 4cd4bd2 + ee0ada1 commit 9c88bd9
Show file tree
Hide file tree
Showing 37 changed files with 1,318 additions and 92 deletions.
7 changes: 6 additions & 1 deletion src/i18n/keysets/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
"error": {
"label_no-data": "No data",
"label_unknown-plugin": "Unknown plugin type \"{{type}}\"",
"label_unknown-error": "Unknown error"
"label_unknown-error": "Unknown error",
"label_invalid-axis-category-data-point": "It seems you are trying to use inappropriate data type for \"{{key}}\" value in series \"{{seriesName}}\" for axis with type \"category\". Strings and numbers are allowed.",
"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}}]."
},
"highcharts": {
"reset-zoom-title": "Reset zoom",
Expand Down
7 changes: 6 additions & 1 deletion src/i18n/keysets/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@
"error": {
"label_no-data": "Нет данных",
"label_unknown-plugin": "Неизвестный тип плагина \"{{type}}\"",
"label_unknown-error": "Неизвестная ошибка"
"label_unknown-error": "Неизвестная ошибка",
"label_invalid-axis-category-data-point": "Похоже, что вы пытаетесь использовать недопустимый тип данных для значения \"{{key}}\" в серии \"{{seriesName}}\" для оси с типом \"category\". Допускается использование строк и чисел.",
"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}}]."
},
"highcharts": {
"reset-zoom-title": "Сбросить увеличение",
Expand Down
1 change: 1 addition & 0 deletions src/libs/chartkit-error/chartkit-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type ChartKitErrorArgs = {

export const CHARTKIT_ERROR_CODE = {
NO_DATA: 'ERR.CK.NO_DATA',
INVALID_DATA: 'ERR.CK.INVALID_DATA',
UNKNOWN: 'ERR.CK.UNKNOWN_ERROR',
UNKNOWN_PLUGIN: 'ERR.CK.UNKNOWN_PLUGIN',
TOO_MANY_LINES: 'ERR.CK.TOO_MANY_LINES',
Expand Down
16 changes: 16 additions & 0 deletions src/plugins/d3/__stories__/Showcase.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import {Container, Row, Col, Text} from '@gravity-ui/uikit';
import {BasicPie} from '../examples/pie/Basic';
import {Basic as BasicScatter} from '../examples/scatter/Basic';
import {Basic as BasicLine} from '../examples/line/Basic';
import {Basic as BasicArea} from '../examples/area/Basic';
import {DataLabels as LineWithDataLabels} from '../examples/line/DataLabels';
import {Donut} from '../examples/pie/Donut';
import {LineAndBarXCombinedChart} from '../examples/combined/LineAndBarX';
import {LineWithMarkers} from '../examples/line/LineWithMarkers';
import {StackedArea} from '../examples/area/StackedArea';

const ShowcaseStory = () => {
const [loading, setLoading] = React.useState(true);
Expand Down Expand Up @@ -50,6 +52,20 @@ const ShowcaseStory = () => {
<LineWithMarkers />
</Col>
</Row>
<Row space={1}>
<Text variant="header-2">Area charts</Text>
</Row>
<Row space={3} style={{minHeight: 280}}>
<Col>
<Text variant="subheader-1">Basic area chart</Text>
<BasicArea />
</Col>
<Col>
<Text variant="subheader-1">Stacked area</Text>
<StackedArea />
</Col>
<Col></Col>
</Row>
<Row space={1}>
<Text variant="header-2">Bar-x charts</Text>
</Row>
Expand Down
48 changes: 48 additions & 0 deletions src/plugins/d3/__stories__/area/Basic.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import {StoryObj} from '@storybook/react';
import {withKnobs} from '@storybook/addon-knobs';
import {Button} from '@gravity-ui/uikit';
import {settings} from '../../../../libs';
import {D3Plugin} from '../..';
import {Basic} from '../../examples/area/Basic';
import {StackedArea} from '../../examples/area/StackedArea';

const ChartStory = ({Chart}: {Chart: React.FC}) => {
const [shown, setShown] = React.useState(false);

if (!shown) {
settings.set({plugins: [D3Plugin]});
return <Button onClick={() => setShown(true)}>Show chart</Button>;
}

return (
<div
style={{
height: '80vh',
width: '100%',
}}
>
<Chart />
</div>
);
};

export const BasicAreaChartStory: StoryObj<typeof ChartStory> = {
name: 'Basic',
args: {
Chart: Basic,
},
};

export const StackedAreaChartStory: StoryObj<typeof ChartStory> = {
name: 'Stacked',
args: {
Chart: StackedArea,
},
};

export default {
title: 'Plugins/D3/Area',
decorators: [withKnobs],
component: ChartStory,
};
41 changes: 41 additions & 0 deletions src/plugins/d3/examples/area/Basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import {ChartKit} from '../../../../components/ChartKit';
import type {ChartKitWidgetData, AreaSeries, AreaSeriesData} from '../../../../types';
import nintendoGames from '../nintendoGames';

function prepareData(): AreaSeries[] {
const games = nintendoGames.filter((d) => {
return d.date && d.user_score && d.genres.includes('Puzzle');
});

return [
{
name: 'User score',
type: 'area',
data: games.map<AreaSeriesData>((d) => {
return {
x: Number(d.date),
y: Number(d.user_score),
};
}),
},
];
}

export const Basic = () => {
const series = prepareData();

const widgetData: ChartKitWidgetData = {
title: {
text: 'User score (puzzle genre)',
},
series: {
data: series,
},
xAxis: {
type: 'datetime',
},
};

return <ChartKit type="d3" data={widgetData} />;
};
67 changes: 67 additions & 0 deletions src/plugins/d3/examples/area/StackedArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {groups} from 'd3';
import React from 'react';
import {ChartKit} from '../../../../components/ChartKit';
import type {ChartKitWidgetData, AreaSeries, AreaSeriesData} from '../../../../types';
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 StackedArea = () => {
const {series} = prepareData();

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

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

return <ChartKit type="d3" data={widgetData} />;
};
21 changes: 15 additions & 6 deletions src/plugins/d3/examples/bar-x/Basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@ import type {ChartKitWidgetData, BarXSeries, BarXSeriesData} from '../../../../t
import nintendoGames from '../nintendoGames';
import {groups} from 'd3';

function prepareData(field: 'platform' | 'meta_score' | 'date' = 'platform') {
function prepareData(
{field, filterNulls}: {field: 'platform' | 'meta_score' | 'date'; filterNulls?: boolean} = {
field: 'platform',
},
) {
const gamesByPlatform = groups(nintendoGames, (item) => item[field]);
const data = gamesByPlatform.map(([value, games]) => ({
let resultData = gamesByPlatform;

if (filterNulls) {
resultData = gamesByPlatform.filter(([value]) => typeof value === 'number');
}

const data = resultData.map(([value, games]) => ({
x: value,
y: games.length,
}));

return {
categories: gamesByPlatform.map(([key]) => key),
categories: resultData.map(([key]) => key),
series: [
{
data,
Expand All @@ -24,7 +34,6 @@ function prepareData(field: 'platform' | 'meta_score' | 'date' = 'platform') {

export const BasicBarXChart = () => {
const {categories, series} = prepareData();

const widgetData: ChartKitWidgetData = {
series: {
data: series.map<BarXSeries>((s) => ({
Expand All @@ -47,7 +56,7 @@ export const BasicBarXChart = () => {
};

export const BasicLinearBarXChart = () => {
const {series} = prepareData('meta_score');
const {series} = prepareData({field: 'meta_score'});

const widgetData: ChartKitWidgetData = {
series: {
Expand All @@ -68,7 +77,7 @@ export const BasicLinearBarXChart = () => {
};

export const BasicDateTimeBarXChart = () => {
const {series} = prepareData('date');
const {series} = prepareData({field: 'date', filterNulls: true});

const widgetData: ChartKitWidgetData = {
series: {
Expand Down
55 changes: 30 additions & 25 deletions src/plugins/d3/renderer/D3Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import afterFrame from 'afterframe';
import type {ChartKitProps, ChartKitWidgetRef} from '../../../types';
import {getRandomCKId, measurePerformance} from '../../../utils';
import {Chart} from './components';
import {validateData} from './validation';

type ChartDimensions = {
width: number;
Expand All @@ -23,31 +24,6 @@ const D3Widget = React.forwardRef<ChartKitWidgetRef | undefined, ChartKitProps<'
measurePerformance(),
);

React.useLayoutEffect(() => {
if (onChartLoad) {
onChartLoad({});
}
}, [onChartLoad]);

React.useLayoutEffect(() => {
if (dimensions?.width) {
if (!performanceMeasure.current) {
performanceMeasure.current = measurePerformance();
}

afterFrame(() => {
const renderTime = performanceMeasure.current?.end();
onRender?.({
renderTime,
});
onLoad?.({
widgetRendering: renderTime,
});
performanceMeasure.current = null;
});
}
}, [data, onRender, onLoad, dimensions]);

const handleResize = React.useCallback(() => {
const parentElement = ref.current?.parentElement;

Expand Down Expand Up @@ -90,6 +66,35 @@ const D3Widget = React.forwardRef<ChartKitWidgetRef | undefined, ChartKitProps<'
handleResize();
}, [handleResize]);

React.useEffect(() => {
validateData(data);
}, [data]);

React.useLayoutEffect(() => {
if (onChartLoad) {
onChartLoad({});
}
}, [onChartLoad]);

React.useLayoutEffect(() => {
if (dimensions?.width) {
if (!performanceMeasure.current) {
performanceMeasure.current = measurePerformance();
}

afterFrame(() => {
const renderTime = performanceMeasure.current?.end();
onRender?.({
renderTime,
});
onLoad?.({
widgetRendering: renderTime,
});
performanceMeasure.current = null;
});
}
}, [data, onRender, onLoad, dimensions]);

return (
<div
ref={ref}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => {
switch (series.type) {
case 'scatter':
case 'line':
case 'area':
case 'bar-x': {
const xRow = getXRowData(xAxis, data);
const yRow = getYRowData(yAxis, data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const TooltipTriggerArea = (args: Args) => {

const xLineData = React.useMemo(() => {
const result = shapesData
.filter((sd) => sd.series.type === 'line')
.filter((sd) => ['line', 'area'].includes(sd.series.type))
.reduce((acc, sd) => {
return acc.concat(
(sd as PreparedLineData).points.map<XLineData>((d) => ({
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/d3/renderer/constants/defaults/axis.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {ChartKitWidgetAxisType} from '../../../../../types';

export const axisLabelsDefaults = {
margin: 10,
padding: 10,
Expand All @@ -18,3 +20,5 @@ export const yAxisTitleDefaults = {
...axisTitleDefaults,
margin: 8,
};

export const DEFAULT_AXIS_TYPE: ChartKitWidgetAxisType = 'linear';
Loading

0 comments on commit 9c88bd9

Please sign in to comment.