From f80f8887220ce89353117a2773ea0fd78bf83264 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 08:30:39 -0400 Subject: [PATCH 01/42] shimming NP for spaces client-side plugin --- x-pack/legacy/plugins/spaces/index.ts | 37 +------- .../spaces/public/hacks/init_np_plugin.ts | 15 ++++ x-pack/legacy/plugins/spaces/public/index.ts | 11 +++ .../legacy/plugins/spaces/public/lib/index.ts | 2 +- .../spaces/public/lib/spaces_manager.mock.ts | 1 + .../spaces/public/lib/spaces_manager.ts | 85 ++++++++++++------- .../legacy/plugins/spaces/public/plugin.tsx | 23 +++++ .../confirm_delete_modal.test.tsx.snap | 22 ----- .../components/confirm_delete_modal.test.tsx | 13 --- .../components/confirm_delete_modal.tsx | 23 +++-- .../edit_space/delete_spaces_button.test.tsx | 7 -- .../edit_space/delete_spaces_button.tsx | 9 +- .../edit_space/manage_space_page.test.tsx | 31 ++----- .../edit_space/manage_space_page.tsx | 33 ++++--- .../public/views/management/page_routes.tsx | 31 +++---- .../spaces_grid/spaces_grid_page.tsx | 9 +- .../spaces_grid/spaces_grid_pages.test.tsx | 18 +--- .../nav_control_popover.test.tsx.snap | 13 +-- .../spaces/public/views/nav_control/index.ts | 4 +- .../public/views/nav_control/nav_control.tsx | 82 ++++++------------ .../nav_control/nav_control_popover.test.tsx | 17 ++-- .../views/nav_control/nav_control_popover.tsx | 27 ++---- .../space_selector.test.tsx.snap | 16 +--- .../public/views/space_selector/index.tsx | 18 ++-- .../space_selector/space_selector.test.tsx | 36 +------- .../views/space_selector/space_selector.tsx | 16 ++-- .../api/__fixtures__/create_test_handler.ts | 2 +- .../server/routes/api/v1/spaces.test.ts | 30 +++++++ .../spaces/server/routes/api/v1/spaces.ts | 32 +++++++ 29 files changed, 292 insertions(+), 371 deletions(-) create mode 100644 x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts create mode 100644 x-pack/legacy/plugins/spaces/public/index.ts create mode 100644 x-pack/legacy/plugins/spaces/public/plugin.tsx diff --git a/x-pack/legacy/plugins/spaces/index.ts b/x-pack/legacy/plugins/spaces/index.ts index 4b6b238d20136..82df69091ab88 100644 --- a/x-pack/legacy/plugins/spaces/index.ts +++ b/x-pack/legacy/plugins/spaces/index.ts @@ -11,9 +11,6 @@ import { createOptionalPlugin } from '../../server/lib/optional_plugin'; // @ts-ignore import { AuditLogger } from '../../server/lib/audit_logger'; import mappings from './mappings.json'; -import { wrapError } from './server/lib/errors'; -import { getActiveSpace } from './server/lib/get_active_space'; -import { getSpaceSelectorUrl } from './server/lib/get_space_selector_url'; import { migrateToKibana660 } from './server/lib/migrations'; import { plugin } from './server/new_platform'; import { @@ -51,7 +48,6 @@ export const spaces = (kibana: Record) => }, uiExports: { - chromeNavControls: ['plugins/spaces/views/nav_control'], styleSheetPaths: resolve(__dirname, 'public/index.scss'), managementSections: ['plugins/spaces/views/management'], apps: [ @@ -63,7 +59,7 @@ export const spaces = (kibana: Record) => hidden: true, }, ], - hacks: [], + hacks: ['plugins/spaces/hacks/init_np_plugin'], mappings, migrations: { space: { @@ -76,37 +72,6 @@ export const spaces = (kibana: Record) => }, }, home: ['plugins/spaces/register_feature'], - injectDefaultVars(server: any) { - return { - spaces: [], - activeSpace: null, - spaceSelectorURL: getSpaceSelectorUrl(server.config()), - }; - }, - async replaceInjectedVars( - vars: Record, - request: Record, - server: Record - ) { - const spacesClient = await server.plugins.spaces.getScopedSpacesClient(request); - try { - vars.activeSpace = { - valid: true, - space: await getActiveSpace( - spacesClient, - request.getBasePath(), - server.config().get('server.basePath') - ), - }; - } catch (e) { - vars.activeSpace = { - valid: false, - error: wrapError(e).output.payload, - }; - } - - return vars; - }, }, async init(server: Server) { diff --git a/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts b/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts new file mode 100644 index 0000000000000..b634ce73353d2 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { npSetup, npStart } from 'ui/new_platform'; +import { plugin } from '../index'; +import { Plugin } from '../plugin'; + +const spacesPlugin: Plugin = plugin({}); + +export const waitForSpacesNPInit = spacesPlugin + .start(npStart.core) + .then(() => spacesPlugin.setup(npSetup.core)); diff --git a/x-pack/legacy/plugins/spaces/public/index.ts b/x-pack/legacy/plugins/spaces/public/index.ts new file mode 100644 index 0000000000000..226f399b3b02f --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { PluginInitializerContext } from 'src/core/public'; +import { Plugin } from './plugin'; + +export const plugin = (initializerContext: PluginInitializerContext) => { + return new Plugin(initializerContext); +}; diff --git a/x-pack/legacy/plugins/spaces/public/lib/index.ts b/x-pack/legacy/plugins/spaces/public/lib/index.ts index 538dd77e053f5..243f85509c55c 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/index.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { SpacesManager } from './spaces_manager'; +export { SpacesManager, initSpacesManager, getSpacesManager } from './spaces_manager'; diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts index 7d4fb1b90fe11..80ed765121da2 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts @@ -10,6 +10,7 @@ function createSpacesManagerMock() { return ({ getSpaces: jest.fn().mockResolvedValue([]), getSpace: jest.fn().mockResolvedValue(undefined), + getActiveSpace: jest.fn().mockResolvedValue(undefined), createSpace: jest.fn().mockResolvedValue(undefined), updateSpace: jest.fn().mockResolvedValue(undefined), deleteSpace: jest.fn().mockResolvedValue(undefined), diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts index cd2939f83e20b..48e97182691c6 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts @@ -4,58 +4,85 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { toastNotifications } from 'ui/notify'; import { EventEmitter } from 'events'; -import { kfetch } from 'ui/kfetch'; +import { NotificationsSetup, HttpSetup } from 'src/core/public'; import { Space } from '../../common/model/space'; +let spacesManager: SpacesManager | undefined; +export const initSpacesManager = ( + spaceSelectorUrl: string, + http: HttpSetup, + notifications: NotificationsSetup +) => { + if (spacesManager) { + throw new Error('SpacesManager has already been initialized!'); + } + spacesManager = new SpacesManager(spaceSelectorUrl, http, notifications); + return spacesManager; +}; + +export const getSpacesManager = () => { + if (!spacesManager) { + throw new Error('SpacesManager has not been initialized yet'); + } + + return spacesManager; +}; + export class SpacesManager extends EventEmitter { - private spaceSelectorURL: string; + private activeSpace: Space | undefined; - constructor(spaceSelectorURL: string) { + constructor( + private readonly spaceSelectorURL: string, + private readonly http: HttpSetup, + private readonly notifications: NotificationsSetup + ) { super(); - this.spaceSelectorURL = spaceSelectorURL; } public async getSpaces(): Promise { - return await kfetch({ pathname: '/api/spaces/space' }); + return await this.http.get('/api/spaces/space'); } public async getSpace(id: string): Promise { - return await kfetch({ pathname: `/api/spaces/space/${encodeURIComponent(id)}` }); + return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`); + } + + public async getActiveSpace(): Promise { + if (!this.activeSpace) { + this.activeSpace = (await this.http.get('/api/spaces/v1/activeSpace')) as Space; + } + return this.activeSpace; } public async createSpace(space: Space) { - return await kfetch({ - pathname: `/api/spaces/space`, - method: 'POST', - body: JSON.stringify(space), - }); + return this.http + .post(`/api/spaces/space`, { + body: JSON.stringify(space), + }) + .then(() => this.requestRefresh()); } public async updateSpace(space: Space) { - return await kfetch({ - pathname: `/api/spaces/space/${encodeURIComponent(space.id)}`, - query: { - overwrite: true, - }, - method: 'PUT', - body: JSON.stringify(space), - }); + return this.http + .put(`/api/spaces/space/${encodeURIComponent(space.id)}`, { + query: { + overwrite: true, + }, + body: JSON.stringify(space), + }) + .then(() => this.requestRefresh()); } public async deleteSpace(space: Space) { - return await kfetch({ - pathname: `/api/spaces/space/${encodeURIComponent(space.id)}`, - method: 'DELETE', - }); + return this.http + .delete(`/api/spaces/space/${encodeURIComponent(space.id)}`) + .then(() => this.requestRefresh()); } public async changeSelectedSpace(space: Space) { - await kfetch({ - pathname: `/api/spaces/v1/space/${encodeURIComponent(space.id)}/select`, - method: 'POST', - }) + await this.http + .post(`/api/spaces/v1/space/${encodeURIComponent(space.id)}/select`) .then(response => { if (response.location) { window.location = response.location; @@ -75,7 +102,7 @@ export class SpacesManager extends EventEmitter { } public _displayError() { - toastNotifications.addDanger({ + this.notifications.toasts.addDanger({ title: i18n.translate('xpack.spaces.spacesManager.unableToChangeSpaceWarningTitle', { defaultMessage: 'Unable to change your Space', }), diff --git a/x-pack/legacy/plugins/spaces/public/plugin.tsx b/x-pack/legacy/plugins/spaces/public/plugin.tsx new file mode 100644 index 0000000000000..6a9862c039fbd --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/plugin.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext, CoreSetup, CoreStart } from 'src/core/public'; +import { initSpacesManager } from './lib'; +import { initSpacesNavControl } from './views/nav_control'; + +export class Plugin { + // @ts-ignore + constructor(private readonly initializerContext: PluginInitializerContext) {} + + public async start(core: CoreStart) { + const { spaceSelectorUrl } = await core.http.get('/api/spaces/v1/npStart'); + const spacesManager = initSpacesManager(spaceSelectorUrl, core.http, core.notifications); + + initSpacesNavControl(spacesManager, core); + } + + public async setup(core: CoreSetup) {} +} diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/confirm_delete_modal.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/confirm_delete_modal.test.tsx.snap index b3a491749f5a4..cae21995f9ab1 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/confirm_delete_modal.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/confirm_delete_modal.test.tsx.snap @@ -63,28 +63,6 @@ exports[`ConfirmDeleteModal renders as expected 1`] = ` value="" /> - - - - ( - - My Space - - ) - , - } - } - /> - - diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx index b34b52cd48c7a..a2245a2236707 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { SpacesNavState } from '../../nav_control'; import { ConfirmDeleteModal } from './confirm_delete_modal'; import { spacesManagerMock } from '../../../lib/mocks'; @@ -20,11 +19,6 @@ describe('ConfirmDeleteModal', () => { const spacesManager = spacesManagerMock.create(); - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; - const onCancel = jest.fn(); const onConfirm = jest.fn(); @@ -33,7 +27,6 @@ describe('ConfirmDeleteModal', () => { { const spacesManager = spacesManagerMock.create(); - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; - const onCancel = jest.fn(); const onConfirm = jest.fn(); @@ -63,7 +51,6 @@ describe('ConfirmDeleteModal', () => { void; onConfirm: () => void; intl: InjectedIntl; @@ -41,6 +39,7 @@ interface State { confirmSpaceName: string; error: boolean | null; deleteInProgress: boolean; + isDeletingCurrentSpace: boolean; } class ConfirmDeleteModalUI extends Component { @@ -48,13 +47,23 @@ class ConfirmDeleteModalUI extends Component { confirmSpaceName: '', error: null, deleteInProgress: false, + isDeletingCurrentSpace: false, }; + public componentWillMount() { + isCurrentSpace(this.props.space, this.props.spacesManager).then(result => { + this.setState({ + isDeletingCurrentSpace: result, + }); + }); + } + public render() { - const { space, spacesNavState, onCancel, intl } = this.props; + const { space, onCancel, intl } = this.props; + const { isDeletingCurrentSpace } = this.state; let warning = null; - if (isDeletingCurrentSpace(space, spacesNavState)) { + if (isDeletingCurrentSpace) { const name = ( ({space.name}) @@ -182,7 +191,7 @@ class ConfirmDeleteModalUI extends Component { private onConfirm = async () => { if (this.state.confirmSpaceName === this.props.space.name) { - const needsRedirect = isDeletingCurrentSpace(this.props.space, this.props.spacesNavState); + const needsRedirect = this.state.isDeletingCurrentSpace; const spacesManager = this.props.spacesManager; this.setState({ @@ -206,8 +215,8 @@ class ConfirmDeleteModalUI extends Component { }; } -function isDeletingCurrentSpace(space: Space, spacesNavState: SpacesNavState) { - return space.id === spacesNavState.getActiveSpace().id; +async function isCurrentSpace(space: Space, spacesManager: SpacesManager) { + return space.id === (await spacesManager.getActiveSpace()).id; } export const ConfirmDeleteModal = injectI18n(ConfirmDeleteModalUI); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx index 4a7f419bde82c..8f1f022038346 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { SpacesNavState } from '../../nav_control'; import { DeleteSpacesButton } from './delete_spaces_button'; import { spacesManagerMock } from '../../../lib/mocks'; @@ -20,16 +19,10 @@ describe('DeleteSpacesButton', () => { it('renders as expected', () => { const spacesManager = spacesManagerMock.create(); - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; - const wrapper = shallowWithIntl( diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx index 7f3dd0aea485e..216dd7c41f124 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx @@ -6,7 +6,6 @@ import { EuiButton, EuiButtonIcon, EuiButtonIconProps } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import { SpacesNavState } from 'plugins/spaces/views/nav_control'; import React, { Component, Fragment } from 'react'; // @ts-ignore import { toastNotifications } from 'ui/notify'; @@ -18,7 +17,6 @@ interface Props { style?: 'button' | 'icon'; space: Space; spacesManager: SpacesManager; - spacesNavState: SpacesNavState; onDelete: () => void; intl: InjectedIntl; } @@ -81,12 +79,11 @@ class DeleteSpacesButtonUI extends Component { return null; } - const { spacesNavState, spacesManager } = this.props; + const { spacesManager } = this.props; return ( { this.setState({ @@ -99,7 +96,7 @@ class DeleteSpacesButtonUI extends Component { }; public deleteSpaces = async () => { - const { spacesManager, space, spacesNavState, intl } = this.props; + const { spacesManager, space, intl } = this.props; try { await spacesManager.deleteSpace(space); @@ -139,8 +136,6 @@ class DeleteSpacesButtonUI extends Component { if (this.props.onDelete) { this.props.onDelete(); } - - spacesNavState.refreshSpacesList(); }; } diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx index bf0e995858839..a6ff71ddba019 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx @@ -11,7 +11,6 @@ import { EuiButton, EuiLink, EuiSwitch } from '@elastic/eui'; import { ReactWrapper } from 'enzyme'; import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { SpacesNavState } from '../../nav_control'; import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; import { ManageSpacePage } from './manage_space_page'; import { SectionPanel } from './section_panel'; @@ -27,18 +26,10 @@ describe('ManageSpacePage', () => { it('allows a space to be created', async () => { const spacesManager = spacesManagerMock.create(); spacesManager.createSpace = jest.fn(spacesManager.createSpace); - - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; + spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); const wrapper = mountWithIntl( - + ); await waitForDataLoad(wrapper); @@ -73,16 +64,12 @@ describe('ManageSpacePage', () => { initials: 'AB', disabledFeatures: [], }); + spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; const wrapper = mountWithIntl( ); @@ -119,16 +106,12 @@ describe('ManageSpacePage', () => { initials: 'AB', disabledFeatures: [], }); + spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; const wrapper = mountWithIntl( ); @@ -174,16 +157,12 @@ describe('ManageSpacePage', () => { initials: 'AB', disabledFeatures: [], }); + spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); - const spacesNavState: SpacesNavState = { - getActiveSpace: () => space, - refreshSpacesList: jest.fn(), - }; const wrapper = mountWithIntl( ); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx index 2ee860744fdda..804c1d73d8be7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx @@ -16,7 +16,6 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import _ from 'lodash'; -import { SpacesNavState } from 'plugins/spaces/views/nav_control'; import React, { Component, Fragment } from 'react'; import { capabilities } from 'ui/capabilities'; import { Breadcrumb } from 'ui/chrome'; @@ -39,7 +38,6 @@ import { ReservedSpaceBadge } from './reserved_space_badge'; interface Props { spacesManager: SpacesManager; spaceId?: string; - spacesNavState: SpacesNavState; intl: InjectedIntl; setBreadcrumbs?: (breadcrumbs: Breadcrumb[]) => void; } @@ -269,7 +267,6 @@ class ManageSpacePageUI extends Component { data-test-subj="delete-space-button" space={this.state.space as Space} spacesManager={this.props.spacesManager} - spacesNavState={this.props.spacesNavState} onDelete={this.backToSpacesList} /> @@ -298,27 +295,30 @@ class ManageSpacePageUI extends Component { } if (this.editingExistingSpace()) { - const { spacesNavState } = this.props; + const { spacesManager } = this.props; const originalSpace: Space = this.state.originalSpace as Space; const space: Space = this.state.space as Space; - const editingActiveSpace = spacesNavState.getActiveSpace().id === originalSpace.id; + spacesManager.getActiveSpace().then(activeSpace => { + const editingActiveSpace = activeSpace.id === originalSpace.id; - const haveDisabledFeaturesChanged = - space.disabledFeatures.length !== originalSpace.disabledFeatures.length || - _.difference(space.disabledFeatures, originalSpace.disabledFeatures).length > 0; + const haveDisabledFeaturesChanged = + space.disabledFeatures.length !== originalSpace.disabledFeatures.length || + _.difference(space.disabledFeatures, originalSpace.disabledFeatures).length > 0; - if (editingActiveSpace && haveDisabledFeaturesChanged) { - this.setState({ - showAlteringActiveSpaceDialog: true, - }); + if (editingActiveSpace && haveDisabledFeaturesChanged) { + this.setState({ + showAlteringActiveSpaceDialog: true, + }); - return; - } + return; + } + this.performSave(); + }); + } else { + this.performSave(); } - - this.performSave(); }; private performSave = (requireRefresh = false) => { @@ -356,7 +356,6 @@ class ManageSpacePageUI extends Component { action .then(() => { - this.props.spacesNavState.refreshSpacesList(); toastNotifications.addSuccess( intl.formatMessage( { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx b/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx index d38c5c1998b3a..2a8c2a41531e7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx @@ -5,32 +5,34 @@ */ // @ts-ignore import template from 'plugins/spaces/views/management/template.html'; -import { SpacesNavState } from 'plugins/spaces/views/nav_control'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import 'ui/autoload/styles'; import { I18nContext } from 'ui/i18n'; // @ts-ignore import routes from 'ui/routes'; -import { SpacesManager } from '../../lib/spaces_manager'; +import { getSpacesManager } from '../../lib/spaces_manager'; import { ManageSpacePage } from './edit_space'; import { getCreateBreadcrumbs, getEditBreadcrumbs, getListBreadcrumbs } from './lib'; import { SpacesGridPage } from './spaces_grid'; + +import { waitForSpacesNPInit } from '../../hacks/init_np_plugin'; + const reactRootNodeId = 'manageSpacesReactRoot'; routes.when('/management/spaces/list', { template, k7Breadcrumbs: getListBreadcrumbs, requireUICapability: 'management.kibana.spaces', - controller($scope: any, spacesNavState: SpacesNavState, spaceSelectorURL: string) { + controller($scope: any) { $scope.$$postDigest(async () => { const domNode = document.getElementById(reactRootNodeId); - const spacesManager = new SpacesManager(spaceSelectorURL); + await waitForSpacesNPInit; render( - + , domNode ); @@ -49,15 +51,15 @@ routes.when('/management/spaces/create', { template, k7Breadcrumbs: getCreateBreadcrumbs, requireUICapability: 'management.kibana.spaces', - controller($scope: any, spacesNavState: SpacesNavState, spaceSelectorURL: string) { + controller($scope: any) { $scope.$$postDigest(async () => { const domNode = document.getElementById(reactRootNodeId); - const spacesManager = new SpacesManager(spaceSelectorURL); + await waitForSpacesNPInit; render( - + , domNode ); @@ -80,26 +82,19 @@ routes.when('/management/spaces/edit/:spaceId', { template, k7Breadcrumbs: () => getEditBreadcrumbs(), requireUICapability: 'management.kibana.spaces', - controller( - $scope: any, - $route: any, - chrome: any, - spacesNavState: SpacesNavState, - spaceSelectorURL: string - ) { + controller($scope: any, $route: any, chrome: any) { $scope.$$postDigest(async () => { const domNode = document.getElementById(reactRootNodeId); const { spaceId } = $route.current.params; - const spacesManager = new SpacesManager(spaceSelectorURL); + await waitForSpacesNPInit; render( { chrome.breadcrumbs.set(breadcrumbs); }} diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx index 803b786b2a5b6..e572230f2cfc0 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx @@ -30,7 +30,6 @@ import { Space } from '../../../../common/model/space'; import { SpaceAvatar } from '../../../components'; import { getSpacesFeatureDescription } from '../../../lib/constants'; import { SpacesManager } from '../../../lib/spaces_manager'; -import { SpacesNavState } from '../../nav_control'; import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; import { SecureSpaceMessage } from '../components/secure_space_message'; import { UnauthorizedPrompt } from '../components/unauthorized_prompt'; @@ -38,7 +37,6 @@ import { getEnabledFeatures } from '../lib/feature_utils'; interface Props { spacesManager: SpacesManager; - spacesNavState: SpacesNavState; intl: InjectedIntl; } @@ -158,12 +156,11 @@ class SpacesGridPageUI extends Component { return null; } - const { spacesNavState, spacesManager } = this.props; + const { spacesManager } = this.props; return ( { this.setState({ @@ -177,7 +174,7 @@ class SpacesGridPageUI extends Component { public deleteSpace = async () => { const { intl } = this.props; - const { spacesManager, spacesNavState } = this.props; + const { spacesManager } = this.props; const space = this.state.selectedSpace; @@ -220,8 +217,6 @@ class SpacesGridPageUI extends Component { ); toastNotifications.addSuccess(message); - - spacesNavState.refreshSpacesList(); }; public loadGrid = async () => { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx index a59ad8084de22..c8ae846adb9e7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx @@ -11,7 +11,6 @@ import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { SpaceAvatar } from '../../../components'; import { spacesManagerMock } from '../../../lib/mocks'; -import { SpacesNavState } from '../../nav_control'; import { SpacesGridPage } from './spaces_grid_page'; const spaces = [ @@ -36,11 +35,6 @@ const spaces = [ }, ]; -const spacesNavState: SpacesNavState = { - getActiveSpace: () => spaces[0], - refreshSpacesList: jest.fn(), -}; - const spacesManager = spacesManagerMock.create(); spacesManager.getSpaces = jest.fn().mockResolvedValue(spaces); @@ -48,22 +42,14 @@ describe('SpacesGridPage', () => { it('renders as expected', () => { expect( shallowWithIntl( - + ) ).toMatchSnapshot(); }); it('renders the list of spaces', async () => { const wrapper = mountWithIntl( - + ); // allow spacesManager to load spaces diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap index fdebc42496002..a1288f3b9ae28 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap @@ -6,20 +6,13 @@ exports[`NavControlPopover renders without crashing 1`] = ` button={ } - linkTitle="foo" + linkTitle="loading" spaceSelectorShown={false} toggleSpaceSelector={[Function]} /> diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/index.ts b/x-pack/legacy/plugins/spaces/public/views/nav_control/index.ts index 541c79a8fd4a3..649aeee9eab9e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/index.ts +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './nav_control'; - -export { SpacesNavState } from './nav_control'; +export { initSpacesNavControl } from './nav_control'; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx index ad2ae08374708..3ecb6662e7d63 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx @@ -5,69 +5,37 @@ */ import { SpacesManager } from 'plugins/spaces/lib/spaces_manager'; -// @ts-ignore -import template from 'plugins/spaces/views/nav_control/nav_control.html'; import { NavControlPopover } from 'plugins/spaces/views/nav_control/nav_control_popover'; // @ts-ignore import { Path } from 'plugins/xpack_main/services/path'; import React from 'react'; import ReactDOM from 'react-dom'; -import { I18nContext } from 'ui/i18n'; -// @ts-ignore -import { uiModules } from 'ui/modules'; -import { - chromeHeaderNavControlsRegistry, - NavControlSide, -} from 'ui/registry/chrome_header_nav_controls'; -// @ts-ignore -import { Space } from '../../../common/model/space'; +import { CoreStart } from 'src/core/public'; import { SpacesHeaderNavButton } from './components/spaces_header_nav_button'; -const module = uiModules.get('spaces_nav', ['kibana']); - -export interface SpacesNavState { - getActiveSpace: () => Space; - refreshSpacesList: () => void; -} - -let spacesManager: SpacesManager; - -module.service('spacesNavState', (activeSpace: any) => { - return { - getActiveSpace: () => { - return activeSpace.space; - }, - refreshSpacesList: () => { - if (spacesManager) { - spacesManager.requestRefresh(); +export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreStart) { + const I18nContext = core.i18n.Context; + core.chrome.navControls.registerLeft({ + order: 1000, + mount(targetDomElement: HTMLElement) { + if (Path.isUnauthenticated()) { + return () => null; } - }, - } as SpacesNavState; -}); - -chromeHeaderNavControlsRegistry.register((chrome: any, activeSpace: any) => ({ - name: 'spaces', - order: 1000, - side: NavControlSide.Left, - render(el: HTMLElement) { - if (Path.isUnauthenticated()) { - return; - } - - const spaceSelectorURL = chrome.getInjected('spaceSelectorURL'); - spacesManager = new SpacesManager(spaceSelectorURL); - - ReactDOM.render( - - - , - el - ); - }, -})); + ReactDOM.render( + + + , + targetDomElement + ); + + return () => { + ReactDOM.unmountComponentAtNode(targetDomElement); + }; + }, + }); +} diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx index c30433fb3bf08..666126e1355a6 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx @@ -13,16 +13,10 @@ import { NavControlPopover } from './nav_control_popover'; describe('NavControlPopover', () => { it('renders without crashing', () => { - const activeSpace = { - space: { id: '', name: 'foo', disabledFeatures: [] }, - valid: true, - }; - const spacesManager = spacesManagerMock.create(); const wrapper = shallow( { }); it('renders a SpaceAvatar with the active space', async () => { - const activeSpace = { - space: { id: 'foo-space', name: 'foo', disabledFeatures: [] }, - valid: true, - }; - const spacesManager = spacesManagerMock.create(); spacesManager.getSpaces = jest.fn().mockResolvedValue([ { @@ -50,10 +39,14 @@ describe('NavControlPopover', () => { disabledFeatures: [], }, ]); + spacesManager.getActiveSpace = jest.fn().mockResolvedValue({ + id: 'foo-space', + name: 'foo', + disabledFeatures: [], + }); const wrapper = mount( ; } @@ -37,7 +32,7 @@ export class NavControlPopover extends Component { this.state = { showSpaceSelector: false, loading: false, - activeSpace: props.activeSpace.space, + activeSpace: null, spaces: [], }; } @@ -91,24 +86,20 @@ export class NavControlPopover extends Component { } private async loadSpaces() { - const { spacesManager, activeSpace } = this.props; + const { spacesManager } = this.props; this.setState({ loading: true, }); - const spaces = await spacesManager.getSpaces(); - - // Update the active space definition, if it changed since the last load operation - let activeSpaceEntry: Space | null = activeSpace.space; - - if (activeSpace.valid) { - activeSpaceEntry = spaces.find(space => space.id === this.props.activeSpace.space.id) || null; - } + const [activeSpace, spaces] = await Promise.all([ + spacesManager.getActiveSpace(), + spacesManager.getSpaces(), + ]); this.setState({ spaces, - activeSpace: activeSpaceEntry, + activeSpace, loading: false, }); } @@ -118,8 +109,8 @@ export class NavControlPopover extends Component { if (!activeSpace) { return this.getButton( - , - 'error' + , + 'loading' ); } diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/__snapshots__/space_selector.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/space_selector/__snapshots__/space_selector.test.tsx.snap index 84ed864c36231..1516157188cc6 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/__snapshots__/space_selector.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/space_selector/__snapshots__/space_selector.test.tsx.snap @@ -62,21 +62,9 @@ exports[`it renders without crashing 1`] = ` - - - - - diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx b/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx index 935e79e73517e..c731454bbb351 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SpacesManager } from 'plugins/spaces/lib/spaces_manager'; +import { getSpacesManager } from 'plugins/spaces/lib/spaces_manager'; // @ts-ignore import template from 'plugins/spaces/views/space_selector/space_selector.html'; import 'ui/autoload/styles'; @@ -15,20 +15,20 @@ import { uiModules } from 'ui/modules'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Space } from '../../../common/model/space'; import { SpaceSelector } from './space_selector'; +import { waitForSpacesNPInit } from '../../hacks/init_np_plugin'; + const module = uiModules.get('spaces_selector', []); -module.controller( - 'spacesSelectorController', - ($scope: any, spaces: Space[], spaceSelectorURL: string) => { +module.controller('spacesSelectorController', ($scope: any) => { + $scope.$$postDigest(async () => { const domNode = document.getElementById('spaceSelectorRoot'); - const spacesManager = new SpacesManager(spaceSelectorURL); + await waitForSpacesNPInit; render( - + , domNode ); @@ -39,7 +39,7 @@ module.controller( unmountComponentAtNode(domNode); } }); - } -); + }); +}); chrome.setVisible(false).setRootTemplate(template); diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx b/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx index 78779e38713db..829312061ca98 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { Space } from '../../../common/model/space'; import { spacesManagerMock } from '../../lib/mocks'; import { SpaceSelector } from './space_selector'; @@ -19,42 +19,12 @@ function getSpacesManager(spaces: Space[] = []) { test('it renders without crashing', () => { const spacesManager = getSpacesManager(); const component = shallowWithIntl( - + ); expect(component).toMatchSnapshot(); }); -test('it uses the spaces on props, when provided', () => { - const spacesManager = getSpacesManager(); - - const spaces = [ - { - id: 'space-1', - name: 'Space 1', - description: 'This is the first space', - disabledFeatures: [], - }, - ]; - - const component = renderWithIntl( - - ); - - return Promise.resolve().then(() => { - expect(component.find('.spaceCard')).toHaveLength(1); - expect(spacesManager.getSpaces).toHaveBeenCalledTimes(0); - }); -}); - -test('it queries for spaces when not provided on props', () => { +test('it queries for spaces when loaded', () => { const spaces = [ { id: 'space-1', diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx b/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx index 776e5f848296f..2b91649c3a1f4 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx @@ -16,6 +16,7 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiLoadingSpinner, } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { SpacesManager } from 'plugins/spaces/lib'; @@ -25,7 +26,6 @@ import { Space } from '../../../common/model/space'; import { SpaceCards } from '../components/space_cards'; interface Props { - spaces?: Space[]; spacesManager: SpacesManager; intl: InjectedIntl; } @@ -47,10 +47,6 @@ class SpaceSelectorUI extends Component { spaces: [], }; - if (Array.isArray(props.spaces)) { - state.spaces = [...props.spaces]; - } - this.state = state; } @@ -130,9 +126,13 @@ class SpaceSelectorUI extends Component { - + {this.state.loading && } + + {!this.state.loading && ( + + )} - {filteredSpaces.length === 0 && ( + {!this.state.loading && filteredSpaces.length === 0 && ( { public getSearchField = () => { const { intl } = this.props; - if (!this.props.spaces || this.props.spaces.length < SPACE_SEARCH_COUNT_THRESHOLD) { + if (!this.state.spaces || this.state.spaces.length < SPACE_SEARCH_COUNT_THRESHOLD) { return null; } return ( diff --git a/x-pack/legacy/plugins/spaces/server/routes/api/__fixtures__/create_test_handler.ts b/x-pack/legacy/plugins/spaces/server/routes/api/__fixtures__/create_test_handler.ts index f84194160971d..e7607a9a14e04 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/api/__fixtures__/create_test_handler.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/api/__fixtures__/create_test_handler.ts @@ -182,7 +182,7 @@ export function createTestHandler( initApiFn({ http: ({ - server, + ...httpServiceMock.createSetupContract(), route: server.route.bind(server), } as unknown) as SpacesHttpServiceSetup, routePreCheckLicenseFn: pre, diff --git a/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.test.ts b/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.test.ts index 4d9952f4ab3dc..2f21e338dad96 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.test.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.test.ts @@ -41,6 +41,36 @@ describe('Spaces API', () => { await Promise.all(teardowns.splice(0).map(fn => fn())); }); + test('GET /npStart should return the space selector URL', async () => { + const { response } = await request('GET', '/api/spaces/v1/npStart', { + expectSpacesClientCall: false, + }); + + const { statusCode, payload } = response; + + expect(statusCode).toEqual(200); + + const result = JSON.parse(payload); + expect(result).toEqual({ + spaceSelectorUrl: '/', + }); + }); + + test('GET /activeSpace should return the active space', async () => { + const { response } = await request('GET', '/api/spaces/v1/activeSpace'); + + const { statusCode, payload } = response; + + expect(statusCode).toEqual(200); + + const result = JSON.parse(payload); + expect(result).toEqual({ + id: 'default', + name: 'Default Space', + _reserved: true, + }); + }); + test('POST space/{id}/select should respond with the new space location', async () => { const { response } = await request('POST', '/api/spaces/v1/space/a-space/select'); diff --git a/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts b/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts index 8765bd635a45a..c79827978a5b2 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts @@ -5,16 +5,48 @@ */ import Boom from 'boom'; +import { Legacy } from 'kibana'; import { Space } from '../../../../common/model/space'; import { wrapError } from '../../../lib/errors'; import { SpacesClient } from '../../../lib/spaces_client'; import { addSpaceIdToPath } from '../../../lib/spaces_url_parser'; import { getSpaceById } from '../../lib'; import { InternalRouteDeps } from '.'; +import { getActiveSpace } from '../../../lib/get_active_space'; +import { getSpaceSelectorUrl } from '../../../lib/get_space_selector_url'; export function initInternalSpacesApi(deps: InternalRouteDeps) { const { http, config, spacesService, savedObjects, routePreCheckLicenseFn } = deps; + http.route({ + method: 'GET', + path: '/api/spaces/v1/activeSpace', + async handler(request: Legacy.Request) { + const spacesClient: SpacesClient = await spacesService.scopedClient(request); + return await getActiveSpace( + spacesClient, + http.basePath.get(request), + config.get('server.basePath') + ); + }, + options: { + pre: [routePreCheckLicenseFn], + }, + }); + + http.route({ + method: 'GET', + path: '/api/spaces/v1/npStart', + async handler() { + return { + spaceSelectorUrl: getSpaceSelectorUrl(config), + }; + }, + options: { + pre: [routePreCheckLicenseFn], + }, + }); + http.route({ method: 'POST', path: '/api/spaces/v1/space/{id}/select', From 452d592b598cfa511afd207ac2d790ea23bfcccc Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 08:58:40 -0400 Subject: [PATCH 02/42] refresh active space in nav control when updated --- x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts | 4 ++-- .../spaces/public/views/nav_control/nav_control_popover.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts index 48e97182691c6..6b41afbcb62d0 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts @@ -48,8 +48,8 @@ export class SpacesManager extends EventEmitter { return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`); } - public async getActiveSpace(): Promise { - if (!this.activeSpace) { + public async getActiveSpace(forceRefresh: boolean = false): Promise { + if (!this.activeSpace || forceRefresh) { this.activeSpace = (await this.http.get('/api/spaces/v1/activeSpace')) as Space; } return this.activeSpace; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx index dca9e843021ed..5f0284401e977 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx @@ -42,7 +42,7 @@ export class NavControlPopover extends Component { if (this.props.spacesManager) { this.props.spacesManager.on('request_refresh', () => { - this.loadSpaces(); + this.loadSpaces(true); }); } } @@ -85,7 +85,7 @@ export class NavControlPopover extends Component { ); } - private async loadSpaces() { + private async loadSpaces(refreshActiveSpace: boolean = false) { const { spacesManager } = this.props; this.setState({ @@ -93,7 +93,7 @@ export class NavControlPopover extends Component { }); const [activeSpace, spaces] = await Promise.all([ - spacesManager.getActiveSpace(), + spacesManager.getActiveSpace(refreshActiveSpace), spacesManager.getSpaces(), ]); From b41073c9192b7446679b90a27fcd9843e3bf1b8e Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 11:04:55 -0400 Subject: [PATCH 03/42] fix advanced settings screen --- .../advanced_settings_subtitle.test.tsx.snap | 29 ---------- .../advanced_settings_subtitle.test.tsx | 17 ++++-- .../advanced_settings_subtitle.tsx | 54 +++++++++++-------- .../advanced_settings_title.test.tsx.snap | 45 ---------------- .../advanced_settings_title.test.tsx | 15 ++++-- .../advanced_settings_title.tsx | 48 ++++++++++------- .../spaces/public/views/management/index.tsx | 13 +++-- .../public/components/telemetry_form.js | 1 - .../public/views/management/management.js | 6 +-- 9 files changed, 98 insertions(+), 130 deletions(-) delete mode 100644 x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/__snapshots__/advanced_settings_subtitle.test.tsx.snap delete mode 100644 x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/__snapshots__/advanced_settings_title.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/__snapshots__/advanced_settings_subtitle.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/__snapshots__/advanced_settings_subtitle.test.tsx.snap deleted file mode 100644 index 69d1011de3c55..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/__snapshots__/advanced_settings_subtitle.test.tsx.snap +++ /dev/null @@ -1,29 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AdvancedSettingsSubtitle renders as expected 1`] = ` - - - - - My Space - , - } - } - /> -

