Add Navbar component, emotion for css and storybook

This commit is contained in:
Charles Bochet
2022-12-04 22:59:30 +01:00
parent eba76274c6
commit 0f2d8a556e
15 changed files with 37849 additions and 4090 deletions

View File

@ -1,9 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -1,16 +1,19 @@
import React from 'react';
import Tasks from './pages/Tasks';
import History from './pages/History';
import Performances from './pages/Performances';
import AppLayout from './layout/AppLayout';
import { Routes, Route } from 'react-router-dom';
function App() {
return (
<div className="App">
<AppLayout>
<Routes>
<Route path="/" element={<Tasks />} />
<Route path="/history" element={<History />} />
<Route path="/performances" element={<Performances />} />
</Routes>
</div>
</AppLayout>
);
}

View File

@ -0,0 +1,22 @@
import Navbar from './Navbar';
import styled from '@emotion/styled';
const StyledLayout = styled.div`
display: flex;
flex-direction: column;
`;
type OwnProps = {
children: JSX.Element;
};
function AppLayout({ children }: OwnProps) {
return (
<StyledLayout>
<Navbar />
<div>{children}</div>
</StyledLayout>
);
}
export default AppLayout;

View File

@ -0,0 +1,53 @@
import styled from '@emotion/styled';
import { useNavigate } from 'react-router-dom';
type OwnProps = {
label: string;
to: string;
active?: boolean;
};
type StyledItemProps = {
active?: boolean;
};
const StyledItem = styled.button`
display: flex;
height: 60px;
background: inherit;
align-items: center;
padding-left: 10px;
padding-right: 10px;
margin-left: 10px;
margin-right: 10px;
font-size: 16px;
margin-bottom: -2px;
cursor: pointer;
color: ${(props: StyledItemProps) => (props.active ? 'black' : '#2e3138')};
font-weight: ${(props: StyledItemProps) =>
props.active ? 'bold' : 'inherit'};
border: 0;
border-bottom: ${(props: StyledItemProps) =>
props.active ? '2px solid black' : '2px solid #eaecee'};
&:hover {
border-bottom: 2px solid #2e3138;
}
`;
function NavItem({ label, to, active }: OwnProps) {
const navigate = useNavigate();
return (
<StyledItem
onClick={() => {
navigate(to);
}}
active={active}
aria-selected={active}
>
{label}
</StyledItem>
);
}
export default NavItem;

View File

@ -0,0 +1,53 @@
import styled from '@emotion/styled';
import { useMatch, useResolvedPath } from 'react-router-dom';
import NavItem from './NavItem';
const NavbarContainer = styled.div`
display: flex;
flex-direction: row;
align-items: stretch;
padding-left: 12px;
height: 58px;
border-bottom: 2px solid #eaecee;
`;
function Navbar() {
return (
<>
<NavbarContainer>
<NavItem
label="Tasks"
to="/"
active={
!!useMatch({
path: useResolvedPath('/').pathname,
end: true,
})
}
/>
<NavItem
label="History"
to="/history"
active={
!!useMatch({
path: useResolvedPath('/history').pathname,
end: true,
})
}
/>
<NavItem
label="Performances"
to="/performances"
active={
!!useMatch({
path: useResolvedPath('/performances').pathname,
end: true,
})
}
/>
</NavbarContainer>
</>
);
}
export default Navbar;

View File

@ -0,0 +1,9 @@
function Performances() {
return (
<div>
<h1>This is the performances page</h1>
</div>
);
}
export default Performances;

View File

@ -0,0 +1,20 @@
import { MemoryRouter } from 'react-router-dom';
import NavItem from '../../layout/NavItem';
export default {
title: 'NavItem',
component: NavItem,
};
export const NavItemDefault = () => (
<MemoryRouter>
<NavItem label="Test" to="/test" />
</MemoryRouter>
);
export const NavItemActive = () => (
<MemoryRouter initialEntries={['/test']}>
<NavItem label="Test" to="/test" active={true} />
</MemoryRouter>
);

View File

@ -0,0 +1,14 @@
import { MemoryRouter } from 'react-router-dom';
import Navbar from '../../layout/Navbar';
export default {
title: 'Navbar',
component: Navbar,
};
export const NavbarOnPerformance = () => (
<MemoryRouter initialEntries={['/performances']}>
<Navbar />
</MemoryRouter>
);

View File

@ -0,0 +1,19 @@
import { render, fireEvent } from '@testing-library/react';
import { NavItemDefault } from '../../stories/layout/NavItem.stories'; //👈 Our stories imported here.
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockedNavigate,
}));
it('Checks the NavItem renders', () => {
const { getByRole } = render(<NavItemDefault />);
const button = getByRole('button');
expect(button).toHaveTextContent('Test');
fireEvent.click(button);
expect(mockedNavigate).toHaveBeenCalledWith('/test');
});

View File

@ -0,0 +1,17 @@
import { render } from '@testing-library/react';
import { NavbarOnPerformance } from '../../stories/layout/Navbar.stories';
it('Checks the NavItem renders', () => {
const { getByRole } = render(<NavbarOnPerformance />);
expect(getByRole('button', { name: 'Performances' })).toHaveAttribute(
'aria-selected',
'true',
);
expect(getByRole('button', { name: 'Tasks' })).toHaveAttribute(
'aria-selected',
'false',
);
});