mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-22 19:41:53 +03:00
Merge pull request #7 from twentyhq/cbo-left-nav
Add Task list on Tasks page
This commit is contained in:
commit
dcfc4c9e45
9
front/.storybook/preview-head.html
Normal file
9
front/.storybook/preview-head.html
Normal 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>
|
@ -20,7 +20,8 @@
|
|||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"storybook": "start-storybook -p 6006 -s public",
|
"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": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
@ -41,6 +42,11 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"react-refresh": "0.14.0"
|
"react-refresh": "0.14.0"
|
||||||
},
|
},
|
||||||
|
"jest": {
|
||||||
|
"coveragePathIgnorePatterns" : [
|
||||||
|
"src/stories"
|
||||||
|
]
|
||||||
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
@ -10,34 +10,14 @@
|
|||||||
content="Web site created using create-react-app"
|
content="Web site created using create-react-app"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<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
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro' rel='stylesheet' type='text/css'>
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
<title>Twenty</title>
|
||||||
<title>React App</title>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Tasks from './pages/Tasks';
|
import Tasks from './pages/tasks/Tasks';
|
||||||
import History from './pages/History';
|
import History from './pages/History';
|
||||||
import Performances from './pages/Performances';
|
import Performances from './pages/Performances';
|
||||||
import AppLayout from './layout/AppLayout';
|
import AppLayout from './layout/AppLayout';
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import Navbar from './Navbar';
|
import Navbar from './navbar/Navbar';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
const StyledLayout = styled.div`
|
const StyledLayout = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
16
front/src/layout/containers/FullWidthContainer.tsx
Normal file
16
front/src/layout/containers/FullWidthContainer.tsx
Normal 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;
|
@ -1,9 +0,0 @@
|
|||||||
function Tasks() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>This is the home page</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Tasks;
|
|
50
front/src/pages/tasks/TaskList.tsx
Normal file
50
front/src/pages/tasks/TaskList.tsx
Normal 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:
|
||||||
|
'I’m looking for my order but couldn’t find it. Could you help me find it. I don’t 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;
|
17
front/src/pages/tasks/TaskListHeader.tsx
Normal file
17
front/src/pages/tasks/TaskListHeader.tsx
Normal 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;
|
98
front/src/pages/tasks/TaskListItem.tsx
Normal file
98
front/src/pages/tasks/TaskListItem.tsx
Normal 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;
|
12
front/src/pages/tasks/Tasks.tsx
Normal file
12
front/src/pages/tasks/Tasks.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import FullWidthContainer from '../../layout/containers/FullWidthContainer';
|
||||||
|
import TaskList from './TaskList';
|
||||||
|
|
||||||
|
function Tasks() {
|
||||||
|
return (
|
||||||
|
<FullWidthContainer>
|
||||||
|
<TaskList />
|
||||||
|
</FullWidthContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tasks;
|
@ -1,6 +1,6 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import NavItem from '../../layout/NavItem';
|
import NavItem from '../../../layout/navbar/NavItem';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'NavItem',
|
title: 'NavItem',
|
@ -1,6 +1,6 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import Navbar from '../../layout/Navbar';
|
import Navbar from '../../../layout/navbar/Navbar';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Navbar',
|
title: 'Navbar',
|
8
front/src/stories/pages/tasks/TaskList.stories.tsx
Normal file
8
front/src/stories/pages/tasks/TaskList.stories.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import TaskList from '../../../pages/tasks/TaskList';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Tasks',
|
||||||
|
component: TaskList,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TaskListDefault = () => <TaskList />;
|
9
front/src/stories/pages/tasks/TaskListHeader.stories.tsx
Normal file
9
front/src/stories/pages/tasks/TaskListHeader.stories.tsx
Normal 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 />;
|
20
front/src/stories/pages/tasks/TaskListItem.stories.tsx
Normal file
20
front/src/stories/pages/tasks/TaskListItem.stories.tsx
Normal 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:
|
||||||
|
'I’m looking for my order but couldn’t find it. Could you help me find it. I don’t know where ...',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
13
front/src/stories/pages/tasks/Tasks.stories.tsx
Normal file
13
front/src/stories/pages/tasks/Tasks.stories.tsx
Normal 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>
|
||||||
|
);
|
@ -1,6 +1,6 @@
|
|||||||
import { render, fireEvent } from '@testing-library/react';
|
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();
|
const mockedNavigate = jest.fn();
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
@ -1,6 +1,6 @@
|
|||||||
import { render } from '@testing-library/react';
|
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', () => {
|
it('Checks the NavItem renders', () => {
|
||||||
const { getByRole } = render(<NavbarOnPerformance />);
|
const { getByRole } = render(<NavbarOnPerformance />);
|
10
front/src/tests/pages/tasks/TaskList.test.tsx
Normal file
10
front/src/tests/pages/tasks/TaskList.test.tsx
Normal 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');
|
||||||
|
});
|
10
front/src/tests/pages/tasks/TaskListHeader.test.tsx
Normal file
10
front/src/tests/pages/tasks/TaskListHeader.test.tsx
Normal 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();
|
||||||
|
});
|
10
front/src/tests/pages/tasks/TaskListItem.test.tsx
Normal file
10
front/src/tests/pages/tasks/TaskListItem.test.tsx
Normal 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();
|
||||||
|
});
|
10
front/src/tests/pages/tasks/Tasks.test.tsx
Normal file
10
front/src/tests/pages/tasks/Tasks.test.tsx
Normal 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');
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user