grafana/public/app/core/components/NavBar/NavBarDropdown.tsx
Ashley Harrison 90d2d1f4da
Navigation: Refactor mobile menu into it's own component (#41308)
* Navigation: Start creating new NavBarMenu component

* Navigation: Apply new NavBarMenu to NavBarNext

* Navigation: Remove everything to do with .sidemenu-open--xs

* Navigation: Ensure search is passed to NavBarMenu

* Navigation: Standardise NavBarMenuItem

* This extra check isn't needed anymore

* Navigation: Refactor <li> out of NavBarMenu

* Navigation: Combine NavBarMenuItem with DropdownChild

* use spread syntax since performance shouldn't be a concern for such small arrays

* Improve active item logic

* Ensure unique keys

* Remove this duplicate code

* Add unit tests for getActiveItem

* Add tests for NavBarMenu

* Rename mobileMenuOpen -> menuOpen in NavBarNext (since it can be used for mobile menu or megamenu)

* just use index to key the items

* Use exact versions of @react-aria packages

* Navigation: Make the dropdown header a NavBarMenuItem

* Navigation: Stop using dropdown-menu for styles

* Navigation: Hide divider in NavBarMenu + tweak color on section header
2021-11-09 13:41:38 +00:00

104 lines
3.1 KiB
TypeScript

import React from 'react';
import { css } from '@emotion/css';
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
import { IconName, useTheme2 } from '@grafana/ui';
import { NavBarMenuItem } from './NavBarMenuItem';
interface Props {
headerTarget?: HTMLAnchorElement['target'];
headerText: string;
headerUrl?: string;
isVisible?: boolean;
items?: NavModelItem[];
onHeaderClick?: () => void;
reverseDirection?: boolean;
subtitleText?: string;
}
const NavBarDropdown = ({
headerTarget,
headerText,
headerUrl,
isVisible,
items = [],
onHeaderClick,
reverseDirection = false,
subtitleText,
}: Props) => {
const filteredItems = items.filter((item) => !item.hideFromMenu);
const theme = useTheme2();
const styles = getStyles(theme, reverseDirection, filteredItems, isVisible);
return (
<ul className={`${styles.menu} navbar-dropdown`} role="menu">
<NavBarMenuItem
onClick={onHeaderClick}
styleOverrides={styles.header}
target={headerTarget}
text={headerText}
url={headerUrl}
/>
{filteredItems.map((child, index) => (
<NavBarMenuItem
key={index}
isDivider={child.divider}
icon={child.icon as IconName}
onClick={child.onClick}
target={child.target}
text={child.text}
url={child.url}
/>
))}
{subtitleText && <li className={styles.subtitle}>{subtitleText}</li>}
</ul>
);
};
export default NavBarDropdown;
const getStyles = (
theme: GrafanaTheme2,
reverseDirection: Props['reverseDirection'],
filteredItems: Props['items'],
isVisible: Props['isVisible']
) => {
const adjustHeightForBorder = filteredItems!.length === 0;
return {
header: css`
background-color: ${theme.colors.background.secondary};
height: ${theme.components.sidemenu.width - (adjustHeightForBorder ? 2 : 1)}px;
font-size: ${theme.typography.h4.fontSize};
font-weight: ${theme.typography.h4.fontWeight};
padding: ${theme.spacing(1)} ${theme.spacing(2)};
white-space: nowrap;
width: 100%;
`,
menu: css`
background-color: ${theme.colors.background.primary};
border: 1px solid ${theme.components.panel.borderColor};
bottom: ${reverseDirection ? 0 : 'auto'};
box-shadow: ${theme.shadows.z3};
display: flex;
flex-direction: ${reverseDirection ? 'column-reverse' : 'column'};
left: 100%;
list-style: none;
min-width: 140px;
opacity: ${isVisible ? 1 : 0};
position: absolute;
top: ${reverseDirection ? 'auto' : 0};
transition: ${theme.transitions.create('opacity')};
visibility: ${isVisible ? 'visible' : 'hidden'};
z-index: ${theme.zIndex.sidemenu};
`,
subtitle: css`
border-${reverseDirection ? 'bottom' : 'top'}: 1px solid ${theme.colors.border.weak};
color: ${theme.colors.text.secondary};
font-size: ${theme.typography.bodySmall.fontSize};
font-weight: ${theme.typography.bodySmall.fontWeight};
padding: ${theme.spacing(1)} ${theme.spacing(2)} ${theme.spacing(1)};
white-space: nowrap;
`,
};
};