Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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