Skip to content
This repository has been archived by the owner on Sep 1, 2022. It is now read-only.

Technical test - Bingbing YAN #37

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
41 changes: 41 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"jsx": true,
"useJSXTextNode": true
},
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/@typescript-eslint",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
],
"rules": {
"no-unused-vars": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": false,
"argsIgnorePattern": "^_"
}
]
},
"overrides": [
{
"files": ["*.js"],
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
}
]
}
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"singleQuote": true,
"printWidth": 100,
"semi": true,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always"
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}
36 changes: 36 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module.exports = {
// The root of your source code, typically /src
// `<rootDir>` is a token Jest substitutes
roots: ['<rootDir>/src'],

// Jest transformations -- this adds support for TypeScript
// using ts-jest
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.svg$': '<rootDir>/src/test-util/svgTransform.js',
},

// Runs special logic, such as cleaning up components
// when using React Testing Library and adds special
// extended assertions to Jest
setupFilesAfterEnv: ['./src/test-util/setupTest.ts'],
globalSetup: './src/test-util/globalSetup.js',
collectCoverageFrom: [
'<rootDir>/**/*.ts',
'<rootDir>/**/*.tsx',
'!<rootDir>/__tests__/**/*.ts',
'!<rootDir>/__tests__/**/*.tsx',
'!<rootDir>/**/*.d.ts',
],

// The directory where Jest should output its coverage files
coverageDirectory: '<rootDir>/../coverage',
// Test spec file resolution pattern
// Matches parent folder `__tests__` and filename
// should contain `test` or `spec`.
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',

