From 658fd45c9c322710fa04e844d43d2d63365807c3 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 4 Mar 2019 17:11:59 +0100 Subject: [PATCH 01/31] Prepare control flow to use embeddable factories in add panel --- .../kibana/public/dashboard/dashboard_app.js | 2 +- .../dashboard/top_nav/show_add_panel.js | 4 +-- .../embeddable/search_embeddable_factory.ts | 13 +++++++- .../visualize_embeddable_factory.ts | 33 +++++++++++++++++-- .../visualize_embeddable_factory_provider.ts | 7 +++- .../service/saved_objects_client.d.ts | 4 +++ .../public/embeddable/embeddable_factory.ts | 14 ++++++-- src/legacy/ui/public/registry/vis_types.d.ts | 25 -------------- .../registry/{vis_types.js => vis_types.ts} | 11 +++++-- 9 files changed, 75 insertions(+), 38 deletions(-) delete mode 100644 src/legacy/ui/public/registry/vis_types.d.ts rename src/legacy/ui/public/registry/{vis_types.js => vis_types.ts} (79%) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js index 880455cbd82d7..67ac47d9b1ace 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.js @@ -478,7 +478,7 @@ app.directive('dashboardApp', function ($injector) { showNewVisModal(visTypes, { editorParams: [DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM] }); }; - showAddPanel(dashboardStateManager.addNewPanel, addNewVis, visTypes); + showAddPanel(dashboardStateManager.addNewPanel, addNewVis, embeddableFactories); }; navActions[TopNavIds.OPTIONS] = (menuItem, navController, anchorElement) => { showOptionsPopover({ diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js index adb0908a623eb..ede1432d10480 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_add_panel.js @@ -24,7 +24,7 @@ import ReactDOM from 'react-dom'; let isOpen = false; -export function showAddPanel(addNewPanel, addNewVis, visTypes) { +export function showAddPanel(addNewPanel, addNewVis, embeddableFactories) { if (isOpen) { return; } @@ -47,9 +47,9 @@ export function showAddPanel(addNewPanel, addNewVis, visTypes) { ); 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 83028ea62d881..f755ca39b9461 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 @@ -19,6 +19,7 @@ import 'ui/doc_table'; +import { i18n } from '@kbn/i18n'; import { EmbeddableFactory } from 'ui/embeddable'; import { EmbeddableInstanceConfiguration, @@ -33,7 +34,17 @@ export class SearchEmbeddableFactory extends EmbeddableFactory { private $rootScope: ng.IRootScopeService, private searchLoader: SavedSearchLoader ) { - super({ name: 'search' }); + super({ + name: 'search', + savedObjectMetaData: { + name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { + defaultMessage: 'Saved Search', + }), + type: 'search', + getIconForSavedObject: () => 'search', + showSavedObject: () => true, + }, + }); } public getEditPath(panelId: string) { diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts index e7a81a61b9831..f6d8c70129825 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts @@ -17,6 +17,8 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; +import chrome from 'ui/chrome'; import { EmbeddableFactory } from 'ui/embeddable'; import { getVisualizeLoader } from 'ui/visualize/loader'; import { VisualizeEmbeddable } from './visualize_embeddable'; @@ -26,16 +28,41 @@ import { EmbeddableInstanceConfiguration, OnEmbeddableStateChanged, } from 'ui/embeddable/embeddable_factory'; +import { VisTypesRegistry } from 'ui/registry/vis_types'; +import { VisualizationAttributes } from '../../../../../server/saved_objects/service/saved_objects_client'; import { SavedVisualizations } from '../types'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { getIndexPattern } from './get_index_pattern'; -export class VisualizeEmbeddableFactory extends EmbeddableFactory { +export class VisualizeEmbeddableFactory extends EmbeddableFactory { private savedVisualizations: SavedVisualizations; private config: Legacy.KibanaConfig; - constructor(savedVisualizations: SavedVisualizations, config: Legacy.KibanaConfig) { - super({ name: 'visualization' }); + constructor( + savedVisualizations: SavedVisualizations, + config: Legacy.KibanaConfig, + visTypes: VisTypesRegistry + ) { + super({ + name: 'visualization', + savedObjectMetaData: { + name: i18n.translate('kbn.visualize.savedObjectName', { defaultMessage: 'Visualization' }), + type: 'visualization', + getIconForSavedObject: savedObject => { + return ( + visTypes.byName[JSON.parse(savedObject.attributes.visState).type].icon || 'visualizeApp' + ); + }, + showSavedObject: savedObject => { + if (chrome.getUiSettingsClient().get('visualize:enableLabs')) { + return true; + } + const typeName: string = JSON.parse(savedObject.attributes.visState).type; + const visType = visTypes.byName[typeName]; + return visType.stage !== 'experimental'; + }, + }, + }); this.config = config; this.savedVisualizations = savedVisualizations; } diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts index da30433d11d72..abea267d6d149 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts @@ -20,6 +20,7 @@ import { Legacy } from 'kibana'; import { EmbeddableFactoriesRegistryProvider } from 'ui/embeddable/embeddable_factories_registry'; import { IPrivate } from 'ui/private'; +import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { SavedVisualizations } from '../types'; import { VisualizeEmbeddableFactory } from './visualize_embeddable_factory'; @@ -28,7 +29,11 @@ export function visualizeEmbeddableFactoryProvider(Private: IPrivate) { savedVisualizations: SavedVisualizations, config: Legacy.KibanaConfig ) => { - return new VisualizeEmbeddableFactory(savedVisualizations, config); + return new VisualizeEmbeddableFactory( + savedVisualizations, + config, + Private(VisTypesRegistryProvider as any) + ); }; return Private(VisualizeEmbeddableFactoryProvider); } diff --git a/src/legacy/server/saved_objects/service/saved_objects_client.d.ts b/src/legacy/server/saved_objects/service/saved_objects_client.d.ts index be807c9d17df7..be99c704b16ca 100644 --- a/src/legacy/server/saved_objects/service/saved_objects_client.d.ts +++ b/src/legacy/server/saved_objects/service/saved_objects_client.d.ts @@ -82,6 +82,10 @@ export interface SavedObjectAttributes { [key: string]: SavedObjectAttributes | string | number | boolean | null; } +export interface VisualizationAttributes extends SavedObjectAttributes { + visState: string; +} + export interface SavedObject { id: string; type: string; diff --git a/src/legacy/ui/public/embeddable/embeddable_factory.ts b/src/legacy/ui/public/embeddable/embeddable_factory.ts index 07bfd2a2b33f0..e71813b22995f 100644 --- a/src/legacy/ui/public/embeddable/embeddable_factory.ts +++ b/src/legacy/ui/public/embeddable/embeddable_factory.ts @@ -17,6 +17,8 @@ * under the License. */ +import { SavedObjectAttributes } from '../../../server/saved_objects'; +import { SavedObjectMetaData } from '../saved_objects/components/saved_object_finder'; import { Embeddable } from './embeddable'; import { EmbeddableState } from './types'; export interface EmbeddableInstanceConfiguration { @@ -28,16 +30,24 @@ export type OnEmbeddableStateChanged = (embeddableStateChanges: EmbeddableState) /** * The EmbeddableFactory creates and initializes an embeddable instance */ -export abstract class EmbeddableFactory { +export abstract class EmbeddableFactory { public readonly name: string; + public readonly savedObjectMetaData?: SavedObjectMetaData; /** * * @param name - a unique identified for this factory, which will be used to map an embeddable spec to * a factory that can generate an instance of it. */ - constructor({ name }: { name: string }) { + constructor({ + name, + savedObjectMetaData, + }: { + name: string; + savedObjectMetaData?: SavedObjectMetaData; + }) { this.name = name; + this.savedObjectMetaData = savedObjectMetaData; } /** diff --git a/src/legacy/ui/public/registry/vis_types.d.ts b/src/legacy/ui/public/registry/vis_types.d.ts deleted file mode 100644 index 77d5515dc5745..0000000000000 --- a/src/legacy/ui/public/registry/vis_types.d.ts +++ /dev/null @@ -1,25 +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 { VisType } from '../vis'; -import { UIRegistry } from './_registry'; - -declare type VisTypesRegistryProvider = UIRegistry & { - byName: { [typeName: string]: VisType }; -}; diff --git a/src/legacy/ui/public/registry/vis_types.js b/src/legacy/ui/public/registry/vis_types.ts similarity index 79% rename from src/legacy/ui/public/registry/vis_types.js rename to src/legacy/ui/public/registry/vis_types.ts index a6e95f5576d57..754d19624b8ba 100644 --- a/src/legacy/ui/public/registry/vis_types.js +++ b/src/legacy/ui/public/registry/vis_types.ts @@ -17,10 +17,15 @@ * under the License. */ -import { uiRegistry } from './_registry'; +import { VisType } from '../vis'; +import { uiRegistry, UIRegistry } from './_registry'; + +export type VisTypesRegistry = UIRegistry & { + byName: { [typeName: string]: VisType }; +}; export const VisTypesRegistryProvider = uiRegistry({ name: 'visTypes', index: ['name'], - order: ['title'] -}); + order: ['title'], +}) as VisTypesRegistry; From d3b27599246307e03c5b71792c1b90a4a41e7489 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 4 Mar 2019 17:12:46 +0100 Subject: [PATCH 02/31] Rewrite saved object finder and add tests --- src/legacy/ui/public/_index.scss | 1 + .../ui/public/saved_objects/_index.scss | 1 + .../saved_objects/components/_index.scss | 1 + .../components/saved_object_finder.scss | 3 + .../components/saved_object_finder.test.tsx | 494 ++++++++++++++++++ .../components/saved_object_finder.tsx | 419 ++++++++++----- 6 files changed, 796 insertions(+), 123 deletions(-) create mode 100644 src/legacy/ui/public/saved_objects/_index.scss create mode 100644 src/legacy/ui/public/saved_objects/components/_index.scss create mode 100644 src/legacy/ui/public/saved_objects/components/saved_object_finder.scss create mode 100644 src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index f6f6314edcc01..79913b66ee09d 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -28,6 +28,7 @@ @import './partials/index'; @import './query_bar/index'; @import './share/index'; +@import './saved_objects/index'; @import './filter_bar/index'; @import './style_compile/index'; diff --git a/src/legacy/ui/public/saved_objects/_index.scss b/src/legacy/ui/public/saved_objects/_index.scss new file mode 100644 index 0000000000000..30ac0c9fe9b27 --- /dev/null +++ b/src/legacy/ui/public/saved_objects/_index.scss @@ -0,0 +1 @@ +@import './components/index'; \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/_index.scss b/src/legacy/ui/public/saved_objects/components/_index.scss new file mode 100644 index 0000000000000..a876f055d0e40 --- /dev/null +++ b/src/legacy/ui/public/saved_objects/components/_index.scss @@ -0,0 +1 @@ +@import './saved_object_finder'; \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss b/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss new file mode 100644 index 0000000000000..61da8f980ed68 --- /dev/null +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss @@ -0,0 +1,3 @@ +.savedObjectFilterHeading:hover { + text-decoration: none; +} \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx new file mode 100644 index 0000000000000..1f91180e14baf --- /dev/null +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx @@ -0,0 +1,494 @@ +/* + * 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. + */ + +jest.mock('ui/chrome', () => ({ + getUiSettingsClient: () => ({ + get: () => 10, + }), +})); + +jest.mock('lodash', () => ({ + debounce: (fn: any) => fn, +})); + +const nextTick = () => new Promise(res => process.nextTick(res)); + +import { + EuiEmptyPrompt, + EuiListGroup, + // @ts-ignore + EuiListGroupItem, + EuiLoadingSpinner, + EuiPagination, + EuiTablePagination, +} from '@elastic/eui'; +import { shallow } from 'enzyme'; +import React from 'react'; +import * as sinon from 'sinon'; +import { SavedObjectFinder } from './saved_object_finder'; + +describe('SavedObjectsFinder', () => { + let objectsClientStub: sinon.SinonStub; + + const doc = { + id: '1', + type: 'search', + attributes: { title: 'Example title' }, + }; + + const doc2 = { + id: '2', + type: 'search', + attributes: { title: 'Another title' }, + }; + + const doc3 = { type: 'vis', id: '3', attributes: { title: 'Vis' } }; + + const searchMetaData = [ + { + type: 'search', + name: 'Search', + getIconForSavedObject: () => 'search', + showSavedObject: () => true, + }, + ]; + + beforeEach(() => { + objectsClientStub = sinon.stub(); + objectsClientStub.returns(Promise.resolve({ savedObjects: [] })); + require('ui/chrome').getSavedObjectsClient = () => ({ + find: async (...args: any[]) => { + return objectsClientStub(...args); + }, + }); + }); + + it('should call saved object client on startup', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + expect( + objectsClientStub.calledWith({ + type: ['search'], + fields: ['title', 'visState'], + search: undefined, + page: 1, + perPage: 10, + searchFields: ['title^3', 'description'], + defaultSearchOperator: 'AND', + }) + ).toBe(true); + }); + + it('should list initial items', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + await nextTick(); + expect( + wrapper.containsMatchingElement() + ).toEqual(true); + }); + + it('should call onChoose on item click', async () => { + const chooseStub = sinon.stub(); + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc] })); + + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find(EuiListGroupItem) + .first() + .simulate('click'); + expect(chooseStub.calledWith('1', 'search', 'Search')).toEqual(true); + }); + + describe('sorting', () => { + it('should list items ascending', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc, doc2] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + await nextTick(); + const list = wrapper.find(EuiListGroup); + expect(list.childAt(0).key()).toBe('2'); + expect(list.childAt(1).key()).toBe('1'); + }); + + it('should list items descending after sort click', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc, doc2] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find('[data-test-subj="savedObjectFinderSortButton"]') + .first() + .simulate('click'); + const list = wrapper.find(EuiListGroup); + expect(list.childAt(0).key()).toBe('1'); + expect(list.childAt(1).key()).toBe('2'); + }); + }); + + it('should not show the saved objects which get filtered by showSavedObject', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc, doc2] })); + + const wrapper = shallow( + 'search', + showSavedObject: ({ id }) => id !== '1', + }, + ]} + /> + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find('[data-test-subj="savedObjectFinderSortButton"]') + .first() + .simulate('click'); + const list = wrapper.find(EuiListGroup); + expect(list.childAt(0).key()).toBe('2'); + expect(list.children().length).toBe(1); + }); + + describe('search', () => { + it('should request filtered list on search input', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc, doc2] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find('[data-test-subj="savedObjectFinderSearchInput"]') + .first() + .simulate('change', { target: { value: 'abc' } }); + + expect( + objectsClientStub.calledWith({ + type: ['search'], + fields: ['title', 'visState'], + search: 'abc*', + page: 1, + perPage: 10, + searchFields: ['title^3', 'description'], + defaultSearchOperator: 'AND', + }) + ).toBe(true); + }); + + it('should respect response order on search input', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc, doc2] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find('[data-test-subj="savedObjectFinderSearchInput"]') + .first() + .simulate('change', { target: { value: 'abc' } }); + await nextTick(); + const list = wrapper.find(EuiListGroup); + expect(list.childAt(0).key()).toBe('1'); + expect(list.childAt(1).key()).toBe('2'); + }); + }); + + it('should request multiple saved object types at once', async () => { + const wrapper = shallow( + 'search', + showSavedObject: () => true, + }, + { + type: 'vis', + name: 'Vis', + getIconForSavedObject: () => 'visualization', + showSavedObject: () => true, + }, + ]} + /> + ); + wrapper.instance().componentDidMount!(); + + expect( + objectsClientStub.calledWith({ + type: ['search', 'vis'], + fields: ['title', 'visState'], + search: undefined, + page: 1, + perPage: 10, + searchFields: ['title^3', 'description'], + defaultSearchOperator: 'AND', + }) + ).toBe(true); + }); + + describe('filter', () => { + const metaDataConfig = [ + { + type: 'search', + name: 'Search', + getIconForSavedObject: () => 'search', + showSavedObject: () => true, + }, + { + type: 'vis', + name: 'Vis', + getIconForSavedObject: () => 'document', + showSavedObject: () => true, + }, + ]; + + it('should not render filter buttons if disabled', async () => { + objectsClientStub.returns( + Promise.resolve({ + savedObjects: [doc, doc2, doc3], + }) + ); + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + expect(wrapper.find('[data-test-subj="savedObjectFinderFilter-search"]').exists()).toBe( + false + ); + }); + + it('should not render filter buttons if there is only one type in the list', async () => { + objectsClientStub.returns( + Promise.resolve({ + savedObjects: [doc, doc2], + }) + ); + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + expect(wrapper.find('[data-test-subj="savedObjectFinderFilter-search"]').exists()).toBe( + false + ); + }); + + it('should apply filter if selected', async () => { + objectsClientStub.returns( + Promise.resolve({ + savedObjects: [doc, doc2, doc3], + }) + ); + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find('[data-test-subj="savedObjectFinderFilter-vis"]') + .first() + .simulate('click'); + const list = wrapper.find(EuiListGroup); + expect(list.childAt(0).key()).toBe('3'); + expect(list.children().length).toBe(1); + + wrapper + .find('[data-test-subj="savedObjectFinderFilter-search"]') + .first() + .simulate('click'); + expect(wrapper.find(EuiListGroup).children().length).toBe(3); + }); + }); + + it('should display passed in action button', () => { + const actionButton = ; + const wrapper = shallow( + + ); + + expect(wrapper.contains(actionButton)).toBe(true); + }); + + it('should display no items message if there are no items', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [] })); + const noItemsMessage = ; + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + + expect( + wrapper + .find(EuiEmptyPrompt) + .first() + .prop('body') + ).toEqual(noItemsMessage); + }); + + describe('pagination', () => { + const longItemList = new Array(50).fill(undefined).map((_, i) => ({ + id: String(i), + type: 'search', + attributes: { + title: `Title ${i < 10 ? '0' : ''}${i}`, + }, + })); + + beforeEach(() => { + objectsClientStub.returns(Promise.resolve({ savedObjects: longItemList })); + }); + + it('should show a table pagination with initial per page', async () => { + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + expect( + wrapper + .find(EuiTablePagination) + .first() + .prop('itemsPerPage') + ).toEqual(15); + expect(wrapper.find(EuiListGroup).children().length).toBe(15); + }); + + it('should allow switching the page size', async () => { + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find(EuiTablePagination) + .first() + .prop('onChangeItemsPerPage')!(5); + expect(wrapper.find(EuiListGroup).children().length).toBe(5); + }); + + it('should switch page correctly', async () => { + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find(EuiTablePagination) + .first() + .prop('onChangePage')!(1); + expect( + wrapper + .find(EuiListGroup) + .children() + .first() + .key() + ).toBe('15'); + }); + + it('should show an ordinary pagination for fixed page sizes', async () => { + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + expect( + wrapper + .find(EuiPagination) + .first() + .prop('pageCount') + ).toEqual(2); + expect(wrapper.find(EuiListGroup).children().length).toBe(33); + }); + + it('should switch page correctly for fixed page sizes', async () => { + const wrapper = shallow( + + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find(EuiPagination) + .first() + .prop('onPageClick')!(1); + expect( + wrapper + .find(EuiListGroup) + .children() + .first() + .key() + ).toBe('33'); + }); + }); + + describe('loading state', () => { + it('should display a spinner during loading', () => { + const wrapper = shallow(); + + expect(wrapper.containsMatchingElement()).toBe(true); + }); + + it('should hide the spinner if data is shown', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc] })); + + const wrapper = shallow( + 'search', + showSavedObject: () => true, + }, + ]} + /> + ); + wrapper.instance().componentDidMount!(); + await nextTick(); + expect(wrapper.containsMatchingElement()).toBe(false); + }); + + it('should show the spinner again on search input', async () => { + objectsClientStub.returns(Promise.resolve({ savedObjects: [doc] })); + + const wrapper = shallow(); + wrapper.instance().componentDidMount!(); + await nextTick(); + wrapper + .find('[data-test-subj="savedObjectFinderSearchInput"]') + .first() + .simulate('change', { target: { value: 'abc' } }); + + expect(wrapper.containsMatchingElement()).toBe(true); + }); + }); +}); diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 481eca0edaf67..8bc8057dc5fbd 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -23,25 +23,51 @@ import React from 'react'; import chrome from 'ui/chrome'; import { - EuiBasicTable, + CommonProps, + EuiButtonEmpty, + EuiEmptyPrompt, EuiFieldSearch, + // @ts-ignore + EuiFilterGroup, + // @ts-ignore + EuiFilterSelectItem, EuiFlexGroup, EuiFlexItem, - EuiLink, - EuiTableCriteria, + EuiIcon, + EuiListGroup, + // @ts-ignore + EuiListGroupItem, + EuiLoadingSpinner, + EuiPagination, + EuiPopover, + EuiSpacer, + EuiTablePagination, + EuiText, + EuiToolTip, } from '@elastic/eui'; import { Direction } from '@elastic/eui/src/services/sort/sort_direction'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { SavedObjectAttributes } from '../../../../server/saved_objects'; -import { VisTypesRegistryProvider } from '../../registry/vis_types'; import { SimpleSavedObject } from '../simple_saved_object'; -interface SavedObjectFinderUIState { +// TODO the typings for EuiListGroup are incorrect - maxWidth is missing. This can be removed when the types are adjusted +const FixedEuiListGroup = (EuiListGroup as any) as React.SFC; + +export interface SavedObjectMetaData { + type: string; + name: string; + getIconForSavedObject(savedObject: SimpleSavedObject): string | undefined; + showSavedObject(savedObject: SimpleSavedObject): boolean; +} + +interface SavedObjectFinderState { items: Array<{ title: string | null; id: SimpleSavedObject['id']; type: SimpleSavedObject['type']; + savedObject: SimpleSavedObject; }>; filter: string; isFetchingItems: boolean; @@ -49,18 +75,20 @@ interface SavedObjectFinderUIState { perPage: number; sortField?: string; sortDirection?: Direction; + filterOpen: boolean; + filteredTypes: string[]; } interface BaseSavedObjectFinder { callToActionButton?: React.ReactNode; onChoose?: ( id: SimpleSavedObject['id'], - type: SimpleSavedObject['type'] + type: SimpleSavedObject['type'], + name: string ) => void; - makeUrl?: (id: SimpleSavedObject['id']) => void; noItemsMessage?: React.ReactNode; - savedObjectType: 'visualization' | 'search' | 'index-pattern'; - visTypes?: VisTypesRegistryProvider; + savedObjectMetaData: Array>; + showFilter?: boolean; } interface SavedObjectFinderFixedPage extends BaseSavedObjectFinder { @@ -69,28 +97,29 @@ interface SavedObjectFinderFixedPage extends BaseSavedObjectFinder { } interface SavedObjectFinderInitialPageSize extends BaseSavedObjectFinder { - initialPageSize?: 5 | 10 | 15; + initialPageSize?: 5 | 10 | 15 | 25; fixedPageSize?: undefined; } type SavedObjectFinderProps = SavedObjectFinderFixedPage | SavedObjectFinderInitialPageSize; -class SavedObjectFinder extends React.Component { +class SavedObjectFinder extends React.Component { public static propTypes = { callToActionButton: PropTypes.node, onChoose: PropTypes.func, - makeUrl: PropTypes.func, noItemsMessage: PropTypes.node, - savedObjectType: PropTypes.oneOf(['visualization', 'search', 'index-pattern']).isRequired, - visTypes: PropTypes.object, - initialPageSize: PropTypes.oneOf([5, 10, 15]), + savedObjectMetaData: PropTypes.array.isRequired, + initialPageSize: PropTypes.oneOf([5, 10, 15, 25]), fixedPageSize: PropTypes.number, + showFilter: PropTypes.bool, }; private isComponentMounted: boolean = false; private debouncedFetch = _.debounce(async (filter: string) => { + const metaDataMap = this.getSavedObjectMetaDataMap(); + const resp = await chrome.getSavedObjectsClient().find({ - type: this.props.savedObjectType, + type: Object.keys(metaDataMap), fields: ['title', 'visState'], search: filter ? `${filter}*` : undefined, page: 1, @@ -99,21 +128,10 @@ class SavedObjectFinder extends React.Component { - if (typeof savedObject.attributes.visState !== 'string') { - return false; - } - const typeName: string = JSON.parse(savedObject.attributes.visState).type; - const visType = visTypes.byName[typeName]; - return visType.stage !== 'experimental'; - }); - } + resp.savedObjects = resp.savedObjects.filter( + savedObject => + metaDataMap[savedObject.type] && metaDataMap[savedObject.type].showSavedObject(savedObject) + ); if (!this.isComponentMounted) { return; @@ -124,11 +142,17 @@ class SavedObjectFinder extends React.Component { + items: resp.savedObjects.map(savedObject => { + const { + attributes: { title }, + id, + type, + } = savedObject; return { title: typeof title === 'string' ? title : '', id, type, + savedObject, }; }), }); @@ -144,6 +168,8 @@ class SavedObjectFinder extends React.Component {this.renderSearchBar()} - {this.renderTable()} + {this.renderListing()} ); } - private onTableChange = ({ page, sort = {} }: EuiTableCriteria) => { - let sortField: string | undefined = sort.field; - let sortDirection: Direction | undefined = sort.direction; - - // 3rd sorting state that is not captured by sort - native order (no sort) - // when switching from desc to asc for the same field - use native order - if ( - this.state.sortField === sortField && - this.state.sortDirection === 'desc' && - sortDirection === 'asc' - ) { - sortField = undefined; - sortDirection = undefined; - } + private getSavedObjectMetaDataMap(): Record> { + return this.props.savedObjectMetaData.reduce( + (map, metaData) => ({ ...map, [metaData.type]: metaData }), + {} + ); + } - this.setState({ - page: page.index, - perPage: page.size, - sortField, - sortDirection, - }); - }; + private getPageCount() { + return Math.ceil( + (this.state.filteredTypes.length === 0 + ? this.state.items.length + : this.state.items.filter( + item => + this.state.filteredTypes.length === 0 || this.state.filteredTypes.includes(item.type) + ).length) / this.state.perPage + ); + } // server-side paging not supported // 1) saved object client does not support sorting by title because title is only mapped as analyzed @@ -197,17 +218,15 @@ class SavedObjectFinder extends React.Component { // do not sort original list to preserve elasticsearch ranking order const items = this.state.items.slice(); - const { sortField } = this.state; + const { sortDirection } = this.state; - if (sortField) { - items.sort((a, b) => { - const fieldA = _.get(a, sortField, ''); - const fieldB = _.get(b, sortField, ''); + if (sortDirection || !this.state.filter) { + items.sort(({ title: titleA }, { title: titleB }) => { let order = 1; - if (this.state.sortDirection === 'desc') { + if (sortDirection === 'desc') { order = -1; } - return order * fieldA.toLowerCase().localeCompare(fieldB.toLowerCase()); + return order * (titleA || '').toLowerCase().localeCompare((titleB || '').toLowerCase()); }); } @@ -215,7 +234,12 @@ class SavedObjectFinder extends React.Component + this.state.filteredTypes.length === 0 || this.state.filteredTypes.includes(item.type) + ) + .slice(startIndex, lastIndex); }; private fetchItems = () => { @@ -227,9 +251,62 @@ class SavedObjectFinder extends React.Component(); + this.state.items.forEach(item => { + typesInItems.add(item.type); + }); + return this.props.savedObjectMetaData.filter(metaData => typesInItems.has(metaData.type)); + } + + private getCurrentSort() { + return this.state.sortDirection || (this.state.filter ? 'auto' : 'asc'); + } + + private getCurrentSortIcon() { + switch (this.getCurrentSort()) { + case 'asc': + return 'sortUp'; + case 'desc': + return 'sortDown'; + case 'auto': + return 'search'; + } + } + + private getCurrentSortTooltip() { + switch (this.getCurrentSort()) { + case 'asc': + return i18n.translate('common.ui.savedObjects.finder.sortAsc', { + defaultMessage: 'Sort ascending', + }); + case 'desc': + return i18n.translate('common.ui.savedObjects.finder.sortDesc', { + defaultMessage: 'Sort descending', + }); + case 'auto': + return i18n.translate('common.ui.savedObjects.finder.sortAuto', { + defaultMessage: 'Sort by best match', + }); + } + } + + private getNextDirection(dir: Direction | undefined, hasFilter: boolean) { + switch (dir) { + case 'asc': + return 'desc'; + case 'desc': + return hasFilter ? undefined : 'asc'; + default: + return hasFilter ? 'asc' : 'desc'; + } + } + private renderSearchBar() { + const availableSavedObjectMetaData = this.getAvailableSavedObjectMetaData(); + return ( - + + + + + + this.setState(({ sortDirection, filter }) => ({ + sortDirection: this.getNextDirection(sortDirection, Boolean(filter)), + })) + } + > + + + + + {this.props.showFilter && availableSavedObjectMetaData.length > 1 && ( + this.setState({ filterOpen: false })} + button={ + + this.setState(({ filterOpen }) => ({ + filterOpen: !filterOpen, + })) + } + iconSide="right" + iconType="filter" + size="s" + color="text" + data-test-subj="savedObjectFinderFilterButton" + > + + + } + > +
+ + + + {availableSavedObjectMetaData.map(metaData => ( + { + this.setState(({ filteredTypes }) => ({ + filteredTypes: filteredTypes.includes(metaData.type) + ? filteredTypes.filter(t => t !== metaData.type) + : [...filteredTypes, metaData.type], + page: 0, + })); + }} + > + {metaData.name} + + ))} +
+
+ )} +
+
+ {this.props.callToActionButton} +
+ ); + } - {this.props.callToActionButton && ( - {this.props.callToActionButton} - )} + private renderLoadingIndicator() { + return ( + + + + + ); } - private renderTable() { - const pagination = { - pageIndex: this.state.page, - pageSize: this.state.perPage, - totalItemCount: this.state.items.length, - hidePerPageOptions: Boolean(this.props.fixedPageSize), - pageSizeOptions: [5, 10, 15], - }; - // TODO there should be a Type in EUI for that, replace if it exists - const sorting: { sort?: EuiTableCriteria['sort'] } = {}; - if (this.state.sortField) { - sorting.sort = { - field: this.state.sortField, - direction: this.state.sortDirection, - }; - } - const tableColumns = [ - { - field: 'title', - name: i18n.translate('common.ui.savedObjects.finder.titleLabel', { - defaultMessage: 'Title', - }), - sortable: true, - render: (title: string, record: SimpleSavedObject) => { - 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} - - ); - }, - }, - ]; + private renderListing() { const items = this.state.items.length === 0 ? [] : this.getPageOfItems(); + const { onChoose, savedObjectMetaData } = this.props; + return ( - + <> + {this.state.isFetchingItems ? ( + this.renderLoadingIndicator() + ) : items.length > 0 ? ( + + {items.map(item => { + const currentSavedObjectMetaData = savedObjectMetaData.find( + metaData => metaData.type === item.type + ); + const fullName = `${item.title} (${currentSavedObjectMetaData!.name})`; + const iconType = ( + currentSavedObjectMetaData || + ({ + getIconForSavedObject: () => 'document', + } as Pick, 'getIconForSavedObject'>) + ).getIconForSavedObject(item.savedObject); + return ( + { + onChoose(item.id, item.type, fullName); + } + : undefined + } + title={fullName} + data-test-subj={`savedObjectTitle${(item.title || '').split(' ').join('-')}`} + /> + ); + })} + + ) : ( + + )} + {this.props.fixedPageSize ? ( + { + this.setState({ + page, + }); + }} + /> + ) : ( + { + this.setState({ + page, + }); + }} + onChangeItemsPerPage={perPage => { + this.setState({ + perPage, + }); + }} + itemsPerPage={this.state.perPage} + itemsPerPageOptions={[5, 10, 15, 25]} + /> + )} + ); } } From 501f38ad7b78d23e15ddc0fb298d8022430c4db1 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 4 Mar 2019 17:13:30 +0100 Subject: [PATCH 03/31] Fix usages of new saved object finder --- .../public/dashboard/top_nav/add_panel.js | 163 +++++------------- .../discover/top_nav/open_search_panel.js | 18 +- .../search_selection/search_selection.tsx | 130 ++++---------- .../services/dashboard/add_panel.js | 33 ++-- .../translations/translations/zh-CN.json | 6 - 5 files changed, 117 insertions(+), 233 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js index e9f4709c7e927..cfad25e2ca1a3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js @@ -19,109 +19,21 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { toastNotifications } from 'ui/notify'; import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; import { EuiFlyout, EuiFlyoutBody, - EuiButton, - EuiTabs, - EuiTab, + EuiButtonEmpty, EuiSpacer, EuiTitle, } from '@elastic/eui'; -const VIS_TAB_ID = 'vis'; -const SAVED_SEARCH_TAB_ID = 'search'; - -class DashboardAddPanelUi extends React.Component { - constructor(props) { - super(props); - - const addNewVisBtn = ( - - - - ); - - const tabs = [{ - id: VIS_TAB_ID, - name: props.intl.formatMessage({ - id: 'kbn.dashboard.topNav.addPanel.visualizationTabName', - defaultMessage: 'Visualization', - }), - dataTestSubj: 'addVisualizationTab', - toastDataTestSubj: 'addVisualizationToDashboardSuccess', - savedObjectFinder: ( - - ) - }, { - id: SAVED_SEARCH_TAB_ID, - name: props.intl.formatMessage({ - id: 'kbn.dashboard.topNav.addPanel.savedSearchTabName', - defaultMessage: 'Saved Search', - }), - dataTestSubj: 'addSavedSearchTab', - toastDataTestSubj: 'addSavedSearchToDashboardSuccess', - savedObjectFinder: ( - - ) - }]; - - this.state = { - tabs: tabs, - selectedTab: tabs[0], - }; - } - - onSelectedTabChanged = tab => { - this.setState({ - selectedTab: tab, - }); - } - - renderTabs() { - return this.state.tabs.map((tab) => { - return ( - this.onSelectedTabChanged(tab)} - isSelected={tab.id === this.state.selectedTab.id} - key={tab.id} - data-test-subj={tab.dataTestSubj} - > - {tab.name} - - ); - }); - } - - onAddPanel = (id, type) => { +export class DashboardAddPanel extends React.Component { + onAddPanel = (id, type, name) => { this.props.addNewPanel(id, type); // To avoid the clutter of having toast messages cover flyout @@ -131,25 +43,37 @@ class DashboardAddPanelUi extends React.Component { } this.lastToast = toastNotifications.addSuccess({ - title: this.props.intl.formatMessage({ - id: 'kbn.dashboard.topNav.addPanel.selectedTabAddedToDashboardSuccessMessageTitle', - defaultMessage: '{selectedTabName} was added to your dashboard', - }, { - selectedTabName: this.state.selectedTab.name, - }), - 'data-test-subj': this.state.selectedTab.toastDataTestSubj, + title: i18n.translate( + 'kbn.dashboard.topNav.addPanel.savedObjectAddedToDashboardSuccessMessageTitle', + { + defaultMessage: '{savedObjectName} was added to your dashboard', + values: { + savedObjectName: name, + }, + } + ), + 'data-test-subj': 'addObjectToDashboardSuccess', }); - } + }; render() { - return ( - - + + + ); + return ( + +

- - {this.renderTabs()} - - - {this.state.selectedTab.savedObjectFinder} - + Boolean(embeddableFactory.savedObjectMetaData)) + .map(({ savedObjectMetaData }) => savedObjectMetaData)} + showFilter={true} + callToActionButton={addNewVisBtn} + noItemsMessage={i18n.translate( + 'kbn.dashboard.topNav.addPanel.noMatchingObjectsMessage', + { + defaultMessage: 'No matching objects found.', + } + )} + /> ); } } -DashboardAddPanelUi.propTypes = { +DashboardAddPanel.propTypes = { onClose: PropTypes.func.isRequired, - visTypes: PropTypes.object.isRequired, addNewPanel: PropTypes.func.isRequired, addNewVis: PropTypes.func.isRequired, }; - -export const DashboardAddPanel = injectI18n(DashboardAddPanelUi); diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js index 1235272588fcb..2a8bcf0a8777f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js +++ b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js @@ -28,7 +28,7 @@ import { EuiFlyout, EuiFlyoutBody, EuiTitle, - EuiButton, + EuiButtonEmpty, } from '@elastic/eui'; const SEARCH_OBJECT_TYPE = 'search'; @@ -37,7 +37,9 @@ export class OpenSearchPanel extends React.Component { renderMangageSearchesButton() { return ( - @@ -45,7 +47,7 @@ export class OpenSearchPanel extends React.Component { id="kbn.discover.topNav.openSearchPanel.manageSearchesButtonLabel" defaultMessage="Manage searches" /> - + ); } @@ -76,8 +78,14 @@ export class OpenSearchPanel extends React.Component { defaultMessage="No matching searches found." /> } - savedObjectType={SEARCH_OBJECT_TYPE} - makeUrl={this.props.makeUrl} + savedObjectMetaData={[ + { + type: SEARCH_OBJECT_TYPE, + showSavedObject: () => true, + getIconForSavedObject: () => 'search', + name: 'Search', + }, + ]} onChoose={this.props.onClose} callToActionButton={this.renderMangageSearchesButton()} /> diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx index 2c945a5ccccbf..f7119106afb70 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx @@ -17,14 +17,7 @@ * under the License. */ -import { - EuiModalBody, - EuiModalHeader, - EuiModalHeaderTitle, - EuiSpacer, - EuiTab, - EuiTabs, -} from '@elastic/eui'; +import { EuiModalBody, EuiModalHeader, EuiModalHeaderTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; @@ -38,22 +31,7 @@ interface SearchSelectionProps { visType: VisType; } -interface SearchSelectionState { - selectedTabId: string; -} - -interface TabProps { - id: string; - name: string; -} - -const INDEX_PATTERNS_TAB_ID = 'indexPatterns'; -const SAVED_SEARCHES_TAB_ID = 'savedSearches'; - -export class SearchSelection extends React.Component { - public state = { - selectedTabId: INDEX_PATTERNS_TAB_ID, - }; +export class SearchSelection extends React.Component { private fixedPageSize: number = 8; public render() { @@ -74,77 +52,43 @@ export class SearchSelection extends React.Component - {this.renderTabs()} - - - - {this.renderTab()} + true, + getIconForSavedObject: () => 'search', + name: i18n.translate( + 'kbn.visualize.newVisWizard.searchSelection.savedObjectType.search', + { + defaultMessage: 'Saved Search', + } + ), + }, + { + type: 'index-pattern', + showSavedObject: () => true, + getIconForSavedObject: () => 'indexPatternApp', + name: i18n.translate( + 'kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern', + { + defaultMessage: 'Index Pattern', + } + ), + }, + ]} + fixedPageSize={this.fixedPageSize} + /> ); } - - private onSelectedTabChanged = (tab: TabProps) => { - this.setState({ - selectedTabId: tab.id, - }); - }; - - private renderTabs() { - const tabs = [ - { - id: INDEX_PATTERNS_TAB_ID, - name: i18n.translate('kbn.visualize.newVisWizard.indexPatternTabLabel', { - defaultMessage: 'Index pattern', - }), - }, - { - id: SAVED_SEARCHES_TAB_ID, - name: i18n.translate('kbn.visualize.newVisWizard.savedSearchTabLabel', { - defaultMessage: 'Saved search', - }), - }, - ]; - const { selectedTabId } = this.state; - - return tabs.map(tab => ( - this.onSelectedTabChanged(tab)} - isSelected={tab.id === selectedTabId} - key={tab.id} - data-test-subj={`${tab.id}Tab`} - > - {tab.name} - - )); - } - - private renderTab() { - if (this.state.selectedTabId === SAVED_SEARCHES_TAB_ID) { - return ( - - ); - } - - return ( - - ); - } } diff --git a/test/functional/services/dashboard/add_panel.js b/test/functional/services/dashboard/add_panel.js index 18f5bea91d1a5..1bdd73fcf4d73 100644 --- a/test/functional/services/dashboard/add_panel.js +++ b/test/functional/services/dashboard/add_panel.js @@ -24,7 +24,6 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { const testSubjects = getService('testSubjects'); const flyout = getService('flyout'); const PageObjects = getPageObjects(['header', 'common']); - const find = getService('find'); return new class DashboardAddPanel { async clickOpenAddPanel() { @@ -36,14 +35,22 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { await testSubjects.click('addNewSavedObjectLink'); } - async clickSavedSearchTab() { - await testSubjects.click('addSavedSearchTab'); + async toggleFilterPopover() { + log.debug('DashboardAddPanel.openFilter'); + await testSubjects.click('savedObjectFinderFilterButton'); + } + + async toggleFilter(type) { + log.debug(`DashboardAddPanel.addToFilter(${type})`); + await this.toggleFilterPopover(); + await testSubjects.click(`savedObjectFinderFilter-${type}`); + await this.toggleFilterPopover(); } async addEveryEmbeddableOnCurrentPage() { log.debug('addEveryEmbeddableOnCurrentPage'); const addPanel = await testSubjects.find('dashboardAddPanel'); - const embeddableRows = await addPanel.findAllByClassName('euiLink'); + const embeddableRows = await addPanel.findAllByClassName('euiListGroupItem'); for (let i = 0; i < embeddableRows.length; i++) { await embeddableRows[i].click(); await PageObjects.common.closeToast(); @@ -95,10 +102,9 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { } } - async waitForEuiTableLoading() { + async waitForListLoading() { await retry.waitFor('dashboard add panel loading to complete', async () => { - const table = await find.byClassName('euiBasicTable'); - return !((await table.getAttribute('class')).includes('loading')); + return !(await testSubjects.exists('savedObjectFinderLoadingIndicator')); }); } @@ -109,6 +115,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { async addEveryVisualization(filter) { log.debug('DashboardAddPanel.addEveryVisualization'); await this.ensureAddPanelIsShowing(); + await this.toggleFilter('visualization'); if (filter) { await this.filterEmbeddableNames(filter.replace('-', ' ')); } @@ -123,7 +130,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { async addEverySavedSearch(filter) { log.debug('DashboardAddPanel.addEverySavedSearch'); await this.ensureAddPanelIsShowing(); - await this.clickSavedSearchTab(); + await this.toggleFilter('search'); if (filter) { await this.filterEmbeddableNames(filter.replace('-', ' ')); } @@ -139,11 +146,11 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { log.debug(`addSavedSearch(${searchName})`); await this.ensureAddPanelIsShowing(); - await this.clickSavedSearchTab(); + await this.toggleFilter('search'); await this.filterEmbeddableNames(searchName); await testSubjects.click(`savedObjectTitle${searchName.split(' ').join('-')}`); - await testSubjects.exists('addSavedSearchToDashboardSuccess'); + await testSubjects.exists('addObjectToDashboardSuccess'); await this.closeAddPanel(); } @@ -163,16 +170,18 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { async addVisualization(vizName) { log.debug(`DashboardAddPanel.addVisualization(${vizName})`); await this.ensureAddPanelIsShowing(); + await this.toggleFilter('visualization'); await this.filterEmbeddableNames(`"${vizName.replace('-', ' ')}"`); await testSubjects.click(`savedObjectTitle${vizName.split(' ').join('-')}`); + await testSubjects.exists('addObjectToDashboardSuccess'); await this.closeAddPanel(); } async filterEmbeddableNames(name) { // The search input field may be disabled while the table is loading so wait for it - await this.waitForEuiTableLoading(); + await this.waitForListLoading(); await testSubjects.setValue('savedObjectFinderSearchInput', name); - await this.waitForEuiTableLoading(); + await this.waitForListLoading(); } async panelAddLinkExists(name) { diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b7575863f4fbf..744890feb4cd9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -578,7 +578,6 @@ "common.ui.savedObjects.confirmModal.saveDuplicateButtonLabel": "保存“{name}”", "common.ui.savedObjects.confirmModal.saveDuplicateConfirmationMessage": "具有标题 “{title}” 的 “{name}” 已存在。是否确定要保存?", "common.ui.savedObjects.finder.searchPlaceholder": "搜索……", - "common.ui.savedObjects.finder.titleLabel": "标题", "common.ui.savedObjects.howToSaveAsNewDescription": "在 Kibana 的以前版本中,更改 {savedObjectName} 的名称将创建具有新名称的副本。使用“另存为新的 {savedObjectName}” 复选框可立即达到此目的。", "common.ui.savedObjects.overwriteRejectedDescription": "已拒绝覆盖确认", "common.ui.savedObjects.saveAsNewLabel": "另存为新的 {savedObjectName}", @@ -1220,11 +1219,6 @@ "kbn.dashboard.strings.dashboardEditTitle": "编辑 {title}", "kbn.dashboard.strings.dashboardUnsavedEditTitle": "编辑 {title}(未保存)", "kbn.dashboard.topNav.addPanel.addNewVisualizationButtonLabel": "添加新的可视化", - "kbn.dashboard.topNav.addPanel.savedSearchTabName": "已保存搜索", - "kbn.dashboard.topNav.addPanel.searchSavedObjectFinder.noMatchingVisualizationsMessage": "未找到匹配的已保存搜索。", - "kbn.dashboard.topNav.addPanel.selectedTabAddedToDashboardSuccessMessageTitle": "“{selectedTabName}” 已添加到您的仪表板", - "kbn.dashboard.topNav.addPanel.visSavedObjectFinder.noMatchingVisualizationsMessage": "未找到任何匹配的可视化。", - "kbn.dashboard.topNav.addPanel.visualizationTabName": "可视化", "kbn.dashboard.topNav.addPanelsTitle": "添加面板", "kbn.dashboard.topNav.cloneModal.cancelButtonLabel": "取消", "kbn.dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "克隆面板", From bcb630c296de86aebc5f3daf94c6224f942fc444 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 5 Mar 2019 11:24:11 +0100 Subject: [PATCH 04/31] fix test failures --- .../__snapshots__/add_panel.test.js.snap | 36 ++++--------------- .../dashboard/top_nav/add_panel.test.js | 5 +-- .../open_search_panel.test.js.snap | 18 +++++++--- .../components/saved_object_finder.test.tsx | 2 +- .../apps/dashboard/_dashboard_filtering.js | 3 -- test/functional/page_objects/discover_page.js | 4 +-- .../services/dashboard/add_panel.js | 9 +++-- 7 files changed, 31 insertions(+), 46 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap index 31e5edc76f1c2..f579d83c7c897 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap @@ -23,39 +23,16 @@ exports[`render 1`] = ` />

- - - Visualization - - - Saved Search - - @@ -64,13 +41,12 @@ exports[`render 1`] = ` id="kbn.dashboard.topNav.addPanel.addNewVisualizationButtonLabel" values={Object {}} /> - + } - key="visSavedObjectFinder" - noItemsMessage="No matching visualizations found." + noItemsMessage="No matching objects found." onChoose={[Function]} - savedObjectType="visualization" - visTypes={Object {}} + savedObjectMetaData={Array []} + showFilter={true} />
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js index 3f233eed6b100..eccf9198939e3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.test.js @@ -19,7 +19,7 @@ import React from 'react'; import sinon from 'sinon'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { shallow } from 'enzyme'; import { DashboardAddPanel, @@ -38,11 +38,12 @@ beforeEach(() => { }); test('render', () => { - const component = shallowWithIntl( {}} addNewVis={() => {}} + embeddableFactories={[]} />); expect(component).toMatchSnapshot(); }); 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/top_nav/__snapshots__/open_search_panel.test.js.snap index 05b8fae9ab1dd..e2b1923435658 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -28,11 +28,11 @@ exports[`render 1`] = ` /> @@ -41,9 +41,8 @@ exports[`render 1`] = ` id="kbn.discover.topNav.openSearchPanel.manageSearchesButtonLabel" values={Object {}} /> - + } - makeUrl={[Function]} noItemsMessage={ } onChoose={[Function]} - savedObjectType="search" + savedObjectMetaData={ + Array [ + Object { + "getIconForSavedObject": [Function], + "name": "Search", + "showSavedObject": [Function], + "type": "search", + }, + ] + } />
diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx index 1f91180e14baf..5a82b32c39943 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx @@ -121,7 +121,7 @@ describe('SavedObjectsFinder', () => { .find(EuiListGroupItem) .first() .simulate('click'); - expect(chooseStub.calledWith('1', 'search', 'Search')).toEqual(true); + expect(chooseStub.calledWith('1', 'search', `${doc.attributes.title} (Search)`)).toEqual(true); }); describe('sorting', () => { diff --git a/test/functional/apps/dashboard/_dashboard_filtering.js b/test/functional/apps/dashboard/_dashboard_filtering.js index 8742851249b7b..eca064ef04646 100644 --- a/test/functional/apps/dashboard/_dashboard_filtering.js +++ b/test/functional/apps/dashboard/_dashboard_filtering.js @@ -46,9 +46,6 @@ export default function ({ getService, getPageObjects }) { await dashboardAddPanel.addEveryVisualization('"Filter Bytes Test"'); await dashboardAddPanel.addEverySavedSearch('"Filter Bytes Test"'); - // TODO: Remove once https://github.com/elastic/kibana/issues/22561 is fixed - await dashboardPanelActions.removePanelByTitle('Filter Bytes Test: timelion split 5 on bytes'); - await dashboardAddPanel.closeAddPanel(); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.dashboard.waitForRenderComplete(); diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js index 3c2f36df21823..531fd9f984a0b 100644 --- a/test/functional/page_objects/discover_page.js +++ b/test/functional/page_objects/discover_page.js @@ -86,13 +86,13 @@ export function DiscoverPageProvider({ getService, getPageObjects }) { } async hasSavedSearch(searchName) { - const searchLink = await find.byPartialLinkText(searchName); + const searchLink = await find.byButtonText(searchName); return searchLink.isDisplayed(); } async loadSavedSearch(searchName) { await this.openLoadSavedSearchPanel(); - const searchLink = await find.byPartialLinkText(searchName); + const searchLink = await find.byButtonText(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 1bdd73fcf4d73..c589c4d57054f 100644 --- a/test/functional/services/dashboard/add_panel.js +++ b/test/functional/services/dashboard/add_panel.js @@ -42,9 +42,12 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { async toggleFilter(type) { log.debug(`DashboardAddPanel.addToFilter(${type})`); - await this.toggleFilterPopover(); - await testSubjects.click(`savedObjectFinderFilter-${type}`); - await this.toggleFilterPopover(); + const filterButtonExists = await testSubjects.exists('savedObjectFinderFilterButton'); + if (filterButtonExists) { + await this.toggleFilterPopover(); + await testSubjects.click(`savedObjectFinderFilter-${type}`); + await this.toggleFilterPopover(); + } } async addEveryEmbeddableOnCurrentPage() { From 7b2fee1cebd618f0533041353b09320afd7a3a09 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 5 Mar 2019 15:24:14 +0100 Subject: [PATCH 05/31] fix some functional tests and re-introduce makeUrl --- .../__snapshots__/open_search_panel.test.js.snap | 1 + .../public/discover/top_nav/open_search_panel.js | 1 + .../components/saved_object_finder.tsx | 16 ++++++++++++++-- test/functional/page_objects/common_page.js | 8 +++++++- test/functional/page_objects/visualize_page.js | 1 - 5 files changed, 23 insertions(+), 4 deletions(-) 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/top_nav/__snapshots__/open_search_panel.test.js.snap index e2b1923435658..88a199652da1c 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -43,6 +43,7 @@ exports[`render 1`] = ` /> } + makeUrl={[Function]} noItemsMessage={ diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 8bc8057dc5fbd..0af4c1f6c84e7 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -86,6 +86,7 @@ interface BaseSavedObjectFinder { type: SimpleSavedObject['type'], name: string ) => void; + makeUrl?: (id: SimpleSavedObject['id']) => string; noItemsMessage?: React.ReactNode; savedObjectMetaData: Array>; showFilter?: boolean; @@ -111,6 +112,7 @@ class SavedObjectFinder extends React.Component @@ -444,7 +446,17 @@ class SavedObjectFinder extends React.Component { + onChoose(item.id, item.type, fullName); + } + : undefined + } + href={makeUrl ? makeUrl(item.id) : undefined} + onMouseUp={ + // this is a workaround because onClick doesn't fire if a href props is provided + // can be removed as soon as this bug is fixed in EUI + onChoose && makeUrl ? () => { onChoose(item.id, item.type, fullName); } diff --git a/test/functional/page_objects/common_page.js b/test/functional/page_objects/common_page.js index 1c9ce3972c9c1..9962aa2049431 100644 --- a/test/functional/page_objects/common_page.js +++ b/test/functional/page_objects/common_page.js @@ -301,7 +301,13 @@ export function CommonPageProvider({ getService, getPageObjects }) { } async closeToast() { - const toast = await find.byCssSelector('.euiToast'); + let toast; + await retry.try(async () => { + toast = await find.byCssSelector('.euiToast'); + if (!toast) { + throw new Error('Toast is not visible yet'); + } + }); await browser.moveMouseTo(toast); const title = await (await find.byCssSelector('.euiToastHeader__title')).getVisibleText(); log.debug(title); diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index 7e7726a050405..c14e34687ddac 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -397,7 +397,6 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } async clickSavedSearch(savedSearchName) { - await testSubjects.click('savedSearchesTab'); await testSubjects.click(`savedObjectTitle${savedSearchName.split(' ').join('-')}`); await PageObjects.header.waitUntilLoadingHasFinished(); } From 21736aecc9f1e182c6b209b2d0531cb6a70442f9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 5 Mar 2019 17:54:33 +0100 Subject: [PATCH 06/31] fix tests --- .../public/saved_objects/components/saved_object_finder.tsx | 2 +- test/functional/page_objects/discover_page.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 0af4c1f6c84e7..8f2deb1c9df09 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -168,7 +168,7 @@ class SavedObjectFinder extends React.Component Date: Tue, 5 Mar 2019 18:40:04 +0100 Subject: [PATCH 07/31] remove direct hrefs in saved_object_lists --- .../__snapshots__/open_search_panel.test.js.snap | 1 - .../public/discover/top_nav/open_search_panel.js | 6 ++++-- .../components/saved_object_finder.tsx | 16 ++-------------- test/functional/page_objects/discover_page.js | 4 ++-- 4 files changed, 8 insertions(+), 19 deletions(-) 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/top_nav/__snapshots__/open_search_panel.test.js.snap index 88a199652da1c..e2b1923435658 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -43,7 +43,6 @@ exports[`render 1`] = ` /> } - makeUrl={[Function]} noItemsMessage={ { + window.open(this.props.makeUrl(id), '_self'); + this.props.onClose(); + }} callToActionButton={this.renderMangageSearchesButton()} /> diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 8f2deb1c9df09..94f0246faf9e2 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -86,7 +86,6 @@ interface BaseSavedObjectFinder { type: SimpleSavedObject['type'], name: string ) => void; - makeUrl?: (id: SimpleSavedObject['id']) => string; noItemsMessage?: React.ReactNode; savedObjectMetaData: Array>; showFilter?: boolean; @@ -112,7 +111,6 @@ class SavedObjectFinder extends React.Component @@ -446,17 +444,7 @@ class SavedObjectFinder extends React.Component { - onChoose(item.id, item.type, fullName); - } - : undefined - } - href={makeUrl ? makeUrl(item.id) : undefined} - onMouseUp={ - // this is a workaround because onClick doesn't fire if a href props is provided - // can be removed as soon as this bug is fixed in EUI - onChoose && makeUrl + onChoose ? () => { onChoose(item.id, item.type, fullName); } diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js index 3c2f36df21823..531fd9f984a0b 100644 --- a/test/functional/page_objects/discover_page.js +++ b/test/functional/page_objects/discover_page.js @@ -86,13 +86,13 @@ export function DiscoverPageProvider({ getService, getPageObjects }) { } async hasSavedSearch(searchName) { - const searchLink = await find.byPartialLinkText(searchName); + const searchLink = await find.byButtonText(searchName); return searchLink.isDisplayed(); } async loadSavedSearch(searchName) { await this.openLoadSavedSearchPanel(); - const searchLink = await find.byPartialLinkText(searchName); + const searchLink = await find.byButtonText(searchName); await searchLink.click(); await PageObjects.header.waitUntilLoadingHasFinished(); } From 6acb88e8866d6b95843236e70908bc627707fe1d Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 6 Mar 2019 15:06:51 +0100 Subject: [PATCH 08/31] PR review fixes --- .../embeddable/search_embeddable_factory.ts | 1 - .../discover/top_nav/open_search_panel.js | 8 ++++--- .../visualize_embeddable_factory_provider.ts | 2 +- .../search_selection/search_selection.tsx | 2 -- src/legacy/ui/public/registry/_registry.d.ts | 16 ++++++++++---- .../registry/chrome_header_nav_controls.ts | 17 ++++++++------- src/legacy/ui/public/registry/vis_types.ts | 10 +++++---- .../components/saved_object_finder.test.tsx | 5 ----- .../components/saved_object_finder.tsx | 21 +++++++++++-------- typings/@elastic/eui/index.d.ts | 3 +++ .../public/views/nav_control/nav_control.tsx | 1 - 11 files changed, 48 insertions(+), 38 deletions(-) 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 f755ca39b9461..d164b4246372e 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 @@ -42,7 +42,6 @@ export class SearchEmbeddableFactory extends EmbeddableFactory { }), type: 'search', getIconForSavedObject: () => 'search', - showSavedObject: () => true, }, }); } diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js index 49006360b8435..d70b5b86c90e6 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js +++ b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js @@ -21,6 +21,7 @@ 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 { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -81,13 +82,14 @@ export class OpenSearchPanel extends React.Component { savedObjectMetaData={[ { type: SEARCH_OBJECT_TYPE, - showSavedObject: () => true, getIconForSavedObject: () => 'search', - name: 'Search', + name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { + defaultMessage: 'Saved Search', + }), }, ]} onChoose={(id) => { - window.open(this.props.makeUrl(id), '_self'); + window.location.assign(this.props.makeUrl(id)); this.props.onClose(); }} callToActionButton={this.renderMangageSearchesButton()} diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts index abea267d6d149..dcdf58a52d918 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory_provider.ts @@ -32,7 +32,7 @@ export function visualizeEmbeddableFactoryProvider(Private: IPrivate) { return new VisualizeEmbeddableFactory( savedVisualizations, config, - Private(VisTypesRegistryProvider as any) + Private(VisTypesRegistryProvider) ); }; return Private(VisualizeEmbeddableFactoryProvider); diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx index f7119106afb70..dafd71732b18c 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx @@ -64,7 +64,6 @@ export class SearchSelection extends React.Component { savedObjectMetaData={[ { type: 'search', - showSavedObject: () => true, getIconForSavedObject: () => 'search', name: i18n.translate( 'kbn.visualize.newVisWizard.searchSelection.savedObjectType.search', @@ -75,7 +74,6 @@ export class SearchSelection extends React.Component { }, { type: 'index-pattern', - showSavedObject: () => true, getIconForSavedObject: () => 'indexPatternApp', name: i18n.translate( 'kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern', diff --git a/src/legacy/ui/public/registry/_registry.d.ts b/src/legacy/ui/public/registry/_registry.d.ts index 425ab45036519..9b95a2e02ee90 100644 --- a/src/legacy/ui/public/registry/_registry.d.ts +++ b/src/legacy/ui/public/registry/_registry.d.ts @@ -19,13 +19,21 @@ import { IndexedArray, IndexedArrayConfig } from '../indexed_array'; -interface UIRegistry extends IndexedArray { - register(privateModule: T): UIRegistry; -} +interface UIRegistry extends IndexedArray {} interface UIRegistrySpec extends IndexedArrayConfig { name: string; filter?(item: T): boolean; } -declare function uiRegistry(spec: UIRegistrySpec): UIRegistry; +/** + * Creates a new UiRegistry (See js method for detailed documentation) + * The generic type T is the type of objects which are stored in the registry. + * The generic type A is an interface of accessors which depend on the + * fields of the objects stored in the registry. + * Example: if there is a string field "name" in type T, then A should be + * `{ byName: { [typeName: string]: T }; }` + */ +declare function uiRegistry( + spec: UIRegistrySpec +): { (): UIRegistry & A; register(privateModule: T): UIRegistry & A }; diff --git a/src/legacy/ui/public/registry/chrome_header_nav_controls.ts b/src/legacy/ui/public/registry/chrome_header_nav_controls.ts index 5207113db6899..a626d013dde7b 100644 --- a/src/legacy/ui/public/registry/chrome_header_nav_controls.ts +++ b/src/legacy/ui/public/registry/chrome_header_nav_controls.ts @@ -21,17 +21,18 @@ import { NavControl } from '../chrome/directives/header_global_nav'; import { IndexedArray } from '../indexed_array'; import { uiRegistry, UIRegistry } from './_registry'; -interface BySideDictionary { - // this key should be from NavControlSide - [side: string]: IndexedArray; +interface ChromeHeaderNavControlsRegistryAccessors { + bySide: { [typeName: string]: IndexedArray }; } -export interface ChromeHeaderNavControlsRegistry extends UIRegistry { - bySide: BySideDictionary; -} +export type ChromeHeaderNavControlsRegistry = UIRegistry & + ChromeHeaderNavControlsRegistryAccessors; -export const chromeHeaderNavControlsRegistry: ChromeHeaderNavControlsRegistry = uiRegistry({ +export const chromeHeaderNavControlsRegistry = uiRegistry< + NavControl, + ChromeHeaderNavControlsRegistryAccessors +>({ name: 'chromeHeaderNavControls', order: ['order'], group: ['side'], -}) as ChromeHeaderNavControlsRegistry; +}); diff --git a/src/legacy/ui/public/registry/vis_types.ts b/src/legacy/ui/public/registry/vis_types.ts index 754d19624b8ba..7f4c2e96eee44 100644 --- a/src/legacy/ui/public/registry/vis_types.ts +++ b/src/legacy/ui/public/registry/vis_types.ts @@ -20,12 +20,14 @@ import { VisType } from '../vis'; import { uiRegistry, UIRegistry } from './_registry'; -export type VisTypesRegistry = UIRegistry & { +interface VisTypesRegistryAccessors { byName: { [typeName: string]: VisType }; -}; +} -export const VisTypesRegistryProvider = uiRegistry({ +export type VisTypesRegistry = UIRegistry & VisTypesRegistryAccessors; + +export const VisTypesRegistryProvider = uiRegistry({ name: 'visTypes', index: ['name'], order: ['title'], -}) as VisTypesRegistry; +}); diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx index 5a82b32c39943..4fe9d612defcd 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx @@ -228,13 +228,11 @@ describe('SavedObjectsFinder', () => { type: 'search', name: 'Search', getIconForSavedObject: () => 'search', - showSavedObject: () => true, }, { type: 'vis', name: 'Vis', getIconForSavedObject: () => 'visualization', - showSavedObject: () => true, }, ]} /> @@ -260,13 +258,11 @@ describe('SavedObjectsFinder', () => { type: 'search', name: 'Search', getIconForSavedObject: () => 'search', - showSavedObject: () => true, }, { type: 'vis', name: 'Vis', getIconForSavedObject: () => 'document', - showSavedObject: () => true, }, ]; @@ -467,7 +463,6 @@ describe('SavedObjectsFinder', () => { type: 'search', name: 'Search', getIconForSavedObject: () => 'search', - showSavedObject: () => true, }, ]} /> diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 94f0246faf9e2..dde65700f234f 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -27,15 +27,12 @@ import { EuiButtonEmpty, EuiEmptyPrompt, EuiFieldSearch, - // @ts-ignore EuiFilterGroup, - // @ts-ignore EuiFilterSelectItem, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiListGroup, - // @ts-ignore EuiListGroupItem, EuiLoadingSpinner, EuiPagination, @@ -53,13 +50,15 @@ import { SavedObjectAttributes } from '../../../../server/saved_objects'; import { SimpleSavedObject } from '../simple_saved_object'; // TODO the typings for EuiListGroup are incorrect - maxWidth is missing. This can be removed when the types are adjusted -const FixedEuiListGroup = (EuiListGroup as any) as React.SFC; +const FixedEuiListGroup = (EuiListGroup as any) as React.FunctionComponent< + CommonProps & { maxWidth: boolean } +>; export interface SavedObjectMetaData { type: string; name: string; getIconForSavedObject(savedObject: SimpleSavedObject): string | undefined; - showSavedObject(savedObject: SimpleSavedObject): boolean; + showSavedObject?(savedObject: SimpleSavedObject): boolean; } interface SavedObjectFinderState { @@ -128,10 +127,14 @@ class SavedObjectFinder extends React.Component - metaDataMap[savedObject.type] && metaDataMap[savedObject.type].showSavedObject(savedObject) - ); + resp.savedObjects = resp.savedObjects.filter(savedObject => { + const metaData = metaDataMap[savedObject.type]; + if (metaData.showSavedObject) { + return metaData.showSavedObject(savedObject); + } else { + return true; + } + }); if (!this.isComponentMounted) { return; diff --git a/typings/@elastic/eui/index.d.ts b/typings/@elastic/eui/index.d.ts index fcdc481c17770..cf2919fa8abab 100644 --- a/typings/@elastic/eui/index.d.ts +++ b/typings/@elastic/eui/index.d.ts @@ -25,6 +25,9 @@ declare module '@elastic/eui' { export const EuiCopy: React.SFC; export const EuiOutsideClickDetector: React.SFC; export const EuiSideNav: React.SFC; + export const EuiFilterGroup: React.FunctionComponent; + export const EuiFilterSelectItem: React.FunctionComponent; + export const EuiListGroupItem: React.FunctionComponent; export interface EuiTableCriteria { page: { index: number; size: number }; diff --git a/x-pack/plugins/spaces/public/views/nav_control/nav_control.tsx b/x-pack/plugins/spaces/public/views/nav_control/nav_control.tsx index 93efcdce0295a..6385e74280a12 100644 --- a/x-pack/plugins/spaces/public/views/nav_control/nav_control.tsx +++ b/x-pack/plugins/spaces/public/views/nav_control/nav_control.tsx @@ -19,7 +19,6 @@ import { NavControlSide } from 'ui/chrome/directives/header_global_nav'; import { I18nContext } from 'ui/i18n'; // @ts-ignore import { uiModules } from 'ui/modules'; -// @ts-ignore import { chromeHeaderNavControlsRegistry } from 'ui/registry/chrome_header_nav_controls'; // @ts-ignore import { chromeNavControlsRegistry } from 'ui/registry/chrome_nav_controls'; From d024b52f45b1a9ae4d278f11a97cc67b3cab70fe Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 6 Mar 2019 17:19:50 +0100 Subject: [PATCH 09/31] update snapshot --- .../top_nav/__snapshots__/open_search_panel.test.js.snap | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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/top_nav/__snapshots__/open_search_panel.test.js.snap index e2b1923435658..0bd3906552001 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -55,8 +55,7 @@ exports[`render 1`] = ` Array [ Object { "getIconForSavedObject": [Function], - "name": "Search", - "showSavedObject": [Function], + "name": "Saved Search", "type": "search", }, ] From d8823ccf3d4041ef4acd27a69d393c7e6e26acf3 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 6 Mar 2019 17:28:13 +0100 Subject: [PATCH 10/31] overwrite width of viz dialog --- .../core_plugins/kibana/public/visualize/wizard/_dialog.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss b/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss index 01ecbd9ff5197..ce18d3df67946 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss @@ -7,6 +7,7 @@ } .visNewVisSearchDialog { + min-width: $euiSizeL * 30; min-height: $euiSizeL * 20; } @@ -91,6 +92,10 @@ .visNewVisDialog__description { display: none; } + + .visNewVisSearchDialog { + min-width: 0; + } } @include internetExplorerOnly { From a4c202063a9a7190ab10a2e57493bd800c5db0a3 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 08:22:50 +0100 Subject: [PATCH 11/31] Update src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js Co-Authored-By: flash1293 --- .../core_plugins/kibana/public/dashboard/top_nav/add_panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js index cfad25e2ca1a3..30f7c0ae26998 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js @@ -66,7 +66,7 @@ export class DashboardAddPanel extends React.Component { > ); From ad643682acae8cdf038cba5eac00baf3049fae43 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 08:23:05 +0100 Subject: [PATCH 12/31] Update src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts Co-Authored-By: flash1293 --- .../public/discover/embeddable/search_embeddable_factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d164b4246372e..17a5afb749f2e 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 @@ -38,7 +38,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory { name: 'search', savedObjectMetaData: { name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { - defaultMessage: 'Saved Search', + defaultMessage: 'Saved search', }), type: 'search', getIconForSavedObject: () => 'search', From e7fc38853aa1693d71195b76d74ec6471597668a Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 08:23:19 +0100 Subject: [PATCH 13/31] Update src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js Co-Authored-By: flash1293 --- .../kibana/public/discover/top_nav/open_search_panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js index d70b5b86c90e6..4fbe90cf76ae8 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js +++ b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js @@ -84,7 +84,7 @@ export class OpenSearchPanel extends React.Component { type: SEARCH_OBJECT_TYPE, getIconForSavedObject: () => 'search', name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { - defaultMessage: 'Saved Search', + defaultMessage: 'Saved search', }), }, ]} From 3dc0474a4661fab935111c888173c286fb8bbc3d Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 08:23:47 +0100 Subject: [PATCH 14/31] Update src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx Co-Authored-By: flash1293 --- .../visualize/wizard/search_selection/search_selection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx index dafd71732b18c..29a48575078c8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx @@ -58,7 +58,7 @@ export class SearchSelection extends React.Component { noItemsMessage={i18n.translate( 'kbn.visualize.newVisWizard.searchSelection.notFoundLabel', { - defaultMessage: 'No matching indices and saved searches found.', + defaultMessage: 'No matching indices or saved searches found.', } )} savedObjectMetaData={[ From d9bd3713f50391a12eb77c4720fc7a590955d163 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 08:24:08 +0100 Subject: [PATCH 15/31] Update src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx Co-Authored-By: flash1293 --- .../visualize/wizard/search_selection/search_selection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx index 29a48575078c8..5f5deb5f91106 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx @@ -68,7 +68,7 @@ export class SearchSelection extends React.Component { name: i18n.translate( 'kbn.visualize.newVisWizard.searchSelection.savedObjectType.search', { - defaultMessage: 'Saved Search', + defaultMessage: 'Saved search', } ), }, From 59f6a7eba0fe45cc7aaa07787db0c240c80c0e08 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 08:24:24 +0100 Subject: [PATCH 16/31] Update src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx Co-Authored-By: flash1293 --- .../visualize/wizard/search_selection/search_selection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx index 5f5deb5f91106..91e4acd281b94 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx @@ -78,7 +78,7 @@ export class SearchSelection extends React.Component { name: i18n.translate( 'kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern', { - defaultMessage: 'Index Pattern', + defaultMessage: 'Index pattern', } ), }, From f4934e5e97c9b32547df304d401ac6e65c0674db Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 11:11:44 +0100 Subject: [PATCH 17/31] fix tests --- .../__snapshots__/add_panel.test.js.snap | 47 +++++++++--------- .../open_search_panel.test.js.snap | 49 ++++++++++--------- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap index f579d83c7c897..d7c586cda87f8 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap @@ -10,44 +10,45 @@ exports[`render 1`] = ` ownFocus={true} size="m" > - + -

+

-

+
- +
+ - - - } noItemsMessage="No matching objects found." onChoose={[Function]} savedObjectMetaData={Array []} showFilter={true} /> + + + + + `; 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/top_nav/__snapshots__/open_search_panel.test.js.snap index 0bd3906552001..c75a2b08fdf5a 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -10,39 +10,24 @@ exports[`render 1`] = ` ownFocus={true} size="m" > - + -

+

-

+
- +
+ - - - } noItemsMessage={ + + + + + `; From b37ad87b4bfad05ec8eb0658b2042a4c00763e15 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 11:18:31 +0100 Subject: [PATCH 18/31] review fixes #1 --- .../public/dashboard/top_nav/add_panel.js | 45 ++++--- .../discover/top_nav/open_search_panel.js | 116 ++++++++---------- .../public/visualize/wizard/_dialog.scss | 6 +- .../components/saved_object_finder.tsx | 57 ++++----- 4 files changed, 102 insertions(+), 122 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js index 30f7c0ae26998..ba626b474b50d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js @@ -26,9 +26,10 @@ import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_find import { EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutFooter, EuiFlyoutBody, - EuiButtonEmpty, - EuiSpacer, + EuiButton, EuiTitle, } from '@elastic/eui'; @@ -57,41 +58,25 @@ export class DashboardAddPanel extends React.Component { }; render() { - const addNewVisBtn = ( - - - - ); - return ( - - -

+ + +

-

+

- - - + + Boolean(embeddableFactory.savedObjectMetaData)) .map(({ savedObjectMetaData }) => savedObjectMetaData)} showFilter={true} - callToActionButton={addNewVisBtn} noItemsMessage={i18n.translate( 'kbn.dashboard.topNav.addPanel.noMatchingObjectsMessage', { @@ -100,6 +85,18 @@ export class DashboardAddPanel extends React.Component { )} /> + + + + +
); } diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js index 4fbe90cf76ae8..f06e49aa9c24f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js +++ b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js @@ -25,80 +25,66 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiSpacer, + EuiButton, EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutFooter, EuiFlyoutBody, EuiTitle, - EuiButtonEmpty, } from '@elastic/eui'; const SEARCH_OBJECT_TYPE = 'search'; -export class OpenSearchPanel extends React.Component { - - renderMangageSearchesButton() { - return ( - - + + +

+ +

+
+
+ + + } + savedObjectMetaData={[ + { + type: SEARCH_OBJECT_TYPE, + getIconForSavedObject: () => 'search', + name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { + defaultMessage: 'Saved search', + }), + }, + ]} + onChoose={id => { + window.location.assign(props.makeUrl(id)); + props.onClose(); + }} /> -
- ); - } - - render() { - return ( - - - - -

- -

-
- - - - - } - savedObjectMetaData={[ - { - type: SEARCH_OBJECT_TYPE, - getIconForSavedObject: () => 'search', - name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { - defaultMessage: 'Saved search', - }), - }, - ]} - onChoose={(id) => { - window.location.assign(this.props.makeUrl(id)); - this.props.onClose(); - }} - callToActionButton={this.renderMangageSearchesButton()} +
+ + + - -
- - ); - } + + + + ); } OpenSearchPanel.propTypes = { diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss b/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss index ce18d3df67946..fc6a982e08ee2 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss @@ -7,7 +7,7 @@ } .visNewVisSearchDialog { - min-width: $euiSizeL * 30; + width: $euiSizeL * 30; min-height: $euiSizeL * 20; } @@ -92,10 +92,6 @@ .visNewVisDialog__description { display: none; } - - .visNewVisSearchDialog { - min-width: 0; - } } @include internetExplorerOnly { diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index dde65700f234f..3b0fb9bf9250f 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -462,34 +462,35 @@ class SavedObjectFinder extends React.Component )} - {this.props.fixedPageSize ? ( - { - this.setState({ - page, - }); - }} - /> - ) : ( - { - this.setState({ - page, - }); - }} - onChangeItemsPerPage={perPage => { - this.setState({ - perPage, - }); - }} - itemsPerPage={this.state.perPage} - itemsPerPageOptions={[5, 10, 15, 25]} - /> - )} + {this.getPageCount() > 1 && + (this.props.fixedPageSize ? ( + { + this.setState({ + page, + }); + }} + /> + ) : ( + { + this.setState({ + page, + }); + }} + onChangeItemsPerPage={perPage => { + this.setState({ + perPage, + }); + }} + itemsPerPage={this.state.perPage} + itemsPerPageOptions={[5, 10, 15, 25]} + /> + ))} ); } From d6566712e411ce218a111b1bb0596dab1bbb72ea Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 13:09:31 +0100 Subject: [PATCH 19/31] review fixes #2 --- .../visualize_embeddable_factory.ts | 4 + .../components/saved_object_finder.scss | 4 +- .../components/saved_object_finder.tsx | 249 ++++++++++-------- typings/@elastic/eui/index.d.ts | 1 + 4 files changed, 142 insertions(+), 116 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts index f6d8c70129825..ef1debdb218e9 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_factory.ts @@ -53,6 +53,10 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory { + const visType = visTypes.byName[JSON.parse(savedObject.attributes.visState).type].title; + return `${savedObject.attributes.title} (${visType})`; + }, showSavedObject: savedObject => { if (chrome.getUiSettingsClient().get('visualize:enableLabs')) { return true; diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss b/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss index 61da8f980ed68..6457e0a9ad0e4 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss @@ -1,3 +1,3 @@ -.savedObjectFilterHeading:hover { - text-decoration: none; +.savedObjectFinderLoadingBar { + margin-top: -1 * ($euiSizeXS / 2); } \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 3b0fb9bf9250f..9321134736925 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -24,27 +24,26 @@ import chrome from 'ui/chrome'; import { CommonProps, - EuiButtonEmpty, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiContextMenuPanelProps, EuiEmptyPrompt, EuiFieldSearch, + EuiFilterButton, EuiFilterGroup, - EuiFilterSelectItem, EuiFlexGroup, EuiFlexItem, - EuiIcon, EuiListGroup, EuiListGroupItem, EuiLoadingSpinner, EuiPagination, EuiPopover, + EuiProgress, EuiSpacer, EuiTablePagination, - EuiText, - EuiToolTip, } from '@elastic/eui'; import { Direction } from '@elastic/eui/src/services/sort/sort_direction'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import { SavedObjectAttributes } from '../../../../server/saved_objects'; import { SimpleSavedObject } from '../simple_saved_object'; @@ -54,10 +53,16 @@ const FixedEuiListGroup = (EuiListGroup as any) as React.FunctionComponent< CommonProps & { maxWidth: boolean } >; +// TODO the typings for EuiContextMenuPanel are incorrect - watchedItemProps is missing. This can be removed when the types are adjusted +const FixedEuiContextMenuPanel = (EuiContextMenuPanel as any) as React.FunctionComponent< + EuiContextMenuPanelProps & { watchedItemProps: string[] } +>; + export interface SavedObjectMetaData { type: string; name: string; getIconForSavedObject(savedObject: SimpleSavedObject): string | undefined; + getTooltipForSavedObject?(savedObject: SimpleSavedObject): string; showSavedObject?(savedObject: SimpleSavedObject): boolean; } @@ -68,12 +73,12 @@ interface SavedObjectFinderState { type: SimpleSavedObject['type']; savedObject: SimpleSavedObject; }>; - filter: string; + query: string; isFetchingItems: boolean; page: number; perPage: number; - sortField?: string; sortDirection?: Direction; + sortOpen: boolean; filterOpen: boolean; filteredTypes: string[]; } @@ -114,13 +119,13 @@ class SavedObjectFinder extends React.Component { + private debouncedFetch = _.debounce(async (query: string) => { const metaDataMap = this.getSavedObjectMetaDataMap(); const resp = await chrome.getSavedObjectsClient().find({ type: Object.keys(metaDataMap), fields: ['title', 'visState'], - search: filter ? `${filter}*` : undefined, + search: query ? `${query}*` : undefined, page: 1, perPage: chrome.getUiSettingsClient().get('savedObjects:listingLimit'), searchFields: ['title^3', 'description'], @@ -142,7 +147,7 @@ class SavedObjectFinder extends React.Component { @@ -170,9 +175,10 @@ class SavedObjectFinder extends React.Component { let order = 1; if (sortDirection === 'desc') { @@ -250,7 +256,7 @@ class SavedObjectFinder extends React.Component typesInItems.has(metaData.type)); } - private getCurrentSort() { - return this.state.sortDirection || (this.state.filter ? 'auto' : 'asc'); - } - - private getCurrentSortIcon() { - switch (this.getCurrentSort()) { - case 'asc': - return 'sortUp'; - case 'desc': - return 'sortDown'; - case 'auto': - return 'search'; - } - } - - private getCurrentSortTooltip() { - switch (this.getCurrentSort()) { - case 'asc': - return i18n.translate('common.ui.savedObjects.finder.sortAsc', { - defaultMessage: 'Sort ascending', - }); - case 'desc': - return i18n.translate('common.ui.savedObjects.finder.sortDesc', { - defaultMessage: 'Sort descending', - }); - case 'auto': - return i18n.translate('common.ui.savedObjects.finder.sortAuto', { - defaultMessage: 'Sort by best match', - }); - } - } - - private getNextDirection(dir: Direction | undefined, hasFilter: boolean) { - switch (dir) { - case 'asc': - return 'desc'; - case 'desc': - return hasFilter ? undefined : 'asc'; - default: - return hasFilter ? 'asc' : 'desc'; + private getSortOptions() { + const sortOptions = [ + { + this.setState({ + sortDirection: 'asc', + }); + }} + > + {i18n.translate('common.ui.savedObjects.finder.sortAsc', { + defaultMessage: 'Ascending', + })} + , + { + this.setState({ + sortDirection: 'desc', + }); + }} + > + {i18n.translate('common.ui.savedObjects.finder.sortDesc', { + defaultMessage: 'Descending', + })} + , + ]; + if (this.state.query) { + sortOptions.push( + { + this.setState({ + sortDirection: undefined, + }); + }} + > + {i18n.translate('common.ui.savedObjects.finder.sortAuto', { + defaultMessage: 'Best match', + })} + + ); } + return sortOptions; } private renderSearchBar() { @@ -316,76 +333,83 @@ class SavedObjectFinder extends React.Component { this.setState( { - filter: e.target.value, + query: e.target.value, }, this.fetchItems ); }} data-test-subj="savedObjectFinderSearchInput" /> + {this.state.isFetchingItems && ( + + )} - - - this.setState(({ sortDirection, filter }) => ({ - sortDirection: this.getNextDirection(sortDirection, Boolean(filter)), - })) - } - > - - - - + this.setState({ sortOpen: false })} + button={ + + this.setState(({ sortOpen }) => ({ + sortOpen: !sortOpen, + })) + } + iconType="arrowDown" + isSelected={this.state.sortOpen} + data-test-subj="savedObjectFinderSortButton" + > + {i18n.translate('common.ui.savedObjects.finder.sortButtonLabel', { + defaultMessage: 'Sort', + })} + + } + > + + {this.props.showFilter && availableSavedObjectMetaData.length > 1 && ( this.setState({ filterOpen: false })} button={ - this.setState(({ filterOpen }) => ({ filterOpen: !filterOpen, })) } - iconSide="right" - iconType="filter" - size="s" - color="text" + iconType="arrowDown" data-test-subj="savedObjectFinderFilterButton" + isSelected={this.state.filterOpen} + numFilters={ + this.state.filteredTypes.length > 0 + ? this.state.filteredTypes.length + : undefined + } > - - + {i18n.translate('common.ui.savedObjects.finder.filterButtonLabel', { + defaultMessage: 'Types', + })} + } > -
- - - - {availableSavedObjectMetaData.map(metaData => ( - ( + { this.setState(({ filteredTypes }) => ({ @@ -397,9 +421,9 @@ class SavedObjectFinder extends React.Component {metaData.name} - + ))} -
+ />
)}
@@ -409,32 +433,29 @@ class SavedObjectFinder extends React.Component - - - - -
- ); - } - private renderListing() { const items = this.state.items.length === 0 ? [] : this.getPageOfItems(); const { onChoose, savedObjectMetaData } = this.props; return ( <> - {this.state.isFetchingItems ? ( - this.renderLoadingIndicator() - ) : items.length > 0 ? ( + {this.state.isFetchingItems && this.state.items.length === 0 && ( + + + + + + + )} + {items.length > 0 ? ( {items.map(item => { const currentSavedObjectMetaData = savedObjectMetaData.find( metaData => metaData.type === item.type - ); - const fullName = `${item.title} (${currentSavedObjectMetaData!.name})`; + )!; + const fullName = currentSavedObjectMetaData.getTooltipForSavedObject + ? currentSavedObjectMetaData.getTooltipForSavedObject(item.savedObject) + : `${item.title} (${currentSavedObjectMetaData!.name})`; const iconType = ( currentSavedObjectMetaData || ({ @@ -460,7 +481,7 @@ class SavedObjectFinder extends React.Component ) : ( - + !this.state.isFetchingItems && )} {this.getPageCount() > 1 && (this.props.fixedPageSize ? ( diff --git a/typings/@elastic/eui/index.d.ts b/typings/@elastic/eui/index.d.ts index cf2919fa8abab..1d593e845dcf0 100644 --- a/typings/@elastic/eui/index.d.ts +++ b/typings/@elastic/eui/index.d.ts @@ -28,6 +28,7 @@ declare module '@elastic/eui' { export const EuiFilterGroup: React.FunctionComponent; export const EuiFilterSelectItem: React.FunctionComponent; export const EuiListGroupItem: React.FunctionComponent; + export const EuiFilterButton: React.FunctionComponent; export interface EuiTableCriteria { page: { index: number; size: number }; From df6dbf0c3c93c0be576e6627f726e9981177c441 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 13:19:01 +0100 Subject: [PATCH 20/31] dont use classname in functional test --- .../public/saved_objects/components/saved_object_finder.tsx | 2 +- test/functional/services/dashboard/add_panel.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 9321134736925..59652032dd16e 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -448,7 +448,7 @@ class SavedObjectFinder extends React.Component )} {items.length > 0 ? ( - + {items.map(item => { const currentSavedObjectMetaData = savedObjectMetaData.find( metaData => metaData.type === item.type diff --git a/test/functional/services/dashboard/add_panel.js b/test/functional/services/dashboard/add_panel.js index c589c4d57054f..96df6fbbe414b 100644 --- a/test/functional/services/dashboard/add_panel.js +++ b/test/functional/services/dashboard/add_panel.js @@ -52,8 +52,8 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { async addEveryEmbeddableOnCurrentPage() { log.debug('addEveryEmbeddableOnCurrentPage'); - const addPanel = await testSubjects.find('dashboardAddPanel'); - const embeddableRows = await addPanel.findAllByClassName('euiListGroupItem'); + const itemList = await testSubjects.find('savedObjectFinderItemList'); + const embeddableRows = await itemList.findAllByCssSelector('li'); for (let i = 0; i < embeddableRows.length; i++) { await embeddableRows[i].click(); await PageObjects.common.closeToast(); From bed5cba703444e8dacf1edeb0b8ddae809690185 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 13:23:50 +0100 Subject: [PATCH 21/31] remove call to action button prop --- .../components/saved_object_finder.test.tsx | 9 --------- .../saved_objects/components/saved_object_finder.tsx | 3 --- 2 files changed, 12 deletions(-) diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx index 4fe9d612defcd..79b581489bfaa 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx @@ -325,15 +325,6 @@ describe('SavedObjectsFinder', () => { }); }); - it('should display passed in action button', () => { - const actionButton = ; - const wrapper = shallow( - - ); - - expect(wrapper.contains(actionButton)).toBe(true); - }); - it('should display no items message if there are no items', async () => { objectsClientStub.returns(Promise.resolve({ savedObjects: [] })); const noItemsMessage = ; diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 59652032dd16e..519bcccb68a5b 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -84,7 +84,6 @@ interface SavedObjectFinderState { } interface BaseSavedObjectFinder { - callToActionButton?: React.ReactNode; onChoose?: ( id: SimpleSavedObject['id'], type: SimpleSavedObject['type'], @@ -108,7 +107,6 @@ type SavedObjectFinderProps = SavedObjectFinderFixedPage | SavedObjectFinderInit class SavedObjectFinder extends React.Component { public static propTypes = { - callToActionButton: PropTypes.node, onChoose: PropTypes.func, noItemsMessage: PropTypes.node, savedObjectMetaData: PropTypes.array.isRequired, @@ -428,7 +426,6 @@ class SavedObjectFinder extends React.Component - {this.props.callToActionButton} ); } From 360dac06ceb76b973fce77b3f0c27df8e809571e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 13:43:05 +0100 Subject: [PATCH 22/31] align buttons correctly --- .../public/dashboard/top_nav/add_panel.js | 22 +++++++++------- .../discover/top_nav/open_search_panel.js | 26 ++++++++++++------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js index ba626b474b50d..6e37f353b135b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js @@ -25,6 +25,8 @@ import { toastNotifications } from 'ui/notify'; import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; import { + EuiFlexGroup, + EuiFlexItem, EuiFlyout, EuiFlyoutHeader, EuiFlyoutFooter, @@ -86,16 +88,16 @@ export class DashboardAddPanel extends React.Component { /> - - - + + + + + + + ); diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js index f06e49aa9c24f..8e43ab703e153 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js +++ b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js @@ -26,6 +26,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, + EuiFlexGroup, + EuiFlexItem, EuiFlyout, EuiFlyoutHeader, EuiFlyoutFooter, @@ -72,16 +74,20 @@ export function OpenSearchPanel(props) { /> - - - + + + + + + + ); From 0d1c8653c4e6aa7697426290c9042cca665dfa6c Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 15:01:41 +0100 Subject: [PATCH 23/31] fix tests --- .../__snapshots__/add_panel.test.js.snap | 41 +++++++++++++------ .../open_search_panel.test.js.snap | 41 +++++++++++++------ .../components/saved_object_finder.test.tsx | 31 +++++--------- 3 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap index d7c586cda87f8..a3347e6228e38 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap @@ -35,20 +35,35 @@ exports[`render 1`] = ` /> - - - + + + + + + `; 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/top_nav/__snapshots__/open_search_panel.test.js.snap index c75a2b08fdf5a..997d819dfd3cd 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -48,20 +48,35 @@ exports[`render 1`] = ` /> - - - + + + + + + `; diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx index 79b581489bfaa..3159ca5588869 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx @@ -32,7 +32,6 @@ const nextTick = () => new Promise(res => process.nextTick(res)); import { EuiEmptyPrompt, EuiListGroup, - // @ts-ignore EuiListGroupItem, EuiLoadingSpinner, EuiPagination, @@ -136,16 +135,13 @@ describe('SavedObjectsFinder', () => { expect(list.childAt(1).key()).toBe('1'); }); - it('should list items descending after sort click', async () => { + it('should list items descending', async () => { objectsClientStub.returns(Promise.resolve({ savedObjects: [doc, doc2] })); const wrapper = shallow(); wrapper.instance().componentDidMount!(); await nextTick(); - wrapper - .find('[data-test-subj="savedObjectFinderSortButton"]') - .first() - .simulate('click'); + wrapper.setState({ sortDirection: 'desc' }); const list = wrapper.find(EuiListGroup); expect(list.childAt(0).key()).toBe('1'); expect(list.childAt(1).key()).toBe('2'); @@ -169,10 +165,6 @@ describe('SavedObjectsFinder', () => { ); wrapper.instance().componentDidMount!(); await nextTick(); - wrapper - .find('[data-test-subj="savedObjectFinderSortButton"]') - .first() - .simulate('click'); const list = wrapper.find(EuiListGroup); expect(list.childAt(0).key()).toBe('2'); expect(list.children().length).toBe(1); @@ -309,18 +301,12 @@ describe('SavedObjectsFinder', () => { ); wrapper.instance().componentDidMount!(); await nextTick(); - wrapper - .find('[data-test-subj="savedObjectFinderFilter-vis"]') - .first() - .simulate('click'); + wrapper.setState({ filteredTypes: ['vis'] }); const list = wrapper.find(EuiListGroup); expect(list.childAt(0).key()).toBe('3'); expect(list.children().length).toBe(1); - wrapper - .find('[data-test-subj="savedObjectFinderFilter-search"]') - .first() - .simulate('click'); + wrapper.setState({ filteredTypes: ['vis', 'search'] }); expect(wrapper.find(EuiListGroup).children().length).toBe(3); }); }); @@ -438,7 +424,7 @@ describe('SavedObjectsFinder', () => { }); describe('loading state', () => { - it('should display a spinner during loading', () => { + it('should display a spinner during initial loading', () => { const wrapper = shallow(); expect(wrapper.containsMatchingElement()).toBe(true); @@ -463,7 +449,7 @@ describe('SavedObjectsFinder', () => { expect(wrapper.containsMatchingElement()).toBe(false); }); - it('should show the spinner again on search input', async () => { + it('should not show the spinner if there are already items', async () => { objectsClientStub.returns(Promise.resolve({ savedObjects: [doc] })); const wrapper = shallow(); @@ -474,7 +460,10 @@ describe('SavedObjectsFinder', () => { .first() .simulate('change', { target: { value: 'abc' } }); - expect(wrapper.containsMatchingElement()).toBe(true); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + + expect(wrapper.containsMatchingElement()).toBe(false); }); }); }); From 09f39b6fb4fbb0b642c5228afb721bc73dc2e0fb Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 16:03:26 +0100 Subject: [PATCH 24/31] remove debugging statement --- .../public/saved_objects/components/saved_object_finder.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx index 3159ca5588869..09412322a5558 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.test.tsx @@ -461,7 +461,6 @@ describe('SavedObjectsFinder', () => { .simulate('change', { target: { value: 'abc' } }); wrapper.update(); - expect(wrapper).toMatchSnapshot(); expect(wrapper.containsMatchingElement()).toBe(false); }); From 0f0abbbb49a528dee76fae0b22bd5abbb5b8ccf6 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 16:33:00 +0100 Subject: [PATCH 25/31] Update src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js Co-Authored-By: flash1293 --- .../core_plugins/kibana/public/dashboard/top_nav/add_panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js index 6e37f353b135b..1efd9c2a7aae8 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js @@ -67,7 +67,7 @@ export class DashboardAddPanel extends React.Component {

From 8a105ceead209a1d76e6359d9b22052e4fac777c Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 8 Mar 2019 16:33:21 +0100 Subject: [PATCH 26/31] Update src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js Co-Authored-By: flash1293 --- .../kibana/public/discover/top_nav/open_search_panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js index 8e43ab703e153..aee33d9dcb604 100644 --- a/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js +++ b/src/legacy/core_plugins/kibana/public/discover/top_nav/open_search_panel.js @@ -45,7 +45,7 @@ export function OpenSearchPanel(props) {

From 6f7320e7ea9d9a88567560d331cdfd38d676a9db Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 16:39:08 +0100 Subject: [PATCH 27/31] review fixes #3 --- .../dashboard/top_nav/__snapshots__/add_panel.test.js.snap | 2 +- .../top_nav/__snapshots__/open_search_panel.test.js.snap | 2 +- src/legacy/ui/public/_index.scss | 1 - src/legacy/ui/public/saved_objects/_index.scss | 1 - src/legacy/ui/public/saved_objects/components/_index.scss | 1 - .../public/saved_objects/components/saved_object_finder.scss | 3 --- .../public/saved_objects/components/saved_object_finder.tsx | 5 +---- 7 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 src/legacy/ui/public/saved_objects/_index.scss delete mode 100644 src/legacy/ui/public/saved_objects/components/_index.scss delete mode 100644 src/legacy/ui/public/saved_objects/components/saved_object_finder.scss diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap index a3347e6228e38..a959aab55cc94 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap @@ -19,7 +19,7 @@ exports[`render 1`] = ` >

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/top_nav/__snapshots__/open_search_panel.test.js.snap index 997d819dfd3cd..6c4eaa7f2f4c7 100644 --- 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/top_nav/__snapshots__/open_search_panel.test.js.snap @@ -19,7 +19,7 @@ exports[`render 1`] = ` >

diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index 79913b66ee09d..f6f6314edcc01 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -28,7 +28,6 @@ @import './partials/index'; @import './query_bar/index'; @import './share/index'; -@import './saved_objects/index'; @import './filter_bar/index'; @import './style_compile/index'; diff --git a/src/legacy/ui/public/saved_objects/_index.scss b/src/legacy/ui/public/saved_objects/_index.scss deleted file mode 100644 index 30ac0c9fe9b27..0000000000000 --- a/src/legacy/ui/public/saved_objects/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './components/index'; \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/_index.scss b/src/legacy/ui/public/saved_objects/components/_index.scss deleted file mode 100644 index a876f055d0e40..0000000000000 --- a/src/legacy/ui/public/saved_objects/components/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './saved_object_finder'; \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss b/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss deleted file mode 100644 index 6457e0a9ad0e4..0000000000000 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.scss +++ /dev/null @@ -1,3 +0,0 @@ -.savedObjectFinderLoadingBar { - margin-top: -1 * ($euiSizeXS / 2); -} \ No newline at end of file diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 519bcccb68a5b..73ee231752833 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -38,7 +38,6 @@ import { EuiLoadingSpinner, EuiPagination, EuiPopover, - EuiProgress, EuiSpacer, EuiTablePagination, } from '@elastic/eui'; @@ -341,10 +340,8 @@ class SavedObjectFinder extends React.Component - {this.state.isFetchingItems && ( - - )} From b512467bfe05ab4c3532548d60b84a4ab1710a90 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 20:14:16 +0100 Subject: [PATCH 28/31] improve filter behavior and enable it for search wizard --- .../kibana/public/visualize/wizard/_dialog.scss | 2 +- .../wizard/search_selection/search_selection.tsx | 1 + .../components/saved_object_finder.tsx | 13 ++++++------- typings/@elastic/eui/index.d.ts | 3 --- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss b/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss index fc6a982e08ee2..829c18c3644f0 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/_dialog.scss @@ -8,7 +8,7 @@ .visNewVisSearchDialog { width: $euiSizeL * 30; - min-height: $euiSizeL * 20; + min-height: $euiSizeL * 25; } .visNewVisDialog__body { diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx index 91e4acd281b94..34c95b43991e8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/search_selection/search_selection.tsx @@ -55,6 +55,7 @@ export class SearchSelection extends React.Component { - {this.props.showFilter && availableSavedObjectMetaData.length > 1 && ( + {this.props.showFilter && ( 0 - ? this.state.filteredTypes.length - : undefined - } + numFilters={this.props.savedObjectMetaData.length} + hasActiveFilters={this.state.filteredTypes.length > 0} + numActiveFilters={this.state.filteredTypes.length} > {i18n.translate('common.ui.savedObjects.finder.filterButtonLabel', { defaultMessage: 'Types', @@ -401,9 +399,10 @@ class SavedObjectFinder extends React.Component ( + items={this.props.savedObjectMetaData.map(metaData => ( { diff --git a/typings/@elastic/eui/index.d.ts b/typings/@elastic/eui/index.d.ts index 1d593e845dcf0..bdd6d4d72fbd2 100644 --- a/typings/@elastic/eui/index.d.ts +++ b/typings/@elastic/eui/index.d.ts @@ -25,10 +25,7 @@ declare module '@elastic/eui' { export const EuiCopy: React.SFC; export const EuiOutsideClickDetector: React.SFC; export const EuiSideNav: React.SFC; - export const EuiFilterGroup: React.FunctionComponent; - export const EuiFilterSelectItem: React.FunctionComponent; export const EuiListGroupItem: React.FunctionComponent; - export const EuiFilterButton: React.FunctionComponent; export interface EuiTableCriteria { page: { index: number; size: number }; From 64a273e10e9f95be9b6fe9865a7a75fc4fb5bb71 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 8 Mar 2019 21:27:03 +0100 Subject: [PATCH 29/31] adjust functional tests for new filter behavior --- .../saved_objects/components/saved_object_finder.tsx | 7 +++++-- test/functional/services/dashboard/add_panel.js | 12 +++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx index 2a9f8c0db8a36..8575fa66ee747 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_finder.tsx @@ -368,7 +368,10 @@ class SavedObjectFinder extends React.Component } > - + {this.props.showFilter && ( ( Date: Mon, 11 Mar 2019 10:16:13 +0100 Subject: [PATCH 30/31] Change translation id due to string change --- .../core_plugins/kibana/public/dashboard/top_nav/add_panel.js | 2 +- x-pack/plugins/translations/translations/zh-CN.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js index 1efd9c2a7aae8..febf53de9670a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/add_panel.js @@ -92,7 +92,7 @@ export class DashboardAddPanel extends React.Component { diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 96dff2f5ca591..004e5b8c5b41a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1214,7 +1214,6 @@ "kbn.dashboard.stateManager.timeNotSavedWithDashboardErrorMessage": "时间未随此仪表板保存,因此无法同步。", "kbn.dashboard.strings.dashboardEditTitle": "编辑 {title}", "kbn.dashboard.strings.dashboardUnsavedEditTitle": "编辑 {title}(未保存)", - "kbn.dashboard.topNav.addPanel.addNewVisualizationButtonLabel": "添加新的可视化", "kbn.dashboard.topNav.addPanelsTitle": "添加面板", "kbn.dashboard.topNav.cloneModal.cancelButtonLabel": "取消", "kbn.dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "克隆面板", @@ -8197,4 +8196,4 @@ "xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +} From 7ddce1dea1f1da1656b7eeb5ccaeaecf6a1151ca Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Mon, 11 Mar 2019 11:43:42 +0100 Subject: [PATCH 31/31] Update Jest snapshot --- .../dashboard/top_nav/__snapshots__/add_panel.test.js.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap index a959aab55cc94..32311d82587c4 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/__snapshots__/add_panel.test.js.snap @@ -58,7 +58,7 @@ exports[`render 1`] = ` >