Frontend

Development Setup

  • Use a Tailwind CSS linter (code plugin) if you write CSS
  • Use Storybook to develop components
  • For CSS-specific questions and guidelines, refer to frontend-css.md - This document covers units, Tailwind usage, CSS Modules, custom properties, responsive design, dark mode, and third-party library styling

Source Code Best Practices

We use Bulletproof React with the following specialisations/exceptions:

  • We use camelCase in file names and identifiers.
  • We use absolute imports.
  • We use minimal number of packages: need to be noted in solutions or in a decision
  • Be thoughtful of memory consumption and runtime
  • Use debounce hook as in PlantingAttributeEditForm.tsx
  • Use react-query correctly, see our guidelines doc/guidelines/frontend-api-calls.md
  • Always validate form, use zod for more complicated validations
  • Wrap APIs using create*API in api folder
  • Be careful in writing hooks, especially when using useEffect
  • Error Handling https://tkdodo.eu/blog/breaking-react-querys-api-on-purpose#a-bad-api
  • Lists are called *List and items of such a list are called *ListItem, e.g., LayerList and LayerListItem for a list of layers
  • Always label fields/attributes with their units like "Width (cm)" or "Scaling (pixels/meter)"
  • When checking for empty values in DTOs or validating form values, consider both null and undefined as empty value.
    Find more details in Validation section of Solution Strategy.

Feature Imports

The index.ts file of each feature should serve as its public API, and all elements within that feature should be exported from it. When importing elements from other features, use the feature's root directory, like this:

import { MyComponent } from "@/features/my-feature";

Avoid importing elements directly from subdirectories within a feature, like this:

import { MyComponent } from "@/features/my-feature/components/MyComponent";

Think of a feature as a library or a module that is self-contained but can expose different parts to other features via its entry point.

Utility Function Conventions

  • Use utility functions whenever code would be duplicated otherwise.
  • Add new utility functions to the appropriate utility file.
    • i.e. getPlantWidth is added to plant-utils.ts in the plant layer utils folder.
  • Add new utility files to the appropriate utils folder.
    • The global utils folder should contain files like date-utils.ts or string-utils.ts.
    • The map planning utils folder should contain files like layer-utils.ts or ShapesSelection.ts.
    • The plant layer utils folder contain files like plant-utils.ts or planting-utils.ts.
  • The utils folders should only contain TypeScript code.

Route Naming Conventions

  • Follow RESTful conventions for route names.
  • Use descriptive, plural nouns for resource collections.
  • Use placeholders for dynamic segments in route names.

Examples

  • View: /resource
  • Edit: /resource/:id/edit
  • Create: /resource/create

Use React Router's <Link> for user-visible navigation. Use useNavigate() only for programmatic navigation.

Do not place buttons inside a <Link>.

External links opened in a new tab must use:

<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  External website
</a>

Incorporate Accessibility Best Practices

Writing code that is highly accessible and easily testable should be a priority whenever possible. While it may not always be the primary focus, if you can achieve both goals simultaneously, it's worth pursuing that path. Additionally, many accessibility best practices are inherent in standard coding practices. Such as using appropriate HTML elements like h1s, button tags, providing alt attributes for images and using appropriate ARIA roles and attributes.

Documentation

We use Storybook to document shared components. Story files are named *.stories.tsx and should be located in the same folder as the component they are documenting. For an example please take a look in the src/components folder.

For other API documentation like Hooks and utility functions, we use TypeDoc. Run npm run doc to generate the TypeDoc documentation under src/generated/docs. The generated documentation is automatically integrated into Storybook under the menu DOCS. There is also the possibility to display additional documentation inside the DOCS menu in storybook. Such additional documentation files should be created in the src/docs directory for Storybook to recognize them. Furthermore, an additional documentation file must be a .mdx file and it has to export a header like this.

import { Meta } from "@storybook/addon-docs";

<Meta
  title="docs/<name_of_documentation_file>"
  parameters={{
    viewMode: "docs",
    previewTabs: {
      canvas: { hidden: true },
    },
  }}
/>;

Storybook will display an error if the .mdx file does not have valid MDX2 syntax.

Minimum requirements for every component:

  • one story file, probably with several exports for different states, see SimpleButton.stories.tsx
  • Document all props
  • Any deviation from the default scroll direction (vertical) should be documented as part of the component documentation.