// Module file extensions for importing
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
testEnvironment: 'jest-environment-jsdom-sixteen',
};
45 changes: 41 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,65 @@
"name": "frontend-test-react",
"version": "1.0.0",
"description": "Technical test for Frontend Engineer position at Aircall",
"main": "index.js",
"repository": "https://github.com/aircall/frontend-test-react.git",
"author": "Xavier Durand <[email protected]>",
"license": "MIT",
"private": false,
"scripts": {
"start": "webpack-dev-server --mode development --open",
"build": "webpack --mode production"
"build": "webpack --mode production",
"lint:fix": "yarn lint --fix",
"lint": "eslint . --ext .js --ext .ts --ext .tsx",
"prettier": "prettier --write 'src/**/*.ts' 'src/**/*.tsx'",
"test": "jest"
},
"dependencies": {
"react": "^16.3.1",
"react-dom": "^16.3.1"
"@reduxjs/toolkit": "^1.4.0",
"axios": "^0.19.2",
"date-fns": "^2.14.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react-router-dom": "^5.2.0",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-saga": "^1.1.3",
"styled-components": "^5.1.1",
"ts-jest": "^26.1.3",
"url-loader": "^4.1.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.11.1",
"@testing-library/react": "^10.4.7",
"@types/jest": "^26.0.5",
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"@types/react-redux": "^7.1.9",
"@types/react-router-dom": "^5.1.5",
"@types/styled-components": "^5.1.1",
"@typescript-eslint/eslint-plugin": "^3.6.1",
"@typescript-eslint/parser": "^3.6.1",
"axios-mock-adapter": "^1.18.2",
"babel-core": "6.26.*",
"babel-eslint": "^10.1.0",
"babel-loader": "7.1.*",
"babel-preset-env": "1.7.0",
"babel-preset-react": "6.24.*",
"css-loader": "2.1.*",
"eslint": "^7.5.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-react": "^7.20.3",
"eslint-plugin-react-hooks": "^4.0.8",
"html-loader": "0.5.*",
"html-webpack-plugin": "3.2.*",
"jest": "^26.1.0",
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-styled-components": "^7.0.2",
"prettier": "^2.0.5",
"source-map-loader": "^1.0.1",
"style-loader": "0.23.*",
"ts-loader": "^8.0.0",
"typescript": "^3.9.6",
"webpack": "4.29.*",
"webpack-cli": "3.2.*",
"webpack-dev-server": "3.1.*"
Expand Down
11 changes: 11 additions & 0 deletions src/@types/call.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Call {
id: string;
created_at: string;
direction: 'outbound' | 'inbound';
from: string;
to: string | null;
via: string;
duration: number;
is_archived: boolean;
call_type: 'missed' | 'answered' | 'voicemail';
}
7 changes: 7 additions & 0 deletions src/@types/custom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// To be able to import svg in .ts file :
// https://webpack.js.org/guides/typescript/#importing-other-assets

declare module '*.svg' {
const content: any;
export default content;
}
6 changes: 6 additions & 0 deletions src/@types/styled.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'styled-components';
import { Theme } from '../theme';

declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
17 changes: 0 additions & 17 deletions src/App.jsx

This file was deleted.

35 changes: 35 additions & 0 deletions src/GlobalStyle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
html, body, h1, h2, h3, ul, li {
margin: 0;
padding: 0;
border: 0;
font: inherit;
font-size: 13px;
}

body {
background: #233142;
font-family: helvetica, arial, -serif;
color: #333333;
line-height: 1;
}

#app{
position: absolute;
display: flex;
align-items: center;
justify-content: center;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
a {
color: inherit;
text-decoration: inherit;
}
`;

export default GlobalStyle;
25 changes: 0 additions & 25 deletions src/Header.jsx

This file was deleted.

48 changes: 48 additions & 0 deletions src/app/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { HeaderWrapper } from './styledComponents';

export const Header = () => {
return (
<HeaderWrapper>
<svg
width="486px"
height="168px"
viewBox="0 0 486 168"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<g transform="translate(207.000000, 24.000000)">
<rect fill="#424242" x="270" y="6" width="9" height="90" rx="4.5" />
<rect fill="#424242" x="249" y="6" width="9" height="90" rx="4.5" />
<path
d="M228,44.5757016 L228,40.5097518 C228,38.0190847 230.019776,36 232.5,36 C234.985281,36 237,38.0134641 237,40.5097518 L237,91.4902482 C237,93.9809153 234.980224,96 232.5,96 C230.014719,96 228,93.9865359 228,91.4902482 L228,87.4242984 C222.588748,92.729045 215.17636,96 207,96 C190.431458,96 177,82.5685425 177,66 C177,49.4314575 190.431458,36 207,36 C215.17636,36 222.588748,39.270955 228,44.5757016 Z M207,87 C218.59798,87 228,77.5979797 228,66 C228,54.4020203 218.59798,45 207,45 C195.40202,45 186,54.4020203 186,66 C186,77.5979797 195.40202,87 207,87 Z"
fill="#424242"
/>
<path
d="M171.566757,44.4332432 C159.655766,32.5222523 140.344234,32.5222523 128.433243,44.4332432 C116.522252,56.3442341 116.522252,75.6557659 128.433243,87.5667568 C140.344234,99.4777477 159.655766,99.4777477 171.566757,87.5667568 C173.324116,85.8093975 173.324116,82.9601551 171.566757,81.2027958 C169.809398,79.4454365 166.960155,79.4454365 165.202796,81.2027958 C156.806524,89.5990681 143.193476,89.5990681 134.797204,81.2027958 C126.400932,72.8065235 126.400932,59.1934765 134.797204,50.7972042 C143.193476,42.4009319 156.806524,42.4009319 165.202796,50.7972042 C166.960155,52.5545635 169.809398,52.5545635 171.566757,50.7972042 C173.324116,49.0398449 173.324116,46.1906025 171.566757,44.4332432 Z"
fill="#424242"
fillRule="nonzero"
/>
<path
d="M102,41.650086 L102,40.5097518 C102,38.0190847 99.9802243,36 97.5,36 C95.0147186,36 93,38.0134641 93,40.5097518 L93,91.4902482 C93,93.9809153 95.0197757,96 97.5,96 C99.9852814,96 102,93.9865359 102,91.4902482 L102,65.0800149 C102.269821,52.772034 107.976408,45.0292969 116.914551,45.0292969 C119.399832,45.0292969 121.414551,43.0145782 121.414551,40.5292969 C121.414551,38.0440155 119.399832,36.0292969 116.914551,36.0292969 C110.916968,36.0292969 105.88006,38.0781857 102,41.650086 L102,41.650086 Z"
fill="#424242"
/>
<rect fill="#424242" x="72" y="36" width="9" height="60" rx="4.5" />
<circle fill="#2AC420" cx="76.5" cy="10.5" r="10.5" />
<path
d="M51,44.5757016 L51,40.5097518 C51,38.0190847 53.0197757,36 55.5,36 C57.9852814,36 60,38.0134641 60,40.5097518 L60,91.4902482 C60,93.9809153 57.9802243,96 55.5,96 C53.0147186,96 51,93.9865359 51,91.4902482 L51,87.4242984 C45.588748,92.729045 38.1763602,96 30,96 C13.4314575,96 0,82.5685425 0,66 C0,49.4314575 13.4314575,36 30,36 C38.1763602,36 45.588748,39.270955 51,44.5757016 Z M30,87 C41.5979797,87 51,77.5979797 51,66 C51,54.4020203 41.5979797,45 30,45 C18.4020203,45 9,54.4020203 9,66 C9,77.5979797 18.4020203,87 30,87 Z"
fill="#424242"
/>
</g>
<path
d="M84,168 C37.8,168 0,130.2 0,84 C0,37.8 37.8,0 84,0 C130.2,0 168,37.8 168,84 C168,130.2 130.2,168 84,168 Z M84,12 C44.4,12 12,44.4 12,84 C12,123.6 44.4,156 84,156 C123.6,156 156,123.6 156,84 C156,44.4 123.6,12 84,12 Z M122.342895,100.615799 C120.353028,98.7917542 118.031517,97.4651764 115.710006,96.3044208 C112.227739,94.6461985 107.08725,91.9930429 103.273338,94.4803763 C101.946761,95.3094874 100.951827,96.8018875 99.7910717,97.7968209 C98.2986716,99.2892209 96.6404493,100.781621 94.6505826,101.776554 C86.1936489,106.087932 75.0835595,104.263888 68.1190259,97.7968209 C64.1392924,93.6512651 61.651959,87.6816649 61.8177812,81.8778869 C61.9836034,77.4006867 63.4760035,72.7576643 66.2949814,69.1095752 C67.455737,67.6171752 68.948137,66.2905973 70.2747149,64.7981973 C71.6012927,63.4716194 72.4304039,61.9792194 72.4304039,59.9893526 C72.4304039,57.5020192 71.2696482,55.180508 70.2747149,52.8589968 C69.2797815,50.7033078 68.2848481,48.3817966 66.7924481,46.5577521 C65.4658702,44.7337075 63.4760035,42.7438408 61.3203145,41.9147296 C60.3253811,41.5830852 59.3304477,41.4172629 58.3355144,41.7489074 C57.0089365,42.0805519 56.0140032,43.0754852 55.0190698,43.9045964 C50.7076918,47.221041 46.2304916,51.0349522 44.0748027,56.0096191 C40.4267136,63.9690861 42.0849359,73.0893087 45.2355583,80.8829535 C48.8836473,89.6715316 54.8532476,97.6309986 61.8177812,104.098066 C65.9633369,108.243621 70.6063593,112.057533 75.5810262,115.208155 C82.0480931,119.187888 89.5100935,122.504333 97.137916,123.333444 C102.444227,123.996733 108.082183,123.167622 112.725206,120.680289 C114.715072,119.519533 116.539117,118.192955 118.197339,116.534733 C120.021384,114.710688 122.01125,112.720821 123.50365,110.730955 C124.498584,109.570199 125.825162,108.409444 125.990984,106.585399 C126.156806,104.263888 124.001117,102.108199 122.342895,100.615799 Z"
fill="#2AC420"
/>
</g>
</svg>
</HeaderWrapper>
);
};

export default Header;
21 changes: 21 additions & 0 deletions src/app/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Link } from 'react-router-dom';

import { NavWrapper, NavIcon } from './styledComponents';
import phone from '../../asset/phone.svg';
import profile from '../../asset/profile.svg';
import setting from '../../asset/setting.svg';

const Nav = () => {
return (
<NavWrapper>
<Link to="/">
<NavIcon src={phone} alt="" active />
</Link>
<NavIcon src={profile} alt="" />
<NavIcon src={setting} alt="" />
</NavWrapper>
);
};

export default Nav;
15 changes: 15 additions & 0 deletions src/app/components/Routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Feed from '../../feed';
import Detail from '../../detail';

const Routes = () => {
return (
<Switch>
<Route path="/:id" component={Detail} />
<Route path="/" component={Feed} />
</Switch>
);
};

export default Routes;
32 changes: 32 additions & 0 deletions src/app/components/styledComponents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import styled from 'styled-components';

export const HeaderWrapper = styled.header`
height: 40px;
margin: 0 auto;
padding: 20px 0;
text-align: center;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
svg {
display: block;
width: 100%;
height: 100%;
}
`;

export const NavWrapper = styled.div`
position: absolute;
width: 100%;
bottom: 0;
display: flex;
justify-content: space-around;
background: ${({ theme }) => theme.colors.white};
border-top: 0.0625rem solid ${({ theme }) => theme.colors.muted};
`;

export const NavIcon = styled.img<{ active?: boolean }>`
padding: 1rem;
border-bottom: ${({ theme, active }) =>
active ? `0.25rem solid ${theme.colors.primary}` : 'none'};
height: 1.5rem;
display: block;
`;
Loading