diff --git a/.gitignore b/.gitignore index 16901e6..1fb211c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ # testing /coverage /cypress/integration/examples +/cypress/fixtures/ +/cypress/screenshots/ # production /build diff --git a/src/App.tsx b/src/App.tsx index 82d9ffa..86fd002 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,10 @@ -import React from 'react'; +import React, {FC} from 'react'; import {BrowserRouter} from 'react-router-dom'; import NavBar from './components/NavBar'; import Router from './router/Router'; /** Main App container */ -const App = () => { +const App: FC = () => { return ( diff --git a/src/components/Header.tsx b/src/components/Header.tsx index bcfb935..2bfb852 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -4,6 +4,10 @@ import { faUser, faCodeBranch, faGraduationCap, + faExclamation, + faExclamationCircle, + faExclamationTriangle, + faCode, } from '@fortawesome/free-solid-svg-icons'; import {faConnectdevelop} from '@fortawesome/free-brands-svg-icons'; @@ -30,9 +34,15 @@ const Header: FC = ({title, lead, icon = 'faUser'}) => { if (icon === 'code-branch') { return ; } + if (icon === 'code') { + return null; + } if (icon === 'graduation-cap') { return ; } + if (icon === 'not-found') { + return ; + } }; return ( diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index a88a8f8..fbfadc0 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -1,29 +1,77 @@ import React, {FC} from 'react'; +import {Link} from 'react-router-dom'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faCode} from '@fortawesome/free-solid-svg-icons'; +import {faCode, faSignOutAlt, faUser} from '@fortawesome/free-solid-svg-icons'; +import * as ROUTES from '../constants/routes'; +interface IProps { + isAuthenticated?: boolean; + loading?: boolean; +} /** * Main Navbar serves navigation routes. */ -const NavBar: FC = () => ( - -); + ); + + const privateLinks = ( +
    +
  • + + Developers + +
  • +
  • + + Posts + +
  • +
  • + + + Dashboard + +
  • +
  • + + + Log out + +
  • +
