Skip to content

Commit a8ee626

Browse files
Anush2303Anush
andauthored
feat(react-charts): log axis support in line chart and scatter chart (microsoft#35112)
Co-authored-by: Anush <[email protected]>
1 parent 4c80139 commit a8ee626

File tree

19 files changed

+967
-622
lines changed

19 files changed

+967
-622
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "log axis support in linechart and scatter chart",
4+
"packageName": "@fluentui/react-charts",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/charts/react-charts/library/etc/react-charts.api.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ export interface AreaChartStyles extends CartesianChartStyles {
7272
// @public
7373
export type AxisCategoryOrder = 'default' | 'data' | string[] | 'category ascending' | 'category descending' | 'total ascending' | 'total descending' | 'min ascending' | 'min descending' | 'max ascending' | 'max descending' | 'sum ascending' | 'sum descending' | 'mean ascending' | 'mean descending' | 'median ascending' | 'median descending';
7474

75+
// @public
76+
export type AxisScaleType = 'default' | 'log';
77+
7578
// @public (undocumented)
7679
export interface Basestate {
7780
// (undocumented)
@@ -164,6 +167,7 @@ export interface CartesianChartProps {
164167
yMinValue?: number;
165168
yMaxValue?: number;
166169
};
170+
secondaryYScaleType?: AxisScaleType;
167171
showXAxisLablesTooltip?: boolean;
168172
strokeWidth?: number;
169173
styles?: CartesianChartStyles;
@@ -182,13 +186,15 @@ export interface CartesianChartProps {
182186
xAxistickSize?: number;
183187
xAxisTitle?: string;
184188
xMaxValue?: number;
189+
xScaleType?: AxisScaleType;
185190
yAxisAnnotation?: string;
186191
yAxisCategoryOrder?: AxisCategoryOrder;
187192
yAxisTickCount?: number;
188193
yAxisTickFormat?: any;
189194
yAxisTitle?: string;
190195
yMaxValue?: number;
191196
yMinValue?: number;
197+
yScaleType?: AxisScaleType;
192198
}
193199

194200
// @public
@@ -1202,7 +1208,7 @@ export interface ModifiedCartesianChartProps extends CartesianChartProps {
12021208
chartType: ChartTypes;
12031209
children(props: ChildProps): React_2.ReactNode;
12041210
createStringYAxis: (yAxisParams: IYAxisParams, dataPoints: string[], isRtl: boolean, barWidth: number | undefined, chartType?: ChartTypes) => ScaleBand<string>;
1205-
createYAxis: (yAxisParams: IYAxisParams, isRtl: boolean, axisData: IAxisData, isIntegralDataset: boolean, chartType: ChartTypes, useSecondaryYScale?: boolean, roundedTicks?: boolean) => ScaleLinear<number, number, never>;
1211+
createYAxis: (yAxisParams: IYAxisParams, isRtl: boolean, axisData: IAxisData, isIntegralDataset: boolean, chartType: ChartTypes, useSecondaryYScale?: boolean, roundedTicks?: boolean, scaleType?: AxisScaleType) => ScaleLinear<number, number, never>;
12061212
culture?: string;
12071213
customizedCallout?: any;
12081214
datasetForXAxisDomain?: string[];
@@ -1213,7 +1219,7 @@ export interface ModifiedCartesianChartProps extends CartesianChartProps {
12131219
getDomainNRangeValues: (points: LineChartPoints[] | VerticalBarChartDataPoint[] | VerticalStackedBarDataPoint[] | HorizontalBarChartWithAxisDataPoint[] | GroupedVerticalBarChartData[] | HeatMapChartDataPoint[] | GanttChartDataPoint[], margins: Margins, width: number, chartType: ChartTypes, isRTL: boolean, xAxisType: XAxisTypes, barWidth: number, tickValues: Date[] | number[] | string[] | undefined, shiftX: number) => IDomainNRange;
12141220
getGraphData?: any;
12151221
getmargins?: (margins: Margins) => void;
1216-
getMinMaxOfYAxis: (points: LineChartPoints[] | HorizontalBarChartWithAxisDataPoint[] | VerticalBarChartDataPoint[] | DataPoint[] | ScatterChartDataPoint[] | GanttChartDataPoint[], yAxisType: YAxisType | undefined, useSecondaryYScale?: boolean) => {
1222+
getMinMaxOfYAxis: (points: LineChartPoints[] | HorizontalBarChartWithAxisDataPoint[] | VerticalBarChartDataPoint[] | DataPoint[] | ScatterChartPoints[] | GanttChartDataPoint[], yAxisType: YAxisType | undefined, useSecondaryYScale?: boolean) => {
12171223
startValue: number;
12181224
endValue: number;
12191225
};

packages/charts/react-charts/library/src/components/AreaChart/AreaChart.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ import {
3636
findNumericMinMaxOfY,
3737
createNumericYAxis,
3838
IDomainNRange,
39-
domainRangeOfNumericForAreaChart,
40-
domainRangeOfDateForAreaLineVerticalBarChart,
39+
domainRangeOfNumericForAreaLineScatterCharts,
40+
domainRangeOfDateForAreaLineScatterVerticalBarCharts,
4141
createStringYAxis,
4242
useRtl,
4343
YAxisType,
@@ -167,9 +167,9 @@ export const AreaChart: React.FunctionComponent<AreaChartProps> = React.forwardR
167167
) {
168168
let domainNRangeValue: IDomainNRange;
169169
if (xAxisType === XAxisTypes.NumericAxis) {
170-
domainNRangeValue = domainRangeOfNumericForAreaChart(points, margins, width, isRTL);
170+
domainNRangeValue = domainRangeOfNumericForAreaLineScatterCharts(points, margins, width, isRTL);
171171
} else if (xAxisType === XAxisTypes.DateAxis) {
172-
domainNRangeValue = domainRangeOfDateForAreaLineVerticalBarChart(
172+
domainNRangeValue = domainRangeOfDateForAreaLineScatterVerticalBarCharts(
173173
points,
174174
margins,
175175
width,

packages/charts/react-charts/library/src/components/CommonComponents/CartesianChart.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,13 @@ export const CartesianChart: React.FunctionComponent<ModifiedCartesianChartProps
282282
let tickValues: (string | number)[];
283283
switch (props.xAxisType!) {
284284
case XAxisTypes.NumericAxis:
285-
({ xScale, tickValues } = createNumericXAxis(XAxisParams, props.tickParams!, props.chartType, culture));
285+
({ xScale, tickValues } = createNumericXAxis(
286+
XAxisParams,
287+
props.tickParams!,
288+
props.chartType,
289+
culture,
290+
props.xScaleType,
291+
));
286292
break;
287293
case XAxisTypes.DateAxis:
288294
({ xScale, tickValues } = createDateXAxis(
@@ -305,7 +311,13 @@ export const CartesianChart: React.FunctionComponent<ModifiedCartesianChartProps
305311
));
306312
break;
307313
default:
308-
({ xScale, tickValues } = createNumericXAxis(XAxisParams, props.tickParams!, props.chartType, culture));
314+
({ xScale, tickValues } = createNumericXAxis(
315+
XAxisParams,
316+
props.tickParams!,
317+
props.chartType,
318+
culture,
319+
props.xScaleType,
320+
));
309321
}
310322
_xScale = xScale;
311323
_tickValues = tickValues;
@@ -377,6 +389,7 @@ export const CartesianChart: React.FunctionComponent<ModifiedCartesianChartProps
377389
chartType,
378390
true,
379391
props.roundedTicks!,
392+
props.secondaryYScaleType,
380393
);
381394
}
382395
yScalePrimary = props.createYAxis(
@@ -387,6 +400,7 @@ export const CartesianChart: React.FunctionComponent<ModifiedCartesianChartProps
387400
chartType,
388401
false,
389402
props.roundedTicks!,
403+
props.yScaleType,
390404
);
391405
}
392406

packages/charts/react-charts/library/src/components/CommonComponents/CartesianChart.types.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import {
1212
LineChartPoints,
1313
VerticalBarChartDataPoint,
1414
VerticalStackedBarDataPoint,
15-
ScatterChartDataPoint,
15+
ScatterChartPoints,
1616
GanttChartDataPoint,
1717
AxisCategoryOrder,
18+
AxisScaleType,
1819
} from '../../types/index';
1920
import { TimeLocaleDefinition } from 'd3-time-format';
2021
import { ChartPopoverProps } from './ChartPopover.types';
@@ -460,6 +461,24 @@ export interface CartesianChartProps {
460461
* @default 'default'
461462
*/
462463
yAxisCategoryOrder?: AxisCategoryOrder;
464+
465+
/**
466+
* Defines the scale type for the x-axis.
467+
* @default 'default'
468+
*/
469+
xScaleType?: AxisScaleType;
470+
471+
/**
472+
* Defines the scale type for the primary y-axis.
473+
* @default 'default'
474+
*/
475+
yScaleType?: AxisScaleType;
476+
477+
/**
478+
* Defines the scale type for the secondary y-axis.
479+
* @default 'default'
480+
*/
481+
secondaryYScaleType?: AxisScaleType;
463482
}
464483

465484
export interface YValueHover {
@@ -642,7 +661,7 @@ export interface ModifiedCartesianChartProps extends CartesianChartProps {
642661
| HorizontalBarChartWithAxisDataPoint[]
643662
| VerticalBarChartDataPoint[]
644663
| DataPoint[]
645-
| ScatterChartDataPoint[]
664+
| ScatterChartPoints[]
646665
| GanttChartDataPoint[],
647666
yAxisType: YAxisType | undefined,
648667
useSecondaryYScale?: boolean,
@@ -659,6 +678,7 @@ export interface ModifiedCartesianChartProps extends CartesianChartProps {
659678
chartType: ChartTypes,
660679
useSecondaryYScale?: boolean,
661680
roundedTicks?: boolean,
681+
scaleType?: AxisScaleType,
662682
) => ScaleLinear<number, number, never>;
663683

664684
/**

packages/charts/react-charts/library/src/components/DeclarativeChart/PlotlySchemaAdapter.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ import {
6565
isYearArray,
6666
isInvalidValue,
6767
formatToLocaleString,
68+
getAxisIds,
69+
getAxisKey,
6870
} from '@fluentui/chart-utilities';
6971
import { curveCardinal as d3CurveCardinal } from 'd3-shape';
7072
import type { ColorwayType } from './PlotlyColorAdapter';
@@ -894,6 +896,7 @@ const transformPlotlyJsonToScatterTraceProps = (
894896
...getTitles(input.layout),
895897
...getXAxisTickFormat(input.data[0], input.layout),
896898
...yAxisTickFormat,
899+
...getAxisScaleTypeProps(input.data, input.layout),
897900
};
898901

899902
if (isAreaChart) {
@@ -2247,7 +2250,14 @@ const getBarProps = (
22472250
let padding: number | undefined;
22482251

22492252
if (typeof layout?.bargap === 'number') {
2250-
padding = layout.bargap;
2253+
if (layout.bargap >= 0 && layout.bargap <= 1) {
2254+
padding = layout.bargap;
2255+
} else {
2256+
// Plotly uses a default bargap of 0.2, as noted here: https://github.com/plotly/plotly.js/blob/1d5a249e43dd31ae50acf02117a19e5ac97387e9/src/traces/bar/layout_defaults.js#L58.
2257+
// However, we don't use this value as our default padding because it causes the bars to
2258+
// appear disproportionately wide in large containers.
2259+
padding = 0.2;
2260+
}
22512261
}
22522262

22532263
const plotlyBarWidths = data
@@ -2282,3 +2292,42 @@ const getBarProps = (
22822292
xAxisOuterPadding: padding / 2,
22832293
};
22842294
};
2295+
2296+
type GetAxisScaleTypePropsResult = Pick<CartesianChartProps, 'xScaleType' | 'yScaleType' | 'secondaryYScaleType'>;
2297+
2298+
const getAxisScaleTypeProps = (
2299+
data: Data[],
2300+
layout: Partial<Layout> | undefined,
2301+
): Pick<CartesianChartProps, 'xScaleType' | 'yScaleType' | 'secondaryYScaleType'> => {
2302+
const result: GetAxisScaleTypePropsResult = {};
2303+
2304+
// Traces are grouped by their xaxis property, and for each group/subplot, the adapter functions
2305+
// are called with the corresponding filtered data. As a result, all traces passed to an adapter
2306+
// function share the same xaxis.
2307+
let xAxisId: number | undefined;
2308+
const yAxisIds = new Set<number>();
2309+
data.forEach((series: Partial<PlotData>) => {
2310+
const axisIds = getAxisIds(series);
2311+
xAxisId = axisIds.x;
2312+
yAxisIds.add(axisIds.y);
2313+
});
2314+
2315+
const isLogAxis = (axLetter: 'x' | 'y', axId: number) => {
2316+
const axisKey = getAxisKey(axLetter, axId);
2317+
return layout?.[axisKey]?.type === 'log';
2318+
};
2319+
2320+
if (typeof xAxisId === 'number' && isLogAxis('x', xAxisId)) {
2321+
result.xScaleType = 'log';
2322+
}
2323+
2324+
const sortedYAxisIds = Array.from(yAxisIds).sort();
2325+
if (sortedYAxisIds.length > 0 && isLogAxis('y', sortedYAxisIds[0])) {
2326+
result.yScaleType = 'log';
2327+
}
2328+
if (sortedYAxisIds.length > 1 && isLogAxis('y', sortedYAxisIds[1])) {
2329+
result.secondaryYScaleType = 'log';
2330+
}
2331+
2332+
return result;
2333+
};

packages/charts/react-charts/library/src/components/DeclarativeChart/__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5611,8 +5611,8 @@ Object {
56115611
"Apr",
56125612
"May",
56135613
],
5614-
"xAxisInnerPadding": 11.864179406671795,
5615-
"xAxisOuterPadding": 5.9320897033358975,
5614+
"xAxisInnerPadding": 0.2,
5615+
"xAxisOuterPadding": 0.1,
56165616
"xAxisTitle": "",
56175617
"yAxisTitle": "",
56185618
"yMaxValue": 3400,

0 commit comments

Comments
 (0)