Skip to content

Frontend

Introduction

Integrates’ frontend is a client-side rendered web application written in TypeScript and built with React.

Browser support

Supported browsers are listed in the Browser compatibility article.

Thanks to TypeScript, we can use newer ECMAScript language features, as it takes care of downleveling as needed when transpiling, but as for runtime features, it is important to ensure compatibility.

Helpful resources:

Principles

  • Functional: The view should be a function of state and props. The codebase only uses functional components, avoids direct mutations and favors a declarative style.
  • The state should not exist: Keep the state to a minimum.
  • There’s a component for that: If there isn’t, feel free to create it.
  • No need to reinvent the wheel: Sometimes, third-party packages have already figured it out.

Getting started

To observe changes as you edit the code, run the following command:

Terminal window
m . /integrates

Moreover, the application and the local program are compiled using Vite, which facilitates React-refresh for real-time observation of changes.

Linting

The frontend uses ESLint, Prettier and Stylelint to enforce compliance with a defined coding style.

To view and auto-fix linting issues globally, you can run:

Terminal window
m . /integrates/front/lint

This linting process might take too long. Its default argument is all, but you can choose local or commit arguments as alternatives.

To run only for committed changes in the last HEAD, you can run:

Terminal window
m . /integrates/front/lint commit

To run only for local (non committed) changes above HEAD, you can run:

Terminal window
m . /integrates/front/lint local

Testing

The frontend uses Jest as a test runner, MSW to mock API responses and the utilities provided by React Testing Library to test the UI behavior from a user perspective.

To execute the test cases on your computer, you can run:

Terminal window
m . /integrates/front/test

To execute test cases individually, you can pass the path of the test as an argument to this command:

Terminal window
m . /integrates/front/test /path/to/index.test.tsx

Core

State Management

  • Transferred state: This makes up the majority of the frontend state. Data loaded from the backend is managed by the Apollo Client.
  • Forms: Their ephemeral state is managed by Formik.
  • Local state: useState and Context where needed.
  • Additional state management: Zustand for simpler, more predictable state management in certain parts of the application.

API Client

The frontend uses the Apollo Client to interact with the backend.

You can use the useQuery hook to load data and the useMutation hook to trigger create, update or delete operations.

Refer to Apollo’s documentation for more details.

Forms

The frontend uses Formik to power its forms, fields and validations.

You can use the <Formik> component, with inputs from src/components/Input to compose forms, and Yup to declare a validation schema to be checked on submit.

Refer to Formik and Yup’s documentation for more details.

Tables

The frontend uses TanStack Table to power its data tables, fitting them with features such as sorting, pagination, column toggling, row expansion, exports and more.

You can use the <Table> component, from src/components/Table to build a table from a dataset and columns.

Refer to TanStack Table’s documentation for more details.

Tours

The frontend uses a custom hook to manage the status of tours.

You can use the useTour hook, from src/hooks/use-tour to get and set the current status of each Tour.

For example:

import { Tour } from "components/Tour";
import { useTour } from "src/hooks/use-tour";
const { tours, setCompleted } = useTour();
const runTour = !tours.myTour;
function finishTour() {
setCompleted("myTour");
}
return <Tour run={runTour} onFinish={finishTour}>;

Authorization

Integrates uses Attribute-based access control to manage its authorization model, and the frontend uses some utilities from CASL to implement it.

You can do conditional rendering based on what the user is allowed to access:

import { Can } from "utils/authz/Can";
const DemoComponent = () => {
return (
<div>
<Can I={"do_some_action"}>
<p>{"Welcome"}</p>
</Can>
<Can I={"do_some_action"} not={true}>
<p>{"Get outta here"}</p>
</Can>
</div>
);
};

You can also use it outside JSX

import { useAbility } from "@casl/react";
import { authzPermissionsContext } from "utils/authz/config";
const DemoComponent = () => {
const permissions = useAbility(authzPermissionsContext);
function handleClick() {
if (permissions.can("do_some_action")) {
alert("Welcome");
} else {
alert("Get outta here");
}
}
return <button onClick={handleClick}>{"Click me"}</button>;
};

