diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 91364071579ab..af7aa3daf76cc 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -62,11 +62,11 @@ export default function (kibana) { uiExports: { hacks: [ + 'plugins/kibana/discover', 'plugins/kibana/dev_tools', ], savedObjectTypes: [ 'plugins/kibana/visualize/saved_visualizations/saved_visualization_register', - 'plugins/kibana/discover/saved_searches/saved_search_register', 'plugins/kibana/dashboard/saved_dashboard/saved_dashboard_register', ], app: { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js index 1abe8581c54c6..3f764cf576668 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js @@ -20,24 +20,26 @@ import { uiModules } from 'ui/modules'; import { capabilities } from 'ui/capabilities'; -uiModules.get('kibana') - .provider('dashboardConfig', () => { - let hideWriteControls = !capabilities.get().dashboard.showWriteControls; +export function dashboardConfigProvider() { + let hideWriteControls = !capabilities.get().dashboard.showWriteControls; + + return { + /** + * Part of the exposed plugin API - do not remove without careful consideration. + * @type {boolean} + */ + turnHideWriteControlsOn() { + hideWriteControls = true; + }, + $get() { + return { + getHideWriteControls() { + return hideWriteControls; + } + }; + } + }; +} - return { - /** - * Part of the exposed plugin API - do not remove without careful consideration. - * @type {boolean} - */ - turnHideWriteControlsOn() { - hideWriteControls = true; - }, - $get() { - return { - getHideWriteControls() { - return hideWriteControls; - } - }; - } - }; - }); +uiModules.get('kibana') + .provider('dashboardConfig', dashboardConfigProvider); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js index 9ac76bfcfe04e..92df04c536e43 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js +++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js @@ -23,7 +23,7 @@ import _ from 'lodash'; import sinon from 'sinon'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; -import '../../components/field_chooser/discover_field'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; // Load the kibana app dependencies. @@ -32,8 +32,9 @@ describe('discoverField', function () { let $scope; let indexPattern; let $elem; - - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeServices(true)); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); beforeEach(ngMock.inject(function (Private, $rootScope, $compile) { $elem = angular.element(` pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); beforeEach(ngMock.inject(function (Private) { indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js index bac56f008233c..34c6483349af6 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js +++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js @@ -23,7 +23,7 @@ import _ from 'lodash'; import sinon from 'sinon'; import expect from '@kbn/expect'; import $ from 'jquery'; -import '../../components/field_chooser/field_chooser'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import FixturesHitsProvider from 'fixtures/hits'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { SimpleSavedObject } from '../../../../../../../core/public'; @@ -70,8 +70,10 @@ describe('discover field chooser directives', function () { on-remove-field="removeField" > `); + beforeEach(() => pluginInstance.initializeServices(true)); + beforeEach(() => pluginInstance.initializeInnerAngular()); - beforeEach(ngMock.module('kibana', ($provide) => { + beforeEach(ngMock.module('app/discover', ($provide) => { $provide.decorator('config', ($delegate) => { // disable shortDots for these tests $delegate.get = _.wrap($delegate.get, function (origGet, name) { diff --git a/src/legacy/core_plugins/kibana/public/discover/_index.scss b/src/legacy/core_plugins/kibana/public/discover/_index.scss index b311dd8a34778..0d70bb993fac1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/_index.scss +++ b/src/legacy/core_plugins/kibana/public/discover/_index.scss @@ -20,7 +20,7 @@ @import 'embeddable/index'; // Doc Viewer -@import 'doc_viewer/index'; +@import 'components/doc_viewer/index'; // Context styles @import 'angular/context/index'; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context.js b/src/legacy/core_plugins/kibana/public/discover/angular/context.js index 58d1626ca4b14..989712a16b250 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context.js @@ -19,12 +19,13 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getServices, subscribeWithScope } from './../kibana_services'; +import { getAngularModule, getServices, subscribeWithScope } from './../kibana_services'; import './context_app'; import contextAppRouteTemplate from './context.html'; -import { getRootBreadcrumbs } from '../breadcrumbs'; -const { FilterBarQueryFilterProvider, uiRoutes, chrome } = getServices(); +import { getRootBreadcrumbs } from '../helpers/breadcrumbs'; +import { FilterStateManager } from '../../../../data/public/filter/filter_manager'; +const { chrome } = getServices(); const k7Breadcrumbs = $route => { const { indexPattern } = $route.current.locals; @@ -44,26 +45,33 @@ const k7Breadcrumbs = $route => { ]; }; -uiRoutes - // deprecated route, kept for compatibility - // should be removed in the future - .when('/context/:indexPatternId/:type/:id*', { - redirectTo: '/context/:indexPatternId/:id', - }) - .when('/context/:indexPatternId/:id*', { - controller: ContextAppRouteController, - k7Breadcrumbs, - controllerAs: 'contextAppRoute', - resolve: { - indexPattern: function ($route, indexPatterns) { - return indexPatterns.get($route.current.params.indexPatternId); +getAngularModule().config($routeProvider => { + $routeProvider + // deprecated route, kept for compatibility + // should be removed in the future + .when('/discover/context/:indexPatternId/:type/:id*', { + redirectTo: '/discover/context/:indexPatternId/:id', + }) + .when('/discover/context/:indexPatternId/:id*', { + controller: ContextAppRouteController, + k7Breadcrumbs, + controllerAs: 'contextAppRoute', + resolve: { + indexPattern: ($route, Promise) => { + const indexPattern = getServices().indexPatterns.get( + $route.current.params.indexPatternId + ); + return Promise.props({ ip: indexPattern }); + }, }, - }, - template: contextAppRouteTemplate, - }); + template: contextAppRouteTemplate, + }); +}); -function ContextAppRouteController($routeParams, $scope, AppState, config, indexPattern, Private) { - const queryFilter = Private(FilterBarQueryFilterProvider); +function ContextAppRouteController($routeParams, $scope, AppState, config, $route, getAppState, globalState) { + const filterManager = getServices().filterManager; + const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager); + const indexPattern = $route.current.locals.indexPattern.ip; this.state = new AppState(createDefaultAppState(config, indexPattern)); this.state.save(true); @@ -77,19 +85,20 @@ function ContextAppRouteController($routeParams, $scope, AppState, config, index () => this.state.save(true) ); - const updateSubsciption = subscribeWithScope($scope, queryFilter.getUpdates$(), { + const updateSubsciption = subscribeWithScope($scope, filterManager.getUpdates$(), { next: () => { - this.filters = _.cloneDeep(queryFilter.getFilters()); + this.filters = _.cloneDeep(filterManager.getFilters()); }, }); - $scope.$on('$destroy', function () { + $scope.$on('$destroy', () => { + filterStateManager.destroy(); updateSubsciption.unsubscribe(); }); this.anchorId = $routeParams.id; this.indexPattern = indexPattern; this.discoverUrl = chrome.navLinks.get('kibana:discover').url; - this.filters = _.cloneDeep(queryFilter.getFilters()); + this.filters = _.cloneDeep(filterManager.getFilters()); } function createDefaultAppState(config, indexPattern) { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/anchor.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/anchor.js index 4eb68c1bf50bc..8c6e53974ba36 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/anchor.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/anchor.js @@ -19,13 +19,15 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import { createIndexPatternsStub, createSearchSourceStub } from './_stubs'; import { fetchAnchorProvider } from '../anchor'; describe('context app', function () { - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); describe('function fetchAnchor', function () { let fetchAnchor; @@ -35,11 +37,11 @@ describe('context app', function () { $provide.value('indexPatterns', createIndexPatternsStub()); })); - beforeEach(ngMock.inject(function createPrivateStubs(Private) { + beforeEach(ngMock.inject(function createPrivateStubs() { searchSourceStub = createSearchSourceStub([ { _id: 'hit1' }, ]); - fetchAnchor = Private(fetchAnchorProvider); + fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub); })); afterEach(() => { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/predecessors.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/predecessors.js index ea6a8c092e242..a21e2117d1db6 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/predecessors.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/predecessors.js @@ -21,6 +21,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import moment from 'moment'; import * as _ from 'lodash'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; @@ -33,7 +34,8 @@ const ANCHOR_TIMESTAMP_1000 = (new Date(MS_PER_DAY * 1000)).toJSON(); const ANCHOR_TIMESTAMP_3000 = (new Date(MS_PER_DAY * 3000)).toJSON(); describe('context app', function () { - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); describe('function fetchPredecessors', function () { let fetchPredecessors; @@ -43,7 +45,7 @@ describe('context app', function () { $provide.value('indexPatterns', createIndexPatternsStub()); })); - beforeEach(ngMock.inject(function createPrivateStubs(Private) { + beforeEach(ngMock.inject(function createPrivateStubs() { searchSourceStub = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); fetchPredecessors = (indexPatternId, timeField, sortDir, timeValIso, timeValNr, tieBreakerField, tieBreakerValue, size) => { const anchor = { @@ -53,7 +55,7 @@ describe('context app', function () { sort: [timeValNr, tieBreakerValue] }; - return Private(fetchContextProvider).fetchSurroundingDocs( + return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs( 'predecessors', indexPatternId, anchor, diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/successors.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/successors.js index 486c8ed9b410e..145de081f0d44 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/successors.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/successors.js @@ -21,6 +21,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import moment from 'moment'; import * as _ from 'lodash'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; @@ -32,17 +33,14 @@ const ANCHOR_TIMESTAMP_3 = (new Date(MS_PER_DAY * 3)).toJSON(); const ANCHOR_TIMESTAMP_3000 = (new Date(MS_PER_DAY * 3000)).toJSON(); describe('context app', function () { - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); describe('function fetchSuccessors', function () { let fetchSuccessors; let searchSourceStub; - beforeEach(ngMock.module(function createServiceStubs($provide) { - $provide.value('indexPatterns', createIndexPatternsStub()); - })); - - beforeEach(ngMock.inject(function createPrivateStubs(Private) { + beforeEach(ngMock.inject(function createPrivateStubs() { searchSourceStub = createContextSearchSourceStub([], '@timestamp'); fetchSuccessors = (indexPatternId, timeField, sortDir, timeValIso, timeValNr, tieBreakerField, tieBreakerValue, size) => { @@ -53,7 +51,7 @@ describe('context app', function () { sort: [timeValNr, tieBreakerValue] }; - return Private(fetchContextProvider).fetchSurroundingDocs( + return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs( 'successors', indexPatternId, anchor, diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/anchor.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/anchor.js index 8c4cce810ca13..730b963d0474e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/anchor.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/anchor.js @@ -19,17 +19,15 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getServices } from '../../../kibana_services'; -const { SearchSource } = getServices(); -export function fetchAnchorProvider(indexPatterns) { +export function fetchAnchorProvider(indexPatterns, searchSource) { return async function fetchAnchor( indexPatternId, anchorId, sort ) { const indexPattern = await indexPatterns.get(indexPatternId); - const searchSource = new SearchSource() + searchSource .setParent(undefined) .setField('index', indexPattern) .setField('version', true) diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts index 68ccf56594e72..674f5616faa30 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts @@ -17,9 +17,8 @@ * under the License. */ -import { SortDirection } from '../../../../../../../ui/public/courier'; -import { IndexPatterns, IndexPattern, getServices } from '../../../kibana_services'; -import { reverseSortDir } from './utils/sorting'; +import { IndexPatterns, IndexPattern, SearchSource } from '../../../kibana_services'; +import { reverseSortDir, SortDirection } from './utils/sorting'; import { extractNanos, convertIsoToMillis } from './utils/date_conversion'; import { fetchHitsInInterval } from './utils/fetch_hits_in_interval'; import { generateIntervals } from './utils/generate_intervals'; @@ -35,8 +34,6 @@ export interface EsHitRecord { } export type EsHitRecordList = EsHitRecord[]; -const { SearchSource } = getServices(); - const DAY_MILLIS = 24 * 60 * 60 * 1000; // look from 1 day up to 10000 days into the past and future diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/__tests__/sorting.test.ts b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/__tests__/sorting.test.ts index 33f4454c18d40..eeae2aa2c5d0a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/__tests__/sorting.test.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/__tests__/sorting.test.ts @@ -16,10 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { reverseSortDir } from '../sorting'; -import { SortDirection } from '../../../../../../../../../ui/public/courier'; - -jest.mock('ui/new_platform'); +import { reverseSortDir, SortDirection } from '../sorting'; describe('function reverseSortDir', function() { test('reverse a given sort direction', function() { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/sorting.ts b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/sorting.ts index 47385aecb1937..4a0f531845f46 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/sorting.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/utils/sorting.ts @@ -17,9 +17,13 @@ * under the License. */ -import { SortDirection } from '../../../../../../../../ui/public/courier'; import { IndexPattern } from '../../../../kibana_services'; +export enum SortDirection { + asc = 'asc', + desc = 'desc', +} + /** * The list of field names that are allowed for sorting, but not included in * index pattern fields. diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/components/action_bar/action_bar_directive.ts b/src/legacy/core_plugins/kibana/public/discover/angular/context/components/action_bar/action_bar_directive.ts index 579d9d95c6f71..55a378367392c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/components/action_bar/action_bar_directive.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/components/action_bar/action_bar_directive.ts @@ -16,11 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { getServices } from '../../../../kibana_services'; +import { getAngularModule, wrapInI18nContext } from '../../../../kibana_services'; import { ActionBar } from './action_bar'; -const { uiModules, wrapInI18nContext } = getServices(); - -uiModules.get('apps/context').directive('contextActionBar', function(reactDirective: any) { +getAngularModule().directive('contextActionBar', function(reactDirective: any) { return reactDirective(wrapInI18nContext(ActionBar)); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query/actions.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query/actions.js index b88e54379f448..4a9480f9ea2ea 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query/actions.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query/actions.js @@ -20,22 +20,22 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { toastNotifications } from '../../../kibana_services'; +import { getServices, SearchSource } from '../../../kibana_services'; import { fetchAnchorProvider } from '../api/anchor'; import { fetchContextProvider } from '../api/context'; -import { QueryParameterActionsProvider } from '../query_parameters'; +import { getQueryParameterActions } from '../query_parameters'; import { FAILURE_REASONS, LOADING_STATUS } from './constants'; import { MarkdownSimple } from '../../../../../../kibana_react/public'; -export function QueryActionsProvider(Private, Promise) { - const fetchAnchor = Private(fetchAnchorProvider); - const { fetchSurroundingDocs } = Private(fetchContextProvider); +export function QueryActionsProvider(Promise) { + const fetchAnchor = fetchAnchorProvider(getServices().indexPatterns, new SearchSource()); + const { fetchSurroundingDocs } = fetchContextProvider(getServices().indexPatterns); const { setPredecessorCount, setQueryParameters, setSuccessorCount, - } = Private(QueryParameterActionsProvider); + } = getQueryParameterActions(); const setFailedStatus = (state) => (subject, details = {}) => ( state.loadingStatus[subject] = { @@ -79,7 +79,7 @@ export function QueryActionsProvider(Private, Promise) { }, (error) => { setFailedStatus(state)('anchor', { error }); - toastNotifications.addDanger({ + getServices().toastNotifications.addDanger({ title: i18n.translate('kbn.context.unableToLoadAnchorDocumentDescription', { defaultMessage: 'Unable to load the anchor document' }), @@ -128,7 +128,7 @@ export function QueryActionsProvider(Private, Promise) { }, (error) => { setFailedStatus(state)(type, { error }); - toastNotifications.addDanger({ + getServices().toastNotifications.addDanger({ title: i18n.translate('kbn.context.unableToLoadDocumentDescription', { defaultMessage: 'Unable to load documents' }), diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js index 5a445a65939ed..645ca32924ede 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js @@ -19,15 +19,16 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { getServices } from '../../../../kibana_services'; import { createStateStub } from './_utils'; -import { QueryParameterActionsProvider } from '../actions'; +import { getQueryParameterActions } from '../actions'; import { createIndexPatternsStub } from '../../api/__tests__/_stubs'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import { npStart } from 'ui/new_platform'; describe('context app', function () { - beforeEach(ngMock.module('kibana')); - + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(() => pluginInstance.initializeServices(true)); + beforeEach(ngMock.module('app/discover')); beforeEach(ngMock.module(function createServiceStubs($provide) { $provide.value('indexPatterns', createIndexPatternsStub()); })); @@ -35,9 +36,8 @@ describe('context app', function () { describe('action addFilter', function () { let addFilter; - beforeEach(ngMock.inject(function createPrivateStubs(Private) { - Private.stub(getServices().FilterBarQueryFilterProvider); - addFilter = Private(QueryParameterActionsProvider).addFilter; + beforeEach(ngMock.inject(function createPrivateStubs() { + addFilter = getQueryParameterActions().addFilter; })); it('should pass the given arguments to the filterManager', function () { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_predecessor_count.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_predecessor_count.js index 7c1fa320ae17b..a8bef6fe75c79 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_predecessor_count.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_predecessor_count.js @@ -19,19 +19,21 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; - +import { pluginInstance } from 'plugins/kibana/discover/index'; import { createStateStub } from './_utils'; -import { QueryParameterActionsProvider } from '../actions'; +import { getQueryParameterActions } from '../actions'; describe('context app', function () { - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(() => pluginInstance.initializeServices(true)); + beforeEach(ngMock.module('app/discover')); describe('action setPredecessorCount', function () { let setPredecessorCount; - beforeEach(ngMock.inject(function createPrivateStubs(Private) { - setPredecessorCount = Private(QueryParameterActionsProvider).setPredecessorCount; + beforeEach(ngMock.inject(function createPrivateStubs() { + setPredecessorCount = getQueryParameterActions().setPredecessorCount; })); it('should set the predecessorCount to the given value', function () { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_query_parameters.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_query_parameters.js index 853c5726b3da5..a43a8a11a7bf8 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_query_parameters.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_query_parameters.js @@ -19,19 +19,22 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import { createStateStub } from './_utils'; -import { QueryParameterActionsProvider } from '../actions'; +import { getQueryParameterActions } from '../actions'; describe('context app', function () { - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(() => pluginInstance.initializeServices(true)); + beforeEach(ngMock.module('app/discover')); describe('action setQueryParameters', function () { let setQueryParameters; - beforeEach(ngMock.inject(function createPrivateStubs(Private) { - setQueryParameters = Private(QueryParameterActionsProvider).setQueryParameters; + beforeEach(ngMock.inject(function createPrivateStubs() { + setQueryParameters = getQueryParameterActions().setQueryParameters; })); it('should update the queryParameters with valid properties from the given object', function () { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_successor_count.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_successor_count.js index d63bf2ecf53af..4bbd462aaa4b0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_successor_count.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_set_successor_count.js @@ -19,19 +19,22 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import { createStateStub } from './_utils'; -import { QueryParameterActionsProvider } from '../actions'; +import { getQueryParameterActions } from '../actions'; describe('context app', function () { - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(() => pluginInstance.initializeServices(true)); + beforeEach(ngMock.module('app/discover')); describe('action setSuccessorCount', function () { let setSuccessorCount; - beforeEach(ngMock.inject(function createPrivateStubs(Private) { - setSuccessorCount = Private(QueryParameterActionsProvider).setSuccessorCount; + beforeEach(ngMock.inject(function createPrivateStubs() { + setSuccessorCount = getQueryParameterActions().setSuccessorCount; })); it('should set the successorCount to the given value', function () { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js index 10fe6c0e2eda1..28b35a1b81a7b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js @@ -18,8 +18,8 @@ */ import _ from 'lodash'; +import { getServices } from '../../../kibana_services'; import { generateFilters } from '../../../../../../../../plugins/data/public'; -import { npStart } from 'ui/new_platform'; import { MAX_CONTEXT_SIZE, @@ -28,8 +28,8 @@ import { } from './constants'; -export function QueryParameterActionsProvider(indexPatterns) { - const { filterManager } = npStart.plugins.data.query; +export function getQueryParameterActions() { + const filterManager = getServices().filterManager; const setPredecessorCount = (state) => (predecessorCount) => ( state.queryParameters.predecessorCount = clamp( @@ -62,7 +62,7 @@ export function QueryParameterActionsProvider(indexPatterns) { const indexPatternId = state.queryParameters.indexPatternId; const newFilters = generateFilters(filterManager, field, values, operation, indexPatternId); filterManager.addFilters(newFilters); - const indexPattern = await indexPatterns.get(indexPatternId); + const indexPattern = await getServices().indexPatterns.get(indexPatternId); indexPattern.popularizeField(field.name, 1); }; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/index.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/index.js index 3e7f47668df59..14be90a3f61a4 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/index.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/index.js @@ -17,7 +17,7 @@ * under the License. */ -export { QueryParameterActionsProvider } from './actions'; +export { getQueryParameterActions } from './actions'; export { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context_app.js b/src/legacy/core_plugins/kibana/public/discover/angular/context_app.js index c9856ad794952..4609317712379 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context_app.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context_app.js @@ -18,13 +18,13 @@ */ import _ from 'lodash'; -import { getServices, callAfterBindingsWorkaround } from './../kibana_services'; +import { getServices, callAfterBindingsWorkaround, getAngularModule } from './../kibana_services'; import contextAppTemplate from './context_app.html'; import './context/components/action_bar'; import { getFirstSortableField } from './context/api/utils/sorting'; import { createInitialQueryParametersState, - QueryParameterActionsProvider, + getQueryParameterActions, QUERY_PARAMETER_KEYS, } from './context/query_parameters'; import { @@ -34,17 +34,12 @@ import { QueryActionsProvider, } from './context/query'; -const { uiModules, timefilter } = getServices(); +const { timefilter } = getServices(); // load directives import '../../../../data/public/legacy'; -const module = uiModules.get('apps/context', [ - 'elasticsearch', - 'kibana', - 'kibana/config', - 'ngRoute', -]); +const module = getAngularModule(); module.directive('contextApp', function ContextApp() { return { @@ -67,7 +62,7 @@ module.directive('contextApp', function ContextApp() { }); function ContextAppController($scope, config, Private) { - const queryParameterActions = Private(QueryParameterActionsProvider); + const queryParameterActions = getQueryParameterActions(); const queryActions = Private(QueryActionsProvider); timefilter.disableAutoRefreshSelector(); diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/directives/histogram.tsx b/src/legacy/core_plugins/kibana/public/discover/angular/directives/histogram.tsx index ab336396b5bed..496e1cf375588 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/directives/histogram.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/angular/directives/histogram.tsx @@ -70,7 +70,7 @@ export class DiscoverHistogram extends Component this.setState({ chartsTheme })); + .subscribe((chartsTheme: EuiChartThemeType['theme']) => this.setState({ chartsTheme })); } componentWillUnmount() { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/directives/index.js b/src/legacy/core_plugins/kibana/public/discover/angular/directives/index.js index b0b766478450f..f1e783c56263e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/directives/index.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/directives/index.js @@ -23,10 +23,9 @@ import { DiscoverNoResults } from './no_results'; import { DiscoverUninitialized } from './uninitialized'; import { DiscoverUnsupportedIndexPattern } from './unsupported_index_pattern'; import { DiscoverHistogram } from './histogram'; -import { getServices } from '../../kibana_services'; +import { getAngularModule, wrapInI18nContext } from '../../kibana_services'; -const { wrapInI18nContext, uiModules } = getServices(); -const app = uiModules.get('apps/discover', ['react']); +const app = getAngularModule(); app.directive('discoverNoResults', reactDirective => reactDirective(wrapInI18nContext(DiscoverNoResults)) diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/angular/discover.js index 0e92d048a65a9..7af7ee235a166 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/discover.js @@ -23,7 +23,6 @@ import { Subscription } from 'rxjs'; import moment from 'moment'; import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; -import '../saved_searches/saved_searches'; import '../components/field_chooser/field_chooser'; // doc table @@ -33,7 +32,7 @@ import { getSortForSearchSource } from './doc_table/lib/get_sort_for_search_sour import * as columnActions from './doc_table/actions/columns'; import indexTemplate from './discover.html'; -import { showOpenSearchPanel } from '../top_nav/show_open_search_panel'; +import { showOpenSearchPanel } from '../components/top_nav/show_open_search_panel'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; import '../components/fetch_error'; import { getPainlessError } from './get_painless_error'; @@ -57,28 +56,31 @@ import { vislibSeriesResponseHandlerProvider, Vis, SavedObjectSaveModal, + getAngularModule, ensureDefaultIndexPattern, } from '../kibana_services'; const { core, chrome, + data, docTitle, - FilterBarQueryFilterProvider, + filterManager, + State, share, - StateProvider, timefilter, - npData, toastNotifications, - uiModules, - uiRoutes, -} = getServices(); + uiSettings +} = getServices(); -import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../breadcrumbs'; +import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; +import { start as dataLP } from '../../../../data/public/legacy'; import { generateFilters } from '../../../../../../plugins/data/public'; -import { start as data } from '../../../../data/public/legacy'; +import { getIndexPatternId } from '../helpers/get_index_pattern_id'; +import { registerTimefilterWithGlobalStateFactory } from '../../../../../ui/public/timefilter/setup_router'; +import { FilterStateManager } from '../../../../data/public/filter/filter_manager'; -const savedQueryService = npData.query.savedQueries; +const { savedQueryService } = data.query.savedQueries; const fetchStatuses = { UNINITIALIZED: 'uninitialized', @@ -86,20 +88,20 @@ const fetchStatuses = { COMPLETE: 'complete', }; -const app = uiModules.get('apps/discover', [ - 'kibana/url', - 'kibana/index_patterns' -]); +const app = getAngularModule(); +app.run((globalState, $rootScope) => {registerTimefilterWithGlobalStateFactory( + timefilter, + globalState, + $rootScope +); +}); -uiRoutes - .defaults(/^\/discover(\/|$)/, { +app.config($routeProvider => { + const defaults = { + requireDefaultIndex: true, requireUICapability: 'discover.show', k7Breadcrumbs: ($route, $injector) => - $injector.invoke( - $route.current.params.id - ? getSavedSearchBreadcrumbs - : getRootBreadcrumbs - ), + $injector.invoke($route.current.params.id ? getSavedSearchBreadcrumbs : getRootBreadcrumbs), badge: uiCapabilities => { if (uiCapabilities.discover.save) { return undefined; @@ -112,21 +114,21 @@ uiRoutes tooltip: i18n.translate('kbn.discover.badge.readOnly.tooltip', { defaultMessage: 'Unable to save searches', }), - iconType: 'glasses' + iconType: 'glasses', }; - } - }) - .when('/discover/:id?', { + }, + }; + $routeProvider.when('/discover/:id?', { + ...defaults, template: indexTemplate, reloadOnSearch: false, resolve: { - savedObjects: function (Promise, indexPatterns, config, Private, $rootScope, kbnUrl, redirectWhenMissing, savedSearches, $route) { - const State = Private(StateProvider); + savedObjects: function (redirectWhenMissing, $route, kbnUrl, Promise, $rootScope) { + const indexPatterns = dataLP.indexPatterns.indexPatterns; const savedSearchId = $route.current.params.id; - - return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl).then(() => { + return ensureDefaultIndexPattern(core, dataLP, $rootScope, kbnUrl).then(() => { return Promise.props({ - ip: indexPatterns.getCache().then((savedObjects) => { + ip: indexPatterns.getCache().then((indexPatternList) => { /** * In making the indexPattern modifiable it was placed in appState. Unfortunately, * the load order of AppState conflicts with the load order of many other things @@ -138,19 +140,16 @@ uiRoutes */ const state = new State('_a', {}); - const specified = !!state.index; - const exists = _.findIndex(savedObjects, o => o.id === state.index) > -1; - const id = exists ? state.index : config.get('defaultIndex'); + const id = getIndexPatternId(state.index, indexPatternList, uiSettings.get('defaultIndex')); state.destroy(); - return Promise.props({ - list: savedObjects, + list: indexPatternList, loaded: indexPatterns.get(id), stateVal: state.index, - stateValFound: specified && exists + stateValFound: !!state.index && id === state.index, }); }), - savedSearch: savedSearches.get(savedSearchId) + savedSearch: getServices().getSavedSearchById(savedSearchId, kbnUrl) .then((savedSearch) => { if (savedSearchId) { chrome.recentlyAccessed.add( @@ -169,6 +168,7 @@ uiRoutes }, } }); +}); app.directive('discoverApp', function () { return { @@ -190,12 +190,14 @@ function discoverController( config, kbnUrl, localStorage, - uiCapabilities + uiCapabilities, + getAppState, + globalState, ) { const responseHandler = vislibSeriesResponseHandlerProvider().handler; const getUnhashableStates = Private(getUnhashableStatesProvider); + const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager); - const queryFilter = Private(FilterBarQueryFilterProvider); const inspectorAdapters = { requests: new RequestAdapter() @@ -235,6 +237,7 @@ function discoverController( if (abortController) abortController.abort(); savedSearch.destroy(); subscriptions.unsubscribe(); + filterStateManager.destroy(); }); const $appStatus = $scope.appStatus = this.appStatus = { @@ -393,7 +396,7 @@ function discoverController( $scope.searchSource.setParent(timeRangeSearchSource); const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : ''; - docTitle.change(`Discover${pageTitleSuffix}`); + chrome.docTitle.change(`Discover${pageTitleSuffix}`); const discoverBreadcrumbsTitle = i18n.translate('kbn.discover.discoverBreadcrumbTitle', { defaultMessage: 'Discover', }); @@ -413,16 +416,16 @@ function discoverController( const $state = $scope.state = new AppState(getStateDefaults()); - $scope.filters = queryFilter.getFilters(); + $scope.filters = filterManager.getFilters(); $scope.screenTitle = savedSearch.title; $scope.onFiltersUpdated = filters => { - // The filters will automatically be set when the queryFilter emits an update event (see below) - queryFilter.setFilters(filters); + // The filters will automatically be set when the filterManager emits an update event (see below) + filterManager.setFilters(filters); }; const getFieldCounts = async () => { - // the field counts aren't set until we have the data back, + // the field counts aren't set until we have the dataLP back, // so we wait for the fetch to be done before proceeding if ($scope.fetchStatus === fetchStatuses.COMPLETE) { return $scope.fieldCounts; @@ -578,22 +581,22 @@ function discoverController( if (!angular.equals(sort, currentSort)) $scope.fetch(); }); - // update data source when filters update - subscriptions.add(subscribeWithScope($scope, queryFilter.getUpdates$(), { + // update dataLP source when filters update + subscriptions.add(subscribeWithScope($scope, filterManager.getUpdates$(), { next: () => { - $scope.filters = queryFilter.getFilters(); + $scope.filters = filterManager.getFilters(); $scope.updateDataSource().then(function () { $state.save(); }); } })); - // fetch data when filters fire fetch event - subscriptions.add(subscribeWithScope($scope, queryFilter.getUpdates$(), { + // fetch dataLP when filters fire fetch event + subscriptions.add(subscribeWithScope($scope, filterManager.getUpdates$(), { next: $scope.fetch })); - // update data source when hitting forward/back and the query changes + // update dataLP source when hitting forward/back and the query changes $scope.$listen($state, 'fetch_with_changes', function (diff) { if (diff.indexOf('query') >= 0) $scope.fetch(); }); @@ -633,7 +636,7 @@ function discoverController( let prev = {}; const status = { UNINITIALIZED: 'uninitialized', - LOADING: 'loading', // initial data load + LOADING: 'loading', // initial dataLP load READY: 'ready', // results came back NO_RESULTS: 'none' // no results came back }; @@ -704,7 +707,7 @@ function discoverController( savedSearchTitle: savedSearch.title, } }), - 'data-test-subj': 'saveSearchSuccess', + 'dataLP-test-subj': 'saveSearchSuccess', }); if (savedSearch.id !== $route.current.params.id) { @@ -765,7 +768,7 @@ function discoverController( } else { toastNotifications.addError(error, { title: i18n.translate('kbn.discover.errorLoadingData', { - defaultMessage: 'Error loading data', + defaultMessage: 'Error loading dataLP', }), }); } @@ -813,10 +816,10 @@ function discoverController( function logInspectorRequest() { inspectorAdapters.requests.reset(); const title = i18n.translate('kbn.discover.inspectorRequestDataTitle', { - defaultMessage: 'Data', + defaultMessage: 'dataLP', }); const description = i18n.translate('kbn.discover.inspectorRequestDescription', { - defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.', + defaultMessage: 'This request queries Elasticsearch to fetch the dataLP for the search.', }); inspectorRequest = inspectorAdapters.requests.start(title, { description }); inspectorRequest.stats(getRequestInspectorStats($scope.searchSource)); @@ -875,7 +878,7 @@ function discoverController( .setField('size', $scope.opts.sampleSize) .setField('sort', getSortForSearchSource($state.sort, indexPattern)) .setField('query', !$state.query ? null : $state.query) - .setField('filter', queryFilter.getFilters()); + .setField('filter', filterManager.getFilters()); }); $scope.setSortOrder = function setSortOrder(sortPair) { @@ -885,8 +888,8 @@ function discoverController( // TODO: On array fields, negating does not negate the combination, rather all terms $scope.filterQuery = function (field, values, operation) { $scope.indexPattern.popularizeField(field, 1); - const newFilters = generateFilters(queryFilter, field, values, operation, $scope.indexPattern.id); - return queryFilter.addFilters(newFilters); + const newFilters = generateFilters(filterManager, field, values, operation, $scope.indexPattern.id); + return filterManager.addFilters(newFilters); }; $scope.addColumn = function addColumn(columnName) { @@ -933,7 +936,7 @@ function discoverController( query: '', language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'), }; - queryFilter.removeAll(); + filterManager.removeAll(); $state.save(); $scope.fetch(); }; @@ -941,8 +944,7 @@ function discoverController( const updateStateFromSavedQuery = (savedQuery) => { $state.query = savedQuery.attributes.query; $state.save(); - - queryFilter.setFilters(savedQuery.attributes.filters || []); + filterManager.setFilters(savedQuery.attributes.filters || []); if (savedQuery.attributes.timefilter) { timefilter.setTime({ @@ -972,7 +974,6 @@ function discoverController( $scope.savedQuery = undefined; return; } - if (!$scope.savedQuery || newSavedQueryId !== $scope.savedQuery.id) { savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery) => { $scope.$evalAsync(() => { @@ -983,6 +984,7 @@ function discoverController( } }); + async function setupVisualization() { // If no timefield has been specified we don't create a histogram of messages if (!$scope.opts.timefield) return; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc.ts b/src/legacy/core_plugins/kibana/public/discover/angular/doc.ts index e6c890c9a66a2..af9556656afab 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc.ts @@ -16,13 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -import { getServices, IndexPatterns } from '../kibana_services'; +import { getAngularModule, wrapInI18nContext, getServices } from '../kibana_services'; // @ts-ignore -import { getRootBreadcrumbs } from '../breadcrumbs'; +import { getRootBreadcrumbs } from '../helpers/breadcrumbs'; import html from './doc.html'; -import { Doc } from '../doc/doc'; -const { uiRoutes, uiModules, wrapInI18nContext, timefilter } = getServices(); -uiModules.get('apps/discover').directive('discoverDoc', function(reactDirective: any) { +import { Doc } from '../components/doc/doc'; + +interface LazyScope extends ng.IScope { + [key: string]: any; +} + +const { timefilter } = getServices(); +const app = getAngularModule(); +app.directive('discoverDoc', function(reactDirective: any) { return reactDirective( wrapInI18nContext(Doc), [ @@ -36,28 +42,28 @@ uiModules.get('apps/discover').directive('discoverDoc', function(reactDirective: ); }); -uiRoutes - // the old, pre 8.0 route, no longer used, keep it to stay compatible - // somebody might have bookmarked his favorite log messages - .when('/doc/:indexPattern/:index/:type', { - redirectTo: '/doc/:indexPattern/:index', - }) - // the new route, es 7 deprecated types, es 8 removed them - .when('/doc/:indexPattern/:index', { - controller: ($scope: any, $route: any, es: any, indexPatterns: IndexPatterns) => { - timefilter.disableAutoRefreshSelector(); - timefilter.disableTimeRangeSelector(); - $scope.esClient = es; - $scope.id = $route.current.params.id; - $scope.index = $route.current.params.index; - $scope.indexPatternId = $route.current.params.indexPattern; - $scope.indexPatternService = indexPatterns; - }, - template: html, - k7Breadcrumbs: ($route: any) => [ - ...getRootBreadcrumbs(), - { - text: `${$route.current.params.index}#${$route.current.params.id}`, +app.config(($routeProvider: any) => { + $routeProvider + .when('/discover/doc/:indexPattern/:index/:type', { + redirectTo: '/discover/doc/:indexPattern/:index', + }) + // the new route, es 7 deprecated types, es 8 removed them + .when('/discover/doc/:indexPattern/:index', { + controller: ($scope: LazyScope, $route: any, es: any) => { + timefilter.disableAutoRefreshSelector(); + timefilter.disableTimeRangeSelector(); + $scope.esClient = es; + $scope.id = $route.current.params.id; + $scope.index = $route.current.params.index; + $scope.indexPatternId = $route.current.params.indexPattern; + $scope.indexPatternService = getServices().indexPatterns; }, - ], - }); + template: html, + k7Breadcrumbs: ($route: any) => [ + ...getRootBreadcrumbs(), + { + text: `${$route.current.params.index}#${$route.current.params.id}`, + }, + ], + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/doc_table.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/doc_table.js index 417d521dd44ed..2c6718e44894f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/doc_table.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/doc_table.js @@ -22,7 +22,7 @@ import expect from '@kbn/expect'; import _ from 'lodash'; import ngMock from 'ng_mock'; import 'ui/private'; -import '..'; +import { pluginInstance } from 'plugins/kibana/discover/index'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import hits from 'fixtures/real_hits'; @@ -65,8 +65,8 @@ const destroy = function () { describe('docTable', function () { let $elem; - - beforeEach(ngMock.module('kibana')); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); beforeEach(function () { $elem = angular.element(` pluginInstance.initializeServices(true)); + beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(ngMock.module('app/discover')); beforeEach( ngMock.inject(function (_config_, $rootScope, Private) { config = _config_; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/index.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/index.ts similarity index 78% rename from src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/index.js rename to src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/index.ts index 7462de544dbce..3a037971a1253 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/index.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/index.ts @@ -16,18 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { getServices } from '../../../../kibana_services'; +import { wrapInI18nContext } from '../../../../kibana_services'; import { ToolBarPagerText } from './tool_bar_pager_text'; import { ToolBarPagerButtons } from './tool_bar_pager_buttons'; -const { wrapInI18nContext, uiModules } = getServices(); - -const app = uiModules.get('kibana'); - -app.directive('toolBarPagerText', function (reactDirective) { +export function createToolBarPagerTextDirective(reactDirective: any) { return reactDirective(wrapInI18nContext(ToolBarPagerText)); -}); +} -app.directive('toolBarPagerButtons', function (reactDirective) { +export function createToolBarPagerButtonsDirective(reactDirective: any) { return reactDirective(wrapInI18nContext(ToolBarPagerButtons)); -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_header.ts b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_header.ts index f447c54507729..c4312b3bc468a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_header.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_header.ts @@ -17,11 +17,10 @@ * under the License. */ import { wrapInI18nContext } from 'ui/i18n'; -import { getServices } from '../../../kibana_services'; +import { UiSettingsClient } from 'kibana/public'; import { TableHeader } from './table_header/table_header'; -const module = getServices().uiModules.get('app/discover'); -module.directive('kbnTableHeader', function(reactDirective: any, config: any) { +export function createTableHeaderDirective(reactDirective: any, config: UiSettingsClient) { return reactDirective( wrapInI18nContext(TableHeader), [ @@ -40,4 +39,4 @@ module.directive('kbnTableHeader', function(reactDirective: any, config: any) { isShortDots: config.get('shortDots:enable'), } ); -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.ts similarity index 86% rename from src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js rename to src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.ts index 6f5a94442e977..883513173187a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.ts @@ -19,32 +19,34 @@ import _ from 'lodash'; import $ from 'jquery'; +import { UiSettingsClient } from 'kibana/public'; +// @ts-ignore import rison from 'rison-node'; import '../../doc_viewer'; +// @ts-ignore import { noWhiteSpace } from '../../../../../common/utils/no_white_space'; import openRowHtml from './table_row/open.html'; import detailsHtml from './table_row/details.html'; -import { getServices } from '../../../kibana_services'; + import { dispatchRenderComplete } from '../../../../../../../../plugins/kibana_utils/public'; import cellTemplateHtml from '../components/table_row/cell.html'; import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html'; import { esFilters } from '../../../../../../../../plugins/data/public'; -const module = getServices().uiModules.get('app/discover'); - // guesstimate at the minimum number of chars wide cells in the table should be const MIN_LINE_LENGTH = 20; -/** - * kbnTableRow directive - * - * Display a row in the table - * ``` - * - * ``` - */ -module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl, config) { +interface LazyScope extends ng.IScope { + [key: string]: any; +} + +export function createTableRowDirective( + $compile: ng.ICompileService, + $httpParamSerializer: any, + kbnUrl: any, + config: UiSettingsClient +) { const cellTemplate = _.template(noWhiteSpace(cellTemplateHtml)); const truncateByHeightTemplate = _.template(noWhiteSpace(truncateByHeightTemplateHtml)); @@ -59,18 +61,18 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl onAddColumn: '=?', onRemoveColumn: '=?', }, - link: function ($scope, $el) { + link: ($scope: LazyScope, $el: JQuery) => { $el.after(''); $el.empty(); // when we compile the details, we use this $scope - let $detailsScope; + let $detailsScope: LazyScope; // when we compile the toggle button in the summary, we use this $scope let $toggleScope; // toggle display of the rows details, a full list of the fields from each row - $scope.toggleRow = function () { + $scope.toggleRow = () => { const $detailsTr = $el.next(); $scope.open = !$scope.open; @@ -99,18 +101,18 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl $compile($detailsTr)($detailsScope); }; - $scope.$watchMulti(['indexPattern.timeFieldName', 'row.highlight', '[]columns'], function () { - createSummaryRow($scope.row, $scope.row._id); + $scope.$watchMulti(['indexPattern.timeFieldName', 'row.highlight', '[]columns'], () => { + createSummaryRow($scope.row); }); - $scope.inlineFilter = function inlineFilter($event, type) { + $scope.inlineFilter = function inlineFilter($event: any, type: string) { const column = $($event.target).data().column; const field = $scope.indexPattern.fields.getByName(column); $scope.filter(field, $scope.flattenedRow[column], type); }; $scope.getContextAppHref = () => { - const path = kbnUrl.eval('#/context/{{ indexPattern }}/{{ anchorId }}', { + const path = kbnUrl.eval('#/discover/context/{{ indexPattern }}/{{ anchorId }}', { anchorId: $scope.row._id, indexPattern: $scope.indexPattern.id, }); @@ -124,7 +126,7 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl }; // create a tr element that lists the value for each *column* - function createSummaryRow(row) { + function createSummaryRow(row: any) { const indexPattern = $scope.indexPattern; $scope.flattenedRow = indexPattern.flattenHit(row); @@ -145,7 +147,7 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl ); } - $scope.columns.forEach(function (column) { + $scope.columns.forEach(function(column: any) { const isFilterable = $scope.flattenedRow[column] !== undefined && mapping(column) && @@ -164,11 +166,11 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl }); let $cells = $el.children(); - newHtmls.forEach(function (html, i) { + newHtmls.forEach(function(html, i) { const $cell = $cells.eq(i); if ($cell.data('discover:html') === html) return; - const reuse = _.find($cells.slice(i + 1), function (cell) { + const reuse = _.find($cells.slice(i + 1), function(cell: any) { return $.data(cell, 'discover:html') === html; }); @@ -202,7 +204,7 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl /** * Fill an element with the value of a field */ - function _displayField(row, fieldName, truncate) { + function _displayField(row: any, fieldName: string, truncate = false) { const indexPattern = $scope.indexPattern; const text = indexPattern.formatField(row, fieldName); @@ -216,4 +218,4 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl } }, }; -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row/details.html b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row/details.html index 5c8785e8dc5f9..d149a9023816a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row/details.html +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row/details.html @@ -30,7 +30,7 @@ @@ -48,5 +48,5 @@ on-remove-column="onRemoveColumn" > - + diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.js deleted file mode 100644 index 72943671fec22..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.js +++ /dev/null @@ -1,126 +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 _ from 'lodash'; -import html from './doc_table.html'; -import './infinite_scroll'; -import './components/table_header'; -import './components/table_row'; -import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; -import { getServices } from '../../kibana_services'; -import './components/pager'; -import './lib/pager'; - -import { getLimitedSearchResultsMessage } from './doc_table_strings'; - -const { uiModules } = getServices(); - -uiModules.get('app/discover') - .directive('docTable', function (config, getAppState, pagerFactory, $filter) { - return { - restrict: 'E', - template: html, - scope: { - sorting: '=', - columns: '=', - hits: '=', - totalHitCount: '=', - indexPattern: '=', - isLoading: '=?', - infiniteScroll: '=?', - filter: '=?', - filters: '=?', - minimumVisibleRows: '=?', - onAddColumn: '=?', - onChangeSortOrder: '=?', - onMoveColumn: '=?', - onRemoveColumn: '=?', - inspectorAdapters: '=?', - }, - link: function ($scope, $el) { - $scope.$watch('minimumVisibleRows', (minimumVisibleRows) => { - $scope.limit = Math.max(minimumVisibleRows || 50, $scope.limit || 50); - }); - - $scope.persist = { - sorting: $scope.sorting, - columns: $scope.columns - }; - - const limitTo = $filter('limitTo'); - const calculateItemsOnPage = () => { - $scope.pager.setTotalItems($scope.hits.length); - $scope.pageOfItems = limitTo($scope.hits, $scope.pager.pageSize, $scope.pager.startIndex); - }; - - $scope.limitedResultsWarning = getLimitedSearchResultsMessage(config.get('discover:sampleSize')); - - $scope.addRows = function () { - $scope.limit += 50; - }; - - // This exists to fix the problem of an empty initial column list not playing nice with watchCollection. - $scope.$watch('columns', function (columns) { - if (columns.length !== 0) return; - - const $state = getAppState(); - $scope.columns.push('_source'); - if ($state) $state.replace(); - }); - - $scope.$watchCollection('columns', function (columns, oldColumns) { - if (oldColumns.length === 1 && oldColumns[0] === '_source' && $scope.columns.length > 1) { - _.pull($scope.columns, '_source'); - } - - if ($scope.columns.length === 0) $scope.columns.push('_source'); - }); - - $scope.$watch('hits', hits => { - if (!hits) return; - - // Reset infinite scroll limit - $scope.limit = 50; - - if (hits.length === 0) { - dispatchRenderComplete($el[0]); - } - - if ($scope.infiniteScroll) return; - $scope.pager = pagerFactory.create(hits.length, 50, 1); - calculateItemsOnPage(); - }); - - $scope.pageOfItems = []; - $scope.onPageNext = () => { - $scope.pager.nextPage(); - calculateItemsOnPage(); - }; - - $scope.onPagePrevious = () => { - $scope.pager.previousPage(); - calculateItemsOnPage(); - }; - - $scope.shouldShowLimitedResultsWarning = () => ( - !$scope.pager.hasNextPage && $scope.pager.totalItems < $scope.totalHitCount - ); - } - }; - }); diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.ts b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.ts new file mode 100644 index 0000000000000..1be87bfa1f3b2 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/doc_table.ts @@ -0,0 +1,133 @@ +/* + * 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 _ from 'lodash'; +import { UiSettingsClient } from 'kibana/public'; +import html from './doc_table.html'; +import './infinite_scroll'; +import './components/table_header'; +import './components/table_row'; +import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; +import './components/pager'; +import './lib/pager'; +// @ts-ignore +import { getLimitedSearchResultsMessage } from './doc_table_strings'; + +interface LazyScope extends ng.IScope { + [key: string]: any; +} + +export function createDocTableDirective( + config: UiSettingsClient, + getAppState: any, + pagerFactory: any, + $filter: any +) { + return { + restrict: 'E', + template: html, + scope: { + sorting: '=', + columns: '=', + hits: '=', + totalHitCount: '=', + indexPattern: '=', + isLoading: '=?', + infiniteScroll: '=?', + filter: '=?', + filters: '=?', + minimumVisibleRows: '=?', + onAddColumn: '=?', + onChangeSortOrder: '=?', + onMoveColumn: '=?', + onRemoveColumn: '=?', + inspectorAdapters: '=?', + }, + link: ($scope: LazyScope, $el: JQuery) => { + $scope.$watch('minimumVisibleRows', (minimumVisibleRows: number) => { + $scope.limit = Math.max(minimumVisibleRows || 50, $scope.limit || 50); + }); + + $scope.persist = { + sorting: $scope.sorting, + columns: $scope.columns, + }; + + const limitTo = $filter('limitTo'); + const calculateItemsOnPage = () => { + $scope.pager.setTotalItems($scope.hits.length); + $scope.pageOfItems = limitTo($scope.hits, $scope.pager.pageSize, $scope.pager.startIndex); + }; + + $scope.limitedResultsWarning = getLimitedSearchResultsMessage( + config.get('discover:sampleSize') + ); + + $scope.addRows = function() { + $scope.limit += 50; + }; + + // This exists to fix the problem of an empty initial column list not playing nice with watchCollection. + $scope.$watch('columns', function(columns: string[]) { + if (columns.length !== 0) return; + + const $state = getAppState(); + $scope.columns.push('_source'); + if ($state) $state.replace(); + }); + + $scope.$watchCollection('columns', function(columns: string[], oldColumns: string[]) { + if (oldColumns.length === 1 && oldColumns[0] === '_source' && $scope.columns.length > 1) { + _.pull($scope.columns, '_source'); + } + + if ($scope.columns.length === 0) $scope.columns.push('_source'); + }); + + $scope.$watch('hits', (hits: any) => { + if (!hits) return; + + // Reset infinite scroll limit + $scope.limit = 50; + + if (hits.length === 0) { + dispatchRenderComplete($el[0]); + } + + if ($scope.infiniteScroll) return; + $scope.pager = pagerFactory.create(hits.length, 50, 1); + calculateItemsOnPage(); + }); + + $scope.pageOfItems = []; + $scope.onPageNext = () => { + $scope.pager.nextPage(); + calculateItemsOnPage(); + }; + + $scope.onPagePrevious = () => { + $scope.pager.previousPage(); + calculateItemsOnPage(); + }; + + $scope.shouldShowLimitedResultsWarning = () => + !$scope.pager.hasNextPage && $scope.pager.totalItems < $scope.totalHitCount; + }, + }; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/infinite_scroll.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/infinite_scroll.ts similarity index 68% rename from src/legacy/core_plugins/kibana/public/discover/angular/doc_table/infinite_scroll.js rename to src/legacy/core_plugins/kibana/public/discover/angular/doc_table/infinite_scroll.ts index bf12deeb6b05f..1a8ad372bbb8a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/infinite_scroll.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/infinite_scroll.ts @@ -18,30 +18,32 @@ */ import $ from 'jquery'; -import { getServices } from '../../kibana_services'; -const module = getServices().uiModules.get('app/discover'); +interface LazyScope extends ng.IScope { + [key: string]: any; +} -module.directive('kbnInfiniteScroll', function () { +export function createInfiniteScrollDirective() { return { restrict: 'E', scope: { - more: '=' + more: '=', }, - link: function ($scope, $element) { + link: ($scope: LazyScope, $element: JQuery) => { const $window = $(window); - let checkTimer; + let checkTimer: any; function onScroll() { if (!$scope.more) return; - const winHeight = $window.height(); - const winBottom = winHeight + $window.scrollTop(); - const elTop = $element.offset().top; + const winHeight = Number($window.height()); + const winBottom = Number(winHeight) + Number($window.scrollTop()); + const offset = $element.offset(); + const elTop = offset ? offset.top : 0; const remaining = elTop - winBottom; - if (remaining <= winHeight * 0.50) { - $scope[$scope.$$phase ? '$eval' : '$apply'](function () { + if (remaining <= winHeight * 0.5) { + $scope[$scope.$$phase ? '$eval' : '$apply'](function() { $scope.more(); }); } @@ -49,18 +51,18 @@ module.directive('kbnInfiniteScroll', function () { function scheduleCheck() { if (checkTimer) return; - checkTimer = setTimeout(function () { + checkTimer = setTimeout(function() { checkTimer = null; onScroll(); }, 50); } $window.on('scroll', scheduleCheck); - $scope.$on('$destroy', function () { + $scope.$on('$destroy', function() { clearTimeout(checkTimer); $window.off('scroll', scheduleCheck); }); scheduleCheck(); - } + }, }; -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/lib/pager/pager_factory.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/lib/pager/pager_factory.ts similarity index 83% rename from src/legacy/core_plugins/kibana/public/discover/angular/doc_table/lib/pager/pager_factory.js rename to src/legacy/core_plugins/kibana/public/discover/angular/doc_table/lib/pager/pager_factory.ts index 5d488fab0c87f..fe576b63568dd 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/lib/pager/pager_factory.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/lib/pager/pager_factory.ts @@ -16,16 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - -import { getServices } from '../../../../kibana_services'; +// @ts-ignore import { Pager } from './pager'; -const app = getServices().uiModules.get('kibana'); - -app.factory('pagerFactory', () => { +export function createPagerFactory() { return { - create(...args) { + create(...args: unknown[]) { return new Pager(...args); - } + }, }; -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_viewer.ts b/src/legacy/core_plugins/kibana/public/discover/angular/doc_viewer.ts index c13c354528413..6ba47b839563b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_viewer.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_viewer.ts @@ -17,13 +17,9 @@ * under the License. */ -// @ts-ignore -import { getServices } from '../kibana_services'; -import { DocViewer } from '../doc_viewer/doc_viewer'; +import { DocViewer } from '../components/doc_viewer/doc_viewer'; -const { uiModules } = getServices(); - -uiModules.get('apps/discover').directive('docViewer', (reactDirective: any) => { +export function createDocViewerDirective(reactDirective: any) { return reactDirective( DocViewer, [ @@ -46,4 +42,4 @@ uiModules.get('apps/discover').directive('docViewer', (reactDirective: any) => { }, } ); -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/application.ts b/src/legacy/core_plugins/kibana/public/discover/application.ts new file mode 100644 index 0000000000000..83f4a5962e3cd --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/application.ts @@ -0,0 +1,43 @@ +/* + * 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 angular from 'angular'; + +/** + * Here's where Discover's inner angular is mounted and rendered + */ +export async function renderApp(moduleName: string, element: HTMLElement) { + await import('./angular'); + const $injector = mountDiscoverApp(moduleName, element); + return () => $injector.get('$rootScope').$destroy(); +} + +function mountDiscoverApp(moduleName: string, element: HTMLElement) { + const mountpoint = document.createElement('div'); + const appWrapper = document.createElement('div'); + appWrapper.setAttribute('ng-view', ''); + mountpoint.appendChild(appWrapper); + // bootstrap angular into detached element and attach it later to + // make angular-within-angular possible + const $injector = angular.bootstrap(mountpoint, [moduleName]); + // initialize global state handler + $injector.get('globalState'); + element.appendChild(mountpoint); + return $injector; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/doc/doc.test.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc/doc.test.tsx similarity index 98% rename from src/legacy/core_plugins/kibana/public/discover/doc/doc.test.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc/doc.test.tsx index ee80f29c053dc..4df56483fa5c6 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc/doc.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc/doc.test.tsx @@ -28,7 +28,7 @@ jest.mock('../doc_viewer/doc_viewer', () => ({ DocViewer: 'test', })); -jest.mock('../kibana_services', () => { +jest.mock('../../kibana_services', () => { return { getServices: () => ({ metadata: { diff --git a/src/legacy/core_plugins/kibana/public/discover/doc/doc.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc/doc.tsx similarity index 99% rename from src/legacy/core_plugins/kibana/public/discover/doc/doc.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc/doc.tsx index 0e0e6ed110ca6..85308d9c7e03e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc/doc.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc/doc.tsx @@ -21,7 +21,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent } from '@elastic/eui'; import { DocViewer } from '../doc_viewer/doc_viewer'; import { ElasticRequestState, useEsDocSearch } from './use_es_doc_search'; -import { IndexPatterns, ElasticSearchHit, getServices } from '../kibana_services'; +import { IndexPatterns, ElasticSearchHit, getServices } from '../../kibana_services'; export interface ElasticSearchResult { hits: { diff --git a/src/legacy/core_plugins/kibana/public/discover/doc/use_es_doc_search.test.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc/use_es_doc_search.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/doc/use_es_doc_search.test.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc/use_es_doc_search.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/doc/use_es_doc_search.ts b/src/legacy/core_plugins/kibana/public/discover/components/doc/use_es_doc_search.ts similarity index 97% rename from src/legacy/core_plugins/kibana/public/discover/doc/use_es_doc_search.ts rename to src/legacy/core_plugins/kibana/public/discover/components/doc/use_es_doc_search.ts index 538fbed821f00..20bffe829de16 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc/use_es_doc_search.ts +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc/use_es_doc_search.ts @@ -17,7 +17,7 @@ * under the License. */ import { useEffect, useState } from 'react'; -import { ElasticSearchHit, IndexPattern } from '../kibana_services'; +import { ElasticSearchHit, IndexPattern } from '../../kibana_services'; import { DocProps } from './doc'; export enum ElasticRequestState { diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/_doc_viewer.scss b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/_doc_viewer.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/_doc_viewer.scss rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/_doc_viewer.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/_index.scss b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/_index.scss similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/_index.scss rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/_index.scss diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer.test.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer.test.tsx similarity index 98% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer.test.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer.test.tsx index 12473b25802f2..158ed4ccc7759 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer.test.tsx @@ -28,7 +28,7 @@ import { getDocViewsSorted as mockGetDocViewsSorted, } from 'ui/registry/doc_views'; -jest.mock('../kibana_services', () => { +jest.mock('../../kibana_services', () => { return { getServices: () => ({ docViewsRegistry: { diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer.tsx similarity index 90% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer.tsx index aa737ebd8dcf1..a2d58439ad031 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer.tsx @@ -17,8 +17,9 @@ * under the License. */ import React from 'react'; +import { DocView } from 'ui/registry/doc_views_types'; import { EuiTabbedContent } from '@elastic/eui'; -import { getServices, DocViewRenderProps } from '../kibana_services'; +import { getServices, DocViewRenderProps } from '../../kibana_services'; import { DocViewerTab } from './doc_viewer_tab'; /** @@ -31,7 +32,7 @@ export function DocViewer(renderProps: DocViewRenderProps) { const { docViewsRegistry } = getServices(); const tabs = docViewsRegistry .getDocViewsSorted(renderProps.hit) - .map(({ title, render, component }, idx) => { + .map(({ title, render, component }: DocView, idx: number) => { return { id: title, name: title, diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_error.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_error.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_error.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_error.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_tab.test.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_tab.test.tsx similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_tab.test.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_tab.test.tsx index 5fa2d24dfa04c..476d7cef159fb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_tab.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_tab.test.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { DocViewRenderTab } from './doc_viewer_render_tab'; -import { DocViewRenderProps } from '../kibana_services'; +import { DocViewRenderProps } from '../../kibana_services'; test('Mounting and unmounting DocViewerRenderTab', () => { const unmountFn = jest.fn(); diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_tab.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_tab.tsx similarity index 95% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_tab.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_tab.tsx index 750ef6b6061e1..8ac11caefff90 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_tab.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_tab.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useRef, useEffect } from 'react'; -import { DocViewRenderFn, DocViewRenderProps } from '../kibana_services'; +import { DocViewRenderFn, DocViewRenderProps } from '../../kibana_services'; interface Props { render: DocViewRenderFn; diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_tab.tsx b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_tab.tsx similarity index 97% rename from src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_tab.tsx rename to src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_tab.tsx index 3721ba5818d41..19558129eae8d 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_viewer/doc_viewer_tab.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_tab.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; -import { DocViewRenderProps, DocViewRenderFn } from '../kibana_services'; +import { DocViewRenderProps, DocViewRenderFn } from '../../kibana_services'; import { DocViewRenderTab } from './doc_viewer_render_tab'; import { DocViewerError } from './doc_viewer_render_error'; diff --git a/src/legacy/core_plugins/kibana/public/discover/components/fetch_error/fetch_error.js b/src/legacy/core_plugins/kibana/public/discover/components/fetch_error/fetch_error.tsx similarity index 79% rename from src/legacy/core_plugins/kibana/public/discover/components/fetch_error/fetch_error.js rename to src/legacy/core_plugins/kibana/public/discover/components/fetch_error/fetch_error.tsx index 612ca860f8031..8f67c1952f998 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/fetch_error/fetch_error.js +++ b/src/legacy/core_plugins/kibana/public/discover/components/fetch_error/fetch_error.tsx @@ -19,10 +19,18 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; -import { getServices } from '../../kibana_services'; -const { uiModules, wrapInI18nContext, chrome } = getServices(); +import { getAngularModule, wrapInI18nContext, getServices } from '../../kibana_services'; -const DiscoverFetchError = ({ fetchError }) => { +interface Props { + fetchError: { + lang: string; + script: string; + message: string; + error: string; + }; +} + +const DiscoverFetchError = ({ fetchError }: Props) => { if (!fetchError) { return null; } @@ -30,7 +38,9 @@ const DiscoverFetchError = ({ fetchError }) => { let body; if (fetchError.lang === 'painless') { - const managementUrl = chrome.navLinks.get('kibana:management').url; + const { chrome } = getServices(); + const mangagementUrlObj = chrome.navLinks.get('kibana:management'); + const managementUrl = mangagementUrlObj ? mangagementUrlObj.url : ''; const url = `${managementUrl}/kibana/index_patterns`; body = ( @@ -80,8 +90,8 @@ const DiscoverFetchError = ({ fetchError }) => { ); }; -const app = uiModules.get('apps/discover', ['react']); +export function createFetchErrorDirective(reactDirective: any) { + return reactDirective(wrapInI18nContext(DiscoverFetchError)); +} -app.directive('discoverFetchError', reactDirective => - reactDirective(wrapInI18nContext(DiscoverFetchError)) -); +getAngularModule().directive('discoverFetchError', createFetchErrorDirective); diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field.js b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field.js index cfcb654077152..0c633e23c5e4b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field.js +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field.js @@ -20,16 +20,15 @@ import $ from 'jquery'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getServices } from '../../kibana_services'; +import { getServices } from '../../kibana_services'; import html from './discover_field.html'; import 'ui/directives/css_truncate'; import 'ui/directives/field_name'; import './string_progress_bar'; import detailsHtml from './lib/detail_views/string.html'; -const { uiModules, capabilities } = getServices(); -const app = uiModules.get('apps/discover'); -app.directive('discoverField', function ($compile) { + +export function createDiscoverFieldDirective($compile) { return { restrict: 'E', template: html, @@ -78,7 +77,7 @@ app.directive('discoverField', function ($compile) { }; - $scope.canVisualize = capabilities.visualize.show; + $scope.canVisualize = getServices().capabilities.visualize.show; $scope.toggleDisplay = function (field) { if (field.display) { @@ -135,4 +134,5 @@ app.directive('discoverField', function ($compile) { init(); } }; -}); +} + diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts index b78f993e18772..69865ec424325 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts @@ -16,18 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -// @ts-ignore -import { getServices } from '../../kibana_services'; +import { wrapInI18nContext } from '../../kibana_services'; import { DiscoverFieldSearch } from './discover_field_search'; -const { wrapInI18nContext, uiModules } = getServices(); - -const app = uiModules.get('apps/discover'); - -app.directive('discoverFieldSearch', function(reactDirective: any) { +export function createFieldSearchDirective(reactDirective: any) { return reactDirective(wrapInI18nContext(DiscoverFieldSearch), [ ['onChange', { watchDepth: 'reference' }], ['value', { watchDepth: 'value' }], ['types', { watchDepth: 'value' }], ]); -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_directive.ts b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_directive.ts index 5e3f678e388ad..46c8fa854847a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_directive.ts +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_directive.ts @@ -16,18 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -// @ts-ignore -import { getServices } from '../../kibana_services'; +import { wrapInI18nContext } from '../../kibana_services'; import { DiscoverIndexPattern } from './discover_index_pattern'; -const { wrapInI18nContext, uiModules } = getServices(); - -const app = uiModules.get('apps/discover'); - -app.directive('discoverIndexPatternSelect', function(reactDirective: any) { +export function createIndexPatternSelectDirective(reactDirective: any) { return reactDirective(wrapInI18nContext(DiscoverIndexPattern), [ ['indexPatternList', { watchDepth: 'reference' }], ['selectedIndexPattern', { watchDepth: 'reference' }], ['setIndexPattern', { watchDepth: 'reference' }], ]); -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.js b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.js index 99a63efc0e0fc..cc3d864fd371e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.js +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.js @@ -16,25 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -//field_name directive will be replaced very soon -import 'ui/directives/field_name'; -import './discover_field'; -import './discover_field_search_directive'; -import './discover_index_pattern_directive'; import _ from 'lodash'; import $ from 'jquery'; import rison from 'rison-node'; import { fieldCalculator } from './lib/field_calculator'; -import { - getServices, - FieldList -} from '../../kibana_services'; +import './discover_field'; +import './discover_field_search_directive'; +import './discover_index_pattern_directive'; +import { FieldList } from '../../kibana_services'; import fieldChooserTemplate from './field_chooser.html'; -const { uiModules } = getServices(); -const app = uiModules.get('apps/discover'); - -app.directive('discFieldChooser', function ($location, config, $route) { +export function createFieldChooserDirective($location, config, $route) { return { restrict: 'E', scope: { @@ -50,12 +42,11 @@ app.directive('discFieldChooser', function ($location, config, $route) { }, template: fieldChooserTemplate, link: function ($scope) { - $scope.showFilter = false; - $scope.toggleShowFilter = () => $scope.showFilter = !$scope.showFilter; + $scope.toggleShowFilter = () => ($scope.showFilter = !$scope.showFilter); $scope.selectedIndexPattern = $scope.indexPatternList.find( - (pattern) => pattern.id === $scope.indexPattern.id + pattern => pattern.id === $scope.indexPattern.id ); $scope.indexPatternList = _.sortBy($scope.indexPatternList, o => o.get('title')); $scope.setIndexPattern = function (id) { @@ -68,23 +59,17 @@ app.directive('discFieldChooser', function ($location, config, $route) { $route.reload(); }); - const filter = $scope.filter = { - props: [ - 'type', - 'aggregatable', - 'searchable', - 'missing', - 'name' - ], + const filter = ($scope.filter = { + props: ['type', 'aggregatable', 'searchable', 'missing', 'name'], defaults: { missing: true, type: 'any', - name: '' + name: '', }, boolOpts: [ { label: 'any', value: undefined }, { label: 'yes', value: true }, - { label: 'no', value: false } + { label: 'no', value: false }, ], reset: function () { filter.vals = _.clone(filter.defaults); @@ -105,15 +90,18 @@ app.directive('discFieldChooser', function ($location, config, $route) { return _.some(filter.props, function (prop) { return filter.vals[prop] !== filter.defaults[prop]; }); - } - }; + }, + }); function isFieldFiltered(field) { - const matchFilter = (filter.vals.type === 'any' || field.type === filter.vals.type); - const isAggregatable = (filter.vals.aggregatable == null || field.aggregatable === filter.vals.aggregatable); - const isSearchable = (filter.vals.searchable == null || field.searchable === filter.vals.searchable); - const scriptedOrMissing = !filter.vals.missing || field.type === '_source' || field.scripted || field.rowCount > 0; - const matchName = (!filter.vals.name || field.name.indexOf(filter.vals.name) !== -1); + const matchFilter = filter.vals.type === 'any' || field.type === filter.vals.type; + const isAggregatable = + filter.vals.aggregatable == null || field.aggregatable === filter.vals.aggregatable; + const isSearchable = + filter.vals.searchable == null || field.searchable === filter.vals.searchable; + const scriptedOrMissing = + !filter.vals.missing || field.type === '_source' || field.scripted || field.rowCount > 0; + const matchName = !filter.vals.name || field.name.indexOf(filter.vals.name) !== -1; return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName; } @@ -131,7 +119,7 @@ app.directive('discFieldChooser', function ($location, config, $route) { filter.active = filter.getActive(); if (filter.vals) { let count = 0; - Object.keys(filter.vals).forEach((key) => { + Object.keys(filter.vals).forEach(key => { if (key === 'missing' || key === 'name') { return; } @@ -144,11 +132,7 @@ app.directive('discFieldChooser', function ($location, config, $route) { } }); - $scope.$watchMulti([ - '[]fieldCounts', - '[]columns', - '[]hits' - ], function (cur, prev) { + $scope.$watchMulti(['[]fieldCounts', '[]columns', '[]hits'], function (cur, prev) { const newHits = cur[2] !== prev[2]; let fields = $scope.fields; const columns = $scope.columns || []; @@ -198,7 +182,9 @@ app.directive('discFieldChooser', function ($location, config, $route) { }; function getVisualizeUrl(field) { - if (!$scope.state) {return '';} + if (!$scope.state) { + return ''; + } let agg = {}; const isGeoPoint = field.type === 'geo_point'; @@ -211,18 +197,17 @@ app.directive('discFieldChooser', function ($location, config, $route) { schema: 'segment', params: { field: field.name, - interval: 'auto' - } + interval: 'auto', + }, }; - } else if (isGeoPoint) { agg = { type: 'geohash_grid', schema: 'segment', params: { field: field.name, - precision: 3 - } + precision: 3, + }, }; } else { agg = { @@ -231,26 +216,28 @@ app.directive('discFieldChooser', function ($location, config, $route) { params: { field: field.name, size: parseInt(config.get('discover:aggs:terms:size'), 10), - orderBy: '2' - } + orderBy: '2', + }, }; } - return '#/visualize/create?' + $.param(_.assign(_.clone($location.search()), { - indexPattern: $scope.state.index, - type: type, - _a: rison.encode({ - filters: $scope.state.filters || [], - query: $scope.state.query || undefined, - vis: { + return ( + '#/visualize/create?' + + $.param( + _.assign(_.clone($location.search()), { + indexPattern: $scope.state.index, type: type, - aggs: [ - { schema: 'metric', type: 'count', 'id': '2' }, - agg, - ] - } - }) - })); + _a: rison.encode({ + filters: $scope.state.filters || [], + query: $scope.state.query || undefined, + vis: { + type: type, + aggs: [{ schema: 'metric', type: 'count', id: '2' }, agg], + }, + }), + }) + ) + ); } $scope.computeDetails = function (field, recompute) { @@ -261,7 +248,7 @@ app.directive('discFieldChooser', function ($location, config, $route) { hits: $scope.hits, field: field, count: 5, - grouped: false + grouped: false, }), }; _.each(field.details.buckets, function (bucket) { @@ -285,13 +272,14 @@ app.directive('discFieldChooser', function ($location, config, $route) { const fieldNamesInDocs = _.keys(fieldCounts); const fieldNamesInIndexPattern = _.map(indexPattern.fields, 'name'); - _.difference(fieldNamesInDocs, fieldNamesInIndexPattern) - .forEach(function (unknownFieldName) { - fieldSpecs.push({ - name: unknownFieldName, - type: 'unknown' - }); + _.difference(fieldNamesInDocs, fieldNamesInIndexPattern).forEach(function ( + unknownFieldName + ) { + fieldSpecs.push({ + name: unknownFieldName, + type: 'unknown', }); + }); const fields = new FieldList(indexPattern, fieldSpecs); @@ -303,6 +291,6 @@ app.directive('discFieldChooser', function ($location, config, $route) { return fields; } - } + }, }; -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/string_progress_bar.js b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/string_progress_bar.tsx similarity index 72% rename from src/legacy/core_plugins/kibana/public/discover/components/field_chooser/string_progress_bar.js rename to src/legacy/core_plugins/kibana/public/discover/components/field_chooser/string_progress_bar.tsx index ca3a47cad5075..7e4fc79839a52 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/string_progress_bar.js +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/string_progress_bar.tsx @@ -17,19 +17,15 @@ * under the License. */ import React from 'react'; -import { getServices } from '../../kibana_services'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiProgress, - EuiText, - EuiToolTip, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiText, EuiToolTip } from '@elastic/eui'; +import { wrapInI18nContext } from '../../kibana_services'; -const { wrapInI18nContext, uiModules } = getServices(); -const module = uiModules.get('discover/field_chooser'); +interface Props { + percent: number; + count: number; +} -function StringFieldProgressBar(props) { +function StringFieldProgressBar(props: Props) { return ( - + - - {props.percent}% - + {props.percent}% ); } -module.directive('stringFieldProgressBar', function (reactDirective) { +export function createStringFieldProgressBarDirective(reactDirective: any) { return reactDirective(wrapInI18nContext(StringFieldProgressBar)); -}); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/__snapshots__/open_search_panel.test.js.snap b/src/legacy/core_plugins/kibana/public/discover/components/top_nav/__snapshots__/open_search_panel.test.js.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/top_nav/__snapshots__/open_search_panel.test.js.snap rename to src/legacy/core_plugins/kibana/public/discover/components/top_nav/__snapshots__/open_search_panel.test.js.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/components/top_nav/open_search_panel.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js rename to src/legacy/core_plugins/kibana/public/discover/components/top_nav/open_search_panel.js diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.test.js b/src/legacy/core_plugins/kibana/public/discover/components/top_nav/open_search_panel.test.js similarity index 96% rename from src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.test.js rename to src/legacy/core_plugins/kibana/public/discover/components/top_nav/open_search_panel.test.js index 3531088e3847c..ea5c0ef39604d 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.test.js +++ b/src/legacy/core_plugins/kibana/public/discover/components/top_nav/open_search_panel.test.js @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -jest.mock('../kibana_services', () => { +jest.mock('../../kibana_services', () => { return { getServices: () => ({ SavedObjectFinder: jest.fn() diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/show_open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/components/top_nav/show_open_search_panel.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/top_nav/show_open_search_panel.js rename to src/legacy/core_plugins/kibana/public/discover/components/top_nav/show_open_search_panel.js diff --git a/src/legacy/core_plugins/kibana/public/discover/context/README.md b/src/legacy/core_plugins/kibana/public/discover/context/README.md deleted file mode 100644 index 18ba118b4da79..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/context/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# DISCOVER CONTEXT - -Placeholder for Discover's context functionality, that's currently in [../angular/context](../angular/context). -Once fully de-angularized it should be moved to this location \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/index.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/index.ts index beeb6a7338f9d..3138008f3e3a0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/index.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/index.ts @@ -20,4 +20,3 @@ export * from './types'; export * from './search_embeddable_factory'; export * from './search_embeddable'; -export { SEARCH_EMBEDDABLE_TYPE } from './constants'; diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index 9fee0cfc3ea00..273c7d80f216c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -21,7 +21,6 @@ import * as Rx from 'rxjs'; import { Subscription } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; -import { npStart } from 'ui/new_platform'; import { SearchSourceContract } from '../../../../../ui/public/courier'; import { esFilters, @@ -55,8 +54,6 @@ import { } from '../kibana_services'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; -const { data } = npStart.plugins; - interface SearchScope extends ng.IScope { columns?: string[]; description?: string; @@ -81,7 +78,7 @@ interface SearchEmbeddableConfig { editUrl: string; indexPatterns?: IndexPattern[]; editable: boolean; - queryFilter: unknown; + filterManager: FilterManager; } export class SearchEmbeddable extends Embeddable @@ -112,7 +109,7 @@ export class SearchEmbeddable extends Embeddable editUrl, indexPatterns, editable, - queryFilter, + filterManager, }: SearchEmbeddableConfig, initialInput: SearchInput, private readonly executeTriggerActions: TExecuteTriggerActions, @@ -124,7 +121,7 @@ export class SearchEmbeddable extends Embeddable parent ); - this.filterManager = queryFilter as FilterManager; + this.filterManager = filterManager; this.savedSearch = savedSearch; this.$rootScope = $rootScope; this.$compile = $compile; @@ -132,9 +129,10 @@ export class SearchEmbeddable extends Embeddable requests: new RequestAdapter(), }; this.initializeSearchScope(); - const { timefilter } = data.query.timefilter; - this.autoRefreshFetchSubscription = timefilter.getAutoRefreshFetch$().subscribe(this.fetch); + this.autoRefreshFetchSubscription = getServices() + .timefilter.getAutoRefreshFetch$() + .subscribe(this.fetch); this.subscription = Rx.merge(this.getOutput$(), this.getInput$()).subscribe(() => { this.panelTitle = this.output.title || ''; @@ -281,10 +279,9 @@ export class SearchEmbeddable extends Embeddable }); const inspectorRequest = this.inspectorAdaptors.requests.start(title, { description }); inspectorRequest.stats(getRequestInspectorStats(searchSource)); - searchSource.getSearchRequestBody().then((body: any) => { + searchSource.getSearchRequestBody().then((body: Record) => { inspectorRequest.json(body); }); - this.searchScope.isLoading = true; try { @@ -292,7 +289,6 @@ export class SearchEmbeddable extends Embeddable const resp = await searchSource.fetch({ abortSignal: this.abortController.signal, }); - this.searchScope.isLoading = false; // Log response to inspector diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts index ebea646a09889..b5475b2629c70 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts @@ -16,18 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -import { IPrivate } from 'ui/private'; import { i18n } from '@kbn/i18n'; import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; -import '../angular/doc_table'; +import { IInjector } from 'ui/chrome'; import { getServices } from '../kibana_services'; import { EmbeddableFactory, ErrorEmbeddable, Container, } from '../../../../../../plugins/embeddable/public'; + import { TimeRange } from '../../../../../../plugins/data/public'; -import { SavedSearchLoader } from '../types'; import { SearchEmbeddable } from './search_embeddable'; import { SearchInput, SearchOutput } from './types'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; @@ -38,8 +37,15 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< SearchEmbeddable > { public readonly type = SEARCH_EMBEDDABLE_TYPE; + private $injector: IInjector | null; + private getInjector: () => Promise | null; + public isEditable: () => boolean; - constructor(private readonly executeTriggerActions: TExecuteTriggerActions) { + constructor( + private readonly executeTriggerActions: TExecuteTriggerActions, + getInjector: () => Promise, + isEditable: () => boolean + ) { super({ savedObjectMetaData: { name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { @@ -49,10 +55,9 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< getIconForSavedObject: () => 'search', }, }); - } - - public isEditable() { - return getServices().capabilities.discover.save as boolean; + this.$injector = null; + this.getInjector = getInjector; + this.isEditable = isEditable; } public canCreateNew() { @@ -70,20 +75,19 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< input: Partial & { id: string; timeRange: TimeRange }, parent?: Container ): Promise { - const $injector = await getServices().getInjector(); + if (!this.$injector) { + this.$injector = await this.getInjector(); + } + const $injector = this.$injector as IInjector; const $compile = $injector.get('$compile'); const $rootScope = $injector.get('$rootScope'); - const searchLoader = $injector.get('savedSearches'); - const editUrl = await getServices().addBasePath( - `/app/kibana${searchLoader.urlFor(savedObjectId)}` - ); - - const Private = $injector.get('Private'); + const filterManager = getServices().filterManager; - const queryFilter = Private(getServices().FilterBarQueryFilterProvider); + const url = await getServices().getSavedSearchUrlById(savedObjectId); + const editUrl = getServices().addBasePath(`/app/kibana${url}`); try { - const savedObject = await searchLoader.get(savedObjectId); + const savedObject = await getServices().getSavedSearchById(savedObjectId); const indexPattern = savedObject.searchSource.getField('index'); return new SearchEmbeddable( { @@ -91,7 +95,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< $rootScope, $compile, editUrl, - queryFilter, + filterManager, editable: getServices().capabilities.discover.save as boolean, indexPatterns: indexPattern ? [indexPattern] : [], }, @@ -109,6 +113,3 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< return new ErrorEmbeddable('Saved searches can only be created from a saved object', input); } } - -const factory = new SearchEmbeddableFactory(getServices().uiActions.executeTriggerActions); -getServices().embeddable.registerEmbeddableFactory(factory.type, factory); diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts new file mode 100644 index 0000000000000..93a823a054756 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -0,0 +1,328 @@ +/* + * 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. + */ + +// inner angular imports +// these are necessary to bootstrap the local angular. +// They can stay even after NP cutover +import angular from 'angular'; +import 'ui/angular-bootstrap'; +import { IPrivate } from 'ui/private'; +import { EuiIcon } from '@elastic/eui'; +// @ts-ignore +import { EventsProvider } from 'ui/events'; +import { PersistedState } from 'ui/persisted_state'; +// @ts-ignore +import { PromiseServiceCreator } from 'ui/promises/promises'; +// @ts-ignore +import { createEsService } from 'ui/es'; +import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; +// @ts-ignore +import { PrivateProvider } from 'ui/private/private'; +import { CoreStart, LegacyCoreStart, UiSettingsClientContract } from 'kibana/public'; +// @ts-ignore +import { watchMultiDecorator } from 'ui/directives/watch_multi/watch_multi'; +// @ts-ignore +import { registerListenEventListener } from 'ui/directives/listen/listen'; +// @ts-ignore +import { KbnAccessibleClickProvider } from 'ui/accessibility/kbn_accessible_click'; +// @ts-ignore +import { FieldNameDirectiveProvider } from 'ui/directives/field_name'; +// @ts-ignore +import { CollapsibleSidebarProvider } from 'ui/collapsible_sidebar/collapsible_sidebar'; +// @ts-ignore +import { CssTruncateProvide } from 'ui/directives/css_truncate'; +// @ts-ignore +import { FixedScrollProvider } from 'ui/fixed_scroll'; +// @ts-ignore +import { DebounceProviderTimeout } from 'ui/directives/debounce/debounce'; +// @ts-ignore +import { AppStateProvider } from 'ui/state_management/app_state'; +// @ts-ignore +import { GlobalStateProvider } from 'ui/state_management/global_state'; +// @ts-ignore +import { createRenderCompleteDirective } from 'ui/render_complete/directive'; +// @ts-ignore +import { StateManagementConfigProvider } from 'ui/state_management/config_provider'; +// @ts-ignore +import { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; +// @ts-ignore +import { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; +import { configureAppAngularModule } from 'ui/legacy_compat'; +import { IndexPatterns } from '../../../data/public/index_patterns/index_patterns'; +import { Storage } from '../../../../../plugins/kibana_utils/public'; +import { NavigationStart } from '../../../navigation/public'; +import { createDocTableDirective } from './angular/doc_table/doc_table'; +import { createTableHeaderDirective } from './angular/doc_table/components/table_header'; +import { + createToolBarPagerButtonsDirective, + createToolBarPagerTextDirective, +} from './angular/doc_table/components/pager'; +import { createTableRowDirective } from './angular/doc_table/components/table_row'; +import { createPagerFactory } from './angular/doc_table/lib/pager/pager_factory'; +import { createInfiniteScrollDirective } from './angular/doc_table/infinite_scroll'; +import { createDocViewerDirective } from './angular/doc_viewer'; +import { createFieldSearchDirective } from './components/field_chooser/discover_field_search_directive'; +import { createIndexPatternSelectDirective } from './components/field_chooser/discover_index_pattern_directive'; +import { createStringFieldProgressBarDirective } from './components/field_chooser/string_progress_bar'; +// @ts-ignore +import { createFieldChooserDirective } from './components/field_chooser/field_chooser'; + +// @ts-ignore +import { createDiscoverFieldDirective } from './components/field_chooser/discover_field'; +import { DiscoverStartPlugins } from './plugin'; + +/** + * returns the main inner angular module, it contains all the parts of Angular Discover + * needs to render, so in the end the current 'kibana' angular module is no longer necessary + */ +export function getInnerAngularModule(name: string, core: CoreStart, deps: DiscoverStartPlugins) { + const module = initializeInnerAngularModule(name, core, deps.navigation); + configureAppAngularModule(module, core as LegacyCoreStart, true); + return module; +} + +/** + * returns a slimmer inner angular module for embeddable rendering + */ +export function getInnerAngularModuleEmbeddable( + name: string, + core: CoreStart, + deps: DiscoverStartPlugins +) { + const module = initializeInnerAngularModule(name, core, deps.navigation, true); + configureAppAngularModule(module, core as LegacyCoreStart, true); + return module; +} + +let initialized = false; + +export function initializeInnerAngularModule( + name = 'app/discover', + core: CoreStart, + navigation: NavigationStart, + embeddable = false +) { + if (!initialized) { + createLocalI18nModule(); + createLocalPrivateModule(); + createLocalPromiseModule(); + createLocalConfigModule(core.uiSettings); + createLocalKbnUrlModule(); + createLocalPersistedStateModule(); + createLocalTopNavModule(navigation); + createLocalGlobalStateModule(); + createLocalAppStateModule(); + createLocalStorageModule(); + createElasticSearchModule(); + createIndexPatternsModule(); + createPagerFactoryModule(); + createDocTableModule(); + initialized = true; + } + + if (embeddable) { + return angular + .module(name, [ + 'ngSanitize', + 'react', + 'ui.bootstrap', + 'discoverI18n', + 'discoverPrivate', + 'discoverDocTable', + 'discoverPagerFactory', + 'discoverPersistedState', + ]) + .config(watchMultiDecorator) + .directive('icon', reactDirective => reactDirective(EuiIcon)) + .directive('fieldName', FieldNameDirectiveProvider) + .directive('renderComplete', createRenderCompleteDirective) + .service('debounce', ['$timeout', DebounceProviderTimeout]); + } + + return angular + .module(name, [ + 'ngSanitize', + 'ngRoute', + 'react', + 'ui.bootstrap', + 'elasticsearch', + 'discoverConfig', + 'discoverI18n', + 'discoverPrivate', + 'discoverPersistedState', + 'discoverTopNav', + 'discoverGlobalState', + 'discoverAppState', + 'discoverLocalStorageProvider', + 'discoverIndexPatterns', + 'discoverEs', + 'discoverDocTable', + 'discoverPagerFactory', + ]) + .config(watchMultiDecorator) + .run(registerListenEventListener) + .directive('icon', reactDirective => reactDirective(EuiIcon)) + .directive('kbnAccessibleClick', KbnAccessibleClickProvider) + .directive('fieldName', FieldNameDirectiveProvider) + .directive('collapsibleSidebar', CollapsibleSidebarProvider) + .directive('cssTruncate', CssTruncateProvide) + .directive('fixedScroll', FixedScrollProvider) + .directive('renderComplete', createRenderCompleteDirective) + .directive('discoverFieldSearch', createFieldSearchDirective) + .directive('discoverIndexPatternSelect', createIndexPatternSelectDirective) + .directive('stringFieldProgressBar', createStringFieldProgressBarDirective) + .directive('discoverField', createDiscoverFieldDirective) + .directive('discFieldChooser', createFieldChooserDirective) + .service('debounce', ['$timeout', DebounceProviderTimeout]); +} + +export function createLocalGlobalStateModule() { + angular + .module('discoverGlobalState', [ + 'discoverPrivate', + 'discoverConfig', + 'discoverKbnUrl', + 'discoverPromise', + ]) + .service('globalState', function(Private: IPrivate) { + return Private(GlobalStateProvider); + }); +} + +function createLocalPersistedStateModule() { + angular + .module('discoverPersistedState', ['discoverPrivate', 'discoverPromise']) + .factory('PersistedState', (Private: IPrivate) => { + const Events = Private(EventsProvider); + return class AngularPersistedState extends PersistedState { + constructor(value: any, path: any) { + super(value, path, Events); + } + }; + }); +} + +function createLocalKbnUrlModule() { + angular + .module('discoverKbnUrl', ['discoverPrivate', 'ngRoute']) + .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)) + .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider)); +} + +function createLocalConfigModule(uiSettings: UiSettingsClientContract) { + angular + .module('discoverConfig', ['discoverPrivate']) + .provider('stateManagementConfig', StateManagementConfigProvider) + .provider('config', () => { + return { + $get: () => ({ + get: (value: string) => { + return uiSettings ? uiSettings.get(value) : undefined; + }, + }), + }; + }); +} + +function createLocalPromiseModule() { + angular.module('discoverPromise', []).service('Promise', PromiseServiceCreator); +} + +function createLocalPrivateModule() { + angular.module('discoverPrivate', []).provider('Private', PrivateProvider); +} + +function createLocalTopNavModule(navigation: NavigationStart) { + angular + .module('discoverTopNav', ['react']) + .directive('kbnTopNav', createTopNavDirective) + .directive('kbnTopNavHelper', createTopNavHelper(navigation.ui)); +} + +function createLocalI18nModule() { + angular + .module('discoverI18n', []) + .provider('i18n', I18nProvider) + .filter('i18n', i18nFilter) + .directive('i18nId', i18nDirective); +} + +function createLocalAppStateModule() { + angular + .module('discoverAppState', [ + 'discoverGlobalState', + 'discoverPrivate', + 'discoverConfig', + 'discoverKbnUrl', + 'discoverPromise', + ]) + .service('AppState', function(Private: IPrivate) { + return Private(AppStateProvider); + }) + .service('getAppState', function(Private: any) { + return Private(AppStateProvider).getAppState; + }); +} + +function createLocalStorageModule() { + angular + .module('discoverLocalStorageProvider', ['discoverPrivate']) + .service('localStorage', createLocalStorageService('localStorage')) + .service('sessionStorage', createLocalStorageService('sessionStorage')); +} + +const createLocalStorageService = function(type: string) { + return function($window: any) { + return new Storage($window[type]); + }; +}; + +function createElasticSearchModule() { + angular + .module('discoverEs', ['elasticsearch', 'discoverConfig']) + // Elasticsearch client used for requesting data. Connects to the /elasticsearch proxy + .service('es', createEsService); +} + +function createIndexPatternsModule() { + angular.module('discoverIndexPatterns', []).service('indexPatterns', IndexPatterns); +} + +function createPagerFactoryModule() { + angular.module('discoverPagerFactory', []).factory('pagerFactory', createPagerFactory); +} + +function createDocTableModule() { + angular + .module('discoverDocTable', [ + 'discoverKbnUrl', + 'discoverConfig', + 'discoverAppState', + 'discoverPagerFactory', + 'react', + ]) + .directive('docTable', createDocTableDirective) + .directive('kbnTableHeader', createTableHeaderDirective) + .directive('toolBarPagerText', createToolBarPagerTextDirective) + .directive('toolBarPagerText', createToolBarPagerTextDirective) + .directive('kbnTableRow', createTableRowDirective) + .directive('toolBarPagerButtons', createToolBarPagerButtonsDirective) + .directive('kbnInfiniteScroll', createInfiniteScrollDirective) + .directive('docViewer', createDocViewerDirective); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/breadcrumbs.ts b/src/legacy/core_plugins/kibana/public/discover/helpers/breadcrumbs.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/breadcrumbs.ts rename to src/legacy/core_plugins/kibana/public/discover/helpers/breadcrumbs.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/helpers/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/helpers/build_services.ts new file mode 100644 index 0000000000000..33a099cce5fd8 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/helpers/build_services.ts @@ -0,0 +1,110 @@ +/* + * 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 { + Capabilities, + ChromeStart, + CoreStart, + DocLinksStart, + ToastsStart, + UiSettingsClientContract, +} from 'kibana/public'; +import * as docViewsRegistry from 'ui/registry/doc_views'; +import chromeLegacy from 'ui/chrome'; +import { IPrivate } from 'ui/private'; +import { FilterManager, TimefilterContract } from 'src/plugins/data/public'; +// @ts-ignore +import { StateProvider } from 'ui/state_management/state'; +// @ts-ignore +import { createSavedSearchesService } from '../saved_searches/saved_searches'; +// @ts-ignore +import { createSavedSearchFactory } from '../saved_searches/_saved_search'; +import { DiscoverStartPlugins } from '../plugin'; +import { start as legacyData } from '../../../../data/public/legacy'; +import { DataStart, IndexPatterns } from '../../../../data/public'; +import { EuiUtilsStart } from '../../../../../../plugins/eui_utils/public'; +import { SavedSearch } from '../types'; +import { SharePluginStart } from '../../../../../../plugins/share/public'; + +export interface DiscoverServices { + addBasePath: (path: string) => string; + capabilities: Capabilities; + chrome: ChromeStart; + core: CoreStart; + data: DataStart; + docLinks: DocLinksStart; + docViewsRegistry: docViewsRegistry.DocViewsRegistry; + eui_utils: EuiUtilsStart; + filterManager: FilterManager; + indexPatterns: IndexPatterns; + inspector: unknown; + metadata: { branch: string }; + share: SharePluginStart; + timefilter: TimefilterContract; + toastNotifications: ToastsStart; + // legacy + getSavedSearchById: (id: string) => Promise; + getSavedSearchUrlById: (id: string) => Promise; + State: unknown; + uiSettings: UiSettingsClientContract; +} + +export async function buildGlobalAngularServices() { + const injector = await chromeLegacy.dangerouslyGetActiveInjector(); + const Private = injector.get('Private'); + const kbnUrl = injector.get('kbnUrl'); + const State = Private(StateProvider); + const SavedSearchFactory = createSavedSearchFactory(Private); + const service = createSavedSearchesService(Private, SavedSearchFactory, kbnUrl, chromeLegacy); + return { + getSavedSearchById: async (id: string) => service.get(id), + getSavedSearchUrlById: async (id: string) => service.urlFor(id), + State, + }; +} + +export async function buildServices(core: CoreStart, plugins: DiscoverStartPlugins, test: false) { + const globalAngularServices = !test + ? await buildGlobalAngularServices() + : { + getSavedSearchById: async (id: string) => void id, + getSavedSearchUrlById: async (id: string) => void id, + State: null, + }; + + return { + ...globalAngularServices, + addBasePath: core.http.basePath.prepend, + capabilities: core.application.capabilities, + chrome: core.chrome, + core, + data: plugins.data, + docLinks: core.docLinks, + docViewsRegistry, + eui_utils: plugins.eui_utils, + filterManager: plugins.data.query.filterManager, + indexPatterns: legacyData.indexPatterns.indexPatterns, + inspector: plugins.inspector, + // @ts-ignore + metadata: core.injectedMetadata.getLegacyMetadata(), + share: plugins.share, + timefilter: plugins.data.query.timefilter.timefilter, + toastNotifications: core.notifications.toasts, + uiSettings: core.uiSettings, + }; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/helpers/get_index_pattern_id.ts b/src/legacy/core_plugins/kibana/public/discover/helpers/get_index_pattern_id.ts new file mode 100644 index 0000000000000..bd62460fd6868 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/helpers/get_index_pattern_id.ts @@ -0,0 +1,60 @@ +/* + * 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 { IIndexPattern } from '../../../../../../plugins/data/common/index_patterns'; + +export function findIndexPatternById( + indexPatterns: IIndexPattern[], + id: string +): IIndexPattern | undefined { + if (!Array.isArray(indexPatterns) || !id) { + return; + } + return indexPatterns.find(o => o.id === id); +} + +/** + * Checks if the given defaultIndex exists and returns + * the first available index pattern id if not + */ +export function getFallbackIndexPatternId( + indexPatterns: IIndexPattern[], + defaultIndex: string = '' +): string { + if (defaultIndex && findIndexPatternById(indexPatterns, defaultIndex)) { + return defaultIndex; + } + return !indexPatterns || !indexPatterns.length || !indexPatterns[0].id ? '' : indexPatterns[0].id; +} + +/** + * A given index pattern id is checked for existence and a fallback is provided if it doesn't exist + * The provided defaultIndex is usually configured in Advanced Settings, if it's also invalid + * the first entry of the given list of Indexpatterns is used + */ +export function getIndexPatternId( + id: string = '', + indexPatterns: IIndexPattern[], + defaultIndex: string = '' +): string { + if (!id || !findIndexPatternById(indexPatterns, id)) { + return getFallbackIndexPatternId(indexPatterns, defaultIndex); + } + return id; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/index.ts b/src/legacy/core_plugins/kibana/public/discover/index.ts index 35e48598f07a8..7f8ca4e96c5ac 100644 --- a/src/legacy/core_plugins/kibana/public/discover/index.ts +++ b/src/legacy/core_plugins/kibana/public/discover/index.ts @@ -18,7 +18,9 @@ */ import { PluginInitializer, PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; +import { SavedObjectRegistryProvider } from 'ui/saved_objects'; import { DiscoverPlugin, DiscoverSetup, DiscoverStart } from './plugin'; +import { start as navigation } from '../../../navigation/public/legacy'; // Core will be looking for this when loading our plugin in the new platform export const plugin: PluginInitializer = ( @@ -27,6 +29,13 @@ export const plugin: PluginInitializer = ( return new DiscoverPlugin(initializerContext); }; -const pluginInstance = plugin({} as PluginInitializerContext); -export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins); -export const start = pluginInstance.start(npStart.core, npStart.plugins); +// Legacy compatiblity part - to be removed at cutover, replaced by a kibana.json file +export const pluginInstance = plugin({} as PluginInitializerContext); +(async () => { + pluginInstance.setup(npSetup.core, npSetup.plugins); + pluginInstance.start(npStart.core, { ...npStart.plugins, navigation }); +})(); + +SavedObjectRegistryProvider.register((savedSearches: any) => { + return savedSearches; +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 822bf69e52e0b..393399f7da345 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -16,71 +16,40 @@ * specific language governing permissions and limitations * under the License. */ -import 'ui/collapsible_sidebar'; -import 'ui/directives/listen'; -import 'ui/directives/storage'; -import 'ui/fixed_scroll'; -import 'ui/directives/css_truncate'; - -import { npStart } from 'ui/new_platform'; -import chromeLegacy from 'ui/chrome'; import angular from 'angular'; // just used in embeddables and discover controller -import uiRoutes from 'ui/routes'; -// @ts-ignore -import { uiModules } from 'ui/modules'; -// @ts-ignore -import { StateProvider } from 'ui/state_management/state'; -// @ts-ignore -import { SavedObjectProvider } from 'ui/saved_objects/saved_object'; -import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; -import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; -// @ts-ignore -import { IndexPattern, IndexPatterns } from 'ui/index_patterns'; -import { wrapInI18nContext } from 'ui/i18n'; -// @ts-ignore -import { docTitle } from 'ui/doc_title'; -// @ts-ignore -import * as docViewsRegistry from 'ui/registry/doc_views'; -import { SearchSource } from '../../../../ui/public/courier'; +import { DiscoverServices } from './helpers/build_services'; -const services = { - // new plattform - core: npStart.core, - addBasePath: npStart.core.http.basePath.prepend, - capabilities: npStart.core.application.capabilities, - chrome: npStart.core.chrome, - docLinks: npStart.core.docLinks, - eui_utils: npStart.plugins.eui_utils, - inspector: npStart.plugins.inspector, - metadata: npStart.core.injectedMetadata.getLegacyMetadata(), - toastNotifications: npStart.core.notifications.toasts, - uiSettings: npStart.core.uiSettings, - uiActions: npStart.plugins.uiActions, - embeddable: npStart.plugins.embeddable, - npData: npStart.plugins.data, - share: npStart.plugins.share, - timefilter: npStart.plugins.data.query.timefilter.timefilter, - // legacy - docTitle, - docViewsRegistry, - FilterBarQueryFilterProvider, - getInjector: () => { - return chromeLegacy.dangerouslyGetActiveInjector(); - }, - SavedObjectRegistryProvider, - SavedObjectProvider, - SearchSource, - StateProvider, - uiModules, - uiRoutes, - wrapInI18nContext, -}; -export function getServices() { +let angularModule: any = null; +let services: DiscoverServices | null = null; + +/** + * set bootstrapped inner angular module + */ +export function setAngularModule(module: any) { + angularModule = module; +} + +/** + * get boostrapped inner angular module + */ +export function getAngularModule() { + return angularModule; +} + +export function getServices(): DiscoverServices { + if (!services) { + throw new Error('Discover services are not yet available'); + } return services; } -// EXPORT legacy static dependencies +export function setServices(newServices: any) { + services = newServices; +} + +// EXPORT legacy static dependencies, should be migrated when available in a new version; export { angular }; +export { wrapInI18nContext } from 'ui/i18n'; export { buildVislibDimensions } from '../../../visualizations/public'; // @ts-ignore export { callAfterBindingsWorkaround } from 'ui/compat'; diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts index 7c2fb4f118915..6679e8a3b8826 100644 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts @@ -16,12 +16,20 @@ * specific language governing permissions and limitations * under the License. */ - import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public'; +import angular from 'angular'; import { IUiActionsStart } from 'src/plugins/ui_actions/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; import { registerFeature } from './helpers/register_feature'; import './kibana_services'; import { IEmbeddableStart, IEmbeddableSetup } from '../../../../../plugins/embeddable/public'; +import { getInnerAngularModule, getInnerAngularModuleEmbeddable } from './get_inner_angular'; +import { setAngularModule, setServices } from './kibana_services'; +import { NavigationStart } from '../../../navigation/public'; +import { EuiUtilsStart } from '../../../../../plugins/eui_utils/public'; +import { buildServices } from './helpers/build_services'; +import { SharePluginStart } from '../../../../../plugins/share/public'; +import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; /** * These are the interfaces with your public contracts. You should export these @@ -30,28 +38,108 @@ import { IEmbeddableStart, IEmbeddableSetup } from '../../../../../plugins/embed */ export type DiscoverSetup = void; export type DiscoverStart = void; -interface DiscoverSetupPlugins { +export interface DiscoverSetupPlugins { uiActions: IUiActionsStart; embeddable: IEmbeddableSetup; + kibana_legacy: KibanaLegacySetup; } -interface DiscoverStartPlugins { +export interface DiscoverStartPlugins { uiActions: IUiActionsStart; embeddable: IEmbeddableStart; + navigation: NavigationStart; + eui_utils: EuiUtilsStart; + data: DataPublicPluginStart; + share: SharePluginStart; + inspector: any; } +const innerAngularName = 'app/discover'; +const embeddableAngularName = 'app/discoverEmbeddable'; +/** + * Contains Discover, one of the oldest parts of Kibana + * There are 2 kinds of Angular bootstrapped for rendering, additionally to the main Angular + * Discover provides embeddables, those contain a slimmer Angular + */ export class DiscoverPlugin implements Plugin { + private servicesInitialized: boolean = false; + private innerAngularInitialized: boolean = false; + /** + * why are those functions public? they are needed for some mocha tests + * can be removed once all is Jest + */ + public initializeInnerAngular?: () => void; + public initializeServices?: () => void; constructor(initializerContext: PluginInitializerContext) {} setup(core: CoreSetup, plugins: DiscoverSetupPlugins): DiscoverSetup { - registerFeature(); - require('./angular'); + plugins.kibana_legacy.registerLegacyApp({ + id: 'discover', + title: 'Discover', + order: -1004, + euiIconType: 'discoverApp', + mount: async (context, params) => { + if (!this.initializeServices) { + throw Error('Discover plugin method initializeServices is undefined'); + } + if (!this.initializeInnerAngular) { + throw Error('Discover plugin method initializeInnerAngular is undefined'); + } + await this.initializeServices(); + await this.initializeInnerAngular(); + const { renderApp } = await import('./application'); + return renderApp(innerAngularName, params.element); + }, + }); } start(core: CoreStart, plugins: DiscoverStartPlugins): DiscoverStart { - // TODO enable this when possible, seems it broke a functional test: - // dashboard mode Dashboard View Mode Dashboard viewer can paginate on a saved search - // const factory = new SearchEmbeddableFactory(plugins.uiActions.executeTriggerActions); - // plugins.embeddable.registerEmbeddableFactory(factory.type, factory); + // we need to register the application service at setup, but to render it + // there are some start dependencies necessary, for this reason + // initializeInnerAngular + initializeServices are assigned at start and used + // when the application/embeddable is mounted + this.initializeInnerAngular = async () => { + if (this.innerAngularInitialized) { + return; + } + // this is used by application mount and tests + const module = getInnerAngularModule(innerAngularName, core, plugins); + setAngularModule(module); + this.innerAngularInitialized = true; + }; + + this.initializeServices = async (test = false) => { + if (this.servicesInitialized) { + return; + } + const services = await buildServices(core, plugins, test); + setServices(services); + this.servicesInitialized = true; + }; + + this.registerEmbeddable(core, plugins); + registerFeature(); } - stop() {} + /** + * register embeddable with a slimmer embeddable version of inner angular + */ + private async registerEmbeddable(core: CoreStart, plugins: DiscoverStartPlugins) { + const { SearchEmbeddableFactory } = await import('./embeddable'); + const getInjector = async () => { + if (!this.initializeServices) { + throw Error('Discover plugin registerEmbeddable: initializeServices is undefined'); + } + await this.initializeServices(); + getInnerAngularModuleEmbeddable(embeddableAngularName, core, plugins); + const mountpoint = document.createElement('div'); + return angular.bootstrap(mountpoint, [embeddableAngularName]); + }; + const isEditable = () => core.application.capabilities.discover.save as boolean; + + const factory = new SearchEmbeddableFactory( + plugins.uiActions.executeTriggerActions, + getInjector, + isEditable + ); + plugins.embeddable.registerEmbeddableFactory(factory.type, factory); + } } diff --git a/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.js b/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.js index 9bbc5baf4fc22..db2b2b5b22af7 100644 --- a/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.js +++ b/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.js @@ -18,13 +18,13 @@ */ import { createLegacyClass } from 'ui/utils/legacy_class'; -import { getServices } from '../kibana_services'; +import { SavedObjectProvider } from 'ui/saved_objects/saved_object'; -const { uiModules, SavedObjectProvider } = getServices(); +import { uiModules } from 'ui/modules'; const module = uiModules.get('discover/saved_searches', []); -module.factory('SavedSearch', function (Private) { +export function createSavedSearchFactory(Private) { const SavedObject = Private(SavedObjectProvider); createLegacyClass(SavedSearch).inherits(SavedObject); function SavedSearch(id) { @@ -32,7 +32,6 @@ module.factory('SavedSearch', function (Private) { type: SavedSearch.type, mapping: SavedSearch.mapping, searchSource: SavedSearch.searchSource, - id: id, defaults: { title: '', @@ -68,4 +67,6 @@ module.factory('SavedSearch', function (Private) { }; return SavedSearch; -}); +} + +module.factory('SavedSearch', createSavedSearchFactory); diff --git a/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_search_register.js b/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_search_register.js deleted file mode 100644 index 9554642c225fd..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_search_register.js +++ /dev/null @@ -1,26 +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 { getServices } from '../kibana_services'; -import './saved_searches'; - - -getServices().SavedObjectRegistryProvider.register((savedSearches) => { - return savedSearches; -}); diff --git a/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.js b/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.js index d68b7f0e0d097..7ebcba903cc7f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.js +++ b/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.js @@ -18,32 +18,33 @@ */ import './_saved_search'; -import 'ui/notify'; import { uiModules } from 'ui/modules'; import { SavedObjectLoader, SavedObjectsClientProvider } from 'ui/saved_objects'; import { savedObjectManagementRegistry } from '../../management/saved_object_registry'; -const module = uiModules.get('discover/saved_searches'); + // Register this service with the saved object registry so it can be // edited by the object editor. savedObjectManagementRegistry.register({ service: 'savedSearches', - title: 'searches' + title: 'searches', }); -module.service('savedSearches', function (Private, SavedSearch, kbnUrl, chrome) { +export function createSavedSearchesService(Private, SavedSearch, kbnUrl, chrome) { const savedObjectClient = Private(SavedObjectsClientProvider); const savedSearchLoader = new SavedObjectLoader(SavedSearch, kbnUrl, chrome, savedObjectClient); // Customize loader properties since adding an 's' on type doesn't work for type 'search' . savedSearchLoader.loaderProperties = { name: 'searches', noun: 'Saved Search', - nouns: 'saved searches' + nouns: 'saved searches', }; - savedSearchLoader.urlFor = function (id) { + savedSearchLoader.urlFor = (id) => { return kbnUrl.eval('#/discover/{{id}}', { id: id }); }; return savedSearchLoader; -}); +} +const module = uiModules.get('discover/saved_searches'); +module.service('savedSearches', createSavedSearchesService); diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index 98def2252b75c..cc438d338c7d5 100644 --- a/src/legacy/core_plugins/kibana/public/kibana.js +++ b/src/legacy/core_plugins/kibana/public/kibana.js @@ -23,6 +23,7 @@ import chrome from 'ui/chrome'; import routes from 'ui/routes'; import { uiModules } from 'ui/modules'; +import { npSetup } from 'ui/new_platform'; // import the uiExports that we want to "use" import 'uiExports/home'; @@ -59,11 +60,13 @@ import { showAppRedirectNotification } from 'ui/notify'; import 'leaflet'; import { localApplicationService } from './local_application_service'; + +npSetup.plugins.kibana_legacy.forwardApp('doc', 'discover', { keepPrefix: true }); +npSetup.plugins.kibana_legacy.forwardApp('context', 'discover', { keepPrefix: true }); localApplicationService.attachToAngular(routes); routes.enable(); - routes .otherwise({ redirectTo: `/${chrome.getInjected('kbnDefaultAppId', 'discover')}` diff --git a/src/legacy/ui/public/accessibility/kbn_accessible_click.js b/src/legacy/ui/public/accessibility/kbn_accessible_click.js index 1abf322daa9a8..0b5016d882c41 100644 --- a/src/legacy/ui/public/accessibility/kbn_accessible_click.js +++ b/src/legacy/ui/public/accessibility/kbn_accessible_click.js @@ -43,52 +43,54 @@ import { } from '@elastic/eui'; import { uiModules } from '../modules'; -uiModules.get('kibana') - .directive('kbnAccessibleClick', function () { - return { - restrict: 'A', - controller: $element => { - $element.on('keydown', e => { +export function KbnAccessibleClickProvider() { + return { + restrict: 'A', + controller: $element => { + $element.on('keydown', e => { // Prevent a scroll from occurring if the user has hit space. - if (e.keyCode === keyCodes.SPACE) { - e.preventDefault(); - } - }); - }, - link: (scope, element, attrs) => { + if (e.keyCode === keyCodes.SPACE) { + e.preventDefault(); + } + }); + }, + link: (scope, element, attrs) => { // The whole point of this directive is to hack in functionality that native buttons provide // by default. - const elementType = element.prop('tagName'); + const elementType = element.prop('tagName'); - if (elementType === 'BUTTON') { - throw new Error(`kbnAccessibleClick doesn't need to be used on a button.`); - } + if (elementType === 'BUTTON') { + throw new Error(`kbnAccessibleClick doesn't need to be used on a button.`); + } - if (elementType === 'A' && attrs.href !== undefined) { - throw new Error(`kbnAccessibleClick doesn't need to be used on a link if it has a href attribute.`); - } + if (elementType === 'A' && attrs.href !== undefined) { + throw new Error(`kbnAccessibleClick doesn't need to be used on a link if it has a href attribute.`); + } - // We're emulating a click action, so we should already have a regular click handler defined. - if (!attrs.ngClick) { - throw new Error('kbnAccessibleClick requires ng-click to be defined on its element.'); - } + // We're emulating a click action, so we should already have a regular click handler defined. + if (!attrs.ngClick) { + throw new Error('kbnAccessibleClick requires ng-click to be defined on its element.'); + } - // If the developer hasn't already specified attributes required for accessibility, add them. - if (attrs.tabindex === undefined) { - element.attr('tabindex', '0'); - } + // If the developer hasn't already specified attributes required for accessibility, add them. + if (attrs.tabindex === undefined) { + element.attr('tabindex', '0'); + } - if (attrs.role === undefined) { - element.attr('role', 'button'); - } + if (attrs.role === undefined) { + element.attr('role', 'button'); + } - element.on('keyup', e => { + element.on('keyup', e => { // Support keyboard accessibility by emulating mouse click on ENTER or SPACE keypress. - if (accessibleClickKeys[e.keyCode]) { + if (accessibleClickKeys[e.keyCode]) { // Delegate to the click handler on the element (assumed to be ng-click). - element.click(); - } - }); - }, - }; - }); + element.click(); + } + }); + }, + }; +} + +uiModules.get('kibana') + .directive('kbnAccessibleClick', KbnAccessibleClickProvider); diff --git a/src/legacy/ui/public/collapsible_sidebar/collapsible_sidebar.js b/src/legacy/ui/public/collapsible_sidebar/collapsible_sidebar.js index 1a70e5b820003..3de138b7cd93d 100644 --- a/src/legacy/ui/public/collapsible_sidebar/collapsible_sidebar.js +++ b/src/legacy/ui/public/collapsible_sidebar/collapsible_sidebar.js @@ -21,65 +21,66 @@ import _ from 'lodash'; import $ from 'jquery'; import { uiModules } from '../modules'; - -uiModules - .get('kibana') - .directive('collapsibleSidebar', function () { +export function CollapsibleSidebarProvider() { // simply a list of all of all of angulars .col-md-* classes except 12 - const listOfWidthClasses = _.times(11, function (i) { return 'col-md-' + i; }); + const listOfWidthClasses = _.times(11, function (i) { + return 'col-md-' + i; + }); - return { - restrict: 'C', - link: function ($scope, $elem) { - let isCollapsed = false; - const $collapser = $( - `` - ); - // If the collapsable element has an id, also set aria-controls - if ($elem.attr('id')) { - $collapser.attr('aria-controls', $elem.attr('id')); - } - const $icon = $(''); - $collapser.append($icon); - const $siblings = $elem.siblings(); + ); + // If the collapsable element has an id, also set aria-controls + if ($elem.attr('id')) { + $collapser.attr('aria-controls', $elem.attr('id')); + } + const $icon = $(''); + $collapser.append($icon); + const $siblings = $elem.siblings(); - const siblingsClass = listOfWidthClasses.reduce(function (prev, className) { - if (prev) return prev; - return $siblings.hasClass(className) && className; - }, false); + const siblingsClass = listOfWidthClasses.reduce(function (prev, className) { + if (prev) return prev; + return $siblings.hasClass(className) && className; + }, false); - // If there is are only two elements we can assume the other one will take 100% of the width. - const hasSingleSibling = $siblings.length === 1 && siblingsClass; + // If there is are only two elements we can assume the other one will take 100% of the width. + const hasSingleSibling = $siblings.length === 1 && siblingsClass; - $collapser.on('click', function () { - if (isCollapsed) { - isCollapsed = false; - $elem.removeClass('closed'); - $icon.addClass('fa-chevron-circle-left'); - $icon.removeClass('fa-chevron-circle-right'); - $collapser.attr('aria-expanded', 'true'); - } else { - isCollapsed = true; - $elem.addClass('closed'); - $icon.removeClass('fa-chevron-circle-left'); - $icon.addClass('fa-chevron-circle-right'); - $collapser.attr('aria-expanded', 'false'); - } + $collapser.on('click', function () { + if (isCollapsed) { + isCollapsed = false; + $elem.removeClass('closed'); + $icon.addClass('fa-chevron-circle-left'); + $icon.removeClass('fa-chevron-circle-right'); + $collapser.attr('aria-expanded', 'true'); + } else { + isCollapsed = true; + $elem.addClass('closed'); + $icon.removeClass('fa-chevron-circle-left'); + $icon.addClass('fa-chevron-circle-right'); + $collapser.attr('aria-expanded', 'false'); + } - if (hasSingleSibling) { - $siblings.toggleClass(siblingsClass + ' col-md-12'); - } + if (hasSingleSibling) { + $siblings.toggleClass(siblingsClass + ' col-md-12'); + } - if ($scope.toggleSidebar) $scope.toggleSidebar(); - }); + if ($scope.toggleSidebar) $scope.toggleSidebar(); + }); - $collapser.appendTo($elem); - } - }; - }); + $collapser.appendTo($elem); + }, + }; +} + +uiModules.get('kibana').directive('collapsibleSidebar', CollapsibleSidebarProvider); diff --git a/src/legacy/ui/public/directives/css_truncate.js b/src/legacy/ui/public/directives/css_truncate.js index 7105563e0ce5f..ac8e29258fddb 100644 --- a/src/legacy/ui/public/directives/css_truncate.js +++ b/src/legacy/ui/public/directives/css_truncate.js @@ -20,12 +20,11 @@ import { uiModules } from '../modules'; const module = uiModules.get('kibana'); -module.directive('cssTruncate', function () { +export function CssTruncateProvide() { return { restrict: 'A', scope: {}, link: function ($scope, $elem, attrs) { - $elem.css({ overflow: 'hidden', 'white-space': 'nowrap', @@ -35,10 +34,12 @@ module.directive('cssTruncate', function () { if (attrs.cssTruncateExpandable != null) { $scope.$watch( - function () { return $elem.html(); }, + function () { + return $elem.html(); + }, function () { if ($elem[0].offsetWidth < $elem[0].scrollWidth) { - $elem.css({ 'cursor': 'pointer' }); + $elem.css({ cursor: 'pointer' }); $elem.bind('click', function () { $scope.toggle(); }); @@ -59,6 +60,8 @@ module.directive('cssTruncate', function () { $elem.unbind('click'); $elem.unbind('mouseenter'); }); - } + }, }; -}); +} + +module.directive('cssTruncate', CssTruncateProvide); diff --git a/src/legacy/ui/public/directives/debounce/debounce.js b/src/legacy/ui/public/directives/debounce/debounce.js index 3b384d7088743..06c82d4356a4a 100644 --- a/src/legacy/ui/public/directives/debounce/debounce.js +++ b/src/legacy/ui/public/directives/debounce/debounce.js @@ -24,7 +24,7 @@ import { uiModules } from '../../modules'; const module = uiModules.get('kibana'); -module.service('debounce', ['$timeout', function ($timeout) { +export function DebounceProviderTimeout($timeout) { return function (func, wait, options) { let timeout; let args; @@ -68,7 +68,9 @@ module.service('debounce', ['$timeout', function ($timeout) { return debounce; }; -}]); +} + +module.service('debounce', ['$timeout', DebounceProviderTimeout]); export function DebounceProvider(debounce) { return debounce; diff --git a/src/legacy/ui/public/directives/field_name.js b/src/legacy/ui/public/directives/field_name.js index b159b71238186..aff849fc5602f 100644 --- a/src/legacy/ui/public/directives/field_name.js +++ b/src/legacy/ui/public/directives/field_name.js @@ -21,7 +21,7 @@ import { uiModules } from '../modules'; import { wrapInI18nContext } from 'ui/i18n'; const module = uiModules.get('kibana'); -module.directive('fieldName', function (config, reactDirective) { +export function FieldNameDirectiveProvider(config, reactDirective) { return reactDirective( wrapInI18nContext(FieldName), [ @@ -34,4 +34,6 @@ module.directive('fieldName', function (config, reactDirective) { useShortDots: config.get('shortDots:enable'), } ); -}); +} + +module.directive('fieldName', FieldNameDirectiveProvider); diff --git a/src/legacy/ui/public/directives/listen/listen.js b/src/legacy/ui/public/directives/listen/listen.js index b877e8ee6b08e..fddc85a4f4914 100644 --- a/src/legacy/ui/public/directives/listen/listen.js +++ b/src/legacy/ui/public/directives/listen/listen.js @@ -19,22 +19,22 @@ import { uiModules } from '../../modules'; -uiModules.get('kibana') - .run(function ($rootScope) { +export function registerListenEventListener($rootScope) { + /** + * Helper that registers an event listener, and removes that listener when + * the $scope is destroyed. + * + * @param {SimpleEmitter} emitter - the event emitter to listen to + * @param {string} eventName - the event name + * @param {Function} handler - the event handler + * @return {undefined} + */ + $rootScope.constructor.prototype.$listen = function (emitter, eventName, handler) { + emitter.on(eventName, handler); + this.$on('$destroy', function () { + emitter.off(eventName, handler); + }); + }; +} - /** - * Helper that registers an event listener, and removes that listener when - * the $scope is destroyed. - * - * @param {SimpleEmitter} emitter - the event emitter to listen to - * @param {string} eventName - the event name - * @param {Function} handler - the event handler - * @return {undefined} - */ - $rootScope.constructor.prototype.$listen = function (emitter, eventName, handler) { - emitter.on(eventName, handler); - this.$on('$destroy', function () { - emitter.off(eventName, handler); - }); - }; - }); +uiModules.get('kibana').run(registerListenEventListener); diff --git a/src/legacy/ui/public/directives/watch_multi/watch_multi.js b/src/legacy/ui/public/directives/watch_multi/watch_multi.js index add95e8146f26..d1b43f74f56ab 100644 --- a/src/legacy/ui/public/directives/watch_multi/watch_multi.js +++ b/src/legacy/ui/public/directives/watch_multi/watch_multi.js @@ -21,10 +21,8 @@ import _ from 'lodash'; import { uiModules } from '../../modules'; import { callEach } from '../../utils/function'; -uiModules.get('kibana') - .config(function ($provide) { - - $provide.decorator('$rootScope', function ($delegate) { +export function watchMultiDecorator($provide) { + $provide.decorator('$rootScope', function ($delegate) { /** * Watch multiple expressions with a single callback. Along * with making code simpler it also merges all of the watcher @@ -53,23 +51,30 @@ uiModules.get('kibana') * @param {Function} fn - the callback function * @return {Function} - an unwatch function, just like the return value of $watch */ - $delegate.constructor.prototype.$watchMulti = function (expressions, fn) { - if (!Array.isArray(expressions)) throw new TypeError('expected an array of expressions to watch'); - if (!_.isFunction(fn)) throw new TypeError('expected a function that is triggered on each watch'); - - const $scope = this; - const vals = new Array(expressions.length); - const prev = new Array(expressions.length); - let fire = false; - let init = 0; - const neededInits = expressions.length; - - // first, register all of the multi-watchers - const unwatchers = expressions.map(function (expr, i) { - expr = normalizeExpression($scope, expr); - if (!expr) return; - - return expr.fn.call($scope, expr.get, function (newVal, oldVal) { + $delegate.constructor.prototype.$watchMulti = function (expressions, fn) { + if (!Array.isArray(expressions)) { + throw new TypeError('expected an array of expressions to watch'); + } + + if (!_.isFunction(fn)) { + throw new TypeError('expected a function that is triggered on each watch'); + } + const $scope = this; + const vals = new Array(expressions.length); + const prev = new Array(expressions.length); + let fire = false; + let init = 0; + const neededInits = expressions.length; + + // first, register all of the multi-watchers + const unwatchers = expressions.map(function (expr, i) { + expr = normalizeExpression($scope, expr); + if (!expr) return; + + return expr.fn.call( + $scope, + expr.get, + function (newVal, oldVal) { if (newVal === oldVal) { init += 1; } @@ -77,60 +82,69 @@ uiModules.get('kibana') vals[i] = newVal; prev[i] = oldVal; fire = true; - }, expr.deep); - }); - - // then, the watcher that checks to see if any of - // the other watchers triggered this cycle - let flip = false; - unwatchers.push($scope.$watch(function () { - if (init < neededInits) return init; - - if (fire) { - fire = false; - flip = !flip; + }, + expr.deep + ); + }); + + // then, the watcher that checks to see if any of + // the other watchers triggered this cycle + let flip = false; + unwatchers.push( + $scope.$watch( + function () { + if (init < neededInits) return init; + + if (fire) { + fire = false; + flip = !flip; + } + return flip; + }, + function () { + if (init < neededInits) return false; + + fn(vals.slice(0), prev.slice(0)); + vals.forEach(function (v, i) { + prev[i] = v; + }); } - return flip; - }, function () { - if (init < neededInits) return false; + ) + ); - fn(vals.slice(0), prev.slice(0)); - vals.forEach(function (v, i) { - prev[i] = v; - }); - })); + return _.partial(callEach, unwatchers); + }; - return _.partial(callEach, unwatchers); + function normalizeExpression($scope, expr) { + if (!expr) return; + const norm = { + fn: $scope.$watch, + deep: false, }; - function normalizeExpression($scope, expr) { - if (!expr) return; - const norm = { - fn: $scope.$watch, - deep: false - }; - - if (_.isFunction(expr)) return _.assign(norm, { get: expr }); - if (_.isObject(expr)) return _.assign(norm, expr); - if (!_.isString(expr)) return; - - if (expr.substr(0, 2) === '[]') { - return _.assign(norm, { - fn: $scope.$watchCollection, - get: expr.substr(2) - }); - } - - if (expr.charAt(0) === '=') { - return _.assign(norm, { - deep: true, - get: expr.substr(1) - }); - } - - return _.assign(norm, { get: expr }); + if (_.isFunction(expr)) return _.assign(norm, { get: expr }); + if (_.isObject(expr)) return _.assign(norm, expr); + if (!_.isString(expr)) return; + + if (expr.substr(0, 2) === '[]') { + return _.assign(norm, { + fn: $scope.$watchCollection, + get: expr.substr(2), + }); } - return $delegate; - }); + if (expr.charAt(0) === '=') { + return _.assign(norm, { + deep: true, + get: expr.substr(1), + }); + } + + return _.assign(norm, { get: expr }); + } + + return $delegate; }); +} + +uiModules.get('kibana').config(watchMultiDecorator); diff --git a/src/legacy/ui/public/es.js b/src/legacy/ui/public/es.js index d1204f8316f0d..601beba832c33 100644 --- a/src/legacy/ui/public/es.js +++ b/src/legacy/ui/public/es.js @@ -44,16 +44,17 @@ const plugins = [function (Client, config) { config.connectionClass = CustomAngularConnector; }]; +export function createEsService(esFactory, esUrl, esApiVersion, esRequestTimeout) { + return esFactory({ + host: esUrl, + log: 'info', + requestTimeout: esRequestTimeout, + apiVersion: esApiVersion, + plugins + }); +} + uiModules .get('kibana', ['elasticsearch', 'kibana/config']) - //Elasticsearch client used for requesting data. Connects to the /elasticsearch proxy - .service('es', function (esFactory, esUrl, esApiVersion, esRequestTimeout) { - return esFactory({ - host: esUrl, - log: 'info', - requestTimeout: esRequestTimeout, - apiVersion: esApiVersion, - plugins - }); - }); + .service('es', createEsService); diff --git a/src/legacy/ui/public/fixed_scroll.js b/src/legacy/ui/public/fixed_scroll.js index fcdde7bbdeb32..64c0ae5850bc9 100644 --- a/src/legacy/ui/public/fixed_scroll.js +++ b/src/legacy/ui/public/fixed_scroll.js @@ -24,30 +24,22 @@ import { DebounceProvider } from 'ui/directives/debounce'; const SCROLLER_HEIGHT = 20; -/** - * This directive adds a fixed horizontal scrollbar to the bottom of the window that proxies its scroll events - * to the target element's real scrollbar. This is useful when the target element's horizontal scrollbar - * might be waaaay down the page, like the doc table on Discover. - */ -uiModules - .get('kibana') - .directive('fixedScroll', function (Private) { - const debounce = Private(DebounceProvider); - - return { - restrict: 'A', - link: function ($scope, $el) { - let $window = $(window); - let $scroller = $('
').height(SCROLLER_HEIGHT); +export function FixedScrollProvider(Private) { + const debounce = Private(DebounceProvider); + return { + restrict: 'A', + link: function ($scope, $el) { + let $window = $(window); + let $scroller = $('
').height(SCROLLER_HEIGHT); - /** + /** * Remove the listeners bound in listen() * @type {function} */ - let unlisten = _.noop; + let unlisten = _.noop; - /** + /** * Listen for scroll events on the $scroller and the $el, sets unlisten() * * unlisten must be called before calling or listen() will throw an Error @@ -58,98 +50,109 @@ uiModules * @throws {Error} If unlisten was not called first * @return {undefined} */ - function listen() { - if (unlisten !== _.noop) { - throw new Error('fixedScroll listeners were not cleaned up properly before re-listening!'); - } + function listen() { + if (unlisten !== _.noop) { + throw new Error( + 'fixedScroll listeners were not cleaned up properly before re-listening!' + ); + } - let blockTo; - function bind($from, $to) { - function handler() { - if (blockTo === $to) return (blockTo = null); - $to.scrollLeft((blockTo = $from).scrollLeft()); - } - - $from.on('scroll', handler); - return function () { - $from.off('scroll', handler); - }; + let blockTo; + function bind($from, $to) { + function handler() { + if (blockTo === $to) return (blockTo = null); + $to.scrollLeft((blockTo = $from).scrollLeft()); } - unlisten = _.flow( - bind($el, $scroller), - bind($scroller, $el), - function () { unlisten = _.noop; } - ); + $from.on('scroll', handler); + return function () { + $from.off('scroll', handler); + }; } - /** + unlisten = _.flow( + bind($el, $scroller), + bind($scroller, $el), + function () { + unlisten = _.noop; + } + ); + } + + /** * Revert DOM changes and event listeners * @return {undefined} */ - function cleanUp() { - unlisten(); - $scroller.detach(); - $el.css('padding-bottom', 0); - } + function cleanUp() { + unlisten(); + $scroller.detach(); + $el.css('padding-bottom', 0); + } - /** + /** * Modify the DOM and attach event listeners based on need. * Is called many times to re-setup, must be idempotent * @return {undefined} */ - function setup() { - cleanUp(); + function setup() { + cleanUp(); - const containerWidth = $el.width(); - const contentWidth = $el.prop('scrollWidth'); - const containerHorizOverflow = contentWidth - containerWidth; + const containerWidth = $el.width(); + const contentWidth = $el.prop('scrollWidth'); + const containerHorizOverflow = contentWidth - containerWidth; - const elTop = $el.offset().top - $window.scrollTop(); - const elBottom = elTop + $el.height(); - const windowVertOverflow = elBottom - $window.height(); + const elTop = $el.offset().top - $window.scrollTop(); + const elBottom = elTop + $el.height(); + const windowVertOverflow = elBottom - $window.height(); - const requireScroller = containerHorizOverflow > 0 && windowVertOverflow > 0; - if (!requireScroller) return; + const requireScroller = containerHorizOverflow > 0 && windowVertOverflow > 0; + if (!requireScroller) return; - // push the content away from the scroller - $el.css('padding-bottom', SCROLLER_HEIGHT); + // push the content away from the scroller + $el.css('padding-bottom', SCROLLER_HEIGHT); - // fill the scroller with a dummy element that mimics the content - $scroller - .width(containerWidth) - .html($('
').css({ width: contentWidth, height: SCROLLER_HEIGHT })) - .insertAfter($el); + // fill the scroller with a dummy element that mimics the content + $scroller + .width(containerWidth) + .html($('
').css({ width: contentWidth, height: SCROLLER_HEIGHT })) + .insertAfter($el); - // listen for scroll events - listen(); - } + // listen for scroll events + listen(); + } - let width; - let scrollWidth; - function checkWidth() { - const newScrollWidth = $el.prop('scrollWidth'); - const newWidth = $el.width(); + let width; + let scrollWidth; + function checkWidth() { + const newScrollWidth = $el.prop('scrollWidth'); + const newWidth = $el.width(); - if (scrollWidth !== newScrollWidth || width !== newWidth) { - $scope.$apply(setup); + if (scrollWidth !== newScrollWidth || width !== newWidth) { + $scope.$apply(setup); - scrollWidth = newScrollWidth; - width = newWidth; - } + scrollWidth = newScrollWidth; + width = newWidth; } - - const debouncedCheckWidth = debounce(checkWidth, 100, { - invokeApply: false, - }); - $scope.$watch(debouncedCheckWidth); - - // cleanup when the scope is destroyed - $scope.$on('$destroy', function () { - cleanUp(); - debouncedCheckWidth.cancel(); - $scroller = $window = null; - }); } - }; - }); + + const debouncedCheckWidth = debounce(checkWidth, 100, { + invokeApply: false, + }); + $scope.$watch(debouncedCheckWidth); + + // cleanup when the scope is destroyed + $scope.$on('$destroy', function () { + cleanUp(); + debouncedCheckWidth.cancel(); + $scroller = $window = null; + }); + }, + }; +} + +/** + * This directive adds a fixed horizontal scrollbar to the bottom of the window that proxies its scroll events + * to the target element's real scrollbar. This is useful when the target element's horizontal scrollbar + * might be waaaay down the page, like the doc table on Discover. + */ +uiModules.get('kibana').directive('fixedScroll', FixedScrollProvider); diff --git a/src/legacy/ui/public/registry/doc_views.ts b/src/legacy/ui/public/registry/doc_views.ts index 097808c5dcfcc..bf1e8416ae66d 100644 --- a/src/legacy/ui/public/registry/doc_views.ts +++ b/src/legacy/ui/public/registry/doc_views.ts @@ -21,6 +21,12 @@ import { DocView, DocViewInput, ElasticSearchHit, DocViewInputFn } from './doc_v export { DocViewRenderProps, DocView, DocViewRenderFn } from './doc_views_types'; +export interface DocViewsRegistry { + docViews: DocView[]; + addDocView: (docView: DocViewInput) => void; + getDocViewsSorted: (hit: ElasticSearchHit) => DocView[]; +} + export const docViews: DocView[] = []; /** diff --git a/src/legacy/ui/public/registry/doc_views_helpers.tsx b/src/legacy/ui/public/registry/doc_views_helpers.tsx index 1ff00713b10ef..d9e42e71dfff1 100644 --- a/src/legacy/ui/public/registry/doc_views_helpers.tsx +++ b/src/legacy/ui/public/registry/doc_views_helpers.tsx @@ -26,7 +26,7 @@ import { AngularController, AngularDirective, } from './doc_views_types'; -import { DocViewerError } from '../../../core_plugins/kibana/public/discover/doc_viewer/doc_viewer_render_error'; +import { DocViewerError } from '../../../core_plugins/kibana/public/discover/components/doc_viewer/doc_viewer_render_error'; /** * Compiles and injects the give angular template into the given dom node diff --git a/src/legacy/ui/public/render_complete/directive.js b/src/legacy/ui/public/render_complete/directive.js index 6bde2293898b6..0e37ec964d3f0 100644 --- a/src/legacy/ui/public/render_complete/directive.js +++ b/src/legacy/ui/public/render_complete/directive.js @@ -20,13 +20,14 @@ import { uiModules } from '../modules'; import { RenderCompleteHelper } from '../../../../plugins/kibana_utils/public'; -uiModules - .get('kibana') - .directive('renderComplete', () => ({ +export function createRenderCompleteDirective() { + return { controller($scope, $element) { const el = $element[0]; const renderCompleteHelper = new RenderCompleteHelper(el); - $scope.$on('$destroy', renderCompleteHelper.destroy); } - })); + }; +} + +uiModules.get('kibana').directive('renderComplete', createRenderCompleteDirective); diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js index 5c1669b716eca..291d9feea3c40 100644 --- a/src/legacy/ui/ui_exports/ui_export_defaults.js +++ b/src/legacy/ui/ui_exports/ui_export_defaults.js @@ -55,7 +55,6 @@ export const UI_EXPORT_DEFAULTS = { ], embeddableFactories: [ 'plugins/kibana/visualize/embeddable/visualize_embeddable_factory', - 'plugins/kibana/discover/embeddable/search_embeddable_factory', ], search: [ 'ui/courier/search_strategy/default_search_strategy', diff --git a/test/visual_regression/tests/discover/chart_visualization.js b/test/visual_regression/tests/discover/chart_visualization.js index 540d95973b547..c90f29c66acb8 100644 --- a/test/visual_regression/tests/discover/chart_visualization.js +++ b/test/visual_regression/tests/discover/chart_visualization.js @@ -27,6 +27,7 @@ export default function ({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); const visualTesting = getService('visualTesting'); + const find = getService('find'); const defaultSettings = { defaultIndex: 'logstash-*', 'discover:sampleSize': 1 @@ -48,10 +49,12 @@ export default function ({ getService, getPageObjects }) { describe('query', function () { this.tags(['skipFirefox']); + let renderCounter = 0; it('should show bars in the correct time zone', async function () { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -61,6 +64,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.discover.setChartInterval('Hourly'); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -70,6 +74,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.discover.setChartInterval('Daily'); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -79,6 +84,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.discover.setChartInterval('Weekly'); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -92,6 +98,7 @@ export default function ({ getService, getPageObjects }) { }); await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -101,6 +108,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.discover.setChartInterval('Monthly'); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -110,6 +118,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.discover.setChartInterval('Yearly'); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); @@ -119,6 +128,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.discover.setChartInterval('Auto'); + await find.byCssSelector(`.echChart[data-ech-render-count="${++renderCounter}"]`); await visualTesting.snapshot({ show: ['discoverChart'], }); diff --git a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index 3a872b4c1e327..ebb57d34c01a1 100644 --- a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -19,10 +19,9 @@ import { IEmbeddable, CONTEXT_MENU_TRIGGER, } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { - ISearchEmbeddable, - SEARCH_EMBEDDABLE_TYPE, -} from '../../../../../../src/legacy/core_plugins/kibana/public/discover/embeddable'; +import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/embeddable/constants'; +import { ISearchEmbeddable } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/embeddable/types'; + import { API_BASE_URL_V1 } from '../../common/constants'; const API_BASE_URL = `${API_BASE_URL_V1}/generate/immediate/csv/saved-object`;