Skip to content

Commit

Permalink
[Observability] Update breadcrumbs for observability project based na…
Browse files Browse the repository at this point in the history
…vigation (elastic#196785)

~⚠️ I'm still putting out some fires with tests, but this is ready to
start being reviewed.~

## Summary

A continuation of elastic#196169 for
Observability (please read that PR description first).

Related: elastic#192050

## Overview of changes

There are essentially three types of breadcrumbs - serverless (which is
project style), stateful project style (set through spaces settings),
and classic style (the old breadcrumbs we've seen for years). Whilst
serverless and stateful project style both use the project based style
the navigation trees are slightly different, so the breadcrumbs results
are not identical [when they derive the "nav
crumbs"](https://github.com/elastic/kibana/blob/9577aa980dd1565fba05e34292fb5c0bba692889/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/breadcrumbs.tsx#L55).

Here "project style" will refer to serverless and stateful project
style.

In these changes I've, for the most part, tried to refactor things so
Observability solutions route their breadcrumbs through the
observability-shared `useBreadcrumbs` hook, this way the logic around
project style, adding an Observability crumb in classic etc is
consolidated in one place.

[For several solutions `absolute` breadcrumbs are being
used](https://github.com/elastic/kibana/blob/9577aa980dd1565fba05e34292fb5c0bba692889/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/breadcrumbs.tsx#L46),
and this means we'll roughly have the same breadcrumbs across the 3
experiences (bar Observability being prepended). Teams may want to
refine this going forward to pass curated breadcrumbs that take into
account the navigation derived "nav crumbs" (again, bearing in mind the
trees from serverless and project based chrome do differ), and not use
absolute mode. APM is an example of this at the moment. Right now this
is an 8.16 bug though, so this aims to make things acceptable, but not
necessarily perfect.

### APM

- Project style chrome crumbs have been modelled off the serverless
ones. The navigation trees here are the same so this should be fine.

### Infra

- The `infra` `useBreadcrumbs` hook has been removed, it was only being
used by logs. Logs now goes via the Observability shared hook using
`classicOnly`.

- Metrics (`useMetricsBreadcrumbs` hook) has been slightly amended to
route more of it's logic through the shared hook.

### Logs explorer

- Wasn't setting any nested breadcrumbs at the moment so the logic has
been simplified to just set some classic crumbs, and defer the rest to
the nav crumbs via the shared hook.

### Synthetics

- Removed custom logic around prepending Observability, adding link
handlers etc in favour of the shared hook.

### Alerts / rules / SLOs etc

- Simple breadcrumb needs so these are mostly setting `classicOnly` and
deferring to the nav crumbs in project style.

Several solutions have had their usage of the shared hook updated to
pass in the `serverless` plugin. This was missing before, so calls to
`serverless.setBreadcrumbs` weren't explicitly happening.

## Testing

- Add the following to your `kibana.dev.yml`:

```yml
xpack.cloud.id: "ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM="
xpack.cloud.base_url: "https://cloud.elastic.co"
```

- For testing stateful project style chrome you'll need to go to Stack
Management > Spaces and change the solution view:

![Screenshot 2024-10-21 at 12 44
21](https://github.com/user-attachments/assets/e3d9fe64-f79f-4e31-a5b6-45a06ca4915d)

- Set the above to Classic to test classic breadcrumbs.

- As a reviewer please check your solution against the 3 modes.

## Comparison

Before these changes we'd see something like the following in APM:

![Screenshot 2024-10-11 at 10 56
54](https://github.com/user-attachments/assets/4938b31e-9d4a-429e-abf0-add04d69b62a)

Now we'll see something like this in project style:

![Screenshot 2024-10-21 at 12 48
54](https://github.com/user-attachments/assets/0645a3ae-909e-4a70-a077-d9f83bd1d639)

(cherry picked from commit 641d915)
  • Loading branch information
Kerry350 committed Oct 29, 2024
1 parent bbf8547 commit e368e42
Show file tree
Hide file tree
Showing 38 changed files with 371 additions and 409 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function BreadcrumbsContextProvider({ children }: { children: React.React
};
});

useBreadcrumbs(formattedBreadcrumbs, { serverless });
useBreadcrumbs(formattedBreadcrumbs, { serverless, absoluteProjectStyleBreadcrumbs: false });

return <BreadcrumbsContext.Provider value={api}>{children}</BreadcrumbsContext.Provider>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
import { useCurrentRoute } from '@kbn/typed-react-router-config';
import { useContext, useEffect, useRef } from 'react';
import { castArray } from 'lodash';
import useObservable from 'react-use/lib/useObservable';
import { Breadcrumb, BreadcrumbsContext } from './context';
import { useKibanaEnvironmentContext } from '../kibana_environment_context/use_kibana_environment_context';
import { useKibana } from '../kibana_context/use_kibana';

export function useBreadcrumb(
callback: () => Breadcrumb | Breadcrumb[],
fnDeps: any[],
options?: { omitRootOnServerless?: boolean; omitOnServerless?: boolean }
) {
const { isServerlessEnv } = useKibanaEnvironmentContext();
const {
services: { chrome },
} = useKibana();
const { omitRootOnServerless = false, omitOnServerless = false } = options || {};

const api = useContext(BreadcrumbsContext);
Expand All @@ -29,8 +34,11 @@ export function useBreadcrumb(

const matchedRoute = useRef(match?.route);

const chromeStyle = useObservable(chrome.getChromeStyle$());

useEffect(() => {
if (isServerlessEnv && omitOnServerless) {
const isProjectStyle = isServerlessEnv || chromeStyle === 'project';
if (isProjectStyle && omitOnServerless) {
return;
}

Expand All @@ -42,10 +50,9 @@ export function useBreadcrumb(

if (matchedRoute.current) {
const breadcrumbs = castArray(callback());

api.set(
matchedRoute.current,
isServerlessEnv && omitRootOnServerless && breadcrumbs.length >= 1
isProjectStyle && omitRootOnServerless && breadcrumbs.length >= 1
? breadcrumbs.slice(1)
: breadcrumbs
);
Expand All @@ -57,5 +64,5 @@ export function useBreadcrumb(
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [match, ...fnDeps]);
}, [match, chromeStyle, ...fnDeps]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function ExploratoryViewPage({
}),
},
],
{ app }
{ app, classicOnly: true }
);

const kbnUrlStateStorage = useSessionStorage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,15 @@ export const Page = ({ tabs = [], links = [] }: ContentTemplateProps) => {

const parentBreadcrumbResolver = useParentBreadcrumbResolver();
const breadcrumbOptions = parentBreadcrumbResolver.getBreadcrumbOptions(asset.type);
useMetricsBreadcrumbs(
[
{
...breadcrumbOptions.link,
text: breadcrumbOptions.text,
},
{
text: asset.name,
},
],
{ deeperContextServerless: true }
);
useMetricsBreadcrumbs([
{
...breadcrumbOptions.link,
text: breadcrumbOptions.text,
},
{
text: asset.name,
},
]);

useEffect(() => {
if (trackOnlyOnce.current) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
*/

import { ChromeBreadcrumb } from '@kbn/core/public';
import { useBreadcrumbs } from './use_breadcrumbs';
import { useBreadcrumbs, useLinkProps } from '@kbn/observability-shared-plugin/public';
import { LOGS_APP } from '../../common/constants';
import { logsTitle } from '../translations';

export const useLogsBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => {
useBreadcrumbs(LOGS_APP, logsTitle, extraCrumbs);
const appLinkProps = useLinkProps({ app: LOGS_APP });
const breadcrumbs = [
{
...appLinkProps,
text: logsTitle,
},
...extraCrumbs,
];
useBreadcrumbs(breadcrumbs, { classicOnly: true });
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,25 @@
* 2.0.
*/

import { useEffect, useMemo } from 'react';
import { ChromeBreadcrumb } from '@kbn/core/public';
import { useBreadcrumbs, useLinkProps } from '@kbn/observability-shared-plugin/public';
import { METRICS_APP } from '../../common/constants';
import { metricsTitle } from '../translations';
import { useKibanaContextForPlugin } from './use_kibana';

export const useMetricsBreadcrumbs = (
extraCrumbs: ChromeBreadcrumb[],
options?: { deeperContextServerless: boolean }
) => {
export const useMetricsBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => {
const {
services: { serverless },
} = useKibanaContextForPlugin();
const appLinkProps = useLinkProps({ app: METRICS_APP });

const breadcrumbs = useMemo(
() => [
{
...appLinkProps,
text: metricsTitle,
},
...extraCrumbs,
],
[appLinkProps, extraCrumbs]
);
const breadcrumbs = [
{
...appLinkProps,
text: metricsTitle,
},
...extraCrumbs,
];

useBreadcrumbs(breadcrumbs);

useEffect(() => {
// For deeper context breadcrumbs in serveless, the `serverless` plugin provides its own breadcrumb service.
// https://docs.elastic.dev/kibana-dev-docs/serverless-project-navigation#breadcrumbs
if (serverless && options?.deeperContextServerless) {
// The initial path is already set in the breadcrumbs
const [, ...serverlessBreadcrumbs] = breadcrumbs;
serverless.setBreadcrumbs(serverlessBreadcrumbs);
}
}, [breadcrumbs, options?.deeperContextServerless, serverless]);
useBreadcrumbs(breadcrumbs, { serverless });
};
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,15 @@ export const MetricDetailPage = () => {
});

const breadcrumbOptions = parentBreadcrumbResolver.getBreadcrumbOptions(nodeType);
useMetricsBreadcrumbs(
[
{
...breadcrumbOptions.link,
text: breadcrumbOptions.text,
},
{
text: name,
},
],
{ deeperContextServerless: true }
);
useMetricsBreadcrumbs([
{
...breadcrumbOptions.link,
text: breadcrumbOptions.text,
},
{
text: name,
},
]);

const [sideNav, setSideNav] = useState<NavItem[]>([]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const metricsTitle = i18n.translate('xpack.infra.header.infrastructureTit
});

export const inventoryTitle = i18n.translate('xpack.infra.metrics.infrastructureInventoryTitle', {
defaultMessage: 'Infrastructure Inventory',
defaultMessage: 'Infrastructure inventory',
});

export const metricsExplorerTitle = i18n.translate('xpack.infra.metrics.metricsExplorerTitle', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export function AlertDetails() {
triggersActionsUi: { ruleTypeRegistry },
observabilityAIAssistant,
uiSettings,
serverless,
} = useKibana().services;

const { search } = useLocation();
Expand Down Expand Up @@ -158,20 +159,23 @@ export function AlertDetails() {
}
}, [alertDetail, ruleTypeRegistry]);

useBreadcrumbs([
{
href: http.basePath.prepend(paths.observability.alerts),
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
defaultMessage: 'Alerts',
}),
deepLinkId: 'observability-overview:alerts',
},
{
text: alertDetail
? getPageTitle(alertDetail.formatted.fields[ALERT_RULE_CATEGORY])
: defaultBreadcrumb,
},
]);
useBreadcrumbs(
[
{
href: http.basePath.prepend(paths.observability.alerts),
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
defaultMessage: 'Alerts',
}),
deepLinkId: 'observability-overview:alerts',
},
{
text: alertDetail
? getPageTitle(alertDetail.formatted.fields[ALERT_RULE_CATEGORY])
: defaultBreadcrumb,
},
],
{ serverless }
);

const onUntrackAlert = () => {
setAlertStatus(ALERT_STATUS_UNTRACKED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,18 @@ function InternalAlertsPage() {
[alertSearchBarStateProps.rangeFrom, alertSearchBarStateProps.rangeTo, bucketSize, esQuery]
);

useBreadcrumbs([
useBreadcrumbs(
[
{
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
defaultMessage: 'Alerts',
}),
},
],
{
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
defaultMessage: 'Alerts',
}),
},
]);
classicOnly: true,
}
);

async function loadRuleStats() {
setRuleStatsLoading(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,18 @@ export function OverviewPage() {

const { ObservabilityPageTemplate, observabilityRuleTypeRegistry } = usePluginContext();

useBreadcrumbs([
useBreadcrumbs(
[
{
text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', {
defaultMessage: 'Overview',
}),
},
],
{
text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', {
defaultMessage: 'Overview',
}),
},
]);
classicOnly: true,
}
);

const { data: newsFeed } = useFetcher(
() => getNewsFeed({ http, kibanaVersion }),
Expand Down
Loading

0 comments on commit e368e42

Please sign in to comment.