forked from blakeblackshear/frigate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(web): Split AppBar and add tests
- Loading branch information
1 parent
ddb6127
commit e729bd5
Showing
5 changed files
with
261 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { h, Fragment } from 'preact'; | ||
import BaseAppBar from './components/AppBar'; | ||
import LinkedLogo from './components/LinkedLogo'; | ||
import Menu, { MenuItem, MenuSeparator } from './components/Menu'; | ||
import AutoAwesomeIcon from './icons/AutoAwesome'; | ||
import LightModeIcon from './icons/LightMode'; | ||
import DarkModeIcon from './icons/DarkMode'; | ||
import { useDarkMode } from './context'; | ||
import { useCallback, useRef, useState } from 'preact/hooks'; | ||
|
||
export default function AppBar() { | ||
const [showMoreMenu, setShowMoreMenu] = useState(false); | ||
const { setDarkMode } = useDarkMode(); | ||
|
||
const handleSelectDarkMode = useCallback( | ||
(value, label) => { | ||
setDarkMode(value); | ||
setShowMoreMenu(false); | ||
}, | ||
[setDarkMode, setShowMoreMenu] | ||
); | ||
|
||
const moreRef = useRef(null); | ||
|
||
const handleShowMenu = useCallback(() => { | ||
setShowMoreMenu(true); | ||
}, [setShowMoreMenu]); | ||
|
||
const handleDismissMoreMenu = useCallback(() => { | ||
setShowMoreMenu(false); | ||
}, [setShowMoreMenu]); | ||
|
||
return ( | ||
<Fragment> | ||
<BaseAppBar title={LinkedLogo} overflowRef={moreRef} onOverflowClick={handleShowMenu} /> | ||
{showMoreMenu ? ( | ||
<Menu onDismiss={handleDismissMoreMenu} relativeTo={moreRef}> | ||
<MenuItem icon={AutoAwesomeIcon} label="Auto dark mode" value="media" onSelect={handleSelectDarkMode} /> | ||
<MenuSeparator /> | ||
<MenuItem icon={LightModeIcon} label="Light" value="light" onSelect={handleSelectDarkMode} /> | ||
<MenuItem icon={DarkModeIcon} label="Dark" value="dark" onSelect={handleSelectDarkMode} /> | ||
</Menu> | ||
) : null} | ||
</Fragment> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { h } from 'preact'; | ||
import * as Context from '../context'; | ||
import AppBar from '../AppBar'; | ||
import { fireEvent, render, screen } from '@testing-library/preact'; | ||
|
||
describe('AppBar', () => { | ||
beforeEach(() => { | ||
jest.spyOn(Context, 'useDarkMode').mockImplementation(() => ({ | ||
setDarkMode: jest.fn(), | ||
})); | ||
jest.spyOn(Context, 'DarkModeProvider').mockImplementation(({ children }) => { | ||
return <div>{children}</div>; | ||
}); | ||
}); | ||
|
||
test('shows a menu on overflow click', async () => { | ||
render( | ||
<Context.DarkModeProvider> | ||
<Context.DrawerProvider> | ||
<AppBar /> | ||
</Context.DrawerProvider> | ||
</Context.DarkModeProvider> | ||
); | ||
|
||
const overflowButton = await screen.findByLabelText('More options'); | ||
fireEvent.click(overflowButton); | ||
|
||
const menu = await screen.findByRole('listbox'); | ||
expect(menu).toBeInTheDocument(); | ||
}); | ||
|
||
test('sets dark mode on MenuItem select', async () => { | ||
const setDarkModeSpy = jest.fn(); | ||
jest.spyOn(Context, 'useDarkMode').mockImplementation(() => ({ | ||
setDarkMode: setDarkModeSpy, | ||
})); | ||
render( | ||
<Context.DarkModeProvider> | ||
<Context.DrawerProvider> | ||
<AppBar /> | ||
</Context.DrawerProvider> | ||
</Context.DarkModeProvider> | ||
); | ||
|
||
const overflowButton = await screen.findByLabelText('More options'); | ||
fireEvent.click(overflowButton); | ||
|
||
await screen.findByRole('listbox'); | ||
|
||
fireEvent.click(screen.getByText('Light')); | ||
expect(setDarkModeSpy).toHaveBeenCalledWith('light'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { h } from 'preact'; | ||
import { DrawerProvider } from '../../context'; | ||
import AppBar from '../AppBar'; | ||
import { fireEvent, render, screen } from '@testing-library/preact'; | ||
import { useRef } from 'preact/hooks'; | ||
|
||
function Title() { | ||
return <div>I am the title</div>; | ||
} | ||
|
||
describe('AppBar', () => { | ||
test('renders the title', async () => { | ||
render( | ||
<DrawerProvider> | ||
<AppBar title={Title} /> | ||
</DrawerProvider> | ||
); | ||
expect(screen.getByText('I am the title')).toBeInTheDocument(); | ||
}); | ||
|
||
describe('overflow menu', () => { | ||
test('is not rendered if a ref is not provided', async () => { | ||
const handleOverflow = jest.fn(); | ||
render( | ||
<DrawerProvider> | ||
<AppBar title={Title} onOverflowClick={handleOverflow} /> | ||
</DrawerProvider> | ||
); | ||
expect(screen.queryByLabelText('More options')).not.toBeInTheDocument(); | ||
}); | ||
|
||
test('is not rendered if a click handler is not provided', async () => { | ||
function Wrapper() { | ||
const ref = useRef(null); | ||
return <AppBar title={Title} overflowRef={ref} />; | ||
} | ||
|
||
render( | ||
<DrawerProvider> | ||
<Wrapper /> | ||
</DrawerProvider> | ||
); | ||
expect(screen.queryByLabelText('More options')).not.toBeInTheDocument(); | ||
}); | ||
|
||
test('is rendered with click handler and ref', async () => { | ||
const handleOverflow = jest.fn(); | ||
|
||
function Wrapper() { | ||
const ref = useRef(null); | ||
return <AppBar title={Title} overflowRef={ref} onOverflowClick={handleOverflow} />; | ||
} | ||
|
||
render( | ||
<DrawerProvider> | ||
<Wrapper /> | ||
</DrawerProvider> | ||
); | ||
expect(screen.queryByLabelText('More options')).toBeInTheDocument(); | ||
}); | ||
|
||
test('calls the handler when clicked', async () => { | ||
const handleOverflow = jest.fn(); | ||
|
||
function Wrapper() { | ||
const ref = useRef(null); | ||
return <AppBar title={Title} overflowRef={ref} onOverflowClick={handleOverflow} />; | ||
} | ||
|
||
render( | ||
<DrawerProvider> | ||
<Wrapper /> | ||
</DrawerProvider> | ||
); | ||
|
||
fireEvent.click(screen.queryByLabelText('More options')); | ||
|
||
expect(handleOverflow).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe('scrolling', () => { | ||
test('is visible initially', async () => { | ||
render( | ||
<DrawerProvider> | ||
<AppBar title={Title} /> | ||
</DrawerProvider> | ||
); | ||
|
||
const classes = screen.getByTestId('appbar').classList; | ||
|
||
expect(classes.contains('translate-y-0')).toBe(true); | ||
expect(classes.contains('-translate-y-full')).toBe(false); | ||
}); | ||
|
||
test('hides when scrolled downward', async () => { | ||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb()); | ||
render( | ||
<DrawerProvider> | ||
<AppBar title={Title} /> | ||
</DrawerProvider> | ||
); | ||
|
||
window.scrollY = 300; | ||
await fireEvent.scroll(document, { target: { scrollY: 300 } }); | ||
|
||
const classes = screen.getByTestId('appbar').classList; | ||
|
||
expect(classes.contains('translate-y-0')).toBe(false); | ||
expect(classes.contains('-translate-y-full')).toBe(true); | ||
}); | ||
|
||
test('reappears when scrolled upward', async () => { | ||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb()); | ||
render( | ||
<DrawerProvider> | ||
<AppBar title={Title} /> | ||
</DrawerProvider> | ||
); | ||
|
||
window.scrollY = 300; | ||
await fireEvent.scroll(document, { target: { scrollY: 300 } }); | ||
window.scrollY = 280; | ||
await fireEvent.scroll(document, { target: { scrollY: 280 } }); | ||
|
||
const classes = screen.getByTestId('appbar').classList; | ||
|
||
expect(classes.contains('translate-y-0')).toBe(true); | ||
expect(classes.contains('-translate-y-full')).toBe(false); | ||
}); | ||
}); | ||
}); |