diff --git a/src/legacy/core_plugins/console/public/index.html b/src/legacy/core_plugins/console/public/index.html index 91bcb05bf64c..f2454baf4381 100644 --- a/src/legacy/core_plugins/console/public/index.html +++ b/src/legacy/core_plugins/console/public/index.html @@ -1,7 +1,7 @@ - +>
diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 0bd8d386c426..895e3e9c98b6 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -185,7 +185,7 @@ class SearchBarUI extends Component { } public render() { - // This is needed, as kbn-top-nav-v2 might render before npSetup.core.uiSettings is set. + // This is needed, as kbn-top-nav might render before npSetup.core.uiSettings is set. // This won't be needed when it's loaded exclusively with React. if (!this.props.uiSettings) { return null; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index bf9d97b3867f..b6dcfd1e4de5 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -3,7 +3,7 @@ ng-class="{'dshAppContainer--withMargins': model.useMargins}" > - - + - - +
diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html index 101a45819829..022c4bfa2f0b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html @@ -32,7 +32,7 @@ All visualizaions also have least a timepicker \ autorefresh component, which is why show-query-bar is set to "true". --> - - + - - +
diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index d898c06a73fd..1c3a9c006acf 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -18,7 +18,6 @@ @import './error_url_overflow/index'; @import './exit_full_screen/index'; @import './field_editor/index'; -@import './kbn_top_nav/index'; @import './notify/index'; @import './share/index'; @import './style_compile/index'; diff --git a/src/legacy/ui/public/kbn_top_nav/__tests__/kbn_top_nav.js b/src/legacy/ui/public/kbn_top_nav/__tests__/kbn_top_nav.js deleted file mode 100644 index 6aff951f581e..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/__tests__/kbn_top_nav.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import { assign, pluck } from 'lodash'; -import $ from 'jquery'; - -import '../kbn_top_nav'; -import { KbnTopNavControllerProvider } from '../kbn_top_nav_controller'; - -describe('kbnTopNav directive', function () { - let build; - let KbnTopNavController; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function ($compile, $rootScope, Private) { - KbnTopNavController = Private(KbnTopNavControllerProvider); - - build = function (scopeVars = {}, attrs = {}) { - const $el = $('').attr(attrs); - const $scope = $rootScope.$new(); - assign($scope, scopeVars); - $compile($el)($scope); - $scope.$digest(); - return { $el, $scope }; - }; - })); - - it('sets the proper functions on the kbnTopNav prop on scope', function () { - const { $scope } = build(); - expect($scope.kbnTopNav.open).to.be.a(Function); - expect($scope.kbnTopNav.close).to.be.a(Function); - expect($scope.kbnTopNav.getCurrent).to.be.a(Function); - expect($scope.kbnTopNav.toggle).to.be.a(Function); - }); - - it('allows config at nested keys', function () { - const scopeVars = { - kbn: { - top: { - nav: [ - { key: 'foo' } - ] - } - } - }; - - const { $scope } = build(scopeVars, { config: 'kbn.top.nav' }); - const optKeys = pluck($scope.kbnTopNav.opts, 'key'); - expect(optKeys).to.eql(['foo']); - }); - - it('uses the KbnTopNavController if passed via config attribute', function () { - const controller = new KbnTopNavController(); - const { $scope } = build({ controller }, { config: 'controller' }); - expect($scope.kbnTopNav).to.be(controller); - }); - - it('should allow setting CSS classes via className', () => { - const scope = { - config: [ - { key: 'foo', testId: 'foo', className: 'fooClass' }, - { key: 'test', testId: 'test', className: 'class1 class2' }, - ], - }; - const { $el } = build(scope, { config: 'config' }); - expect($el.find('[data-test-subj="foo"]').hasClass('fooClass')).to.be(true); - expect($el.find('[data-test-subj="test"]').hasClass('class1')).to.be(true); - expect($el.find('[data-test-subj="test"]').hasClass('class2')).to.be(true); - }); -}); diff --git a/src/legacy/ui/public/kbn_top_nav/__tests__/kbn_top_nav_controller.js b/src/legacy/ui/public/kbn_top_nav/__tests__/kbn_top_nav_controller.js deleted file mode 100644 index e0ad7ecceb9c..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/__tests__/kbn_top_nav_controller.js +++ /dev/null @@ -1,354 +0,0 @@ -/* - * 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 ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import { pluck } from 'lodash'; -import sinon from 'sinon'; -import { KbnTopNavControllerProvider } from '../kbn_top_nav_controller'; - -describe('KbnTopNavController', function () { - let KbnTopNavController; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - KbnTopNavController = Private(KbnTopNavControllerProvider); - })); - - describe('opts', function () { - it('supports giving it no options', function () { - new KbnTopNavController(); - }); - - it('support empty options list', function () { - new KbnTopNavController([]); - }); - - describe('key:', function () { - it('requires every opt to have a key', function () { - expect(function () { - new KbnTopNavController([ - { foo: 'bar' } - ]); - }).to.throwError(TypeError); - }); - }); - - describe('label:', function () { - it('gives each menu item a label based on the key', function () { - const controller = new KbnTopNavController([ - { key: 'foo' }, - { key: 'Bar' }, - { key: '1234' }, - ]); - - expect(pluck(controller.opts, 'label')).to.eql([ - 'Foo', - 'Bar', - '1234', - ]); - }); - }); - - describe('description:', function () { - it('defaults to "Toggle ${label} view" when using templates', function () { - const controller = new KbnTopNavController([ - { key: 'foo', template: '

' }, - { key: 'Bar', description: 'not the default' }, - { key: '1234', run: ()=>{} }, - ]); - - expect(pluck(controller.opts, 'description')).to.eql([ - 'Toggle Foo view', - 'not the default', - '1234', - ]); - }); - }); - - describe('hideButton:', function () { - it('defaults to a function that returns false', function () { - const controller = new KbnTopNavController([ - { key: 'foo' }, - { key: '1234' }, - ]); - - pluck(controller.opts, 'hideButton').forEach(prop => { - expect(prop).to.be.a(Function); - expect(prop()).to.eql(false); - }); - }); - - it('excludes opts from opts when set to true', function () { - const controller = new KbnTopNavController([ - { key: 'foo' }, - { key: '1234', hideButton: true }, - ]); - - expect(controller.menuItems).to.have.length(1); - }); - - it('excludes opts from opts when set to a function that returns true', function () { - const controller = new KbnTopNavController([ - { key: 'foo' }, - { key: '1234', hideButton: () => true }, - ]); - - expect(controller.menuItems).to.have.length(1); - }); - }); - - describe('disableButton:', function () { - it('defaults to a function that returns false', function () { - const controller = new KbnTopNavController([ - { key: 'foo' }, - { key: '1234' }, - ]); - - pluck(controller.opts, 'disableButton').forEach(prop => { - expect(prop).to.be.a(Function); - expect(prop()).to.eql(false); - }); - }); - }); - - describe('tooltip:', function () { - it('defaults to a function that returns undefined', function () { - const controller = new KbnTopNavController([ - { key: 'foo' }, - { key: '1234' }, - ]); - - pluck(controller.opts, 'tooltip').forEach(prop => { - expect(prop).to.be.a(Function); - expect(prop()).to.eql(undefined); - }); - }); - }); - - describe('run:', function () { - it('defaults to a function that toggles the key', function () { - const controller = new KbnTopNavController([ - { key: 'foo', template: 'Hi' } - ]); - - const opt = controller.opts[0]; - expect(opt.run).to.be.a('function'); - expect(controller.getCurrent()).to.not.be(opt.key); - opt.run(opt); - expect(controller.getCurrent()).to.be(opt.key); - opt.run(opt); - expect(controller.getCurrent()).to.not.be(opt.key); - }); - - it('uses the supplied run function otherwise', function (done) { // eslint-disable-line mocha/handle-done-callback - const controller = new KbnTopNavController([ - { key: 'foo', run: done } - ]); - - const opt = controller.opts[0]; - opt.run(); - }); - }); - }); - - describe('methods', function () { - const init = function () { - const controller = new KbnTopNavController([ - { key: 'foo', template: 'Say Foo!' }, - { key: 'bar', template: 'Whisper Bar' }, - ]); - const render = sinon.spy(controller, '_render'); - const setCurrent = sinon.spy(controller, 'setCurrent'); - const getCurrent = sinon.spy(controller, 'getCurrent'); - - return { controller, render, setCurrent, getCurrent }; - }; - - describe('#setCurrent([key])', function () { - it('assigns the passed key to the current key', function () { - const { controller } = init(); - expect(controller.getCurrent()).to.not.be('foo'); - controller.setCurrent('foo'); - expect(controller.getCurrent()).to.be('foo'); - }); - - it('throws if the key does not match a known template', function () { - const { controller } = init(); - expect(function () { - controller.setCurrent('june'); - }).to.throwError(/unknown template key/); - }); - - it('sets to "null" for falsy values', function () { - const { controller } = init(); - - controller.setCurrent(); - expect(controller.getCurrent()).to.be(null); - - controller.setCurrent(false); - expect(controller.getCurrent()).to.be(null); - - controller.setCurrent(null); - expect(controller.getCurrent()).to.be(null); - - controller.setCurrent(''); - expect(controller.getCurrent()).to.be(null); - }); - - it('rerenders after setting', function () { - const { controller, render } = init(); - - sinon.assert.notCalled(render); - controller.setCurrent('bar'); - sinon.assert.calledOnce(render); - controller.setCurrent('bar'); - sinon.assert.calledTwice(render); - }); - }); - - describe('#isCurrent(key)', function () { - it('returns true when key matches', function () { - const { controller } = init(); - - controller.setCurrent('foo'); - expect(controller.isCurrent('foo')).to.be(true); - expect(controller.isCurrent('bar')).to.be(false); - - controller.setCurrent('bar'); - expect(controller.isCurrent('bar')).to.be(true); - expect(controller.isCurrent('foo')).to.be(false); - }); - }); - - describe('#open(key)', function () { - it('alias for set', function () { - const { controller, setCurrent } = init(); - - controller.open('foo'); - sinon.assert.calledOnce(setCurrent); - sinon.assert.calledWithExactly(setCurrent, 'foo'); - }); - }); - - describe('#close()', function () { - it('set the current key to null', function () { - const { controller } = init(); - - controller.open('foo'); - controller.close(); - expect(controller.getCurrent()).to.be(null); - }); - }); - - describe('#close(key)', function () { - it('sets to null if key is open', function () { - const { controller } = init(); - - expect(controller.getCurrent()).to.be(null); - controller.close('foo'); - expect(controller.getCurrent()).to.be(null); - controller.open('foo'); - expect(controller.getCurrent()).to.be('foo'); - controller.close('foo'); - expect(controller.getCurrent()).to.be(null); - }); - - it('ignores if other key is open', function () { - const { controller } = init(); - - expect(controller.getCurrent()).to.be(null); - controller.open('foo'); - expect(controller.getCurrent()).to.be('foo'); - controller.close('bar'); - expect(controller.getCurrent()).to.be('foo'); - }); - }); - - describe('#toggle()', function () { - it('opens if closed', function () { - const { controller } = init(); - - expect(controller.getCurrent()).to.be(null); - controller.toggle('foo'); - expect(controller.getCurrent()).to.be('foo'); - }); - - it('opens if other is open', function () { - const { controller } = init(); - - controller.open('bar'); - expect(controller.getCurrent()).to.be('bar'); - controller.toggle('foo'); - expect(controller.getCurrent()).to.be('foo'); - }); - - it('closes if open', function () { - const { controller } = init(); - - controller.open('bar'); - expect(controller.getCurrent()).to.be('bar'); - controller.toggle('bar'); - expect(controller.getCurrent()).to.be(null); - }); - }); - - describe('#addItems(opts)', function () { - it('should append to existing menu items', function () { - const { controller } = init(); - const newItems = [ - { key: 'green', template: 'Green means go' }, - { key: 'red', template: 'Red means stop' }, - ]; - - expect(controller.menuItems).to.have.length(2); - controller.addItems(newItems); - expect(controller.menuItems).to.have.length(4); - - // check that the items were added - const matches = controller.menuItems.reduce((acc, item) => { - if (item.key === 'green' || item.key === 'red') { - acc[item.key] = item; - } - return acc; - }, {}); - expect(matches).to.have.property('green'); - expect(matches.green).to.have.property('run'); - expect(matches).to.have.property('red'); - expect(matches.red).to.have.property('run'); - }); - - it('should take a single menu item object', function () { - const { controller } = init(); - const newItem = { key: 'green', template: 'Green means go' }; - - expect(controller.menuItems).to.have.length(2); - controller.addItems(newItem); - expect(controller.menuItems).to.have.length(3); - - // check that the items were added - const match = controller.menuItems.filter((item) => { - return item.key === 'green'; - }); - expect(match[0]).to.have.property('run'); - }); - }); - - }); -}); diff --git a/src/legacy/ui/public/kbn_top_nav/_index.scss b/src/legacy/ui/public/kbn_top_nav/_index.scss deleted file mode 100644 index c47305e8293a..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './kbn_top_nav'; diff --git a/src/legacy/ui/public/kbn_top_nav/_kbn_top_nav.scss b/src/legacy/ui/public/kbn_top_nav/_kbn_top_nav.scss deleted file mode 100644 index 47fd377681c9..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/_kbn_top_nav.scss +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 1. Make sure the timepicker is always one right, even if the main menu doesn't exist - */ - -kbn-top-nav { - z-index: 5; -} - -.kbnTopNav { - background-color: $euiPageBackgroundColor; - border-bottom: $euiBorderThin; -} - -.kbnTopNav__row { - display: flex; - align-items: center; - justify-content: flex-end; /* 1 */ -} - -.kbnTopNav__mainMenu { - flex-grow: 1; /* 1 */ - - &:empty { - // Make sure the area doesn't take up any space if it is empty - padding: 0; - } - - .kuiLocalMenuItem { - @include euiFontSizeS; - font-weight: $euiFontWeightMedium; - } - - .kuiLocalMenuItem:not(.kuiLocalMenuItem-isDisabled) { - color: $euiLinkColor; - } -} diff --git a/src/legacy/ui/public/kbn_top_nav/index.js b/src/legacy/ui/public/kbn_top_nav/index.js index 4bca1972e990..8a93972c4b22 100644 --- a/src/legacy/ui/public/kbn_top_nav/index.js +++ b/src/legacy/ui/public/kbn_top_nav/index.js @@ -18,4 +18,3 @@ */ import './kbn_top_nav'; -import './kbn_top_nav2'; diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.html b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.html deleted file mode 100644 index cdcb781b0b96..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.html +++ /dev/null @@ -1,47 +0,0 @@ -
- -
- -
- - -
- -
- - - -
- - -
- -
-
- -
- -
-
-
diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js index 997454d0798a..6e070d401ad3 100644 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js +++ b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js @@ -17,158 +17,109 @@ * under the License. */ -/** - * A configuration object for a top nav component. - * @typedef {Object} KbnTopNavConfig - * @type Object - * @property {string} key - identifier of menu item. - * @property {string} label - A display string which will be shown in the top nav for this option. - * @property {string} [description] - optional, used for the screen-reader description of this - * menu. Defaults to "Toggle ${label} view" for templated menu items and just "${label}" for - * programmatic menu items - * @property {string} testId - for testing purposes, can be used to retrieve this item. - * @property {Object} [template] - an html template that will be shown when this item is clicked. - * If template is not given then run should be supplied. - * @property {function} [run] - an optional function that will be run when the nav item is clicked. - * Either this or template parameter should be specified. - * @param {boolean} [hideButton] - optional, set to true to prevent a menu item from being created. - * This allow injecting templates into the navbar that don't have an associated template - */ +import 'ngreact'; +import { wrapInI18nContext } from 'ui/i18n'; +import { uiModules } from 'ui/modules'; +import { TopNavMenu } from '../../../core_plugins/kibana_react/public'; +import { Storage } from 'ui/storage'; +import chrome from 'ui/chrome'; -/** - * kbnTopNav directive - * - * The top section that optionally shows the timepicker and menu items. - * - * ``` - * - * ``` - * - * Menu items/templates are passed to the kbnTopNav via the config attribute - * and should be defined as an array of objects. Each object represents a menu - * item and should be of type kbnTopNavConfig. - * - * @param {Array|KbnTopNavController} config - * - * Programmatic control of the navbar can be achieved one of two ways - */ - -import _ from 'lodash'; -import angular from 'angular'; -import './timepicker'; -import '../directives/watch_multi'; -import '../directives/input_focus'; -import { uiModules } from '../modules'; -import template from './kbn_top_nav.html'; -import { KbnTopNavControllerProvider } from './kbn_top_nav_controller'; -import { NavBarExtensionsRegistryProvider } from '../registry/navbar_extensions'; const module = uiModules.get('kibana'); -module.directive('kbnTopNav', function (Private) { - const KbnTopNavController = Private(KbnTopNavControllerProvider); - const navbarExtensions = Private(NavBarExtensionsRegistryProvider); - const getNavbarExtensions = _.memoize(function (name) { - if (!name) throw new Error('navbar directive requires a name attribute'); - return _.sortBy(navbarExtensions.byAppName[name], 'order'); - }); - +module.directive('kbnTopNav', () => { return { restrict: 'E', - transclude: true, - template, - - // TODO: The kbnTopNav currently requires that it share a scope with - // its parent directive. This allows it to export the kbnTopNav controller - // and allows the config templates to use values from the parent scope. - // - // Moving this to an isolate scope will require modifying the config - // directive to support child directives, instead of templates, so that - // parent controllers can be imported/required rather than simply referenced - // directly in the template. - // - // TODO: Our fake multi-slot transclusion solution also depends on an inherited - // scope. Moving this to an isolate scope will cause this to break. - // - // scope: {} - - controller($scope, $attrs, $element, $transclude) { - // This is a semi-hacky solution to missing slot-transclusion support in Angular 1.4.7 - // (it was added as a core feature in 1.5). Borrowed from http://stackoverflow.com/a/22080765. - $scope.transcludes = {}; - - // Extract transcluded elements for use in the link function. - $transclude(clone => { - // We expect the transcluded elements to be wrapped in a single div. - const transcludedContentContainer = _.find(clone, item => { - if (item.attributes) { - return _.find(item.attributes, attr => { - return attr.name.indexOf('data-transclude-slots') !== -1; - }); - } - }); - - if (!transcludedContentContainer) { - return; - } - - const transcludedContent = transcludedContentContainer.children; - _.forEach(transcludedContent, transcludedItem => { - const transclusionSlot = transcludedItem.getAttribute('data-transclude-slot'); - $scope.transcludes[transclusionSlot] = transcludedItem; - }); - }); - const extensions = getNavbarExtensions($attrs.name); - - function initTopNav(newConfig, oldConfig) { - if (_.isEqual(oldConfig, newConfig)) return; - - if (newConfig instanceof KbnTopNavController) { - newConfig.addItems(extensions); - $scope.kbnTopNav = new KbnTopNavController(newConfig); - } else { - newConfig = newConfig.concat(extensions); - $scope.kbnTopNav = new KbnTopNavController(newConfig); - } - $scope.kbnTopNav._link($scope, $element); - } - - const getTopNavConfig = () => { - return _.get($scope, $attrs.config, []); - }; + template: '', + compile: (elem) => { + const child = document.createElement('kbn-top-nav-helper'); - const topNavConfig = getTopNavConfig(); - - // Because we store $scope and $element on the kbnTopNavController, if this was passed an instance - // instead of a configuration, it will enter an infinite digest loop. Only watch for updates if a config - // was passed instead. This is ugly, but without diving into a larger refactor, the smallest temporary solution - // to get dynamic nav updates working for dashboard. Console is currently the only place that passes a - // KbnTopNavController (and a slew of tests). - if (!(topNavConfig instanceof KbnTopNavController)) { - $scope.$watch(getTopNavConfig, initTopNav, true); + // Copy attributes to the child directive + for (const attr of elem[0].attributes) { + child.setAttribute(attr.name, attr.value); } - initTopNav(topNavConfig, null); - - if (!_.has($scope, 'showTimepickerInTopNav')) { - $scope.showTimepickerInTopNav = true; - } + // Add a special attribute that will change every time that one + // of the config array's disableButton function return value changes. + child.setAttribute('disabled-buttons', 'disabledButtons'); + + // Pass in storage + const localStorage = new Storage(window.localStorage); + child.setAttribute('store', 'store'); + child.setAttribute('ui-settings', 'uiSettings'); + + // Append helper directive + elem.append(child); + + const linkFn = ($scope, _, $attr) => { + $scope.store = localStorage; + $scope.uiSettings = chrome.getUiSettingsClient(); + + // Watch config changes + $scope.$watch(() => { + const config = $scope.$eval($attr.config) || []; + return config.map((item) => { + // Copy key into id, as it's a reserved react propery. + // This is done for Angular directive backward compatibility. + // In React only id is recognized. + if (item.key && !item.id) { + item.id = item.key; + } + + // Watch the disableButton functions + if (typeof item.disableButton === 'function') { + return item.disableButton(); + } + return item.disableButton; + }); + }, (newVal) => { + $scope.disabledButtons = newVal; + }, + true); + }; - return $scope.kbnTopNav; - }, - - link(scope) { - // These are the slots where transcluded elements can go. - const transclusionSlotNames = ['topLeftCorner', 'bottomRow']; - - // Transclude elements into specified "slots" in the top nav. - transclusionSlotNames.forEach(name => { - const transcludedItem = scope.transcludes[name]; - if (transcludedItem) { - const transclusionSlot = document.querySelector(`[data-transclude-slot="${name}"]`); - angular.element(transclusionSlot).replaceWith(transcludedItem); - } - }); + return linkFn; } }; }); + +module.directive('kbnTopNavHelper', (reactDirective) => { + return reactDirective( + wrapInI18nContext(TopNavMenu), + [ + ['name', { watchDepth: 'reference' }], + ['config', { watchDepth: 'value' }], + ['disabledButtons', { watchDepth: 'reference' }], + + ['query', { watchDepth: 'reference' }], + ['store', { watchDepth: 'reference' }], + ['uiSettings', { watchDepth: 'reference' }], + ['intl', { watchDepth: 'reference' }], + ['store', { watchDepth: 'reference' }], + + ['onQuerySubmit', { watchDepth: 'reference' }], + ['onFiltersUpdated', { watchDepth: 'reference' }], + ['onRefreshChange', { watchDepth: 'reference' }], + + ['indexPatterns', { watchDepth: 'collection' }], + ['filters', { watchDepth: 'collection' }], + + // All modifiers default to true. + // Set to false to hide subcomponents. + 'showSearchBar', + 'showFilterBar', + 'showQueryBar', + 'showQueryInput', + 'showDatePicker', + + 'appName', + 'screenTitle', + 'dateRangeFrom', + 'dateRangeTo', + 'isRefreshPaused', + 'refreshInterval', + 'disableAutoFocus', + 'showAutoRefreshOnly', + ], + ); +}); diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js deleted file mode 100644 index 1d697622182c..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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'; -import { TopNavMenu } from '../../../core_plugins/kibana_react/public'; -import { Storage } from 'ui/storage'; -import chrome from 'ui/chrome'; - - -const module = uiModules.get('kibana'); - -module.directive('kbnTopNavV2', () => { - return { - restrict: 'E', - template: '', - compile: (elem) => { - const child = document.createElement('kbn-top-nav-v2-helper'); - - // Copy attributes to the child directive - for (const attr of elem[0].attributes) { - child.setAttribute(attr.name, attr.value); - } - - // Add a special attribute that will change every time that one - // of the config array's disableButton function return value changes. - child.setAttribute('disabled-buttons', 'disabledButtons'); - - // Pass in storage - const localStorage = new Storage(window.localStorage); - child.setAttribute('store', 'store'); - child.setAttribute('ui-settings', 'uiSettings'); - - // Append helper directive - elem.append(child); - - const linkFn = ($scope, _, $attr) => { - $scope.store = localStorage; - $scope.uiSettings = chrome.getUiSettingsClient(); - - // Watch config changes - $scope.$watch(() => { - const config = $scope.$eval($attr.config) || []; - return config.map((item) => { - // Copy key into id, as it's a reserved react propery. - // This is done for Angular directive backward compatibility. - // In React only id is recognized. - if (item.key && !item.id) { - item.id = item.key; - } - - // Watch the disableButton functions - if (typeof item.disableButton === 'function') { - return item.disableButton(); - } - return item.disableButton; - }); - }, (newVal) => { - $scope.disabledButtons = newVal; - }, - true); - }; - - return linkFn; - } - }; -}); - -module.directive('kbnTopNavV2Helper', (reactDirective) => { - return reactDirective( - wrapInI18nContext(TopNavMenu), - [ - ['name', { watchDepth: 'reference' }], - ['config', { watchDepth: 'value' }], - ['disabledButtons', { watchDepth: 'reference' }], - - ['query', { watchDepth: 'reference' }], - ['store', { watchDepth: 'reference' }], - ['uiSettings', { watchDepth: 'reference' }], - ['intl', { watchDepth: 'reference' }], - ['store', { watchDepth: 'reference' }], - - ['onQuerySubmit', { watchDepth: 'reference' }], - ['onFiltersUpdated', { watchDepth: 'reference' }], - ['onRefreshChange', { watchDepth: 'reference' }], - - ['indexPatterns', { watchDepth: 'collection' }], - ['filters', { watchDepth: 'collection' }], - - // All modifiers default to true. - // Set to false to hide subcomponents. - 'showSearchBar', - 'showFilterBar', - 'showQueryBar', - 'showQueryInput', - 'showDatePicker', - - 'appName', - 'screenTitle', - 'dateRangeFrom', - 'dateRangeTo', - 'isRefreshPaused', - 'refreshInterval', - 'disableAutoFocus', - 'showAutoRefreshOnly', - ], - ); -}); diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav_controller.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav_controller.js deleted file mode 100644 index 00400b93f994..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav_controller.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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 { capitalize, isArray, isFunction, get } from 'lodash'; - -import chrome from '../chrome'; -import { i18n } from '@kbn/i18n'; - -export function KbnTopNavControllerProvider($compile) { - return class KbnTopNavController { - constructor(opts = []) { - if (opts instanceof KbnTopNavController) { - return opts; - } - - this.opts = []; - this.menuItems = []; - this.currentKey = null; - this.templates = {}; - this.locals = new Map(); - - this.addItems(opts); - } - - isVisible() { - return chrome.getVisible(); - } - - addItems(rawOpts) { - if (!isArray(rawOpts)) rawOpts = [rawOpts]; - - rawOpts.forEach((rawOpt) => { - const opt = this._applyOptDefault(rawOpt); - if (!opt.key) throw new TypeError('KbnTopNav: menu items must have a key'); - this.opts.push(opt); - if (!opt.hideButton()) this.menuItems.push(opt); - if (opt.template) this.templates[opt.key] = opt.template; - if (opt.locals) { - this.locals.set(opt.key, opt.locals); - } - }); - } - - // change the current key and rerender - setCurrent = (key) => { - if (key && !this.templates.hasOwnProperty(key)) { - throw new TypeError(`KbnTopNav: unknown template key "${key}"`); - } - - this.currentKey = key || null; - this._render(); - }; - - // little usability helpers - getCurrent = () => { return this.currentKey; }; - isCurrent = (key) => { return this.getCurrent() === key; }; - open = (key) => { this.setCurrent(key); }; - close = (key) => { (!key || this.isCurrent(key)) && this.setCurrent(null); }; - toggle = (key) => { this.setCurrent(this.isCurrent(key) ? null : key); }; - click = (key) => { this.handleClick(this.getItem(key)); }; - getItem = (key) => { return this.menuItems.find(i => i.key === key); }; - handleClick = (menuItem, event) => { - if (menuItem.disableButton()) { - return false; - } - // event will be undefined when method is called from click - menuItem.run(menuItem, this, get(event, 'target')); - }; - // apply the defaults to individual options - _applyOptDefault(opt = {}) { - const optLabel = opt.label ? opt.label : capitalize(opt.key); - const defaultedOpt = { - label: optLabel, - hasFunction: !!opt.run, - description: opt.run ? optLabel : i18n.translate('common.ui.topNav.toggleViewAriaLabel', { - defaultMessage: 'Toggle {optLabel} view', - values: { optLabel } - }), - run: (item) => this.toggle(item.key), - ...opt - }; - - defaultedOpt.hideButton = isFunction(opt.hideButton) ? opt.hideButton : () => !!opt.hideButton; - defaultedOpt.disableButton = isFunction(opt.disableButton) ? opt.disableButton : () => !!opt.disableButton; - defaultedOpt.tooltip = isFunction(opt.tooltip) ? opt.tooltip : () => opt.tooltip; - - return defaultedOpt; - } - - // enable actual rendering - _link($scope, $element) { - this.$scope = $scope; - this.$element = $element; - this._render(); - } - - // render the current template to the $element if possible - // function is idempotent - _render() { - const { $scope, $element, rendered, currentKey } = this; - const templateToRender = currentKey && this.templates[currentKey]; - - if (rendered) { - if (rendered.key !== currentKey) { - // we have an invalid render, clear it - rendered.$childScope.$destroy(); - rendered.$el.remove(); - this.rendered = null; - } else { - // our previous render is still valid, keep it - return; - } - } - - if (!templateToRender || !$scope || !$element) { - // we either have nothing to render, or we can't render - return; - } - - const $childScope = $scope.$new(); - if (this.locals.has(currentKey)) { - Object.assign($childScope, this.locals.get(currentKey)); - } - const $el = $element.find('#template_wrapper').html(templateToRender).contents(); - $compile($el)($childScope); - - this.rendered = { $childScope, $el, key: currentKey }; - } - }; -} diff --git a/src/legacy/ui/public/kbn_top_nav/timepicker/index.js b/src/legacy/ui/public/kbn_top_nav/timepicker/index.js deleted file mode 100644 index 5e68b5bc4110..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/timepicker/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 './kbn_global_timepicker'; diff --git a/src/legacy/ui/public/kbn_top_nav/timepicker/kbn_global_timepicker.html b/src/legacy/ui/public/kbn_top_nav/timepicker/kbn_global_timepicker.html deleted file mode 100644 index 8ef4dcfe1c96..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/timepicker/kbn_global_timepicker.html +++ /dev/null @@ -1,20 +0,0 @@ -
- -
diff --git a/src/legacy/ui/public/kbn_top_nav/timepicker/kbn_global_timepicker.js b/src/legacy/ui/public/kbn_top_nav/timepicker/kbn_global_timepicker.js deleted file mode 100644 index 7c60cd16b0bf..000000000000 --- a/src/legacy/ui/public/kbn_top_nav/timepicker/kbn_global_timepicker.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 { uiModules } from '../../modules'; - -import toggleHtml from './kbn_global_timepicker.html'; -import { timefilter } from 'ui/timefilter'; -import { timeHistory } from 'ui/timefilter/time_history'; - -import { - EuiSuperDatePicker, -} from '@elastic/eui'; - -uiModules - .get('kibana') - .directive('superDatePicker', reactDirective => reactDirective(EuiSuperDatePicker, [ - 'start', - 'end', - 'isPaused', - 'refreshInterval', - 'commonlyUsedRanges', - 'dateFormat', - 'recentlyUsedRanges', - 'onTimeChange', - 'onRefreshChange', - 'isAutoRefreshOnly', - 'commonlyUsedRanges', - 'dateFormat', - 'recentlyUsedRanges', - ])); - - -uiModules - .get('kibana') - .directive('kbnGlobalTimepicker', (globalState, config) => { - const listenForUpdates = ($scope) => { - $scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', () => { - setTimefilterValues($scope); - }); - $scope.$listenAndDigestAsync(timefilter, 'timeUpdate', () => { - setTimefilterValues($scope); - }); - $scope.$listenAndDigestAsync(timefilter, 'enabledUpdated', () => { - setTimefilterValues($scope); - }); - }; - - function setTimefilterValues($scope) { - const time = timefilter.getTime(); - const refreshInterval = timefilter.getRefreshInterval(); - $scope.timefilterValues = { - refreshInterval: refreshInterval, - time: time, - isAutoRefreshSelectorEnabled: timefilter.isAutoRefreshSelectorEnabled, - isTimeRangeSelectorEnabled: timefilter.isTimeRangeSelectorEnabled, - }; - $scope.recentlyUsedRanges = timeHistory.get().map(({ from, to }) => { - return { - start: from, - end: to, - }; - }); - } - - return { - template: toggleHtml, - replace: true, - link: ($scope) => { - listenForUpdates($scope); - - setTimefilterValues($scope); - - config.watch('timepicker:quickRanges', (quickRanges) => { - // quickRanges is null when timepicker:quickRanges is set to default value - const ranges = quickRanges ? quickRanges : config.get('timepicker:quickRanges'); - $scope.commonlyUsedRanges = ranges.map(({ from, to, display }) => { - return { - start: from, - end: to, - label: display, - }; - }); - }); - - config.watch('dateFormat', (dateFormat) => { - // dateFormat is null when dateFormat is set to default value - $scope.dateFormat = dateFormat ? dateFormat : config.get('dateFormat'); - }); - - $scope.updateFilter = function ({ start, end }) { - timefilter.setTime({ from: start, to: end }); - }; - - $scope.updateInterval = function ({ isPaused, refreshInterval }) { - timefilter.setRefreshInterval({ - pause: isPaused, - value: refreshInterval ? refreshInterval : $scope.timefilterValues.refreshInterval.value - }); - }; - - $scope.getSharedTimeFilterFromDate = function () { - return (timefilter.isAutoRefreshSelectorEnabled || timefilter.isTimeRangeSelectorEnabled) - ? timefilter.getBounds().min.clone().utc().format() - : null; - }; - - $scope.getSharedTimeFilterToDate = function () { - return (timefilter.isAutoRefreshSelectorEnabled || timefilter.isTimeRangeSelectorEnabled) - ? timefilter.getBounds().max.clone().utc().format() - : null; - }; - }, - }; - }); diff --git a/x-pack/legacy/plugins/graph/public/templates/_graph.scss b/x-pack/legacy/plugins/graph/public/templates/_graph.scss index 23adcbd87dc5..e73909177151 100644 --- a/x-pack/legacy/plugins/graph/public/templates/_graph.scss +++ b/x-pack/legacy/plugins/graph/public/templates/_graph.scss @@ -27,10 +27,6 @@ flex: 1; } -.kbnGlobalNav.kbnGlobalNav-isOpen + .app-wrapper .gphGraph__container { - width: calc(100% - #{$kbnGlobalNavOpenWidth}); /* 1 */ -} - .gphGraph { // SASSTODO: Can't definitively change child class // because it's not easy to tell what's a class and what's diff --git a/x-pack/legacy/plugins/graph/public/templates/index.html b/x-pack/legacy/plugins/graph/public/templates/index.html index 42679b05052b..fc536d54547e 100644 --- a/x-pack/legacy/plugins/graph/public/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/templates/index.html @@ -1,7 +1,7 @@
- - + +
diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index a0a10a97ae7e..f17b2d537b26 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -1,7 +1,7 @@
- - +
diff --git a/x-pack/legacy/plugins/monitoring/public/directives/main/index.html b/x-pack/legacy/plugins/monitoring/public/directives/main/index.html index a6226701d8dc..c989074b6de0 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/legacy/plugins/monitoring/public/directives/main/index.html @@ -1,5 +1,5 @@
- - +