diff --git a/change/@fluentui-react-charting-7925662b-9688-4022-9347-a3e7ebb7c42d.json b/change/@fluentui-react-charting-7925662b-9688-4022-9347-a3e7ebb7c42d.json new file mode 100644 index 00000000000000..f54486e2facf0a --- /dev/null +++ b/change/@fluentui-react-charting-7925662b-9688-4022-9347-a3e7ebb7c42d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "port changes from v9 to v8 using custom github agent", + "packageName": "@fluentui/react-charting", + "email": "anushgupta@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-charts-4fe41249-5868-447b-80ae-10aee8a78584.json b/change/@fluentui-react-charts-4fe41249-5868-447b-80ae-10aee8a78584.json new file mode 100644 index 00000000000000..3b89c70d94ea12 --- /dev/null +++ b/change/@fluentui-react-charts-4fe41249-5868-447b-80ae-10aee8a78584.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix scatter marker issue", + "packageName": "@fluentui/react-charts", + "email": "anushgupta@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charting/etc/react-charting.api.md b/packages/charts/react-charting/etc/react-charting.api.md index ac1517cf42026e..535375c572a2e0 100644 --- a/packages/charts/react-charting/etc/react-charting.api.md +++ b/packages/charts/react-charting/etc/react-charting.api.md @@ -458,6 +458,7 @@ export interface ICartesianChartProps { xAxistickSize?: number; xAxisTitle?: string; xMaxValue?: number; + xMinValue?: number; xScaleType?: AxisScaleType; yAxis?: AxisProps; yAxisAnnotation?: string; diff --git a/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.base.tsx b/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.base.tsx index bbbcc8c24013bc..0a62a2f9d0f623 100644 --- a/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.base.tsx +++ b/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.base.tsx @@ -244,6 +244,8 @@ export class CartesianChartBase containerWidth: this.state.containerWidth, hideTickOverlap: this.props.hideTickOverlap, calcMaxLabelWidth: this._calcMaxLabelWidthWithTransform, + xMinValue: this.props.xMinValue, + xMaxValue: this.props.xMaxValue, ...this.props.xAxis, }; diff --git a/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.types.ts b/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.types.ts index 30ccc50c9a2579..cc4982c4243d58 100644 --- a/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.types.ts +++ b/packages/charts/react-charting/src/components/CommonComponents/CartesianChart.types.ts @@ -322,6 +322,11 @@ export interface ICartesianChartProps { */ yMaxValue?: number; + /** + * minimum data value point in x-axis (for numeric x-axis) + */ + xMinValue?: number; + /** * maximum data value point in x-axis */ diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 29534bad7e9c82..5ff61111ade162 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -212,6 +212,18 @@ const getYMinMaxValues = (series: Data, layout: Partial | undefined) => return {}; }; +const getXMinMaxValues = (series: Data, layout: Partial | undefined) => { + const range = getXAxisProperties(series, layout)?.range; + if (range && range.length === 2) { + return { + xMinValue: range[0], + xMaxValue: range[1], + showRoundOffXTickValues: false, + }; + } + return {}; +}; + const getYAxisProperties = (series: Data, layout: Partial | undefined): Partial | undefined => { return layout?.yaxis; }; @@ -1408,6 +1420,14 @@ export const transformPlotlyJsonToVSBCProps = ( .forEach((shape, shapeIdx) => { const lineColor = shape.line?.color; const resolveX = (val: Datum) => { + if (shape.xref === 'x domain') { + if (val === 0) { + return xCategories[0]; + } + if (val === 1) { + return xCategories[xCategories.length - 1]; + } + } if (typeof val === 'number' && Array.isArray(xCategories)) { if (xCategories[val] !== undefined) { return xCategories[val]; @@ -1480,7 +1500,9 @@ export const transformPlotlyJsonToVSBCProps = ( showYAxisLables: true, noOfCharsToTruncate: 20, showYAxisLablesTooltip: true, + roundedTicks: true, wrapXAxisLables: typeof vsbcData[0]?.xAxisPoint === 'string', + ...getXMinMaxValues(input.data[0], input.layout), ...getTitles(input.layout), ...getAxisCategoryOrderProps(input.data, input.layout), ...getYMinMaxValues(input.data[0], input.layout), @@ -1654,8 +1676,10 @@ export const transformPlotlyJsonToGVBCProps = ( hideTickOverlap: true, hideLegend, roundCorners: true, + roundedTicks: true, wrapXAxisLables: true, showYAxisLables: true, + ...getXMinMaxValues(processedInput.data[0], processedInput.layout), ...getTitles(processedInput.layout), ...getAxisCategoryOrderProps(processedInput.data, processedInput.layout), ...getYMinMaxValues(processedInput.data[0], processedInput.layout), @@ -1769,8 +1793,10 @@ export const transformPlotlyJsonToVBCProps = ( maxBarWidth: 50, hideLegend, roundCorners: true, + roundedTicks: true, wrapXAxisLables: typeof vbcData[0]?.x === 'string', showYAxisLables: true, + ...getXMinMaxValues(input.data[0], input.layout), ...getTitles(input.layout), ...getAxisCategoryOrderProps(input.data, input.layout), ...getYMinMaxValues(input.data[0], input.layout), @@ -2057,6 +2083,8 @@ const transformPlotlyJsonToScatterTraceProps = ( optimizeLargeData: numDataPoints > 1000, wrapXAxisLables: shouldWrapLabels, showYAxisLables: true, + roundedTicks: true, + ...getXMinMaxValues(input.data[0], input.layout), ...getTitles(input.layout), ...getXAxisTickFormat(input.data[0], input.layout), ...yAxisTickFormat, @@ -2074,7 +2102,6 @@ const transformPlotlyJsonToScatterTraceProps = ( } else { return { data: isScatterChart ? scatterChartProps : chartProps, - roundedTicks: true, enableReflow: false, ...commonProps, ...yMinMax, @@ -2166,6 +2193,8 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( noOfCharsToTruncate: 20, showYAxisLablesTooltip: true, roundCorners: true, + roundedTicks: true, + ...getXMinMaxValues(input.data[0], input.layout), ...getTitles(input.layout), ...getAxisCategoryOrderProps(input.data, input.layout), ...getBarProps(input.data, input.layout, true), @@ -2468,6 +2497,14 @@ export const transformPlotlyJsonToSankeyProps = ( colorMap, isDarkTheme, ); + const extractedLinkColors = extractColor( + input.layout?.template?.layout?.colorway, + colorwayType, + link?.color, + colorMap, + isDarkTheme, + ); + const sankeyChartData = { nodes: node.label?.map((label: string, index: number) => { const color = resolveColor( @@ -2487,8 +2524,17 @@ export const transformPlotlyJsonToSankeyProps = ( }), // eslint-disable-next-line @typescript-eslint/no-explicit-any links: validLinks.map((validLink: any, index: number) => { + const color = resolveColor( + extractedLinkColors, + index, + validLink.target, + colorMap, + input.layout?.template?.layout?.colorway, + isDarkTheme, + ); return { ...validLink, + color, }; }), } as ISankeyChartData; diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap index fab3435e8a90b3..aec6ee5d6578a4 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap +++ b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap @@ -334,7 +334,7 @@ exports[`DeclarativeChart Should render areachart in DeclarativeChart 1`] = ` - 3 + 2 + + + + + + + + + - 9 + 10 @@ -396,7 +432,7 @@ exports[`DeclarativeChart Should render areachart in DeclarativeChart 1`] = ` { public render(): JSXElement { const { pie, data } = this.props; - const focusData = this._pieForFocusRing(data); - const piechart = pie(data); + + // Filter out data points with value 0 to avoid gaps in the donut chart + const filteredData = data.filter((d: IChartDataPoint) => d.data !== 0); + + const focusData = this._pieForFocusRing(filteredData.map(d => d.data!)); + const piechart = pie(filteredData); const translate = `translate(${this.props.width / 2}, ${this.props.height / 2})`; const classNames = getClassNames(getStyles, { theme: this.props.theme!, diff --git a/packages/charts/react-charting/src/components/LineChart/LineChart.base.tsx b/packages/charts/react-charting/src/components/LineChart/LineChart.base.tsx index aac681c72efc61..70b8fc9db7d32e 100644 --- a/packages/charts/react-charting/src/components/LineChart/LineChart.base.tsx +++ b/packages/charts/react-charting/src/components/LineChart/LineChart.base.tsx @@ -441,6 +441,8 @@ export class LineChartBase extends React.Component; diff --git a/packages/charts/react-charting/src/utilities/utilities.ts b/packages/charts/react-charting/src/utilities/utilities.ts index 8b72cac8179225..115f1bb4d5a545 100644 --- a/packages/charts/react-charting/src/utilities/utilities.ts +++ b/packages/charts/react-charting/src/utilities/utilities.ts @@ -179,6 +179,8 @@ export interface IXAxisParams extends AxisProps { containerWidth: number; hideTickOverlap?: boolean; calcMaxLabelWidth: (x: (string | number)[]) => number; + xMaxValue?: number; + xMinValue?: number; } export interface ITickParams { tickValues?: Date[] | number[] | string[]; @@ -262,8 +264,12 @@ export function createNumericXAxis( tick0, tickText, } = xAxisParams; + const dStartValue = domainNRangeValues.dStartValue as number; + const dEndValue = domainNRangeValues.dEndValue as number; + const finalXmin = xAxisParams.xMinValue !== undefined ? Math.min(dStartValue, xAxisParams.xMinValue) : dStartValue; + const finalXmax = xAxisParams.xMaxValue !== undefined ? Math.max(dEndValue, xAxisParams.xMaxValue) : dEndValue; const xAxisScale = createNumericScale(scaleType) - .domain([domainNRangeValues.dStartValue, domainNRangeValues.dEndValue]) + .domain([finalXmin, finalXmax]) .range([domainNRangeValues.rStartValue, domainNRangeValues.rEndValue]); showRoundOffXTickValues && xAxisScale.nice(); @@ -1334,12 +1340,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; } @@ -2133,6 +2141,8 @@ export const getDomainPaddingForMarkers = ( minVal: number, maxVal: number, scaleType?: AxisScaleType, + userMinVal?: number, + userMaxVal?: number, ): { start: number; end: number } => { if (scaleType === 'log') { return { @@ -2141,10 +2151,26 @@ 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, }; }; diff --git a/packages/charts/react-charts/library/src/components/LineChart/LineChart.tsx b/packages/charts/react-charts/library/src/components/LineChart/LineChart.tsx index b50811f54c9852..fb07ae138084ae 100644 --- a/packages/charts/react-charts/library/src/components/LineChart/LineChart.tsx +++ b/packages/charts/react-charts/library/src/components/LineChart/LineChart.tsx @@ -549,10 +549,6 @@ export const LineChart: React.FunctionComponent = 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) { diff --git a/packages/charts/react-charts/library/src/components/ScatterChart/ScatterChart.tsx b/packages/charts/react-charts/library/src/components/ScatterChart/ScatterChart.tsx index 1555f8500531a6..41f9dc4ac3561d 100644 --- a/packages/charts/react-charts/library/src/components/ScatterChart/ScatterChart.tsx +++ b/packages/charts/react-charts/library/src/components/ScatterChart/ScatterChart.tsx @@ -391,10 +391,6 @@ export const ScatterChart: React.FunctionComponent = React.fo yScalePrimary: _yAxisScale, xScaleType: props.xScaleType, yScaleType: props.yScaleType, - xMinValue: props.xMinValue, - xMaxValue: props.xMaxValue, - yMinValue: props.yMinValue, - yMaxValue: props.yMaxValue, }) : 0; diff --git a/packages/charts/react-charts/library/src/utilities/utilities.ts b/packages/charts/react-charts/library/src/utilities/utilities.ts index ab1a0c712ce60b..5eef6b620f429a 100644 --- a/packages/charts/react-charts/library/src/utilities/utilities.ts +++ b/packages/charts/react-charts/library/src/utilities/utilities.ts @@ -2304,10 +2304,6 @@ export const getRangeForScatterMarkerSize = ({ xScaleType, yScaleType: primaryYScaleType, secondaryYScaleType, - xMinValue, - xMaxValue, - yMinValue, - yMaxValue, }: { data: LineChartPoints[] | ScatterChartPoints[]; xScale: ScaleContinuousNumeric | ScaleTime; @@ -2317,10 +2313,6 @@ 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 @@ -2332,14 +2324,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, xMinValue, xMaxValue); + const xPadding = getDomainPaddingForMarkers(+xMin, +xMax, xScaleType); 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, yMinValue, yMaxValue); + const yPadding = getDomainPaddingForMarkers(yMin, yMax, yScaleType); const scaleYMin = yMin - yPadding.start; const scaleYMax = yMax + yPadding.end; const yScale = (useSecondaryYScale ? yScaleSecondary : yScalePrimary)!;