Skip to content

Commit

Permalink
[Index Management] Fix breadcrumbs for index details tabs (elastic#17…
Browse files Browse the repository at this point in the history
…7743)

## Summary

Fixes elastic#177722

This PR fixes a bug that index details tabs added via extensions service
(currently only ILM on stateful and Documents on serverless search)
didn't update breadcrumbs.
The suggested solution is to set the breadcrumb to the tab's label with
an option to specify another text for the breadcrumb if needed.

### How to test
#### Stateful
1. Navigate to Index Management 
2. In the filters enable hidden indices and select "Lifecycle status:
managed"
3. Click on any index and click on the "Index lifecycle" tab
4. Check that the breadcrumb is correctly set


### Screenshots 
<img width="1640" alt="Screenshot 2024-02-23 at 18 05 45"
src="https://github.com/elastic/kibana/assets/6585477/f112f1fd-b2dd-4bd1-9cc1-cb172b827c07">



#### Serverless
1. Start a serverless search project (`yarn es serverless
--projectType=es --ssl` and `yarn serverless-es --ssl`)
2. Navigate to index management and open any index 
3. Open the documents tab
4. Check that the breadcrumb is correctly set


### Screenshots 

<img width="1638" alt="Screenshot 2024-03-04 at 14 40 31"
src="https://github.com/elastic/kibana/assets/6585477/256929ed-2571-494f-b2ab-7a4d34baf485">
  • Loading branch information
yuliacech authored Mar 5, 2024
1 parent dda8c8f commit 321eaae
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ describe('<IndexDetailsPage />', () => {
it('updates the breadcrumbs to index details stats', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsStats
IndexManagementBreadcrumb.indexDetails,
{ text: 'Statistics' }
);
});

