Skip to content

Commit

Permalink
[ML] Migrate Explorer Charts to React. (#22622)
Browse files Browse the repository at this point in the history
This migrates the Anomaly Explorer charts to React. The PR aims to change as little of the actual logic to create and render the charts.
  • Loading branch information
walterra authored Sep 3, 2018
1 parent d5ea684 commit 3667040
Show file tree
Hide file tree
Showing 26 changed files with 806 additions and 621 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/

import ngMock from 'ng_mock';
import expect from 'expect.js';

describe('ML - mlChartTooltipService', () => {
let mlChartTooltipService;

beforeEach(ngMock.module('kibana'));
beforeEach(() => {
ngMock.inject(function ($injector) {
mlChartTooltipService = $injector.get('mlChartTooltipService');
});
});
import { mlChartTooltipService } from '../chart_tooltip_service';

describe('ML - mlChartTooltipService', () => {
it('service API duck typing', () => {
expect(mlChartTooltipService).to.be.an('object');
expect(mlChartTooltipService.show).to.be.a('function');
Expand Down
71 changes: 4 additions & 67 deletions x-pack/plugins/ml/public/components/chart_tooltip/chart_tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
*/



import $ from 'jquery';
import template from './chart_tooltip.html';

import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');

module.directive('mlChartTooltip', function (mlChartTooltipService) {
import { mlChartTooltipService } from './chart_tooltip_service';

module.directive('mlChartTooltip', function () {
return {
restrict: 'E',
replace: true,
Expand All @@ -21,67 +21,4 @@ module.directive('mlChartTooltip', function (mlChartTooltipService) {
mlChartTooltipService.element = element;
}
};
})
.service('mlChartTooltipService', function ($timeout) {
this.element = null;
this.fadeTimeout = null;
const doc = document.documentElement;
const FADE_TIMEOUT_MS = 200;

this.show = function (contents, target, offset = { x: 0, y: 0 }) {
if (this.element !== null) {

// if a previous fade out was happening, stop it
if (this.fadeTimeout !== null) {
$timeout.cancel(this.fadeTimeout);
}

// populate the tooltip contents
this.element.html(contents);

// side bar width
const navOffset = $('.global-nav').width();
const contentWidth = $('body').width() - navOffset - 10;
const tooltipWidth = this.element.width();
const scrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);

const pos = target.getBoundingClientRect();
const x = (pos.left + (offset.x) + 4) - navOffset;
const y = pos.top + (offset.y) + scrollTop;

if (x + tooltipWidth > contentWidth) {
// the tooltip is hanging off the side of the page,
// so move it to the other side of the target
this.element.css({
'left': x - (tooltipWidth + offset.x + 22),
'top': (y - 28)
});
} else {
this.element.css({
'left': x,
'top': (y - 28)
});
}

this.element.css({
'opacity': '0.9',
'display': 'block'
});
}
};

this.hide = function () {
if (this.element !== null) {
this.element.css({
'opacity': '0',
});

// after the fade out transition has finished, set the display to
// none so it doesn't block any mouse events underneath it.
this.fadeTimeout = $timeout(() => {
this.element.css('display', 'none');
this.fadeTimeout = null;
}, FADE_TIMEOUT_MS);
}
};
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/



import $ from 'jquery';

const doc = document.documentElement;
const FADE_TIMEOUT_MS = 200;

export const mlChartTooltipService = {
element: null,
fadeTimeout: null,
};

mlChartTooltipService.show = function (contents, target, offset = { x: 0, y: 0 }) {
if (this.element === null) {
return;
}

// if a previous fade out was happening, stop it
if (this.fadeTimeout !== null) {
clearTimeout(this.fadeTimeout);
}

// populate the tooltip contents
this.element.html(contents);

// side bar width
const navOffset = $('.global-nav').width();
const contentWidth = $('body').width() - navOffset - 10;
const tooltipWidth = this.element.width();
const scrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);

const pos = target.getBoundingClientRect();
const x = (pos.left + (offset.x) + 4) - navOffset;
const y = pos.top + (offset.y) + scrollTop;

if (x + tooltipWidth > contentWidth) {
// the tooltip is hanging off the side of the page,
// so move it to the other side of the target
this.element.css({
left: x - (tooltipWidth + offset.x + 22),
top: (y - 28)
});
} else {
this.element.css({
left: x,
top: (y - 28)
});
}

this.element.css({
opacity: '0.9',
display: 'block'
});
};

mlChartTooltipService.hide = function () {
if (this.element === null) {
return;
}

this.element.css({
opacity: '0',
});

// after the fade out transition has finished, set the display to
// none so it doesn't block any mouse events underneath it.
this.fadeTimeout = setTimeout(() => {
this.element.css('display', 'none');
this.fadeTimeout = null;
}, FADE_TIMEOUT_MS);
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@ import d3 from 'd3';
import moment from 'moment';

import { parseInterval } from 'ui/utils/parse_interval';
import { numTicksForDateFormat } from 'plugins/ml/util/chart_utils';
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
import { numTicksForDateFormat } from '../../util/chart_utils';
import { calculateTextWidth } from '../../util/string_utils';
import { IntervalHelperProvider } from '../../util/ml_time_buckets';
import { mlChartTooltipService } from '../../components/chart_tooltip/chart_tooltip_service';

import { uiModules } from 'ui/modules';
import { timefilter } from 'ui/timefilter';
const module = uiModules.get('apps/ml');

module.directive('mlDocumentCountChart', function (
Private,
mlChartTooltipService) {

module.directive('mlDocumentCountChart', function (Private) {
function link(scope, element, attrs) {
const svgWidth = attrs.width ? +attrs.width : 400;
const svgHeight = scope.height = attrs.height ? +attrs.height : 400;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
import _ from 'lodash';
import d3 from 'd3';

import { numTicks } from 'plugins/ml/util/chart_utils';
import { numTicks } from '../../util/chart_utils';
import { ordinalSuffix } from 'ui/utils/ordinal_suffix';
import { mlChartTooltipService } from '../../components/chart_tooltip/chart_tooltip_service';

import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');

module.directive('mlMetricDistributionChart', function (mlChartTooltipService) {
module.directive('mlMetricDistributionChart', function () {

function link(scope, element, attrs) {
const svgWidth = attrs.width ? +attrs.width : 400;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/



import './styles/main.less';

import PropTypes from 'prop-types';
import React from 'react';

export function LoadingIndicator({ height }) {
height = height ? +height : 100;
return (
<div className="ml-loading-indicator" style={{ height: `${height}px` }}>
<div className="loading-spinner"><i className="fa fa-spinner fa-spin" /></div>
</div>
);
}
LoadingIndicator.propTypes = {
height: PropTypes.number
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* angular */
ml-loading-indicator {
.loading-indicator {
text-align: center;
Expand All @@ -12,3 +13,17 @@ ml-loading-indicator {
}
}
}

/* react */
.ml-loading-indicator {
text-align: center;
font-size: 17px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

.loading-spinner {
font-size: 24px;
}
}
14 changes: 2 additions & 12 deletions x-pack/plugins/ml/public/explorer/explorer.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,8 @@ <h4 class="euiTitle euiTitle--small">No {{swimlaneViewByFieldName}} influencers
</div>
</div>

<div ng-controller="MlExplorerChartsContainerController" class="euiText">
<ml-explorer-charts-container
series-to-plot="seriesToPlot"
time-field-name="timeFieldName"
plot-earliest="plotEarliest"
plot-latest="plotLatest"
selected-earliest="selectedEarliest"
selected-latest="selectedLatest"
charts-per-row="chartsPerRow"
layout-cells-per-chart="layoutCellsPerChart"
too-many-buckets="tooManyBuckets">
</ml-explorer-charts-container>
<div class="euiText explorer-charts">
<ml-explorer-charts-container />
</div>

<ml-anomalies-table
Expand Down
Loading

0 comments on commit 3667040

Please sign in to comment.