diff --git a/x-pack/plugins/ml/public/components/influencers_list/index.js b/x-pack/plugins/ml/public/components/influencers_list/index.js
index 43aca379549976..ab50fbb57f37a7 100644
--- a/x-pack/plugins/ml/public/components/influencers_list/index.js
+++ b/x-pack/plugins/ml/public/components/influencers_list/index.js
@@ -5,7 +5,5 @@
*/
-
-
import './influencers_list_directive';
import './styles/main.less';
diff --git a/x-pack/plugins/ml/public/components/influencers_list/influencers_list.html b/x-pack/plugins/ml/public/components/influencers_list/influencers_list.html
deleted file mode 100644
index 7f64cc3b3dad54..00000000000000
--- a/x-pack/plugins/ml/public/components/influencers_list/influencers_list.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
+
+ {(influencerFieldName !== 'mlcategory') ? (
+
{valueData.influencerFieldValue}
+ ) : (
+
mlcategory {valueData.influencerFieldValue}
+ )}
+
+
+
+
+
+
+
+ {maxScoreLabel}
+
+
+
+
+
+ {(totalScore > 0) ? abbreviateWholeNumber(totalScore, 4) : totalScoreLabel}
+
+
+
+ );
+}
+Influencer.propTypes = {
+ influencerFieldName: PropTypes.string.isRequired,
+ valueData: PropTypes.object.isRequired
+};
+
+function InfluencersByName({ influencerFieldName, fieldValues }) {
+ const influencerValues = fieldValues.map(valueData => (
+
+ ));
+
+ return (
+
+
+ {influencerFieldName}
+
+
+ {influencerValues}
+
+ );
+}
+InfluencersByName.propTypes = {
+ influencerFieldName: PropTypes.string.isRequired,
+ fieldValues: PropTypes.array.isRequired
+};
+
+export function InfluencersList({ influencers }) {
+
+ if (influencers === undefined || Object.keys(influencers).length === 0) {
+ return (
+
+
+
+
+ No influencers found
+
+
+
+ );
+ }
+
+ const influencersByName = Object.keys(influencers).map(influencerFieldName => (
+
+ ));
+
+ return (
+
+ {influencersByName}
+
+ );
+}
+InfluencersList.propTypes = {
+ influencers: PropTypes.object
+};
diff --git a/x-pack/plugins/ml/public/components/influencers_list/influencers_list_directive.js b/x-pack/plugins/ml/public/components/influencers_list/influencers_list_directive.js
index fb2daccb2732af..114620b49ab336 100644
--- a/x-pack/plugins/ml/public/components/influencers_list/influencers_list_directive.js
+++ b/x-pack/plugins/ml/public/components/influencers_list/influencers_list_directive.js
@@ -5,113 +5,19 @@
*/
-
-/*
- * AngularJS directive for rendering a list of Machine Learning influencers.
- */
-
-import _ from 'lodash';
-
-import 'plugins/ml/lib/angular_bootstrap_patch';
-import 'plugins/ml/formatters/abbreviate_whole_number';
-
-import template from './influencers_list.html';
-import { getSeverity } from 'plugins/ml/util/anomaly_utils';
-import { mlEscape } from 'plugins/ml/util/string_utils';
-
-import { FilterManagerProvider } from 'ui/filter_manager';
+import 'ngreact';
import { uiModules } from 'ui/modules';
-const module = uiModules.get('apps/ml');
-
-module.directive('mlInfluencersList', function (Private) {
-
- const filterManager = Private(FilterManagerProvider);
-
- function link(scope, element) {
-
- scope.$on('render', function () {
- render();
- });
-
- element.on('$destroy', function () {
- scope.$destroy();
- });
-
- scope.tooltipPlacement = scope.tooltipPlacement === undefined ? 'top' : scope.tooltipPlacement;
-
- function render() {
- if (scope.influencersData === undefined) {
- return;
- }
-
- const dataByViewBy = {};
-
- // TODO - position tooltip so it doesn't go off edge of window.
- const compiledTooltip = _.template(
- '
<%= influencerFieldName %>: <%= influencerFieldValue %>' +
- '
Max anomaly score: <%= maxScoreLabel %>' +
- '
Total anomaly score: <%= totalScoreLabel %>');
-
- _.each(scope.influencersData, (fieldValues, influencerFieldName) => {
- const valuesForViewBy = [];
-
- _.each(fieldValues, function (valueData) {
- const influencerFieldValue = valueData.influencerFieldValue;
- const maxScorePrecise = valueData.maxAnomalyScore;
- const maxScore = parseInt(maxScorePrecise);
- const totalScore = parseInt(valueData.sumAnomalyScore);
- const barScore = maxScore !== 0 ? maxScore : 1;
- const maxScoreLabel = maxScore !== 0 ? maxScore : '< 1';
- const totalScoreLabel = totalScore !== 0 ? totalScore : '< 1';
- const severity = getSeverity(maxScore);
-
- // Store the data for each influencerfieldname in an array to ensure
- // reliable sorting by max score.
- // If it was sorted as an object, the order when rendered using the AngularJS
- // ngRepeat directive could not be relied upon to be the same as they were
- // returned in the ES aggregation e.g. for numeric keys from a mlcategory influencer.
- valuesForViewBy.push({
- influencerFieldValue,
- maxScorePrecise,
- barScore,
- maxScoreLabel,
- totalScore,
- severity,
- tooltip: compiledTooltip({
- influencerFieldName: mlEscape(influencerFieldName),
- influencerFieldValue: mlEscape(influencerFieldValue),
- maxScoreLabel,
- totalScoreLabel
- })
- });
- });
-
-
- dataByViewBy[influencerFieldName] = _.sortBy(valuesForViewBy, 'maxScorePrecise').reverse();
- });
-
- scope.influencers = dataByViewBy;
- }
-
- // Provide a filter function so filters can be added.
- scope.filter = function (field, value, operator) {
- filterManager.add(field, value, operator, scope.indexPatternId);
- };
+const module = uiModules.get('apps/ml', ['react']);
- scope.showNoResultsMessage = function () {
- return (scope.influencersData === undefined) || (_.keys(scope.influencersData).length === 0);
- };
+import { InfluencersList } from './influencers_list';
- }
+module.directive('mlInfluencersList', function ($injector) {
+ const reactDirective = $injector.get('reactDirective');
- return {
- scope: {
- influencersData: '=',
- indexPatternId: '=',
- tooltipPlacement: '@'
- },
- template,
- link: link
- };
+ return reactDirective(
+ InfluencersList,
+ undefined,
+ { restrict: 'E' }
+ );
});
diff --git a/x-pack/plugins/ml/public/components/influencers_list/styles/main.less b/x-pack/plugins/ml/public/components/influencers_list/styles/main.less
index ba36498b065b04..3fe182b89c7cea 100644
--- a/x-pack/plugins/ml/public/components/influencers_list/styles/main.less
+++ b/x-pack/plugins/ml/public/components/influencers_list/styles/main.less
@@ -1,59 +1,26 @@
.ml-influencers-list {
- width: 100%;
- padding: 0px 0px;
line-height: 1.45;
- .visualize-error {
- display: inline;
-
- h4 {
- font-size: 16px;
- margin-top: 65px;
- }
- }
-
- .section-label {
- background-color: #9c9fa6;
- color: #ffffff;
- font-size:13px;
- /* eui h3 equivalent font-weight */
- font-weight: 500;
- margin-bottom: 5px;
- padding: 2px 5px;
- }
-
- .influencer-content {
- padding-right: 4px;
- padding-left: 4px;
- }
-
.field-label {
font-size: 12px;
- padding-left: 2px;
text-align: left;
- .influencerfieldvalue {
- max-width: calc(~"100% - 40px");
+ .field-value {
+ max-width: calc(~"100% - 34px");
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: inline-block;
vertical-align: bottom;
}
-
- .filter-buttons {
- padding-left: 5px;
- display: inline-block;
- vertical-align: bottom;
- }
}
.progress {
display:inline-block;
- width: calc(~"100% - 40px");
- height: 20px;
+ width: calc(~"100% - 34px");
+ height: 22px;
min-width: 70px;
- margin-bottom: 2px;
+ margin-bottom: 0px;
color: #555;
background-color : transparent;
@@ -62,63 +29,76 @@
}
.progress-bar {
- height: 6px;
- margin: 6px 0px;
+ height: 2px;
+ margin-top: 8px;
text-align: right;
line-height: 18px;
- border-radius: 5px;
display: inline-block;
}
+ }
- .progress-bar.critical {
+ .progress.critical {
+ .progress-bar {
background-color: #fe5050;
}
+ .score-label {
+ border-color: #fe5050;
+ }
+ }
- .progress-bar.major {
+ .progress.major {
+ .progress-bar {
background-color: #fba740;
}
+ .score-label {
+ border-color: #fba740;
+ }
+ }
- .progress-bar.minor {
+ .progress.minor {
+ .progress-bar {
background-color: #ffdd00;
}
+ .score-label {
+ border-color: #ffdd00;
+ }
+ }
- .progress-bar.warning {
+ .progress.warning {
+ .progress-bar {
background-color: #8bc8fb;
}
-
+ .score-label {
+ border-color: #8bc8fb;
+ }
}
.score-label {
- margin-left: 3px;
text-align: center;
- color: #444444;
- padding: 2px 2px 2px 2px;
- border-radius: 4px;
line-height: 14px;
white-space: nowrap;
font-size: 12px;
- display: inline-block;
+ display: inline;
+ margin-left: 4px;
}
- .score-label.total-score-label {
+ .total-score-label {
width: 32px;
vertical-align: top;
- background-color: #444444;
- color: white;
+ text-align: center;
+ color: #555;
font-size: 11px;
+ line-height: 14px;
+ border-radius: 4px;
+ padding: 2px;
+ margin-top: 1px;
+ display: inline-block;
+ border: 1px solid #bbbbbb;
}
}
.ml-influencers-list-tooltip {
- color: #ffffff;
- font-family: Roboto, Droid, Helvetica Neue, Helvetica, Arial, sans-serif;
- font-size: 12px;
- text-align: left;
-
- hr {
- margin-top: 3px;
- margin-bottom: 3px;
- border-color: #95a5a6;
- }
+ word-break: break-all;
}
+
diff --git a/x-pack/plugins/ml/public/explorer/explorer.html b/x-pack/plugins/ml/public/explorer/explorer.html
index 9fcf3d90b9aac8..3ec8ca7056be8d 100644
--- a/x-pack/plugins/ml/public/explorer/explorer.html
+++ b/x-pack/plugins/ml/public/explorer/explorer.html
@@ -37,10 +37,8 @@
Top Influencers
-
+ influencers="influencers"
+ />
diff --git a/x-pack/plugins/ml/public/explorer/explorer_controller.js b/x-pack/plugins/ml/public/explorer/explorer_controller.js
index 74c0e1cbec2806..2ac6c515a760e8 100644
--- a/x-pack/plugins/ml/public/explorer/explorer_controller.js
+++ b/x-pack/plugins/ml/public/explorer/explorer_controller.js
@@ -588,8 +588,8 @@ module.controller('MlExplorerController', function (
MAX_INFLUENCER_FIELD_VALUES
).then((resp) => {
// TODO - sort the influencers keys so that the partition field(s) are first.
- $scope.influencersData = resp.influencers;
- console.log('Explorer top influencers data set:', $scope.influencersData);
+ $scope.influencers = resp.influencers;
+ console.log('Explorer top influencers data set:', $scope.influencers);
finish(counter);
});
diff --git a/x-pack/plugins/ml/public/formatters/abbreviate_whole_number.js b/x-pack/plugins/ml/public/formatters/abbreviate_whole_number.js
index 51748c71fe1095..802aeb3a5d19c3 100644
--- a/x-pack/plugins/ml/public/formatters/abbreviate_whole_number.js
+++ b/x-pack/plugins/ml/public/formatters/abbreviate_whole_number.js
@@ -12,9 +12,6 @@
*/
import numeral from '@elastic/numeral';
-import { uiModules } from 'ui/modules';
-const module = uiModules.get('apps/ml');
-
export function abbreviateWholeNumber(value, maxDigits) {
const maxNumDigits = (maxDigits !== undefined ? maxDigits : 3);
if (Math.abs(value) < Math.pow(10, maxNumDigits)) {
@@ -23,6 +20,3 @@ export function abbreviateWholeNumber(value, maxDigits) {
return numeral(value).format('0a');
}
}
-
-// TODO - remove the filter once all uses of the abbreviateWholeNumber Angular filter have been removed.
-module.filter('abbreviateWholeNumber', () => abbreviateWholeNumber);