Skip to content

radkr/intro-component-with-signup-form

Repository files navigation

Frontend Mentor - QR code component solution

This is a solution to the Intro component with sign-up form. Frontend Mentor challenges help you improve your coding skills by building realistic projects.

With my solution I went a bit further and created a

  • Next.js site deployed on github pages that is
  • dinamically adjusts to the browser's default font size and is
  • reponsive but still
  • stick to the Frontend Mentor's design when the default font size is 16px.

Altough the client side input validation in this project is so basic that could be solved more simple packages like validator.js

  • I used Zod package to gain experience using a more sofisticated validator library for future more complex projects written in typescript.

The unit, integration and accessibility tests increase the confidence that the site works as intended.

Table of contents

Overview

Screenshot

Desktop view:

Desktop view

Desktop view with errors:

Desktop view with errors

Mobile view:

Mobile view

Links

My process

Built with

  • Semantic HTML5 markup
  • CSS custom properties
  • Mobile-first workflow
  • Responsive design
  • Next.js + React.js
  • Zod schema validation package

Improved with

  • Autoprefixer to increase browser coverage

Tested with

  • WAVE Web Accessibility Evaluation Tool
  • Jest + React Testing Library + User Event Testing Library

Iterations

Iteration 1

Show the styled page on mobile devices.

Iteration 2

Show the styled page on both desktop and mobile devices.

Iteration 3

Refactor: Rewrite the style to use grid instead of flexbox to prevent glitches around the breakpoint.

Iteration 4

Refactor: Factor the reused Input component and the functional SignUpForm component out into React Functional Components to enhance the maintainablilty and the testability of the page.

Iteration 5

Show error messages on submittion attempt and prevent submittion when no or invalid data has been entered to any field so as the user be able to fix any error in the form right away.

Iteration 6

Show error messages on lost input focus when no or invalid data has been entered to any field so as the user is notified even more earlier about any form error.

What I learned

Next.js fonts

When using static fonts instead of variable ones in Next.js, you will need to specify the weights like this:

import { Poppins } from "next/font/google";

const poppinsSansSerif = Poppins({
  weight: ["400", "500", "600", "700"],
  variable: "--font-poppins-sans-serif",
});

Next.js static assets on Github Pages

"Next.js can serve static files, like images, under a folder called public in the root directory. Files inside public can then be referenced by your code starting from the base URL (/)." (Next.js about public folder)

Next.js assumes the application will be served from the root of a domain (e.g. mydomain.com/) and its routing and asset handling are configured accordingly. Github Pages, however will host them from a subpath in the format <account_name>.github.io/<repository_name>/. Practically this leads to the following issues:

  • ✗ - Routing: Internal links within your Next.js app will likely not work correctly as they will be looking for paths relative to the root of the domain, not the subdirectory.
  • ✗ - Asset Loading: Images, CSS, and other static assets might fail to load because their paths will also be incorrect.

"To deploy a Next.js application under a sub-path of a domain you can use the basePath config option." (Next.js about basePath)

//next.config.mjs

/** @type {import('next').NextConfig} */
const nextConfig = {
  basePath: "/<repository_name>",
};

export default nextConfig;
  • ✔ - Routing: "When linking to other pages using next/link and next/router the basePath will be automatically applied."
  • ✗ - Asset Loading: "When using the next/image component, you will need to add the basePath in front of src."

Next.js recommendation is: "To use a local image, import your .jpg, .png, or .webp image files." So importing image like below and applying basePath will handle the image src to link under the subpath.

import Image from "next/image";
import icon from "@/public/icon-error.svg";

export default MyImage(){
  return <Image src={icon} alt="Error Icon" className={styles.errorIcon} />;
}
  • Asset Loading:
    • ✔ - For local imported images in .js
    • ✗ - For images referenced by url() in .css

To prevent replacing the subpath manually on css url()s I inject css variables inline in the layout.js and apply them in the globals.css as follows:

//app/layout.js

import backgroundMobile from "../public/bg-intro-mobile.png";
import backgroundDesktop from "../public/bg-intro-desktop.png";

//...

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body
        style={{
          "--background-mobile": `url(${backgroundMobile.src})`,
          "--background-desktop": `url(${backgroundDesktop.src})`,
        }}
        className={`${poppinsSansSerif.variable}`}
      >
        {children}
      </body>
    </html>
  );
}
/*app/globals.css*/

/*...*/

body {
  background-image: var(--background-mobile);
  /*...*/
}

@media (min-width: 400px) {
  body {
    background-image: var(--background-desktop);
    /*...*/
  }
  • Asset Loading:
    • ✔ - For local imported images in .js: "When using the next/image component, you will need to add the basePath in front of src."
    • ✔ - For images referenced by url() in .css

Note: assetPrefix replaces the basePath if any with the asset prefix of the static assets like .css and .js files only while it does not replace those like the files (e.g. images, icons and so on) in the public folder. So setting assetPrefix is not necessary in this case.

Note: The Next.js Github Action automatically replaces the next config with output: export and basePath: "/<repository_name>" set. That is the reason why deploying on Github Pages work well without config modification decently except for the css url() reference for the background images.

Iteration 1 - Lesson learned

Building a webpage according to a reference screenshot instead of a figma file is much harder and will probably not pixel accurate even not to the degree when working with figma files.

I found the PerfectPixel Chrome extension that overlays the reference screenshots over the rendered webpage under construction that helps visually see the differences.

Iteration 2 - Lesson learned

When the element sizes change with the layout, using flexbox can be tricky. Iteration 3 will maybe to refactor the current solution to grid layout to prevent the undesired glitches around the breakpoint.

Testing style applied through CSS

My intent was to test: that the Input component renders input that change its border color when get in error state with code something like this:

it("renders input that change its border color when get in error state", () => {
  // Arrange and act
  //
  // Not in error state
  render(<Input id="firstName" placeholder="First Name" />);
  const inactiveInput = screen.getByLabelText("First Name");
  const inactiveBorderColor =
    window.getComputedStyle(inactiveInput).borderColor;

  // In error state
  render(<Input id="firstName" placeholder="First Name" active />);
  const activeInput = screen.getByLabelText("First Name");
  const activeBorderColor = window.getComputedStyle(activeInput).borderColor;

  // Assert
  expect(activeBorderColor).not.toBe(inactiveInput);
});

This test failed because both activeBorderColor and inactiveInput result to be empty strings.

As I found out jest with jsdom does not populates the dom with style properties spcified in the css files. There are packages like jest-transform-css that intend to do the work.

Lessons learned:

  • The toHaveStyle Jest DOM function can check against concrete style properties
  • jsdom provides the document and window objects that implements many browser API for testing
  • By default, Jest doesn't understand how to process CSS files or CSS Modules. It's primarily a JavaScript testing framework.
  • CSS files or CSS modules are mocked in a default next.js environment this allows to verify that the correct class names are being applied to your HTML elements, but the actual CSS style properties defined in those files are not populated in the jsdom environment.
  • There is a package jest-transform-css that do the work.
  • Performance: Fully parsing and applying CSS in a unit testing environment can be resource-intensive and slow down the test suite. Mocking provides a faster and more focused approach.

Form no validation

Zod works properly only if noValidate is set on the form element. Otherwise, the form element catches email format error before passed to Zod. This prevented showing the error message specified.

Useful resources

Next.js background images:

Host Next.js project from Github pages

Styling form and form controls

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published