Feature preview

In a constantly evolving product, it may be useful to conduct A/B testing.

This strategy allows trying new ideas with a small subset of the users, analyzing reception, iterating and improving before releasing them to everyone.

You can do conditional rendering based on user preference:

import { featurePreviewContext } from "context/featurePreview";
const DemoComponent = () => {
const { featurePreview } = useContext(featurePreviewContext);
if (featurePreview) {
return <h1>{"Shiny new view"}</h1>;
}
return <h2>{"Current default view"}</h2>;
};

Routing

The frontend uses React Router to declare routes and manage navigation between them.

You can use the <Route> and <Switch> components to declare routes, the useParams hook to get the URL parameters, the <Link> component for declarative navigation, and the useHistory hook for imperative navigation.

Refer to React Router’s documentation for more details.

Styling

The frontend uses styled-components and Tachyons to create and compose UI styles.

You can declare styled components with the styled tag, reference tachyons classes, and also add custom CSS as needed.

Refer to styled-components and Tachyons’ documentation for more details.

Internationalization

The frontend uses i18next to manage translations.

While we currently support only English, more languages may be added later on, making it a good idea to avoid hardcoding texts and having them instead as translations.

You can declare texts in the respective file for the language, at src/utils/translations, and then, use the useTranslation hook to access them in the component.

Refer to i18next’s documentation for more details.

Dependencies

The frontend uses npm as its package manager.

When adding or updating dependencies, keep this requirement in mind. Always make sure to pin the dependency to an exact version, as semantic versioning is often unreliable and may cause regressions due to unexpected incompatibilities.

Refer to https://github.com/fluidattacks/makes#makenodejslock if you need to generate a lock file without having Node.js installed on your computer.

Logging

console.log usage is not allowed per https://eslint.org/docs/latest/rules/no-console, though it can still be used locally for debugging.

You can use src/utils/logger.ts, which sends errors and warnings to Bugsnag, the bug-tracking platform we currently use.

To access Bugsnag, sign in to your Okta account. If you can’t find the app, feel free to request access via help@fluidattacks.com

Design

Visual consistency is key to providing users with a good experience. This motivation led to the creation of the components library, a collection of UI components that can be easily used by developers and continuously refined by designers.

You can access it on:

  • Production: design.fluidattacks.com
  • Developer branch: integrates.front.development.fluidattacks.com/branch-name/storybook/index.html
  • Locally: localhost:6006, after running m . /integrates/front storybook

Most of these components are implementations of the design guidelines defined by the Customer Experience team at Fluid Attacks.

Resources

Our platform utilizes various media assets, including images, GIFs, and other media to enhance user experience. These resources are hosted and managed through Cloudinary. Our developers can access and manage these assets via our Cloudinary media explorer here.

Type definitions

Type definitions ensure consistency and clarity in our codebase, facilitating seamless communication between the frontend and backend. They can be auto-generated with m . /integrates/front/types to maintain coherence and accuracy in the data exchanged between different parts of the application.

Tooling

Helpful tools that enhance development experience when working on the frontend:

Troubleshooting

Helpful tips and tricks that have proven to be useful when diagnosing issues on the frontend:

  • How do I do X?: Refer to the documentation of the core packages mentioned above. If that doesn’t solve it, feel free to reach out for help on the development channel or chat with a domain expert.
  • Why is X not working?: Look for error traces on the browser console or use breakpoints to inspect the code and variable values as it runs.
  • Is the backend returning an error?: Use the network tab to view more details about the request and its response.
  • Can’t find an element on a test?: Try increasing the print limit to view more details and suggestions or try a snippet on testing playground.

Frontend Testing

Frontend tests are classified into the following levels:

  • Unit tests: From the test, start to create a function.
  • End-to-end tests: These tests verify that the flows a user follows work as expected, for example: loading a web page, logging in, verifying email notifications, and online payments, validating that these flows are done and that they work correctly.