Development

This is the technical overview of the project.

Getting started

Prerequisites

Required:

  • Node.js 18+

Recommended:

Some widgets require a 3rd-party API key:

Those keys need to be stored at server/.env (copy server/.env.example as a template). The project will run with missing API keys, but the affected widgets will not work.

Installation

Clone the Git repository, change the directory and run the setup script:

git clone git@github.com:darekkay/dashboard.git
cd dashboard
npm run setup

The setup task will install dependencies in all available modules (app, server, storybook, docs).

Quick start

  • Run start in the root folder to run the app and server modules in parallel:
npm run start

Alternatively, check out available npm tasks in each of the module's package.json file. For example, you can start the app and server modules in separate terminal sessions:

# Shell 1
cd app
npm run start

# Shell 2
cd server
npm run dev

Notice: The server module offers the npm run dev task that uses nodemon. It will restart the server every time a file is changed (hot reload). The app module uses hot reload by default.

Available tasks

The package.json files contain other useful scripts, which you can execute using npm run <command> or npm run <command>:

CommandDescription
buildBuilds the app for production to the build folder.
ciRuns linter, typescript compiler and tests.
formatReformat all files with prettier.
generateGenerate boilerplate code (components, widgets).
lintRun ESLint, apply automatic fixes if possible.
testRun tests and check test coverage.

The master branch is (manually) deployed to dashboard.darekkay.comopen in new window.

Technology Stack

The project consists of three different modules: client, server and docs.

Client

This client module was bootstrapped with Create React Appopen in new window (Typescript preset).

Storybook

This project uses Storybookopen in new window, a tool for developing UI components in isolation. It is automatically deployed hereopen in new window on every push to production. Every widget and every common component should provide a story in a __stories__ sub-folder.

Run npm run start in the storybook module to start a local development instance of Storybook.

Server

The server module is an Expressopen in new window application. Currently, it is being used as a "gateway" to access 3rd-party data. Here are some reasons why routing external requests through the server module is a good idea:

  • Some 3rd party services cannot be called directly from the client because they don't allow CORSopen in new window.
  • API keys will be leaked if the request is being sent directly from the client.
  • Routing the request through the server module enables internal caching, so the 3rd party service doesn't get as many (duplicate) requests.

Documentation

The documentation is created with VuePressopen in new window. It is hosted at dashboard.darekkay.com/docs/open in new window.

Architecture

Creating a new widget

  1. Run the file generator (npm run generate Widget) in the app module.
  2. Adjust the widget's properties.ts file with sane initial values.
  3. Re-scan the available widgets (npm run scan:widgets).
  4. Add mandatory widget labels (at least name) to all translation files under common/translations.
  5. Edit documentation under docs/widgets.

Creating a new server endpoint

  1. Run the file generator (npm run generate) in the server module.
  2. Implement endpoint
  3. Run npm run build-api (or npm run build-api:dev for live reload) to generate OpenAPI/Swagger definition using tsoaopen in new window.

Auto-updating data

Many widgets depend on external data. To keep this data up-to-date, each widget defines an update cycle in its properties.ts file:

export const initialMeta = {
  updateCycle: { hours: 24 },
  updateStatus: "idle",
};

When creating a new widget (using npm run generate), make sure to choose the option to "use sagas". This will set up much of the necessary boilerplate. The core update logic is implemented in useTriggerUpdate. An update will be automatically triggered:

  • After each update cycle (per widget type)
  • Whenever a dependent prop changes (e.g. when adjusting the settings)
  • On page reload

The server module uses the updateCycle value as a TTL, i.e., a 3rd-party service call is being cached for the time defined in updateCycle. This means, when the user reloads a page, the server module will be called, but the data might be cached.

Internationalization

The react-i18nextopen in new window library handles internationalization. Translations are located under src/common/translations, one file per language. Currently, English (en.json) and German (de.json) translations are available. If a label translation is missing, the English label will be used as fallback.

The label keys can be viewed in a debug mode by adding debug.labels to the URL, e.g. https://dashboard.darekkay.com/?debug.labelsopen in new window.

Default UI language is based on the browser language and can be changed by the user in the settings dialog.

Numbers are localized using a custom i18next formatteropen in new window: t("number", { value }.

To find missing label translations, run npm run i18n in the app module.

Typography

Use CSS utility classes text-1 to text-7 to adjust font sizes. To preserve a consistent look across all widgets, use text-4 as the base size. Check out the Style Guideopen in new window for more information.

Color palette

Base style definitions are extracted into a common @darekkay/stylesopen in new window package. This includes a color palette based on the U.S. Web Design Systemopen in new window. It provides some unique accessibility properties: a foreground/background color ratio with a number of 50+ will always be accessible (WCAG AA). All available colors can be viewed in Storybookopen in new window.

Currently, there are two different color themes (default and dark). Those themes are implemented using CSS custom properties, making it easy to maintain and extend. The naming should be color-agnostic (e.g. primary instead of red), but it is still a work in progress.

Icons

This project uses icons from Font Awesomeopen in new window. The icons are imported and (re-)defined one at a time to include only used icons in the build bundle (components/icon/font-awesome):

import { faHome, ... } from "@fortawesome/free-solid-svg-icons";

export type FontAwesomeIconName = "home" | ...;

const icons = {
  home: faHome,
  ...
}

Example usage:

<Icon name="home" />

Grid

The actual dashboard/grid functionality is implemented with the react-grid-layoutopen in new window library. Because of some critical issues, a forkopen in new window is currently being used.

Browser support

All current browsers are supported (Chrome, Firefox, Safari, Edge). IE11 is not supported, mostly due to the usage of CSS Custom Propertiesopen in new window. I've tried using a polyfillopen in new window, but the results weren't great.

Debugging

To debug certain issues, include the following string in the URL as a query parameter or hash:

  • debug.log: Enable debug logs.
  • debug.labels: Show label IDs.
  • debug.delay: Delay server module responses by a random time between 1 and 5 seconds.
  • debug.error: Let the server always return error responses.

Contributing

Please run npm run ci for every changed module to make sure there are not linting issues or failing unit tests. Run npm run lint:fix to fix some common issues, incl. prettier formatting.

Commit message format

  • Use imperative form (e.g. "Fix" instead of "Fixed" or "Fixes").

Changelog format

The changelog uses emojis to categorize changes. View the emoji format hereopen in new window.

Code of Conduct

This project is released with a Contributor Code of Conductopen in new window. By participating in this project you agree to abide by its terms.