NavBar: Styling tweaks to tidy up appearance (#39871)

* NavBar: Styling tweaks to tidy up appearance

* NavBar: Add external link icon to external links

* NavBar: Dim the external link icon

* bump drone

* NavBar: Rename variable to better describe what it's doing
This commit is contained in:
Ashley Harrison
2021-10-01 15:53:56 +01:00
committed by GitHub
parent 4dadb8fc51
commit 4350f4931d
8 changed files with 104 additions and 72 deletions

View File

@ -80,7 +80,7 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th
background: colors.mode === 'dark' ? 'rgba(0, 0, 0, 0.45)' : 'rgba(208, 209, 211, 0.24)', background: colors.mode === 'dark' ? 'rgba(0, 0, 0, 0.45)' : 'rgba(208, 209, 211, 0.24)',
}, },
sidemenu: { sidemenu: {
width: 60, width: 44,
}, },
}; };
} }

View File

@ -101,7 +101,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
display: none; display: none;
${theme.breakpoints.up('md')} { ${theme.breakpoints.up('md')} {
display: block; display: flex;
flex-direction: inherit;
margin-bottom: ${theme.spacing(2)}; margin-bottom: ${theme.spacing(2)};
} }

View File

@ -31,6 +31,12 @@ describe('DropdownChild', () => {
expect(icon).toBeInTheDocument(); expect(icon).toBeInTheDocument();
}); });
it('displays an external link icon if the target is _blank', () => {
render(<DropdownChild text={mockText} icon={mockIcon} url={mockUrl} target="_blank" />);
const icon = screen.getByTestId('external-link-icon');
expect(icon).toBeInTheDocument();
});
it('displays a divider instead when isDivider is true', () => { it('displays a divider instead when isDivider is true', () => {
render(<DropdownChild text={mockText} icon={mockIcon} url={mockUrl} isDivider />); render(<DropdownChild text={mockText} icon={mockIcon} url={mockUrl} isDivider />);

View File

@ -17,10 +17,15 @@ const DropdownChild = ({ isDivider = false, icon, onClick, target, text, url }:
const styles = getStyles(theme); const styles = getStyles(theme);
const linkContent = ( const linkContent = (
<> <div className={styles.linkContent}>
{icon && <Icon data-testid="dropdown-child-icon" name={icon} className={styles.icon} />} <div>
{text} {icon && <Icon data-testid="dropdown-child-icon" name={icon} className={styles.icon} />}
</> {text}
</div>
{target === '_blank' && (
<Icon data-testid="external-link-icon" name="external-link-alt" className={styles.externalLinkIcon} />
)}
</div>
); );
let element = ( let element = (
@ -53,7 +58,17 @@ const getStyles = (theme: GrafanaTheme2) => ({
display: flex; display: flex;
width: 100%; width: 100%;
`, `,
externalLinkIcon: css`
color: ${theme.colors.text.secondary};
margin-left: ${theme.spacing(1)};
`,
icon: css` icon: css`
margin-right: ${theme.spacing(1)}; margin-right: ${theme.spacing(1)};
`, `,
linkContent: css`
display: flex;
flex: 1;
flex-direction: row;
justify-content: space-between;
`,
}); });

View File

@ -49,16 +49,16 @@ NavBar.displayName = 'NavBar';
const getStyles = (theme: GrafanaTheme2) => ({ const getStyles = (theme: GrafanaTheme2) => ({
sidemenu: css` sidemenu: css`
border-right: 1px solid ${theme.components.panel.borderColor};
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: fixed; position: fixed;
width: ${theme.components.sidemenu.width}px;
z-index: ${theme.zIndex.sidemenu}; z-index: ${theme.zIndex.sidemenu};
${theme.breakpoints.up('md')} { ${theme.breakpoints.up('md')} {
background-color: ${theme.colors.background.primary}; background-color: ${theme.colors.background.primary};
border-right: 1px solid ${theme.components.panel.borderColor};
position: relative; position: relative;
width: ${theme.components.sidemenu.width}px;
} }
.sidemenu-hidden & { .sidemenu-hidden & {

View File

@ -23,8 +23,9 @@ const NavBarDropdown = ({
reverseDirection = false, reverseDirection = false,
subtitleText, subtitleText,
}: Props) => { }: Props) => {
const filteredItems = items.filter((item) => !item.hideFromMenu);
const theme = useTheme2(); const theme = useTheme2();
const styles = getStyles(theme, reverseDirection); const styles = getStyles(theme, reverseDirection, filteredItems);
let header = ( let header = (
<button onClick={onHeaderClick} className={styles.header}> <button onClick={onHeaderClick} className={styles.header}>
@ -47,19 +48,17 @@ const NavBarDropdown = ({
return ( return (
<ul className={`${styles.menu} dropdown-menu dropdown-menu--sidemenu`} role="menu"> <ul className={`${styles.menu} dropdown-menu dropdown-menu--sidemenu`} role="menu">
<li>{header}</li> <li>{header}</li>
{items {filteredItems.map((child, index) => (
.filter((item) => !item.hideFromMenu) <DropdownChild
.map((child, index) => ( key={`${child.url}-${index}`}
<DropdownChild isDivider={child.divider}
key={`${child.url}-${index}`} icon={child.icon as IconName}
isDivider={child.divider} onClick={child.onClick}
icon={child.icon as IconName} target={child.target}
onClick={child.onClick} text={child.text}
target={child.target} url={child.url}
text={child.text} />
url={child.url} ))}
/>
))}
{subtitleText && <li className={styles.subtitle}>{subtitleText}</li>} {subtitleText && <li className={styles.subtitle}>{subtitleText}</li>}
</ul> </ul>
); );
@ -67,52 +66,62 @@ const NavBarDropdown = ({
export default NavBarDropdown; export default NavBarDropdown;
const getStyles = (theme: GrafanaTheme2, reverseDirection: Props['reverseDirection']) => ({ const getStyles = (
header: css` theme: GrafanaTheme2,
background-color: ${theme.colors.background.secondary}; reverseDirection: Props['reverseDirection'],
border: none; filteredItems: Props['items']
color: ${theme.colors.text.primary}; ) => {
font-size: ${theme.typography.h4.fontSize}; const adjustHeightForBorder = filteredItems!.length === 0;
font-weight: ${theme.typography.h4.fontWeight};
padding: ${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(2)} !important;
white-space: nowrap;
width: 100%;
&:hover { return {
background-color: ${theme.colors.action.hover}; header: css`
} background-color: ${theme.colors.background.secondary};
border: none;
.sidemenu-open--xs & { color: ${theme.colors.text.primary};
display: flex; height: ${theme.components.sidemenu.width - (adjustHeightForBorder ? 2 : 1)}px;
font-size: ${theme.typography.body.fontSize}; font-size: ${theme.typography.h4.fontSize};
font-weight: ${theme.typography.body.fontWeight}; font-weight: ${theme.typography.h4.fontWeight};
padding-left: ${theme.spacing(1)} !important; padding: ${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(2)} !important;
} white-space: nowrap;
`,
menu: css`
flex-direction: ${reverseDirection ? 'column-reverse' : 'column'};
.sidemenu-open--xs & {
display: flex;
flex-direction: column;
float: none;
margin-bottom: ${theme.spacing(1)};
position: unset;
width: 100%; width: 100%;
}
`,
subtitle: css`
border-bottom: 1px solid ${theme.colors.border.weak};
color: ${theme.colors.text.secondary};
font-size: ${theme.typography.bodySmall.fontSize};
font-weight: ${theme.typography.bodySmall.fontWeight};
margin-bottom: ${theme.spacing(1)};
padding: ${theme.spacing(1)} ${theme.spacing(2)} ${theme.spacing(1)};
white-space: nowrap;
.sidemenu-open--xs & { &:hover {
border-bottom: none; background-color: ${theme.colors.action.hover};
margin-bottom: 0; }
}
`, .sidemenu-open--xs & {
}); display: flex;
font-size: ${theme.typography.body.fontSize};
font-weight: ${theme.typography.body.fontWeight};
padding-left: ${theme.spacing(1)} !important;
}
`,
menu: css`
border: 1px solid ${theme.components.panel.borderColor};
flex-direction: ${reverseDirection ? 'column-reverse' : 'column'};
.sidemenu-open--xs & {
display: flex;
flex-direction: column;
float: none;
margin-bottom: ${theme.spacing(1)};
position: unset;
width: 100%;
}
`,
subtitle: css`
border-bottom: 1px solid ${theme.colors.border.weak};
color: ${theme.colors.text.secondary};
font-size: ${theme.typography.bodySmall.fontSize};
font-weight: ${theme.typography.bodySmall.fontWeight};
margin-bottom: ${theme.spacing(1)};
padding: ${theme.spacing(1)} ${theme.spacing(2)} ${theme.spacing(1)};
white-space: nowrap;
.sidemenu-open--xs & {
border-bottom: none;
margin-bottom: 0;
}
`,
};
};

View File

@ -95,7 +95,6 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive']) => ({
.dropdown-menu { .dropdown-menu {
animation: dropdown-anim 150ms ease-in-out 100ms forwards; animation: dropdown-anim 150ms ease-in-out 100ms forwards;
border: none;
display: flex; display: flex;
// important to overlap it otherwise it can be hidden // important to overlap it otherwise it can be hidden
// again by the mouse getting outside the hover space // again by the mouse getting outside the hover space
@ -117,9 +116,10 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive']) => ({
border: none; border: none;
color: inherit; color: inherit;
display: block; display: block;
line-height: 42px; line-height: ${theme.components.sidemenu.width}px;
padding: 0;
text-align: center; text-align: center;
width: ${theme.components.sidemenu.width - 1}px; width: ${theme.components.sidemenu.width}px;
&::before { &::before {
display: ${isActive ? 'block' : 'none'}; display: ${isActive ? 'block' : 'none'};

View File

@ -53,7 +53,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
flex-grow: 1; flex-grow: 1;
${theme.breakpoints.up('md')} { ${theme.breakpoints.up('md')} {
display: block; display: flex;
flex-direction: inherit;
margin-top: ${theme.spacing(5)}; margin-top: ${theme.spacing(5)};
} }