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

feat(annotations): option to render rect annotations outside chart #1207

Merged
merged 9 commits into from
Jun 24, 2021
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