Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Charting: Vertical stacked bar chart code refactor. Cartesian chart implemented in base file. #15061

Merged
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,8 @@
{
"type": "minor",
"comment": "Vertical stacked bar chart code refactored - Cartesinan chart impletemented in base file.",
"packageName": "@uifabric/charting",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-09-16T05:19:51.644Z"
}
23 changes: 11 additions & 12 deletions packages/charting/src/components/AreaChart/AreaChart.base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import { area as d3Area, stack as d3Stack, curveMonotoneX as d3CurveBasis } from
import { getId, find } from 'office-ui-fabric-react/lib/Utilities';
import { IPalette } from 'office-ui-fabric-react/lib/Styling';
import {
CartesianChart,
IAreaChartProps,
IChildProps,
IRefArrayData,
IBasestate,
ILineChartDataPoint,
ILineChartPoints,
} from '../AreaChart/index';
} from '@uifabric/charting';
import { warnDeprecations } from 'office-ui-fabric-react/lib/Utilities';
import { calloutData, getXAxisType, ChartTypes, XAxisTypes } from '../../utilities/index';
import { ILegend, Legends } from '../Legends/index';
import { DirectionalHint } from 'office-ui-fabric-react/lib/Callout';
import { calloutData, getXAxisType, ChartTypes } from '../../utilities/index';
import { CartesianChart } from '../CommonComponents/CartesianChart';

const COMPONENT_NAME = 'AREA CHART';
export interface IAreaChartAreaPoint {
xVal: string | number;
values: IAreaChartDataSetPoint;
Expand Down Expand Up @@ -73,6 +75,9 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
activeCircleId: '',
isCircleClicked: false,
};
warnDeprecations(COMPONENT_NAME, props, {
showYAxisGridLines: 'Dont use this property. Lines are drawn by default',
});
this._refArray = [];
this._points = this.props.data.lineChartData ? this.props.data.lineChartData : [];
this._uniqueIdForGraph = getId('areaChart_');
Expand All @@ -96,13 +101,6 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
}

public render(): JSX.Element {
// eslint-disable-next-line deprecation/deprecation
if (this.props.showYAxisGridLines !== undefined) {
// eslint-disable-next-line no-console
console.warn(
'Warning: the prop showYAxisGridLines is deprecated, please do not use it, now lines are shown by default',
);
}
const isXAxisDateType = getXAxisType(this._points);
this._keys = this._createKeys();
this._colors = this._getColors();
Expand All @@ -125,6 +123,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
gapSpace: 15,
isBeakVisible: false,
setInitialFocus: true,
...this.props.calloutProps,
};
return (
<CartesianChart
Expand All @@ -133,8 +132,8 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
chartType={ChartTypes.AreaChart}
calloutProps={calloutProps}
legendBars={legends}
isMultiStackCallout
isXAxisDateType={isXAxisDateType}
isCalloutForStack
xAxisType={isXAxisDateType ? XAxisTypes.DateAxis : XAxisTypes.NumericAxis}
tickParams={tickParams}
maxOfYVal={stackedInfo.maxOfYVal}
getGraphData={this._getGraphData}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import {
ICartesianChartStyleProps,
IModifiedCartesianChartProps,
IYValueHover,
} from './CartesianChart.types';
} from '@uifabric/charting';
import {
createNumericXAxis,
createStringXAxis,
getDomainNRangeValues,
createDateXAxis,
createYAxis,
additionalMarginRight,
IMargins,
getMinMaxOfYAxis,
XAxisTypes,
} from '../../utilities/index';
import { ChartHoverCard } from '../../utilities/ChartHoverCard/index';
import { FocusZone, FocusZoneDirection } from '@fluentui/react-focus';
Expand All @@ -29,6 +31,12 @@ export interface ICartesianChartState {
_height: number;
}

/**
* Cartesian chart used for
* 1.draw X and Y axis of the chart
* 2.Callout
* 3.Fit parent Continer
*/
export class CartesianChartBase extends React.Component<IModifiedCartesianChartProps, ICartesianChartState> {
private _classNames: IProcessedStyleSet<ICartesianChartStyles>;
private chartContainer: HTMLDivElement;
Expand Down Expand Up @@ -73,7 +81,7 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
}

public render(): JSX.Element {
const { calloutProps, isXAxisDateType, points, chartType } = this.props;
const { calloutProps, points, chartType } = this.props;
if (this.props.parentRef) {
this._fitParentContainer();
}
Expand All @@ -86,8 +94,9 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
this.margins,
this.state.containerWidth,
chartType,
isXAxisDateType,
this._isRtl,
this.props.xAxisType,
this.props.barwidth!,
),
xAxisElement: this.xAxisElement!,
showRoundOffXTickValues: true,
Expand All @@ -107,9 +116,34 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
yMinMaxValues: getMinMaxOfYAxis(points, chartType),
};

const xScale = this.props.isXAxisDateType
? createDateXAxis(XAxisParams, this.props.tickParams!, this._isRtl)
: createNumericXAxis(XAxisParams, this._isRtl);
/**
* These scales used for 2 purposes.
* 1. To create x and y axis
* 2. To draw the graph.
* For area/line chart using same scales. For other charts, creating their own scales to draw the graph.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let xScale: any;
switch (this.props.xAxisType!) {
case XAxisTypes.NumericAxis:
xScale = createNumericXAxis(XAxisParams, this._isRtl);
break;
case XAxisTypes.DateAxis:
xScale = createDateXAxis(XAxisParams, this.props.tickParams!, this._isRtl);
break;
case XAxisTypes.StringAxis:
xScale = createStringXAxis(XAxisParams, this.props.tickParams!, this.props.datasetForXAxisDomain!);
break;
default:
xScale = createNumericXAxis(XAxisParams, this._isRtl);
}

/**
* These scales used for 2 purposes.
* 1. To create x and y axis
* 2. To draw the graph.
* For area/line chart using same scales. For other charts, creating their own scales to draw the graph.
*/
const yScale = createYAxis(YAxisParams, this._isRtl);

