Skip to content

Commit

Permalink
feat(D3 plugin): add logarithmic type for x and y axes (#493)
Browse files Browse the repository at this point in the history
* feat(D3 plugin): add logarithmic type for x and y axes

* fix type
  • Loading branch information
kuzmadom authored Jun 17, 2024
1 parent 99bdfbd commit f633d91
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 10 deletions.
44 changes: 44 additions & 0 deletions src/plugins/d3/__stories__/line/Line.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';

import {StoryObj} from '@storybook/react';

import {D3Plugin} from '../..';
import {Loader} from '../../../../components/Loader/Loader';
import {settings} from '../../../../libs';
import {LineWithLogarithmicAxis} from '../../examples/line/LogarithmicAxis';

const ChartStory = ({Chart}: {Chart: React.FC}) => {
const [loading, setLoading] = React.useState(true);

React.useEffect(() => {
settings.set({plugins: [D3Plugin]});
setLoading(false);
}, []);

if (loading) {
return <Loader />;
}

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

export const LogarithmicAxis: StoryObj<typeof ChartStory> = {
name: 'Logarithmic axis',
args: {
Chart: LineWithLogarithmicAxis,
},
};

export default {
title: 'Plugins/D3/Line',
component: ChartStory,
};
56 changes: 56 additions & 0 deletions src/plugins/d3/examples/line/LogarithmicAxis.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';

import {Col, Container, Row, Text} from '@gravity-ui/uikit';
import {randomNormal} from 'd3';

import {ChartKit} from '../../../../components/ChartKit';
import {ChartKitWidgetData} from '../../../../types';
import {ExampleWrapper} from '../ExampleWrapper';

export const LineWithLogarithmicAxis = () => {
const randomY = randomNormal(0, 100);
const widgetData: ChartKitWidgetData = {
series: {
data: [
{
type: 'line',
name: 'Line series',
data: new Array(25).fill(null).map((_, index) => ({
x: index,
y: Math.abs(randomY()),
})),
},
],
},
};
const lineWidgetData: ChartKitWidgetData = {...widgetData, title: {text: 'line'}};
const logarithmicWidgetData: ChartKitWidgetData = {
...widgetData,
title: {text: 'logarithmic'},
yAxis: [
{
type: 'logarithmic',
},
],
};

return (
<Container spaceRow={5}>
<Row space={1}>
<Text variant="header-2">logarithmic VS line</Text>
</Row>
<Row space={3}>
<Col s={12} m={6}>
<ExampleWrapper>
<ChartKit type="d3" data={lineWidgetData} />
</ExampleWrapper>
</Col>
<Col s={12} m={6}>
<ExampleWrapper>
<ChartKit type="d3" data={logarithmicWidgetData} />
</ExampleWrapper>
</Col>
</Row>
</Container>
);
};
25 changes: 17 additions & 8 deletions src/plugins/d3/renderer/hooks/useAxisScales/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React from 'react';

import {extent, scaleBand, scaleLinear, scaleUtc} from 'd3';
import {extent, scaleBand, scaleLinear, scaleLog, scaleUtc} from 'd3';
import type {ScaleBand, ScaleLinear, ScaleTime} from 'd3';
import get from 'lodash/get';

import {ChartKitWidgetAxis, ChartKitWidgetSeries} from '../../../../../types';
import {
ChartKitWidgetAxis,
ChartKitWidgetAxisType,
ChartKitWidgetSeries,
} from '../../../../../types';
import {DEFAULT_AXIS_TYPE} from '../../constants';
import {
CHART_SERIES_WITH_VOLUME_ON_Y_AXIS,
Expand Down Expand Up @@ -65,13 +69,14 @@ const filterCategoriesByVisibleSeries = (args: {
};

export function createYScale(axis: PreparedAxis, series: PreparedSeries[], boundsHeight: number) {
const yType = get(axis, 'type', DEFAULT_AXIS_TYPE);
const yType: ChartKitWidgetAxisType = get(axis, 'type', DEFAULT_AXIS_TYPE);
const yMin = get(axis, 'min');
const yCategories = get(axis, 'categories');
const yTimestamps = get(axis, 'timestamps');

switch (yType) {
case 'linear': {
case 'linear':
case 'logarithmic': {
const domain = getDomainDataYBySeries(series);
const range = [boundsHeight, boundsHeight * axis.maxPadding];

Expand All @@ -83,7 +88,8 @@ export function createYScale(axis: PreparedAxis, series: PreparedSeries[], bound
yMaxValue = Math.max(yMaxValue, 0);
}

return scaleLinear().domain([yMinValue, yMaxValue]).range(range).nice();
const scaleFn = yType === 'logarithmic' ? scaleLog : scaleLinear;
return scaleFn().domain([yMinValue, yMaxValue]).range(range).nice();
}

break;
Expand Down Expand Up @@ -150,7 +156,7 @@ export function createXScale(
) {
const xMin = get(axis, 'min');
const xMax = getDefaultMaxXAxisValue(series);
const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
const xType: ChartKitWidgetAxisType = get(axis, 'type', DEFAULT_AXIS_TYPE);
const xCategories = get(axis, 'categories');
const xTimestamps = get(axis, 'timestamps');
const maxPadding = get(axis, 'maxPadding', 0);
Expand All @@ -159,15 +165,18 @@ export function createXScale(
const xRange = [0, boundsWidth - xAxisMinPadding];

switch (xType) {
case 'linear': {
case 'linear':
case 'logarithmic': {
const domain = getDomainDataXBySeries(series);

if (isNumericalArrayData(domain)) {
const [domainXMin, domainXMax] = extent(domain) as [number, number];
const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
const xMaxValue =
typeof xMax === 'number' ? Math.max(xMax, domainXMax) : domainXMax;
return scaleLinear().domain([xMinValue, xMaxValue]).range(xRange).nice();

const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
return scaleFn().domain([xMinValue, xMaxValue]).range(xRange).nice();
}

break;
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/d3/renderer/hooks/useChartOptions/y-axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import get from 'lodash/get';
import type {BaseTextStyle, ChartKitWidgetSeries, ChartKitWidgetYAxis} from '../../../../../types';
import {
DEFAULT_AXIS_LABEL_FONT_SIZE,
DEFAULT_AXIS_TYPE,
axisLabelsDefaults,
yAxisTitleDefaults,
} from '../../constants';
Expand Down Expand Up @@ -107,7 +108,7 @@ export const getPreparedYAxis = ({
const titleStyle: BaseTextStyle = {
fontSize: get(axisItem, 'title.style.fontSize', yAxisTitleDefaults.fontSize),
};
const axisType = get(axisItem, 'type', 'linear');
const axisType = get(axisItem, 'type', DEFAULT_AXIS_TYPE);
const preparedAxis: PreparedAxis = {
type: axisType,
labels: {
Expand Down
2 changes: 1 addition & 1 deletion src/types/widget-data/axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {FormatNumberOptions} from '../../plugins/shared';

import type {BaseTextStyle} from './base';

export type ChartKitWidgetAxisType = 'category' | 'datetime' | 'linear';
export type ChartKitWidgetAxisType = 'category' | 'datetime' | 'linear' | 'logarithmic';

export type ChartKitWidgetAxisLabels = {
/** Enable or disable the axis labels. */
Expand Down

0 comments on commit f633d91

Please sign in to comment.