Expand Down Expand Up @@ -237,7 +238,8 @@ describe('<IndexDetailsPage />', () => {
describe('Overview tab', () => {
it('updates the breadcrumbs to index details overview', async () => {
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsOverview
IndexManagementBreadcrumb.indexDetails,
{ text: 'Overview' }
);
});

Expand Down Expand Up @@ -455,7 +457,8 @@ describe('<IndexDetailsPage />', () => {
it('updates the breadcrumbs to index details mappings', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsMappings
IndexManagementBreadcrumb.indexDetails,
{ text: 'Mappings' }
);
});
it('loads mappings from the API', async () => {
Expand Down Expand Up @@ -551,7 +554,8 @@ describe('<IndexDetailsPage />', () => {
it('updates the breadcrumbs to index details settings', async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Settings);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetailsSettings
IndexManagementBreadcrumb.indexDetails,
{ text: 'Settings' }
);
});

Expand Down Expand Up @@ -938,6 +942,34 @@ describe('<IndexDetailsPage />', () => {
expect(content).toEqual(testContent);
});

it("sets breadcrumbs for the tab using the tab's name", async () => {
await testBed.actions.clickIndexDetailsTab(testTabId);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetails,
{ text: 'Test tab' }
);
});

it('sets breadcrumbs for the tab using the tab property', async () => {
const extensionsServiceMock = {
indexDetailsTabs: [{ ...additionalTab, breadcrumb: { text: 'special breadcrumb' } }],
};
await act(async () => {
testBed = await setup({
httpSetup,
dependencies: {
services: { extensionsService: extensionsServiceMock },
},
});
});
testBed.component.update();
await testBed.actions.clickIndexDetailsTab(testTabId);
expect(breadcrumbService.setBreadcrumbs).toHaveBeenLastCalledWith(
IndexManagementBreadcrumb.indexDetails,
{ text: 'special breadcrumb' }
);
});

it('additional tab is the first in the order', () => {
const tabs = testBed.actions.getIndexDetailsTabs();
expect(tabs).toEqual(['Test tab', 'Overview', 'Mappings', 'Settings', 'Statistics']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { FunctionComponent, ReactNode } from 'react';
import { ApplicationStart } from '@kbn/core-application-browser';
import { EuiBreadcrumb } from '@elastic/eui';
import { Index } from '../types';

export enum Section {
Expand Down Expand Up @@ -41,4 +42,5 @@ export interface IndexDetailsTab {
// an optional function to return a boolean for when to render the tab
// if omitted, the tab is always rendered
shouldRenderTab?: (args: { index: Index }) => boolean;
breadcrumb?: EuiBreadcrumb;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '@elastic/eui';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { RouteComponentProps } from 'react-router-dom';

import { resetIndexUrlParams } from './reset_index_url_params';
Expand All @@ -39,33 +40,35 @@ import { DetailsPageTab } from './details_page_tab';
const defaultTabs: IndexDetailsTab[] = [
{
id: IndexDetailsSection.Overview,
name: (
<FormattedMessage id="xpack.idxMgmt.indexDetails.overviewTitle" defaultMessage="Overview" />
),
name: i18n.translate('xpack.idxMgmt.indexDetails.overviewTitle', {
defaultMessage: 'Overview',
}),
renderTabContent: ({ index }) => <DetailsPageOverview indexDetails={index} />,
order: 10,
},
{
id: IndexDetailsSection.Mappings,
name: (
<FormattedMessage id="xpack.idxMgmt.indexDetails.mappingsTitle" defaultMessage="Mappings" />
),
name: i18n.translate('xpack.idxMgmt.indexDetails.mappingsTitle', {
defaultMessage: 'Mappings',
}),
renderTabContent: ({ index }) => <DetailsPageMappings index={index} />,
order: 20,
},
{
id: IndexDetailsSection.Settings,
name: (
<FormattedMessage id="xpack.idxMgmt.indexDetails.settingsTitle" defaultMessage="Settings" />
),
name: i18n.translate('xpack.idxMgmt.indexDetails.settingsTitle', {
defaultMessage: 'Settings',
}),
renderTabContent: ({ index }) => <DetailsPageSettings indexName={index.name} />,
order: 30,
},
];

const statsTab: IndexDetailsTab = {
id: IndexDetailsSection.Stats,
name: <FormattedMessage id="xpack.idxMgmt.indexDetails.statsTitle" defaultMessage="Statistics" />,
name: i18n.translate('xpack.idxMgmt.indexDetails.statsTitle', {
defaultMessage: 'Statistics',
}),
renderTabContent: ({ index }) => (
<DetailsPageStats indexName={index.name} isIndexOpen={index.status === INDEX_OPEN} />
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { FunctionComponent, useEffect, useMemo, useState } from 'react';
import React, { FunctionComponent, useMemo, useState } from 'react';
import { EuiButton, EuiPageTemplate, EuiSpacer, EuiText } from '@elastic/eui';

import { FormattedMessage } from '@kbn/i18n-react';
Expand All @@ -14,15 +14,11 @@ import { SectionLoading } from '@kbn/es-ui-shared-plugin/public';
import { DetailsPageMappingsContent } from './details_page_mappings_content';
import { Index } from '../../../../../../common';
import { useLoadIndexMappings } from '../../../../services';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';

export const DetailsPageMappings: FunctionComponent<{ index: Index }> = ({ index }) => {
const { isLoading, data, error, resendRequest } = useLoadIndexMappings(index.name);
const [jsonError, setJsonError] = useState<boolean>(false);

useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsMappings);
}, []);
const stringifiedData = useMemo(() => {
if (data) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState, useMemo, useEffect } from 'react';
import React, { useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import {
Expand All @@ -30,7 +30,6 @@ import { StatusDetails } from './status_details';
import type { Index } from '../../../../../../../common';
import { useAppContext } from '../../../../../app_context';
import { documentationService } from '../../../../../services';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../../services/breadcrumbs';
import { languageDefinitions, curlDefinition } from './languages';
import { DataStreamDetails } from './data_stream_details';
import { StorageDetails } from './storage_details';
Expand Down Expand Up @@ -60,10 +59,6 @@ export const DetailsPageOverview: React.FunctionComponent<Props> = ({ indexDetai
services: { extensionsService },
} = useAppContext();

useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsOverview);
}, []);

const [selectedLanguage, setSelectedLanguage] = useState<LanguageDefinition>(curlDefinition);

const elasticsearchURL = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,19 @@
* 2.0.
*/

import React, { FunctionComponent, useEffect } from 'react';
import React, { FunctionComponent } from 'react';
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<{
indexName: string;
}> = ({ indexName }) => {
const { isLoading, data, error, resendRequest } = useLoadIndexSettings(indexName);

useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsSettings);
}, []);

if (isLoading) {
return (
<SectionLoading>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ 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';

export const DetailsPageStats: FunctionComponent<{ indexName: string; isIndexOpen: boolean }> = ({
indexName,
Expand All @@ -36,10 +35,6 @@ export const DetailsPageStats: FunctionComponent<{ indexName: string; isIndexOpe
const [error, setError] = useState<Error | null>();
const [indexStats, setIndexStats] = useState<IndicesStatsResponse | null>();

useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsStats);
}, []);

const fetchIndexStats = useCallback(async () => {
setIsLoading(true);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* 2.0.
*/

import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useEffect } from 'react';
import { EuiBreadcrumb } from '@elastic/eui';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs';
import { Index } from '../../../../../../common';
import { IndexDetailsTab, IndexDetailsTabId } from '../../../../../../common/constants';
import { useAppContext } from '../../../../app_context';
Expand All @@ -21,6 +23,12 @@ export const DetailsPageTab: FunctionComponent<Props> = ({ tabs, tab, index }) =
const {
core: { getUrlForApp },
} = useAppContext();

useEffect(() => {
const breadcrumb: EuiBreadcrumb = selectedTab?.breadcrumb ?? { text: selectedTab?.name };
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetails, breadcrumb);
}, [selectedTab]);

return selectedTab ? (
selectedTab.renderTabContent({ index, getUrlForApp })
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ export enum IndexManagementBreadcrumb {
/**
* Index details page
*/
indexDetailsOverview = 'indexDetailsOverview',
indexDetailsMappings = 'indexDetailsMappings',
indexDetailsSettings = 'indexDetailsSettings',
indexDetailsStats = 'indexDetailsStats',
indexDetails = 'indexDetails',
/**
* Data streams tab
*/
Expand Down Expand Up @@ -79,48 +76,11 @@ class BreadcrumbService {
},
];

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.indexDetails = [
...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',
text: i18n.translate('xpack.idxMgmt.breadcrumb.indexDetailsLabel', {
defaultMessage: 'Index details',
}),
},
];
Expand Down Expand Up @@ -236,7 +196,10 @@ class BreadcrumbService {
];
}

public setBreadcrumbs(type: IndexManagementBreadcrumb): void {
public setBreadcrumbs(
type: IndexManagementBreadcrumb,
additionalBreadcrumb?: EuiBreadcrumb
): void {
if (!this.setBreadcrumbsHandler) {
throw new Error(`BreadcrumbService#setup() must be called first!`);
}
Expand All @@ -245,6 +208,10 @@ class BreadcrumbService {
? [...this.breadcrumbs[type]!]
: [...this.breadcrumbs.home!];

if (additionalBreadcrumb) {
newBreadcrumbs.push(additionalBreadcrumb);
}

// Pop off last breadcrumb
const lastBreadcrumb = newBreadcrumbs.pop() as {
text: string;
Expand Down
4 changes: 0 additions & 4 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -18100,10 +18100,6 @@
"xpack.idxMgmt.breadcrumb.enrichPolicyLabel": "Politiques d'enrichissement",
"xpack.idxMgmt.breadcrumb.homeLabel": "Gestion des index",
"xpack.idxMgmt.breadcrumb.indexDetailsLabel": "Détails de l'index",
"xpack.idxMgmt.breadcrumb.indexDetailsMappingsLabel": "Mappings",
"xpack.idxMgmt.breadcrumb.indexDetailsOverviewLabel": "Aperçu",
"xpack.idxMgmt.breadcrumb.indexDetailsSettingsLabel": "Paramètres",
"xpack.idxMgmt.breadcrumb.indexDetailsStatsLabel": "Statistiques",
"xpack.idxMgmt.breadcrumb.indicesLabel": "Index",
"xpack.idxMgmt.breadcrumb.templatesLabel": "Modèles",
"xpack.idxMgmt.cloneComponentTemplate.pageTitle": "Cloner le modèle de composant",
Expand Down
4 changes: 0 additions & 4 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -18113,10 +18113,6 @@
"xpack.idxMgmt.breadcrumb.enrichPolicyLabel": "エンリッチポリシー",
"xpack.idxMgmt.breadcrumb.homeLabel": "インデックス管理",
"xpack.idxMgmt.breadcrumb.indexDetailsLabel": "インデックス詳細",
"xpack.idxMgmt.breadcrumb.indexDetailsMappingsLabel": "マッピング",
"xpack.idxMgmt.breadcrumb.indexDetailsOverviewLabel": "概要",
"xpack.idxMgmt.breadcrumb.indexDetailsSettingsLabel": "設定",
"xpack.idxMgmt.breadcrumb.indexDetailsStatsLabel": "統計",
"xpack.idxMgmt.breadcrumb.indicesLabel": "インデックス",
"xpack.idxMgmt.breadcrumb.templatesLabel": "テンプレート",
"xpack.idxMgmt.cloneComponentTemplate.pageTitle": "コンポーネントテンプレートの複製",
Expand Down
4 changes: 0 additions & 4 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -18207,10 +18207,6 @@
"xpack.idxMgmt.breadcrumb.enrichPolicyLabel": "扩充策略",
"xpack.idxMgmt.breadcrumb.homeLabel": "索引管理",
"xpack.idxMgmt.breadcrumb.indexDetailsLabel": "索引详情",
"xpack.idxMgmt.breadcrumb.indexDetailsMappingsLabel": "映射",
"xpack.idxMgmt.breadcrumb.indexDetailsOverviewLabel": "概览",
"xpack.idxMgmt.breadcrumb.indexDetailsSettingsLabel": "设置",
"xpack.idxMgmt.breadcrumb.indexDetailsStatsLabel": "统计信息",
"xpack.idxMgmt.breadcrumb.indicesLabel": "索引",
"xpack.idxMgmt.breadcrumb.templatesLabel": "模板",
"xpack.idxMgmt.cloneComponentTemplate.pageTitle": "克隆组件模板",
Expand Down

0 comments on commit 321eaae

Please sign in to comment.