diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_app.js b/src/core_plugins/kibana/public/dashboard/dashboard_app.js
index ce6ac9e7d48e47..5926e2ed133619 100644
--- a/src/core_plugins/kibana/public/dashboard/dashboard_app.js
+++ b/src/core_plugins/kibana/public/dashboard/dashboard_app.js
@@ -372,10 +372,7 @@ app.directive('dashboardApp', function ($injector) {
$scope.$apply();
};
- const isLabsEnabled = config.get('visualize:enableLabs');
- const listingLimit = config.get('savedObjects:listingLimit');
-
- showAddPanel(chrome.getSavedObjectsClient(), dashboardStateManager.addNewPanel, addNewVis, listingLimit, isLabsEnabled, visTypes);
+ showAddPanel(dashboardStateManager.addNewPanel, addNewVis, visTypes);
};
navActions[TopNavIds.OPTIONS] = (menuItem, navController, anchorElement) => {
showOptionsPopover({
diff --git a/src/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap b/src/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap
index 45e90bc21fa3ea..85684aecc461fb 100644
--- a/src/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap
+++ b/src/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap
@@ -11,28 +11,13 @@ exports[`render 1`] = `
size="s"
>
-
-
-
-
- Add Panels
-
-
-
-
+
+ Add Panels
+
+
}
- find={[Function]}
key="visSavedObjectFinder"
noItemsMessage="No matching visualizations found."
onChoose={[Function]}
savedObjectType="visualization"
+ visTypes={Object {}}
/>
diff --git a/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.js
index 3346a41a5dbd95..cee97c4f07dad8 100644
--- a/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.js
+++ b/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.js
@@ -23,8 +23,6 @@ import { toastNotifications } from 'ui/notify';
import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
import {
- EuiFlexGroup,
- EuiFlexItem,
EuiFlyout,
EuiFlyoutBody,
EuiButton,
@@ -60,7 +58,7 @@ export class DashboardAddPanel extends React.Component {
key="visSavedObjectFinder"
callToActionButton={addNewVisBtn}
onChoose={this.onAddPanel}
- find={this.props.find}
+ visTypes={this.props.visTypes}
noItemsMessage="No matching visualizations found."
savedObjectType="visualization"
/>
@@ -74,7 +72,6 @@ export class DashboardAddPanel extends React.Component {
@@ -133,13 +130,9 @@ export class DashboardAddPanel extends React.Component {
>
-
-
-
- Add Panels
-
-
-
+
+ Add Panels
+
{this.renderTabs()}
@@ -157,7 +150,7 @@ export class DashboardAddPanel extends React.Component {
DashboardAddPanel.propTypes = {
onClose: PropTypes.func.isRequired,
- find: PropTypes.func.isRequired,
+ visTypes: PropTypes.object.isRequired,
addNewPanel: PropTypes.func.isRequired,
addNewVis: PropTypes.func.isRequired,
};
diff --git a/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js b/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js
index 6af26997acc8be..9c17980f7b9cda 100644
--- a/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js
+++ b/src/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js
@@ -40,7 +40,7 @@ beforeEach(() => {
test('render', () => {
const component = shallow( {}}
+ visTypes={{}}
addNewPanel={() => {}}
addNewVis={() => {}}
/>);
diff --git a/src/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js b/src/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js
index 0f7bf3b72a3a38..00719a850aca42 100644
--- a/src/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js
+++ b/src/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js
@@ -23,7 +23,7 @@ import ReactDOM from 'react-dom';
let isOpen = false;
-export function showAddPanel(savedObjectsClient, addNewPanel, addNewVis, listingLimit, isLabsEnabled, visTypes) {
+export function showAddPanel(addNewPanel, addNewVis, visTypes) {
if (isOpen) {
return;
}
@@ -35,26 +35,6 @@ export function showAddPanel(savedObjectsClient, addNewPanel, addNewVis, listing
document.body.removeChild(container);
isOpen = false;
};
- const find = async (type, search) => {
- const resp = await savedObjectsClient.find({
- type: type,
- fields: ['title', 'visState'],
- search: search ? `${search}*` : undefined,
- page: 1,
- perPage: listingLimit,
- searchFields: ['title^3', 'description']
- });
-
- if (type === 'visualization' && !isLabsEnabled) {
- resp.savedObjects = resp.savedObjects.filter(savedObject => {
- const typeName = JSON.parse(savedObject.attributes.visState).type;
- const visType = visTypes.byName[typeName];
- return visType.stage !== 'lab';
- });
- }
-
- return resp;
- };
const addNewVisWithCleanup = () => {
onClose();
@@ -65,7 +45,7 @@ export function showAddPanel(savedObjectsClient, addNewPanel, addNewVis, listing
const element = (
diff --git a/src/core_plugins/kibana/public/discover/controllers/discover.js b/src/core_plugins/kibana/public/discover/controllers/discover.js
index f62ea2794d3861..3085856b570734 100644
--- a/src/core_plugins/kibana/public/discover/controllers/discover.js
+++ b/src/core_plugins/kibana/public/discover/controllers/discover.js
@@ -58,6 +58,7 @@ import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing';
import { Inspector } from 'ui/inspector';
import { RequestAdapter } from 'ui/inspector/adapters';
import { getRequestInspectorStats, getResponseInspectorStats } from 'ui/courier/utils/courier_inspector_utils';
+import { showOpenSearchPanel } from '../top_nav/show_open_search_panel';
import { tabifyAggResponse } from 'ui/agg_response/tabify';
const app = uiModules.get('apps/discover', [
@@ -198,8 +199,14 @@ function discoverController(
}, {
key: 'open',
description: 'Open Saved Search',
- template: require('plugins/kibana/discover/partials/load_search.html'),
testId: 'discoverOpenButton',
+ run: () => {
+ showOpenSearchPanel({
+ makeUrl: (searchId) => {
+ return kbnUrl.eval('#/discover/{{id}}', { id: searchId });
+ }
+ });
+ }
}, {
key: 'share',
description: 'Share Search',
diff --git a/src/core_plugins/kibana/public/discover/partials/load_search.html b/src/core_plugins/kibana/public/discover/partials/load_search.html
deleted file mode 100644
index 4b944def8dcae2..00000000000000
--- a/src/core_plugins/kibana/public/discover/partials/load_search.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/core_plugins/kibana/public/discover/top_nav/__snapshots__/open_search_panel.test.js.snap b/src/core_plugins/kibana/public/discover/top_nav/__snapshots__/open_search_panel.test.js.snap
new file mode 100644
index 00000000000000..e35d7fc76868e8
--- /dev/null
+++ b/src/core_plugins/kibana/public/discover/top_nav/__snapshots__/open_search_panel.test.js.snap
@@ -0,0 +1,44 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`render 1`] = `
+
+
+
+
+ Open Search
+
+
+
+
+ Manage searches
+
+ }
+ makeUrl={[Function]}
+ noItemsMessage="No matching searches found."
+ onChoose={[Function]}
+ savedObjectType="search"
+ />
+
+
+`;
diff --git a/src/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/core_plugins/kibana/public/discover/top_nav/open_search_panel.js
new file mode 100644
index 00000000000000..f06dcf5d9c3d65
--- /dev/null
+++ b/src/core_plugins/kibana/public/discover/top_nav/open_search_panel.js
@@ -0,0 +1,80 @@
+/*
+ * 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 React from 'react';
+import PropTypes from 'prop-types';
+import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
+import rison from 'rison-node';
+
+import {
+ EuiSpacer,
+ EuiFlyout,
+ EuiFlyoutBody,
+ EuiTitle,
+ EuiButton,
+} from '@elastic/eui';
+
+const SEARCH_OBJECT_TYPE = 'search';
+
+export class OpenSearchPanel extends React.Component {
+
+ renderMangageSearchesButton() {
+ return (
+
+ Manage searches
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
+
+ Open Search
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+OpenSearchPanel.propTypes = {
+ onClose: PropTypes.func.isRequired,
+ makeUrl: PropTypes.func.isRequired,
+};
diff --git a/src/core_plugins/kibana/public/discover/top_nav/open_search_panel.test.js b/src/core_plugins/kibana/public/discover/top_nav/open_search_panel.test.js
new file mode 100644
index 00000000000000..2bec532110379e
--- /dev/null
+++ b/src/core_plugins/kibana/public/discover/top_nav/open_search_panel.test.js
@@ -0,0 +1,33 @@
+/*
+ * 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 React from 'react';
+import { shallow } from 'enzyme';
+
+import {
+ OpenSearchPanel,
+} from './open_search_panel';
+
+test('render', () => {
+ const component = shallow( {}}
+ makeUrl={() => {}}
+ />);
+ expect(component).toMatchSnapshot();
+});
diff --git a/src/core_plugins/kibana/public/discover/top_nav/show_open_search_panel.js b/src/core_plugins/kibana/public/discover/top_nav/show_open_search_panel.js
new file mode 100644
index 00000000000000..5b76f9ebc66527
--- /dev/null
+++ b/src/core_plugins/kibana/public/discover/top_nav/show_open_search_panel.js
@@ -0,0 +1,47 @@
+/*
+ * 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 React from 'react';
+import ReactDOM from 'react-dom';
+import { OpenSearchPanel } from './open_search_panel';
+
+let isOpen = false;
+
+export function showOpenSearchPanel({ makeUrl }) {
+ if (isOpen) {
+ return;
+ }
+
+ isOpen = true;
+ const container = document.createElement('div');
+ const onClose = () => {
+ ReactDOM.unmountComponentAtNode(container);
+ document.body.removeChild(container);
+ isOpen = false;
+ };
+
+ document.body.appendChild(container);
+ const element = (
+
+ );
+ ReactDOM.render(element, container);
+}
diff --git a/src/ui/public/saved_objects/components/saved_object_finder.js b/src/ui/public/saved_objects/components/saved_object_finder.js
index 68d709e29f99ba..fd8819449d21d4 100644
--- a/src/ui/public/saved_objects/components/saved_object_finder.js
+++ b/src/ui/public/saved_objects/components/saved_object_finder.js
@@ -20,6 +20,7 @@
import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
+import chrome from 'ui/chrome';
import {
EuiFieldSearch,
@@ -104,7 +105,24 @@ export class SavedObjectFinder extends React.Component {
}
debouncedFetch = _.debounce(async (filter) => {
- const response = await this.props.find(this.props.savedObjectType, filter);
+ const resp = await chrome.getSavedObjectsClient().find({
+ type: this.props.savedObjectType,
+ fields: ['title', 'visState'],
+ search: filter ? `${filter}*` : undefined,
+ page: 1,
+ perPage: chrome.getUiSettingsClient().get('savedObjects:listingLimit'),
+ searchFields: ['title^3', 'description']
+ });
+
+ if (this.props.savedObjectType === 'visualization'
+ && !chrome.getUiSettingsClient().get('visualize:enableLabs')
+ && this.props.visTypes) {
+ resp.savedObjects = resp.savedObjects.filter(savedObject => {
+ const typeName = JSON.parse(savedObject.attributes.visState).type;
+ const visType = this.props.visTypes.byName[typeName];
+ return visType.stage !== 'lab';
+ });
+ }
if (!this._isMounted) {
return;
@@ -115,7 +133,7 @@ export class SavedObjectFinder extends React.Component {
if (filter === this.state.filter) {
this.setState({
isFetchingItems: false,
- items: response.savedObjects.map(savedObject => {
+ items: resp.savedObjects.map(savedObject => {
return {
title: savedObject.attributes.title,
id: savedObject.id,
@@ -183,16 +201,26 @@ export class SavedObjectFinder extends React.Component {
field: 'title',
name: 'Title',
sortable: true,
- render: (field, record) => (
- {
- this.props.onChoose(record.id, record.type);
- }}
- data-test-subj={`addPanel${field.split(' ').join('-')}`}
- >
- {field}
-
- )
+ render: (title, record) => {
+ const {
+ onChoose,
+ makeUrl
+ } = this.props;
+
+ if (!onChoose && !makeUrl) {
+ return {title};
+ }
+
+ return (
+ { onChoose(record.id, record.type); } : undefined}
+ href={makeUrl ? makeUrl(record.id) : undefined}
+ data-test-subj={`savedObjectTitle${title.split(' ').join('-')}`}
+ >
+ {title}
+
+ );
+ }
}
];
const items = this.state.items.length === 0 ? [] : this.getPageOfItems();
@@ -221,8 +249,9 @@ export class SavedObjectFinder extends React.Component {
SavedObjectFinder.propTypes = {
callToActionButton: PropTypes.node,
- onChoose: PropTypes.func.isRequired,
- find: PropTypes.func.isRequired,
+ onChoose: PropTypes.func,
+ makeUrl: PropTypes.func,
noItemsMessage: PropTypes.node,
savedObjectType: PropTypes.oneOf(['visualization', 'search']).isRequired,
+ visTypes: PropTypes.object,
};
diff --git a/test/functional/apps/visualize/_lab_mode.js b/test/functional/apps/visualize/_lab_mode.js
index f0fd6f59721c5b..d45d9cc6366221 100644
--- a/test/functional/apps/visualize/_lab_mode.js
+++ b/test/functional/apps/visualize/_lab_mode.js
@@ -29,9 +29,10 @@ export default function ({ getService, getPageObjects }) {
it('disabling does not break loading saved searches', async () => {
await PageObjects.common.navigateToUrl('discover', '');
await PageObjects.discover.saveSearch('visualize_lab_mode_test');
- await PageObjects.discover.openSavedSearch();
+ await PageObjects.discover.openLoadSavedSearchPanel();
const hasSaved = await PageObjects.discover.hasSavedSearch('visualize_lab_mode_test');
expect(hasSaved).to.be(true);
+ await PageObjects.discover.closeLoadSaveSearchPanel();
log.info('found saved search before toggling enableLabs mode');
@@ -42,13 +43,14 @@ export default function ({ getService, getPageObjects }) {
// Expect the discover still to list that saved visualization in the open list
await PageObjects.header.clickDiscover();
- await PageObjects.discover.openSavedSearch();
+ await PageObjects.discover.openLoadSavedSearchPanel();
const stillHasSaved = await PageObjects.discover.hasSavedSearch('visualize_lab_mode_test');
expect(stillHasSaved).to.be(true);
log.info('found saved search after toggling enableLabs mode');
});
after(async () => {
+ await PageObjects.discover.closeLoadSaveSearchPanel();
await PageObjects.header.clickManagement();
await PageObjects.settings.clickKibanaSettings();
await PageObjects.settings.clearAdvancedSettings('visualize:enableLabs');
diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js
index 438aae6441fb16..b76959026a54c9 100644
--- a/test/functional/page_objects/discover_page.js
+++ b/test/functional/page_objects/discover_page.js
@@ -25,6 +25,7 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const find = getService('find');
+ const flyout = getService('flyout');
const PageObjects = getPageObjects(['header', 'common']);
const getRemote = () => (
@@ -71,24 +72,38 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
return await Promise.all(headerElements.map(el => el.getVisibleText()));
}
- async openSavedSearch() {
+ async openLoadSavedSearchPanel() {
+ const isOpen = await testSubjects.exists('loadSearchForm');
+ if (isOpen) {
+ return;
+ }
+
// We need this try loop here because previous actions in Discover like
// saving a search cause reloading of the page and the "Open" menu item goes stale.
await retry.try(async () => {
await this.clickLoadSavedSearchButton();
await PageObjects.header.waitUntilLoadingHasFinished();
- const loadIsOpen = await testSubjects.exists('loadSearchForm');
- expect(loadIsOpen).to.be(true);
+ const isOpen = await testSubjects.exists('loadSearchForm');
+ expect(isOpen).to.be(true);
});
}
+ async closeLoadSaveSearchPanel() {
+ const isOpen = await testSubjects.exists('loadSearchForm');
+ if (!isOpen) {
+ return;
+ }
+
+ await flyout.close('loadSearchForm');
+ }
+
async hasSavedSearch(searchName) {
const searchLink = await find.byPartialLinkText(searchName);
return searchLink.isDisplayed();
}
async loadSavedSearch(searchName) {
- await this.clickLoadSavedSearchButton();
+ await this.openLoadSavedSearchPanel();
const searchLink = await find.byPartialLinkText(searchName);
await searchLink.click();
await PageObjects.header.waitUntilLoadingHasFinished();
diff --git a/test/functional/services/dashboard/add_panel.js b/test/functional/services/dashboard/add_panel.js
index d6d3ad6dd282f7..6973bf9ef0ddc1 100644
--- a/test/functional/services/dashboard/add_panel.js
+++ b/test/functional/services/dashboard/add_panel.js
@@ -151,7 +151,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) {
await this.clickSavedSearchTab();
await this.filterEmbeddableNames(searchName);
- await testSubjects.click(`addPanel${searchName.split(' ').join('-')}`);
+ await testSubjects.click(`savedObjectTitle${searchName.split(' ').join('-')}`);
await testSubjects.exists('addSavedSearchToDashboardSuccess');
await this.closeAddPanel();
}
@@ -173,7 +173,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) {
log.debug(`DashboardAddPanel.addVisualization(${vizName})`);
await this.ensureAddPanelIsShowing();
await this.filterEmbeddableNames(`"${vizName.replace('-', ' ')}"`);
- await testSubjects.click(`addPanel${vizName.split(' ').join('-')}`);
+ await testSubjects.click(`savedObjectTitle${vizName.split(' ').join('-')}`);
await this.closeAddPanel();
}
@@ -188,7 +188,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) {
log.debug(`DashboardAddPanel.panelAddLinkExists(${name})`);
await this.ensureAddPanelIsShowing();
await this.filterEmbeddableNames(`"${name}"`);
- return await testSubjects.exists(`addPanel${name.split(' ').join('-')}`);
+ return await testSubjects.exists(`savedObjectTitle${name.split(' ').join('-')}`);
}
};
}