- } - /> -
-`; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx index 43804b9ba44fc..4609f18f9d5a5 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx @@ -4,16 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { AdvancedSettingsSubtitle } from './advanced_settings_subtitle'; +import { EuiCallOut } from '@elastic/eui'; describe('AdvancedSettingsSubtitle', () => { - it('renders as expected', () => { + it('renders as expected', async () => { const space = { id: 'my-space', name: 'My Space', disabledFeatures: [], }; - expect(shallowWithIntl()).toMatchSnapshot(); + + const wrapper = mountWithIntl( + Promise.resolve(space)} /> + ); + + await Promise.resolve(); + await Promise.resolve(); + + wrapper.update(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(1); }); }); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx index 901bce019012f..c30139990add2 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx @@ -6,30 +6,40 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Fragment } from 'react'; +import React, { Fragment, useState, useEffect } from 'react'; import { Space } from '../../../../../common/model/space'; interface Props { - space: Space; + getActiveSpace: () => Promise; } -export const AdvancedSettingsSubtitle = (props: Props) => ( - - - - {props.space.name}, - }} - /> -

- } - /> -
-); +export const AdvancedSettingsSubtitle = (props: Props) => { + const [activeSpace, setActiveSpace] = useState(null); + + useEffect(() => { + props.getActiveSpace().then(space => setActiveSpace(space)); + }, []); + + if (!activeSpace) return null; + + return ( + + + + {activeSpace.name}, + }} + /> +

+ } + /> +
+ ); +}; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/__snapshots__/advanced_settings_title.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/__snapshots__/advanced_settings_title.test.tsx.snap deleted file mode 100644 index eba4a06a8e8aa..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/__snapshots__/advanced_settings_title.test.tsx.snap +++ /dev/null @@ -1,45 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AdvancedSettingsTitle renders as expected 1`] = ` - - - - - - -

- -

-
-
-
-`; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx index 3a91bcb6019ee..7f2b6eee62c45 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx @@ -4,16 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { AdvancedSettingsTitle } from './advanced_settings_title'; +import { SpaceAvatar } from '../../../../components'; describe('AdvancedSettingsTitle', () => { - it('renders as expected', () => { + it('renders without crashing', async () => { const space = { id: 'my-space', name: 'My Space', disabledFeatures: [], }; - expect(shallowWithIntl()).toMatchSnapshot(); + + const wrapper = mountWithIntl( + Promise.resolve(space)} /> + ); + + await Promise.resolve(); + await Promise.resolve(); + wrapper.update(); + expect(wrapper.find(SpaceAvatar)).toHaveLength(1); }); }); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx index 9ba38a12f436a..588503ce13084 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx @@ -6,28 +6,38 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { Space } from '../../../../../common/model/space'; import { SpaceAvatar } from '../../../../components'; interface Props { - space: Space; + getActiveSpace: () => Promise; } -export const AdvancedSettingsTitle = (props: Props) => ( - - - - - - -

- -

-
-
-
-); +export const AdvancedSettingsTitle = (props: Props) => { + const [activeSpace, setActiveSpace] = useState(null); + + useEffect(() => { + props.getActiveSpace().then(space => setActiveSpace(space)); + }, []); + + if (!activeSpace) return null; + + return ( + + + + + + +

+ +

+
+
+
+ ); +}; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/index.tsx b/x-pack/legacy/plugins/spaces/public/views/management/index.tsx index 656193b417aa7..16f00d1ccb74b 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/index.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/index.tsx @@ -15,14 +15,16 @@ import { } from 'ui/management'; // @ts-ignore import routes from 'ui/routes'; +import { getSpacesManager } from 'plugins/spaces/lib'; import { AdvancedSettingsSubtitle } from './components/advanced_settings_subtitle'; import { AdvancedSettingsTitle } from './components/advanced_settings_title'; +import { waitForSpacesNPInit } from '../../hacks/init_np_plugin'; const MANAGE_SPACES_KEY = 'spaces'; routes.defaults(/\/management/, { resolve: { - spacesManagementSection(activeSpace: any) { + spacesManagementSection() { function getKibanaSection() { return management.getSection('kibana'); } @@ -45,10 +47,15 @@ routes.defaults(/\/management/, { }); } - const PageTitle = () => ; + const getActiveSpace = async () => { + await waitForSpacesNPInit; + return getSpacesManager().getActiveSpace(); + }; + + const PageTitle = () => ; registerSettingsComponent(PAGE_TITLE_COMPONENT, PageTitle, true); - const SubTitle = () => ; + const SubTitle = () => ; registerSettingsComponent(PAGE_SUBTITLE_COMPONENT, SubTitle, true); } diff --git a/x-pack/legacy/plugins/telemetry/public/components/telemetry_form.js b/x-pack/legacy/plugins/telemetry/public/components/telemetry_form.js index dc3739bb7ae8e..2fb852d12d5c6 100644 --- a/x-pack/legacy/plugins/telemetry/public/components/telemetry_form.js +++ b/x-pack/legacy/plugins/telemetry/public/components/telemetry_form.js @@ -29,7 +29,6 @@ export class TelemetryForm extends Component { query: PropTypes.object, onQueryMatchChange: PropTypes.func.isRequired, spacesEnabled: PropTypes.bool.isRequired, - activeSpace: PropTypes.object, enableSaving: PropTypes.bool.isRequired, }; diff --git a/x-pack/legacy/plugins/telemetry/public/views/management/management.js b/x-pack/legacy/plugins/telemetry/public/views/management/management.js index 9e580f109d536..88f064fb3601d 100644 --- a/x-pack/legacy/plugins/telemetry/public/views/management/management.js +++ b/x-pack/legacy/plugins/telemetry/public/views/management/management.js @@ -12,17 +12,13 @@ import { TelemetryForm } from '../../components'; routes.defaults(/\/management/, { resolve: { - telemetryManagementSection: function (Private, spacesEnabled, activeSpace) { + telemetryManagementSection: function (Private, spacesEnabled) { const telemetryOptInProvider = Private(TelemetryOptInProvider); const spaceProps = { spacesEnabled, }; - if (spacesEnabled) { - spaceProps.activeSpace = activeSpace ? activeSpace.space : null; - } - const Component = (props) => ( Date: Thu, 11 Jul 2019 12:45:19 -0400 Subject: [PATCH 04/42] allow npStart from unauthed routes --- x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts b/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts index c79827978a5b2..6610072e0560a 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/api/v1/spaces.ts @@ -43,6 +43,7 @@ export function initInternalSpacesApi(deps: InternalRouteDeps) { }; }, options: { + auth: false, pre: [routePreCheckLicenseFn], }, }); From a014d729677eb9e9dd9e271f4ba6b3f281798b14 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 13:17:13 -0400 Subject: [PATCH 05/42] use NP for deriving space management url --- .../spaces/public/components/manage_spaces_button.tsx | 4 ++-- x-pack/legacy/plugins/spaces/public/lib/constants.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx b/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx index 5f05bc74ec86e..1e53b0a48e9b1 100644 --- a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx +++ b/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx @@ -8,7 +8,7 @@ import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, CSSProperties } from 'react'; import { capabilities } from 'ui/capabilities'; -import { MANAGE_SPACES_URL } from '../lib/constants'; +import { getManageSpacesUrl } from '../lib/constants'; interface Props { isDisabled?: boolean; @@ -44,6 +44,6 @@ export class ManageSpacesButton extends Component { if (this.props.onClick) { this.props.onClick(); } - window.location.replace(MANAGE_SPACES_URL); + window.location.replace(getManageSpacesUrl()); }; } diff --git a/x-pack/legacy/plugins/spaces/public/lib/constants.ts b/x-pack/legacy/plugins/spaces/public/lib/constants.ts index 7752ce1b6bc78..94799f6f2b5d8 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/constants.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/constants.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; +import { npSetup } from 'ui/new_platform'; let spacesFeatureDescription: string; @@ -20,4 +20,5 @@ export const getSpacesFeatureDescription = () => { return spacesFeatureDescription; }; -export const MANAGE_SPACES_URL = chrome.addBasePath(`/app/kibana#/management/spaces/list`); +export const getManageSpacesUrl = () => + npSetup.core.http.basePath.prepend(`/app/kibana#/management/spaces/list`); From b0bf4dd9efefc4dd20a16c13f4dfccb982badb81 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 13:34:30 -0400 Subject: [PATCH 06/42] remove security's usage of SpacesManager --- .../security/public/views/management/edit_role/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js index aa5bf65742895..2e03c17ee4795 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js @@ -18,7 +18,6 @@ import 'plugins/security/services/shield_indices'; import { IndexPatternsProvider } from 'ui/index_patterns/index_patterns'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import { SpacesManager } from '../../../../../spaces/public/lib'; import { ROLES_PATH, CLONE_ROLES_PATH, EDIT_ROLES_PATH } from '../management_urls'; import { getEditRoleBreadcrumbs, getCreateRoleBreadcrumbs } from '../breadcrumbs'; @@ -83,7 +82,7 @@ const routeDefinition = (action) => ({ }, spaces(spacesEnabled) { if (spacesEnabled) { - return new SpacesManager().getSpaces(); + return kfetch({ method: 'get', pathname: '/api/spaces/space' }); } return []; }, From c46f2837c7a0e1378d8650bb06a486865b3e07d3 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 13:43:01 -0400 Subject: [PATCH 07/42] remove usages of ui/capabilities --- .../public/__mocks__/ui_capabilities.ts | 24 ---------- .../components/manage_spaces_button.test.tsx | 45 ++++++++++--------- .../components/manage_spaces_button.tsx | 5 ++- .../secure_space_message.test.tsx | 40 ++++++++++------- .../secure_space_message.tsx | 9 ++-- .../enabled_features.test.tsx | 8 ++-- .../enabled_features/enabled_features.tsx | 6 +-- .../edit_space/manage_space_page.test.tsx | 29 +++++++++++- .../edit_space/manage_space_page.tsx | 11 ++--- .../public/views/management/page_routes.tsx | 18 +++++--- .../spaces_grid_pages.test.tsx.snap | 13 +++++- .../spaces_grid/spaces_grid_page.tsx | 9 ++-- .../spaces_grid/spaces_grid_pages.test.tsx | 22 ++++++++- .../nav_control_popover.test.tsx.snap | 10 +++++ .../spaces_description.test.tsx.snap | 10 +++++ .../components/spaces_description.test.tsx | 16 ++++++- .../components/spaces_description.tsx | 3 ++ .../nav_control/components/spaces_menu.tsx | 3 ++ .../public/views/nav_control/nav_control.tsx | 1 + .../nav_control/nav_control_popover.test.tsx | 2 + .../views/nav_control/nav_control_popover.tsx | 10 ++++- .../server/lib/toggle_ui_capabilities.test.ts | 30 ++++++------- .../server/lib/toggle_ui_capabilities.ts | 18 ++++---- 23 files changed, 225 insertions(+), 117 deletions(-) delete mode 100644 x-pack/legacy/plugins/spaces/public/__mocks__/ui_capabilities.ts diff --git a/x-pack/legacy/plugins/spaces/public/__mocks__/ui_capabilities.ts b/x-pack/legacy/plugins/spaces/public/__mocks__/ui_capabilities.ts deleted file mode 100644 index 532f83b27a369..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/__mocks__/ui_capabilities.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -jest.mock('ui/capabilities', () => ({ - capabilities: { - get: jest.fn().mockReturnValue({ - navLinks: {}, - management: {}, - catalogue: {}, - spaces: { - manage: true, - }, - }), - }, -})); - -import { capabilities, UICapabilities } from 'ui/capabilities'; - -export function setMockCapabilities(mockCapabilities: UICapabilities) { - ((capabilities.get as unknown) as jest.Mock).mockReturnValue(mockCapabilities); -} diff --git a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.test.tsx b/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.test.tsx index 00e306fe3d691..2dc6ae919c018 100644 --- a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.test.tsx @@ -4,37 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setMockCapabilities } from '../__mocks__/ui_capabilities'; import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { ManageSpacesButton } from './manage_spaces_button'; describe('ManageSpacesButton', () => { it('renders as expected', () => { - setMockCapabilities({ - navLinks: {}, - management: {}, - catalogue: {}, - spaces: { - manage: true, - }, - }); - - const component = ; + const component = ( + + ); expect(shallowWithIntl(component)).toMatchSnapshot(); }); it(`doesn't render if user profile forbids managing spaces`, () => { - setMockCapabilities({ - navLinks: {}, - management: {}, - catalogue: {}, - spaces: { - manage: false, - }, - }); - - const component = ; + const component = ( + + ); expect(shallowWithIntl(component)).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx b/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx index 1e53b0a48e9b1..beea9ab779f67 100644 --- a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx +++ b/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx @@ -7,7 +7,7 @@ import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, CSSProperties } from 'react'; -import { capabilities } from 'ui/capabilities'; +import { Capabilities } from 'src/core/public'; import { getManageSpacesUrl } from '../lib/constants'; interface Props { @@ -16,11 +16,12 @@ interface Props { size?: 's' | 'l'; style?: CSSProperties; onClick?: () => void; + capabilities: Capabilities; } export class ManageSpacesButton extends Component { public render() { - if (!capabilities.get().spaces.manage) { + if (!this.props.capabilities.spaces.manage) { return null; } diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx index 5be9f8c731eef..9695f02718df3 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx @@ -5,27 +5,37 @@ */ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { setMockCapabilities } from '../../../../__mocks__/ui_capabilities'; + import { SecureSpaceMessage } from './secure_space_message'; describe('SecureSpaceMessage', () => { it(`doesn't render if UI Capabilities does not allow security to be managed`, () => { - setMockCapabilities({ - navLinks: {}, - management: {}, - catalogue: {}, - spaces: { manage: false }, - }); - expect(shallowWithIntl()).toMatchSnapshot(); + expect( + shallowWithIntl( + + ) + ).toMatchSnapshot(); }); it(`renders if user profile allows security to be managed`, () => { - setMockCapabilities({ - navLinks: {}, - management: {}, - catalogue: {}, - spaces: { manage: true }, - }); - expect(shallowWithIntl()).toMatchSnapshot(); + expect( + shallowWithIntl( + + ) + ).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx index 0163260a59e35..6165790cf2a21 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx @@ -8,10 +8,13 @@ import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -import { capabilities } from 'ui/capabilities'; +import { Capabilities } from 'src/core/public'; -export const SecureSpaceMessage = ({}) => { - if (capabilities.get().spaces.manage) { +interface Props { + capabilities: Capabilities; +} +export const SecureSpaceMessage = ({ capabilities }: Props) => { + if (capabilities.spaces.manage) { const rolesLinkTextAriaLabel = i18n.translate( 'xpack.spaces.management.secureSpaceMessage.rolesLinkTextAriaLabel', { defaultMessage: 'Roles management page' } diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx index 7de4ab23cb256..e852e685ce882 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx @@ -33,7 +33,7 @@ const space: Space = { disabledFeatures: ['feature-1', 'feature-2'], }; -const uiCapabilities = { +const capabilities = { navLinks: {}, management: {}, catalogue: {}, @@ -49,7 +49,7 @@ describe('EnabledFeatures', () => { @@ -64,7 +64,7 @@ describe('EnabledFeatures', () => { @@ -99,7 +99,7 @@ describe('EnabledFeatures', () => { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx index a1a1655e3f0e3..95074272536ee 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx @@ -7,7 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component, Fragment, ReactNode } from 'react'; -import { UICapabilities } from 'ui/capabilities'; +import { Capabilities } from 'src/core/public'; import { Feature } from '../../../../../../xpack_main/types'; import { Space } from '../../../../../common/model/space'; import { getEnabledFeatures } from '../../lib/feature_utils'; @@ -17,7 +17,7 @@ import { FeatureTable } from './feature_table'; interface Props { space: Partial; features: Feature[]; - uiCapabilities: UICapabilities; + capabilities: Capabilities; intl: InjectedIntl; onChange: (space: Partial) => void; } @@ -130,7 +130,7 @@ export class EnabledFeatures extends Component { defaultMessage="The feature is hidden in the UI, but is not disabled." />

- {this.props.uiCapabilities.spaces.manage && ( + {this.props.capabilities.spaces.manage && (

{ spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); const wrapper = mountWithIntl( - + ); await waitForDataLoad(wrapper); @@ -71,6 +80,12 @@ describe('ManageSpacePage', () => { spaceId={'existing-space'} spacesManager={spacesManager} intl={null as any} + capabilities={{ + navLinks: {}, + management: {}, + catalogue: {}, + spaces: { manage: false }, + }} /> ); @@ -113,6 +128,12 @@ describe('ManageSpacePage', () => { spaceId={'my-space'} spacesManager={spacesManager} intl={null as any} + capabilities={{ + navLinks: {}, + management: {}, + catalogue: {}, + spaces: { manage: false }, + }} /> ); @@ -164,6 +185,12 @@ describe('ManageSpacePage', () => { spaceId={'my-space'} spacesManager={spacesManager} intl={null as any} + capabilities={{ + navLinks: {}, + management: {}, + catalogue: {}, + spaces: { manage: false }, + }} /> ); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx index 804c1d73d8be7..76b6db8c70783 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx @@ -17,10 +17,10 @@ import { import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; -import { capabilities } from 'ui/capabilities'; import { Breadcrumb } from 'ui/chrome'; import { kfetch } from 'ui/kfetch'; import { toastNotifications } from 'ui/notify'; +import { Capabilities } from 'src/core/public'; import { Feature } from '../../../../../xpack_main/types'; import { isReservedSpace } from '../../../../common'; import { Space } from '../../../../common/model/space'; @@ -40,6 +40,7 @@ interface Props { spaceId?: string; intl: InjectedIntl; setBreadcrumbs?: (breadcrumbs: Breadcrumb[]) => void; + capabilities: Capabilities; } interface State { @@ -71,7 +72,7 @@ class ManageSpacePageUI extends Component { } public async componentDidMount() { - if (!capabilities.get().spaces.manage) { + if (!this.props.capabilities.spaces.manage) { return; } @@ -137,7 +138,7 @@ class ManageSpacePageUI extends Component { ); public getForm = () => { - if (!capabilities.get().spaces.manage) { + if (!this.props.capabilities.spaces.manage) { return ; } @@ -171,7 +172,7 @@ class ManageSpacePageUI extends Component { @@ -214,7 +215,7 @@ class ManageSpacePageUI extends Component { public maybeGetSecureSpacesMessage = () => { if (this.editingExistingSpace()) { - return ; + return ; } return null; }; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx b/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx index 2a8c2a41531e7..1a0ed7811bd18 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx @@ -11,6 +11,7 @@ import 'ui/autoload/styles'; import { I18nContext } from 'ui/i18n'; // @ts-ignore import routes from 'ui/routes'; +import { npStart } from 'ui/new_platform'; import { getSpacesManager } from '../../lib/spaces_manager'; import { ManageSpacePage } from './edit_space'; import { getCreateBreadcrumbs, getEditBreadcrumbs, getListBreadcrumbs } from './lib'; @@ -32,7 +33,10 @@ routes.when('/management/spaces/list', { render( - + , domNode ); @@ -59,7 +63,10 @@ routes.when('/management/spaces/create', { render( - + , domNode ); @@ -82,7 +89,7 @@ routes.when('/management/spaces/edit/:spaceId', { template, k7Breadcrumbs: () => getEditBreadcrumbs(), requireUICapability: 'management.kibana.spaces', - controller($scope: any, $route: any, chrome: any) { + controller($scope: any, $route: any) { $scope.$$postDigest(async () => { const domNode = document.getElementById(reactRootNodeId); @@ -95,9 +102,8 @@ routes.when('/management/spaces/edit/:spaceId', { { - chrome.breadcrumbs.set(breadcrumbs); - }} + setBreadcrumbs={npStart.core.chrome.setBreadcrumbs} + capabilities={npStart.core.application.capabilities} /> , domNode diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap index cfe14ea07b010..d820773dcdfa1 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap @@ -128,7 +128,18 @@ exports[`SpacesGridPage renders as expected 1`] = ` sorting={false} /> - + `; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx index e572230f2cfc0..d34b7fd9a01b9 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx @@ -19,10 +19,10 @@ import { EuiTitle, } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import { capabilities } from 'ui/capabilities'; import { kfetch } from 'ui/kfetch'; // @ts-ignore import { toastNotifications } from 'ui/notify'; +import { Capabilities } from 'src/core/public'; import { Feature } from '../../../../../xpack_main/types'; import { isReservedSpace } from '../../../../common'; import { DEFAULT_SPACE_ID } from '../../../../common/constants'; @@ -38,6 +38,7 @@ import { getEnabledFeatures } from '../lib/feature_utils'; interface Props { spacesManager: SpacesManager; intl: InjectedIntl; + capabilities: Capabilities; } interface State { @@ -63,7 +64,7 @@ class SpacesGridPageUI extends Component { } public componentDidMount() { - if (capabilities.get().spaces.manage) { + if (this.props.capabilities.spaces.manage) { this.loadGrid(); } } @@ -72,7 +73,7 @@ class SpacesGridPageUI extends Component { return (

{this.getPageContent()} - + {this.getConfirmDeleteModal()}
); @@ -81,7 +82,7 @@ class SpacesGridPageUI extends Component { public getPageContent() { const { intl } = this.props; - if (!capabilities.get().spaces.manage) { + if (!this.props.capabilities.spaces.manage) { return ; } diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx index c8ae846adb9e7..42e1b2bd50a81 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx @@ -42,14 +42,32 @@ describe('SpacesGridPage', () => { it('renders as expected', () => { expect( shallowWithIntl( - + ) ).toMatchSnapshot(); }); it('renders the list of spaces', async () => { const wrapper = mountWithIntl( - + ); // allow spacesManager to load spaces diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap index a1288f3b9ae28..1d704161397c5 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap @@ -28,6 +28,16 @@ exports[`NavControlPopover renders without crashing 1`] = ` withTitle={true} > diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/__snapshots__/spaces_description.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/__snapshots__/spaces_description.test.tsx.snap index 8cce2a376746a..079dab701cc1d 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/__snapshots__/spaces_description.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/__snapshots__/spaces_description.test.tsx.snap @@ -19,6 +19,16 @@ exports[`SpacesDescription renders without crashing 1`] = ` key="manageSpacesButton" > { it('renders without crashing', () => { - expect(shallow()).toMatchSnapshot(); + expect( + shallow( + + ) + ).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx index fe071dba25467..c083b1fc96cff 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx @@ -6,11 +6,13 @@ import { EuiContextMenuPanel, EuiText } from '@elastic/eui'; import React, { SFC } from 'react'; +import { Capabilities } from 'src/core/public'; import { ManageSpacesButton } from '../../../components'; import { getSpacesFeatureDescription } from '../../../lib/constants'; interface Props { onManageSpacesClick: () => void; + capabilities: Capabilities; } export const SpacesDescription: SFC = (props: Props) => { @@ -29,6 +31,7 @@ export const SpacesDescription: SFC = (props: Props) => { size="s" style={{ width: `100%` }} onClick={props.onManageSpacesClick} + capabilities={props.capabilities} /> diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx index 76a47ca738627..5db0c03d466cf 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx @@ -7,6 +7,7 @@ import { EuiContextMenuItem, EuiContextMenuPanel, EuiFieldSearch, EuiText } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component } from 'react'; +import { Capabilities } from 'src/core/public'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../common/constants'; import { Space } from '../../../../common/model/space'; import { ManageSpacesButton, SpaceAvatar } from '../../../components'; @@ -16,6 +17,7 @@ interface Props { onSelectSpace: (space: Space) => void; onManageSpacesClick: () => void; intl: InjectedIntl; + capabilities: Capabilities; } interface State { @@ -151,6 +153,7 @@ class SpacesMenuUI extends Component { className="spcMenu__manageButton" size="s" onClick={this.props.onManageSpacesClick} + capabilities={this.props.capabilities} /> ); }; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx index 3ecb6662e7d63..5ff3571e169c0 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx @@ -28,6 +28,7 @@ export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreSta spacesManager={spacesManager} anchorPosition="downLeft" buttonClass={SpacesHeaderNavButton} + capabilities={core.application.capabilities} />
, targetDomElement diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx index 666126e1355a6..633ca44356cb9 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx @@ -20,6 +20,7 @@ describe('NavControlPopover', () => { spacesManager={spacesManager} anchorPosition={'downRight'} buttonClass={SpacesHeaderNavButton} + capabilities={{ navLinks: {}, management: {}, catalogue: {}, spaces: { manage: true } }} /> ); expect(wrapper).toMatchSnapshot(); @@ -50,6 +51,7 @@ describe('NavControlPopover', () => { spacesManager={spacesManager} anchorPosition={'rightCenter'} buttonClass={SpacesHeaderNavButton} + capabilities={{ navLinks: {}, management: {}, catalogue: {}, spaces: { manage: true } }} /> ); diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx index 5f0284401e977..62a69252f1cd7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx @@ -6,6 +6,7 @@ import { EuiAvatar, EuiPopover, PopoverAnchorPosition } from '@elastic/eui'; import React, { Component, ComponentClass } from 'react'; +import { Capabilities } from 'src/core/public'; import { Space } from '../../../common/model/space'; import { SpaceAvatar } from '../../components'; import { SpacesManager } from '../../lib/spaces_manager'; @@ -17,6 +18,7 @@ interface Props { spacesManager: SpacesManager; anchorPosition: PopoverAnchorPosition; buttonClass: ComponentClass; + capabilities: Capabilities; } interface State { @@ -55,13 +57,19 @@ export class NavControlPopover extends Component { let element: React.ReactNode; if (this.state.spaces.length < 2) { - element = ; + element = ( + + ); } else { element = ( ); } diff --git a/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.test.ts b/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.test.ts index fb9c8a85a4e2e..45d1266a0de4f 100644 --- a/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.test.ts +++ b/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.test.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UICapabilities } from 'ui/capabilities'; import { Feature } from '../../../xpack_main/types'; import { Space } from '../../common/model/space'; import { toggleUICapabilities } from './toggle_ui_capabilities'; +import { Capabilities } from 'src/core/public'; const features: Feature[] = [ { @@ -58,7 +58,7 @@ const features: Feature[] = [ }, ]; -const buildUiCapabilities = () => +const buildCapabilities = () => Object.freeze({ navLinks: { feature1: true, @@ -89,7 +89,7 @@ const buildUiCapabilities = () => foo: true, bar: true, }, - }) as UICapabilities; + }) as Capabilities; describe('toggleUiCapabilities', () => { it('does not toggle capabilities when the space has no disabled features', () => { @@ -99,9 +99,9 @@ describe('toggleUiCapabilities', () => { disabledFeatures: [], }; - const uiCapabilities: UICapabilities = buildUiCapabilities(); - const result = toggleUICapabilities(features, uiCapabilities, space); - expect(result).toEqual(buildUiCapabilities()); + const capabilities: Capabilities = buildCapabilities(); + const result = toggleUICapabilities(features, capabilities, space); + expect(result).toEqual(buildCapabilities()); }); it('ignores unknown disabledFeatures', () => { @@ -111,9 +111,9 @@ describe('toggleUiCapabilities', () => { disabledFeatures: ['i-do-not-exist'], }; - const uiCapabilities: UICapabilities = buildUiCapabilities(); - const result = toggleUICapabilities(features, uiCapabilities, space); - expect(result).toEqual(buildUiCapabilities()); + const capabilities: Capabilities = buildCapabilities(); + const result = toggleUICapabilities(features, capabilities, space); + expect(result).toEqual(buildCapabilities()); }); it('disables the corresponding navLink, catalogue, management sections, and all capability flags for disabled features', () => { @@ -123,10 +123,10 @@ describe('toggleUiCapabilities', () => { disabledFeatures: ['feature_2'], }; - const uiCapabilities: UICapabilities = buildUiCapabilities(); - const result = toggleUICapabilities(features, uiCapabilities, space); + const capabilities: Capabilities = buildCapabilities(); + const result = toggleUICapabilities(features, capabilities, space); - const expectedCapabilities = buildUiCapabilities(); + const expectedCapabilities = buildCapabilities(); expectedCapabilities.navLinks.feature2 = false; expectedCapabilities.catalogue.feature2Entry = false; @@ -144,10 +144,10 @@ describe('toggleUiCapabilities', () => { disabledFeatures: ['feature_1', 'feature_2', 'feature_3'], }; - const uiCapabilities: UICapabilities = buildUiCapabilities(); - const result = toggleUICapabilities(features, uiCapabilities, space); + const capabilities: Capabilities = buildCapabilities(); + const result = toggleUICapabilities(features, capabilities, space); - const expectedCapabilities = buildUiCapabilities(); + const expectedCapabilities = buildCapabilities(); expectedCapabilities.feature_1.bar = false; expectedCapabilities.feature_1.foo = false; diff --git a/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.ts b/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.ts index 4cec018959ab3..1cdb8df5c9169 100644 --- a/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.ts +++ b/x-pack/legacy/plugins/spaces/server/lib/toggle_ui_capabilities.ts @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ import _ from 'lodash'; -import { UICapabilities } from 'ui/capabilities'; +import { Capabilities } from 'src/core/public'; import { Feature } from '../../../xpack_main/types'; import { Space } from '../../common/model/space'; export function toggleUICapabilities( features: Feature[], - uiCapabilities: UICapabilities, + capabilities: Capabilities, activeSpace: Space ) { - const clonedCapabilities = _.cloneDeep(uiCapabilities); + const clonedCapabilities = _.cloneDeep(capabilities); toggleDisabledFeatures(features, clonedCapabilities, activeSpace); @@ -22,7 +22,7 @@ export function toggleUICapabilities( function toggleDisabledFeatures( features: Feature[], - uiCapabilities: UICapabilities, + capabilities: Capabilities, activeSpace: Space ) { const disabledFeatureKeys: string[] = activeSpace.disabledFeatures; @@ -31,9 +31,9 @@ function toggleDisabledFeatures( .map(key => features.find(feature => feature.id === key)) .filter(feature => typeof feature !== 'undefined') as Feature[]; - const navLinks: Record = uiCapabilities.navLinks; - const catalogueEntries: Record = uiCapabilities.catalogue; - const managementItems: Record> = uiCapabilities.management; + const navLinks: Record = capabilities.navLinks; + const catalogueEntries: Record = capabilities.catalogue; + const managementItems: Record> = capabilities.management; for (const feature of disabledFeatures) { // Disable associated navLink, if one exists @@ -61,8 +61,8 @@ function toggleDisabledFeatures( }); // Disable "sub features" that match the disabled feature - if (uiCapabilities.hasOwnProperty(feature.id)) { - const capability = uiCapabilities[feature.id]; + if (capabilities.hasOwnProperty(feature.id)) { + const capability = capabilities[feature.id]; Object.keys(capability).forEach(featureKey => { capability[featureKey] = false; }); From aed3888906ca5bc996b85942675670e447875271 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 14:49:10 -0400 Subject: [PATCH 08/42] fix tests --- .../management/edit_space/manage_space_page.test.tsx | 9 ++++----- .../management/spaces_grid/spaces_grid_pages.test.tsx | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx index 151d54d0138ea..3e96c36558c58 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx @@ -6,7 +6,6 @@ jest.mock('ui/kfetch', () => ({ kfetch: () => Promise.resolve([{ id: 'feature-1', name: 'feature 1' }]), })); -import '../../../__mocks__/ui_capabilities'; import { EuiButton, EuiLink, EuiSwitch } from '@elastic/eui'; import { ReactWrapper } from 'enzyme'; import React from 'react'; @@ -36,7 +35,7 @@ describe('ManageSpacePage', () => { navLinks: {}, management: {}, catalogue: {}, - spaces: { manage: false }, + spaces: { manage: true }, }} /> ); @@ -84,7 +83,7 @@ describe('ManageSpacePage', () => { navLinks: {}, management: {}, catalogue: {}, - spaces: { manage: false }, + spaces: { manage: true }, }} /> ); @@ -132,7 +131,7 @@ describe('ManageSpacePage', () => { navLinks: {}, management: {}, catalogue: {}, - spaces: { manage: false }, + spaces: { manage: true }, }} /> ); @@ -189,7 +188,7 @@ describe('ManageSpacePage', () => { navLinks: {}, management: {}, catalogue: {}, - spaces: { manage: false }, + spaces: { manage: true }, }} /> ); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx index 42e1b2bd50a81..7d04693ac41b1 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx @@ -6,7 +6,6 @@ jest.mock('ui/kfetch', () => ({ kfetch: () => Promise.resolve([]), })); -import '../../../__mocks__/ui_capabilities'; import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { SpaceAvatar } from '../../../components'; From 2aa3b71ab5d431368c406be92f974a18963d2904 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 15:42:42 -0400 Subject: [PATCH 09/42] implement NP plugin interface --- x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts | 4 ++-- x-pack/legacy/plugins/spaces/public/index.ts | 4 ++-- x-pack/legacy/plugins/spaces/public/plugin.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts b/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts index b634ce73353d2..fca2a4b3adf9d 100644 --- a/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts +++ b/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts @@ -6,9 +6,9 @@ import { npSetup, npStart } from 'ui/new_platform'; import { plugin } from '../index'; -import { Plugin } from '../plugin'; +import { SpacesPlugin } from '../plugin'; -const spacesPlugin: Plugin = plugin({}); +const spacesPlugin: SpacesPlugin = plugin({}); export const waitForSpacesNPInit = spacesPlugin .start(npStart.core) diff --git a/x-pack/legacy/plugins/spaces/public/index.ts b/x-pack/legacy/plugins/spaces/public/index.ts index 226f399b3b02f..ddd02aeebc5f8 100644 --- a/x-pack/legacy/plugins/spaces/public/index.ts +++ b/x-pack/legacy/plugins/spaces/public/index.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { PluginInitializerContext } from 'src/core/public'; -import { Plugin } from './plugin'; +import { SpacesPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => { - return new Plugin(initializerContext); + return new SpacesPlugin(initializerContext); }; diff --git a/x-pack/legacy/plugins/spaces/public/plugin.tsx b/x-pack/legacy/plugins/spaces/public/plugin.tsx index 6a9862c039fbd..27d08f77588bd 100644 --- a/x-pack/legacy/plugins/spaces/public/plugin.tsx +++ b/x-pack/legacy/plugins/spaces/public/plugin.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext, CoreSetup, CoreStart } from 'src/core/public'; +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { initSpacesManager } from './lib'; import { initSpacesNavControl } from './views/nav_control'; -export class Plugin { +export class SpacesPlugin implements Plugin<{}, {}> { // @ts-ignore constructor(private readonly initializerContext: PluginInitializerContext) {} From b039a99275a71e8aa9bf8ab7c11a7e0455c84040 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 16:17:19 -0400 Subject: [PATCH 10/42] remove hack in favor of convention in migration guide --- x-pack/legacy/plugins/spaces/index.ts | 2 +- .../{hacks/init_np_plugin.ts => legacy.ts} | 9 ++++---- .../legacy/plugins/spaces/public/lib/index.ts | 2 +- .../spaces/public/lib/spaces_manager.ts | 21 ------------------- .../legacy/plugins/spaces/public/plugin.tsx | 18 ++++++++++++---- .../spaces/public/views/management/index.tsx | 7 +++---- .../public/views/management/page_routes.tsx | 15 +++++++------ .../public/views/space_selector/index.tsx | 7 +++---- 8 files changed, 33 insertions(+), 48 deletions(-) rename x-pack/legacy/plugins/spaces/public/{hacks/init_np_plugin.ts => legacy.ts} (63%) diff --git a/x-pack/legacy/plugins/spaces/index.ts b/x-pack/legacy/plugins/spaces/index.ts index 82df69091ab88..82fd441c2a2d5 100644 --- a/x-pack/legacy/plugins/spaces/index.ts +++ b/x-pack/legacy/plugins/spaces/index.ts @@ -59,7 +59,7 @@ export const spaces = (kibana: Record) => hidden: true, }, ], - hacks: ['plugins/spaces/hacks/init_np_plugin'], + hacks: [], mappings, migrations: { space: { diff --git a/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts b/x-pack/legacy/plugins/spaces/public/legacy.ts similarity index 63% rename from x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts rename to x-pack/legacy/plugins/spaces/public/legacy.ts index fca2a4b3adf9d..f2e7fd98e763e 100644 --- a/x-pack/legacy/plugins/spaces/public/hacks/init_np_plugin.ts +++ b/x-pack/legacy/plugins/spaces/public/legacy.ts @@ -5,11 +5,10 @@ */ import { npSetup, npStart } from 'ui/new_platform'; -import { plugin } from '../index'; -import { SpacesPlugin } from '../plugin'; +import { plugin } from './index'; +import { SpacesPlugin } from './plugin'; const spacesPlugin: SpacesPlugin = plugin({}); -export const waitForSpacesNPInit = spacesPlugin - .start(npStart.core) - .then(() => spacesPlugin.setup(npSetup.core)); +export const setup = spacesPlugin.setup(npSetup.core); +export const start = spacesPlugin.start(npStart.core); diff --git a/x-pack/legacy/plugins/spaces/public/lib/index.ts b/x-pack/legacy/plugins/spaces/public/lib/index.ts index 243f85509c55c..538dd77e053f5 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/index.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { SpacesManager, initSpacesManager, getSpacesManager } from './spaces_manager'; +export { SpacesManager } from './spaces_manager'; diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts index 6b41afbcb62d0..c335e8ea197d3 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts +++ b/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts @@ -8,27 +8,6 @@ import { EventEmitter } from 'events'; import { NotificationsSetup, HttpSetup } from 'src/core/public'; import { Space } from '../../common/model/space'; -let spacesManager: SpacesManager | undefined; -export const initSpacesManager = ( - spaceSelectorUrl: string, - http: HttpSetup, - notifications: NotificationsSetup -) => { - if (spacesManager) { - throw new Error('SpacesManager has already been initialized!'); - } - spacesManager = new SpacesManager(spaceSelectorUrl, http, notifications); - return spacesManager; -}; - -export const getSpacesManager = () => { - if (!spacesManager) { - throw new Error('SpacesManager has not been initialized yet'); - } - - return spacesManager; -}; - export class SpacesManager extends EventEmitter { private activeSpace: Space | undefined; diff --git a/x-pack/legacy/plugins/spaces/public/plugin.tsx b/x-pack/legacy/plugins/spaces/public/plugin.tsx index 27d08f77588bd..9bbd828f17881 100644 --- a/x-pack/legacy/plugins/spaces/public/plugin.tsx +++ b/x-pack/legacy/plugins/spaces/public/plugin.tsx @@ -5,18 +5,28 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { initSpacesManager } from './lib'; +import { SpacesManager } from './lib'; import { initSpacesNavControl } from './views/nav_control'; -export class SpacesPlugin implements Plugin<{}, {}> { +export interface SpacesPluginStart { + spacesManager: SpacesManager; +} + +export class SpacesPlugin implements Plugin<{}, SpacesPluginStart> { + private spacesManager: SpacesManager | undefined; + // @ts-ignore constructor(private readonly initializerContext: PluginInitializerContext) {} public async start(core: CoreStart) { const { spaceSelectorUrl } = await core.http.get('/api/spaces/v1/npStart'); - const spacesManager = initSpacesManager(spaceSelectorUrl, core.http, core.notifications); + this.spacesManager = new SpacesManager(spaceSelectorUrl, core.http, core.notifications); + + initSpacesNavControl(this.spacesManager, core); - initSpacesNavControl(spacesManager, core); + return { + spacesManager: this.spacesManager, + }; } public async setup(core: CoreSetup) {} diff --git a/x-pack/legacy/plugins/spaces/public/views/management/index.tsx b/x-pack/legacy/plugins/spaces/public/views/management/index.tsx index 16f00d1ccb74b..1fe117b2ebd8f 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/index.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/index.tsx @@ -15,10 +15,9 @@ import { } from 'ui/management'; // @ts-ignore import routes from 'ui/routes'; -import { getSpacesManager } from 'plugins/spaces/lib'; import { AdvancedSettingsSubtitle } from './components/advanced_settings_subtitle'; import { AdvancedSettingsTitle } from './components/advanced_settings_title'; -import { waitForSpacesNPInit } from '../../hacks/init_np_plugin'; +import { start as spacesNPStart } from '../../legacy'; const MANAGE_SPACES_KEY = 'spaces'; @@ -48,8 +47,8 @@ routes.defaults(/\/management/, { } const getActiveSpace = async () => { - await waitForSpacesNPInit; - return getSpacesManager().getActiveSpace(); + const { spacesManager } = await spacesNPStart; + return spacesManager.getActiveSpace(); }; const PageTitle = () => ; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx b/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx index 1a0ed7811bd18..b23453fcc091c 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx @@ -12,12 +12,11 @@ import { I18nContext } from 'ui/i18n'; // @ts-ignore import routes from 'ui/routes'; import { npStart } from 'ui/new_platform'; -import { getSpacesManager } from '../../lib/spaces_manager'; import { ManageSpacePage } from './edit_space'; import { getCreateBreadcrumbs, getEditBreadcrumbs, getListBreadcrumbs } from './lib'; import { SpacesGridPage } from './spaces_grid'; -import { waitForSpacesNPInit } from '../../hacks/init_np_plugin'; +import { start as spacesNPStart } from '../../legacy'; const reactRootNodeId = 'manageSpacesReactRoot'; @@ -29,12 +28,12 @@ routes.when('/management/spaces/list', { $scope.$$postDigest(async () => { const domNode = document.getElementById(reactRootNodeId); - await waitForSpacesNPInit; + const { spacesManager } = await spacesNPStart; render( , @@ -59,12 +58,12 @@ routes.when('/management/spaces/create', { $scope.$$postDigest(async () => { const domNode = document.getElementById(reactRootNodeId); - await waitForSpacesNPInit; + const { spacesManager } = await spacesNPStart; render( , @@ -95,13 +94,13 @@ routes.when('/management/spaces/edit/:spaceId', { const { spaceId } = $route.current.params; - await waitForSpacesNPInit; + const { spacesManager } = await spacesNPStart; render( diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx b/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx index c731454bbb351..6af31c39e5f0a 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getSpacesManager } from 'plugins/spaces/lib/spaces_manager'; // @ts-ignore import template from 'plugins/spaces/views/space_selector/space_selector.html'; import 'ui/autoload/styles'; @@ -17,18 +16,18 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { SpaceSelector } from './space_selector'; -import { waitForSpacesNPInit } from '../../hacks/init_np_plugin'; +import { start as spacesNPStart } from '../../legacy'; const module = uiModules.get('spaces_selector', []); module.controller('spacesSelectorController', ($scope: any) => { $scope.$$postDigest(async () => { const domNode = document.getElementById('spaceSelectorRoot'); - await waitForSpacesNPInit; + const { spacesManager } = await spacesNPStart; render( - + , domNode ); From 8c161f23f2762feb9284efc15980032d4184e45e Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 11 Jul 2019 16:31:59 -0400 Subject: [PATCH 11/42] shim feature catalogue registration --- x-pack/legacy/plugins/spaces/index.ts | 2 +- x-pack/legacy/plugins/spaces/public/legacy.ts | 13 +++++++++++-- x-pack/legacy/plugins/spaces/public/plugin.tsx | 13 +++++++++++-- .../plugins/spaces/public/register_feature.ts | 10 ++++------ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/spaces/index.ts b/x-pack/legacy/plugins/spaces/index.ts index 82fd441c2a2d5..d2b2899e76d25 100644 --- a/x-pack/legacy/plugins/spaces/index.ts +++ b/x-pack/legacy/plugins/spaces/index.ts @@ -71,7 +71,7 @@ export const spaces = (kibana: Record) => isNamespaceAgnostic: true, }, }, - home: ['plugins/spaces/register_feature'], + home: ['plugins/spaces/legacy'], }, async init(server: Server) { diff --git a/x-pack/legacy/plugins/spaces/public/legacy.ts b/x-pack/legacy/plugins/spaces/public/legacy.ts index f2e7fd98e763e..6c0b064a59da5 100644 --- a/x-pack/legacy/plugins/spaces/public/legacy.ts +++ b/x-pack/legacy/plugins/spaces/public/legacy.ts @@ -5,10 +5,19 @@ */ import { npSetup, npStart } from 'ui/new_platform'; +import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; import { plugin } from './index'; -import { SpacesPlugin } from './plugin'; +import { SpacesPlugin, PluginsSetup } from './plugin'; const spacesPlugin: SpacesPlugin = plugin({}); +const plugins: PluginsSetup = { + kibana: { + registerCatalogueFeature: fn => { + FeatureCatalogueRegistryProvider.register(fn); + }, + }, +}; + export const setup = spacesPlugin.setup(npSetup.core); -export const start = spacesPlugin.start(npStart.core); +export const start = spacesPlugin.start(npStart.core, plugins); diff --git a/x-pack/legacy/plugins/spaces/public/plugin.tsx b/x-pack/legacy/plugins/spaces/public/plugin.tsx index 9bbd828f17881..8d43a6d0dc594 100644 --- a/x-pack/legacy/plugins/spaces/public/plugin.tsx +++ b/x-pack/legacy/plugins/spaces/public/plugin.tsx @@ -5,24 +5,33 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { FeatureCatalogueRegistryFunction } from 'ui/registry/feature_catalogue'; import { SpacesManager } from './lib'; import { initSpacesNavControl } from './views/nav_control'; +import { createSpacesFeatureCatalogueEntry } from './register_feature'; export interface SpacesPluginStart { spacesManager: SpacesManager; } -export class SpacesPlugin implements Plugin<{}, SpacesPluginStart> { +export interface PluginsSetup { + kibana: { + registerCatalogueFeature: (fn: FeatureCatalogueRegistryFunction) => void; + }; +} + +export class SpacesPlugin implements Plugin<{}, SpacesPluginStart, PluginsSetup> { private spacesManager: SpacesManager | undefined; // @ts-ignore constructor(private readonly initializerContext: PluginInitializerContext) {} - public async start(core: CoreStart) { + public async start(core: CoreStart, plugins: PluginsSetup) { const { spaceSelectorUrl } = await core.http.get('/api/spaces/v1/npStart'); this.spacesManager = new SpacesManager(spaceSelectorUrl, core.http, core.notifications); initSpacesNavControl(this.spacesManager, core); + plugins.kibana.registerCatalogueFeature(createSpacesFeatureCatalogueEntry); return { spacesManager: this.spacesManager, diff --git a/x-pack/legacy/plugins/spaces/public/register_feature.ts b/x-pack/legacy/plugins/spaces/public/register_feature.ts index c8ebfd8db5686..9981a542ccd6e 100644 --- a/x-pack/legacy/plugins/spaces/public/register_feature.ts +++ b/x-pack/legacy/plugins/spaces/public/register_feature.ts @@ -4,18 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; import { FeatureCatalogueCategory, - FeatureCatalogueRegistryProvider, - // @ts-ignore + FeatureCatalogueRegistryFunction, } from 'ui/registry/feature_catalogue'; import { getSpacesFeatureDescription } from './lib/constants'; -FeatureCatalogueRegistryProvider.register(() => { +export const createSpacesFeatureCatalogueEntry: FeatureCatalogueRegistryFunction = i18n => { return { id: 'spaces', - title: i18n.translate('xpack.spaces.spacesTitle', { + title: i18n('xpack.spaces.spacesTitle', { defaultMessage: 'Spaces', }), description: getSpacesFeatureDescription(), @@ -24,4 +22,4 @@ FeatureCatalogueRegistryProvider.register(() => { showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, }; -}); +}; From 3a010f18b465d1f26d353466f58c915d776d93c7 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 12 Jul 2019 09:56:56 -0400 Subject: [PATCH 12/42] streamline nav control, and handle async loading more gracefully --- .../nav_control_popover.test.tsx.snap | 29 +++++++------ .../components/spaces_header_nav_button.tsx | 29 ------------- .../nav_control/components/spaces_menu.tsx | 23 ++++++++-- .../public/views/nav_control/nav_control.tsx | 2 - .../nav_control/nav_control_popover.test.tsx | 3 -- .../views/nav_control/nav_control_popover.tsx | 42 +++++++++++-------- 6 files changed, 61 insertions(+), 67 deletions(-) delete mode 100644 x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap index 1d704161397c5..9dee693769e58 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap @@ -4,18 +4,18 @@ exports[`NavControlPopover renders without crashing 1`] = ` - } - linkTitle="loading" - spaceSelectorShown={false} - toggleSpaceSelector={[Function]} - /> + + + } closePopover={[Function]} data-test-subj="spacesNavSelector" @@ -27,7 +27,7 @@ exports[`NavControlPopover renders without crashing 1`] = ` repositionOnScroll={true} withTitle={true} > - `; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx deleted file mode 100644 index 45bb79ee749ee..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - // @ts-ignore - EuiHeaderSectionItemButton, -} from '@elastic/eui'; -import React, { Component } from 'react'; -import { ButtonProps } from '../types'; - -export class SpacesHeaderNavButton extends Component { - public render() { - return ( - - {this.props.linkIcon} - - ); - } -} diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx index 5db0c03d466cf..7a21b1e26c6c0 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiContextMenuItem, EuiContextMenuPanel, EuiFieldSearch, EuiText } from '@elastic/eui'; +import { + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFieldSearch, + EuiText, + EuiLoadingContent, +} from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { Capabilities } from 'src/core/public'; @@ -14,6 +20,7 @@ import { ManageSpacesButton, SpaceAvatar } from '../../../components'; interface Props { spaces: Space[]; + isLoading: boolean; onSelectSpace: (space: Space) => void; onManageSpacesClick: () => void; intl: InjectedIntl; @@ -32,10 +39,12 @@ class SpacesMenuUI extends Component { }; public render() { - const { intl } = this.props; + const { intl, isLoading } = this.props; const { searchTerm } = this.state; - const items = this.getVisibleSpaces(searchTerm).map(this.renderSpaceMenuItem); + const items = isLoading + ? [1, 2, 3].map(this.rendePlaceholderMenuItem) + : this.getVisibleSpaces(searchTerm).map(this.renderSpaceMenuItem); const panelProps = { className: 'spcMenu', @@ -178,6 +187,14 @@ class SpacesMenuUI extends Component { ); }; + + private rendePlaceholderMenuItem = (key: string | number): JSX.Element => { + return ( + null}> + + + ); + }; } export const SpacesMenu = injectI18n(SpacesMenuUI); diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx index 5ff3571e169c0..2646618bf0c6b 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx @@ -11,7 +11,6 @@ import { Path } from 'plugins/xpack_main/services/path'; import React from 'react'; import ReactDOM from 'react-dom'; import { CoreStart } from 'src/core/public'; -import { SpacesHeaderNavButton } from './components/spaces_header_nav_button'; export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreStart) { const I18nContext = core.i18n.Context; @@ -27,7 +26,6 @@ export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreSta , diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx index 633ca44356cb9..de0945d006a4a 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx @@ -8,7 +8,6 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; import { SpaceAvatar } from '../../components'; import { spacesManagerMock } from '../../lib/mocks'; -import { SpacesHeaderNavButton } from './components/spaces_header_nav_button'; import { NavControlPopover } from './nav_control_popover'; describe('NavControlPopover', () => { @@ -19,7 +18,6 @@ describe('NavControlPopover', () => { ); @@ -50,7 +48,6 @@ describe('NavControlPopover', () => { ); diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx index 62a69252f1cd7..d716bc3d65834 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx @@ -4,20 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiAvatar, EuiPopover, PopoverAnchorPosition } from '@elastic/eui'; -import React, { Component, ComponentClass } from 'react'; +import { + EuiPopover, + PopoverAnchorPosition, + EuiLoadingSpinner, + // @ts-ignore + EuiHeaderSectionItemButton, +} from '@elastic/eui'; +import React, { Component } from 'react'; import { Capabilities } from 'src/core/public'; import { Space } from '../../../common/model/space'; import { SpaceAvatar } from '../../components'; import { SpacesManager } from '../../lib/spaces_manager'; import { SpacesDescription } from './components/spaces_description'; import { SpacesMenu } from './components/spaces_menu'; -import { ButtonProps } from './types'; interface Props { spacesManager: SpacesManager; anchorPosition: PopoverAnchorPosition; - buttonClass: ComponentClass; capabilities: Capabilities; } @@ -56,7 +60,7 @@ export class NavControlPopover extends Component { } let element: React.ReactNode; - if (this.state.spaces.length < 2) { + if (!this.state.loading && this.state.spaces.length < 2) { element = ( { element = ( { } return ( - // @ts-ignore repositionOnScroll doesn't exist on EuiPopover { private async loadSpaces(refreshActiveSpace: boolean = false) { const { spacesManager } = this.props; + if (this.state.loading) { + return; + } + this.setState({ loading: true, }); @@ -116,10 +124,7 @@ export class NavControlPopover extends Component { const { activeSpace } = this.state; if (!activeSpace) { - return this.getButton( - , - 'loading' - ); + return this.getButton(, 'loading'); } return this.getButton( @@ -129,14 +134,17 @@ export class NavControlPopover extends Component { }; private getButton = (linkIcon: JSX.Element, linkTitle: string) => { - const Button = this.props.buttonClass; return ( -