Skip to content

Commit

Permalink
Implement new nav hierarchy for stateful/cloud (elastic#192805)
Browse files Browse the repository at this point in the history
<strike>Depends on elastic#193526,
WON'T FIX</strike>

Fixes elastic#192803
Fixes elastic#191509

This PR updates the nav hierarchy for stateful/cloud and changes the
`accordion` to `panelOpener`. The menu items have been updated according
to the Figma file and the discussions happening in this
[issue](elastic#192050). I took a video
below of how the new navigation looks like with the new changes



https://github.com/user-attachments/assets/e73c8dd9-b674-4146-bcde-443490569502

## What was changed
- AI & ML menu is removed and split into
  - `AI Assistant`
- `Machine learning`: this menu was already present in the footer, it
was moved in the body (after Infrastucture)
- `Applications` now opens a `panelOpener` instead of an `accordion`
  - Synthetics was moved to a new section
  - “User Experience” was renamed to “User experience”  
- `Infrastucture` now opens a `panelOpener` instead of an `accordion`
  - Universal Profiling was moved to a separate section below
  - Metrics Explorer was renamed to Metrics explorer
- `Machine learning` now opens a `panelOpener` instead of an `accordion`
- `Supplied Configurations` was added under the Machine learning >
Anomaly Detection section.
- The machine learning `link:
'securitySolutionUI:machine_learning-landing'` was completely removed.
Clicking on the `Machine learning` link will slide out the panel instead
(same behavior with clicking on the icon). More details about why I
removed this
[here](elastic#192805 (comment))
  - Use sentence/case for the menu items
- `Other tools` now opens a `panelOpener` instead of an `accordion`
  - Logs anomalies was added here
  - Logs categories was added here
  - Visualize library was added
- Clicking on the 1st level menu items (Applications, Infrastructure,
Machine learning, Other tools) toggles the panel on the right
(arrowRight).
- The icon was changed from the four small squares to the right-facing
caret
- The sub-nav menu slides out to the right whenever the label OR the
icon are clicked
- `Getting started` in the footer was renamed to `Add data`
- `Data Set Quality` was added under `Stack Management > Data` section
below `Index Management`


## Functional Tests
Some tests have been added that test:
- toggling of the panel when clicking on the link button
- `AI assistant` changes
- `Infrastructure` changes
- `Other Tools` changes
- `Machine Learning` changes


## What this PR doesn't do
I faced some issues with the footer, so footer will be separately
handled in this [issue](elastic#192865)

---------

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Sébastien Loix <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Dominique Clarke <[email protected]>
  • Loading branch information
5 people authored and tiansivive committed Oct 7, 2024
1 parent 1ee5873 commit ef12deb
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 269 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,6 @@ function validateNodeProps<
`[Chrome navigation] Error in node [${id}]. Only one of "href" or "cloudLink" can be provided.`
);
}
if (renderAs === 'panelOpener' && !link) {
throw new Error(
`[Chrome navigation] Error in node [${id}]. If renderAs is set to "panelOpener", a "link" must also be provided.`
);
}
if (renderAs === 'item' && !link && !onClick) {
throw new Error(
`[Chrome navigation] Error in node [${id}]. If renderAs is set to "item", a "link" or "onClick" must also be provided.`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const NavigationItemOpenPanel: FC<Props> = ({ item, navigateToUrl, active
const isNotMobile = useIsWithinMinBreakpoint('s');
const isIconVisible = isNotMobile && !isSideNavCollapsed && !!children && children.length > 0;
const isActive = isActiveFromUrl(item.path, activeNodes);
const hasLandingPage = Boolean(href);

const itemClassNames = classNames(
'sideNavItem',
Expand All @@ -73,30 +74,36 @@ export const NavigationItemOpenPanel: FC<Props> = ({ item, navigateToUrl, active
[`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(() => {
if (selectedNode?.id === item.id) {
closePanel();
} else {
openPanel(item);
}
}, [selectedNode?.id, item, closePanel, openPanel]);

const onLinkClick = useCallback(
(e: React.MouseEvent) => {
if (!href) {
togglePanel();
return;
}
e.preventDefault();
navigateToUrl(href);
closePanel();
},
[closePanel, href, navigateToUrl]
[closePanel, href, navigateToUrl, togglePanel]
);

const onIconClick = useCallback(() => {
if (selectedNode?.id === item.id) {
closePanel();
} else {
openPanel(item);
}
}, [openPanel, closePanel, item, selectedNode]);
togglePanel();
}, [togglePanel]);

const isExpanded = selectedNode?.path === path;

Expand All @@ -123,7 +130,7 @@ export const NavigationItemOpenPanel: FC<Props> = ({ item, navigateToUrl, active
size="s"
color="text"
onClick={onIconClick}
iconType="spaces"
iconType={hasLandingPage ? 'spaces' : 'arrowRight'}
iconSize="m"
aria-label={i18n.translate('sharedUXPackages.chrome.sideNavigation.togglePanel', {
defaultMessage: 'Toggle "{title}" panel navigation',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ const getTestSubj = (selectedNode: PanelSelectedNode | null): string | undefined
});
};

const getTargetTestSubj = (target: EventTarget | null): string | undefined => {
if (!target) return;

return (target as HTMLElement).dataset.testSubj;
};

export const NavigationPanel: FC = () => {
const { euiTheme } = useEuiTheme();
const { isOpen, close, getContent, selectedNode } = usePanel();
Expand All @@ -48,12 +54,22 @@ export const NavigationPanel: FC = () => {

const onOutsideClick = useCallback(
({ target }: Event) => {
// Only close if we are not clicking on the currently selected nav node
if (
!(target as HTMLButtonElement).dataset.testSubj?.includes(
`panelOpener-${selectedNode?.path}`
)
) {
let doClose = true;

if (target) {
// Only close if we are not clicking on the currently selected nav node
const testSubj =
getTargetTestSubj(target) ?? getTargetTestSubj((target as HTMLElement).parentNode);

if (
testSubj?.includes(`nav-item-${selectedNode?.path}`) ||
testSubj?.includes(`panelOpener-${selectedNode?.path}`)
) {
doClose = false;
}
}

if (doClose) {
close();
}
},
Expand Down
36 changes: 33 additions & 3 deletions test/functional/page_objects/solution_navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,22 @@ export function SolutionNavigationProvider(ctx: Pick<FtrProviderContext, 'getSer
// side nav related actions
sidenav: {
async expectLinkExists(
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
by:
| { deepLinkId: AppDeepLinkId }
| { navId: string }
| { text: string }
| { panelNavLinkId: string }
) {
if ('deepLinkId' in by) {
await testSubjects.existOrFail(`~nav-item-deepLinkId-${by.deepLinkId}`, {
timeout: TIMEOUT_CHECK,
});
} else if ('navId' in by) {
await testSubjects.existOrFail(`~nav-item-id-${by.navId}`, { timeout: TIMEOUT_CHECK });
} else if ('panelNavLinkId' in by) {
await testSubjects.existOrFail(`~panelNavItem-id-${by.panelNavLinkId}`, {
timeout: TIMEOUT_CHECK,
});
} else {
expect(await getByVisibleText('~nav-item', by.text)).not.be(null);
}
Expand Down Expand Up @@ -130,6 +138,9 @@ export function SolutionNavigationProvider(ctx: Pick<FtrProviderContext, 'getSer
});
}
},
async clickPanelLink(deepLinkId: string) {
await testSubjects.click(`~panelNavItem-id-${deepLinkId}`);
},
async expectSectionExists(sectionId: NavigationId) {
log.debug('SolutionNavigation.sidenav.expectSectionExists', sectionId);
await testSubjects.existOrFail(getSectionIdTestSubj(sectionId), { timeout: TIMEOUT_CHECK });
Expand Down Expand Up @@ -186,14 +197,33 @@ export function SolutionNavigationProvider(ctx: Pick<FtrProviderContext, 'getSer
return false;
}
},
async openPanel(sectionId: NavigationId) {
async openPanel(
sectionId: NavigationId,
{ button }: { button: 'icon' | 'link' } = { button: 'icon' }
) {
log.debug('SolutionNavigation.sidenav.openPanel', sectionId);

const isOpen = await this.isPanelOpen(sectionId);
if (isOpen) return;

const panelOpenerBtn = await testSubjects.find(
`~panelOpener-id-${sectionId}`,
button === 'icon' ? `~panelOpener-id-${sectionId}` : `~nav-item-id-${sectionId}`,
TIMEOUT_CHECK
);

await panelOpenerBtn.click();
},
async closePanel(
sectionId: NavigationId,
{ button }: { button: 'icon' | 'link' } = { button: 'icon' }
) {
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
);

Expand Down
Loading

0 comments on commit ef12deb

Please sign in to comment.