Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix double padding issue in scatter chart",
"packageName": "@fluentui/react-charts",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export interface CartesianChartProps {
xMinValue?: number;

/**
* maximum data value point in x-axis (for numeric x-axis)
* maximum data value point in x-axis
*/
xMaxValue?: number;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ export const LineChart: React.FunctionComponent<LineChartProps> = React.forwardR
isRTL,
props.xScaleType,
_hasMarkersMode,
props.xMinValue,
props.xMaxValue,
);
} else if (xAxisType === XAxisTypes.DateAxis) {
domainNRangeValue = domainRangeOfDateForAreaLineScatterVerticalBarCharts(
Expand Down Expand Up @@ -549,6 +551,10 @@ export const LineChart: React.FunctionComponent<LineChartProps> = React.forwardR
xScaleType: props.xScaleType,
yScaleType: props.yScaleType,
secondaryYScaleType: props.secondaryYScaleType,
xMinValue: props.xMinValue,
xMaxValue: props.xMaxValue,
yMinValue: props.yMinValue,
yMaxValue: props.yMaxValue,
})
: 0;
if (_points[i].data.length === 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ export const ScatterChart: React.FunctionComponent<ScatterChartProps> = React.fo
isRTL,
props.xScaleType,
true,
props.xMinValue,
props.xMaxValue,
);
} else if (xAxisType === XAxisTypes.DateAxis) {
domainNRangeValue = domainRangeOfDateForAreaLineScatterVerticalBarCharts(
Expand Down Expand Up @@ -383,15 +385,20 @@ export const ScatterChart: React.FunctionComponent<ScatterChartProps> = React.fo
});
}) ?? 0;
const isContinuousXY = _xAxisType !== XAxisTypes.StringAxis && _yAxisType !== YAxisType.StringAxis;
const extraMaxPixels = isContinuousXY
? getRangeForScatterMarkerSize({
data: _points,
xScale: _xAxisScale,
yScalePrimary: _yAxisScale,
xScaleType: props.xScaleType,
yScaleType: props.yScaleType,
})
: 0;
const extraMaxPixels =
_xAxisType !== XAxisTypes.StringAxis && _yAxisType !== YAxisType.StringAxis
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looks inintentional

? getRangeForScatterMarkerSize({
data: _points,
xScale: _xAxisScale,
yScalePrimary: _yAxisScale,
xScaleType: props.xScaleType,
yScaleType: props.yScaleType,
xMinValue: props.xMinValue,
xMaxValue: props.xMaxValue,
yMinValue: props.yMinValue,
yMaxValue: props.yMaxValue,
})
: 0;

for (let i = _points.length - 1; i >= 0; i--) {
const pointsForSeries: JSXElement[] = [];
Expand Down
39 changes: 33 additions & 6 deletions packages/charts/react-charts/library/src/utilities/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1362,12 +1362,14 @@ export function domainRangeOfNumericForAreaLineScatterCharts(
isRTL: boolean,
scaleType?: AxisScaleType,
hasMarkersMode?: boolean,
xMinVal?: number,
xMaxVal?: number,
): IDomainNRange {
const isScatterPolar = isScatterPolarSeries(points);
let [xMin, xMax] = getScatterXDomainExtent(points, scaleType) as [number, number];

if (hasMarkersMode) {
const xPadding = getDomainPaddingForMarkers(xMin, xMax, scaleType);
const xPadding = getDomainPaddingForMarkers(xMin, xMax, scaleType, xMinVal, xMaxVal);
xMin = xMin - xPadding.start;
xMax = xMax + xPadding.end;
}
Expand Down Expand Up @@ -2206,6 +2208,8 @@ export const getDomainPaddingForMarkers = (
minVal: number,
maxVal: number,
scaleType?: AxisScaleType,
userMinVal?: number,
userMaxVal?: number,
): { start: number; end: number } => {
if (scaleType === 'log') {
return {
Expand All @@ -2214,10 +2218,25 @@ export const getDomainPaddingForMarkers = (
};
}

const defaultPadding = (maxVal - minVal) * 0.1;
/* if user explicitly sets userMinVal or userMaxVal, we will check that to avoid excessive padding on either side.
If the difference between minVal and userMinVal is more than 10% of the data range, we set padding to 0 on that side.
this is to avoid cases where userMinVal is significantly smaller than minVal or userMaxVal is significantly larger than
maxVal, which would lead to excessive padding. In other cases, we apply the default 10% padding on both sides.
*/
const rangePadding = (maxVal - minVal) * 0.1;

// If explicit bounds are set and they're far from the data range, don't add extra padding
const paddingAlreadySatisfiedAtMin =
userMinVal !== undefined && rangePadding > Math.abs(minVal - Math.min(minVal, userMinVal));
const paddingAlreadySatisfiedAtMax =
userMaxVal !== undefined && rangePadding > Math.abs(maxVal - Math.max(maxVal, userMaxVal));

const startPadding = paddingAlreadySatisfiedAtMin ? 0 : rangePadding;
const endPadding = paddingAlreadySatisfiedAtMax ? 0 : rangePadding;

return {
start: defaultPadding,
end: defaultPadding,
start: startPadding,
end: endPadding,
};
};

Expand Down Expand Up @@ -2264,6 +2283,10 @@ export const getRangeForScatterMarkerSize = ({
xScaleType,
yScaleType: primaryYScaleType,
secondaryYScaleType,
xMinValue,
xMaxValue,
yMinValue,
yMaxValue,
}: {
data: LineChartPoints[] | ScatterChartPoints[];
xScale: ScaleContinuousNumeric<number, number> | ScaleTime<number, number>;
Expand All @@ -2273,6 +2296,10 @@ export const getRangeForScatterMarkerSize = ({
xScaleType?: AxisScaleType;
yScaleType?: AxisScaleType;
secondaryYScaleType?: AxisScaleType;
xMinValue?: number;
xMaxValue?: number;
yMinValue?: number;
yMaxValue?: number;
}): number => {
// Note: This function is executed after the scale is created, so the actual padding can be
// obtained by calculating the difference between the respective minimums or maximums of the
Expand All @@ -2284,14 +2311,14 @@ export const getRangeForScatterMarkerSize = ({
// it the other way around (i.e., adjusting the scale domain first with padding and then scaling
// the markers to fit inside the plot area).
const [xMin, xMax] = getScatterXDomainExtent(data, xScaleType);
const xPadding = getDomainPaddingForMarkers(+xMin, +xMax, xScaleType);
const xPadding = getDomainPaddingForMarkers(+xMin, +xMax, xScaleType, xMinValue, xMaxValue);
const scaleXMin = xMin instanceof Date ? new Date(+xMin - xPadding.start) : xMin - xPadding.start;
const scaleXMax = xMax instanceof Date ? new Date(+xMax + xPadding.end) : xMax + xPadding.end;
const extraXPixels = Math.min(Math.abs(xScale(xMin) - xScale(scaleXMin)), Math.abs(xScale(scaleXMax) - xScale(xMax)));

const yScaleType = useSecondaryYScale ? secondaryYScaleType : primaryYScaleType;
const { startValue: yMin, endValue: yMax } = findNumericMinMaxOfY(data, undefined, useSecondaryYScale, yScaleType);
const yPadding = getDomainPaddingForMarkers(yMin, yMax, yScaleType);
const yPadding = getDomainPaddingForMarkers(yMin, yMax, yScaleType, yMinValue, yMaxValue);
const scaleYMin = yMin - yPadding.start;
const scaleYMax = yMax + yPadding.end;
const yScale = (useSecondaryYScale ? yScaleSecondary : yScalePrimary)!;
Expand Down