diff --git a/src/legacy/core_plugins/timelion/public/components/timelionhelp_tabs.js b/src/legacy/core_plugins/timelion/public/components/timelionhelp_tabs.js new file mode 100644 index 00000000000000..833478ec974416 --- /dev/null +++ b/src/legacy/core_plugins/timelion/public/components/timelionhelp_tabs.js @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import PropTypes from 'prop-types'; +import React from 'react'; + +import { + EuiTabs, + EuiTab +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +function handleClick(activateTab, tabName) { + activateTab(tabName); +} + +export function TimelionHelpTabs(props) { + return ( + + handleClick(props.activateTab, 'funcref')} + > + + + handleClick(props.activateTab, 'keyboardtips')} + > + + + + ); +} + +TimelionHelpTabs.propTypes = { + activeTab: PropTypes.string.isRequired, + activateTab: PropTypes.func.isRequired +}; + diff --git a/src/legacy/core_plugins/timelion/public/components/timelionhelp_tabs_directive.js b/src/legacy/core_plugins/timelion/public/components/timelionhelp_tabs_directive.js new file mode 100644 index 00000000000000..4cee7b5159bbc3 --- /dev/null +++ b/src/legacy/core_plugins/timelion/public/components/timelionhelp_tabs_directive.js @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import 'ngreact'; + +import { wrapInI18nContext } from 'ui/i18n'; +import { uiModules } from 'ui/modules'; +const module = uiModules.get('apps/timelion', ['react']); + +import { TimelionHelpTabs } from './timelionhelp_tabs'; + +module.directive('timelionHelpTabs', function (reactDirective) { + return reactDirective( + wrapInI18nContext(TimelionHelpTabs), + undefined, + { restrict: 'E' } + ); +}); diff --git a/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.html b/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.html index 38dcd7c1027b79..54ad485c51cc43 100644 --- a/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.html +++ b/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.html @@ -611,131 +611,131 @@ i18n-default-message="Help" > - - -
-
- - . -
+ + -
- - - - - - - - -
.{{function.name}}(){{function.help}}
-
- - - - - - - - - - - -
{{arg.name}}{{arg.types.join(', ')}}{{arg.help}}
-
- -
-
-
-
-
-
+
+
+ + . +
- -
- -
-
-
-
-
Ctrl/Cmd + Enter
-
-
+
+ + + + + + + + +
.{{function.name}}(){{function.help}}
+
+ + + + + + + + + + + +
{{arg.name}}{{arg.types.join(', ')}}{{arg.help}}
+
+ +
+
+
+
+
- -
-
-
- -
-
-
-
Enter/Tab
-
-
Esc
-
-
-
- -
+
+ +
+
+
+
+
Ctrl/Cmd + Enter
+
+
+ + +
+
+
+ +
+
+
+
Enter/Tab
+
+
Esc
+
+
+
diff --git a/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.js b/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.js index f9a44fc03b6d2d..97ca1b2edb9fb5 100644 --- a/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.js +++ b/src/legacy/core_plugins/timelion/public/directives/timelion_help/timelion_help.js @@ -21,6 +21,7 @@ import template from './timelion_help.html'; import { uiModules } from 'ui/modules'; import _ from 'lodash'; import moment from 'moment'; +import '../../components/timelionhelp_tabs_directive'; const app = uiModules.get('apps/timelion', []); @@ -34,6 +35,11 @@ app.directive('timelionHelp', function ($http, i18n) { details: null }; + $scope.activeTab = 'funcref'; + $scope.activateTab = function (tabName) { + $scope.activeTab = tabName; + }; + function init() { $scope.es = { invalidCount: 0 diff --git a/src/legacy/ui/public/angular-bootstrap/index.js b/src/legacy/ui/public/angular-bootstrap/index.js index dd3ca05c9ebd90..e05df0df444588 100644 --- a/src/legacy/ui/public/angular-bootstrap/index.js +++ b/src/legacy/ui/public/angular-bootstrap/index.js @@ -33,7 +33,6 @@ angular.module('ui.bootstrap', [ 'ui.bootstrap.tooltip', 'ui.bootstrap.popover', 'ui.bootstrap.progressbar', - 'ui.bootstrap.tabs', 'ui.bootstrap.timepicker', 'ui.bootstrap.typeahead' ]); @@ -50,8 +49,6 @@ angular.module('ui.bootstrap.tpls', [ 'template/progressbar/bar.html', 'template/progressbar/progress.html', 'template/progressbar/progressbar.html', - 'template/tabs/tab.html', - 'template/tabs/tabset.html', 'template/timepicker/timepicker.html', 'template/typeahead/typeahead-match.html', 'template/typeahead/typeahead-popup.html' @@ -66,7 +63,6 @@ import './pagination/pagination'; import './popover/popover'; import './position/position'; import './progressbar/progressbar'; -import './tabs/tabs'; import './timepicker/timepicker'; import './tooltip/tooltip'; import './transition/transition'; @@ -138,18 +134,6 @@ angular.module('template/progressbar/progressbar.html', []).run(['$templateCache $templateCache.put('template/progressbar/progressbar.html', progressbar); }]); -import tab from './tabs/tab.html'; - -angular.module('template/tabs/tab.html', []).run(['$templateCache', function($templateCache) { - $templateCache.put('template/tabs/tab.html', tab); -}]); - -import tabset from './tabs/tabset.html'; - -angular.module('template/tabs/tabset.html', []).run(['$templateCache', function($templateCache) { - $templateCache.put('template/tabs/tabset.html', tabset); -}]); - import timepicker from './timepicker/timepicker.html'; angular.module('template/timepicker/timepicker.html', []).run(['$templateCache', function($templateCache) { diff --git a/src/legacy/ui/public/angular-bootstrap/tabs/tab.html b/src/legacy/ui/public/angular-bootstrap/tabs/tab.html deleted file mode 100755 index d76dd67caf234d..00000000000000 --- a/src/legacy/ui/public/angular-bootstrap/tabs/tab.html +++ /dev/null @@ -1,3 +0,0 @@ -
  • - {{heading}} -
  • diff --git a/src/legacy/ui/public/angular-bootstrap/tabs/tabs.js b/src/legacy/ui/public/angular-bootstrap/tabs/tabs.js deleted file mode 100755 index d6f8161d6a57a7..00000000000000 --- a/src/legacy/ui/public/angular-bootstrap/tabs/tabs.js +++ /dev/null @@ -1,279 +0,0 @@ - -/** - * @ngdoc overview - * @name ui.bootstrap.tabs - * - * @description - * AngularJS version of the tabs directive. - */ - -angular.module('ui.bootstrap.tabs', []) - -.controller('TabsetController', ['$scope', function TabsetCtrl($scope) { - var ctrl = this, - tabs = ctrl.tabs = $scope.tabs = []; - - ctrl.select = function(selectedTab) { - angular.forEach(tabs, function(tab) { - if (tab.active && tab !== selectedTab) { - tab.active = false; - tab.onDeselect(); - } - }); - selectedTab.active = true; - selectedTab.onSelect(); - }; - - ctrl.addTab = function addTab(tab) { - tabs.push(tab); - // we can't run the select function on the first tab - // since that would select it twice - if (tabs.length === 1) { - tab.active = true; - } else if (tab.active) { - ctrl.select(tab); - } - }; - - ctrl.removeTab = function removeTab(tab) { - var index = tabs.indexOf(tab); - //Select a new tab if the tab to be removed is selected and not destroyed - if (tab.active && tabs.length > 1 && !destroyed) { - //If this is the last tab, select the previous tab. else, the next tab. - var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1; - ctrl.select(tabs[newActiveIndex]); - } - tabs.splice(index, 1); - }; - - var destroyed; - $scope.$on('$destroy', function() { - destroyed = true; - }); -}]) - -/** - * @ngdoc directive - * @name ui.bootstrap.tabs.directive:tabset - * @restrict EA - * - * @description - * Tabset is the outer container for the tabs directive - * - * @param {boolean=} vertical Whether or not to use vertical styling for the tabs. - * @param {boolean=} justified Whether or not to use justified styling for the tabs. - * - * @example - - - - First Content! - Second Content! - -
    - - First Vertical Content! - Second Vertical Content! - - - First Justified Content! - Second Justified Content! - -
    -
    - */ -.directive('tabset', function() { - return { - restrict: 'EA', - transclude: true, - replace: true, - scope: { - type: '@' - }, - controller: 'TabsetController', - templateUrl: 'template/tabs/tabset.html', - link: function(scope, element, attrs) { - scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; - scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; - } - }; -}) - -/** - * @ngdoc directive - * @name ui.bootstrap.tabs.directive:tab - * @restrict EA - * - * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}. - * @param {string=} select An expression to evaluate when the tab is selected. - * @param {boolean=} active A binding, telling whether or not this tab is selected. - * @param {boolean=} disabled A binding, telling whether or not this tab is disabled. - * - * @description - * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}. - * - * @example - - -
    - - -
    - - First Tab - - Alert me! - Second Tab, with alert callback and html heading! - - - {{item.content}} - - -
    -
    - - function TabsDemoCtrl($scope) { - $scope.items = [ - { title:"Dynamic Title 1", content:"Dynamic Item 0" }, - { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true } - ]; - - $scope.alertMe = function() { - setTimeout(function() { - alert("You've selected the alert tab!"); - }); - }; - }; - -
    - */ - -/** - * @ngdoc directive - * @name ui.bootstrap.tabs.directive:tabHeading - * @restrict EA - * - * @description - * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element. - * - * @example - - - - - HTML in my titles?! - And some content, too! - - - Icon heading?!? - That's right. - - - - - */ -.directive('tab', ['$parse', function($parse) { - return { - require: '^tabset', - restrict: 'EA', - replace: true, - templateUrl: 'template/tabs/tab.html', - transclude: true, - scope: { - active: '=?', - heading: '@', - onSelect: '&select', //This callback is called in contentHeadingTransclude - //once it inserts the tab's content into the dom - onDeselect: '&deselect' - }, - controller: function() { - //Empty controller so other directives can require being 'under' a tab - }, - compile: function(elm, attrs, transclude) { - return function postLink(scope, elm, attrs, tabsetCtrl) { - scope.$watch('active', function(active) { - if (active) { - tabsetCtrl.select(scope); - } - }); - - scope.disabled = false; - if ( attrs.disabled ) { - scope.$parent.$watch($parse(attrs.disabled), function(value) { - scope.disabled = !! value; - }); - } - - scope.select = function() { - if ( !scope.disabled ) { - scope.active = true; - } - }; - - tabsetCtrl.addTab(scope); - scope.$on('$destroy', function() { - tabsetCtrl.removeTab(scope); - }); - - //We need to transclude later, once the content container is ready. - //when this link happens, we're inside a tab heading. - scope.$transcludeFn = transclude; - }; - } - }; -}]) - -.directive('tabHeadingTransclude', [function() { - return { - restrict: 'A', - require: '^tab', - link: function(scope, elm, attrs, tabCtrl) { - scope.$watch('headingElement', function updateHeadingElement(heading) { - if (heading) { - elm.html(''); - elm.append(heading); - } - }); - } - }; -}]) - -.directive('tabContentTransclude', function() { - return { - restrict: 'A', - require: '^tabset', - link: function(scope, elm, attrs) { - var tab = scope.$eval(attrs.tabContentTransclude); - - //Now our tab is ready to be transcluded: both the tab heading area - //and the tab content area are loaded. Transclude 'em both. - tab.$transcludeFn(tab.$parent, function(contents) { - angular.forEach(contents, function(node) { - if (isTabHeading(node)) { - //Let tabHeadingTransclude know. - tab.headingElement = node; - } else { - elm.append(node); - } - }); - }); - } - }; - function isTabHeading(node) { - return node.tagName && ( - node.hasAttribute('tab-heading') || - node.hasAttribute('data-tab-heading') || - node.tagName.toLowerCase() === 'tab-heading' || - node.tagName.toLowerCase() === 'data-tab-heading' - ); - } -}) - -; diff --git a/src/legacy/ui/public/angular-bootstrap/tabs/tabset.html b/src/legacy/ui/public/angular-bootstrap/tabs/tabset.html deleted file mode 100755 index b953a49161925d..00000000000000 --- a/src/legacy/ui/public/angular-bootstrap/tabs/tabset.html +++ /dev/null @@ -1,10 +0,0 @@ -
    - -
    -
    -
    -
    -
    diff --git a/x-pack/plugins/searchprofiler/public/app.js b/x-pack/plugins/searchprofiler/public/app.js index ae720e587a7357..ee879f188aa3bf 100644 --- a/x-pack/plugins/searchprofiler/public/app.js +++ b/x-pack/plugins/searchprofiler/public/app.js @@ -18,6 +18,7 @@ import _ from 'lodash'; import 'ace'; import 'angular-ui-ace'; import 'plugins/searchprofiler/directives'; +import './components/searchprofiler_tabs_directive'; import { Range } from './range'; import { nsToPretty } from 'plugins/searchprofiler/filters/ns_to_pretty'; import { msToPretty } from 'plugins/searchprofiler/filters/ms_to_pretty'; @@ -149,10 +150,10 @@ function profileVizController($scope, $route, $interval, $http, HighlightService return id.replace('[', '').replace(']', ''); }); } - $scope.hasAggregations = data[0].aggregations != null && data[0].aggregations.length > 0; - $scope.hasSearch = data[0].searches != null && data[0].searches.length > 0; $scope.profileResponse = data; - if (!$scope.hasAggregations) { + + const hasAggregations = data[0].aggregations != null && data[0].aggregations.length > 0; + if (!hasAggregations) { // No aggs, reset back to search panel $scope.activateTab('search'); } @@ -163,7 +164,7 @@ function profileVizController($scope, $route, $interval, $http, HighlightService $scope.resetHighlightPanel(); // Reset active tab map $scope.activeTab = {}; - if (tab === 'aggregations' && $scope.hasAggregations) { + if (tab === 'aggregations') { $scope.activeTab.aggregations = true; } else { // Everything has a search, so default to this diff --git a/x-pack/plugins/searchprofiler/public/components/searchprofiler_tabs.js b/x-pack/plugins/searchprofiler/public/components/searchprofiler_tabs.js new file mode 100644 index 00000000000000..5394e56cef70ad --- /dev/null +++ b/x-pack/plugins/searchprofiler/public/components/searchprofiler_tabs.js @@ -0,0 +1,65 @@ +/* + * 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 'lodash'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import { + EuiTabs, + EuiTab +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +function hasSearch(profileResponse) { + const aggs = _.get(profileResponse, '[0].searches', []); + return aggs.length > 0; +} + +function hasAggregations(profileResponse) { + const aggs = _.get(profileResponse, '[0].aggregations', []); + return aggs.length > 0; +} + + +function handleClick(activateTab, tabName) { + activateTab(tabName); +} + +export function SearchProfilerTabs(props) { + return ( + + handleClick(props.activateTab, 'search')} + > + + + handleClick(props.activateTab, 'aggregations')} + > + + + + ); +} + +SearchProfilerTabs.propTypes = { + activeTab: PropTypes.any.isRequired, + activateTab: PropTypes.func.isRequired, + profileResponse: PropTypes.array.isRequired, +}; diff --git a/x-pack/plugins/searchprofiler/public/components/searchprofiler_tabs_directive.js b/x-pack/plugins/searchprofiler/public/components/searchprofiler_tabs_directive.js new file mode 100644 index 00000000000000..6cfeb97f05ebd4 --- /dev/null +++ b/x-pack/plugins/searchprofiler/public/components/searchprofiler_tabs_directive.js @@ -0,0 +1,21 @@ +/* + * 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 'ngreact'; + +import { wrapInI18nContext } from 'ui/i18n'; +import { uiModules } from 'ui/modules'; +const module = uiModules.get('apps/searchprofiler', ['react']); + +import { SearchProfilerTabs } from './searchprofiler_tabs'; + +module.directive('searchProfilerTabs', function (reactDirective) { + return reactDirective( + wrapInI18nContext(SearchProfilerTabs), + undefined, + { restrict: 'E' } + ); +}); diff --git a/x-pack/plugins/searchprofiler/public/templates/index.html b/x-pack/plugins/searchprofiler/public/templates/index.html index 9ddac34a3d0733..e5039e937c5561 100644 --- a/x-pack/plugins/searchprofiler/public/templates/index.html +++ b/x-pack/plugins/searchprofiler/public/templates/index.html @@ -2,20 +2,17 @@
    - - - - - - - - + + + + + +