mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-22 11:31:39 +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",
|
||||
"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%",
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
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 NavItem from '../../layout/NavItem';
|
||||
import NavItem from '../../../layout/navbar/NavItem';
|
||||
|
||||
export default {
|
||||
title: 'NavItem',
|
@ -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',
|
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 { 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', () => ({
|
@ -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 />);
|
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