NavigationItemOpenPanel: remove handling of landing page ("four squares" design) (#210893)

## Summary

Part of Epic: https://github.com/elastic/kibana-team/issues/1439
Requires: https://github.com/elastic/kibana/issues/212903

Changes:
1. Moves the Solution Side Nav away from the "four squares" design
pattern: where clicking the item label opens a landing page and the item
icon opens the secondary nav panel. This was a custom component
implemented in the Kibana package, not part of the EUI
`EuiCollapsibleNavBeta` component.
2. Changes some usage of `@emotion/css` to `@emotion/react` for better
developer experience

### Screenshots

<details><summary>Before</summary>


![01-security-solution-before](https://github.com/user-attachments/assets/259442a2-6cd5-45f2-be27-1b4e9ef26b04)

</details>

<details><summary>After</summary>


![02-security-solution-after](https://github.com/user-attachments/assets/a8ef8476-e36d-479f-9eba-2450b1df71ac)

</details>

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [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
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] This design pattern was only used in Security Solution. There is a
small risk of regression issues in Security Solution navigation. This
was mitigated by manual testing during development.
This commit is contained in:
Tim Sullivan 2025-04-09 07:25:30 -07:00 committed by GitHub
parent a49dc03330
commit 77523f7b15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 93 additions and 251 deletions

View file

@ -56,9 +56,9 @@ describe('Panel', () => {
navTreeDef: of(navigationTree),
});
expect(await findByTestId(/panelOpener-root.group1/)).toBeVisible();
expect(await findByTestId(/nav-item-root.group1/)).toBeVisible();
expect(queryByTestId(/sideNavPanel/)).toBeNull();
(await findByTestId(/panelOpener-root.group1/)).click(); // open the panel
(await findByTestId(/nav-item-root.group1/)).click(); // open the panel
expect(queryByTestId(/sideNavPanel/)).toBeVisible();
});
@ -77,7 +77,7 @@ describe('Panel', () => {
title: 'Group 1',
path: 'root.group1',
href: '/app/item1',
renderAs: 'panelOpener',
renderAs: 'block',
children: [
// All children are hidden, this group should not render
{
@ -109,8 +109,8 @@ describe('Panel', () => {
navTreeDef: of(navigationTree),
});
expect(queryByTestId(/panelOpener-root.group1/)).toBeNull();
expect(queryByTestId(/panelOpener-root.group2/)).toBeVisible();
expect(queryByTestId(/nav-item-root.group1.item1/)).toBeNull();
expect(queryByTestId(/nav-item-root.group2/)).toBeVisible();
});
describe('toggle the panel open and closed', () => {
@ -280,7 +280,7 @@ describe('Panel', () => {
expect(queryByTestId(/sideNavPanel/)).toBeNull();
expect(queryByTestId(/customPanelContent/)).toBeNull();
queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel
queryByTestId(/nav-item-root.group1/)?.click(); // open the panel
expect(queryByTestId(/sideNavPanel/)).not.toBeNull();
expect(queryByTestId(/customPanelContent/)).not.toBeNull();
@ -351,7 +351,7 @@ describe('Panel', () => {
navTreeDef: of(navTree),
});
queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel
queryByTestId(/nav-item-root.group1/)?.click(); // open the panel
expect(queryByTestId(/panelGroupId-foo/)).toBeVisible();
expect(queryByTestId(/panelGroupTitleId-foo/)?.textContent).toBe('Foo');
@ -418,7 +418,7 @@ describe('Panel', () => {
navTreeDef: of(navTree),
});
queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel
queryByTestId(/nav-item-root.group1/)?.click(); // open the panel
expect(queryByTestId(/panelGroupTitleId-foo/)).toBeNull(); // No title rendered
@ -485,7 +485,7 @@ describe('Panel', () => {
navTreeDef: of(navTree),
});
queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel
queryByTestId(/nav-item-root.group1/)?.click(); // open the panel
expect(queryByTestId(/panelGroupId-foo/)).toBeVisible();
@ -554,7 +554,7 @@ describe('Panel', () => {
navTreeDef: of(navTree),
});
queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel
queryByTestId(/nav-item-root.group1/)?.click(); // open the panel
expect(queryByTestId(/panelGroupId-foo/)).toBeVisible(); // no crash
});
@ -611,7 +611,7 @@ describe('Panel', () => {
expect(componentSpy).not.toHaveBeenCalled();
queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel
queryByTestId(/nav-item-root.group1/)?.click(); // open the panel
expect(componentSpy).toHaveBeenCalled();
});

View file

@ -8,115 +8,34 @@
*/
import React, { useCallback, type FC } from 'react';
import { i18n } from '@kbn/i18n';
import classNames from 'classnames';
import { css } from '@emotion/css';
import {
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiListGroup,
EuiListGroupItem,
type EuiThemeComputed,
useEuiTheme,
transparentize,
useIsWithinMinBreakpoint,
EuiButton,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { transparentize, EuiButton } from '@elastic/eui';
import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser';
import { SubItemTitle } from './subitem_title';
import { useNavigation as useServices } from '../../services';
import { isActiveFromUrl } from '../../utils';
import type { NavigateToUrlFn } from '../../types';
import { usePanel } from './panel';
const getStyles = (euiTheme: EuiThemeComputed<{}>) => css`
* {
// EuiListGroupItem changes the links font-weight, we need to override it
font-weight: ${euiTheme.font.weight.regular};
}
&.sideNavItem:hover {
background-color: transparent;
}
&.sideNavItem--isActive:hover,
&.sideNavItem--isActive {
background-color: ${transparentize(euiTheme.colors.lightShade, 0.5)};
& * {
font-weight: ${euiTheme.font.weight.medium};
}
}
`;
const getButtonStyles = (
euiTheme: EuiThemeComputed<{}>,
isActive: boolean,
withBadge?: boolean
) => css`
background-color: ${isActive ? transparentize(euiTheme.colors.lightShade, 0.5) : 'transparent'};
transform: none !important; /* don't translateY 1px */
color: inherit;
font-weight: inherit;
padding-inline: ${euiTheme.size.s};
& > span {
justify-content: flex-start;
position: relative;
}
${!withBadge
? `
& .euiIcon {
position: absolute;
right: 0;
top: 0;
transform: translateY(50%);
}
`
: `
& .euiBetaBadge {
margin-left: -${euiTheme.size.m};
}
`}
`;
interface Props {
item: ChromeProjectNavigationNode;
navigateToUrl: NavigateToUrlFn;
activeNodes: ChromeProjectNavigationNode[][];
}
export const NavigationItemOpenPanel: FC<Props> = ({ item, navigateToUrl, activeNodes }: Props) => {
const { euiTheme } = useEuiTheme();
export const NavigationItemOpenPanel: FC<Props> = ({ item, activeNodes }: Props) => {
const { open: openPanel, close: closePanel, selectedNode } = usePanel();
const { isSideNavCollapsed } = useServices();
const { title, deepLink, children, withBadge } = item;
const { title, deepLink, withBadge } = item;
const { id, path } = item;
const href = deepLink?.url ?? item.href;
const isNotMobile = useIsWithinMinBreakpoint('s');
const isIconVisible = isNotMobile && !isSideNavCollapsed && !!children && children.length > 0;
const hasLandingPage = Boolean(href);
const isExpanded = selectedNode?.path === path;
const isActive = isActiveFromUrl(item.path, activeNodes) || isExpanded;
const itemClassNames = classNames(
'sideNavItem',
{ 'sideNavItem--isActive': isActive },
getStyles(euiTheme)
);
const buttonClassNames = classNames(
'sideNavItem',
getButtonStyles(euiTheme, isActive, withBadge)
);
const dataTestSubj = classNames(`nav-item`, `nav-item-${path}`, {
[`nav-item-deepLinkId-${deepLink?.id}`]: !!deepLink,
[`nav-item-id-${id}`]: id,
[`nav-item-isActive`]: isActive,
});
const buttonDataTestSubj = classNames(`panelOpener`, `panelOpener-${path}`, {
[`panelOpener-id-${id}`]: id,
[`panelOpener-deepLinkId-${deepLink?.id}`]: !!deepLink,
});
const togglePanel = useCallback(
(target: EventTarget) => {
if (selectedNode?.id === item.id) {
@ -130,74 +49,49 @@ export const NavigationItemOpenPanel: FC<Props> = ({ item, navigateToUrl, active
const onLinkClick = useCallback(
(e: React.MouseEvent) => {
if (!href) {
togglePanel(e.target);
return;
}
e.preventDefault();
navigateToUrl(href);
closePanel();
},
[closePanel, href, navigateToUrl, togglePanel]
);
const onIconClick = useCallback(
(e: React.MouseEvent) => {
togglePanel(e.target);
},
[togglePanel]
);
if (!hasLandingPage) {
return (
<EuiButton
onClick={onLinkClick}
iconSide="right"
iconType="arrowRight"
size="s"
fullWidth
className={buttonClassNames}
data-test-subj={dataTestSubj}
>
{withBadge ? <SubItemTitle item={item} /> : title}
</EuiButton>
);
}
return (
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem css={{ flexBasis: isIconVisible ? '80%' : '100%' }}>
<EuiListGroup gutterSize="none">
<EuiListGroupItem
label={withBadge ? <SubItemTitle item={item} /> : title}
href={href}
wrapText
onClick={onLinkClick}
className={itemClassNames}
color="text"
size="s"
data-test-subj={dataTestSubj}
/>
</EuiListGroup>
</EuiFlexItem>
{isIconVisible && (
<EuiFlexItem grow={0} css={{ flexBasis: '15%' }}>
<EuiButtonIcon
display={isExpanded ? 'base' : 'empty'}
size="s"
color="text"
onClick={onIconClick}
iconType="spaces"
iconSize="m"
aria-label={i18n.translate('sharedUXPackages.chrome.sideNavigation.togglePanel', {
defaultMessage: 'Toggle "{title}" panel navigation',
values: { title },
})}
aria-expanded={isExpanded}
data-test-subj={buttonDataTestSubj}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiButton
onClick={onLinkClick}
iconSide="right"
iconType="arrowRight"
size="s"
fullWidth
css={({ euiTheme }) => css`
background-color: ${isActive
? transparentize(euiTheme.colors.lightShade, 0.5)
: 'transparent'};
transform: none !important; /* don't translateY 1px */
color: inherit;
font-weight: inherit;
padding-inline: ${euiTheme.size.s};
& > span {
justify-content: flex-start;
position: relative;
}
${!withBadge
? `
& .euiIcon {
position: absolute;
right: 0;
top: 0;
transform: translateY(50%);
}
`
: `
& .euiBetaBadge {
margin-left: -${euiTheme.size.m};
}
`}
`}
data-test-subj={dataTestSubj}
>
{withBadge ? <SubItemTitle item={item} /> : title}
</EuiButton>
);
};

View file

@ -8,7 +8,7 @@
*/
import React, { type FC, useMemo, useEffect, useState, useCallback } from 'react';
import { css } from '@emotion/css';
import { css } from '@emotion/react';
import {
EuiTitle,
EuiCollapsibleNavItem,
@ -153,7 +153,7 @@ const renderBlockTitle: (
size="xxxs"
className="eui-textTruncate"
data-test-subj={dataTestSubj}
css={({ euiTheme }: any) => {
css={({ euiTheme }) => {
return {
marginTop: spaceBefore ? euiTheme.size[spaceBefore] : undefined,
paddingBlock: euiTheme.size.xs,
@ -396,7 +396,9 @@ function nodeToEuiCollapsibleNavProps(
};
}
if (renderAs === 'panelOpener') {
const hasVisibleSubItems = subItems && subItems.length > 0;
if (renderAs === 'panelOpener' && hasVisibleSubItems) {
// Render as a panel opener (button to open a panel as a second navigation)
return {
items: [...renderPanelOpener(navNode, deps)],
@ -404,7 +406,7 @@ function nodeToEuiCollapsibleNavProps(
};
}
if (renderAs === 'block' && deps.treeDepth > 0 && subItems) {
if (renderAs === 'block' && deps.treeDepth > 0 && hasVisibleSubItems) {
// Render as a group block (bold title + list of links underneath)
return {
items: [...renderGroup(navNode, subItems)],
@ -425,10 +427,10 @@ function nodeToEuiCollapsibleNavProps(
['data-test-subj']: dataTestSubj,
iconProps: { size: deps.treeDepth === 0 ? 'm' : 's' },
// Render as an accordion or a link (handled by EUI) depending if
// "items" is undefined or not. If it is undefined --> a link, otherwise an
// accordion is rendered.
...(subItems
// If navNode has subItems, render as an accordion.
// Otherwise render as a link.
// NavItemProp declarations are handled by us, rendering is handled by EUI.
...(hasVisibleSubItems
? { items: subItems, isCollapsible }
: { href, ...linkProps, external: isExternalLink }),
},
@ -446,12 +448,6 @@ function nodeToEuiCollapsibleNavProps(
return { items, isVisible };
}
const className = css`
.euiAccordion__childWrapper {
transition: none; // Remove the transition as it does not play well with dynamic links added to the accordion
}
`;
interface Props {
navNode: ChromeProjectNavigationNode;
}
@ -562,8 +558,14 @@ export const NavigationSectionUI: FC<Props> = React.memo(({ navNode: _navNode })
return null;
}
const navItemStyles = css`
.euiAccordion__childWrapper {
transition: none; // Remove the transition as it does not play well with dynamic links added to the accordion
}
`;
if (!items) {
return <EuiCollapsibleNavItem {...props} className={className} />;
return <EuiCollapsibleNavItem {...props} css={navItemStyles} />;
}
// Item type ExclusiveUnion - accordions should not contain links
@ -572,7 +574,7 @@ export const NavigationSectionUI: FC<Props> = React.memo(({ navNode: _navNode })
return (
<EuiCollapsibleNavItem
{...rest}
className={className}
css={navItemStyles}
items={items}
accordionProps={getAccordionProps(navNode.path)}
/>

View file

@ -14,7 +14,8 @@
},
"include": [
"**/*.ts",
"**/*.tsx"
"**/*.tsx",
"../../../../../../../typings/emotion.d.ts"
],
"kbn_references": [
"@kbn/core-chrome-browser",
@ -22,7 +23,7 @@
"@kbn/i18n",
"@kbn/shared-ux-storybook-mock",
"@kbn/core-http-browser",
"@kbn/core-analytics-browser",
"@kbn/core-analytics-browser"
],
"exclude": [
"target/**/*"

View file

@ -211,35 +211,23 @@ export function SolutionNavigationProvider(ctx: Pick<FtrProviderContext, 'getSer
return false;
}
},
async openPanel(
sectionId: NavigationId,
{ button }: { button: 'icon' | 'link' } = { button: 'icon' }
) {
async openPanel(sectionId: NavigationId) {
log.debug('SolutionNavigation.sidenav.openPanel', sectionId);
const isOpen = await this.isPanelOpen(sectionId);
if (isOpen) return;
const panelOpenerBtn = await testSubjects.find(
button === 'icon' ? `~panelOpener-id-${sectionId}` : `~nav-item-id-${sectionId}`,
TIMEOUT_CHECK
);
const panelOpenerBtn = await testSubjects.find(`~nav-item-id-${sectionId}`, TIMEOUT_CHECK);
await panelOpenerBtn.click();
},
async closePanel(
sectionId: NavigationId,
{ button }: { button: 'icon' | 'link' } = { button: 'icon' }
) {
async closePanel(sectionId: NavigationId) {
log.debug('SolutionNavigation.sidenav.closePanel', sectionId);
const isOpen = await this.isPanelOpen(sectionId);
if (!isOpen) return;
const panelOpenerBtn = await testSubjects.find(
button === 'icon' ? `~panelOpener-id-${sectionId}` : `~nav-item-id-${sectionId}`,
TIMEOUT_CHECK
);
const panelOpenerBtn = await testSubjects.find(`~nav-item-id-${sectionId}`, TIMEOUT_CHECK);
await panelOpenerBtn.click();
},

View file

@ -7992,7 +7992,6 @@
"sharedUXPackages.chrome.sideNavigation.feedbackCallout.btn": "Faites-nous en part",
"sharedUXPackages.chrome.sideNavigation.feedbackCallout.title": "Comment fonctionne la navigation de votre côté ? Il manque quelque chose ?",
"sharedUXPackages.chrome.sideNavigation.recentlyAccessed.title": "Récent",
"sharedUXPackages.chrome.sideNavigation.togglePanel": "Afficher/Masquer le panneau de navigation \"{title}\"",
"sharedUXPackages.codeEditor.ariaLabel": "Éditeur de code",
"sharedUXPackages.codeEditor.codeEditorEditButton": "{codeEditorAriaLabel}, activer le mode dédition",
"sharedUXPackages.codeEditor.enterKeyLabel": "Entrée",

View file

@ -7985,7 +7985,6 @@
"sharedUXPackages.chrome.sideNavigation.feedbackCallout.btn": "お知らせください",
"sharedUXPackages.chrome.sideNavigation.feedbackCallout.title": "ナビゲーションはどのように機能していますか?ここまでで何か、忘れていることはありませんか?",
"sharedUXPackages.chrome.sideNavigation.recentlyAccessed.title": "最近",
"sharedUXPackages.chrome.sideNavigation.togglePanel": "\"{title}\"パネルナビゲーションを切り替える",
"sharedUXPackages.codeEditor.ariaLabel": "コードエディター",
"sharedUXPackages.codeEditor.codeEditorEditButton": "{codeEditorAriaLabel}、編集モードを有効化",
"sharedUXPackages.codeEditor.enterKeyLabel": "Enter",

View file

@ -7998,7 +7998,6 @@
"sharedUXPackages.chrome.sideNavigation.feedbackCallout.btn": "请告诉我们",
"sharedUXPackages.chrome.sideNavigation.feedbackCallout.title": "导航如何为您提供帮助?缺少什么内容?",
"sharedUXPackages.chrome.sideNavigation.recentlyAccessed.title": "最近",
"sharedUXPackages.chrome.sideNavigation.togglePanel": "切换“{title}”面板导航",
"sharedUXPackages.codeEditor.ariaLabel": "代码编辑器",
"sharedUXPackages.codeEditor.codeEditorEditButton": "{codeEditorAriaLabel},激活编辑模式",
"sharedUXPackages.codeEditor.enterKeyLabel": "Enter",

View file

@ -75,20 +75,6 @@ exports[`arrows ArrowBody renders correctly against snapshot 1`] = `
.css-8kffb{margin-right:8px;overflow:hidden;text-overflow:ellipsis;}
</style>
<style
data-emotion="css"
data-s=""
>
.css-1kcx8qm{}
</style>
<style
data-emotion="css"
data-s=""
>
.css-1kcx8qm .euiAccordion__childWrapper{-webkit-transition:none;transition:none;}
</style>
<style
data-emotion="css-global"
data-s=""

View file

@ -49,21 +49,21 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
deepLinkId: 'observabilityOnboarding',
});
// open apm (Application) panel using the link button (not the button icon)
await solutionNavigation.sidenav.openPanel('apm', { button: 'link' });
// open apm (Application) panel
await solutionNavigation.sidenav.openPanel('apm');
{
const isOpen = await solutionNavigation.sidenav.isPanelOpen('apm');
expect(isOpen).to.be(true);
}
await solutionNavigation.sidenav.closePanel('apm', { button: 'link' });
await solutionNavigation.sidenav.closePanel('apm');
{
const isOpen = await solutionNavigation.sidenav.isPanelOpen('apm');
expect(isOpen).to.be(false);
}
// open Infrastructure panel and navigate to some link inside the panel
await solutionNavigation.sidenav.openPanel('metrics', { button: 'link' });
await solutionNavigation.sidenav.openPanel('metrics');
{
const isOpen = await solutionNavigation.sidenav.isPanelOpen('metrics');
expect(isOpen).to.be(true);

View file

@ -43,7 +43,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'AI Assistant' });
// check Other Tools section
await solutionNavigation.sidenav.openPanel('otherTools', { button: 'link' });
await solutionNavigation.sidenav.openPanel('otherTools');
{
const isOpen = await solutionNavigation.sidenav.isPanelOpen('otherTools');
expect(isOpen).to.be(true);
@ -62,7 +62,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
// check Machine Learning section
await solutionNavigation.sidenav.openPanel('machine_learning-landing', { button: 'link' });
await solutionNavigation.sidenav.openPanel('machine_learning-landing');
{
const isOpen = await solutionNavigation.sidenav.isPanelOpen('machine_learning-landing');
expect(isOpen).to.be(true);

View file

@ -60,12 +60,8 @@ import {
ENTITY_ANALYTICS_URL,
INDICATORS_URL,
DISCOVER_URL,
RULES_LANDING_URL,
RULES_COVERAGE_URL,
INVESTIGATIONS_URL,
OSQUERY_URL,
MACHINE_LEARNING_LANDING_URL,
ASSETS_URL,
HOSTS_URL,
} from '../../../urls/navigation';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
@ -265,10 +261,6 @@ describe('Serverless side navigation links', { tags: '@serverless' }, () => {
cy.url().should('include', DASHBOARDS_URL);
});
it('navigates to the Rules landing page', () => {
navigateFromHeaderTo(ServerlessHeaders.RULES_LANDING, true);
cy.url().should('include', RULES_LANDING_URL);
});
it('navigates to the Rules page', () => {
navigateFromHeaderTo(ServerlessHeaders.RULES, true);
cy.url().should('include', RULES_MANAGEMENT_URL);
@ -304,11 +296,6 @@ describe('Serverless side navigation links', { tags: '@serverless' }, () => {
cy.url().should('include', CASES_URL);
});
it('navigates to the Investigations page', () => {
navigateFromHeaderTo(ServerlessHeaders.INVESTIGATIONS, true);
cy.url().should('include', INVESTIGATIONS_URL);
});
it('navigates to the Timelines page', () => {
navigateFromHeaderTo(ServerlessHeaders.TIMELINES, true);
cy.url().should('include', TIMELINES_URL);
@ -323,11 +310,6 @@ describe('Serverless side navigation links', { tags: '@serverless' }, () => {
cy.url().should('include', INDICATORS_URL);
});
it('navigates to the Explore landing page', () => {
navigateFromHeaderTo(ServerlessHeaders.EXPLORE, true);
cy.url().should('include', EXPLORE_URL);
});
it('navigates to the Hosts page', () => {
navigateFromHeaderTo(ServerlessHeaders.HOSTS, true);
cy.url().should('include', HOSTS_URL);
@ -343,16 +325,8 @@ describe('Serverless side navigation links', { tags: '@serverless' }, () => {
cy.url().should('include', USERS_URL);
});
it('navigates to the Assets page', () => {
navigateFromHeaderTo(ServerlessHeaders.ASSETS, true);
cy.url().should('include', ASSETS_URL);
});
it('navigates to the Endpoints page', () => {
navigateFromHeaderTo(ServerlessHeaders.ENDPOINTS, true);
cy.url().should('include', ENDPOINTS_URL);
});
it('navigates to the Machine learning landing page', () => {
navigateFromHeaderTo(ServerlessHeaders.MACHINE_LEARNING, true);
cy.url().should('include', MACHINE_LEARNING_LANDING_URL);
});
});

View file

@ -8,25 +8,22 @@
// main panels links
export const DASHBOARDS = '[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:dashboards"]';
export const DASHBOARDS_PANEL_BTN =
'[data-test-subj*="panelOpener-deepLinkId-securitySolutionUI:dashboards"]';
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:dashboards"]';
export const INVESTIGATIONS =
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:investigations"]';
export const INVESTIGATIONS_PANEL_BTN =
'[data-test-subj*="panelOpener-deepLinkId-securitySolutionUI:investigations"]';
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:investigations"]';
export const EXPLORE = '[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:explore"]';
export const EXPLORE_PANEL_BTN =
'[data-test-subj*="panelOpener-deepLinkId-securitySolutionUI:explore"]';
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:explore"]';
export const RULES_LANDING =
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:rules-landing"]';
export const RULES_PANEL_BTN =
'[data-test-subj*="panelOpener-deepLinkId-securitySolutionUI:rules-landing"]';
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:rules-landing"]';
export const ASSETS = '[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:assets"]';
export const ASSETS_PANEL_BTN =
'[data-test-subj*="panelOpener-deepLinkId-securitySolutionUI:assets"]';
export const ASSETS_PANEL_BTN = '[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:assets"]';
export const MACHINE_LEARNING =
'[data-test-subj*="nav-item-deepLinkId-securitySolutionUI:machine_learning-landing"]';

View file

@ -24,7 +24,10 @@ export const navigateToDiscoverPageInServerless = () => {
};
export const navigateToExplorePageInServerless = () => {
navigateTo(EXPLORE);
// NOTE: the "Explore" page is not directly accessible from the side nav at this time
navigateTo(EXPLORE); // open secondary navigation panel
navigateTo('[data-test-subj~="panelNavItem-id-hosts"]'); // navigate to "Hosts"
navigateToExploreUsingBreadcrumb();
};
export const navigateToHostsUsingBreadcrumb = () => {

View file

@ -12,7 +12,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'header']);
const openInfraSection = async () => {
await pageObjects.svlCommonNavigation.sidenav.openPanel('metrics', { button: 'link' });
await pageObjects.svlCommonNavigation.sidenav.openPanel('metrics');
};
describe('Infra Side Navigation', () => {