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; } }