Skip to content

Commit

Permalink
feat(annotations): option to render rect annotations outside chart (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
nickofthyme authored Jun 24, 2021
1 parent 1a3d777 commit 4eda382
Show file tree
Hide file tree
Showing 25 changed files with 306 additions and 26 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions integration/tests/annotations_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,46 @@ describe('Annotations stories', () => {
});
});
});

describe('Outside annotations', () => {
describe.each(['x', 'y'])('Domain - %s', (domain) => {
eachRotation.it(async (rotation) => {
await common.expectChartAtUrlToMatchScreenshot(
`http://localhost:9001/?path=/story/annotations-rects--outside&knob-debug=&knob-chartRotation=${rotation}&knob-Domain%20axis_Annotations=${domain}&knob-Render%20outside%20chart_Annotations=true&knob-Red%20groupId_Annotations=primary&knob-Blue%20groupId_Annotations=secondary&knob-Tick%20size=10`,
);
});
});

it('should show tooltip on hover - x domain', async () => {
await common.expectChartWithMouseAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/annotations-rects--outside&knob-Outside dimension_Annotations=20&knob-debug=&knob-chartRotation=0&knob-Tick size=20&knob-Domain axis_Annotations=x&knob-Render outside chart_Annotations=true&knob-Red groupId_Annotations=primary&knob-Blue groupId_Annotations=secondary',
{
top: 60,
right: 100,
},
);
});

it('should show tooltip on hover - y domain', async () => {
await common.expectChartWithMouseAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/annotations-rects--outside&knob-Outside dimension_Annotations=20&knob-debug=&knob-chartRotation=0&knob-Tick size=20&knob-Domain axis_Annotations=y&knob-Render outside chart_Annotations=true&knob-Red groupId_Annotations=primary&knob-Blue groupId_Annotations=secondary',
{
top: 125,
left: 60,
},
);
});

it('should render outside annotations when tickLine is not rendered', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/annotations-rects--outside&knob-debug=&knob-chartRotation=180&knob-Tick size=0&knob-Domain axis_Annotations=x&knob-Render outside chart_Annotations=true&knob-Red groupId_Annotations=primary&knob-Blue groupId_Annotations=secondary',
);
});

it('should render outside annotations when axes are hidden', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/annotations-rects--outside&knob-debug=&knob-chartRotation=0&knob-Tick size=10&knob-Hide all axes=true&knob-Domain axis_Annotations=x&knob-Render outside chart_Annotations=true&knob-Outside dimension_Annotations=5&knob-Red groupId_Annotations=primary&knob-Blue groupId_Annotations=secondary',
);
});
});
});
2 changes: 2 additions & 0 deletions packages/charts/api/charts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,8 @@ export interface RectAnnotationDatum {
export type RectAnnotationSpec = BaseAnnotationSpec<typeof AnnotationType.Rectangle, RectAnnotationDatum, RectAnnotationStyle> & {
renderTooltip?: AnnotationTooltipFormatter;
zIndex?: number;
outside?: boolean;
outsideDimension?: number;
};

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { Dimensions } from '../../../../utils/dimensions';
import { AnnotationId } from '../../../../utils/ids';
import { LineAnnotation } from '../../specs/line_annotation';
import { LineSeries } from '../../specs/line_series';
import { AnnotationDomainType, AnnotationType, AxisSpec, RectAnnotationSpec } from '../../utils/specs';
import { AnnotationDomainType, AnnotationType, RectAnnotationSpec } from '../../utils/specs';
import { computeRectAnnotationTooltipState } from '../tooltip';
import { AnnotationDimensions } from '../types';
import { AnnotationLineProps } from './types';
Expand Down Expand Up @@ -138,7 +138,6 @@ describe('Annotation tooltips', () => {
}),
];
const chartRotation: Rotation = 0;
const localAxesSpecs: AxisSpec[] = [];

