Make sidebar expand on hover by default to prevent adopter impact

Signed-off-by: hiba-aldalaty <hibaaldalaty@gmail.com>
This commit is contained in:
hiba-aldalaty
2021-11-04 13:59:55 +00:00
parent ddf7325869
commit a6cf7598a1
9 changed files with 100 additions and 41 deletions
@@ -40,7 +40,6 @@ import {
SidebarDivider,
SidebarSpace,
SidebarScrollWrapper,
SidebarExpandButton,
} from '@backstage/core-components';
const useSidebarLogoStyles = makeStyles({
@@ -99,7 +98,6 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
<SidebarDivider />
<Shortcuts />
<SidebarSpace />
<SidebarExpandButton />
<SidebarDivider />
<SidebarSettings />
</Sidebar>
@@ -28,7 +28,7 @@ import { SubmenuItem } from './SubmenuItem';
async function renderScalableSidebar() {
await renderInTestApp(
<Sidebar>
<Sidebar disableExpandOnHover>
<SidebarSearchField onSearch={() => {}} to="/search" />
<SidebarItem
icon={CatalogSidebarLogo}
@@ -71,6 +71,10 @@ describe('Sidebar', () => {
userEvent.click(screen.getByTestId('sidebar-expand-button'));
expect(await screen.findByText('Create...')).toBeInTheDocument();
});
it('Sidebar should not show expanded items when hovered on', async () => {
userEvent.hover(screen.getByTestId('sidebar-root'));
expect(await screen.queryByText('Create...')).not.toBeInTheDocument();
});
});
describe('Submenu Items', () => {
it('Extended sidebar with submenu content hidden by default', async () => {
@@ -17,12 +17,7 @@
import { makeStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import clsx from 'clsx';
import React, {
useState,
useContext,
PropsWithChildren,
useEffect,
} from 'react';
import React, { useState, useContext, PropsWithChildren, useRef } from 'react';
import { sidebarConfig, SidebarContext } from './config';
import { BackstageTheme } from '@backstage/theme';
import { SidebarPinStateContext } from './Page';
@@ -88,24 +83,85 @@ const useStyles = makeStyles<BackstageTheme>(
{ name: 'BackstageSidebar' },
);
export function Sidebar({ children }: PropsWithChildren<{}>) {
enum State {
Closed,
Idle,
Open,
}
type Props = {
openDelayMs?: number;
closeDelayMs?: number;
disableExpandOnHover?: boolean;
};
export function Sidebar(props: PropsWithChildren<Props>) {
const {
disableExpandOnHover = false,
openDelayMs = sidebarConfig.defaultOpenDelayMs,
closeDelayMs = sidebarConfig.defaultCloseDelayMs,
children,
} = props;
const classes = useStyles();
const isSmallScreen = useMediaQuery<BackstageTheme>(theme =>
theme.breakpoints.down('md'),
);
const [state, setState] = useState(State.Closed);
const hoverTimerRef = useRef<number>();
const { isPinned } = useContext(SidebarPinStateContext);
const [isOpen, setIsOpen] = useState(isPinned);
useEffect(() => {
const handleOpen = () => {
if (isPinned) {
setIsOpen(true);
return;
}
}, [isPinned]);
if (hoverTimerRef.current) {
clearTimeout(hoverTimerRef.current);
hoverTimerRef.current = undefined;
}
if (state !== State.Open && !isSmallScreen) {
hoverTimerRef.current = window.setTimeout(() => {
hoverTimerRef.current = undefined;
setState(State.Open);
}, openDelayMs);
setState(State.Idle);
}
};
const handleClose = () => {
if (isPinned) {
return;
}
if (hoverTimerRef.current) {
clearTimeout(hoverTimerRef.current);
hoverTimerRef.current = undefined;
}
if (state === State.Idle) {
setState(State.Closed);
} else if (state === State.Open) {
hoverTimerRef.current = window.setTimeout(() => {
hoverTimerRef.current = undefined;
setState(State.Closed);
}, closeDelayMs);
}
};
const isOpen = (state === State.Open && !isSmallScreen) || isPinned;
return (
<div className={classes.root} data-testid="sidebar-root">
<div
className={classes.root}
data-testid="sidebar-root"
onMouseEnter={disableExpandOnHover ? () => {} : handleOpen}
onFocus={disableExpandOnHover ? () => {} : handleOpen}
onMouseLeave={disableExpandOnHover ? () => {} : handleClose}
onBlur={disableExpandOnHover ? () => {} : handleClose}
>
<SidebarContext.Provider
value={{
isOpen,
setIsOpen,
handleOpen,
handleClose,
}}
>
<div
@@ -122,26 +178,21 @@ export function Sidebar({ children }: PropsWithChildren<{}>) {
export const SidebarExpandButton = () => {
const classes = useStyles();
const { isOpen, handleOpen, handleClose } = useContext(SidebarContext);
const { isPinned } = useContext(SidebarPinStateContext);
const isSmallScreen = useMediaQuery<BackstageTheme>(theme =>
theme.breakpoints.down('md'),
);
const { isPinned } = useContext(SidebarPinStateContext);
const { isOpen, setIsOpen } = useContext(SidebarContext);
const openDelayMs = sidebarConfig.defaultOpenDelayMs;
const closeDelayMs = sidebarConfig.defaultCloseDelayMs;
const handleClick = () => {
if (isPinned || isSmallScreen) {
return;
if (isOpen) {
handleClose();
} else {
handleOpen();
}
const delayMs = isOpen ? openDelayMs : closeDelayMs;
setTimeout(async () => {
setIsOpen(!isOpen);
}, delayMs);
};
if (isSmallScreen || isPinned) {
if (isPinned || isSmallScreen) {
return null;
}
@@ -47,7 +47,7 @@ async function renderSidebar() {
<SidebarExpandButton />
</Sidebar>,
);
userEvent.click(screen.getByTestId('sidebar-expand-button'));
userEvent.hover(screen.getByTestId('sidebar-root'));
}
describe('Items', () => {
@@ -53,6 +53,18 @@ const handleSearch = (input: string) => {
export const SampleSidebar = () => (
<Sidebar>
<SidebarSearchField onSearch={handleSearch} to="/search" />
<SidebarDivider />
<SidebarItem icon={HomeOutlinedIcon} to="#" text="Plugins" />
<SidebarItem icon={AddCircleOutlineIcon} to="#" text="Create..." />
<SidebarDivider />
<SidebarIntro />
<SidebarSpace />
</Sidebar>
);
export const SampleScalableSidebar = () => (
<Sidebar disableExpandOnHover>
<SidebarSearchField onSearch={handleSearch} to="/search" />
<SidebarDivider />
<SidebarItem
@@ -59,12 +59,14 @@ export const SIDEBAR_INTRO_LOCAL_STORAGE =
export type SidebarContextType = {
isOpen: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
handleOpen: () => any;
handleClose: () => any;
};
export const SidebarContext = createContext<SidebarContextType>({
isOpen: false,
setIsOpen: () => {},
handleOpen: () => {},
handleClose: () => {},
});
export type ItemWithSubmenuContextType = {
@@ -24,6 +24,7 @@ export const CatalogSidebarLogo = () => {
height="21"
fill="none"
viewBox="0 0 20 21"
fontSize="small"
>
<path
fillRule="evenodd"
-6
View File
@@ -81,9 +81,6 @@ export const lightTheme = createTheme({
},
submenu: {
background: '#404040',
indicator: '#9BF0E1',
color: '#b5b5b5',
selectedColor: '#FFF',
},
},
pinSidebarButton: {
@@ -165,9 +162,6 @@ export const darkTheme = createTheme({
},
submenu: {
background: '#404040',
indicator: '#9BF0E1',
color: '#b5b5b5',
selectedColor: '#FFF',
},
},
pinSidebarButton: {
-3
View File
@@ -58,9 +58,6 @@ export type BackstagePaletteAdditions = {
};
submenu: {
background: string;
indicator: string;
color: string;
selectedColor: string;
};
};
tabbar: {