Skip to content

Commit

Permalink
[ML] Migrate ml-info-icon to React/EUI (elastic#19003)
Browse files Browse the repository at this point in the history
This replaces the angular based tooltip used in the ml-info-icon directive with one based on React/EuiTooltip. It supports transclusion and rendering of angular template snippets inside the tooltip. Because the DOM structure of the EuiTooltips and the angular/bootstrap tooltips is quite different there is a bit more code involved to achieve this, but the result is that for this we don't have to change the markup in the original angular templates where ml-info-icon is used.
  • Loading branch information
walterra committed May 17, 2018
1 parent 0527824 commit 20199b4
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 27 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/ml/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'plugins/ml/explorer';
import 'plugins/ml/timeseriesexplorer';
import 'plugins/ml/components/form_label';
import 'plugins/ml/components/json_tooltip';
import 'plugins/ml/components/tooltip';
import 'plugins/ml/components/confirm_modal';
import 'plugins/ml/components/nav_menu';
import 'plugins/ml/components/loading_indicator';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module.directive('mlFormLabel', function () {
transclude: true,
template: `
<label class="kuiFormLabel" id="ml_aria_label_{{labelId}}" ng-transclude></label>
<i ml-info-icon="{{labelId}}" tooltip-append-to-body="{{tooltipAppendToBody}}" />
<i ml-info-icon="{{labelId}}" position="bottom" />
`
};
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
ml-form-label {
display: inline-flex;

i[ml-info-icon] {
span[ml-info-icon] {
margin-top: 0px;
}

i[ml-info-icon],
span[ml-info-icon],
label {
display: block;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/



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

Expand Down Expand Up @@ -46,6 +44,7 @@ describe('ML - <ml-info-icon>', () => {
expect(scope.id).to.be(id);
expect(scope.text).to.be('');
});

it('Initialize with existing tooltip attribute', () => {
const id = 'new_job_id';
$element = $compile(`<i ml-info-icon="${id}" />`)($scope);
Expand All @@ -57,7 +56,7 @@ describe('ML - <ml-info-icon>', () => {
expect(scope.text).to.be(tooltips[id].text);

// test the rendered span element which should be referenced by aria-describedby
const span = $element.find('span');
const span = $element.find('span.ml-info-tooltip-text');
expect(span[0].id).to.be('ml_aria_description_' + id);
expect(span.text()).to.be(tooltips[id].text);
});
Expand Down
43 changes: 25 additions & 18 deletions x-pack/plugins/ml/public/components/json_tooltip/json_tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,33 @@ module.service('mlJsonTooltipService', function () {
return '';
}
};
})
});

// directive for placing an i icon with a popover tooltip anywhere on a page
// tooltip format: <i ml-info-icon="the_id" />
// tooltip format: <i ml-info-icon="<the_id>" />
// the_id will match an entry in tooltips.json
.directive('mlInfoIcon', function () {
return {
scope: {
id: '@mlInfoIcon'
},
restrict: 'AE',
replace: true,
template: `
<i aria-hidden="true" class="fa fa-info-circle" tooltip="{{text}}">
<span id="ml_aria_description_{{id}}" class="ml-info-tooltip-text">{{text}}</span>
</i>
// the original <i ml-info-icon="<the_id>" /> will be replaced with
// <span ml-tooltip="<tooltip_text>">...</span> to transform the DOM structure
// into one which is suitable for use with EuiTooltip. Because of this replacement
// span[ml-info-icon] has to be used instead of i[ml-info-icon] when using CSS.
module.directive('mlInfoIcon', function () {
return {
scope: {
id: '@mlInfoIcon',
position: '@'
},
restrict: 'AE',
replace: true,
template: `
<span ml-tooltip="{{text}}">
<i aria-hidden="true" class="fa fa-info-circle">
<span id="ml_aria_description_{{id}}" class="ml-info-tooltip-text">{{text}}</span>
</i>
</span>
`,
controller: function ($scope) {
$scope.text = (tooltips[$scope.id]) ? tooltips[$scope.id].text : '';
}
};
controller: function ($scope) {
$scope.text = (tooltips[$scope.id]) ? tooltips[$scope.id].text : '';
}
};

});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
i[ml-info-icon] {
span[ml-info-icon] {
color: #888;
margin: 3px 4px;
transition: color 0.15s;
Expand All @@ -8,7 +8,7 @@ i[ml-info-icon] {
}
}

i[ml-info-icon]:hover {
span[ml-info-icon]:hover {
color: #444;
transition: color 0.15s 0.15s;
}
7 changes: 7 additions & 0 deletions x-pack/plugins/ml/public/components/tooltip/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 './tooltip_directive';
43 changes: 43 additions & 0 deletions x-pack/plugins/ml/public/components/tooltip/tooltip_directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 React from 'react';
import ReactDOM from 'react-dom';

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

import { Tooltip } from './tooltip_view';

module.directive('mlTooltip', function ($compile) {
const link = function (scope, element) {
const content = element.html();
element.html('');

const props = {
position: scope.position,
text: scope.text,
transclude: (el) => {
const transcludeScope = scope.$new();
const compiled = $compile(content)(transcludeScope);
el.append(compiled[0]);
}
};

ReactDOM.render(
React.createElement(Tooltip, props),
element[0]
);
};

return {
restrict: 'A',
replace: true,
scope: false,
transclude: false,
link
};
});
26 changes: 26 additions & 0 deletions x-pack/plugins/ml/public/components/tooltip/tooltip_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 PropTypes from 'prop-types';

import React from 'react';

import {
EuiToolTip
} from '@elastic/eui';

export function Tooltip({ position = 'top', text, transclude }) {
return (
<EuiToolTip position={position} content={text}>
<span ref={transclude} />
</EuiToolTip>
);
}
Tooltip.propTypes = {
position: PropTypes.string,
text: PropTypes.string,
transclude: PropTypes.func
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
text-overflow: ellipsis;
padding-bottom: 10px;

i[ml-info-icon] {
span[ml-info-icon] {
font-size: 14px;
}
}
Expand Down

0 comments on commit 20199b4

Please sign in to comment.