[Index Management] Update breadcrumbs (#165894)

## Summary
Fixes https://github.com/elastic/kibana/issues/165844
Partially addresses https://github.com/elastic/kibana/issues/122577 

This PR fixes the inconsistency with breadcrumbs in Index Management.
Previously, the breadcrumbs were updated only when an index template or
a component template was being created/edited. This PR adds breadcrumbs
for each of 4 main tabs: indices, data streams, index templates,
component templates. It also adds breadcrumbs for the new index details
page.

**Note for reviewers:** Tabs "Documents" and "Pipelines" on the new
index details page will not be implemented for now, so there are no
breadcrumbs for them.

**Note for copy review**: This PR can be reviewed only in terms on
breadcrumbs in Index Management. The copy review for the new index
details page can be done in this
[PR](https://github.com/elastic/kibana/pull/165705).

### Screenshots 

#### Indices list
Breadcrumbs "Indices"

<img width="1318" alt="Screenshot 2023-09-07 at 17 22 56"
src="1a670df6-b0b2-4272-b7ec-e50719884ad8">

#### Index details - overview tab
Breadcrumbs "Indices" (link) => "Index details" -> "Overview"
<img width="1130" alt="Screenshot 2023-09-08 at 17 20 45"
src="87b75a2c-d3e2-454d-8ad5-979feb3dc8a5">


#### Index details - mappings tab
Breadcrumbs "Indices"(link) -> "Index details" -> "Mappings"
<img width="1319" alt="Screenshot 2023-09-07 at 17 23 53"
src="d42baf78-9133-4c87-bdbd-1aedad119e62">

#### Index details - settings tab
Breadcrumbs "Indices"(link) -> "Index details" -> "Settings"
<img width="1305" alt="Screenshot 2023-09-07 at 17 24 00"
src="bf94497c-4ca3-413c-be57-37499c0839ba">

#### Index details - stats tab
Breadcrumbs "Indices"(link) -> "Index details" -> "Stats"
<img width="1300" alt="Screenshot 2023-09-07 at 17 24 12"
src="c1798ac2-7169-4bfe-8732-ed3f8e6d812e">

#### Data streams
Breadcrumbs "Data streams"
<img width="1319" alt="Screenshot 2023-09-07 at 17 23 03"
src="f4d0230e-994f-4537-a43d-065d364ffc7c">

#### Index templates
Breadcrumbs "Templates" 
<img width="1319" alt="Screenshot 2023-09-07 at 17 23 10"
src="610c609c-1213-4388-9c73-68711c2c4bb6">

#### Component templates
Breadcrumbs "Component templates" 
<img width="1303" alt="Screenshot 2023-09-07 at 17 23 28"
src="4f90eadb-a409-4e53-84f0-92b5f40b7639">



### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Yulia Čech 2023-09-21 14:41:09 +02:00 committed by GitHub
parent 0743a11c57
commit 969bbb0a11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 329 additions and 123 deletions

View file

@ -8,6 +8,10 @@
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import {
breadcrumbService,
IndexManagementBreadcrumb,
} from '../../../public/application/services/breadcrumbs';
import { API_BASE_PATH } from '../../../common/constants';
import * as fixtures from '../../../test/fixtures';
import { setupEnvironment } from '../helpers';
@ -40,6 +44,7 @@ const urlServiceMock = {
describe('Data Streams tab', () => {
const { httpSetup, httpRequestsMockHelpers } = setupEnvironment();
let testBed: DataStreamsTabTestBed;
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
describe('when there are no data streams', () => {
beforeEach(async () => {
@ -120,6 +125,12 @@ describe('Data Streams tab', () => {
testBed.component.update();
expect(testBed.find('dataStreamTable').text()).toContain('No data streams found');
});
test('updates the breadcrumbs to data streams', () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.dataStreams
);
});
});
describe('when there are data streams', () => {

View file

@ -8,6 +8,10 @@
import { act } from 'react-dom/test-utils';
import * as fixtures from '../../../test/fixtures';
import {
breadcrumbService,
IndexManagementBreadcrumb,
} from '../../../public/application/services/breadcrumbs';
import { API_BASE_PATH } from '../../../common/constants';
import { setupEnvironment, getRandomString } from '../helpers';
@ -26,8 +30,22 @@ const removeWhiteSpaceOnArrayValues = (array: any[]) =>
describe('Index Templates tab', () => {
const { httpSetup, httpRequestsMockHelpers } = setupEnvironment();
let testBed: IndexTemplatesTabTestBed;
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
describe('when there are no index templates of either kind', () => {
test('updates the breadcrumbs to component templates', async () => {
httpRequestsMockHelpers.setLoadTemplatesResponse({ templates: [], legacyTemplates: [] });
await act(async () => {
testBed = await setup(httpSetup);
});
const { component } = testBed;
component.update();
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.templates
);
});
test('should display an empty prompt', async () => {
httpRequestsMockHelpers.setLoadTemplatesResponse({ templates: [], legacyTemplates: [] });

View file

@ -45,12 +45,17 @@ jest.mock('../../../public/application/lib/ace', () => {
*/
import { stubWebWorker } from '@kbn/test-jest-helpers';
import { createMemoryHistory } from 'history';
import {
breadcrumbService,
IndexManagementBreadcrumb,
} from '../../../public/application/services/breadcrumbs';
stubWebWorker();
describe('<IndexManagementHome />', () => {
let testBed: IndicesTestBed;
let httpSetup: ReturnType<typeof setupEnvironment>['httpSetup'];
let httpRequestsMockHelpers: ReturnType<typeof setupEnvironment>['httpRequestsMockHelpers'];
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
beforeEach(() => {
const mockEnvironment = setupEnvironment();
@ -71,6 +76,12 @@ describe('<IndexManagementHome />', () => {
component.update();
});
test('updates the breadcrumbs to indices', () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indices
);
});
test('toggles the include hidden button through URL hash correctly', () => {
const { actions } = testBed;
expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(true);

View file

@ -8,6 +8,10 @@
import { setupEnvironment } from '../helpers';
import { IndexDetailsPageTestBed, setup } from './index_details_page.helpers';
import { act } from 'react-dom/test-utils';
import {
breadcrumbService,
IndexManagementBreadcrumb,
} from '../../../public/application/services/breadcrumbs';
import { IndexDetailsSection } from '../../../public/application/sections/home/index_list/details_page';
import {
testIndexEditableSettings,
@ -41,6 +45,7 @@ describe('<IndexDetailsPage />', () => {
let testBed: IndexDetailsPageTestBed;
let httpSetup: ReturnType<typeof setupEnvironment>['httpSetup'];
let httpRequestsMockHelpers: ReturnType<typeof setupEnvironment>['httpRequestsMockHelpers'];
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
beforeEach(async () => {
const mockEnvironment = setupEnvironment();
@ -89,6 +94,13 @@ describe('<IndexDetailsPage />', () => {
});
describe('Stats tab', () => {
it('updates the breadcrumbs to index details stats', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsStats
);
});
it('loads index stats from the API', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats);
expect(httpSetup.get).toHaveBeenLastCalledWith(`${API_BASE_PATH}/stats/${testIndexName}`, {
@ -187,6 +199,12 @@ describe('<IndexDetailsPage />', () => {
});
describe('Overview tab', () => {
it('updates the breadcrumbs to index details overview', async () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsOverview
);
});
it('renders index details', () => {
expect(testBed.actions.overview.indexDetailsContentExists()).toBe(true);
expect(testBed.actions.overview.indexStatsContentExists()).toBe(true);
@ -213,6 +231,12 @@ describe('<IndexDetailsPage />', () => {
});
describe('Mappings tab', () => {
it('updates the breadcrumbs to index details mappings', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsMappings
);
});
it('loads mappings from the API', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings);
expect(httpSetup.get).toHaveBeenLastCalledWith(`${API_BASE_PATH}/mapping/${testIndexName}`, {
@ -268,6 +292,13 @@ describe('<IndexDetailsPage />', () => {
});
describe('Settings tab', () => {
it('updates the breadcrumbs to index details settings', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Settings);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsSettings
);
});
it('loads settings from the API', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Settings);
expect(httpSetup.get).toHaveBeenLastCalledWith(`${API_BASE_PATH}/settings/${testIndexName}`, {

View file

@ -10,6 +10,7 @@ import { act } from 'react-dom/test-utils';
import '@kbn/es-ui-shared-plugin/public/components/code_editor/jest_mock';
import '../../../../../../test/global_mocks';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { setupEnvironment } from './helpers';
import { API_BASE_PATH } from './helpers/constants';
import { setup, ComponentTemplateCreateTestBed } from './helpers/component_template_create.helpers';
@ -53,6 +54,7 @@ describe('<ComponentTemplateCreate />', () => {
let testBed: ComponentTemplateCreateTestBed;
const { httpSetup, httpRequestsMockHelpers } = setupEnvironment();
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
describe('On component mount', () => {
beforeEach(async () => {
@ -63,6 +65,12 @@ describe('<ComponentTemplateCreate />', () => {
testBed.component.update();
});
test('updates the breadcrumbs to component templates', () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.componentTemplateCreate
);
});
test('should set the correct page header', async () => {
const { exists, find } = testBed;

View file

@ -9,6 +9,7 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import '../../../../../../test/global_mocks';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { setupEnvironment } from './helpers';
import { API_BASE_PATH } from './helpers/constants';
import { setup, ComponentTemplateEditTestBed } from './helpers/component_template_edit.helpers';
@ -69,6 +70,7 @@ describe('<ComponentTemplateEdit />', () => {
let testBed: ComponentTemplateEditTestBed;
const { httpSetup, httpRequestsMockHelpers } = setupEnvironment();
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
const COMPONENT_TEMPLATE_NAME = 'comp-1';
const COMPONENT_TEMPLATE_TO_EDIT = {
@ -95,6 +97,12 @@ describe('<ComponentTemplateEdit />', () => {
testBed.component.update();
});
test('updates the breadcrumbs to component templates', () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.componentTemplateEdit
);
});
test('should set the correct page title', () => {
const { exists, find } = testBed;

View file

@ -7,6 +7,7 @@
import { act } from 'react-dom/test-utils';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { ComponentTemplateListItem } from '../../shared_imports';
import { setupEnvironment, pageHelpers } from './helpers';
@ -18,6 +19,7 @@ const { setup } = pageHelpers.componentTemplateList;
describe('<ComponentTemplateList />', () => {
const { httpSetup, httpRequestsMockHelpers } = setupEnvironment();
let testBed: ComponentTemplateListTestBed;
jest.spyOn(breadcrumbService, 'setBreadcrumbs');
beforeEach(async () => {
await act(async () => {
@ -27,6 +29,12 @@ describe('<ComponentTemplateList />', () => {
testBed.component.update();
});
test('updates the breadcrumbs to component templates', () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.componentTemplates
);
});
describe('With component templates', () => {
const componentTemplate1: ComponentTemplateListItem = {
name: 'test_component_template_1',

View file

@ -13,6 +13,7 @@ import { executionContextServiceMock } from '@kbn/core-execution-context-browser
import { notificationServiceMock, applicationServiceMock, coreMock } from '@kbn/core/public/mocks';
import { GlobalFlyout } from '@kbn/es-ui-shared-plugin/public';
import { breadcrumbService } from '../../../../../services/breadcrumbs';
import { AppContextProvider } from '../../../../../app_context';
import { MappingsEditorProvider } from '../../../../mappings_editor';
import { ComponentTemplatesProvider } from '../../../component_templates_context';
@ -34,12 +35,14 @@ export const componentTemplatesDependencies = (httpSetup: HttpSetup, coreStart?:
trackMetric: () => {},
docLinks: docLinksServiceMock.createStartContract(),
toasts: notificationServiceMock.createSetupContract().toasts,
setBreadcrumbs: () => {},
getUrlForApp: applicationServiceMock.createStartContract().getUrlForApp,
executionContext: executionContextServiceMock.createInternalStartContract(),
});
export const setupEnvironment = initHttpRequests;
export const setupEnvironment = () => {
breadcrumbService.setup(() => undefined);
return initHttpRequests();
};
export const WithAppDependencies =
(Comp: any, httpSetup: HttpSetup, coreStart?: CoreStart) => (props: any) =>

View file

@ -13,6 +13,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { ScopedHistory } from '@kbn/core/public';
import { EuiLink, EuiText, EuiSpacer } from '@elastic/eui';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../services/breadcrumbs';
import {
APP_WRAPPER_CLASS,
PageLoading,
@ -48,6 +49,10 @@ export const ComponentTemplateList: React.FunctionComponent<Props> = ({
const { api, trackMetric, documentation } = useComponentTemplatesContext();
const redirectTo = useRedirectPath(history);
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.componentTemplates);
}, []);
const { data, isLoading, error, resendRequest } = api.useLoadComponentTemplates();
const [componentTemplatesToDelete, setComponentTemplatesToDelete] = useState<string[]>([]);

View file

@ -10,6 +10,7 @@ import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiPageSection, EuiSpacer, EuiPageHeader } from '@elastic/eui';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { ComponentTemplateDeserialized } from '../../shared_imports';
import { useComponentTemplatesContext } from '../../component_templates_context';
import { ComponentTemplateForm } from '../component_template_form';
@ -28,7 +29,7 @@ export const ComponentTemplateCreate: React.FunctionComponent<RouteComponentProp
const [isSaving, setIsSaving] = useState<boolean>(false);
const [saveError, setSaveError] = useState<any>(null);
const { api, breadcrumbs } = useComponentTemplatesContext();
const { api } = useComponentTemplatesContext();
const onSave = async (componentTemplate: ComponentTemplateDeserialized) => {
const { name } = componentTemplate;
@ -54,19 +55,31 @@ export const ComponentTemplateCreate: React.FunctionComponent<RouteComponentProp
setSaveError(null);
};
const isCloning = Boolean(sourceComponentTemplate);
useEffect(() => {
breadcrumbs.setCreateBreadcrumbs();
}, [breadcrumbs]);
if (isCloning) {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.componentTemplateClone);
} else {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.componentTemplateCreate);
}
}, [isCloning]);
return (
<EuiPageSection restrictWidth style={{ width: '100%' }}>
<EuiPageHeader
pageTitle={
<span data-test-subj="pageTitle">
<FormattedMessage
id="xpack.idxMgmt.createComponentTemplate.pageTitle"
defaultMessage="Create component template"
/>
{isCloning ? (
<FormattedMessage
id="xpack.idxMgmt.cloneComponentTemplate.pageTitle"
defaultMessage="Clone component template"
/>
) : (
<FormattedMessage
id="xpack.idxMgmt.createComponentTemplate.pageTitle"
defaultMessage="Create component template"
/>
)}
</span>
}
bottomBorder

View file

@ -12,6 +12,7 @@ import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import { EuiPageSection, EuiPageHeader, EuiSpacer } from '@elastic/eui';
import { History } from 'history';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { useComponentTemplatesContext } from '../../component_templates_context';
import {
ComponentTemplateDeserialized,
@ -61,7 +62,7 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<
},
history,
}) => {
const { api, breadcrumbs, overlays } = useComponentTemplatesContext();
const { api, overlays } = useComponentTemplatesContext();
const { activeStep: defaultActiveStep, updateStep } = useStepFromQueryString(history);
const redirectTo = useRedirectPath(history);
@ -75,8 +76,8 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<
const dataStreams = useMemo(() => dataStreamResponse?.data_streams ?? [], [dataStreamResponse]);
useEffect(() => {
breadcrumbs.setEditBreadcrumbs();
}, [breadcrumbs]);
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.componentTemplateEdit);
}, []);
const onSave = async (updatedComponentTemplate: ComponentTemplateDeserialized) => {
setIsSaving(true);

View file

@ -15,8 +15,7 @@ import {
CoreStart,
ExecutionContextStart,
} from '@kbn/core/public';
import { ManagementAppMountParams } from '@kbn/management-plugin/public';
import { getApi, getUseRequest, getSendRequest, getDocumentation, getBreadcrumbs } from './lib';
import { getApi, getUseRequest, getSendRequest, getDocumentation } from './lib';
const ComponentTemplatesContext = createContext<Context | undefined>(undefined);
@ -26,7 +25,6 @@ interface Props {
trackMetric: (type: UiCounterMetricType, eventName: string) => void;
docLinks: DocLinksStart;
toasts: NotificationsSetup['toasts'];
setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs'];
getUrlForApp: CoreStart['application']['getUrlForApp'];
executionContext: ExecutionContextStart;
overlays: CoreStart['overlays'];
@ -37,7 +35,6 @@ interface Context {
apiBasePath: string;
api: ReturnType<typeof getApi>;
documentation: ReturnType<typeof getDocumentation>;
breadcrumbs: ReturnType<typeof getBreadcrumbs>;
trackMetric: (type: UiCounterMetricType, eventName: string) => void;
toasts: NotificationsSetup['toasts'];
overlays: CoreStart['overlays'];
@ -59,7 +56,6 @@ export const ComponentTemplatesProvider = ({
trackMetric,
docLinks,
toasts,
setBreadcrumbs,
getUrlForApp,
executionContext,
} = value;
@ -69,7 +65,6 @@ export const ComponentTemplatesProvider = ({
const api = getApi(useRequest, sendRequest, apiBasePath, trackMetric);
const documentation = getDocumentation(docLinks);
const breadcrumbs = getBreadcrumbs(setBreadcrumbs);
return (
<ComponentTemplatesContext.Provider
@ -81,7 +76,6 @@ export const ComponentTemplatesProvider = ({
toasts,
httpClient,
apiBasePath,
breadcrumbs,
getUrlForApp,
executionContext,
}}

View file

@ -1,63 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { ManagementAppMountParams } from '@kbn/management-plugin/public';
export const getBreadcrumbs = (setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']) => {
const baseBreadcrumbs = [
{
text: i18n.translate('xpack.idxMgmt.componentTemplate.breadcrumb.homeLabel', {
defaultMessage: 'Index Management',
}),
href: '/',
},
{
text: i18n.translate('xpack.idxMgmt.componentTemplate.breadcrumb.componentTemplatesLabel', {
defaultMessage: 'Component templates',
}),
href: '/component_templates',
},
];
const setCreateBreadcrumbs = () => {
const createBreadcrumbs = [
...baseBreadcrumbs,
{
text: i18n.translate(
'xpack.idxMgmt.componentTemplate.breadcrumb.createComponentTemplateLabel',
{
defaultMessage: 'Create component template',
}
),
},
];
return setBreadcrumbs(createBreadcrumbs);
};
const setEditBreadcrumbs = () => {
const editBreadcrumbs = [
...baseBreadcrumbs,
{
text: i18n.translate(
'xpack.idxMgmt.componentTemplate.breadcrumb.editComponentTemplateLabel',
{
defaultMessage: 'Edit component template',
}
),
},
];
return setBreadcrumbs(editBreadcrumbs);
};
return {
setCreateBreadcrumbs,
setEditBreadcrumbs,
};
};

View file

@ -10,5 +10,3 @@ export * from './api';
export * from './request';
export * from './documentation';
export * from './breadcrumbs';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
@ -34,6 +34,7 @@ import {
} from '../../../../shared_imports';
import { useAppContext } from '../../../app_context';
import { useLoadDataStreams } from '../../../services/api';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../services/breadcrumbs';
import { documentationService } from '../../../services/documentation';
import { Section } from '../home';
import { DataStreamTable } from './data_stream_table';
@ -66,6 +67,10 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
page: 'indexManagementDataStreamsTab',
});
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.dataStreams);
}, []);
const [isIncludeStatsChecked, setIsIncludeStatsChecked] = useState(false);
const {
error,

View file

@ -5,12 +5,11 @@
* 2.0.
*/
import React, { useEffect } from 'react';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Routes, Route } from '@kbn/shared-ux-router';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonEmpty, EuiPageHeader, EuiSpacer } from '@elastic/eui';
import { breadcrumbService } from '../../services/breadcrumbs';
import { documentationService } from '../../services/documentation';
import { useAppContext } from '../../app_context';
import { ComponentTemplateList } from '../../components/component_templates';
@ -96,10 +95,6 @@ export const IndexManagementHome: React.FunctionComponent<RouteComponentProps<Ma
history.push(`/${newSection}`);
};
useEffect(() => {
breadcrumbService.setBreadcrumbs('home');
}, []);
const indexManagementTabs = (
<>
<EuiPageHeader

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useEffect } from 'react';
import {
EuiButton,
EuiCodeBlock,
@ -24,6 +24,7 @@ import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { SectionLoading } from '@kbn/es-ui-shared-plugin/public';
import { useLoadIndexMappings, documentationService } from '../../../../services';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
export const DetailsPageMappings: FunctionComponent<RouteComponentProps<{ indexName: string }>> = ({
match: {
@ -32,6 +33,10 @@ export const DetailsPageMappings: FunctionComponent<RouteComponentProps<{ indexN
}) => {
const { isLoading, data, error, resendRequest } = useLoadIndexMappings(indexName);
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsMappings);
}, []);
if (isLoading) {
return (
<SectionLoading>

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useState, useMemo } from 'react';
import React, { useState, useMemo, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiSpacer,
@ -26,6 +26,7 @@ import {
} from '@kbn/search-api-panels';
import type { Index } from '../../../../../../../common';
import { useAppContext } from '../../../../../app_context';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../../services/breadcrumbs';
import { languageDefinitions, curlDefinition } from './languages';
interface Props {
@ -44,6 +45,10 @@ export const DetailsPageOverview: React.FunctionComponent<Props> = ({ indexDetai
} = indexDetails;
const { config, core, plugins } = useAppContext();
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsOverview);
}, []);
const [selectedLanguage, setSelectedLanguage] = useState<LanguageDefinition>(curlDefinition);
const elasticsearchURL = useMemo(() => {

View file

@ -5,13 +5,14 @@
* 2.0.
*/
import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { EuiButton, EuiPageTemplate, EuiSpacer, EuiText } from '@elastic/eui';
import { SectionLoading } from '@kbn/es-ui-shared-plugin/public';
import { FormattedMessage } from '@kbn/i18n-react';
import { useLoadIndexSettings } from '../../../../services';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { DetailsPageSettingsContent } from './details_page_settings_content';
export const DetailsPageSettings: FunctionComponent<
@ -24,6 +25,10 @@ export const DetailsPageSettings: FunctionComponent<
}) => {
const { isLoading, data, error, resendRequest } = useLoadIndexSettings(indexName);
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsSettings);
}, []);
if (isLoading) {
return (
<SectionLoading>

View file

@ -27,6 +27,7 @@ import { css } from '@emotion/react';
import { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types';
import { SectionLoading, Error } from '../../../../../shared_imports';
import { loadIndexStatistics, documentationService } from '../../../../services';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
interface Props {
isIndexOpen: boolean;
@ -44,6 +45,10 @@ export const DetailsPageStats: FunctionComponent<
const [error, setError] = useState<Error | null>();
const [indexStats, setIndexStats] = useState<IndicesStatsResponse | null>();
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsStats);
}, []);
const fetchIndexStats = useCallback(async () => {
setIsLoading(true);
try {

View file

@ -5,11 +5,12 @@
* 2.0.
*/
import React, { useCallback } from 'react';
import React, { useCallback, useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { ScopedHistory } from '@kbn/core/public';
import { APP_WRAPPER_CLASS, useExecutionContext } from '../../../../shared_imports';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../services/breadcrumbs';
import { useAppContext } from '../../../app_context';
import { DetailPanel } from './detail_panel';
import { IndexTable } from './index_table';
@ -25,6 +26,10 @@ export const IndexList: React.FunctionComponent<RouteComponentProps> = ({ histor
page: 'indexManagementIndicesTab',
});
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indices);
}, []);
const openDetailPanel = useCallback(
(indexName: string) => {
return history.push(encodeURI(`/indices/${indexName}`));

View file

@ -34,6 +34,7 @@ import {
} from '../../../../shared_imports';
import { LegacyIndexTemplatesDeprecation } from '../../../components';
import { useLoadIndexTemplates } from '../../../services/api';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../services/breadcrumbs';
import { documentationService } from '../../../services/documentation';
import { useAppContext, useServices } from '../../../app_context';
import {
@ -80,6 +81,10 @@ export const TemplateList: React.FunctionComponent<RouteComponentProps<MatchPara
page: 'indexManagementIndexTemplatesTab',
});
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.templates);
}, []);
const [filters, setFilters] = useState<Filters<FilterName>>({
managed: {
name: i18n.translate('xpack.idxMgmt.indexTemplatesList.viewManagedTemplateLabel', {

View file

@ -11,14 +11,13 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { EuiPageSection } from '@elastic/eui';
import { ScopedHistory } from '@kbn/core/public';
import { PageLoading, PageError, Error } from '../../../shared_imports';
import { PageLoading, PageError, Error, attemptToURIDecode } from '../../../shared_imports';
import { TemplateDeserialized } from '../../../../common';
import { TemplateForm } from '../../components';
import { breadcrumbService } from '../../services/breadcrumbs';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../services/breadcrumbs';
import { getTemplateDetailsLink } from '../../services/routing';
import { saveTemplate, useLoadIndexTemplate } from '../../services/api';
import { getIsLegacyFromQueryParams } from '../../lib/index_templates';
import { attemptToURIDecode } from '../../../shared_imports';
import { useAppContext } from '../../app_context';
interface MatchParams {
@ -70,7 +69,7 @@ export const TemplateClone: React.FunctionComponent<RouteComponentProps<MatchPar
};
useEffect(() => {
breadcrumbService.setBreadcrumbs('templateClone');
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.templateClone);
}, []);
if (isLoading) {

View file

@ -6,16 +6,15 @@
*/
import React, { useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { RouteComponentProps, useLocation } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiPageSection } from '@elastic/eui';
import { useLocation } from 'react-router-dom';
import { parse } from 'query-string';
import { ScopedHistory } from '@kbn/core/public';
import { TemplateDeserialized } from '../../../../common';
import { TemplateForm } from '../../components';
import { breadcrumbService } from '../../services/breadcrumbs';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../services/breadcrumbs';
import { saveTemplate } from '../../services/api';
import { getTemplateDetailsLink } from '../../services/routing';
import { useAppContext } from '../../app_context';
@ -53,7 +52,7 @@ export const TemplateCreate: React.FunctionComponent<RouteComponentProps> = ({ h
};
useEffect(() => {
breadcrumbService.setBreadcrumbs('templateCreate');
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.templateCreate);
}, []);
return (

View file

@ -14,7 +14,7 @@ import { ScopedHistory } from '@kbn/core/public';
import { TemplateDeserialized } from '../../../../common';
import { PageError, PageLoading, attemptToURIDecode, Error } from '../../../shared_imports';
import { breadcrumbService } from '../../services/breadcrumbs';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../services/breadcrumbs';
import { useLoadIndexTemplate, updateTemplate } from '../../services/api';
import { getTemplateDetailsLink } from '../../services/routing';
import { TemplateForm } from '../../components';
@ -46,7 +46,7 @@ export const TemplateEdit: React.FunctionComponent<RouteComponentProps<MatchPara
const { error, data: template, isLoading } = useLoadIndexTemplate(decodedTemplateName, isLegacy);
useEffect(() => {
breadcrumbService.setBreadcrumbs('templateEdit');
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.templateEdit);
}, []);
const onSave = async (updatedTemplate: TemplateDeserialized) => {

View file

@ -7,10 +7,41 @@
import { i18n } from '@kbn/i18n';
import { ManagementAppMountParams } from '@kbn/management-plugin/public';
import { EuiBreadcrumb } from '@elastic/eui';
type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs'];
export enum IndexManagementBreadcrumb {
home = 'home',
/**
* Indices tab
*/
indices = 'indices',
/**
* Index details page
*/
indexDetailsOverview = 'indexDetailsOverview',
indexDetailsMappings = 'indexDetailsMappings',
indexDetailsSettings = 'indexDetailsSettings',
indexDetailsStats = 'indexDetailsStats',
/**
* Data streams tab
*/
dataStreams = 'dataStreams',
/**
* Index templates tab
*/
templates = 'templates',
templateCreate = 'templateCreate',
templateEdit = 'templateEdit',
templateClone = 'templateClone',
/**
* Component templates tab
*/
componentTemplates = 'componentTemplates',
componentTemplateCreate = 'componentTemplateCreate',
componentTemplateEdit = 'componentTemplateEdit',
componentTemplateClone = 'componentTemplateClone',
/**
* Enrich policies tab
*/
@ -20,12 +51,9 @@ export enum IndexManagementBreadcrumb {
class BreadcrumbService {
private breadcrumbs: {
[key: string]: Array<{
text: string;
href?: string;
}>;
[key in IndexManagementBreadcrumb]?: EuiBreadcrumb[];
} = {
home: [],
home: [] as EuiBreadcrumb[],
};
private setBreadcrumbsHandler?: SetBreadcrumbs;
@ -41,6 +69,62 @@ class BreadcrumbService {
},
];
this.breadcrumbs.indices = [
...this.breadcrumbs.home,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.indicesLabel', {
defaultMessage: 'Indices',
}),
href: `/indices`,
},
];
const indexDetailsBreadcrumb = {
text: i18n.translate('xpack.idxMgmt.breadcrumb.indexDetailsLabel', {
defaultMessage: 'Index details',
}),
};
this.breadcrumbs.indexDetailsOverview = [
...this.breadcrumbs.indices,
indexDetailsBreadcrumb,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.indexDetailsOverviewLabel', {
defaultMessage: 'Overview',
}),
},
];
this.breadcrumbs.indexDetailsMappings = [
...this.breadcrumbs.indices,
indexDetailsBreadcrumb,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.indexDetailsMappingsLabel', {
defaultMessage: 'Mappings',
}),
},
];
this.breadcrumbs.indexDetailsSettings = [
...this.breadcrumbs.indices,
indexDetailsBreadcrumb,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.indexDetailsSettingsLabel', {
defaultMessage: 'Settings',
}),
},
];
this.breadcrumbs.indexDetailsStats = [
...this.breadcrumbs.indices,
indexDetailsBreadcrumb,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.indexDetailsStatsLabel', {
defaultMessage: 'Stats',
}),
},
];
this.breadcrumbs.templates = [
...this.breadcrumbs.home,
{
@ -78,6 +162,59 @@ class BreadcrumbService {
},
];
this.breadcrumbs.dataStreams = [
...this.breadcrumbs.home,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.dataStreamsLabel', {
defaultMessage: 'Data streams',
}),
href: `/data_streams`,
},
];
this.breadcrumbs.componentTemplates = [
...this.breadcrumbs.home,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.componentTemplatesLabel', {
defaultMessage: 'Component templates',
}),
href: `/component_templates`,
},
];
this.breadcrumbs.componentTemplateCreate = [
...this.breadcrumbs.componentTemplates,
{
text: i18n.translate('xpack.idxMgmt.breadcrumb.createComponentTemplateLabel', {
defaultMessage: 'Create component templates',
}),
},
];
this.breadcrumbs.componentTemplateEdit = [
...this.breadcrumbs.componentTemplates,
{
text: i18n.translate(
'xpack.idxMgmt.componentTemplate.breadcrumb.editComponentTemplateLabel',
{
defaultMessage: 'Edit component template',
}
),
},
];
this.breadcrumbs.componentTemplateClone = [
...this.breadcrumbs.componentTemplates,
{
text: i18n.translate(
'xpack.idxMgmt.componentTemplate.breadcrumb.cloneComponentTemplateLabel',
{
defaultMessage: 'Clone component template',
}
),
},
];
this.breadcrumbs.enrichPolicies = [
...this.breadcrumbs.home,
{
@ -99,14 +236,14 @@ class BreadcrumbService {
];
}
public setBreadcrumbs(type: string): void {
public setBreadcrumbs(type: IndexManagementBreadcrumb): void {
if (!this.setBreadcrumbsHandler) {
throw new Error(`BreadcrumbService#setup() must be called first!`);
}
const newBreadcrumbs = this.breadcrumbs[type]
? [...this.breadcrumbs[type]]
: [...this.breadcrumbs.home];
? [...this.breadcrumbs[type]!]
: [...this.breadcrumbs.home!];
// Pop off last breadcrumb
const lastBreadcrumb = newBreadcrumbs.pop() as {

View file

@ -17221,10 +17221,7 @@
"xpack.idxMgmt.breadcrumb.editTemplateLabel": "Modifier le modèle",
"xpack.idxMgmt.breadcrumb.homeLabel": "Gestion des index",
"xpack.idxMgmt.breadcrumb.templatesLabel": "Modèles",
"xpack.idxMgmt.componentTemplate.breadcrumb.componentTemplatesLabel": "Modèles de composants",
"xpack.idxMgmt.componentTemplate.breadcrumb.createComponentTemplateLabel": "Créer un modèle de composant",
"xpack.idxMgmt.componentTemplate.breadcrumb.editComponentTemplateLabel": "Modifier le modèle de composant",
"xpack.idxMgmt.componentTemplate.breadcrumb.homeLabel": "Gestion des index",
"xpack.idxMgmt.componentTemplateClone.loadComponentTemplateTitle": "Erreur lors du chargement du modèle de composant \"{sourceComponentTemplateName}\".",
"xpack.idxMgmt.componentTemplateDetails.aliasesTabTitle": "Alias",
"xpack.idxMgmt.componentTemplateDetails.cloneActionLabel": "Cloner",

View file

@ -17235,10 +17235,7 @@
"xpack.idxMgmt.breadcrumb.editTemplateLabel": "テンプレートを編集",
"xpack.idxMgmt.breadcrumb.homeLabel": "インデックス管理",
"xpack.idxMgmt.breadcrumb.templatesLabel": "テンプレート",
"xpack.idxMgmt.componentTemplate.breadcrumb.componentTemplatesLabel": "コンポーネントテンプレート",
"xpack.idxMgmt.componentTemplate.breadcrumb.createComponentTemplateLabel": "コンポーネントテンプレートの作成",
"xpack.idxMgmt.componentTemplate.breadcrumb.editComponentTemplateLabel": "コンポーネントテンプレートの編集",
"xpack.idxMgmt.componentTemplate.breadcrumb.homeLabel": "インデックス管理",
"xpack.idxMgmt.componentTemplateClone.loadComponentTemplateTitle": "コンポーネントテンプレート「{sourceComponentTemplateName}」の読み込みエラー",
"xpack.idxMgmt.componentTemplateDetails.aliasesTabTitle": "エイリアス",
"xpack.idxMgmt.componentTemplateDetails.cloneActionLabel": "クローンを作成",

View file

@ -17235,10 +17235,7 @@
"xpack.idxMgmt.breadcrumb.editTemplateLabel": "编辑模板",
"xpack.idxMgmt.breadcrumb.homeLabel": "索引管理",
"xpack.idxMgmt.breadcrumb.templatesLabel": "模板",
"xpack.idxMgmt.componentTemplate.breadcrumb.componentTemplatesLabel": "组件模板",
"xpack.idxMgmt.componentTemplate.breadcrumb.createComponentTemplateLabel": "创建组件模板",
"xpack.idxMgmt.componentTemplate.breadcrumb.editComponentTemplateLabel": "编辑组件模板",
"xpack.idxMgmt.componentTemplate.breadcrumb.homeLabel": "索引管理",
"xpack.idxMgmt.componentTemplateClone.loadComponentTemplateTitle": "加载组件模板“{sourceComponentTemplateName}”时出错。",
"xpack.idxMgmt.componentTemplateDetails.aliasesTabTitle": "别名",
"xpack.idxMgmt.componentTemplateDetails.cloneActionLabel": "克隆",

View file

@ -76,7 +76,11 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Explore', 'Alerts', 'Rules']);
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management:index_management' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Content', 'Index Management']);
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts([
'Content',
'Index Management',
'Indices',
]);
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management:ingest_pipelines' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Content', 'Ingest Pipelines']);