[ML] Mask annotation overflows in Time Series Chart. (#27292)

Fixes the overflow of annotations left and right of the focus area chart using a SVG mask. In addition to the masking, annotation label position are adjusted to not be hidden or cut off when the start or endof a masked annotation is outside the visible area.
This commit is contained in:
Walter Rafelsberger 2018-12-17 16:58:41 +01:00 committed by GitHub
parent 465d56faea
commit e515e0424b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 6 deletions

View file

@ -46,6 +46,7 @@ import { mlEscape } from '../../../util/string_utils';
import { mlFieldFormatService } from '../../../services/field_format_service';
import { mlChartTooltipService } from '../../../components/chart_tooltip/chart_tooltip_service';
import {
ANNOTATION_MASK_ID,
getAnnotationBrush,
getAnnotationLevels,
renderAnnotations,
@ -384,6 +385,21 @@ export class TimeseriesChart extends React.Component {
.attr('class', 'context-chart')
.attr('transform', 'translate(' + margin.left + ',' + (focusHeight + margin.top + chartSpacing) + ')');
// Mask to hide annotations overflow
if (mlAnnotationsEnabled) {
const annotationsMask = svg
.append('defs')
.append('mask')
.attr('id', ANNOTATION_MASK_ID);
annotationsMask.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', this.vizWidth)
.attr('height', focusHeight)
.style('fill', 'white');
}
// Draw each of the component elements.
createFocusChart(focus, this.vizWidth, focusHeight);
drawContextElements(context, this.vizWidth, contextChartHeight, swimlaneHeight);

View file

@ -16,6 +16,8 @@ import { mlChartTooltipService } from '../../../components/chart_tooltip/chart_t
import { TimeseriesChart } from './timeseries_chart';
export const ANNOTATION_MASK_ID = 'mlAnnotationMask';
// getAnnotationBrush() is expected to be called like getAnnotationBrush.call(this)
// so it gets passed on the context of the component it gets called from.
export function getAnnotationBrush(this: TimeseriesChart) {
@ -147,6 +149,7 @@ export function renderAnnotations(
.attr('rx', ANNOTATION_RECT_BORDER_RADIUS)
.attr('ry', ANNOTATION_RECT_BORDER_RADIUS)
.classed('mlAnnotationRect', true)
.attr('mask', `url(#${ANNOTATION_MASK_ID})`)
.on('mouseover', function(this: object, d: Annotation) {
showFocusChartTooltip(d, this);
})
@ -195,11 +198,21 @@ export function renderAnnotations(
.append('text')
.classed('mlAnnotationText', true);
function labelXOffset(ts: number) {
const earliestMs = focusXScale.domain()[0];
const latestMs = focusXScale.domain()[1];
const date = moment(ts);
const minX = Math.max(focusXScale(earliestMs), focusXScale(date));
// To avoid overflow to the right, substract maxOffset which is
// the width of the text label (24px) plus left margin (8xp).
const maxOffset = 32;
return Math.min(focusXScale(latestMs) - maxOffset, minX);
}
texts
.attr('x', (d: Annotation) => {
const date = moment(d.timestamp);
const x = focusXScale(date);
return x + 17;
const leftInnerOffset = 17;
return labelXOffset(d.timestamp) + leftInnerOffset;
})
.attr('y', (d: Annotation) => {
const level = d.key !== undefined ? levels[d.key] : ANNOTATION_DEFAULT_LEVEL;
@ -214,9 +227,8 @@ export function renderAnnotations(
textRects
.attr('x', (d: Annotation) => {
const date = moment(d.timestamp);
const x = focusXScale(date);
return x + 5;
const leftInnerOffset = 5;
return labelXOffset(d.timestamp) + leftInnerOffset;
})
.attr('y', (d: Annotation) => {
const level = d.key !== undefined ? levels[d.key] : ANNOTATION_DEFAULT_LEVEL;