From 20199b4f462c8e67d1d79306e48944c829c89fc9 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Thu, 17 May 2018 09:21:39 +0200 Subject: [PATCH] [ML] Migrate ml-info-icon to React/EUI (#19003) 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. --- x-pack/plugins/ml/public/app.js | 1 + .../components/form_label/form_label.js | 2 +- .../components/form_label/styles/main.less | 4 +- .../json_tooltip/__tests__/json_tooltip.js | 5 +-- .../components/json_tooltip/json_tooltip.js | 43 +++++++++++-------- .../components/json_tooltip/styles/main.less | 4 +- .../ml/public/components/tooltip/index.js | 7 +++ .../components/tooltip/tooltip_directive.js | 43 +++++++++++++++++++ .../public/components/tooltip/tooltip_view.js | 26 +++++++++++ .../jobs_list/edit_job_modal/styles/main.less | 2 +- 10 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/ml/public/components/tooltip/index.js create mode 100644 x-pack/plugins/ml/public/components/tooltip/tooltip_directive.js create mode 100644 x-pack/plugins/ml/public/components/tooltip/tooltip_view.js diff --git a/x-pack/plugins/ml/public/app.js b/x-pack/plugins/ml/public/app.js index a89033a261eb42..ba1d17bdd9b8d8 100644 --- a/x-pack/plugins/ml/public/app.js +++ b/x-pack/plugins/ml/public/app.js @@ -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'; diff --git a/x-pack/plugins/ml/public/components/form_label/form_label.js b/x-pack/plugins/ml/public/components/form_label/form_label.js index 5b145f7d3c0ede..0afba683c27f0d 100644 --- a/x-pack/plugins/ml/public/components/form_label/form_label.js +++ b/x-pack/plugins/ml/public/components/form_label/form_label.js @@ -31,7 +31,7 @@ module.directive('mlFormLabel', function () { transclude: true, template: ` - + ` }; }); diff --git a/x-pack/plugins/ml/public/components/form_label/styles/main.less b/x-pack/plugins/ml/public/components/form_label/styles/main.less index c03327fb3738f7..4965ef14d18b2a 100644 --- a/x-pack/plugins/ml/public/components/form_label/styles/main.less +++ b/x-pack/plugins/ml/public/components/form_label/styles/main.less @@ -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; } diff --git a/x-pack/plugins/ml/public/components/json_tooltip/__tests__/json_tooltip.js b/x-pack/plugins/ml/public/components/json_tooltip/__tests__/json_tooltip.js index 20bb0568143366..49b1194616fbe7 100644 --- a/x-pack/plugins/ml/public/components/json_tooltip/__tests__/json_tooltip.js +++ b/x-pack/plugins/ml/public/components/json_tooltip/__tests__/json_tooltip.js @@ -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'; @@ -46,6 +44,7 @@ describe('ML - ', () => { expect(scope.id).to.be(id); expect(scope.text).to.be(''); }); + it('Initialize with existing tooltip attribute', () => { const id = 'new_job_id'; $element = $compile(``)($scope); @@ -57,7 +56,7 @@ describe('ML - ', () => { 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); }); diff --git a/x-pack/plugins/ml/public/components/json_tooltip/json_tooltip.js b/x-pack/plugins/ml/public/components/json_tooltip/json_tooltip.js index 3df2cdaee64b94..019a11f6462fc3 100644 --- a/x-pack/plugins/ml/public/components/json_tooltip/json_tooltip.js +++ b/x-pack/plugins/ml/public/components/json_tooltip/json_tooltip.js @@ -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: +// tooltip format: // the_id will match an entry in tooltips.json - .directive('mlInfoIcon', function () { - return { - scope: { - id: '@mlInfoIcon' - }, - restrict: 'AE', - replace: true, - template: ` - +// the original will be replaced with +// ... 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: ` + + + `, - controller: function ($scope) { - $scope.text = (tooltips[$scope.id]) ? tooltips[$scope.id].text : ''; - } - }; + controller: function ($scope) { + $scope.text = (tooltips[$scope.id]) ? tooltips[$scope.id].text : ''; + } + }; - }); +}); diff --git a/x-pack/plugins/ml/public/components/json_tooltip/styles/main.less b/x-pack/plugins/ml/public/components/json_tooltip/styles/main.less index 403faa2072e85e..d33956ce3b7991 100644 --- a/x-pack/plugins/ml/public/components/json_tooltip/styles/main.less +++ b/x-pack/plugins/ml/public/components/json_tooltip/styles/main.less @@ -1,4 +1,4 @@ -i[ml-info-icon] { +span[ml-info-icon] { color: #888; margin: 3px 4px; transition: color 0.15s; @@ -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; } diff --git a/x-pack/plugins/ml/public/components/tooltip/index.js b/x-pack/plugins/ml/public/components/tooltip/index.js new file mode 100644 index 00000000000000..b789a3ec28bffe --- /dev/null +++ b/x-pack/plugins/ml/public/components/tooltip/index.js @@ -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'; diff --git a/x-pack/plugins/ml/public/components/tooltip/tooltip_directive.js b/x-pack/plugins/ml/public/components/tooltip/tooltip_directive.js new file mode 100644 index 00000000000000..bd5a7d43f7e6fd --- /dev/null +++ b/x-pack/plugins/ml/public/components/tooltip/tooltip_directive.js @@ -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 + }; +}); diff --git a/x-pack/plugins/ml/public/components/tooltip/tooltip_view.js b/x-pack/plugins/ml/public/components/tooltip/tooltip_view.js new file mode 100644 index 00000000000000..d2ebd4355a2557 --- /dev/null +++ b/x-pack/plugins/ml/public/components/tooltip/tooltip_view.js @@ -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 ( + + + + ); +} +Tooltip.propTypes = { + position: PropTypes.string, + text: PropTypes.string, + transclude: PropTypes.func +}; diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/edit_job_modal/styles/main.less b/x-pack/plugins/ml/public/jobs/jobs_list/edit_job_modal/styles/main.less index df57114441c8d9..d52dcdc4cef88a 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/edit_job_modal/styles/main.less +++ b/x-pack/plugins/ml/public/jobs/jobs_list/edit_job_modal/styles/main.less @@ -6,7 +6,7 @@ text-overflow: ellipsis; padding-bottom: 10px; - i[ml-info-icon] { + span[ml-info-icon] { font-size: 14px; } }