From 4ce08fac9d6a68116e7e58a87e9a78dec3b4579e Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:29:17 +1100 Subject: [PATCH] [8.x] [Stateful sidenav] Fix highlight colours (#201823) (#202237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Backport This will backport the following commits from `main` to `8.x`: - [[Stateful sidenav] Fix highlight colours (#201823)](https://github.com/elastic/kibana/pull/201823) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Sébastien Loix --- .../src/jest/setup/react_testing_library.js | 4 +- .../chrome/navigation/__jest__/panel.test.tsx | 1 + .../components/navigation_item_open_panel.tsx | 42 +++++++++++++++++-- .../ui/components/panel/panel_nav_item.tsx | 14 ++++++- .../src/ui/hooks/use_accordion_state.ts | 7 ++-- .../src/solution_side_nav_panel.styles.ts | 22 +++++----- .../side_nav/src/solution_side_nav_panel.tsx | 4 +- .../tests/observability_sidenav.ts | 4 +- .../apps/observability/sidenav/sidenav.ts | 2 +- .../services/ml/observability_navigation.ts | 2 +- 10 files changed, 76 insertions(+), 26 deletions(-) diff --git a/packages/kbn-test/src/jest/setup/react_testing_library.js b/packages/kbn-test/src/jest/setup/react_testing_library.js index 1444aa41949ef..758a546a511bb 100644 --- a/packages/kbn-test/src/jest/setup/react_testing_library.js +++ b/packages/kbn-test/src/jest/setup/react_testing_library.js @@ -43,7 +43,9 @@ jest.mock('@testing-library/react', () => { const originalConsoleError = console.error; console.error = (...args) => { if (global.IS_REACT_ACT_ENVIRONMENT === false) { - if (args[0].includes('Warning: An update to %s inside a test was not wrapped in act')) { + if ( + args[0].toString().includes('Warning: An update to %s inside a test was not wrapped in act') + ) { return; } } diff --git a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx index 59c3463fb7326..19254d1cfcfc5 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx @@ -90,6 +90,7 @@ describe('Panel', () => { id: 'group2', title: 'Group 2', path: 'root.group2', + href: '/app/group2', renderAs: 'panelOpener', children: [ // sideNavStatus is "visible" by default diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx index 277da24ffeb50..dfe1d26873560 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx @@ -21,6 +21,7 @@ import { useEuiTheme, transparentize, useIsWithinMinBreakpoint, + EuiButton, } from '@elastic/eui'; import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; import { useNavigation as useServices } from '../../services'; @@ -45,6 +46,24 @@ const getStyles = (euiTheme: EuiThemeComputed<{}>) => css` } `; +const getButtonStyles = (euiTheme: EuiThemeComputed<{}>, isActive: 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; + } + & .euiIcon { + position: absolute; + right: 0; + top: 0; + transform: translateY(50%); + } +`; + interface Props { item: ChromeProjectNavigationNode; navigateToUrl: NavigateToUrlFn; @@ -60,8 +79,9 @@ export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl, active const href = deepLink?.url ?? item.href; const isNotMobile = useIsWithinMinBreakpoint('s'); const isIconVisible = isNotMobile && !isSideNavCollapsed && !!children && children.length > 0; - const isActive = isActiveFromUrl(item.path, activeNodes); const hasLandingPage = Boolean(href); + const isExpanded = selectedNode?.path === path; + const isActive = hasLandingPage ? isActiveFromUrl(item.path, activeNodes) : isExpanded; const itemClassNames = classNames( 'sideNavItem', @@ -69,6 +89,8 @@ export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl, active getStyles(euiTheme) ); + const buttonClassNames = classNames('sideNavItem', getButtonStyles(euiTheme, isActive)); + const dataTestSubj = classNames(`nav-item`, `nav-item-${path}`, { [`nav-item-deepLinkId-${deepLink?.id}`]: !!deepLink, [`nav-item-id-${id}`]: id, @@ -105,7 +127,21 @@ export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl, active togglePanel(); }, [togglePanel]); - const isExpanded = selectedNode?.path === path; + if (!hasLandingPage) { + return ( + + {title} + + ); + } return ( @@ -130,7 +166,7 @@ export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl, active size="s" color="text" onClick={onIconClick} - iconType={hasLandingPage ? 'spaces' : 'arrowRight'} + iconType="spaces" iconSize="m" aria-label={i18n.translate('sharedUXPackages.chrome.sideNavigation.togglePanel', { defaultMessage: 'Toggle "{title}" panel navigation', diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx index 4cf3bb8e5e9eb..606d5efd5bb5b 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx @@ -9,7 +9,9 @@ import React, { FC, useCallback } from 'react'; import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; -import { EuiListGroupItem } from '@elastic/eui'; +import { EuiListGroupItem, transparentize, useEuiTheme } from '@elastic/eui'; +import classNames from 'classnames'; +import { css } from '@emotion/css'; import { useNavigation as useServices } from '../../../services'; import { NavItemLabel } from './panel_nav_item_label'; @@ -24,6 +26,7 @@ export const PanelNavItem: FC = ({ item }) => { const { close: closePanel } = usePanel(); const { id, icon, deepLink, openInNewTab } = item; const href = deepLink?.url ?? item.href; + const { euiTheme } = useEuiTheme(); const onClick = useCallback( (e) => { @@ -41,7 +44,14 @@ export const PanelNavItem: FC = ({ item }) => { key={id} label={} wrapText - className="sideNavPanelLink" + className={classNames( + 'sideNavPanelLink', + css` + &.sideNavPanelLink:hover { + background-color: ${transparentize(euiTheme.colors.lightShade, 0.5)}; + } + ` + )} size="s" data-test-subj={`panelNavItem panelNavItem-id-${item.id}`} href={href} diff --git a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts index 8ebf91a949db2..070243470b5e5 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts @@ -92,12 +92,12 @@ export const useAccordionState = ({ navNode }: { navNode: ChromeProjectNavigatio const getAccordionProps = useCallback( ( path: string, - _accordionProps?: Partial + accordionProps?: Partial ): Partial | undefined => { const isCollapsed = accordionStateById[path]?.isCollapsed; const isCollapsible = accordionStateById[path]?.isCollapsible; - if (isCollapsed === undefined) return _accordionProps; // No state set yet + if (isCollapsed === undefined) return accordionProps; // No state set yet let forceState: EuiAccordionProps['forceState'] = isCollapsed ? 'closed' : 'open'; if (!isCollapsible) forceState = 'open'; // Allways open if the accordion is not collapsible @@ -109,9 +109,8 @@ export const useAccordionState = ({ navNode }: { navNode: ChromeProjectNavigatio const updated: Partial = { buttonProps: { 'data-test-subj': 'accordionToggleBtn' }, - ..._accordionProps, + ...accordionProps, arrowProps, - isCollapsible, forceState, onToggle: isCollapsible ? () => { diff --git a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts index f5af4cd05ad24..06d6709ddcb42 100644 --- a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts +++ b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts @@ -39,19 +39,19 @@ export const SolutionSideNavPanelStyles = ( // bottom inset to match timeline bar top shadow inset 0 -6px ${euiTheme.size.xs} -${euiTheme.size.xs} rgb(0 0 0 / 15%); `} +`; - .solutionSideNavPanelLink { - &:focus-within { - background-color: transparent; - a { - text-decoration: auto; - } +export const SolutionSideNavPanelItemStyles = (euiTheme: EuiThemeComputed<{}>) => css` + &:focus-within { + background-color: transparent; + a { + text-decoration: auto; } - &:hover { - background-color: ${transparentize(euiTheme.colors.primary, 0.1)}; - a { - text-decoration: underline; - } + } + &:hover { + background-color: ${transparentize(euiTheme.colors.lightShade, 0.5)}; + a { + text-decoration: underline; } } `; diff --git a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx index 248121872018b..38cce27db1c44 100644 --- a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx +++ b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx @@ -48,6 +48,7 @@ import { SolutionSideNavPanelLinksGroupStyles, panelClassName, accordionButtonClassName, + SolutionSideNavPanelItemStyles, } from './solution_side_nav_panel.styles'; export interface SolutionSideNavPanelContentProps { @@ -354,7 +355,8 @@ interface SolutionSideNavPanelItemProps { const SolutionSideNavPanelItem: React.FC = React.memo( function SolutionSideNavPanelItem({ item, onClose }) { const { tracker } = useTelemetryContext(); - const panelLinkClassNames = classNames('solutionSideNavPanelLink'); + const { euiTheme } = useEuiTheme(); + const panelLinkClassNames = classNames(SolutionSideNavPanelItemStyles(euiTheme)); const { id, href, onClick, iconType, openInNewTab } = item; const onClickHandler = useCallback( (ev) => { diff --git a/x-pack/test/functional_solution_sidenav/tests/observability_sidenav.ts b/x-pack/test/functional_solution_sidenav/tests/observability_sidenav.ts index b28469a935fe4..8a93ffde38346 100644 --- a/x-pack/test/functional_solution_sidenav/tests/observability_sidenav.ts +++ b/x-pack/test/functional_solution_sidenav/tests/observability_sidenav.ts @@ -62,8 +62,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(isOpen).to.be(false); } - // open Infrastructure panel using the icon button and navigate to some link inside the panel - await solutionNavigation.sidenav.openPanel('metrics', { button: 'icon' }); + // open Infrastructure panel and navigate to some link inside the panel + await solutionNavigation.sidenav.openPanel('metrics', { button: 'link' }); { const isOpen = await solutionNavigation.sidenav.isPanelOpen('metrics'); expect(isOpen).to.be(true); diff --git a/x-pack/test/observability_functional/apps/observability/sidenav/sidenav.ts b/x-pack/test/observability_functional/apps/observability/sidenav/sidenav.ts index 201729b0bcc06..461b86deefc90 100644 --- a/x-pack/test/observability_functional/apps/observability/sidenav/sidenav.ts +++ b/x-pack/test/observability_functional/apps/observability/sidenav/sidenav.ts @@ -56,7 +56,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); // check Machine Learning section - await solutionNavigation.sidenav.openPanel('machine_learning-landing'); + await solutionNavigation.sidenav.openPanel('machine_learning-landing', { button: 'link' }); { const isOpen = await solutionNavigation.sidenav.isPanelOpen('machine_learning-landing'); expect(isOpen).to.be(true); diff --git a/x-pack/test_serverless/functional/services/ml/observability_navigation.ts b/x-pack/test_serverless/functional/services/ml/observability_navigation.ts index 83f6cd4ea94d2..5181d4981321a 100644 --- a/x-pack/test_serverless/functional/services/ml/observability_navigation.ts +++ b/x-pack/test_serverless/functional/services/ml/observability_navigation.ts @@ -15,7 +15,7 @@ export function MachineLearningNavigationProviderObservability({ const svlCommonNavigation = getPageObject('svlCommonNavigation'); async function navigateToArea(id: string) { - await svlCommonNavigation.sidenav.openPanel('machine_learning-landing'); + await svlCommonNavigation.sidenav.openPanel('machine_learning-landing', { button: 'link' }); await testSubjects.existOrFail(`~panelNavItem-id-ml:${id}`, { timeout: 60 * 1000, });