diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.scss
index c750f63dab248a..486abeb3dce4c9 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.scss
@@ -6,21 +6,17 @@
*/
.appSearchNavEngineLabel {
- padding-top: $euiSizeS;
+ margin-left: $euiSizeS;
+ padding-top: $euiSizeXS;
padding-bottom: $euiSizeS;
- .euiText {
- font-weight: $euiFontWeightMedium;
- }
.euiBadge {
margin-top: $euiSizeXS;
}
}
-.appSearchNavIcons {
- // EUI override
- &.euiFlexItem {
- flex-grow: 0;
- flex-direction: row;
- }
+.appSearchNavIcon {
+ // EuiSideNav renders icons to the left of the nav link by default, but we use icons
+ // as warning or error indicators & prefer to render them on the right side of the nav
+ order: 1;
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx
index c2b0a6a50fd068..015fb997c29ed4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx
@@ -6,8 +6,14 @@
*/
import { setMockValues } from '../../../__mocks__/kea_logic';
+import { mockUseRouteMatch } from '../../../__mocks__/react_router';
import { mockEngineValues } from '../../__mocks__';
+jest.mock('../../../shared/layout', () => ({
+ ...jest.requireActual('../../../shared/layout'), // TODO: Remove once side nav components are gone
+ generateNavLink: jest.fn(({ to }) => ({ href: to })),
+}));
+
import React from 'react';
import { shallow } from 'enzyme';
@@ -16,7 +22,305 @@ import { EuiBadge, EuiIcon } from '@elastic/eui';
import { rerender } from '../../../test_helpers';
-import { EngineNav } from './engine_nav';
+import { useEngineNav, EngineNav } from './engine_nav';
+
+describe('useEngineNav', () => {
+ const values = { ...mockEngineValues, myRole: {}, dataLoading: false };
+
+ beforeEach(() => {
+ setMockValues(values);
+ mockUseRouteMatch.mockReturnValue(true);
+ });
+
+ describe('returns empty', () => {
+ it('does not return engine nav items if not on an engine route', () => {
+ mockUseRouteMatch.mockReturnValueOnce(false);
+ expect(useEngineNav()).toBeUndefined();
+ });
+
+ it('does not return engine nav items if data is still loading', () => {
+ setMockValues({ ...values, dataLoading: true });
+ expect(useEngineNav()).toBeUndefined();
+ });
+
+ it('does not return engine nav items if engine data is missing', () => {
+ setMockValues({ ...values, engineName: '' });
+ expect(useEngineNav()).toBeUndefined();
+ });
+ });
+
+ describe('returns an array of EUI side nav items', () => {
+ const BASE_NAV = [
+ {
+ id: 'engineName',
+ name: 'some-engine',
+ renderItem: expect.any(Function),
+ 'data-test-subj': 'EngineLabel',
+ },
+ {
+ id: 'overview',
+ name: 'Overview',
+ href: '/engines/some-engine',
+ 'data-test-subj': 'EngineOverviewLink',
+ },
+ ];
+
+ it('always returns an engine label and overview link', () => {
+ expect(useEngineNav()).toEqual(BASE_NAV);
+ });
+
+ describe('engine label', () => {
+ const renderEngineLabel = (engineNav: any) => {
+ return shallow(engineNav[0].renderItem() as any);
+ };
+
+ it('renders the capitalized engine name', () => {
+ const wrapper = renderEngineLabel(useEngineNav());
+ const name = wrapper.find('.eui-textTruncate');
+
+ expect(name.text()).toEqual('SOME-ENGINE');
+ expect(wrapper.find(EuiBadge)).toHaveLength(0);
+ });
+
+ it('renders a sample engine badge for the sample engine', () => {
+ setMockValues({ ...values, isSampleEngine: true });
+ const wrapper = renderEngineLabel(useEngineNav());
+
+ expect(wrapper.find(EuiBadge).prop('children')).toEqual('SAMPLE ENGINE');
+ });
+
+ it('renders a meta engine badge for meta engines', () => {
+ setMockValues({ ...values, isMetaEngine: true });
+ const wrapper = renderEngineLabel(useEngineNav());
+
+ expect(wrapper.find(EuiBadge).prop('children')).toEqual('META ENGINE');
+ });
+ });
+
+ it('returns an analytics nav item', () => {
+ setMockValues({ ...values, myRole: { canViewEngineAnalytics: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'analytics',
+ name: 'Analytics',
+ href: '/engines/some-engine/analytics',
+ 'data-test-subj': 'EngineAnalyticsLink',
+ },
+ ]);
+ });
+
+ it('returns a documents nav item', () => {
+ setMockValues({ ...values, myRole: { canViewEngineDocuments: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'documents',
+ name: 'Documents',
+ href: '/engines/some-engine/documents',
+ 'data-test-subj': 'EngineDocumentsLink',
+ },
+ ]);
+ });
+
+ it('returns a schema nav item', () => {
+ setMockValues({ ...values, myRole: { canViewEngineSchema: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'schema',
+ name: 'Schema',
+ href: '/engines/some-engine/schema',
+ 'data-test-subj': 'EngineSchemaLink',
+ icon: expect.anything(),
+ },
+ ]);
+ });
+
+ describe('schema nav icons', () => {
+ const myRole = { canViewEngineSchema: true };
+
+ const renderIcons = (engineNav: any) => {
+ return shallow(
{engineNav[2].icon}
);
+ };
+
+ it('renders schema errors alert icon', () => {
+ setMockValues({ ...values, myRole, hasSchemaErrors: true });
+ const wrapper = renderIcons(useEngineNav());
+
+ expect(wrapper.find('[data-test-subj="EngineNavSchemaErrors"]')).toHaveLength(1);
+ });
+
+ it('renders unconfirmed schema fields info icon', () => {
+ setMockValues({ ...values, myRole, hasUnconfirmedSchemaFields: true });
+ const wrapper = renderIcons(useEngineNav());
+
+ expect(wrapper.find('[data-test-subj="EngineNavSchemaUnconfirmedFields"]')).toHaveLength(1);
+ });
+
+ it('renders schema conflicts alert icon', () => {
+ setMockValues({ ...values, myRole, hasSchemaConflicts: true });
+ const wrapper = renderIcons(useEngineNav());
+
+ expect(wrapper.find('[data-test-subj="EngineNavSchemaConflicts"]')).toHaveLength(1);
+ });
+ });
+
+ describe('crawler', () => {
+ const myRole = { canViewEngineCrawler: true };
+
+ it('returns a crawler nav item', () => {
+ setMockValues({ ...values, myRole });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'crawler',
+ name: 'Web Crawler',
+ href: '/engines/some-engine/crawler',
+ 'data-test-subj': 'EngineCrawlerLink',
+ },
+ ]);
+ });
+
+ it('does not return a crawler nav item for meta engines', () => {
+ setMockValues({ ...values, myRole, isMetaEngine: true });
+ expect(useEngineNav()).toEqual(BASE_NAV);
+ });
+ });
+
+ describe('meta engine source engines', () => {
+ const myRole = { canViewMetaEngineSourceEngines: true };
+
+ it('returns a source engines nav item', () => {
+ setMockValues({ ...values, myRole, isMetaEngine: true });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'sourceEngines',
+ name: 'Engines',
+ href: '/engines/some-engine/engines',
+ 'data-test-subj': 'MetaEngineEnginesLink',
+ },
+ ]);
+ });
+
+ it('does not return a source engines nav item for non-meta engines', () => {
+ setMockValues({ ...values, myRole, isMetaEngine: false });
+ expect(useEngineNav()).toEqual(BASE_NAV);
+ });
+ });
+
+ it('returns a relevance tuning nav item', () => {
+ setMockValues({ ...values, myRole: { canManageEngineRelevanceTuning: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'relevanceTuning',
+ name: 'Relevance Tuning',
+ href: '/engines/some-engine/relevance_tuning',
+ 'data-test-subj': 'EngineRelevanceTuningLink',
+ icon: expect.anything(),
+ },
+ ]);
+ });
+
+ describe('relevance tuning nav icons', () => {
+ const myRole = { canManageEngineRelevanceTuning: true };
+
+ const renderIcons = (engineNav: any) => {
+ return shallow({engineNav[2].icon}
);
+ };
+
+ it('renders unconfirmed schema fields info icon', () => {
+ setMockValues({ ...values, myRole, engine: { unsearchedUnconfirmedFields: true } });
+ const wrapper = renderIcons(useEngineNav());
+ expect(
+ wrapper.find('[data-test-subj="EngineNavRelevanceTuningUnsearchedFields"]')
+ ).toHaveLength(1);
+ });
+
+ it('renders schema conflicts alert icon', () => {
+ setMockValues({ ...values, myRole, engine: { invalidBoosts: true } });
+ const wrapper = renderIcons(useEngineNav());
+ expect(
+ wrapper.find('[data-test-subj="EngineNavRelevanceTuningInvalidBoosts"]')
+ ).toHaveLength(1);
+ });
+
+ it('can render multiple icons', () => {
+ const engine = { invalidBoosts: true, unsearchedUnconfirmedFields: true };
+ setMockValues({ ...values, myRole, engine });
+ const wrapper = renderIcons(useEngineNav());
+ expect(wrapper.find(EuiIcon)).toHaveLength(2);
+ });
+ });
+
+ it('returns a synonyms nav item', () => {
+ setMockValues({ ...values, myRole: { canManageEngineSynonyms: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'synonyms',
+ name: 'Synonyms',
+ href: '/engines/some-engine/synonyms',
+ 'data-test-subj': 'EngineSynonymsLink',
+ },
+ ]);
+ });
+
+ it('returns a curations nav item', () => {
+ setMockValues({ ...values, myRole: { canManageEngineCurations: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'curations',
+ name: 'Curations',
+ href: '/engines/some-engine/curations',
+ 'data-test-subj': 'EngineCurationsLink',
+ },
+ ]);
+ });
+
+ it('returns a results settings nav item', () => {
+ setMockValues({ ...values, myRole: { canManageEngineResultSettings: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'resultSettings',
+ name: 'Result Settings',
+ href: '/engines/some-engine/result_settings',
+ 'data-test-subj': 'EngineResultSettingsLink',
+ },
+ ]);
+ });
+
+ it('returns a Search UI nav item', () => {
+ setMockValues({ ...values, myRole: { canManageEngineSearchUi: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'searchUI',
+ name: 'Search UI',
+ href: '/engines/some-engine/search_ui',
+ 'data-test-subj': 'EngineSearchUILink',
+ },
+ ]);
+ });
+
+ it('returns an API logs nav item', () => {
+ setMockValues({ ...values, myRole: { canViewEngineApiLogs: true } });
+ expect(useEngineNav()).toEqual([
+ ...BASE_NAV,
+ {
+ id: 'apiLogs',
+ name: 'API Logs',
+ href: '/engines/some-engine/api_logs',
+ 'data-test-subj': 'EngineAPILogsLink',
+ },
+ ]);
+ });
+ });
+});
describe('EngineNav', () => {
const values = { ...mockEngineValues, myRole: {}, dataLoading: false };
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx
index 0edf01bada9381..76e751cf4da5f1 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx
@@ -6,13 +6,21 @@
*/
import React from 'react';
+import { useRouteMatch } from 'react-router-dom';
import { useValues } from 'kea';
-import { EuiText, EuiBadge, EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import {
+ EuiSideNavItemType,
+ EuiText,
+ EuiBadge,
+ EuiIcon,
+ EuiFlexGroup,
+ EuiFlexItem,
+} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { SideNavLink, SideNavItem } from '../../../shared/layout';
+import { generateNavLink, SideNavLink, SideNavItem } from '../../../shared/layout';
import { AppLogic } from '../../app_logic';
import {
ENGINE_PATH,
@@ -47,6 +55,255 @@ import { EngineLogic, generateEnginePath } from './';
import './engine_nav.scss';
+export const useEngineNav = () => {
+ const isEngineRoute = !!useRouteMatch(ENGINE_PATH);
+ const {
+ myRole: {
+ canViewEngineAnalytics,
+ canViewEngineDocuments,
+ canViewEngineSchema,
+ canViewEngineCrawler,
+ canViewMetaEngineSourceEngines,
+ canManageEngineSynonyms,
+ canManageEngineCurations,
+ canManageEngineRelevanceTuning,
+ canManageEngineResultSettings,
+ canManageEngineSearchUi,
+ canViewEngineApiLogs,
+ },
+ } = useValues(AppLogic);
+ const {
+ engineName,
+ dataLoading,
+ isSampleEngine,
+ isMetaEngine,
+ hasSchemaErrors,
+ hasSchemaConflicts,
+ hasUnconfirmedSchemaFields,
+ engine,
+ } = useValues(EngineLogic);
+
+ if (!isEngineRoute) return undefined;
+ if (dataLoading) return undefined;
+ if (!engineName) return undefined;
+
+ const navItems: Array> = [
+ {
+ id: 'engineName',
+ name: engineName,
+ renderItem: () => (
+
+ {engineName.toUpperCase()}
+ {isSampleEngine && (
+
+ {i18n.translate('xpack.enterpriseSearch.appSearch.engine.sampleEngineBadge', {
+ defaultMessage: 'SAMPLE ENGINE',
+ })}
+
+ )}
+ {isMetaEngine && (
+
+ {i18n.translate('xpack.enterpriseSearch.appSearch.engine.metaEngineBadge', {
+ defaultMessage: 'META ENGINE',
+ })}
+
+ )}
+
+ ),
+ 'data-test-subj': 'EngineLabel',
+ },
+ {
+ id: 'overview',
+ name: OVERVIEW_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_PATH) }),
+ 'data-test-subj': 'EngineOverviewLink',
+ },
+ ];
+
+ if (canViewEngineAnalytics) {
+ navItems.push({
+ id: 'analytics',
+ name: ANALYTICS_TITLE,
+ ...generateNavLink({
+ to: generateEnginePath(ENGINE_ANALYTICS_PATH),
+ shouldShowActiveForSubroutes: true,
+ }),
+ 'data-test-subj': 'EngineAnalyticsLink',
+ });
+ }
+
+ if (canViewEngineDocuments) {
+ navItems.push({
+ id: 'documents',
+ name: DOCUMENTS_TITLE,
+ ...generateNavLink({
+ to: generateEnginePath(ENGINE_DOCUMENTS_PATH),
+ shouldShowActiveForSubroutes: true,
+ }),
+ 'data-test-subj': 'EngineDocumentsLink',
+ });
+ }
+
+ if (canViewEngineSchema) {
+ navItems.push({
+ id: 'schema',
+ name: SCHEMA_TITLE,
+ ...generateNavLink({
+ to: generateEnginePath(ENGINE_SCHEMA_PATH),
+ shouldShowActiveForSubroutes: true,
+ }),
+ 'data-test-subj': 'EngineSchemaLink',
+ icon: (
+ <>
+ {hasSchemaErrors && (
+
+ )}
+ {hasUnconfirmedSchemaFields && (
+
+ )}
+ {hasSchemaConflicts && (
+
+ )}
+ >
+ ),
+ });
+ }
+
+ if (canViewEngineCrawler && !isMetaEngine) {
+ navItems.push({
+ id: 'crawler',
+ name: CRAWLER_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_CRAWLER_PATH) }),
+ 'data-test-subj': 'EngineCrawlerLink',
+ });
+ }
+
+ if (canViewMetaEngineSourceEngines && isMetaEngine) {
+ navItems.push({
+ id: 'sourceEngines',
+ name: ENGINES_TITLE,
+ ...generateNavLink({ to: generateEnginePath(META_ENGINE_SOURCE_ENGINES_PATH) }),
+ 'data-test-subj': 'MetaEngineEnginesLink',
+ });
+ }
+
+ if (canManageEngineRelevanceTuning) {
+ const { invalidBoosts, unsearchedUnconfirmedFields } = engine;
+
+ navItems.push({
+ id: 'relevanceTuning',
+ name: RELEVANCE_TUNING_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_RELEVANCE_TUNING_PATH) }),
+ 'data-test-subj': 'EngineRelevanceTuningLink',
+ icon: (
+ <>
+ {invalidBoosts && (
+
+ )}
+ {unsearchedUnconfirmedFields && (
+
+ )}
+ >
+ ),
+ });
+ }
+
+ if (canManageEngineSynonyms) {
+ navItems.push({
+ id: 'synonyms',
+ name: SYNONYMS_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_SYNONYMS_PATH) }),
+ 'data-test-subj': 'EngineSynonymsLink',
+ });
+ }
+
+ if (canManageEngineCurations) {
+ navItems.push({
+ id: 'curations',
+ name: CURATIONS_TITLE,
+ ...generateNavLink({
+ to: generateEnginePath(ENGINE_CURATIONS_PATH),
+ shouldShowActiveForSubroutes: true,
+ }),
+ 'data-test-subj': 'EngineCurationsLink',
+ });
+ }
+
+ if (canManageEngineResultSettings) {
+ navItems.push({
+ id: 'resultSettings',
+ name: RESULT_SETTINGS_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_RESULT_SETTINGS_PATH) }),
+ 'data-test-subj': 'EngineResultSettingsLink',
+ });
+ }
+
+ if (canManageEngineSearchUi) {
+ navItems.push({
+ id: 'searchUI',
+ name: SEARCH_UI_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_SEARCH_UI_PATH) }),
+ 'data-test-subj': 'EngineSearchUILink',
+ });
+ }
+
+ if (canViewEngineApiLogs) {
+ navItems.push({
+ id: 'apiLogs',
+ name: API_LOGS_TITLE,
+ ...generateNavLink({ to: generateEnginePath(ENGINE_API_LOGS_PATH) }),
+ 'data-test-subj': 'EngineAPILogsLink',
+ });
+ }
+
+ return navItems;
+};
+
+// TODO: Delete the below once page template migration is complete
+
export const EngineNav: React.FC = () => {
const {
myRole: {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx
index cb6df5b39880ae..852705583624ba 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx
@@ -19,7 +19,6 @@ import { Switch, Redirect } from 'react-router-dom';
import { shallow } from 'enzyme';
-import { Loading } from '../../../shared/loading';
import { AnalyticsRouter } from '../analytics';
import { ApiLogs } from '../api_logs';
import { CrawlerRouter } from '../crawler';
@@ -80,20 +79,20 @@ describe('EngineRouter', () => {
);
});
- it('renders a loading component if async data is still loading', () => {
+ it('renders a loading page template if async data is still loading', () => {
setMockValues({ ...values, dataLoading: true });
const wrapper = shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
+ expect(wrapper.prop('isLoading')).toEqual(true);
});
// This would happen if a user jumps around from one engine route to another. If the engine name
// on the path has changed, but we still have an engine stored in state, we do not want to load
// any route views as they would be rendering with the wrong data.
- it('renders a loading component if the engine stored in state is stale', () => {
+ it('renders a loading page template if the engine stored in state is stale', () => {
setMockValues({ ...values, engineName: 'some-engine' });
mockUseParams.mockReturnValue({ engineName: 'some-new-engine' });
const wrapper = shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
+ expect(wrapper.prop('isLoading')).toEqual(true);
});
it('renders a default engine overview', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx
index 40cc2ef0368c05..6510e99a000fc5 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx
@@ -13,11 +13,12 @@ import { useValues, useActions } from 'kea';
import { i18n } from '@kbn/i18n';
import { setQueuedErrorMessage } from '../../../shared/flash_messages';
-import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
-import { Loading } from '../../../shared/loading';
+import { Layout } from '../../../shared/layout';
import { AppLogic } from '../../app_logic';
+import { AppSearchNav } from '../../index';
import {
+ ENGINE_PATH,
ENGINES_PATH,
ENGINE_ANALYTICS_PATH,
ENGINE_DOCUMENTS_PATH,
@@ -38,6 +39,7 @@ import { CrawlerRouter } from '../crawler';
import { CurationsRouter } from '../curations';
import { DocumentDetail, Documents } from '../documents';
import { EngineOverview } from '../engine_overview';
+import { AppSearchPageTemplate } from '../layout';
import { RelevanceTuning } from '../relevance_tuning';
import { ResultSettings } from '../result_settings';
import { SchemaRouter } from '../schema';
@@ -45,7 +47,7 @@ import { SearchUI } from '../search_ui';
import { SourceEngines } from '../source_engines';
import { Synonyms } from '../synonyms';
-import { EngineLogic, getEngineBreadcrumbs } from './';
+import { EngineLogic } from './';
export const EngineRouter: React.FC = () => {
const {
@@ -85,74 +87,76 @@ export const EngineRouter: React.FC = () => {
}
const isLoadingNewEngine = engineName !== engineNameFromUrl;
- if (isLoadingNewEngine || dataLoading) return ;
+ if (isLoadingNewEngine || dataLoading) return ;
return (
- {canViewEngineAnalytics && (
-
-
-
- )}
- {canViewEngineDocuments && (
-
-
-
- )}
- {canViewEngineDocuments && (
-
-
-
- )}
- {canViewEngineSchema && (
-
-
-
- )}
- {canManageEngineCurations && (
-
-
-
- )}
- {canManageEngineRelevanceTuning && (
-
-
-
- )}
- {canManageEngineSynonyms && (
-
-
-
- )}
- {canManageEngineResultSettings && (
-
-
-
- )}
- {canViewEngineApiLogs && (
-
-
-
- )}
- {canManageEngineSearchUi && (
-
-
-
- )}
- {canViewMetaEngineSourceEngines && (
-
-
-
- )}
- {canViewEngineCrawler && (
-
-
-
- )}
-
-
+
+ {/* TODO: Remove layout once page template migration is over */}
+ }>
+ {canViewEngineAnalytics && (
+
+
+
+ )}
+ {canViewEngineDocuments && (
+
+
+
+ )}
+ {canViewEngineDocuments && (
+
+
+
+ )}
+ {canViewEngineSchema && (
+
+
+
+ )}
+ {canManageEngineCurations && (
+
+
+
+ )}
+ {canManageEngineRelevanceTuning && (
+
+
+
+ )}
+ {canManageEngineSynonyms && (
+
+
+
+ )}
+ {canManageEngineResultSettings && (
+
+
+
+ )}
+ {canViewEngineApiLogs && (
+
+
+
+ )}
+ {canManageEngineSearchUi && (
+
+
+
+ )}
+ {canViewMetaEngineSourceEngines && (
+
+
+
+ )}
+ {canViewEngineCrawler && (
+
+
+
+ )}
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx
index a3b2f4cfd8b9f5..edacd74e046a28 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx
@@ -12,8 +12,6 @@ import React from 'react';
import { shallow } from 'enzyme';
-import { Loading } from '../../../shared/loading';
-
import { EmptyEngineOverview } from './engine_overview_empty';
import { EngineOverviewMetrics } from './engine_overview_metrics';
@@ -46,10 +44,10 @@ describe('EngineOverview', () => {
expect(actions.pollForOverviewMetrics).toHaveBeenCalledTimes(1);
});
- it('renders a loading component if async data is still loading', () => {
+ it('renders a loading page template if async data is still loading', () => {
setMockValues({ ...values, dataLoading: true });
const wrapper = shallow();
- expect(wrapper.find(Loading)).toHaveLength(1);
+ expect(wrapper.prop('isLoading')).toEqual(true);
});
describe('EmptyEngineOverview', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx
index 77552b36af2391..4c15ffd8b7f947 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx
@@ -9,9 +9,9 @@ import React, { useEffect } from 'react';
import { useActions, useValues } from 'kea';
-import { Loading } from '../../../shared/loading';
import { AppLogic } from '../../app_logic';
import { EngineLogic } from '../engine';
+import { AppSearchPageTemplate } from '../layout';
import { EmptyEngineOverview } from './engine_overview_empty';
@@ -32,9 +32,7 @@ export const EngineOverview: React.FC = () => {
pollForOverviewMetrics();
}, []);
- if (dataLoading) {
- return ;
- }
+ if (dataLoading) return ;
const engineHasDocuments = documentCount > 0;
const canAddDocuments = canManageEngineDocuments && canViewEngineCredentials;
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx
index ea47dc8956ddd9..6750ebf1140e03 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx
@@ -5,13 +5,16 @@
* 2.0.
*/
+import '../../__mocks__/engine_logic.mock';
+
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
-import { EuiPageHeader, EuiButton } from '@elastic/eui';
+import { EuiButton } from '@elastic/eui';
import { docLinks } from '../../../shared/doc_links';
+import { getPageTitle, getPageHeaderActions } from '../../../test_helpers';
import { DocumentCreationButtons, DocumentCreationFlyout } from '../document_creation';
@@ -25,12 +28,13 @@ describe('EmptyEngineOverview', () => {
});
it('renders', () => {
- expect(wrapper.find(EuiPageHeader).prop('pageTitle')).toEqual('Engine setup');
+ expect(getPageTitle(wrapper)).toEqual('Engine setup');
});
it('renders a documentation link', () => {
- const header = wrapper.find(EuiPageHeader).dive().children().dive();
- expect(header.find(EuiButton).prop('href')).toEqual(`${docLinks.appSearchBase}/index.html`);
+ expect(getPageHeaderActions(wrapper).find(EuiButton).prop('href')).toEqual(
+ `${docLinks.appSearchBase}/index.html`
+ );
});
it('renders document creation components', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx
index 959d544a673243..27d9c3723f1268 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx
@@ -7,35 +7,36 @@
import React from 'react';
-import { EuiPageHeader, EuiPageContentBody, EuiButton } from '@elastic/eui';
+import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { FlashMessages } from '../../../shared/flash_messages';
import { DOCS_PREFIX } from '../../routes';
import { DocumentCreationButtons, DocumentCreationFlyout } from '../document_creation';
+import { getEngineBreadcrumbs } from '../engine';
+import { AppSearchPageTemplate } from '../layout';
+
export const EmptyEngineOverview: React.FC = () => {
return (
- <>
-
{i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.overview.empty.headingAction',
{ defaultMessage: 'View documentation' }
)}
,
- ]}
- />
-
-
-
-
-
- >
+ ],
+ }}
+ >
+
+
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx
index 00ac2af219bff5..620d913c5f9a7d 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx
@@ -5,11 +5,13 @@
* 2.0.
*/
+import '../../__mocks__/engine_logic.mock';
+
import React from 'react';
import { shallow } from 'enzyme';
-import { EuiPageHeader } from '@elastic/eui';
+import { getPageTitle } from '../../../test_helpers';
import { TotalStats, TotalCharts, RecentApiLogs } from './components';
import { EngineOverviewMetrics } from './engine_overview_metrics';
@@ -18,7 +20,7 @@ describe('EngineOverviewMetrics', () => {
it('renders', () => {
const wrapper = shallow();
- expect(wrapper.find(EuiPageHeader).prop('pageTitle')).toEqual('Engine overview');
+ expect(getPageTitle(wrapper)).toEqual('Engine overview');
expect(wrapper.find(TotalStats)).toHaveLength(1);
expect(wrapper.find(TotalCharts)).toHaveLength(1);
expect(wrapper.find(RecentApiLogs)).toHaveLength(1);
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx
index 2b01cfae49a201..b47ae21104ae96 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx
@@ -7,23 +7,24 @@
import React from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiPageHeader, EuiSpacer } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { FlashMessages } from '../../../shared/flash_messages';
+import { getEngineBreadcrumbs } from '../engine';
+import { AppSearchPageTemplate } from '../layout';
import { TotalStats, TotalCharts, RecentApiLogs } from './components';
export const EngineOverviewMetrics: React.FC = () => {
return (
- <>
-
-
-
+ }),
+ }}
+ >
@@ -34,6 +35,6 @@ export const EngineOverviewMetrics: React.FC = () => {
- >
+
);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx
index 8b06f4b26835d4..80230394ce2a2f 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx
@@ -10,6 +10,9 @@ import { setMockValues } from '../../../__mocks__/kea_logic';
jest.mock('../../../shared/layout', () => ({
generateNavLink: jest.fn(({ to }) => ({ href: to })),
}));
+jest.mock('../engine/engine_nav', () => ({
+ useEngineNav: () => [],
+}));
import { useAppSearchNav } from './nav';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx
index 57fa740caebec2..4737fbcf07e23c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx
@@ -15,6 +15,7 @@ import { ROLE_MAPPINGS_TITLE } from '../../../shared/role_mapping/constants';
import { AppLogic } from '../../app_logic';
import { ENGINES_PATH, SETTINGS_PATH, CREDENTIALS_PATH, ROLE_MAPPINGS_PATH } from '../../routes';
import { CREDENTIALS_TITLE } from '../credentials';
+import { useEngineNav } from '../engine/engine_nav';
import { ENGINES_TITLE } from '../engines';
import { SETTINGS_TITLE } from '../settings';
@@ -28,7 +29,7 @@ export const useAppSearchNav = () => {
id: 'engines',
name: ENGINES_TITLE,
...generateNavLink({ to: ENGINES_PATH, isRoot: true }),
- items: [], // TODO: Engine nav
+ items: useEngineNav(),
},
];
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx
index b2cd3d7b54a1a9..d724371cf1dc6e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx
@@ -98,6 +98,9 @@ export const AppSearchConfigured: React.FC> = (props) =
+
+
+
{canManageEngines && (
@@ -116,9 +119,6 @@ export const AppSearchConfigured: React.FC> = (props) =
} readOnlyMode={readOnlyMode}>
-
-
-