// Callback function for chart, returns axis
Expand All @@ -131,16 +165,23 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
xScale,
yScale,
});
const yValueHoverSubCountsExists: boolean = this._yValueHoverSubCountsExists(calloutProps.YValueHover);
let focusDirection;
if (this.props.focusZoneDirection === FocusZoneDirection.vertical) {
focusDirection = this.props.focusZoneDirection;
} else if (this.props.focusZoneDirection) {
focusDirection = this.props.focusZoneDirection;
} else {
focusDirection = FocusZoneDirection.horizontal;
}
return (
<div
id={this.idForGraph}
className={this._classNames.root}
role={'presentation'}
ref={(rootElem: HTMLDivElement) => (this.chartContainer = rootElem)}
>
<FocusZone direction={FocusZoneDirection.horizontal}>
<svg width={svgDimensions.width} height={svgDimensions.height}>
<FocusZone direction={focusDirection}>
<svg width={svgDimensions.width} height={svgDimensions.height} style={{ display: 'block' }}>
<g
ref={(e: SVGElement | null) => {
this.xAxisElement = e;
Expand All @@ -166,35 +207,13 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
{this.props.legendBars}
</div>
{!this.props.hideTooltip && calloutProps!.isCalloutVisible && (
// need to handle for single callout (Future purpose)
<Callout {...calloutProps}>
{this.props.isMultiStackCallout ? (
<div className={this._classNames.calloutContentRoot}>
<div
className={this._classNames.calloutDateTimeContainer}
style={yValueHoverSubCountsExists ? { marginBottom: '11px' } : {}}
>
<div className={this._classNames.calloutContentX}>{calloutProps!.hoverXValue} </div>
</div>
<div
className={this._classNames.calloutInfoContainer}
style={yValueHoverSubCountsExists ? { display: 'flex' } : {}}
>
{calloutProps!.YValueHover &&
calloutProps!.YValueHover.map((yValue: IYValueHover, index: number, yValues: IYValueHover[]) => {
const isLast: boolean = index + 1 === yValues.length;
return (
<div
key={`callout-content-${index}`}
style={yValueHoverSubCountsExists ? { display: 'inline-block' } : {}}
>
{this._getCalloutContent(yValue, index, yValueHoverSubCountsExists, isLast)}
</div>
);
})}
</div>
</div>
) : (
{/** Given custom callout, then it will render */}
{this.props.customizedCallout && this.props.customizedCallout}
{/** single x point its corresponding y points of all the bars/lines in chart will render in callout */}
{!this.props.customizedCallout && this.props.isCalloutForStack && this._multiValueCallout(calloutProps)}
{/** single x point its corresponding y point of single line/bar in the chart will render in callout */}
{!this.props.customizedCallout && !this.props.isCalloutForStack && (
<ChartHoverCard
XValue={calloutProps.XValue}
Legend={calloutProps.legend!}
Expand All @@ -208,6 +227,39 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
);
}

// TO DO: Write a common funtional component for Multi value callout and divide sub count method
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _multiValueCallout = (calloutProps: any) => {
const yValueHoverSubCountsExists: boolean = this._yValueHoverSubCountsExists(calloutProps.YValueHover);
return (
<div className={this._classNames.calloutContentRoot}>
<div
className={this._classNames.calloutDateTimeContainer}
style={yValueHoverSubCountsExists ? { marginBottom: '11px' } : {}}
>
<div className={this._classNames.calloutContentX}>{calloutProps!.hoverXValue} </div>
</div>
<div
className={this._classNames.calloutInfoContainer}
style={yValueHoverSubCountsExists ? { display: 'flex' } : {}}
>
{calloutProps!.YValueHover &&
calloutProps!.YValueHover.map((yValue: IYValueHover, index: number, yValues: IYValueHover[]) => {
const isLast: boolean = index + 1 === yValues.length;
return (
<div
key={`callout-content-${index}`}
style={yValueHoverSubCountsExists ? { display: 'inline-block' } : {}}
>
{this._getCalloutContent(yValue, index, yValueHoverSubCountsExists, isLast)}
</div>
);
})}
</div>
</div>
);
};

private _yValueHoverSubCountsExists(yValueHover?: IYValueHover[]) {
if (yValueHover) {
return yValueHover.some(
Expand All @@ -228,6 +280,7 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
y?: number;
color?: string;
yAxisCalloutData?: string | { [id: string]: number };
data?: string | number;
},
index: number,
yValueHoverSubCountsExists: boolean,
Expand All @@ -251,7 +304,7 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
>
<div className={this._classNames.calloutlegendText}> {xValue.legend}</div>
<div className={this._classNames.calloutContentY}>
{xValue.yAxisCalloutData ? xValue.yAxisCalloutData : xValue.y}
{xValue.yAxisCalloutData ? xValue.yAxisCalloutData : xValue.y || xValue.data}
</div>
</div>
</div>
Expand Down Expand Up @@ -281,7 +334,7 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
}
}

private _fitParentContainer(fromDidUpdate?: boolean): void {
private _fitParentContainer(): void {
const { containerWidth, containerHeight } = this.state;

this._reqID = requestAnimationFrame(() => {
Expand Down
Loading