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

Color Fill Bars Feature for Line Chart Component #14597

Merged
merged 25 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d1ebfc1
Added overlays and updated legend
mergmiller Aug 17, 2020
47443b1
Updated variable names
mergmiller Aug 17, 2020
559a523
Updated overlay name to color fill bars
mergmiller Aug 18, 2020
aa4f51c
Change files
mergmiller Aug 18, 2020
0dc796b
Merge branch 'master' into ColorFillBarFeature
mergmiller Aug 18, 2020
8564d2e
Merge branch 'master' of https://github.com/microsoft/fluentui into C…
mergmiller Aug 24, 2020
833f16b
Merge branch 'master' of https://github.com/microsoft/fluentui into C…
mergmiller Aug 26, 2020
7b06a4b
Removed eslint comment
mergmiller Aug 31, 2020
77bb0f6
Merge branch 'master' into ColorFillBarFeature
mergmiller Aug 31, 2020
49934fc
Updated snapshot
mergmiller Sep 1, 2020
b1a94f9
Merge branch 'master' of https://github.com/microsoft/fluentui into C…
mergmiller Sep 1, 2020
d713bef
Merge branch
mergmiller Sep 1, 2020
4df5299
Change files
mergmiller Sep 2, 2020
56b38ff
Merge branch 'master' of https://github.com/microsoft/fluentui into C…
mergmiller Sep 8, 2020
8553b9a
Implemented multiple legend selections
mergmiller Sep 10, 2020
5156835
Removed IStyle
mergmiller Sep 10, 2020
fa2ccc2
Updated examples
mergmiller Sep 15, 2020
d1921dd
Merge branch 'master' of https://github.com/microsoft/fluentui into C…
mergmiller Sep 24, 2020
39dbaf3
Merge branch
mergmiller Oct 1, 2020
e5816cf
Updated import
mergmiller Oct 1, 2020
8c3de7c
Merge branch '7.0' into ColorFillBarFeature
mergmiller Oct 5, 2020
34363f4
Change files
mergmiller Oct 5, 2020
cf36277
updated snapshots
mergmiller Oct 5, 2020
566f9d4
Merge branch '7.0' of https://github.com/microsoft/fluentui into Colo…
mergmiller Oct 7, 2020
952ac8d
Updated snapshot
mergmiller Oct 7, 2020
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
8 changes: 8 additions & 0 deletions change/@uifabric-charting-2020-08-18-13-59-25-master.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Added color fill bar feature to line chart component",
"packageName": "@uifabric/charting",
"email": "email not defined",
"dependentChangeType": "patch",
"date": "2020-08-18T20:59:25.017Z"
}
12 changes: 11 additions & 1 deletion packages/charting/src/components/Legends/Legends.base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ interface ILegendItem extends React.ButtonHTMLAttributes<HTMLButtonElement> {
color: string;
shape?: LegendShape;
key: number;
opacity?: number;
stripePattern?: boolean;
}

