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

Handling central text overflow and adding tooltip for donut charts #26192

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Bug Fix",
"packageName": "@fluentui/react-charting",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,32 @@ export const getStyles = (props: IArcProps): IArcStyles => {
},
},
},
tooltip: {
...theme.fonts.medium,
display: 'flex',
flexDirection: 'column',
padding: '8px',
position: 'absolute',
textAlign: 'center',
top: '0px',
background: theme.semanticColors.bodyBackground,
Copy link
Contributor

Choose a reason for hiding this comment

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

is there a default theme always available

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, the theme is coming as a prop from the parent component, which is a required property

borderRadius: '2px',
pointerEvents: 'none',
},
nodeTextContainer: {
selectors: {
text: {
selectors: {
[HighContrastSelectorBlack]: {
fill: 'rgb(179, 179, 179)',
},
},
},
},
marginTop: '4px',
marginLeft: '8px',
marginBottom: '4px',
marginRight: '8px',
},
};
};
22 changes: 16 additions & 6 deletions packages/react-charting/src/components/DonutChart/Arc/Arc.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react';
import * as shape from 'd3-shape';
import { classNamesFunction } from '@fluentui/react/lib/Utilities';
import { getStyles } from './Arc.styles';
import { IChartDataPoint } from '../index';
import { IArcProps, IArcStyles } from './index';
import { wrapTextInsideDonut } from '../../../utilities/index';
import { IProcessedStyleSet } from '../../../Styling';

export interface IArcState {
isCalloutVisible?: boolean;
Expand All @@ -19,38 +21,44 @@ export class Arc extends React.Component<IArcProps, IArcState> {

public state: {} = {};

private _classNames: IProcessedStyleSet<IArcStyles>;
private currentRef = React.createRef<SVGPathElement>();

public static getDerivedStateFromProps(nextProps: Readonly<IArcProps>): Partial<IArcState> | null {
_updateChart(nextProps);
return null;
}

public constructor(props: IArcProps) {
super(props);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change needed?

}

public updateChart(newProps: IArcProps): void {
_updateChart(newProps);
}

public render(): JSX.Element {
const { arc, href, focusedArcId } = this.props;
const getClassNames = classNamesFunction<IArcProps, IArcStyles>();
const classNames = getClassNames(getStyles, {
this._classNames = getClassNames(getStyles, {
color: this.props.color,
href: href!,
theme: this.props.theme!,
});
const id = this.props.uniqText! + this.props.data!.data.legend!.replace(/\s+/, '') + this.props.data!.data.data;
const opacity: number =
this.props.activeArc === this.props.data!.data.legend || this.props.activeArc === '' ? 1 : 0.1;

return (
<g ref={this.currentRef}>
{!!focusedArcId && focusedArcId === id && (
<path id={id + 'focusRing'} d={arc(this.props.focusData)} className={classNames.focusRing} />
<path id={id + 'focusRing'} d={arc(this.props.focusData)} className={this._classNames.focusRing} />
)}
<path
id={id}
d={arc(this.props.data)}
onFocus={this._onFocus.bind(this, this.props.data!.data, id)}
className={classNames.root}
className={this._classNames.root}
data-is-focusable={true}
onMouseOver={this._hoverOn.bind(this, this.props.data!.data)}
onMouseMove={this._hoverOn.bind(this, this.props.data!.data)}
Expand All @@ -61,9 +69,11 @@ export class Arc extends React.Component<IArcProps, IArcState> {
aria-label={this._getAriaLabel()}
role="img"
/>
<text textAnchor={'middle'} className={classNames.insideDonutString} y={5}>
{this.props.valueInsideDonut!}
</text>
<g className={this._classNames.nodeTextContainer}>
<text id="Donut_center_text" textAnchor={'middle'} className={this._classNames.insideDonutString} y={5}>
{this.props.valueInsideDonut}
</text>
</g>
</g>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,14 @@ export interface IArcStyles {
* styles for the focus
*/
focusRing: IStyle;

/**
* Style for tool tip
*/
tooltip?: IStyle;

/**
* Style for overflow center text container
*/
nodeTextContainer?: IStyle;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
private _currentHoverElement: any;
private _calloutId: string;
private _calloutAnchorPoint: IChartDataPoint | null;
private _tooltip: any;
private _tooltipId: string;
private _rectTooltipId: string;

public static getDerivedStateFromProps(
nextProps: Readonly<IDonutChartProps>,
Expand Down Expand Up @@ -70,6 +73,8 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
this._hoverLeave = this._hoverLeave.bind(this);
this._calloutId = getId('callout');
this._uniqText = getId('_Pie_');
this._tooltipId = getId('_Donut_tooltip_');
this._rectTooltipId = getId('_Rect_tooltip_');
}
public componentDidMount(): void {
/* 80% Height to the Chart
Expand Down Expand Up @@ -106,11 +111,22 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
>
<FocusZone direction={FocusZoneDirection.horizontal} handleTabKey={FocusZoneTabbableElements.all}>
<div>
<div
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use SvgTooltipText control here.

id={this._tooltipId}
style={{ position: 'absolute', display: 'none' }}
className={this._classNames.tooltip!}
/>
<svg
className={this._classNames.chart}
aria-label={data?.chartTitle}
ref={(node: SVGElement | null) => this._setViewBox(node)}
>
<rect
id={this._rectTooltipId}
onMouseMove={this._showTooltip.bind(this, this.props.valueInsideDonut)}
onMouseOut={this._hideTooltip.bind(this)}
className={this._classNames.tooltipContainer!}
/>
<Pie
width={this.state._width!}
height={this.state._height!}
Expand Down Expand Up @@ -302,4 +318,22 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
private _getHighlightedLegend() {
return this.state.selectedLegend || this.state.activeLegend;
}

private _showTooltip = (text: string | number, evt: any) => {
this._tooltip = document.getElementById(this._tooltipId);
if (this._tooltip) {
this._tooltip!.innerHTML = text.toString();
this._tooltip!.style.display = 'block';
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be done using react states and not direct dom interactions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

using react states require the title of the rect to be set in which case, there is no way to override the style of the tooltip as it appears as follows:
tooltip_example
As a result, I have created a ref to the tooltip element as used it instead of querying the dom.

this._tooltip!.style.opacity = '0.9';
this._tooltip!.style.left = evt.pageX + 'px';
this._tooltip!.style.top = evt.pageY - 28 + 'px';
}
};

private _hideTooltip = (evt: any) => {
if (this._tooltip) {
this._tooltip = document.getElementById(this._tooltipId);
this._tooltip!.style.display = 'none';
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,25 @@ export const getStyles = (props: IDonutChartStyleProps): IDonutChartStyles => {
paddingTop: '16px',
width: `${width}px`,
},
tooltipContainer: {
width: '100%',
height: '100%',
textAlign: 'center',
fill: 'transparent',
display: 'flex',
flexDirection: 'column',
},
tooltip: {
...theme.fonts.medium,
display: 'flex',
flexDirection: 'column',
padding: '8px',
position: 'absolute',
textAlign: 'center',
top: '0px',
background: theme.semanticColors.bodyBackground,
borderRadius: '2px',
pointerEvents: 'none',
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,14 @@ export interface IDonutChartStyles {
* Style for the legend container.
*/
legendContainer: IStyle;

/**
* Style for the tool tip container
*/
tooltipContainer?: IStyle;

/**
* Style for the tool tip
*/
tooltip?: IStyle;
}
Loading