const annotationDimensions = new Map<AnnotationId, AnnotationDimensions>();
annotationDimensions.set('foo', annotationLines);
Expand All @@ -165,7 +164,6 @@ describe('Annotation tooltips', () => {
annotationDimensions,
rectAnnotations,
chartRotation,
localAxesSpecs,
chartDimensions,
);

Expand All @@ -186,7 +184,6 @@ describe('Annotation tooltips', () => {
annotationDimensions,
rectAnnotations,
chartRotation,
localAxesSpecs,
chartDimensions,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@

import { Scale, ScaleBand, ScaleContinuous } from '../../../../scales';
import { isBandScale, isContinuousScale } from '../../../../scales/types';
import { isDefined } from '../../../../utils/common';
import { GroupId } from '../../../../utils/ids';
import { isDefined, Position, Rotation } from '../../../../utils/common';
import { AxisId, GroupId } from '../../../../utils/ids';
import { Point } from '../../../../utils/point';
import { AxisStyle } from '../../../../utils/themes/theme';
import { PrimitiveValue } from '../../../partition_chart/layout/utils/group_by_rollup';
import { SmallMultipleScales } from '../../state/selectors/compute_small_multiple_scales';
import { isHorizontalRotation, isVerticalRotation } from '../../state/utils/common';
import { getAxesSpecForSpecId } from '../../state/utils/spec';
import { getPanelSize } from '../../utils/panel';
import { RectAnnotationDatum, RectAnnotationSpec } from '../../utils/specs';
import { AxisSpec, RectAnnotationDatum, RectAnnotationSpec } from '../../utils/specs';
import { Bounds } from '../types';
import { AnnotationRectProps } from './types';

Expand All @@ -42,14 +45,18 @@ export function computeRectAnnotationDimensions(
annotationSpec: RectAnnotationSpec,
yScales: Map<GroupId, Scale>,
xScale: Scale,
axesSpecs: AxisSpec[],
smallMultiplesScales: SmallMultipleScales,
chartRotation: Rotation,
getAxisStyle: (id?: AxisId) => AxisStyle,
isHistogram: boolean = false,
): AnnotationRectProps[] | null {
const { dataValues, groupId } = annotationSpec;
const { dataValues, groupId, outside } = annotationSpec;
const { xAxis, yAxis } = getAxesSpecForSpecId(axesSpecs, groupId);
const yScale = yScales.get(groupId);

const rectsProps: Omit<AnnotationRectProps, 'panel'>[] = [];
const panelSize = getPanelSize(smallMultiplesScales);

dataValues.forEach((datum: RectAnnotationDatum) => {
const { x0: initialX0, x1: initialX1, y0: initialY0, y1: initialY1 } = datum.coordinates;

Expand All @@ -76,14 +83,28 @@ export function computeRectAnnotationDimensions(
if (!xAndWidth) {
return;
}

if (!yScale) {
if (!isDefined(initialY0) && !isDefined(initialY1)) {
const isLeftSide =
(chartRotation === 0 && xAxis?.position === Position.Bottom) ||
(chartRotation === 180 && xAxis?.position === Position.Top) ||
(chartRotation === -90 && yAxis?.position === Position.Right) ||
(chartRotation === 90 && yAxis?.position === Position.Left);
const orthoDimension = isHorizontalRotation(chartRotation) ? panelSize.height : panelSize.width;
const outsideDim = annotationSpec.outsideDimension ?? getOutsideDimension(getAxisStyle(xAxis?.id ?? yAxis?.id));
const rectDimensions = {
...xAndWidth,
y: 0,
height: panelSize.height,
...(outside
? {
y: isLeftSide ? orthoDimension : -outsideDim,
height: outsideDim,
}
: {
y: 0,
height: orthoDimension,
}),
};

rectsProps.push({
rect: rectDimensions,
datum,
Expand Down Expand Up @@ -111,8 +132,20 @@ export function computeRectAnnotationDimensions(
scaledY1 = 0;
}

const orthoDimension = isVerticalRotation(chartRotation) ? panelSize.height : panelSize.width;
const isLeftSide =
(chartRotation === 0 && yAxis?.position === Position.Left) ||
(chartRotation === 180 && yAxis?.position === Position.Right) ||
(chartRotation === -90 && xAxis?.position === Position.Bottom) ||
(chartRotation === 90 && xAxis?.position === Position.Top);
const outsideDim = annotationSpec.outsideDimension ?? getOutsideDimension(getAxisStyle(xAxis?.id ?? yAxis?.id));
const rectDimensions = {
...xAndWidth,
...(!isDefined(initialX0) && !isDefined(initialX1) && outside
? {
x: isLeftSide ? -outsideDim : orthoDimension,
width: outsideDim,
}
: xAndWidth),
y: scaledY1,
height,
};
Expand Down Expand Up @@ -242,3 +275,9 @@ function getMin(min: number, value?: number | string | null) {
}
return value;
}

function getOutsideDimension(style: AxisStyle): number {
const { visible, size, strokeWidth } = style.tickLine;

return visible && size > 0 && strokeWidth > 0 ? size : 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { Rotation } from '../../../utils/common';
import { Dimensions } from '../../../utils/dimensions';
import { AnnotationId } from '../../../utils/ids';
import { Point } from '../../../utils/point';
import { AnnotationSpec, AxisSpec, isRectAnnotation } from '../utils/specs';
import { AnnotationSpec, isRectAnnotation } from '../utils/specs';
import { getRectAnnotationTooltipState } from './rect/tooltip';
import { AnnotationRectProps } from './rect/types';
import { AnnotationDimensions, AnnotationTooltipState } from './types';
Expand All @@ -33,7 +33,6 @@ export function computeRectAnnotationTooltipState(
annotationDimensions: Map<AnnotationId, AnnotationDimensions>,
annotationSpecs: AnnotationSpec[],
chartRotation: Rotation,
axesSpecs: AxisSpec[],
chartDimensions: Dimensions,
): AnnotationTooltipState | null {
// allow picking up the last spec added as the top most or use it's zIndex value
Expand Down
8 changes: 6 additions & 2 deletions packages/charts/src/chart_types/xy_chart/annotations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
import { Scale } from '../../../scales';
import { Rotation, Position } from '../../../utils/common';
import { Dimensions } from '../../../utils/dimensions';
import { AnnotationId, GroupId } from '../../../utils/ids';
import { AnnotationId, AxisId, GroupId } from '../../../utils/ids';
import { Point } from '../../../utils/point';
import { AxisStyle } from '../../../utils/themes/theme';
import { SmallMultipleScales } from '../state/selectors/compute_small_multiple_scales';
import { isHorizontalRotation } from '../state/utils/common';
import { getAxesSpecForSpecId } from '../state/utils/spec';
Expand Down Expand Up @@ -127,13 +128,13 @@ export function invertTransformedCursor(
/** @internal */
export function computeAnnotationDimensions(
annotations: AnnotationSpec[],
chartDimensions: Dimensions,
chartRotation: Rotation,
yScales: Map<GroupId, Scale>,
xScale: Scale,
axesSpecs: AxisSpec[],
isHistogramModeEnabled: boolean,
smallMultipleScales: SmallMultipleScales,
getAxisStyle: (id?: AxisId) => AxisStyle,
): Map<AnnotationId, AnnotationDimensions> {
return annotations.reduce<Map<AnnotationId, AnnotationDimensions>>((annotationDimensions, annotationSpec) => {
const { id } = annotationSpec;
Expand Down Expand Up @@ -161,7 +162,10 @@ export function computeAnnotationDimensions(
annotationSpec,
yScales,
xScale,
axesSpecs,
smallMultipleScales,
chartRotation,
getAxisStyle,
isHistogramModeEnabled,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const defaultProps = {
annotationType: AnnotationType.Rectangle,
zIndex: -1,
style: DEFAULT_ANNOTATION_RECT_STYLE,
outside: false,
};

/** @public */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,49 @@
*/

import { createCustomCachedSelector } from '../../../../state/create_selector';
import { getChartThemeSelector } from '../../../../state/selectors/get_chart_theme';
import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_specs';
import { AnnotationId } from '../../../../utils/ids';
import { AnnotationId, AxisId } from '../../../../utils/ids';
import { AnnotationDimensions } from '../../annotations/types';
import { computeAnnotationDimensions } from '../../annotations/utils';
import { computeChartDimensionsSelector } from './compute_chart_dimensions';
import { computeSeriesGeometriesSelector } from './compute_series_geometries';
import { computeSmallMultipleScalesSelector } from './compute_small_multiple_scales';
import { getAxesStylesSelector } from './get_axis_styles';
import { getAxisSpecsSelector, getAnnotationSpecsSelector } from './get_specs';
import { isHistogramModeEnabledSelector } from './is_histogram_mode_enabled';

/** @internal */
export const computeAnnotationDimensionsSelector = createCustomCachedSelector(
[
getAnnotationSpecsSelector,
computeChartDimensionsSelector,
getSettingsSpecSelector,
computeSeriesGeometriesSelector,
getAxisSpecsSelector,
isHistogramModeEnabledSelector,
computeSmallMultipleScalesSelector,
getAxesStylesSelector,
getChartThemeSelector,
],
(
annotationSpecs,
chartDimensions,
settingsSpec,
{ scales: { yScales, xScale } },
axesSpecs,
isHistogramMode,
smallMultipleScales,
): Map<AnnotationId, AnnotationDimensions> =>
computeAnnotationDimensions(
axisStyles,
{ axes },
): Map<AnnotationId, AnnotationDimensions> => {
const getAxisStyle = (id: AxisId = '') => axisStyles.get(id) ?? axes;
return computeAnnotationDimensions(
annotationSpecs,
chartDimensions.chartDimensions,
settingsSpec.rotation,
yScales,
xScale,
axesSpecs,
isHistogramMode,
smallMultipleScales,
),
getAxisStyle,
);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ function getAnnotationTooltipState(
annotationDimensions,
annotationSpecs,
chartRotation,
axesSpecs,
chartDimensions,
);

Expand Down
10 changes: 10 additions & 0 deletions packages/charts/src/chart_types/xy_chart/utils/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,16 @@ export type RectAnnotationSpec = BaseAnnotationSpec<
* @defaultValue -1
*/
zIndex?: number;
/**
* Renders annotation outside of chart area within axis gutter
*
* @defaultValue false
*/
outside?: boolean;
/**
* Dimension, either height or width, of outside annotation
*/
outsideDimension?: number;
};

/**
Expand Down
Loading

0 comments on commit 4eda382

Please sign in to comment.