diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts
index 4cc907c3de9e4..39392d0c5c78e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts
@@ -33,6 +33,12 @@ describe('KibanaLogic', () => {
expect(KibanaLogic.values.config).toEqual({});
});
+ it('gracefully handles disabled security', () => {
+ mountKibanaLogic({ ...mockKibanaValues, security: undefined } as any);
+
+ expect(KibanaLogic.values.security).toEqual({});
+ });
+
it('gracefully handles non-cloud installs', () => {
mountKibanaLogic({ ...mockKibanaValues, cloud: undefined } as any);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx
index 3d5d0a8e6f2cf..04b0880a7351c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx
@@ -9,6 +9,9 @@ jest.mock('../../../shared/layout', () => ({
...jest.requireActual('../../../shared/layout'),
generateNavLink: jest.fn(({ to }) => ({ href: to })),
}));
+jest.mock('../../views/content_sources/components/source_sub_nav', () => ({
+ useSourceSubNav: () => [],
+}));
jest.mock('../../views/groups/components/group_sub_nav', () => ({
useGroupSubNav: () => [],
}));
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx
index f59679e0ee048..99225bc36e892 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx
@@ -19,6 +19,7 @@ import {
GROUPS_PATH,
ORG_SETTINGS_PATH,
} from '../../routes';
+import { useSourceSubNav } from '../../views/content_sources/components/source_sub_nav';
import { useGroupSubNav } from '../../views/groups/components/group_sub_nav';
import { useSettingsSubNav } from '../../views/settings/components/settings_sub_nav';
@@ -33,7 +34,7 @@ export const useWorkplaceSearchNav = () => {
id: 'sources',
name: NAV.SOURCES,
...generateNavLink({ to: SOURCES_PATH }),
- items: [], // TODO: Source subnav
+ items: useSourceSubNav(),
},
{
id: 'groups',
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss
index 175f6b9ebca20..3287cb21783cb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss
@@ -6,18 +6,20 @@
*/
.personalDashboardLayout {
- $sideBarWidth: $euiSize * 30;
- $consoleHeaderHeight: 48px; // NOTE: Keep an eye on this for changes
- $pageHeight: calc(100vh - #{$consoleHeaderHeight});
+ &__sideBar {
+ padding: $euiSizeXL $euiSizeXXL $euiSizeXXL;
- left: $sideBarWidth;
- width: calc(100% - #{$sideBarWidth});
- min-height: $pageHeight;
+ @include euiBreakpoint('m', 'l') {
+ min-width: $euiSize * 20;
+ }
+ @include euiBreakpoint('xl') {
+ min-width: $euiSize * 30;
+ }
+ }
- &__sideBar {
- padding: 32px 40px 40px;
- width: $sideBarWidth;
- margin-left: -$sideBarWidth;
- height: $pageHeight;
+ &__body {
+ position: relative;
+ width: 100%;
+ height: 100%;
}
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx
index faeaa7323e93f..6847e91d46f6e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx
@@ -5,37 +5,102 @@
* 2.0.
*/
+import { setMockValues } from '../../../../__mocks__/kea_logic';
+import { mockUseRouteMatch } from '../../../../__mocks__/react_router';
+
import React from 'react';
import { shallow } from 'enzyme';
import { EuiCallOut } from '@elastic/eui';
-import { AccountHeader } from '..';
+import { FlashMessages } from '../../../../shared/flash_messages';
+import { SetWorkplaceSearchChrome } from '../../../../shared/kibana_chrome';
+import { Loading } from '../../../../shared/loading';
+
+import { AccountHeader, AccountSettingsSidebar, PrivateSourcesSidebar } from '../index';
import { PersonalDashboardLayout } from './personal_dashboard_layout';
describe('PersonalDashboardLayout', () => {
const children =
test
;
- const sidebar = test
;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ setMockValues({ readOnlyMode: false });
+ });
it('renders', () => {
- const wrapper = shallow(
- {children}
- );
+ const wrapper = shallow({children});
expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="TestSidebar"]')).toHaveLength(1);
+ expect(wrapper.find('.personalDashboardLayout')).toHaveLength(1);
expect(wrapper.find(AccountHeader)).toHaveLength(1);
+ expect(wrapper.find(FlashMessages)).toHaveLength(1);
});
- it('renders callout when in read-only mode', () => {
+ describe('renders sidebar content based on the route', () => {
+ it('renders the private sources sidebar on the private sources path', () => {
+ (mockUseRouteMatch as jest.Mock).mockImplementation((path: string) => path === '/p/sources');
+ const wrapper = shallow({children});
+
+ expect(wrapper.find(PrivateSourcesSidebar)).toHaveLength(1);
+ });
+
+ it('renders the account settings sidebar on the account settings path', () => {
+ (mockUseRouteMatch as jest.Mock).mockImplementation((path: string) => path === '/p/settings');
+ const wrapper = shallow({children});
+
+ expect(wrapper.find(AccountSettingsSidebar)).toHaveLength(1);
+ });
+
+ it('does not render a sidebar if not on a valid personal dashboard path', () => {
+ (mockUseRouteMatch as jest.Mock).mockImplementation((path: string) => path === '/test');
+ const wrapper = shallow({children});
+
+ expect(wrapper.find(AccountSettingsSidebar)).toHaveLength(0);
+ expect(wrapper.find(PrivateSourcesSidebar)).toHaveLength(0);
+ });
+ });
+
+ describe('loading state', () => {
+ it('renders a loading icon in place of children', () => {
+ const wrapper = shallow(
+ {children}
+ );
+
+ expect(wrapper.find(Loading)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(0);
+ });
+
+ it('renders children & does not render a loading icon when the page is done loading', () => {
+ const wrapper = shallow(
+ {children}
+ );
+
+ expect(wrapper.find(Loading)).toHaveLength(0);
+ expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1);
+ });
+ });
+
+ it('sets WS page chrome (primarily document title)', () => {
const wrapper = shallow(
-
+
{children}
);
+ expect(wrapper.find(SetWorkplaceSearchChrome).prop('trail')).toEqual([
+ 'Sources',
+ 'Add source',
+ 'Gmail',
+ ]);
+ });
+
+ it('renders callout when in read-only mode', () => {
+ setMockValues({ readOnlyMode: true });
+ const wrapper = shallow({children});
+
expect(wrapper.find(EuiCallOut)).toHaveLength(1);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx
index 1ab9e07dfa14d..5b68d661ac5df 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx
@@ -6,44 +6,67 @@
*/
import React from 'react';
+import { useRouteMatch } from 'react-router-dom';
-import { EuiPage, EuiPageSideBar, EuiPageBody, EuiCallOut } from '@elastic/eui';
+import { useValues } from 'kea';
-import { AccountHeader } from '..';
+import {
+ EuiPage,
+ EuiPageSideBar,
+ EuiPageBody,
+ EuiPageContentBody,
+ EuiCallOut,
+ EuiSpacer,
+} from '@elastic/eui';
+import { FlashMessages } from '../../../../shared/flash_messages';
+import { HttpLogic } from '../../../../shared/http';
+import { SetWorkplaceSearchChrome } from '../../../../shared/kibana_chrome';
+import { BreadcrumbTrail } from '../../../../shared/kibana_chrome/generate_breadcrumbs';
+import { Loading } from '../../../../shared/loading';
+
+import { PERSONAL_SOURCES_PATH, PERSONAL_SETTINGS_PATH } from '../../../routes';
import { PRIVATE_DASHBOARD_READ_ONLY_MODE_WARNING } from '../../../views/content_sources/constants';
+import { AccountHeader, AccountSettingsSidebar, PrivateSourcesSidebar } from '../index';
import './personal_dashboard_layout.scss';
interface LayoutProps {
- restrictWidth?: boolean;
- readOnlyMode?: boolean;
- sidebar: React.ReactNode;
+ isLoading?: boolean;
+ pageChrome?: BreadcrumbTrail;
}
export const PersonalDashboardLayout: React.FC = ({
children,
- restrictWidth,
- readOnlyMode,
- sidebar,
+ isLoading,
+ pageChrome,
}) => {
+ const { readOnlyMode } = useValues(HttpLogic);
+
return (
<>
+ {pageChrome && }
-
-
- {sidebar}
+
+
+ {useRouteMatch(PERSONAL_SOURCES_PATH) && }
+ {useRouteMatch(PERSONAL_SETTINGS_PATH) && }
-
- {readOnlyMode && (
-
- )}
- {children}
+
+
+ {readOnlyMode && (
+ <>
+
+
+ >
+ )}
+
+ {isLoading ? : children}
+
>
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx
index 387724af970f8..9fa4d4dd1b237 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx
@@ -7,17 +7,22 @@
import { setMockValues } from '../../../../__mocks__/kea_logic';
+jest.mock('../../../views/content_sources/components/source_sub_nav', () => ({
+ useSourceSubNav: () => [],
+}));
+
import React from 'react';
import { shallow } from 'enzyme';
+import { EuiSideNav } from '@elastic/eui';
+
import {
PRIVATE_CAN_CREATE_PAGE_TITLE,
PRIVATE_VIEW_ONLY_PAGE_TITLE,
PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION,
PRIVATE_CAN_CREATE_PAGE_DESCRIPTION,
} from '../../../constants';
-import { SourceSubNav } from '../../../views/content_sources/components/source_sub_nav';
import { ViewContentHeader } from '../../shared/view_content_header';
@@ -26,6 +31,7 @@ import { PrivateSourcesSidebar } from './private_sources_sidebar';
describe('PrivateSourcesSidebar', () => {
const mockValues = {
account: { canCreatePersonalSources: true },
+ contentSource: {},
};
beforeEach(() => {
@@ -36,25 +42,42 @@ describe('PrivateSourcesSidebar', () => {
const wrapper = shallow();
expect(wrapper.find(ViewContentHeader)).toHaveLength(1);
- expect(wrapper.find(SourceSubNav)).toHaveLength(1);
});
- it('uses correct title and description when private sources are enabled', () => {
- const wrapper = shallow();
+ describe('header text', () => {
+ it('uses correct title and description when private sources are enabled', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_CAN_CREATE_PAGE_TITLE);
+ expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
+ PRIVATE_CAN_CREATE_PAGE_DESCRIPTION
+ );
+ });
- expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_CAN_CREATE_PAGE_TITLE);
- expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
- PRIVATE_CAN_CREATE_PAGE_DESCRIPTION
- );
+ it('uses correct title and description when private sources are disabled', () => {
+ setMockValues({ ...mockValues, account: { canCreatePersonalSources: false } });
+ const wrapper = shallow();
+
+ expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_VIEW_ONLY_PAGE_TITLE);
+ expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
+ PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION
+ );
+ });
});
- it('uses correct title and description when private sources are disabled', () => {
- setMockValues({ account: { canCreatePersonalSources: false } });
- const wrapper = shallow();
+ describe('sub nav', () => {
+ it('renders a side nav when viewing a single source', () => {
+ setMockValues({ ...mockValues, contentSource: { id: '1', name: 'test source' } });
+ const wrapper = shallow();
+
+ expect(wrapper.find(EuiSideNav)).toHaveLength(1);
+ });
+
+ it('does not render a side nav if not on a source page', () => {
+ setMockValues({ ...mockValues, contentSource: {} });
+ const wrapper = shallow();
- expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_VIEW_ONLY_PAGE_TITLE);
- expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
- PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION
- );
+ expect(wrapper.find(EuiSideNav)).toHaveLength(0);
+ });
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx
index 5505ae57b2ad5..36496b83b3123 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx
@@ -9,6 +9,8 @@ import React from 'react';
import { useValues } from 'kea';
+import { EuiSideNav } from '@elastic/eui';
+
import { AppLogic } from '../../../app_logic';
import {
PRIVATE_CAN_CREATE_PAGE_TITLE,
@@ -16,7 +18,8 @@ import {
PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION,
PRIVATE_CAN_CREATE_PAGE_DESCRIPTION,
} from '../../../constants';
-import { SourceSubNav } from '../../../views/content_sources/components/source_sub_nav';
+import { useSourceSubNav } from '../../../views/content_sources/components/source_sub_nav';
+import { SourceLogic } from '../../../views/content_sources/source_logic';
import { ViewContentHeader } from '../../shared/view_content_header';
export const PrivateSourcesSidebar = () => {
@@ -31,10 +34,17 @@ export const PrivateSourcesSidebar = () => {
? PRIVATE_CAN_CREATE_PAGE_DESCRIPTION
: PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION;
+ const {
+ contentSource: { id = '', name = '' },
+ } = useValues(SourceLogic);
+
+ const navItems = [{ id, name, items: useSourceSubNav() }];
+
return (
<>
-
+ {/* @ts-expect-error: TODO, uncomment this once EUI 34.x lands in Kibana & `mobileBreakpoints` is a valid prop */}
+ {id && }
>
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx
index f4278d5083143..8a1e9c0275322 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx
@@ -19,11 +19,6 @@ import { NotFound } from '../shared/not_found';
import { AppLogic } from './app_logic';
import { WorkplaceSearchNav, WorkplaceSearchHeaderActions } from './components/layout';
-import {
- PersonalDashboardLayout,
- PrivateSourcesSidebar,
- AccountSettingsSidebar,
-} from './components/layout';
import {
GROUPS_PATH,
SETUP_GUIDE_PATH,
@@ -34,11 +29,11 @@ import {
ROLE_MAPPINGS_PATH,
SECURITY_PATH,
PERSONAL_SETTINGS_PATH,
+ PERSONAL_PATH,
} from './routes';
import { AccountSettings } from './views/account_settings';
import { SourcesRouter } from './views/content_sources';
import { SourceAdded } from './views/content_sources/components/source_added';
-import { SourceSubNav } from './views/content_sources/components/source_sub_nav';
import { ErrorState } from './views/error_state';
import { GroupsRouter } from './views/groups';
import { Overview } from './views/overview';
@@ -60,9 +55,6 @@ export const WorkplaceSearchConfigured: React.FC = (props) => {
const { pathname } = useLocation();
- // We don't want so show the subnavs on the container root pages.
- const showSourcesSubnav = pathname !== SOURCES_PATH && pathname !== PERSONAL_SOURCES_PATH;
-
/**
* Personal dashboard urls begin with /p/
* EX: http://localhost:5601/app/enterprise_search/workplace_search/p/sources
@@ -95,32 +87,18 @@ export const WorkplaceSearchConfigured: React.FC = (props) => {
-
- }
- >
-
-
-
-
- }
- >
-
-
+
+
+
+
+
+
+
+
+
- } />}
- restrictWidth
- readOnlyMode={readOnlyMode}
- >
-
-
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx
index a5a3d6b491bb9..b89a1451f7e57 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx
@@ -76,13 +76,13 @@ describe('getReindexJobRoute', () => {
it('should format org path', () => {
expect(getReindexJobRoute(SOURCE_ID, REINDEX_ID, true)).toEqual(
- `/sources/${SOURCE_ID}/schema_errors/${REINDEX_ID}`
+ `/sources/${SOURCE_ID}/schemas/${REINDEX_ID}`
);
});
it('should format user path', () => {
expect(getReindexJobRoute(SOURCE_ID, REINDEX_ID, false)).toEqual(
- `/p/sources/${SOURCE_ID}/schema_errors/${REINDEX_ID}`
+ `/p/sources/${SOURCE_ID}/schemas/${REINDEX_ID}`
);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts
index 1fe8019c4b364..3c564c1f912ec 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts
@@ -88,7 +88,7 @@ export const SOURCE_CONTENT_PATH = `${SOURCES_PATH}/:sourceId/content`;
export const SOURCE_SCHEMAS_PATH = `${SOURCES_PATH}/:sourceId/schemas`;
export const SOURCE_DISPLAY_SETTINGS_PATH = `${SOURCES_PATH}/:sourceId/display_settings`;
export const SOURCE_SETTINGS_PATH = `${SOURCES_PATH}/:sourceId/settings`;
-export const REINDEX_JOB_PATH = `${SOURCES_PATH}/:sourceId/schema_errors/:activeReindexJobId`;
+export const REINDEX_JOB_PATH = `${SOURCE_SCHEMAS_PATH}/:activeReindexJobId`;
export const DISPLAY_SETTINGS_SEARCH_RESULT_PATH = `${SOURCE_DISPLAY_SETTINGS_PATH}/`;
export const DISPLAY_SETTINGS_RESULT_DETAIL_PATH = `${SOURCE_DISPLAY_SETTINGS_PATH}/result_detail`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.test.tsx
new file mode 100644
index 0000000000000..5ff80a7683db6
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.test.tsx
@@ -0,0 +1,57 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import '../../../__mocks__/shallow_useeffect.mock';
+import { mockKibanaValues } from '../../../__mocks__/kea_logic';
+
+import React from 'react';
+
+import { shallow } from 'enzyme';
+
+import { AccountSettings } from './';
+
+describe('AccountSettings', () => {
+ const {
+ security: {
+ authc: { getCurrentUser },
+ uiApi: {
+ components: { getPersonalInfo, getChangePassword },
+ },
+ },
+ } = mockKibanaValues;
+
+ const mockCurrentUser = (user?: unknown) =>
+ (getCurrentUser as jest.Mock).mockReturnValue(Promise.resolve(user));
+
+ beforeAll(() => {
+ mockCurrentUser();
+ });
+
+ it('gets the current user on mount', () => {
+ shallow();
+
+ expect(getCurrentUser).toHaveBeenCalled();
+ });
+
+ it('does not render if the current user does not exist', async () => {
+ mockCurrentUser(null);
+ const wrapper = await shallow();
+
+ expect(wrapper.isEmptyRender()).toBe(true);
+ });
+
+ it('renders the security UI components when the user exists', async () => {
+ mockCurrentUser({ username: 'mock user' });
+ (getPersonalInfo as jest.Mock).mockReturnValue();
+ (getChangePassword as jest.Mock).mockReturnValue();
+
+ const wrapper = await shallow();
+
+ expect(wrapper.childAt(0).dive().find('[data-test-subj="PersonalInfo"]')).toHaveLength(1);
+ expect(wrapper.childAt(1).dive().find('[data-test-subj="ChangePassword"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx
index e28faaeec8993..313d3ffa59d48 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx
@@ -11,6 +11,8 @@ import { useValues } from 'kea';
import type { AuthenticatedUser } from '../../../../../../security/public';
import { KibanaLogic } from '../../../shared/kibana/kibana_logic';
+import { PersonalDashboardLayout } from '../../components/layout';
+import { ACCOUNT_SETTINGS_TITLE } from '../../constants';
export const AccountSettings: React.FC = () => {
const { security } = useValues(KibanaLogic);
@@ -31,9 +33,9 @@ export const AccountSettings: React.FC = () => {
}
return (
- <>
+
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx
index 92cbfcf6eeafe..0501509b3a8ef 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx
@@ -17,7 +17,10 @@ import React from 'react';
import { shallow } from 'enzyme';
-import { Loading } from '../../../../../shared/loading';
+import {
+ WorkplaceSearchPageTemplate,
+ PersonalDashboardLayout,
+} from '../../../../components/layout';
import { AddSource } from './add_source';
import { AddSourceSteps } from './add_source_logic';
@@ -68,11 +71,27 @@ describe('AddSourceList', () => {
expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep);
});
- it('handles loading state', () => {
- setMockValues({ ...mockValues, dataLoading: true });
+ describe('layout', () => {
+ it('renders the default workplace search layout when on an organization view', () => {
+ setMockValues({ ...mockValues, isOrganization: true });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate);
+ });
+
+ it('renders the personal dashboard layout when not in an organization', () => {
+ setMockValues({ ...mockValues, isOrganization: false });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(PersonalDashboardLayout);
+ });
+ });
+
+ it('renders a breadcrumb fallback while data is loading', () => {
+ setMockValues({ ...mockValues, dataLoading: true, sourceConfigData: {} });
const wrapper = shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
+ expect(wrapper.prop('pageChrome')).toEqual(['Sources', 'Add Source', '...']);
});
it('renders Config Completed step', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx
index ee4bcfb9afd34..b0c3ebe64830c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx
@@ -13,9 +13,12 @@ import { i18n } from '@kbn/i18n';
import { setSuccessMessage } from '../../../../../shared/flash_messages';
import { KibanaLogic } from '../../../../../shared/kibana';
-import { Loading } from '../../../../../shared/loading';
import { AppLogic } from '../../../../app_logic';
-import { CUSTOM_SERVICE_TYPE } from '../../../../constants';
+import {
+ WorkplaceSearchPageTemplate,
+ PersonalDashboardLayout,
+} from '../../../../components/layout';
+import { NAV, CUSTOM_SERVICE_TYPE } from '../../../../constants';
import { SOURCES_PATH, getSourcesPath } from '../../../../routes';
import { SourceDataItem } from '../../../../types';
import { staticSourceData } from '../../source_data';
@@ -71,8 +74,6 @@ export const AddSource: React.FC = (props) => {
return resetSourceState;
}, []);
- if (dataLoading) return ;
-
const goToConfigurationIntro = () => setAddSourceStep(AddSourceSteps.ConfigIntroStep);
const goToSaveConfig = () => setAddSourceStep(AddSourceSteps.SaveConfigStep);
const setConfigCompletedStep = () => setAddSourceStep(AddSourceSteps.ConfigCompletedStep);
@@ -99,9 +100,10 @@ export const AddSource: React.FC = (props) => {
};
const header = ;
+ const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout;
return (
- <>
+
{addSourceCurrentStep === AddSourceSteps.ConfigIntroStep && (
)}
@@ -158,6 +160,6 @@ export const AddSource: React.FC = (props) => {
{addSourceCurrentStep === AddSourceSteps.ReauthenticateStep && (
)}
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx
index 6bf71cd73ec35..b30511f0a6d80 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.test.tsx
@@ -19,7 +19,11 @@ import { shallow } from 'enzyme';
import { EuiEmptyPrompt, EuiFieldSearch } from '@elastic/eui';
-import { Loading } from '../../../../../shared/loading';
+import { getPageDescription } from '../../../../../test_helpers';
+import {
+ WorkplaceSearchPageTemplate,
+ PersonalDashboardLayout,
+} from '../../../../components/layout';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
import { AddSourceList } from './add_source_list';
@@ -54,14 +58,21 @@ describe('AddSourceList', () => {
expect(wrapper.find(AvailableSourcesList)).toHaveLength(1);
});
- it('returns loading when loading', () => {
- setMockValues({
- ...mockValues,
- dataLoading: true,
+ describe('layout', () => {
+ it('renders the default workplace search layout when on an organization view', () => {
+ setMockValues({ ...mockValues, isOrganization: true });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate);
});
- const wrapper = shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
+ it('renders the personal dashboard layout and a header when not in an organization', () => {
+ setMockValues({ ...mockValues, isOrganization: false });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(PersonalDashboardLayout);
+ expect(wrapper.find(ViewContentHeader)).toHaveLength(1);
+ });
});
describe('filters sources', () => {
@@ -97,49 +108,51 @@ describe('AddSourceList', () => {
});
describe('content headings', () => {
- it('should render correct organization heading with sources', () => {
- const wrapper = shallow();
-
- expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
- ADD_SOURCE_ORG_SOURCE_DESCRIPTION
- );
- });
+ describe('organization view', () => {
+ it('should render the correct organization heading with sources', () => {
+ const wrapper = shallow();
- it('should render correct organization heading without sources', () => {
- setMockValues({
- ...mockValues,
- contentSources: [],
+ expect(getPageDescription(wrapper)).toEqual(ADD_SOURCE_ORG_SOURCE_DESCRIPTION);
});
- const wrapper = shallow();
- expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
- ADD_SOURCE_NEW_SOURCE_DESCRIPTION + ADD_SOURCE_ORG_SOURCE_DESCRIPTION
- );
- });
+ it('should render the correct organization heading without sources', () => {
+ setMockValues({
+ ...mockValues,
+ contentSources: [],
+ });
+ const wrapper = shallow();
- it('should render correct account heading with sources', () => {
- const wrapper = shallow();
- setMockValues({
- ...mockValues,
- isOrganization: false,
+ expect(getPageDescription(wrapper)).toEqual(
+ ADD_SOURCE_NEW_SOURCE_DESCRIPTION + ADD_SOURCE_ORG_SOURCE_DESCRIPTION
+ );
});
-
- expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
- ADD_SOURCE_ORG_SOURCE_DESCRIPTION
- );
});
- it('should render correct account heading without sources', () => {
- setMockValues({
- ...mockValues,
- isOrganization: false,
- contentSources: [],
+ describe('personal dashboard view', () => {
+ it('should render the correct personal heading with sources', () => {
+ setMockValues({
+ ...mockValues,
+ isOrganization: false,
+ });
+ const wrapper = shallow();
+
+ expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
+ ADD_SOURCE_PRIVATE_SOURCE_DESCRIPTION
+ );
});
- const wrapper = shallow();
- expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
- ADD_SOURCE_NEW_SOURCE_DESCRIPTION + ADD_SOURCE_PRIVATE_SOURCE_DESCRIPTION
- );
+ it('should render the correct personal heading without sources', () => {
+ setMockValues({
+ ...mockValues,
+ isOrganization: false,
+ contentSources: [],
+ });
+ const wrapper = shallow();
+
+ expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
+ ADD_SOURCE_NEW_SOURCE_DESCRIPTION + ADD_SOURCE_PRIVATE_SOURCE_DESCRIPTION
+ );
+ });
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx
index 80d35553bb8bb..a7a64194cb42f 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_list.tsx
@@ -19,12 +19,15 @@ import {
EuiEmptyPrompt,
} from '@elastic/eui';
-import { Loading } from '../../../../../shared/loading';
import { AppLogic } from '../../../../app_logic';
import noSharedSourcesIcon from '../../../../assets/share_circle.svg';
+import {
+ WorkplaceSearchPageTemplate,
+ PersonalDashboardLayout,
+} from '../../../../components/layout';
import { ContentSection } from '../../../../components/shared/content_section';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
-import { CUSTOM_SERVICE_TYPE } from '../../../../constants';
+import { NAV, CUSTOM_SERVICE_TYPE } from '../../../../constants';
import { SourceDataItem } from '../../../../types';
import { SourcesLogic } from '../../sources_logic';
@@ -58,8 +61,6 @@ export const AddSourceList: React.FC = () => {
return resetSourcesState;
}, []);
- if (dataLoading) return ;
-
const hasSources = contentSources.length > 0;
const showConfiguredSourcesList = configuredSources.find(
({ serviceType }) => serviceType !== CUSTOM_SERVICE_TYPE
@@ -97,12 +98,22 @@ export const AddSourceList: React.FC = () => {
filterConfiguredSources
) as SourceDataItem[];
+ const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout;
+
return (
- <>
-
+
+ {!isOrganization && (
+
+
+
+ )}
{showConfiguredSourcesList || isOrganization ? (
-
{
)}
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx
index aa5cec385738d..e5714bf4bdfbf 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx
@@ -16,7 +16,6 @@ import { shallow } from 'enzyme';
import { EuiButton, EuiTabbedContent } from '@elastic/eui';
-import { Loading } from '../../../../../shared/loading';
import { UnsavedChangesPrompt } from '../../../../../shared/unsaved_changes_prompt';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
@@ -57,13 +56,6 @@ describe('DisplaySettings', () => {
expect(wrapper.find('form')).toHaveLength(1);
});
- it('returns loading when loading', () => {
- setMockValues({ ...values, dataLoading: true });
- const wrapper = shallow();
-
- expect(wrapper.find(Loading)).toHaveLength(1);
- });
-
describe('tabbed content', () => {
const tabs = [
{
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx
index d923fbe7a1a8e..ae47e20026b68 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx
@@ -20,10 +20,10 @@ import {
} from '@elastic/eui';
import { clearFlashMessages } from '../../../../../shared/flash_messages';
-import { Loading } from '../../../../../shared/loading';
import { UnsavedChangesPrompt } from '../../../../../shared/unsaved_changes_prompt';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
-import { SAVE_BUTTON } from '../../../../constants';
+import { NAV, SAVE_BUTTON } from '../../../../constants';
+import { SourceLayout } from '../source_layout';
import {
UNSAVED_MESSAGE,
@@ -64,8 +64,6 @@ export const DisplaySettings: React.FC = ({ tabId }) => {
return clearFlashMessages;
}, []);
- if (dataLoading) return ;
-
const tabs = [
{
id: 'search_results',
@@ -89,7 +87,11 @@ export const DisplaySettings: React.FC = ({ tabId }) => {
};
return (
- <>
+
= ({ tabId }) => {
)}
{addFieldModalVisible && }
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx
index f2cf5f50b813b..d99eac5de74e5 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx
@@ -5,8 +5,6 @@
* 2.0.
*/
-import '../../../../__mocks__/shallow_useeffect.mock';
-
import { setMockValues } from '../../../../__mocks__/kea_logic';
import { fullContentSources } from '../../../__mocks__/content_sources.mock';
@@ -16,7 +14,6 @@ import { shallow } from 'enzyme';
import { EuiEmptyPrompt, EuiPanel, EuiTable } from '@elastic/eui';
-import { Loading } from '../../../../shared/loading';
import { ComponentLoader } from '../../../components/shared/component_loader';
import { Overview } from './overview';
@@ -44,13 +41,6 @@ describe('Overview', () => {
expect(documentSummary.find('[data-test-subj="DocumentSummaryRow"]')).toHaveLength(1);
});
- it('returns Loading when loading', () => {
- setMockValues({ ...mockValues, dataLoading: true });
- const wrapper = shallow();
-
- expect(wrapper.find(Loading)).toHaveLength(1);
- });
-
it('renders ComponentLoader when loading', () => {
setMockValues({
...mockValues,
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx
index 153df1bc00496..cc890e0f104ac 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx
@@ -29,7 +29,6 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { Loading } from '../../../../shared/loading';
import { EuiPanelTo } from '../../../../shared/react_router_helpers';
import { AppLogic } from '../../../app_logic';
import aclImage from '../../../assets/supports_acl.svg';
@@ -78,8 +77,10 @@ import {
} from '../constants';
import { SourceLogic } from '../source_logic';
+import { SourceLayout } from './source_layout';
+
export const Overview: React.FC = () => {
- const { contentSource, dataLoading } = useValues(SourceLogic);
+ const { contentSource } = useValues(SourceLogic);
const { isOrganization } = useValues(AppLogic);
const {
@@ -97,8 +98,6 @@ export const Overview: React.FC = () => {
isFederatedSource,
} = contentSource;
- if (dataLoading) return ;
-
const DocumentSummary = () => {
let totalDocuments = 0;
const tableContent = summary?.map((item, index) => {
@@ -450,8 +449,9 @@ export const Overview: React.FC = () => {
);
return (
- <>
+
+
@@ -513,6 +513,6 @@ export const Overview: React.FC = () => {
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx
index 178c9125ee437..47859e4e67b17 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx
@@ -16,7 +16,6 @@ import { shallow } from 'enzyme';
import { EuiEmptyPrompt, EuiFieldSearch } from '@elastic/eui';
-import { Loading } from '../../../../../shared/loading';
import { SchemaAddFieldModal, SchemaErrorsCallout } from '../../../../../shared/schema';
import { Schema } from './schema';
@@ -71,13 +70,6 @@ describe('Schema', () => {
expect(wrapper.find(SchemaFieldsTable)).toHaveLength(1);
});
- it('returns loading when loading', () => {
- setMockValues({ ...mockValues, dataLoading: true });
- const wrapper = shallow();
-
- expect(wrapper.find(Loading)).toHaveLength(1);
- });
-
it('handles empty state', () => {
setMockValues({ ...mockValues, activeSchema: {} });
const wrapper = shallow();
@@ -106,7 +98,7 @@ describe('Schema', () => {
expect(wrapper.find(SchemaErrorsCallout)).toHaveLength(1);
expect(wrapper.find(SchemaErrorsCallout).prop('viewErrorsPath')).toEqual(
- '/sources/123/schema_errors/123'
+ '/sources/123/schemas/123'
);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx
index 65ed988f45ff0..a0efebdcb5a48 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx
@@ -20,11 +20,12 @@ import {
EuiPanel,
} from '@elastic/eui';
-import { Loading } from '../../../../../shared/loading';
import { SchemaAddFieldModal, SchemaErrorsCallout } from '../../../../../shared/schema';
import { AppLogic } from '../../../../app_logic';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
+import { NAV } from '../../../../constants';
import { getReindexJobRoute } from '../../../../routes';
+import { SourceLayout } from '../source_layout';
import {
SCHEMA_ADD_FIELD_BUTTON,
@@ -65,8 +66,6 @@ export const Schema: React.FC = () => {
initializeSchema();
}, []);
- if (dataLoading) return ;
-
const hasSchemaFields = Object.keys(activeSchema).length > 0;
const { hasErrors, activeReindexJobId } = mostRecentIndexJob;
@@ -77,7 +76,11 @@ export const Schema: React.FC = () => {
);
return (
- <>
+
{
closeAddFieldModal={closeAddFieldModal}
/>
)}
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx
index e300823aa3ed3..eb07beda73327 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx
@@ -12,6 +12,8 @@ import { useActions, useValues } from 'kea';
import { SchemaErrorsAccordion } from '../../../../../shared/schema';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
+import { NAV } from '../../../../constants';
+import { SourceLayout } from '../source_layout';
import { SCHEMA_ERRORS_HEADING } from './constants';
import { SchemaLogic } from './schema_logic';
@@ -30,9 +32,12 @@ export const SchemaChangeErrors: React.FC = () => {
}, []);
return (
- <>
+
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx
index 4bcc4b16166d1..9304f0f344a1b 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx
@@ -25,7 +25,6 @@ import {
} from '@elastic/eui';
import { DEFAULT_META } from '../../../../shared/constants';
-import { Loading } from '../../../../shared/loading';
import { ComponentLoader } from '../../../components/shared/component_loader';
import { TablePaginationBar } from '../../../components/shared/table_pagination_bar';
@@ -61,13 +60,6 @@ describe('SourceContent', () => {
expect(wrapper.find(EuiTable)).toHaveLength(1);
});
- it('returns Loading when loading', () => {
- setMockValues({ ...mockValues, dataLoading: true });
- const wrapper = shallow();
-
- expect(wrapper.find(Loading)).toHaveLength(1);
- });
-
it('returns ComponentLoader when section loading', () => {
setMockValues({ ...mockValues, sectionLoading: true });
const wrapper = shallow();
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx
index fbafe54df7493..a0e3c28f20eb0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx
@@ -31,12 +31,11 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { Loading } from '../../../../shared/loading';
import { TruncatedContent } from '../../../../shared/truncate';
import { ComponentLoader } from '../../../components/shared/component_loader';
import { TablePaginationBar } from '../../../components/shared/table_pagination_bar';
import { ViewContentHeader } from '../../../components/shared/view_content_header';
-import { CUSTOM_SERVICE_TYPE } from '../../../constants';
+import { NAV, CUSTOM_SERVICE_TYPE } from '../../../constants';
import { CUSTOM_SOURCE_DOCS_URL } from '../../../routes';
import { SourceContentItem } from '../../../types';
import {
@@ -51,6 +50,8 @@ import {
} from '../constants';
import { SourceLogic } from '../source_logic';
+import { SourceLayout } from './source_layout';
+
const MAX_LENGTH = 28;
export const SourceContent: React.FC = () => {
@@ -67,7 +68,6 @@ export const SourceContent: React.FC = () => {
},
contentItems,
contentFilterValue,
- dataLoading,
sectionLoading,
} = useValues(SourceLogic);
@@ -75,8 +75,6 @@ export const SourceContent: React.FC = () => {
searchContentSourceDocuments(id);
}, [contentFilterValue, activePage]);
- if (dataLoading) return ;
-
const showPagination = totalPages > 1;
const hasItems = totalItems > 0;
const emptyMessage = contentFilterValue
@@ -193,7 +191,7 @@ export const SourceContent: React.FC = () => {
);
return (
- <>
+
@@ -219,6 +217,6 @@ export const SourceContent: React.FC = () => {
{sectionLoading && }
{!sectionLoading && (hasItems ? contentTable : emptyState)}
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_layout.test.tsx
new file mode 100644
index 0000000000000..7c7d77ec418e7
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_layout.test.tsx
@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import '../../../../__mocks__/shallow_useeffect.mock';
+
+import { setMockValues } from '../../../../__mocks__/kea_logic';
+import { contentSources } from '../../../__mocks__/content_sources.mock';
+
+import React from 'react';
+
+import { shallow } from 'enzyme';
+
+import { EuiCallOut } from '@elastic/eui';
+
+import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../../components/layout';
+
+import { SourceInfoCard } from './source_info_card';
+import { SourceLayout } from './source_layout';
+
+describe('SourceLayout', () => {
+ const contentSource = contentSources[1];
+ const mockValues = {
+ contentSource,
+ dataLoading: false,
+ isOrganization: true,
+ };
+
+ beforeEach(() => {
+ setMockValues({ ...mockValues });
+ });
+
+ it('renders', () => {
+ const wrapper = shallow(
+
+
+
+ );
+
+ expect(wrapper.find(SourceInfoCard)).toHaveLength(1);
+ expect(wrapper.find('.testChild')).toHaveLength(1);
+ });
+
+ it('renders the default Workplace Search layout when on an organization view', () => {
+ setMockValues({ ...mockValues, isOrganization: true });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate);
+ });
+
+ it('renders a personal dashboard layout when not on an organization view', () => {
+ setMockValues({ ...mockValues, isOrganization: false });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(PersonalDashboardLayout);
+ });
+
+ it('passes any page template props to the underlying page template', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find(WorkplaceSearchPageTemplate).prop('pageViewTelemetry')).toEqual('test');
+ });
+
+ it('handles breadcrumbs while loading', () => {
+ setMockValues({
+ ...mockValues,
+ contentSource: {},
+ dataLoading: true,
+ });
+ const wrapper = shallow();
+
+ expect(wrapper.prop('pageChrome')).toEqual(['Sources', '...']);
+ });
+
+ it('renders a callout when the source is not supported by the current license', () => {
+ setMockValues({ ...mockValues, contentSource: { supportedByLicense: false } });
+ const wrapper = shallow();
+
+ expect(wrapper.find(EuiCallOut)).toHaveLength(1);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_layout.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_layout.tsx
new file mode 100644
index 0000000000000..446e93e0c61f3
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_layout.tsx
@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+
+import { useValues } from 'kea';
+import moment from 'moment';
+
+import { EuiButton, EuiCallOut, EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
+
+import { PageTemplateProps } from '../../../../shared/layout';
+import { AppLogic } from '../../../app_logic';
+import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../../components/layout';
+import { NAV } from '../../../constants';
+import { ENT_SEARCH_LICENSE_MANAGEMENT } from '../../../routes';
+
+import {
+ SOURCE_DISABLED_CALLOUT_TITLE,
+ SOURCE_DISABLED_CALLOUT_DESCRIPTION,
+ SOURCE_DISABLED_CALLOUT_BUTTON,
+} from '../constants';
+import { SourceLogic } from '../source_logic';
+
+import { SourceInfoCard } from './source_info_card';
+
+export const SourceLayout: React.FC = ({
+ children,
+ pageChrome = [],
+ ...props
+}) => {
+ const { contentSource, dataLoading } = useValues(SourceLogic);
+ const { isOrganization } = useValues(AppLogic);
+
+ const {
+ name,
+ createdAt,
+ serviceType,
+ serviceName,
+ isFederatedSource,
+ supportedByLicense,
+ } = contentSource;
+
+ const pageHeader = (
+ <>
+
+
+ >
+ );
+
+ const callout = (
+ <>
+
+ {SOURCE_DISABLED_CALLOUT_DESCRIPTION}
+
+ {SOURCE_DISABLED_CALLOUT_BUTTON}
+
+
+
+ >
+ );
+
+ const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout;
+
+ return (
+
+ {!supportedByLicense && callout}
+ {pageHeader}
+ {children}
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx
index 26c37c25dcfee..dab82a31d38f7 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx
@@ -26,6 +26,8 @@ import { AppLogic } from '../../../app_logic';
import { ContentSection } from '../../../components/shared/content_section';
import { SourceConfigFields } from '../../../components/shared/source_config_fields';
import { ViewContentHeader } from '../../../components/shared/view_content_header';
+import { NAV } from '../../../constants';
+
import {
CANCEL_BUTTON,
OK_BUTTON,
@@ -52,6 +54,7 @@ import { staticSourceData } from '../source_data';
import { SourceLogic } from '../source_logic';
import { AddSourceLogic } from './add_source/add_source_logic';
+import { SourceLayout } from './source_layout';
export const SourceSettings: React.FC = () => {
const { updateContentSource, removeContentSource } = useActions(SourceLogic);
@@ -129,7 +132,7 @@ export const SourceSettings: React.FC = () => {
);
return (
- <>
+
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx
index 25c389419d731..7f07c59587f96 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx
@@ -7,34 +7,92 @@
import { setMockValues } from '../../../../__mocks__/kea_logic';
-import React from 'react';
+jest.mock('../../../../shared/layout', () => ({
+ generateNavLink: jest.fn(({ to }) => ({ href: to })),
+}));
-import { shallow } from 'enzyme';
+import { useSourceSubNav } from './source_sub_nav';
-import { SideNavLink } from '../../../../shared/layout';
-import { CUSTOM_SERVICE_TYPE } from '../../../constants';
+describe('useSourceSubNav', () => {
+ it('returns undefined when no content source id present', () => {
+ setMockValues({ contentSource: {} });
-import { SourceSubNav } from './source_sub_nav';
+ expect(useSourceSubNav()).toEqual(undefined);
+ });
-describe('SourceSubNav', () => {
- it('renders empty when no group id present', () => {
- setMockValues({ contentSource: {} });
- const wrapper = shallow();
+ it('returns EUI nav items', () => {
+ setMockValues({ isOrganization: true, contentSource: { id: '1' } });
- expect(wrapper.find(SideNavLink)).toHaveLength(0);
+ expect(useSourceSubNav()).toEqual([
+ {
+ id: 'sourceOverview',
+ name: 'Overview',
+ href: '/sources/1',
+ },
+ {
+ id: 'sourceContent',
+ name: 'Content',
+ href: '/sources/1/content',
+ },
+ {
+ id: 'sourceSettings',
+ name: 'Settings',
+ href: '/sources/1/settings',
+ },
+ ]);
});
- it('renders nav items', () => {
- setMockValues({ contentSource: { id: '1' } });
- const wrapper = shallow();
+ it('returns extra nav items for custom sources', () => {
+ setMockValues({ isOrganization: true, contentSource: { id: '2', serviceType: 'custom' } });
- expect(wrapper.find(SideNavLink)).toHaveLength(3);
+ expect(useSourceSubNav()).toEqual([
+ {
+ id: 'sourceOverview',
+ name: 'Overview',
+ href: '/sources/2',
+ },
+ {
+ id: 'sourceContent',
+ name: 'Content',
+ href: '/sources/2/content',
+ },
+ {
+ id: 'sourceSchema',
+ name: 'Schema',
+ href: '/sources/2/schemas',
+ },
+ {
+ id: 'sourceDisplaySettings',
+ name: 'Display Settings',
+ href: '/sources/2/display_settings',
+ },
+ {
+ id: 'sourceSettings',
+ name: 'Settings',
+ href: '/sources/2/settings',
+ },
+ ]);
});
- it('renders custom source nav items', () => {
- setMockValues({ contentSource: { id: '1', serviceType: CUSTOM_SERVICE_TYPE } });
- const wrapper = shallow();
+ it('returns nav links to personal dashboard when not on an organization page', () => {
+ setMockValues({ isOrganization: false, contentSource: { id: '3' } });
- expect(wrapper.find(SideNavLink)).toHaveLength(5);
+ expect(useSourceSubNav()).toEqual([
+ {
+ id: 'sourceOverview',
+ name: 'Overview',
+ href: '/p/sources/3',
+ },
+ {
+ id: 'sourceContent',
+ name: 'Content',
+ href: '/p/sources/3/content',
+ },
+ {
+ id: 'sourceSettings',
+ name: 'Settings',
+ href: '/p/sources/3/settings',
+ },
+ ]);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx
index 12e1506ec6efd..6b595a06f0404 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx
@@ -5,11 +5,11 @@
* 2.0.
*/
-import React from 'react';
-
import { useValues } from 'kea';
-import { SideNavLink } from '../../../../shared/layout';
+import { EuiSideNavItemType } from '@elastic/eui';
+
+import { generateNavLink } from '../../../../shared/layout';
import { AppLogic } from '../../../app_logic';
import { NAV, CUSTOM_SERVICE_TYPE } from '../../../constants';
import {
@@ -22,40 +22,52 @@ import {
} from '../../../routes';
import { SourceLogic } from '../source_logic';
-export const SourceSubNav: React.FC = () => {
+export const useSourceSubNav = () => {
const { isOrganization } = useValues(AppLogic);
const {
contentSource: { id, serviceType },
} = useValues(SourceLogic);
- if (!id) return null;
+ if (!id) return undefined;
+
+ const navItems: Array> = [
+ {
+ id: 'sourceOverview',
+ name: NAV.OVERVIEW,
+ ...generateNavLink({ to: getContentSourcePath(SOURCE_DETAILS_PATH, id, isOrganization) }),
+ },
+ {
+ id: 'sourceContent',
+ name: NAV.CONTENT,
+ ...generateNavLink({ to: getContentSourcePath(SOURCE_CONTENT_PATH, id, isOrganization) }),
+ },
+ ];
const isCustom = serviceType === CUSTOM_SERVICE_TYPE;
+ if (isCustom) {
+ navItems.push({
+ id: 'sourceSchema',
+ name: NAV.SCHEMA,
+ ...generateNavLink({
+ to: getContentSourcePath(SOURCE_SCHEMAS_PATH, id, isOrganization),
+ shouldShowActiveForSubroutes: true,
+ }),
+ });
+ navItems.push({
+ id: 'sourceDisplaySettings',
+ name: NAV.DISPLAY_SETTINGS,
+ ...generateNavLink({
+ to: getContentSourcePath(SOURCE_DISPLAY_SETTINGS_PATH, id, isOrganization),
+ shouldShowActiveForSubroutes: true,
+ }),
+ });
+ }
+
+ navItems.push({
+ id: 'sourceSettings',
+ name: NAV.SETTINGS,
+ ...generateNavLink({ to: getContentSourcePath(SOURCE_SETTINGS_PATH, id, isOrganization) }),
+ });
- return (
-
-
- {NAV.OVERVIEW}
-
-
- {NAV.CONTENT}
-
- {isCustom && (
- <>
-
- {NAV.SCHEMA}
-
-
- {NAV.DISPLAY_SETTINGS}
-
- >
- )}
-
- {NAV.SETTINGS}
-
-
- );
+ return navItems;
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx
index 9df91406c4b7b..2317c84af2432 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx
@@ -10,14 +10,10 @@ import { setMockValues, setMockActions } from '../../../__mocks__/kea_logic';
import { contentSources } from '../../__mocks__/content_sources.mock';
import React from 'react';
-import { Redirect } from 'react-router-dom';
import { shallow } from 'enzyme';
-import { Loading } from '../../../shared/loading';
import { SourcesTable } from '../../components/shared/sources_table';
-import { ViewContentHeader } from '../../components/shared/view_content_header';
-import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes';
import { OrganizationSources } from './organization_sources';
@@ -42,20 +38,12 @@ describe('OrganizationSources', () => {
const wrapper = shallow();
expect(wrapper.find(SourcesTable)).toHaveLength(1);
- expect(wrapper.find(ViewContentHeader)).toHaveLength(1);
});
- it('returns loading when loading', () => {
+ it('does not render a page header when data is loading (to prevent a jump after redirect)', () => {
setMockValues({ ...mockValues, dataLoading: true });
const wrapper = shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
- });
-
- it('returns redirect when no sources', () => {
- setMockValues({ ...mockValues, contentSources: [] });
- const wrapper = shallow();
-
- expect(wrapper.find(Redirect).prop('to')).toEqual(getSourcesPath(ADD_SOURCE_PATH, true));
+ expect(wrapper.prop('pageHeader')).toBeUndefined();
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx
index 4559003b4597f..a4273ae2ae6a2 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx
@@ -6,16 +6,15 @@
*/
import React, { useEffect } from 'react';
-import { Link, Redirect } from 'react-router-dom';
+import { Redirect } from 'react-router-dom';
import { useActions, useValues } from 'kea';
-import { EuiButton } from '@elastic/eui';
-
-import { Loading } from '../../../shared/loading';
+import { EuiButtonTo } from '../../../shared/react_router_helpers';
+import { WorkplaceSearchPageTemplate } from '../../components/layout';
import { ContentSection } from '../../components/shared/content_section';
import { SourcesTable } from '../../components/shared/sources_table';
-import { ViewContentHeader } from '../../components/shared/view_content_header';
+import { NAV } from '../../constants';
import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes';
import {
@@ -36,33 +35,41 @@ export const OrganizationSources: React.FC = () => {
const { dataLoading, contentSources } = useValues(SourcesLogic);
- if (dataLoading) return ;
-
- if (contentSources.length === 0) return ;
-
return (
-
-
-
- {ORG_SOURCES_LINK}
-
-
- }
- description={ORG_SOURCES_HEADER_DESCRIPTION}
- alignItems="flexStart"
- />
-
-
-
-
-
+
+ {ORG_SOURCES_LINK}
+ ,
+ ],
+ }
+ }
+ isLoading={dataLoading}
+ isEmptyState={!contentSources.length}
+ emptyState={}
+ >
+
+
+
+
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx
index 08f560c984344..e2b0dfba1fa97 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx
@@ -15,7 +15,6 @@ import { shallow } from 'enzyme';
import { EuiCallOut, EuiEmptyPrompt } from '@elastic/eui';
-import { Loading } from '../../../shared/loading';
import { ContentSection } from '../../components/shared/content_section';
import { SourcesTable } from '../../components/shared/sources_table';
@@ -43,13 +42,6 @@ describe('PrivateSources', () => {
expect(wrapper.find(SourcesView)).toHaveLength(1);
});
- it('renders Loading when loading', () => {
- setMockValues({ ...mockValues, dataLoading: true });
- const wrapper = shallow();
-
- expect(wrapper.find(Loading)).toHaveLength(1);
- });
-
it('renders only shared sources section when canCreatePersonalSources is false', () => {
setMockValues({ ...mockValues });
const wrapper = shallow();
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx
index 128c65eeb95da..693c1e8bd5e40 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx
@@ -13,12 +13,13 @@ import { EuiCallOut, EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { LicensingLogic } from '../../../shared/licensing';
-import { Loading } from '../../../shared/loading';
import { EuiButtonTo } from '../../../shared/react_router_helpers';
import { AppLogic } from '../../app_logic';
import noSharedSourcesIcon from '../../assets/share_circle.svg';
+import { PersonalDashboardLayout } from '../../components/layout';
import { ContentSection } from '../../components/shared/content_section';
import { SourcesTable } from '../../components/shared/sources_table';
+import { NAV } from '../../constants';
import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes';
import { toSentenceSerial } from '../../utils';
@@ -53,8 +54,6 @@ export const PrivateSources: React.FC = () => {
account: { canCreatePersonalSources, groups },
} = useValues(AppLogic);
- if (dataLoading) return ;
-
const hasConfiguredConnectors = serviceTypes.some(({ configured }) => configured);
const canAddSources = canCreatePersonalSources && hasConfiguredConnectors;
const hasPrivateSources = privateContentSources?.length > 0;
@@ -144,10 +143,12 @@ export const PrivateSources: React.FC = () => {
);
return (
-
- {hasPrivateSources && !hasPlatinumLicense && licenseCallout}
- {canCreatePersonalSources && privateSourcesSection}
- {sharedSourcesSection}
-
+
+
+ {hasPrivateSources && !hasPlatinumLicense && licenseCallout}
+ {canCreatePersonalSources && privateSourcesSection}
+ {sharedSourcesSection}
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx
index 783fc434fe8e5..afe0d1f89faea 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx
@@ -5,21 +5,17 @@
* 2.0.
*/
-import '../../../__mocks__/shallow_useeffect.mock';
-
import { setMockValues, setMockActions } from '../../../__mocks__/kea_logic';
-import { mockLocation, mockUseParams } from '../../../__mocks__/react_router';
+import { mockUseParams } from '../../../__mocks__/react_router';
import { unmountHandler } from '../../../__mocks__/shallow_useeffect.mock';
import { contentSources } from '../../__mocks__/content_sources.mock';
import React from 'react';
-import { Route, Switch } from 'react-router-dom';
+import { Route } from 'react-router-dom';
import { shallow } from 'enzyme';
-import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
-import { Loading } from '../../../shared/loading';
-import { NAV } from '../../constants';
+import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../components/layout';
import { DisplaySettingsRouter } from './components/display_settings';
import { Overview } from './components/overview';
@@ -37,6 +33,7 @@ describe('SourceRouter', () => {
const mockValues = {
contentSource,
dataLoading: false,
+ isOrganization: true,
};
beforeEach(() => {
@@ -50,11 +47,41 @@ describe('SourceRouter', () => {
}));
});
- it('returns Loading when loading', () => {
- setMockValues({ ...mockValues, dataLoading: true });
- const wrapper = shallow();
+ describe('mount/unmount events', () => {
+ it('fetches & initializes source data on mount', () => {
+ shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
+ expect(initializeSource).toHaveBeenCalledWith(contentSource.id);
+ });
+
+ it('resets state on unmount', () => {
+ shallow();
+ unmountHandler();
+
+ expect(resetSourceState).toHaveBeenCalled();
+ });
+ });
+
+ describe('loading state when fetching source data', () => {
+ // NOTE: The early page isLoading returns are required to prevent a flash of a completely empty
+ // page (instead of preserving the layout/side nav while loading). We also cannot let the code
+ // fall through to the router because some routes are conditionally rendered based on isCustomSource.
+
+ it('returns an empty loading Workplace Search page on organization views', () => {
+ setMockValues({ ...mockValues, dataLoading: true, isOrganization: true });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate);
+ expect(wrapper.prop('isLoading')).toEqual(true);
+ });
+
+ it('returns an empty loading personal dashboard page when not on an organization view', () => {
+ setMockValues({ ...mockValues, dataLoading: true, isOrganization: false });
+ const wrapper = shallow();
+
+ expect(wrapper.type()).toEqual(PersonalDashboardLayout);
+ expect(wrapper.prop('isLoading')).toEqual(true);
+ });
});
it('renders source routes (standard)', () => {
@@ -63,7 +90,6 @@ describe('SourceRouter', () => {
expect(wrapper.find(Overview)).toHaveLength(1);
expect(wrapper.find(SourceSettings)).toHaveLength(1);
expect(wrapper.find(SourceContent)).toHaveLength(1);
- expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(3);
});
@@ -76,55 +102,4 @@ describe('SourceRouter', () => {
expect(wrapper.find(SchemaChangeErrors)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(6);
});
-
- it('handles breadcrumbs while loading (standard)', () => {
- setMockValues({
- ...mockValues,
- contentSource: {},
- });
-
- const loadingBreadcrumbs = ['Sources', '...'];
-
- const wrapper = shallow();
-
- const overviewBreadCrumb = wrapper.find(SetPageChrome).at(0);
- const contentBreadCrumb = wrapper.find(SetPageChrome).at(1);
- const settingsBreadCrumb = wrapper.find(SetPageChrome).at(2);
-
- expect(overviewBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs]);
- expect(contentBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.CONTENT]);
- expect(settingsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SETTINGS]);
- });
-
- it('handles breadcrumbs while loading (custom)', () => {
- setMockValues({
- ...mockValues,
- contentSource: { serviceType: 'custom' },
- });
-
- const loadingBreadcrumbs = ['Sources', '...'];
-
- const wrapper = shallow();
-
- const schemaBreadCrumb = wrapper.find(SetPageChrome).at(2);
- const schemaErrorsBreadCrumb = wrapper.find(SetPageChrome).at(3);
- const displaySettingsBreadCrumb = wrapper.find(SetPageChrome).at(4);
-
- expect(schemaBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]);
- expect(schemaErrorsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]);
- expect(displaySettingsBreadCrumb.prop('trail')).toEqual([
- ...loadingBreadcrumbs,
- NAV.DISPLAY_SETTINGS,
- ]);
- });
-
- describe('reset state', () => {
- it('resets state when leaving source tree', () => {
- mockLocation.pathname = '/home';
- shallow();
- unmountHandler();
-
- expect(resetSourceState).toHaveBeenCalled();
- });
- });
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx
index d5d6c8e541e4f..bf68a60757c0d 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx
@@ -10,18 +10,11 @@ import React, { useEffect } from 'react';
import { Route, Switch, useLocation, useParams } from 'react-router-dom';
import { useActions, useValues } from 'kea';
-import moment from 'moment';
-import { EuiButton, EuiCallOut, EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
-
-import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
-import { Loading } from '../../../shared/loading';
-import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { AppLogic } from '../../app_logic';
-import { NAV } from '../../constants';
+import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../components/layout';
import { CUSTOM_SERVICE_TYPE } from '../../constants';
import {
- ENT_SEARCH_LICENSE_MANAGEMENT,
REINDEX_JOB_PATH,
SOURCE_DETAILS_PATH,
SOURCE_CONTENT_PATH,
@@ -37,13 +30,7 @@ import { Overview } from './components/overview';
import { Schema } from './components/schema';
import { SchemaChangeErrors } from './components/schema/schema_change_errors';
import { SourceContent } from './components/source_content';
-import { SourceInfoCard } from './components/source_info_card';
import { SourceSettings } from './components/source_settings';
-import {
- SOURCE_DISABLED_CALLOUT_TITLE,
- SOURCE_DISABLED_CALLOUT_DESCRIPTION,
- SOURCE_DISABLED_CALLOUT_BUTTON,
-} from './constants';
import { SourceLogic } from './source_logic';
export const SourceRouter: React.FC = () => {
@@ -61,84 +48,43 @@ export const SourceRouter: React.FC = () => {
return resetSourceState;
}, []);
- if (dataLoading) return ;
+ if (dataLoading) {
+ return isOrganization ? (
+
+ ) : (
+
+ );
+ }
- const {
- name,
- createdAt,
- serviceType,
- serviceName,
- isFederatedSource,
- supportedByLicense,
- } = contentSource;
+ const { serviceType } = contentSource;
const isCustomSource = serviceType === CUSTOM_SERVICE_TYPE;
- const pageHeader = (
- <>
-
-
- >
- );
-
- const callout = (
- <>
-
- {SOURCE_DISABLED_CALLOUT_DESCRIPTION}
-
- {SOURCE_DISABLED_CALLOUT_BUTTON}
-
-
-
- >
- );
-
return (
- <>
- {!supportedByLicense && callout}
- {pageHeader}
-
-
-
-
-
+
+
+
+
+
+
+
+ {isCustomSource && (
+
+
-
-
-
-
+ )}
+ {isCustomSource && (
+
+
- {isCustomSource && (
-
-
-
-
-
- )}
- {isCustomSource && (
-
-
-
-
-
- )}
- {isCustomSource && (
-
-
-
-
-
- )}
-
-
-
-
+ )}
+ {isCustomSource && (
+
+
-
- >
+ )}
+
+
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx
index 84bff65e62cef..2abdba07b5c88 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx
@@ -11,12 +11,8 @@ import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
import { Location } from 'history';
import { useActions, useValues } from 'kea';
-import { FlashMessages } from '../../../shared/flash_messages';
-import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { LicensingLogic } from '../../../shared/licensing';
-import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { AppLogic } from '../../app_logic';
-import { NAV } from '../../constants';
import {
ADD_SOURCE_PATH,
SOURCE_DETAILS_PATH,
@@ -52,71 +48,53 @@ export const SourcesRouter: React.FC = () => {
}, [pathname]);
return (
- <>
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {staticSourceData.map(({ addPath, accountContextOnly }, i) => (
+
+ {!hasPlatinumLicense && accountContextOnly ? (
+
+ ) : (
+
+ )}
-
-
-
-
+ ))}
+ {staticSourceData.map(({ addPath }, i) => (
+
+
- {staticSourceData.map(({ addPath, accountContextOnly, name }, i) => (
-
-
- {!hasPlatinumLicense && accountContextOnly ? (
-
- ) : (
-
- )}
-
- ))}
- {staticSourceData.map(({ addPath, name }, i) => (
-
-
-
-
- ))}
- {staticSourceData.map(({ addPath, name }, i) => (
-
-
-
-
- ))}
- {staticSourceData.map(({ addPath, name, configuration: { needsConfiguration } }, i) => {
- if (needsConfiguration)
- return (
-
-
-
-
- );
- })}
- {canCreatePersonalSources ? (
-
-
-
-
-
- ) : (
-
- )}
-
-
-
+ ))}
+ {staticSourceData.map(({ addPath }, i) => (
+
+
-
-
+ ))}
+ {staticSourceData.map(({ addPath, configuration: { needsConfiguration } }, i) => {
+ if (needsConfiguration)
+ return (
+
+
+
+ );
+ })}
+ {canCreatePersonalSources ? (
+
+
-
- >
+ ) : (
+
+ )}
+
+
+
+
+
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.test.tsx
index cf23470e8155e..7bd40d6f04a56 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.test.tsx
@@ -25,6 +25,13 @@ describe('Overview', () => {
expect(mockActions.initializeOverview).toHaveBeenCalled();
});
+ it('does not render a page header when data is loading (to prevent a jump between non/onboarding headers)', () => {
+ setMockValues({ dataLoading: true });
+ const wrapper = shallow();
+
+ expect(wrapper.prop('pageHeader')).toBeUndefined();
+ });
+
it('renders onboarding state', () => {
setMockValues({ dataLoading: false });
const wrapper = shallow();
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.tsx
index 0049c5b732d3d..c51fdb64b8f26 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview.tsx
@@ -53,17 +53,15 @@ export const Overview: React.FC = () => {
const hideOnboarding = hasUsers && hasOrgSources && isOldAccount && orgName !== defaultOrgName;
- const headerTitle = dataLoading || hideOnboarding ? HEADER_TITLE : ONBOARDING_HEADER_TITLE;
- const headerDescription =
- dataLoading || hideOnboarding ? HEADER_DESCRIPTION : ONBOARDING_HEADER_DESCRIPTION;
+ const headerTitle = hideOnboarding ? HEADER_TITLE : ONBOARDING_HEADER_TITLE;
+ const headerDescription = hideOnboarding ? HEADER_DESCRIPTION : ONBOARDING_HEADER_DESCRIPTION;
return (
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx
index b32e3af021827..35619d2b2d560 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx
@@ -40,6 +40,13 @@ describe('SourceConfig', () => {
expect(wrapper.find(EuiConfirmModal)).toHaveLength(1);
});
+ it('renders a breadcrumb fallback while data is loading', () => {
+ setMockValues({ dataLoading: true, sourceConfigData: {} });
+ const wrapper = shallow();
+
+ expect(wrapper.prop('pageChrome')).toEqual(['Settings', 'Content source connectors', '...']);
+ });
+
it('handles delete click', () => {
const wrapper = shallow();
const saveConfig = wrapper.find(SaveConfig);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx
index f1dfda78ee13f..c2a0b60e1eca3 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx
@@ -47,7 +47,7 @@ export const SourceConfig: React.FC = ({ sourceIndex }) => {
return (