+ ); + + /** Display appropriated links after loading given authenticated prop */ + const RenderLinks = !loading && isAuthenticated ? privateLinks : publicLinks; + + return ( + + ); +}; export default NavBar; diff --git a/src/components/__tests__/NavBar.test.tsx b/src/components/__tests__/NavBar.test.tsx new file mode 100644 index 0000000..b041e36 --- /dev/null +++ b/src/components/__tests__/NavBar.test.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import {BrowserRouter} from 'react-router-dom'; +import NavBar from '../NavBar'; +import {render, RenderResult} from '@testing-library/react'; + +interface IProps { + isAuthenticated: boolean; + loading: boolean; +} +describe('Navbar displays', () => { + let context: RenderResult; + let navProps: IProps; + + describe('before loading', () => { + navProps = { + isAuthenticated: false, + loading: true, + }; + + beforeEach(() => { + context = render( + + + , + ); + }); + + test('landing page link', () => { + const {getAllByTestId} = context; + const link = getAllByTestId('homeLink'); + expect(link[0]).toBeTruthy(); + }); + + it('no links while loading', () => { + const {queryByTestId} = context; + const links = queryByTestId('privateLinks'); + expect(links).toBeNull(); + }); + }); + + describe('when loaded', () => { + describe('when user is not authenticated', () => { + navProps = { + isAuthenticated: false, + loading: false, + }; + + beforeEach(() => { + context = render( + + + , + ); + }); + + test('developers link', () => { + const {getAllByTestId} = context; + const link = getAllByTestId('devsLink'); + expect(link[0]).toBeTruthy(); + }); + + test('register link', () => { + const {getAllByTestId} = context; + const link = getAllByTestId('signupLink'); + expect(link[0]).toBeTruthy(); + }); + + test('login page link', () => { + const {getAllByTestId} = context; + const link = getAllByTestId('loginLink'); + expect(link[0]).toBeTruthy(); + }); + }); + + // describe('when user is authenticated', () => { + // navProps = { + // isAuthenticated: true, + // loading: false, + // }; + // beforeEach(() => { + // context = render( + // + // + // , + // ); + // }); + // test('developers link', () => { + // const {getAllByTestId} = context; + // const link = getAllByTestId('devsLink'); + // expect(link[0]).toBeTruthy(); + // }); + // // test('posts page link', () => { + // // const {getAllByTestId} = context; + // // const link = getAllByTestId('postsLink'); + // // expect(link[0]).toBeTruthy(); + // // }); + // // test('dashboard page link', () => { + // // const {getAllByTestId} = context; + // // const link = getAllByTestId('dashboardLink'); + // // expect(link[0]).toBeTruthy(); + // // }); + // // test('logout page link', () => { + // // const {getAllByTestId} = context; + // // const link = getAllByTestId('logoutLink'); + // // expect(link[0]).toBeTruthy(); + // // }); + // // }); + // }); + }); +}); diff --git a/src/pages/Landing.tsx b/src/pages/Landing.tsx index 96b11e8..776a466 100644 --- a/src/pages/Landing.tsx +++ b/src/pages/Landing.tsx @@ -1,4 +1,7 @@ import React, {FC} from 'react'; +import {Link} from 'react-router-dom'; +import * as ROUTES from '../constants/routes'; +import Header from '../components/Header'; /** * Landing page @@ -7,18 +10,18 @@ const Landing: FC = () => (
-

DevBook

-

- Create developer profiles, portfolio, share and get help from other - devs -

+
diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx new file mode 100644 index 0000000..544dfcd --- /dev/null +++ b/src/pages/NotFound.tsx @@ -0,0 +1,28 @@ +import React, {FC} from 'react'; +import {Link} from 'react-router-dom'; +import Header from '../components/Header'; +import * as ROUTES from '../constants/routes'; + +const NotFound: FC = () => ( +
+
+
+
+
+ + Sign up + + + Login + +
+
+
+
+); + +export default NotFound; diff --git a/src/pages/SignIn.tsx b/src/pages/SignIn.tsx index abc5ac6..f1c4960 100644 --- a/src/pages/SignIn.tsx +++ b/src/pages/SignIn.tsx @@ -1,5 +1,7 @@ import React, {FC} from 'react'; +import * as ROUTES from '../constants/routes'; import Header from '../components/Header'; +import {Link} from 'react-router-dom'; /** * Sign in form @@ -19,7 +21,7 @@ const SignIn: FC = () => (

- Don't have an account? Sign in + Don't have an account? Sign up

); diff --git a/src/pages/SignUp.tsx b/src/pages/SignUp.tsx index ef7ff86..6caa060 100644 --- a/src/pages/SignUp.tsx +++ b/src/pages/SignUp.tsx @@ -1,6 +1,7 @@ import React, {FC} from 'react'; +import {Link} from 'react-router-dom'; import Header from '../components/Header'; - +import * as ROUTES from '../constants/routes'; /** * Sign up form */ @@ -26,7 +27,7 @@ const SignUp: FC = () => (

- Already have an account? Sign in + Already have an account? Sign in

); diff --git a/src/router/Router.tsx b/src/router/Router.tsx index 306d4af..f761958 100644 --- a/src/router/Router.tsx +++ b/src/router/Router.tsx @@ -11,6 +11,7 @@ import AddExperience from '../pages/AddExperience'; import AddEducation from '../pages/AddEducation'; import PostPage from '../pages/Post'; import Posts from '../pages/Posts'; +import NotFound from '../pages/NotFound'; import * as ROUTES from '../constants/routes'; /** Register navigation paths accessible */ @@ -27,6 +28,8 @@ const Router: FC = () => ( + + ); diff --git a/src/static/css/style.min.css b/src/static/css/style.min.css index 5ef764d..e6d5d3e 100644 --- a/src/static/css/style.min.css +++ b/src/static/css/style.min.css @@ -540,3 +540,8 @@ img { .post img { width: 150px; } +.not-found { + position: relative; + background: url('../img/404.jpg') no-repeat center center/cover; + height: 100vh; +} diff --git a/src/static/img/404.jpg b/src/static/img/404.jpg new file mode 100644 index 0000000..597e6b2 Binary files /dev/null and b/src/static/img/404.jpg differ diff --git a/src/static/scss/style.scss b/src/static/scss/style.scss index ccaa0c7..6ffa043 100644 --- a/src/static/scss/style.scss +++ b/src/static/scss/style.scss @@ -1,7 +1,7 @@ -@import "_config"; -@import "_utils"; -@import "_form"; -@import "_mobile"; +@import '_config'; +@import '_utils'; +@import '_form'; +@import '_mobile'; * { box-sizing: border-box; @@ -10,7 +10,7 @@ } body { - font-family: "Raleway", sans-serif; + font-family: 'Raleway', sans-serif; font-size: 1rem; line-height: 1.6; background-color: white; @@ -62,7 +62,7 @@ img { // landing .landing { position: relative; - background: url("../img/showcase.jpg") no-repeat center center/cover; + background: url('../img/showcase.jpg') no-repeat center center/cover; height: 100vh; &-inner { @@ -93,7 +93,7 @@ img { .profile-grid { display: grid; - grid-template-areas: "top top" "about about" "exp edu" "github github"; + grid-template-areas: 'top top' 'about about' 'exp edu' 'github github'; grid-gap: 1rem; .profile-top { @@ -190,3 +190,9 @@ img { width: 150px; } } + +.not-found { + position: relative; + background: url('../img/404.jpg') no-repeat center center/cover; + height: 100vh; +}