├── client ├── .gitignore ├── .prettierignore ├── cypress │ └── support │ │ ├── commands.js │ │ ├── component.js │ │ └── component-index.html ├── src │ ├── components │ │ ├── Input │ │ │ ├── index.spec.js │ │ │ ├── style.scss │ │ │ └── index.jsx │ │ ├── List │ │ │ ├── index.spec.js │ │ │ ├── style.scss │ │ │ └── index.jsx │ │ ├── ListItem │ │ │ ├── index.spec.js │ │ │ ├── index.jsx │ │ │ └── style.scss │ │ ├── Button │ │ │ ├── index.jsx │ │ │ ├── style.scss │ │ │ └── index.spec.js │ │ ├── IconList │ │ │ └── index.jsx │ │ └── IllustrationIcon │ │ │ └── index.jsx │ ├── pages │ │ ├── Newsletter │ │ │ ├── index.spec.js │ │ │ ├── style.scss │ │ │ └── index.jsx │ │ └── ConfirmationEmail │ │ │ ├── index.spec.js │ │ │ ├── style.scss │ │ │ └── index.jsx │ ├── index.js │ ├── styles │ │ ├── variables.scss │ │ └── main.scss │ ├── App.jsx │ └── hooks │ │ └── useWindowDimensions.jsx ├── .husky │ └── pre-commit ├── design │ ├── active-states.jpg │ ├── error-states.jpg │ ├── mobile-design.jpg │ ├── desktop-design.jpg │ ├── desktop-preview.jpg │ ├── desktop-success.jpg │ ├── mobile-success.jpg │ └── desktop-success-active.jpg ├── .prettierrc.json ├── assets │ ├── fonts │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Regular.ttf │ │ └── LICENSE.txt │ └── images │ │ ├── favicon-32x32.png │ │ ├── icon-list.svg │ │ ├── icon-success.svg │ │ ├── illustration-sign-up-desktop.svg │ │ └── illustration-sign-up-mobile.svg ├── dist │ ├── 160d7a6ac26376e5e977.ttf │ ├── 35eab922fdbe4b5324d4.ttf │ ├── index.html │ └── main.js.LICENSE.txt ├── babel.config.json ├── tsconfig.json ├── Makefile ├── public │ └── index.html ├── cypress.config.js ├── style-guide.md ├── webpack.config.js ├── package.json └── README.md └── LICENSE /client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /client/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /client/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/Input/index.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/List/index.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/pages/Newsletter/index.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/ListItem/index.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/pages/ConfirmationEmail/index.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run format 5 | -------------------------------------------------------------------------------- /client/design/active-states.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/active-states.jpg -------------------------------------------------------------------------------- /client/design/error-states.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/error-states.jpg -------------------------------------------------------------------------------- /client/design/mobile-design.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/mobile-design.jpg -------------------------------------------------------------------------------- /client/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "tabWidth": 2, 5 | "semi": true 6 | } 7 | -------------------------------------------------------------------------------- /client/design/desktop-design.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/desktop-design.jpg -------------------------------------------------------------------------------- /client/design/desktop-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/desktop-preview.jpg -------------------------------------------------------------------------------- /client/design/desktop-success.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/desktop-success.jpg -------------------------------------------------------------------------------- /client/design/mobile-success.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/mobile-success.jpg -------------------------------------------------------------------------------- /client/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /client/dist/160d7a6ac26376e5e977.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/dist/160d7a6ac26376e5e977.ttf -------------------------------------------------------------------------------- /client/dist/35eab922fdbe4b5324d4.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/dist/35eab922fdbe4b5324d4.ttf -------------------------------------------------------------------------------- /client/src/components/List/style.scss: -------------------------------------------------------------------------------- 1 | .newsletter-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 0; 5 | } 6 | -------------------------------------------------------------------------------- /client/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /client/assets/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/assets/images/favicon-32x32.png -------------------------------------------------------------------------------- /client/design/desktop-success-active.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vicckm/newsletter-sign-up-form/main/client/design/desktop-success-active.jpg -------------------------------------------------------------------------------- /client/cypress/support/component.js: -------------------------------------------------------------------------------- 1 | import './commands'; 2 | 3 | import { mount } from 'cypress/react18'; 4 | 5 | Cypress.Commands.add('mount', mount); 6 | -------------------------------------------------------------------------------- /client/src/components/List/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './style.scss'; 3 | 4 | export default function List({ children }) { 5 | return
    {children}
; 6 | } 7 | -------------------------------------------------------------------------------- /client/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | [ 5 | "@babel/preset-react", 6 | { 7 | "runtime": "automatic" 8 | } 9 | ] 10 | ], 11 | "compact": true 12 | } 13 | -------------------------------------------------------------------------------- /client/assets/images/icon-list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es5", 7 | "jsx": "react", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @npm run build 3 | 4 | build-docker: 5 | @docker build -t newsletter-sign-up-form ./ 6 | 7 | run: 8 | @docker run -p 3000:3000 -d newsletter-sign-up-form 9 | 10 | setup: 11 | @npm i 12 | 13 | test-run: 14 | @npm run cypress:run 15 | 16 | test-open: 17 | @npm run cypress:open -------------------------------------------------------------------------------- /client/src/components/Button/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './style.scss'; 3 | 4 | export default function Button({ text, type = 'button', onClick = null }) { 5 | return ( 6 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /client/src/components/ListItem/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import IconList from '../IconList'; 3 | import './style.scss'; 4 | 5 | export default function ListItem({ text }) { 6 | return ( 7 |
  • 8 |

    {text}

    9 |
  • 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Newsletter 7 | 8 | 9 |
    10 | 11 | 12 | -------------------------------------------------------------------------------- /client/src/components/Button/style.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/variables.scss'; 2 | 3 | .newsletter-btn { 4 | background-color: $dark-blue; 5 | border: none; 6 | border-radius: 8px; 7 | padding: 15px 60px; 8 | color: $white; 9 | font-weight: 600; 10 | cursor: pointer; 11 | width: 100%; 12 | 13 | &:hover { 14 | background: $bg-gradient; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import { App } from './App'; 5 | 6 | const container = document.getElementById('root'); 7 | const root = createRoot(container); 8 | 9 | root.render( 10 | 11 | 12 | , 13 | ); 14 | -------------------------------------------------------------------------------- /client/cypress/support/component-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Components App 8 | 9 | 10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /client/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Newsletter 7 | 8 | 9 | 10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /client/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // Colors 2 | $error: hsl(4, 100%, 67%); 3 | $bg-error: hsla(4, 100%, 67%, 0.196); 4 | $bg-gradient: linear-gradient( 5 | 90deg, 6 | rgba(131, 58, 180, 1) 0%, 7 | rgba(253, 111, 29, 0.989233193277311) 59%, 8 | rgba(252, 176, 69, 1) 100% 9 | ); 10 | $grey: hsl(231, 7%, 60%); 11 | $dark-blue: hsl(234, 29%, 20%); 12 | $white: #fff; 13 | 14 | // Breakpoints 15 | $breakpoint-mb: 468px; 16 | -------------------------------------------------------------------------------- /client/assets/images/icon-success.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './styles/main.scss'; 3 | import Newsletter from './pages/Newsletter'; 4 | import ConfirmationEmail from './pages/ConfirmationEmail'; 5 | import { Routes, Route } from 'react-router-dom'; 6 | 7 | export const App = () => { 8 | return ( 9 | 10 | } /> 11 | } /> 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /client/src/components/ListItem/style.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/variables.scss'; 2 | 3 | .newsletter-list__item { 4 | list-style: none; 5 | display: flex; 6 | align-items: center; 7 | font-family: 'Roboto'; 8 | color: $dark-blue; 9 | margin: 0; 10 | 11 | & > svg { 12 | width: 20px; 13 | height: 20px; 14 | } 15 | 16 | & > p { 17 | margin: 10px 20px; 18 | font-size: 0.875rem; 19 | 20 | @media (max-width: 468px) { 21 | font-size: 1rem; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('cypress'); 2 | const webpackConfig = require('./webpack.config'); 3 | const webpackPreprocessor = require('@cypress/webpack-preprocessor'); 4 | 5 | module.exports = defineConfig({ 6 | component: { 7 | devServer: { 8 | framework: 'react', 9 | bundler: 'webpack', 10 | webpackConfig: webpackConfig, 11 | }, 12 | specPattern: 'src/**/*.spec.{js,jsx,ts,tsx}', 13 | setupNodeEvents(on, config) { 14 | on('file:preprocessor', (file) => { 15 | webpackPreprocessor(); 16 | }); 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /client/src/components/IconList/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function IconList() { 4 | return ( 5 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /client/style-guide.md: -------------------------------------------------------------------------------- 1 | # Front-end Style Guide 2 | 3 | ## Layout 4 | 5 | The designs were created to the following widths: 6 | 7 | - Mobile: 375px 8 | - Desktop: 1440px 9 | 10 | ## Colors 11 | 12 | ### Primary 13 | 14 | - Tomato: hsl(4, 100%, 67%) 15 | 16 | ### Neutral 17 | 18 | - Dark Slate Grey: hsl(234, 29%, 20%) 19 | - Charcoal Grey: hsl(235, 18%, 26%) 20 | - Grey: hsl(231, 7%, 60%) 21 | - White: hsl(0, 0%, 100%) 22 | 23 | ## Typography 24 | 25 | ### Body Copy 26 | 27 | - Font size (paragraph): 16px 28 | 29 | ### Font 30 | 31 | - Family: [Roboto](https://fonts.google.com/specimen/Roboto) 32 | - Weights: 400, 700 33 | -------------------------------------------------------------------------------- /client/src/components/Button/index.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { mount } from 'cypress/react18'; 3 | import Button from '.'; 4 | 5 | describe('if exists text', () => { 6 | it('should return value', () => { 7 | mount(