diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.test.tsx
new file mode 100644
index 00000000000000..e8035f01a94057
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.test.tsx
@@ -0,0 +1,53 @@
+/*
+ * 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 { setMockValues } from '../../../../__mocks__';
+
+import React from 'react';
+
+import { shallow } from 'enzyme';
+
+import { EuiHeader, EuiPopover } from '@elastic/eui';
+
+import { AccountHeader } from './';
+
+describe('AccountHeader', () => {
+ const mockValues = {
+ account: {
+ isAdmin: true,
+ },
+ };
+
+ beforeEach(() => {
+ setMockValues({ ...mockValues });
+ });
+
+ it('renders', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find(EuiHeader)).toHaveLength(1);
+ });
+
+ describe('accountSubNav', () => {
+ it('handles popover trigger click', () => {
+ const wrapper = shallow();
+ const popover = wrapper.find(EuiPopover);
+ const onClick = popover.dive().find('[data-test-subj="AccountButton"]').prop('onClick');
+ onClick!({} as any);
+
+ expect(onClick).toBeDefined();
+ });
+
+ it('handles close popover', () => {
+ const wrapper = shallow();
+ const popover = wrapper.find(EuiPopover);
+ popover.prop('closePopover')!();
+
+ expect(popover.prop('isOpen')).toEqual(false);
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx
new file mode 100644
index 00000000000000..a878d87af09e47
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx
@@ -0,0 +1,107 @@
+/*
+ * 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, { useState } from 'react';
+
+import { useValues } from 'kea';
+
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiHeader,
+ EuiHeaderLogo,
+ EuiHeaderLinks,
+ EuiHeaderSection,
+ EuiHeaderSectionItem,
+ EuiText,
+ EuiContextMenuPanel,
+ EuiContextMenuItem,
+ EuiPopover,
+} from '@elastic/eui';
+
+import { getWorkplaceSearchUrl } from '../../../../shared/enterprise_search_url';
+import { EuiButtonEmptyTo } from '../../../../shared/react_router_helpers';
+import { AppLogic } from '../../../app_logic';
+import { WORKPLACE_SEARCH_TITLE, ACCOUNT_NAV } from '../../../constants';
+import {
+ ALPHA_PATH,
+ PERSONAL_SOURCES_PATH,
+ LOGOUT_ROUTE,
+ KIBANA_ACCOUNT_ROUTE,
+} from '../../../routes';
+
+export const AccountHeader: React.FC = () => {
+ const [isPopoverOpen, setPopover] = useState(false);
+ const onButtonClick = () => {
+ setPopover(!isPopoverOpen);
+ };
+ const closePopover = () => {
+ setPopover(false);
+ };
+
+ const {
+ account: { isAdmin },
+ } = useValues(AppLogic);
+
+ const accountNavItems = [
+
+ {/* TODO: Once auth is completed, we need to have non-admins redirect to the self-hosted form */}
+ {ACCOUNT_NAV.SETTINGS}
+ ,
+
+ {ACCOUNT_NAV.LOGOUT}
+ ,
+ ];
+
+ const accountButton = (
+
+ {ACCOUNT_NAV.ACCOUNT}
+
+ );
+
+ return (
+
+
+
+
+ {WORKPLACE_SEARCH_TITLE}
+
+
+
+ {ACCOUNT_NAV.SOURCES}
+
+
+
+
+
+ {isAdmin && (
+ {ACCOUNT_NAV.ORG_DASHBOARD}
+ )}
+
+
+
+
+ {ACCOUNT_NAV.SEARCH}
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/index.ts
new file mode 100644
index 00000000000000..e6cd2516fc03a4
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export { AccountHeader } from './account_header';
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts
index 2678b5d01b4752..b9a49c416f2831 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts
@@ -7,3 +7,4 @@
export { WorkplaceSearchNav } from './nav';
export { WorkplaceSearchHeaderActions } from './kibana_header_actions';
+export { AccountHeader } from './account_header';
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
index a6e9ce282bf3d3..d7716735067617 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
@@ -9,6 +9,13 @@ import { i18n } from '@kbn/i18n';
import { UPDATE_BUTTON_LABEL, SAVE_BUTTON_LABEL, CANCEL_BUTTON_LABEL } from '../shared/constants';
+export const WORKPLACE_SEARCH_TITLE = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.title',
+ {
+ defaultMessage: 'Workplace Search',
+ }
+);
+
export const NAV = {
OVERVIEW: i18n.translate('xpack.enterpriseSearch.workplaceSearch.nav.overview', {
defaultMessage: 'Overview',
@@ -76,6 +83,30 @@ export const NAV = {
}),
};
+export const ACCOUNT_NAV = {
+ SOURCES: i18n.translate('xpack.enterpriseSearch.workplaceSearch.accountNav.sources.link', {
+ defaultMessage: 'Content sources',
+ }),
+ ORG_DASHBOARD: i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.accountNav.orgDashboard.link',
+ {
+ defaultMessage: 'Go to organizational dashboard',
+ }
+ ),
+ SEARCH: i18n.translate('xpack.enterpriseSearch.workplaceSearch.accountNav.search.link', {
+ defaultMessage: 'Search',
+ }),
+ ACCOUNT: i18n.translate('xpack.enterpriseSearch.workplaceSearch.accountNav.account.link', {
+ defaultMessage: 'My account',
+ }),
+ SETTINGS: i18n.translate('xpack.enterpriseSearch.workplaceSearch.accountNav.settings.link', {
+ defaultMessage: 'Account settings',
+ }),
+ LOGOUT: i18n.translate('xpack.enterpriseSearch.workplaceSearch.accountNav.logout.link', {
+ defaultMessage: 'Logout',
+ }),
+};
+
export const MAX_TABLE_ROW_ICONS = 3;
export const SOURCE_STATUSES = {
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 7a76de43be41ba..a8d6fc54f79246 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
@@ -66,11 +66,13 @@ export const WorkplaceSearchConfigured: React.FC = (props) => {
* Personal dashboard urls begin with /p/
* EX: http://localhost:5601/app/enterprise_search/workplace_search/p/sources
*/
- useEffect(() => {
- const personalSourceUrlRegex = /^\/p\//g; // matches '/p/*'
- const isOrganization = !pathname.match(personalSourceUrlRegex); // TODO: Once auth is figured out, we need to have a check for the equivilent of `isAdmin`.
- setContext(isOrganization);
+ const personalSourceUrlRegex = /^\/p\//g; // matches '/p/*'
+ const isOrganization = !pathname.match(personalSourceUrlRegex); // TODO: Once auth is figured out, we need to have a check for the equivilent of `isAdmin`.
+
+ setContext(isOrganization);
+
+ useEffect(() => {
setChromeIsVisible(isOrganization);
}, [pathname]);
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 9e514d7c734931..e08050335671e5 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
@@ -12,6 +12,8 @@ import { docLinks } from '../shared/doc_links';
export const SETUP_GUIDE_PATH = '/setup_guide';
export const NOT_FOUND_PATH = '/404';
+export const LOGOUT_ROUTE = '/logout';
+export const KIBANA_ACCOUNT_ROUTE = '/security/account';
export const LEAVE_FEEDBACK_EMAIL = 'support@elastic.co';
export const LEAVE_FEEDBACK_URL = `mailto:${LEAVE_FEEDBACK_EMAIL}?Subject=Elastic%20Workplace%20Search%20Feedback`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx
index 9e3b50ea083eb9..7558eb1e4e6621 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx
@@ -15,6 +15,7 @@ import { shallow } from 'enzyme';
import { EuiCallOut } from '@elastic/eui';
+import { AccountHeader } from '../../components/layout';
import { ViewContentHeader } from '../../components/shared/view_content_header';
import { SourceSubNav } from './components/source_sub_nav';
@@ -43,6 +44,7 @@ describe('PrivateSourcesLayout', () => {
expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1);
expect(wrapper.find(SourceSubNav)).toHaveLength(1);
+ expect(wrapper.find(AccountHeader)).toHaveLength(1);
});
it('uses correct title and description when private sources are enabled', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx
index 2a6281075dc400..c565ee5f39a713 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx
@@ -12,6 +12,7 @@ import { useValues } from 'kea';
import { EuiPage, EuiPageSideBar, EuiPageBody, EuiCallOut } from '@elastic/eui';
import { AppLogic } from '../../app_logic';
+import { AccountHeader } from '../../components/layout';
import { ViewContentHeader } from '../../components/shared/view_content_header';
import { SourceSubNav } from './components/source_sub_nav';
@@ -48,22 +49,25 @@ export const PrivateSourcesLayout: React.FC = ({
: PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION;
return (
-
-
-
-
-
-
- {readOnlyMode && (
-
- )}
- {children}
-
-
+ <>
+
+
+
+
+
+
+
+ {readOnlyMode && (
+
+ )}
+ {children}
+
+
+ >
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss
index abab139e32369a..549ca3ae9154e0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss
@@ -20,14 +20,18 @@
.privateSourcesLayout {
$sideBarWidth: $euiSize * 30;
+ $consoleHeaderHeight: 48px; // NOTE: Keep an eye on this for changes
+ $pageHeight: calc(100vh - #{$consoleHeaderHeight});
left: $sideBarWidth;
width: calc(100% - #{$sideBarWidth});
+ min-height: $pageHeight;
&__sideBar {
padding: 32px 40px 40px;
width: $sideBarWidth;
margin-left: -$sideBarWidth;
+ height: $pageHeight;
}
}