Skip to content

Contributing

Development Environment

Overview

All development is done inside the container named devcontainer which contains all necessary tools and dependencies. All commands in the documentation are container-agnostic and are meant to be run directly inside the devcontainer.

The devcontainer orchestrates other service containers behind the scenes via Docker-from-Docker. Other containers are implementation details and should not be accessed directly.

There are two supported ways to access the development environment. The recommended way is to use Development Containers with an IDE, which provides a more seamless experience. The alternative is to use Docker Compose directly, which is suitable for cases when IDE integration is not needed (e.g. terminal-only workflows).

Requirements

Setup

Automatic setup

You can skip this section when using Development Containers. They run this script automatically the first time the project is opened.

Run the setup script to automatically create and configure all necessary files and build Docker images:

bash setup.sh

Manual setup

If you prefer to set up the project manually:

  1. Create .env file and configure it:

    cp .env.dist .env
  2. Create docker-compose.yml and configure it:

    cp docker-compose.yml.dist docker-compose.yml
  3. Build Docker images:

    bash docker/build-docker-images.sh

Environment

The .env file configures services (ports, UID/GID), the devcontainer shell, editor and SSH agent forwarding. See .env.dist for available options.

Accessing the Development Environment

Using Development Containers

Open the project in an IDE that supports Development Containers (e.g. Visual Studio Code, JetBrains IDEs). The IDE will automatically set up the environment using the configuration in .devcontainer/devcontainer.json.

Using Docker Compose

  1. Start the devcontainer in the background:

    docker compose up -d
  2. Open a shell inside the devcontainer:

    docker compose exec devcontainer bash
  3. To stop the environment:

    docker compose down

Customization

To customize the devcontainer, create a docker/rades_react_devcontainer_local/Dockerfile that extends the base image:

# Image name `rades_react_devcontainer` might differ based on Docker compose project name
FROM rades_react_devcontainer as rades_react_devcontainer_local
# Add your customizations here

Then ensure docker-compose.yml has the build directive for the devcontainer service:

devcontainer:
  extends:
    file: docker-compose.base.yml
    service: devcontainer
  build:
    context: ./docker/rades_react_devcontainer_local/
    dockerfile: Dockerfile

Rebuild the images after making changes:

bash docker/build-docker-images.sh

If you need to persist additional data across container restarts, see how it is done in docker-compose.base.yml. You will need to add a volume mapping to the devcontainer service and add a corresponding named volume definition.

What the devcontainer Contains

The devcontainer is built in the following layers:

Base Layer (rades_react_devcontainer)

General-purpose development layer. Makes the environment container-agnostic by wrapping commands to run in the appropriate service containers.

  • OS: Debian Bookworm
  • Shells: Bash, Zsh (with Oh My Zsh), Fish
  • Editors: Vim, Nano
  • Tools: Git, SSH client, Docker CLI (Docker-from-Docker)
  • AI coding assistants: Claude Code, GitHub Copilot CLI, Open Code

Local Layer (rades_react_devcontainer_local)

Optional layer that allows individual developers to customize the environment. See Customization for details.

Service Containers

The devcontainer depends on the following service containers defined in docker-compose.base.yml:

Container Purpose
node Runs Node.js commands (npm, node)
playwright Runs Playwright component tests
docs Serves documentation via MkDocs

All service containers mount the workspace at /workspace so that file changes are shared.

Automatic Service Bootstrap

You can skip this section if you do not want to automatically install dependencies, build, and run the project, or if you are not an experienced developer. If you do use it, you can skip those sections as well, since the steps they describe are performed automatically.

Setting COMPOSE_AUTOSTART=true in .env makes the node and docs service containers automatically install dependencies, build the library bundle and run the documentation server when they start. The default is false.

Setting COMPOSE_AUTOSTART=true comes with the following trade-offs:

  • Changes to dependencies require a container restart. The watcher owns the service container's entrypoint, so updating dependencies (e.g. pulling a branch that changes package-lock.json, or running npm install <pkg>) only takes effect after restarting the node service container. The same applies to changes that affect the documentation server.
  • Service logs are not directly visible. The watcher and docs server run in their own service containers rather than in your devcontainer shell, so their output is not shown alongside your regular terminal work. You have to inspect it via docker compose logs <service> from the host.

If something is not working as expected, or you are not sure what is going on, set COMPOSE_AUTOSTART=false, restart the containers, and follow the manual steps in the sections below instead.

Installing Dependencies

Run it on initial setup or when dependencies have changed:

npm ci

Building

To build the library bundle (consumed by the documentation site):

npm run build

To build the documentation:

mkdocs build

Running

To start building the library bundle in watch mode:

npm start

To start the documentation server:

mkdocs serve

Then open the documentation / preview site at http://localhost:<port>, where <port> is defined by COMPOSE_DOCS_SERVER_PORT in .env (default 8000).

Testing

Please check out our Testing Guidelines. It includes testing guidelines and information on how to run tests.

Git Workflow

  1. One pull request per subject. Don't combine unrelated changes in a single PR unless they are really subtle details such as a fix of a typo.

  2. Name your branches <type>/<JIRA-KEY-or-slug>. type is one of task (default), fix, bug, bugfix, feature, maintenance, release, update or docs. JIRA-keyed branches use the uppercase key, e.g. task/RADES-105 or fix/RA2W-1540.

  3. Write clear, helpful and descriptive commit messages.

    1. Use imperative and write in English, e.g. Update dependencies or Automatic `Icon` size in `Button`. Wrap code identifiers in backticks.
    2. Prefix the subject with the JIRA key and a colon only for ticket-tracked work, e.g. RADES-105: Automatic `Icon` size in `Button`. Use a plain imperative subject for chores, maintenance and release commits, e.g. Update dependencies.
    3. Add a body for non-trivial changes, wrapped at ~72-80 columns, with * bullet points. Do not append a Co-Authored-By trailer.
  4. Keep each commit atomic — never leave the library in a broken state.

Integration is via Bitbucket pull requests merged fast-forward: history stays linear with no merge commits, and feature commits are preserved, not squashed. Rebase onto master to keep your branch fast-forward mergeable. Because commits persist, do not use fixup! commits — fold fixes into clean, standalone commits.

origin is Bitbucket; CI runs on the GitLab mirror. Never push or change the remote without explicit human approval.

Package Linking

The best way for development of RADES is to link rades_react into a consumer application (e.g. RA2W or WECL) with npm link so you can see it in action.

  1. In the RADES repository on your host machine, run npm link.
  2. In your application, run npm link @racom/rades_react.

To prevent Invalid Hook Call Warning when RADES is linked, add the following code to your app's Webpack config:

const path = require('path');

module.exports = {
  resolve: {
    alias: {
      react: path.resolve('./node_modules/react'),
      'react-dom': path.resolve('./node_modules/react-dom'),
    },
  },
};

Documenting

We use a combination of Material for MkDocs and Docoff as the documentation platform. Component docs are co-located beside the source as src/components/<Name>/README.md; cross-cutting pages live under src/docs/. A new page is only published once it is wired into the nav: tree in mkdocs.yml.