Merge pull request #7 from twentyhq/cbo-left-nav

Add Task list on Tasks page
This commit is contained in:
Charles Bochet 2022-12-05 00:51:09 +01:00 committed by GitHub
commit dcfc4c9e45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 311 additions and 48 deletions

View File

@ -0,0 +1,9 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro">
<style type="text/css">
body {
margin: 0;
font-family: 'Source Sans Pro', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>

View File

@ -20,7 +20,8 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public"
"build-storybook": "build-storybook -s public",
"coverage": "react-scripts test --coverage"
},
"eslintConfig": {
"extends": [
@ -41,6 +42,11 @@
"overrides": {
"react-refresh": "0.14.0"
},
"jest": {
"coveragePathIgnorePatterns" : [
"src/stories"
]
},
"browserslist": {
"production": [
">0.2%",

View File

@ -10,34 +10,14 @@
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro' rel='stylesheet' type='text/css'>
<title>Twenty</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -1,5 +1,5 @@
import React from 'react';
import Tasks from './pages/Tasks';
import Tasks from './pages/tasks/Tasks';
import History from './pages/History';
import Performances from './pages/Performances';
import AppLayout from './layout/AppLayout';

View File

@ -1,13 +1,6 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
font-family: 'Source Sans Pro', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -1,9 +1,10 @@
import Navbar from './Navbar';
import Navbar from './navbar/Navbar';
import styled from '@emotion/styled';
const StyledLayout = styled.div`
display: flex;
flex-direction: column;
height: 100vh;
`;
type OwnProps = {

View File

@ -0,0 +1,16 @@
import styled from '@emotion/styled';
type OwnProps = {
children: JSX.Element;
};
const StyledContainer = styled.div`
display: flex;
height: calc(100vh - 60px);
`;
function FullWidthContainer({ children }: OwnProps) {
return <StyledContainer>{children}</StyledContainer>;
}
export default FullWidthContainer;

View File

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

View File

@ -0,0 +1,50 @@
import styled from '@emotion/styled';
import TaskListHeader from './TaskListHeader';
import TaskListItem from './TaskListItem';
const StyledList = styled.div`
display: flex;
width: 325px;
flex-direction: column;
border-right: 2px solid #eaecee;
`;
export type Task = {
id: number;
targetUser: string;
label: string;
time: string;
lastMessage: string;
};
function TaskList() {
const tasks: Task[] = [
{
id: 1,
targetUser: 'Sylvie Vartan',
label: 'Guest at #xxx property',
time: '3h',
lastMessage:
'Im looking for my order but couldnt find it. Could you help me find it. I dont know where ...',
},
{
id: 2,
targetUser: 'Johnny Halliday',
label: 'Guest at #xxx property',
time: '4h',
lastMessage: 'Hello, this is Johnny',
},
];
return (
<StyledList>
<>
<TaskListHeader />
{tasks.map((item) => (
<TaskListItem key={item.id} task={item} />
))}
</>
</StyledList>
);
}
export default TaskList;

View File

@ -0,0 +1,17 @@
import styled from '@emotion/styled';
const StyledHeader = styled.div`
display: flex;
font-weight: bold;
align-items: center;
padding: 16px 24px;
font-size: 18px;
letter-spacing: 1px;
border-bottom: 1px solid #eaecee;
`;
function TaskListHeader() {
return <StyledHeader>6 tasks waiting</StyledHeader>;
}
export default TaskListHeader;

View File

@ -0,0 +1,98 @@
import styled from '@emotion/styled';
import { Task } from './TaskList';
type OwnProps = {
task: Task;
};
const StyledListItem = styled.button`
display: flex;
padding: 16px 24px;
flex-direction: column;
color: #2e3138;
border: 0;
border-bottom: 1px solid #eaecee;
cursor: pointer;
font-family: inherit;
text-align: inherit;
align-items: inherit;
background: #f1f3f5;
`;
const StyledHeader = styled.div`
display: flex;
justify-content: space-between;
`;
const StyledAvatarAndTitle = styled.div`
display: flex;
`;
const StyledAvatar = styled.div`
display: flex;
width: 40px;
height: 40px;
border-radius: 40px;
background: #52555b;
font-size: 20px;
color: white;
align-items: center;
justify-content: center;
font-weight: bold;
`;
const StyledTitle = styled.div`
display: flex;
flex-direction: column;
margin-left: 8px;
`;
const StyledName = styled.div`
font-weight: bold;
font-size: 18px;
color: black;
`;
const StyledLabel = styled.div`
display: flex;
font-size: 14px;
`;
const StyledTime = styled.div`
display: flex;
justify-self: flex-end;
color: #7d8187;
font-size: 14px;
`;
const StyledContent = styled.div`
display: flex;
color: #52555b;
font-size: 14px;
margin-top: 8px;
`;
function TaskListItem({ task }: OwnProps) {
return (
<StyledListItem>
<StyledHeader>
<StyledAvatarAndTitle>
<StyledAvatar>
{task.targetUser
.split(' ')
.map((n) => n[0])
.join('')}
</StyledAvatar>
<StyledTitle>
<StyledName>{task.targetUser}</StyledName>
<StyledLabel>{task.label}</StyledLabel>
</StyledTitle>
</StyledAvatarAndTitle>
<StyledTime>{task.time}</StyledTime>
</StyledHeader>
<StyledContent>{task.lastMessage} </StyledContent>
</StyledListItem>
);
}
export default TaskListItem;

View File

@ -0,0 +1,12 @@
import FullWidthContainer from '../../layout/containers/FullWidthContainer';
import TaskList from './TaskList';
function Tasks() {
return (
<FullWidthContainer>
<TaskList />
</FullWidthContainer>
);
}
export default Tasks;

View File

@ -1,6 +1,6 @@
import { MemoryRouter } from 'react-router-dom';
import NavItem from '../../layout/NavItem';
import NavItem from '../../../layout/navbar/NavItem';
export default {
title: 'NavItem',

View File

@ -1,6 +1,6 @@
import { MemoryRouter } from 'react-router-dom';
import Navbar from '../../layout/Navbar';
import Navbar from '../../../layout/navbar/Navbar';
export default {
title: 'Navbar',

View File

@ -0,0 +1,8 @@
import TaskList from '../../../pages/tasks/TaskList';
export default {
title: 'Tasks',
component: TaskList,
};
export const TaskListDefault = () => <TaskList />;

View File

@ -0,0 +1,9 @@
import { MemoryRouter } from 'react-router-dom';
import TaskListHeader from '../../../pages/tasks/TaskListHeader';
export default {
title: 'Tasks',
component: TaskListHeader,
};
export const TaskListHeaderDefault = () => <TaskListHeader />;

View File

@ -0,0 +1,20 @@
import { MemoryRouter } from 'react-router-dom';
import TaskListItem from '../../../pages/tasks/TaskListItem';
export default {
title: 'Tasks',
component: TaskListItem,
};
export const TaskListItemDefault = () => (
<TaskListItem
task={{
id: 1,
targetUser: 'Sylvie Vartan',
label: 'Guest at #xxx property',
time: '3h',
lastMessage:
'Im looking for my order but couldnt find it. Could you help me find it. I dont know where ...',
}}
/>
);

View File

@ -0,0 +1,13 @@
import { MemoryRouter } from 'react-router-dom';
import Tasks from '../../../pages/tasks/Tasks';
export default {
title: 'Tasks',
component: Tasks,
};
export const TasksDefault = () => (
<MemoryRouter>
<Tasks />
</MemoryRouter>
);

View File

@ -1,6 +1,6 @@
import { render, fireEvent } from '@testing-library/react';
import { NavItemDefault } from '../../stories/layout/NavItem.stories'; //👈 Our stories imported here.
import { NavItemDefault } from '../../../stories/layout/navbar/NavItem.stories'; //👈 Our stories imported here.
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import { NavbarOnPerformance } from '../../stories/layout/Navbar.stories';
import { NavbarOnPerformance } from '../../../stories/layout/navbar/Navbar.stories';
it('Checks the NavItem renders', () => {
const { getByRole } = render(<NavbarOnPerformance />);

View File

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import { TaskListDefault } from '../../../stories/pages/tasks/TaskList.stories';
it('Checks the Tasks page render', () => {
const { getAllByRole } = render(<TaskListDefault />);
const button = getAllByRole('button');
expect(button[0]).toHaveTextContent('Sylvie Vartan');
});

View File

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import { TaskListHeaderDefault } from '../../../stories/pages/tasks/TaskListHeader.stories';
it('Checks the TaskListHeader render', () => {
const { getAllByText } = render(<TaskListHeaderDefault />);
const text = getAllByText('6 tasks waiting');
expect(text).toBeDefined();
});

View File

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import { TaskListItemDefault } from '../../../stories/pages/tasks/TaskListItem.stories';
it('Checks the TaskListItem render', () => {
const { getAllByText } = render(<TaskListItemDefault />);
const text = getAllByText('Sylvie Vartan');
expect(text).toBeDefined();
});

View File

@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import { TasksDefault } from '../../../stories/pages/tasks/Tasks.stories';
it('Checks the Tasks page render', () => {
const { getAllByRole } = render(<TasksDefault />);
const button = getAllByRole('button');
expect(button[0]).toHaveTextContent('Sylvie Vartan');
});