export interface ILegendState {
Expand Down Expand Up @@ -98,6 +100,8 @@ export class LegendsBase extends React.Component<ILegendsProps, ILegendState> {
onMouseOutAction: legend.onMouseOutAction!,
color: legend.color,
shape: legend.shape,
stripePattern: legend.stripePattern,
opacity: legend.opacity,
key: index,
};
});
Expand Down Expand Up @@ -257,6 +261,7 @@ export class LegendsBase extends React.Component<ILegendsProps, ILegendState> {
>
<div
className={classNames.overflowIndicationTextStyle}
// eslint-disable-next-line react/jsx-no-bind
ref={(rootElem: HTMLDivElement) => (this._hoverCardRef = rootElem)}
{...(allowFocusOnLegends && {
'aria-expanded': this.state.isHoverCardVisible,
Expand Down Expand Up @@ -301,6 +306,8 @@ export class LegendsBase extends React.Component<ILegendsProps, ILegendState> {
action: data.action,
hoverAction: data.hoverAction,
onMouseOutAction: data.onMouseOutAction,
stripePattern: data.stripePattern,
opacity: data.opacity,
};
const color = this._getColor(legend.title, legend.color);
const { theme, className, styles } = this.props;
Expand All @@ -310,8 +317,9 @@ export class LegendsBase extends React.Component<ILegendsProps, ILegendState> {
colorOnSelectedState: color,
borderColor: legend.color,
overflow: overflow,
stripePattern: legend.stripePattern,
opacity: legend.opacity,
});

const onClickHandler = () => {
this._onClick(legend);
};
Expand All @@ -333,12 +341,14 @@ export class LegendsBase extends React.Component<ILegendsProps, ILegendState> {
{...(data.nativeButtonProps && { ...data.nativeButtonProps })}
key={index}
className={classNames.legend}
/* eslint-disable react/jsx-no-bind */
onClick={onClickHandler}
onMouseOver={onHoverHandler}
onMouseOut={onMouseOut}
onFocus={onHoverHandler}
onBlur={onMouseOut}
data-is-focusable={allowFocusOnLegends}
/* eslint-enable react/jsx-no-bind */
>
<div className={this._getShapeClass(classNames, legend)} />
<div className={classNames.text}>{legend.title}</div>
Expand Down
8 changes: 6 additions & 2 deletions packages/charting/src/components/Legends/Legends.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ export const getStyles = (props: ILegendStyleProps): ILegendsStyles => {
},
width: '12px',
height: '12px',
backgroundColor: props.colorOnSelectedState,
backgroundColor: props.stripePattern ? '' : props.colorOnSelectedState,
marginRight: '8px',
border: '1px solid',
borderColor: props.borderColor ? props.borderColor : theme?.semanticColors.buttonBorder,
opacity: props.colorOnSelectedState === palette.white ? '0.6' : '',
opacity: props.colorOnSelectedState === palette.white ? '0.6' : props.opacity ? props.opacity : '',
backgroundImage: props.stripePattern
? `repeating-linear-gradient(135deg, transparent, transparent 3px, ${props.colorOnSelectedState} 1px, ${props.colorOnSelectedState} 4px)`
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
: '',
},
triangle: {
width: '0',
Expand All @@ -62,6 +65,7 @@ export const getStyles = (props: ILegendStyleProps): ILegendsStyles => {
borderTop: '10.4px solid',
borderTopColor: props.colorOnSelectedState,
marginRight: '8px',
opacity: props.colorOnSelectedState === palette.white ? '0.6' : props.opacity ? props.opacity : '',
selectors: {
[HighContrastSelector]: {
border: '0px',
Expand Down
12 changes: 12 additions & 0 deletions packages/charting/src/components/Legends/Legends.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,22 @@ export interface ILegend {
*/
color: string;

/**
* The opacity of the legend color
*/
opacity?: number;

/**
* The shape for the legend
*/
shape?: LegendShape;

/**
* Indicated whether of not to apply stripe pattern
*/
stripePattern?: boolean;

/*
* native button props for the legend button
*/
nativeButtonProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
Expand All @@ -99,7 +109,9 @@ export interface ILegendStyleProps {
className?: string;
colorOnSelectedState?: string;
borderColor?: string;
opacity?: number;
overflow?: boolean;
stripePattern?: boolean;
}

export interface ILegendsProps {
Expand Down
102 changes: 96 additions & 6 deletions packages/charting/src/components/LineChart/LineChart.base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Axis as D3Axis } from 'd3-axis';
import { select as d3Select } from 'd3-selection';
import { ILegend, Legends } from '../Legends/index';
import { getId, find } from 'office-ui-fabric-react/lib/Utilities';
import { ILineChartProps, ILineChartPoints, IBasestate, IChildProps } from './LineChart.types';
import { ILineChartProps, ILineChartPoints, IBasestate, IChildProps, IColorFillBarsProps } from './LineChart.types';
import { DirectionalHint } from 'office-ui-fabric-react/lib/Callout';
import { EventsAnnotation } from './eventAnnotation/EventAnnotation';
import { calloutData, IMargins } from '../../utilities/index';
Expand Down Expand Up @@ -34,11 +34,14 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
private _circleId: string;
private _lineId: string;
private _verticalLine: string;
private _colorFillBarPatternId: string;
private _uniqueCallOutID: string;
private _refArray: IRefArrayData[];
private margins: IMargins;
private eventLabelHeight: number = 36;
private lines: JSX.Element[];
private colorFillBars: IColorFillBarsProps[];
private _renderedColorFillBars: JSX.Element[];

constructor(props: ILineChartProps) {
super(props);
Expand All @@ -53,9 +56,11 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
this._refArray = [];
this._points = this.props.data.lineChartData || [];
this._calloutPoints = calloutData(this._points) || [];
this.colorFillBars = this.props.colorFillBars || [];
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
this._circleId = getId('circle');
this._lineId = getId('lineID');
this._verticalLine = getId('verticalLine');
this._colorFillBarPatternId = getId('colorFillBarPattern');
this.margins = {
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
top: this.props.margins?.top || 20,
right: this.props.margins?.right || 20,
Expand Down Expand Up @@ -113,7 +118,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
<ChartHelper
{...this.props}
points={this._points}
getGraphData={this._getLinesData}
getGraphData={this._initializeLineChartData}
calloutProps={calloutProps}
tickParams={tickParams}
legendBars={legendBars}
Expand All @@ -136,7 +141,10 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
visibility={'hidden'}
strokeDasharray={'5,5'}
/>
<g>{this.lines}</g>
<g>
{this._renderedColorFillBars}
{this.lines}
</g>
{eventAnnotationProps && (
<EventsAnnotation
{...eventAnnotationProps}
Expand All @@ -154,10 +162,16 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _getLinesData = (xScale: any, yScale: NumericAxis, containerHeight: number, containerWidth: number) => {
private _initializeLineChartData = (
xScale: any,
yScale: NumericAxis,
containerHeight: number,
containerWidth: number,
) => {
this._xAxisScale = xScale;
this._yAxisScale = yScale;
return (this.lines = this._createLines());
this._renderedColorFillBars = this._createColorFillBars(containerHeight);
this.lines = this._createLines();
};

private _createLegends(data: ILineChartPoints[]): JSX.Element {
Expand Down Expand Up @@ -186,9 +200,37 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
};
return legend;
});

const colorFillBarsLegendDataItems = this.colorFillBars.map((colorFillBar: IColorFillBarsProps, index: number) => {
const title = colorFillBar.name;
const legend: ILegend = {
title,
color: colorFillBar.color,
action: () => {
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
if (this.state.selectedLegend === title) {
this.setState({ selectedLegend: '' });
this._handleColorFillBarLengendClick(colorFillBar, null);
} else {
this.setState({ selectedLegend: title });
this._handleColorFillBarLengendClick(colorFillBar, title);
}
this.setState({ activeLegend: title });
},
onMouseOutAction: () => {
this.setState({ activeLegend: '' });
},
hoverAction: () => {
this.setState({ activeLegend: title });
},
opacity: 0.4,
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
stripePattern: colorFillBar.applyPattern,
};
return legend;
});

const legends = (
<Legends
legends={legendDataItems}
legends={[...legendDataItems, ...colorFillBarsLegendDataItems]}
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
enabledWrapLines={this.props.enabledLegendsWrapLines}
overflowProps={this.props.legendsOverflowProps}
focusZonePropsInHoverCard={this.props.focusZonePropsForLegendsInHoverCard}
Expand Down Expand Up @@ -227,6 +269,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
const y2 = this._points[i].data[j].y;
const xAxisCalloutData = this._points[i].data[j - 1].xAxisCalloutData;
if (this.state.activeLegend === legendVal || this.state.activeLegend === '') {
/* eslint-disable react/jsx-no-bind */
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
lines.push(
<line
id={lineId}
Expand Down Expand Up @@ -300,6 +343,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
strokeWidth={3}
/>,
);
/* eslint-enable react/jsx-no-bind */
}
} else {
lines.push(
Expand All @@ -322,6 +366,43 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
return lines;
}

private _createColorFillBars = (containerHeight: number) => {
const colorFillBars: JSX.Element[] = [];
for (let i = 0; i < this.colorFillBars.length; i++) {
const colorFillBar = this.colorFillBars[i];
const colorFillBarId = getId(colorFillBar.name.replace(/\W/g, ''));
if (colorFillBar.applyPattern) {
colorFillBars.push(
<pattern
id={`${this._colorFillBarPatternId}${i}`}
width={16}
height={16}
key={`${this._colorFillBarPatternId}${i}`}
patternUnits={'userSpaceOnUse'}
>
<path d={'M-4,4 l8,-8 M0,16 l16,-16 M12,20 l8,-8'} stroke={colorFillBar.color} strokeWidth={2} />
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
</pattern>,
);
}
for (let j = 0; j < colorFillBar.data.length; j++) {
const startX = colorFillBar.data[j].startX;
const endX = colorFillBar.data[j].endX;
colorFillBars.push(
<rect
fill={colorFillBar.applyPattern ? `url(#${this._colorFillBarPatternId}${i})` : colorFillBar.color}
fillOpacity={this.state.activeLegend === colorFillBar.name || this.state.activeLegend === '' ? 0.4 : 0.1}
x={this._xAxisScale(startX)}
y={this._yAxisScale(0) - containerHeight}
width={Math.abs(this._xAxisScale(endX) - this._xAxisScale(startX))}
height={containerHeight}
key={`${colorFillBarId}${j}`}
/>,
);
}
}
return colorFillBars;
};

private _refCallback(element: SVGGElement, legendTitle: string): void {
this._refArray.push({ index: legendTitle, refElement: element });
}
Expand Down Expand Up @@ -416,4 +497,13 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
point.onLegendClick(selectedLegend);
}
};

private _handleColorFillBarLengendClick = (
colorFillBar: IColorFillBarsProps,
selectedLegend: string | null,
): void => {
if (colorFillBar.onLegendClick) {
colorFillBar.onLegendClick(selectedLegend);
}
};
}
19 changes: 19 additions & 0 deletions packages/charting/src/components/LineChart/LineChart.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,16 @@ export interface ILineChartProps extends Partial<IChartHelperProps> {
*/
margins?: IMargins;

/*
* Color fill bars for the chart,
*/
colorFillBars?: IColorFillBarsProps[];

/**
* props for the legends in the chart
*/
legendProps?: Partial<ILegendsProps>;

}

export interface IEventsAnnotationProps {
Expand All @@ -173,3 +179,16 @@ export interface IEventsAnnotationProps {
export interface ILineChartStyles extends IChartHelperStyles {}

export interface ILineChartStyleProps extends IChartHelperStyleProps {}

export interface IColorFillBarsProps {
name: string;
color: string;
data: IColorFillBarData[];
applyPattern?: boolean;
onLegendClick?: (selectedLegend: string | null) => void | undefined;
mergmiller marked this conversation as resolved.
Show resolved Hide resolved
}

export interface IColorFillBarData {
startX: number | Date;
endX: number | Date;
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,33 @@ export class LineChartBasicExample extends React.Component<{}, ILineChartBasicSt
height={this.state.height}
width={this.state.width}
margins={margins}
colorFillBars={[
{
name: 'Time range 1',
color: 'blue',
data: [
{
startX: new Date('2020-03-03T10:00:00.000Z'),
endX: new Date('2020-03-05T00:00:00.000Z'),
},
],
},
{
name: 'Time range 2',
color: 'red',
data: [
{
startX: new Date('2020-03-07T00:00:00.000Z'),
endX: new Date('2020-03-08T00:00:00.000Z'),
},
{
startX: new Date('2020-03-04T10:00:00.000Z'),
endX: new Date('2020-03-06T00:00:00.000Z'),
},
],
applyPattern: true,
},
]}
/>
</div>
</>
Expand Down