Frontend Architecture
├── .github/
│ └── ISSUE_TEMPLATE/
├── app/
│ ├── config/
│ └── settings.py
│ ├── frontend/ # Frontend
│ ├── server/
| ├── tests/ # Frontend
| ├── .babelrc # Frontend
| ├── .jest.config.js # Frontend
│ ├── manage.py
│ ├── requirements.txt
│ ├── package.json # Frontend
│ ├── package-lock.json # Frontend
│ └── webpack.config.js # Frontend
├── dev/
│ ├── django.dockerfile
│ ├── webpack.dockerfile
│ └── dev.env
├── .dockerignore
├── .gitignore
├── jsconfig.json
├── CONTRIBUTING.md
├── docker-compose.yml
├── LICENSE
└── README.md
Overall project structure
├── frontend/
│ ├── src/
│ ├── assets/
│ ├── components/
│ ├── Apps.js
│ └── <Components>/
│ ├── context/
│ ├── QualifiersContext.tsx
│ ├── pages/
│ ├── templates/
│ └── index.html
│ ├── index.js
│ └── index.scss
│ ├── static
│ └── templates
├── tests/
├── .babelrc
├── .jest.config.js
├── package-lock.json
├── package.json
└── webpack.config.js
Frontend Architecture
These diagrams show how data flows through the app: Frontend and Backend UML diagrams
Summary
Frontend Tech Stack: React, Babel, webpack, Jest, React Testing Library, HTML, SCSS, JS, TailwindCSS
The over ninety percent of our frontend architecture is housed in our frontend/
directory. This directory is a Django app, which is a set of files that can be ported to any Django-based application.
Since our frontend is a Django app, it takes advantage of the way Django serves its static assets. Every Django app, by default, looks to the templates/
* directory within the app for the html
template file to serve. This template usually contains <script>
and <style>
tags denoting the location of SCSS and JS files. In Django, these files are usually located inside the static/
* directory. Likewise, our frontend app store our templates and static assets within these directories.
Despite these similiarties, however, the files in these two directories should never be manipulated by a developer. These files are automatically generated by an application called webpack via configurations in webpack.config.js
and .babelrc
.
The files that should be manipulated by developers are housed within the src/
directory. Inside of here are directories for assets/
, componenents/
, pages/
, router
, and templates/
. Each of these directories contains the files which webpack reads and then bundle into output files for the static/
and template/
directories.
*Note: The
templates/
andstatic/
directories contain within them afrontend/
directory in order to namespace template and static files. Although this serves little purpose for our project, it is a Django convention that prevents Django from confusing thetemplates/
andstatic/
directories from the frontend app vs another app.
Overview of Directories and Files
- frontend/: houses all the frontend files.
- frontend/src/: houses all the files for developers to manipulate. The files here are read by webpack before being bundled into the
static/
andtemplates/
directories. - assets/: this is where we store all of our miscellaneous files, such as .jpegs, .svgs, .gifs, etc.
- componenents/: this is where we store the files that generate our components, such as buttons and cards. To learn more about this in-depth, read the components section of this guide.
- context/: contains the logic and data management utilities related to context providers and consumers. Contexts are used for managing global state within our application, providing a way to pass data through the component tree without having to pass props manually at every level. - COP (Community of Practice) JSON Structure: The COP data represents different communities of practice within our organization, each consisting of various roles and descriptions. Below is the JSON structure of the COP data for QualifierPageRoles.tsx:
{ "COPs": { "UI/UX": [ "UI/UX_Designer", "UX_Researcher", "UX_Writing", "UX_Practice_Lead" ], "Engineering": [ "Back_End_Developer", "Front_End_Developer", "Full_Stack_Developer", "Engineering_Practice_Lead" ], "Data_Science": [ "Data_Scientist", "Data_Analyst", "Data_Engineer", "Data_Science_Practice_Lead" ], "Project/Product_Management": [ "Product_Manager", "Project_Manager", "Business_Analyst", "Product_Owner", "Special_Projects_Coordinator", "Product_Management_Practice_Lead" ], "DevOps": [ "Site_Reliability_Engineer", "Data_Engineer", "Database_Architect", "Security_Engineer", "DevOps_Practice_Lead" ] } }
- pages/: contains the React files that pools together various components to generate a page.
- router/: contains the routing logic for the project. It uses the React-Router library.
- templates/: contains HTML files that are then generated into the regular templates directory. To learn more about how webpack bundle our files, read the webpack section of this guide.
- index.js: this file serves as the entry point for all other js files*. This file is read by webpack, and then bundled into code in the
static
directory. - index.scss: this file serves as the entry point for all other scss files*.
- frontend/static/: automatically generated by webpack, DO NOT EDIT
- frontend/templates/: automatically generated by webpack, DO NOT EDIT
- tests/: contains our test files. To run these files simply use
docker compose run webpack npm run test
. - .babelrc: Babel's configuration. To learn more about this, please visit babel's documentation.
- .jest.config.js: Jest's configuration. To learn more about configuring Jest, please visit jest's documentation.
- package-lock.json & package.json: These files are created by npm to keep track of dependencies. Please visit npm's documentation to understand them.
- webpack.config.js: webpack's configuration. To learn more about configuring webpack, please visit their documentation. To learn about our specific configuration, see the below guide.
*Note: This is technically a lie. In actuality, index.js, reads index.s as well as the React files, making it the only entryway for all files bundled in the src/
directory.
Components Directory
├── components/
│ ├── Basics/
│ ├── Colors.scss
│ └── Titles.scss
│ ├── Buttons/
│ ├── Button.js
│ └── Button.scss
│ ├── Cards/
│ ├── Cards.js
│ └── Cards.scss
│ └── <Components>/
A closer look at a theoretical expansion of the components directory
The components directory contains our site components. Each directory in here represents a different class of components, such as Buttons/
* or Cards/
*. Within these directories are the files necessary that creates these components. Likewise, the special Basics/
directory contains small CSS classes that are reused, but not, technically, components, such as text-size or text-colors.
*Note: These files are capitalized to follow React convention for components. When making new components, please make sure to follow this convention. This convention is in place to help React differentiate between modules vs other types of imports.
Webpack Configurations
...
module.exports = {
mode: 'development',
entry: {
index: "./frontend/src/index.js"
},
output: {
clean: {
keep: '.gitkeep'
},
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'frontend/static/frontend'),
},
devtool: 'inline-source-map',
module: {
rules: ...
},
optimization: {
moduleIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: ...
},
plugins: [
new HtmlWebpackPlugin({
filename: '../../templates/frontend/index.html',
template: '/frontend/src/templates/index.html',
}),
],
watchOptions: {
ignored: /node_modules/,
},
}
webpack.config.js (truncated)
Our webpack.config.js
file is one of the most important files to understanding how our frontend architecture comes together. Therefore, this section is dedicated to the settings that we have set for this file. Note that we do not explain all the settings, as some can be found and easily deduced from webpack's configuration and guides documentation.
- entry: the file that is ultimately read by webpack to bundle everything together. This file,
index.js
imports all dependencies and files that makes up our product. Note that advanced multiple entry is possible, should we ever need it. - output: contains configurations for the files that are generated in the
static/
directory - output > clean > keep: clean is usually used to clear away old files before generating new ones (filenames are variable to force browser css/js recacheing). However, keep notes files that should not be removed*.
- output > filename: this configures the name of the generated js files. [name] is simply the name of the file noted in the entry configuration, and [contenthash] is a randomly generated string, which forces browser recacheing.
- output > path: the directory to place the generated file. This directory is the one that Django, by default, detects its static files.
- optimization: this contains a catch-all for various ways to enhance either development or deployment. For more on our current configuration, read this guide.
- plugins > HtmlWebpackPlugin: this plugin enables us to dynamically generate templates with the correct
<script>
and<styles>
path by reading the template and outputing it with the path noted by filename. This output path follows Django's default template directory structure. - watchOptions > ignored: configures files to ignore when regenerating watched files.
*Note: The kept .gitkeep
file is there to give an empty file for git to preserve the otherwise empty directory when pushed onto GitHub. As you might have guessed, git does not push empty directories.
Why do we separate Babel from webpack?
If you have explored documentation from webpack, you might learn that the babel-loader in module > rules can accept the settings noted in .babelrc
. The reason why we separate these settings into another file is because webpack is not, in theory, the only application that makes use of these settings. Although we have no other apps that makes use of .babelrc
at the moment, this can change in the future. Therefore, this separation of files is a form of future proofing.
Testing
Component Testing
Our tests exists inside the tests directory with subdirectories that follows frontend/src
. There is also __mock__/
which contains code that bypasses certain tricky imports, such as svg or SCSS assets, which are not needed when testing. In order to understand how to write tests, be sure to take a look at the documentation for React Testing Library, the parent DOM Testing Library, and the support libraries jest-dom and user-event.
To run these tests, use the command docker compose run webpack npm run test
(or with test:w
for watch mode). The tests are run through jest, while the other libraries support react testing by providing functions to render DOM elements and simulate user behavior.
Note: jest-environment-jsdom
is a library that is absolutely required to link jsdom to jest. It provides the classes necessary for jest to interpret the jsdom environment. This information is listed here as it is not listed in jest's or jsdom's docs.
Accessibility Testing
In addition to testing the functioning of our components, we also test the accessability of it via the library, @axe-core/react. This library prints out accessibility issues onto the browser console, providing accessibiltiy testing once the HTML has fully rendered. That said, the library is known to give both false positives and false negatives. As always reading the official documentation is best when it comes to resolving these errors.
ESLint Configuration Documentation for Frontend Developers
Our ESLint configuration is tailored to help us maintain a clean, consistent codebase with special attention to React, TypeScript, Tailwind CSS, and accessibility standards. Below is an overview of the main components and rules in the configuration and how they function.
Plugins and Extensions
This configuration uses several plugins to enhance linting capabilities:
- @eslint/js - Provides basic JavaScript linting rules.
- typescript-eslint - Adds TypeScript support, integrating rules to enforce TypeScript-specific syntax and best practices.
- eslint-plugin-react - Adds React-specific linting rules to ensure best practices with JSX.
- eslint-plugin-prettier - Enforces code formatting consistency, using Prettier's rules.
- eslint-plugin-tailwindcss - Adds rules specific to Tailwind CSS, helping with class management and preventing misconfigured or conflicting classes.
- eslint-plugin-react-hooks - Enforces React Hooks rules, including hook dependency checking.
- eslint-plugin-jsx-a11y - Adds accessibility rules for JSX, ensuring the markup adheres to accessibility standards.
Key Configuration Options
- File Matching
The configuration applies to all JavaScript, JSX, TypeScript, and TSX files in the project, matching the following patterns: **/*.js
**/*.jsx
**/*.ts
-
**/*.tsx
-
Global Environment
Sets up global variables specific to browser environments to avoid undefined variable errors. -
React Settings
Automatically detects the version of React being used, which optimizes the linting experience. -
Rules
-
General Rules:
no-unused-vars
: Warns about variables defined but not used.no-console
: Warns whenconsole
statements are used in production code.indent
: Enforces a 2-space indentation style for code consistency.no-irregular-whitespace
: Prevents errors caused by unexpected whitespace.
-
Prettier Integration:
prettier/prettier
: Enforces Prettier's formatting rules for a consistent code style.
-
React-Specific Rules:
react/no-unescaped-entities
: Disabled globally. To bypass, use/* eslint-disable react/no-unescaped-entities */
at the top of a file when necessary.react-hooks/rules-of-hooks
: Ensures hooks are only used within functional components and custom hooks.react-hooks/exhaustive-deps
: Warns about missing dependencies in effect hooks.
-
TypeScript Rules:
@typescript-eslint/no-unused-vars
: Flags unused variables in TypeScript code as errors.
-
Tailwind CSS Rules:
tailwindcss/no-contradicting-classname
: Prevents usage of conflicting Tailwind CSS classes.tailwindcss/no-unnecessary-arbitrary-value
: Warns about arbitrary values in Tailwind that could be simplified.tailwindcss/classnames-order
: Enforces consistent order of Tailwind CSS classes.
-
Accessibility Rules (JSX A11y):
jsx-a11y/alt-text
: Ensures allimg
elements have analt
attribute for accessibility.
-
Ignored Files and Folders
- Certain files and folders are ignored to avoid unnecessary linting errors, such as
node_modules/
, config files (*.config.js
), and mock data intests/__mocks__
.
Notes on Using ESLint in Development
- Disabling Rules Temporarily: If you encounter specific rule warnings or errors that are intentional or irrelevant to your case, you can disable rules at the file or line level using
// eslint-disable
comments. - Testing New Plugins or Rules: When new plugins or rules are added, test them in a few sample files to ensure compatibility and expected behavior.
Running Linter and Formatter
To help maintain consistent code quality and style across the project, we’ve set up commands for both linting and formatting. Here’s how to use them:
Linting:
Run the linter using npm run lint
. This command will analyze all files in the project for potential linting issues. It will attempt to auto-fix any issues it can and will display whether the code passed or failed the check.
If there are any issues that cannot be auto-fixed, the output will provide details so they can be manually reviewed and addressed.
Formatting:
Run the formatter using npm run format
. This command will format all JavaScript, TypeScript, and JSON files in the project, skipping any files specified in .gitignore.
These steps help ensure a consistent coding style across the project, minimizing style-related issues and making code easier to read and maintain.
This ESLint setup ensures our codebase is both clean and accessible, while supporting best practices in React, TypeScript, and Tailwind CSS usage. For any adjustments to the rules or extensions, reach out to the team for further guidance.
Required Extensions for VS Code
To ensure consistent code quality and style across the team, please install the following extensions in Visual Studio Code:
Prettier: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
ESLint: https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
Additional Resources
Sass Documentation
React Documentation
Webpack Documentation
@babel/preset-react Documentation
React Router Documentation
Jest Documentation
React Testing Library Documentation
@axe-core/react Documentation
WAI-ARIA Authoring Practices 1.1