Skip to content

Latest commit

 

History

History
227 lines (140 loc) · 11.3 KB

README.md

File metadata and controls

227 lines (140 loc) · 11.3 KB

Mathesar front end development

This directory contains the frontend code for Mathesar. However, part of the frontend setup is based on pre-rendered json which is done through django templates. We use Svelte as the frontend library, Scss for styling, and Vite for dev tooling.

Development setup

First make sure you have Mathesar running locally.

Option 1: Setup with containers (recommended)

Once the containers are running, the changes made in frontend code will reflect immediately through hot module replacement. You do not need any additional dev setup.

IDE configuration

There is one catch when running in containers. Your IDEs will not be able to perform any intellisense or linting because there is no local node_modules folder.

Options:

  • Run npm ci locally (or copy the node_modules folder from the container to your host file system). This will not be used for anything except for helping the IDEs provide intellisense.

    If you choose this approach, make sure that you're using the same version of node and npm in your local as it is in the container, and that package-lock.json file is not modified before committing.

  • Bind mount the node_modules named volume to your local path. This introduces additional complexity, depends on the OS, and the requirement that the path needs to exist. Hence, it is not configured by default in Mathesar. Using normal volumes will not work, since the host directories will override the container directories.

  • Use VS Code with the Visual Studio Code Remote - Containers extension. Refer VS Code docs for more details.

Option 2: Manual setup

If you don't want to use Docker, you can run the front end locally.

  1. cd mathesar_ui

  2. npm ci

  3. npm run dev

    This will start a vite server at port 3000. The vite client and main files are referenced by our server rendered html files. Refer backend integration in vite docs.

Caveats

  • When running manually, the package-lock.json file may change depending on your node/npm version or your OS. Make sure to not commit those changes. If you want to add/remove packages, follow the steps in the following section.

  • The rest of the documentation within this README assumes a Docker setup. If you use a local setup instead, you'll need to interpret the remaining documentation for your specific setup.

Architectural overview

See ARCHITECTURE.md

Code standards

See STANDARDS.md

Formatting

We use Prettier to automatically format code.

Automatically when you save a file

Prettier works great when configured to automatically format files when you save them in your editor.

  • VS Code

    1. In "Settings" > "Workspace", search for "format on save".
    2. Install the "Prettier" extension (esbenp.prettier-vscode).
    3. Set "Editor: Default Formatter" to "Prettier".
    4. Enable "Editor: Format On Save".

Manually before you submit a PR

If you don't have your editor configured to auto-format your code, then you'll need to make sure to manually run Prettier before submitting a PR, otherwise your code might fail a test in our continuous integration pipeline which checks to ensure that all code matches the way that Prettier would format it.

  • Format all front end files

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npm run format
    
  • Format a specific file

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npx prettier --write src/App.svelte
    

Linting

We use ESLint to help spot more complex issues within code that cannot be fixed automatically with formatting changes. The code you write will need to be free of linting errors before it can be merged. Make sure to run the linter to check for theses errors before submitting a PR.

  • Lint all front end files:

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npm run lint
    
  • Lint a specific file:

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npx eslint src/App.svelte
    

Auto-fixing linting errors

ESLint can fix some errors automatically. In particular, we rely on ESLint to auto-sort our JavaScript import statements, so if you don't auto-fix your linting errors before submitting a PR, you're likely to get some CI errors in your PR which complain about out-of-order import statements.

  • Auto-fix all possible linting errors:

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npm run lint-fix
    

Testing

Integration tests

See Integration tests.

Unit tests

We use Vitest to run our unit tests, and we use Testing Library to test our Svelte components.

Running unit tests

  • Run all our tests:

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npm test
    
  • Re-run a specific test by name:

    docker exec -it -w /code/mathesar_ui mathesar_service_dev npm run test TextInput
    

    This will run all test files with file names containing TextInput.

Writing unit tests

  • Let's pretend we have a file primeUtils.ts containing function getPrimality that returns true for prime numbers.

    We can test function with a file primeUtils.test.ts as follows:

    import { getPrimality } from './primeUtils';
    
    test('getPrimality', () => {
      expect(getPrimality(2)).toBe(true);
      expect(getPrimality(3)).toBe(true);
      expect(getPrimality(4)).toBe(false);
      expect(getPrimality(5)).toBe(true);
      expect(getPrimality(6)).toBe(false);
      expect(getPrimality(7)).toBe(true);
    
      expect(getPrimality(3484403346821219)).toBe(true);
      expect(getPrimality(4508972191266181)).toBe(false);
    
      expect(() => getPrimality(0)).toThrow(); // Can't check zero
      expect(() => getPrimality(-7)).toThrow(); // No negative numbers
      expect(() => getPrimality(1)).toThrow(); // Primality of 1 is historically ambiguous
      expect(() => getPrimality(5.5)).toThrow(); // No decimals
    });
  • Functions like test and expect are globally imported into scope by the test runner. Your editor should hopefully be smart enough to see that Vitest is configured for our project and provide you some assistance in using those functions. You can find more in the Vitest docs, but there's not much else you'll need if you follow the pattern above.

  • After, expect, you'll use a matcher. In the example above, we've used toBe which is one of the simplest matchers. However toBe only works for primitives like numbers, booleans, and strings. If you want to compare two objects or arrays, you'll need to use toEqual instead, which performs a deep comparison.

  • The expect call is an "assertion". We can put many of them within the same test, but the test will stop running when it hits the first failure.

  • We can put many tests within the same file.

  • Commonly we'll have one test for each function, and many assertions within that test. You can also declare variables and do other logic within the test too if you're trying to build up complex scenarios with assertions throughout a workflow. As the scenarios get more complex, it can be helpful to create multiple tests for the same function.

  • Deciding what to test, and how can be an art! You generally want to try poking and the boundaries and edge cases. We don't have to go crazy with all sorts of assertions for every scenario. Just a few will do. We're not trying to test every possible input -- just some of the important ones.

  • If you find a bug, it's great practice to write a test that fails before even beginning work on the fix.

Components

  • The src/component-library directory contains general-purpose components which will eventually be spun off into its own package, separate from Mathesar.
  • See the Components README for more details.

Live reloading on Windows

  • Hot module replacement does not work well with WSL when the project is present within a Windows filesystem, as mentioned in this issue.

  • The simplest way to get this to work (and the one we advise) is to move the project to a Linux filesystem. This can be achieved by cloning the repo within the WSL shell in a Linux filesystem.

  • If you have to work in the Windows filesystem, you could configure Vite to poll files to identify changes, as mentioned in this issue and in the Vite documentation. However, this is a resource intensive process so we advise the previous option instead.

  • This issue keeps track of problems encountered by Mathesar developers using Windows for local development.

Adding/Removing packages

If you want to add or remove packages, or basically run any npm action, always do it from within the container. Never do it from your local node setup, since it may modify the package-lock.json in ways we would not want it to.

  1. Connect to the container and open the ui folder:

    docker exec -it mathesar_service_dev /bin/bash
    cd mathesar_ui
  2. Add or remove packages.

    root@c273da65c52d:/code/mathesar_ui$ ls
    Dockerfile  jsconfig.json  package-lock.json  public  vite.config.js
    README.md   node_modules   package.json       src
    
    root@c273da65c52d:/code/mathesar_ui# npm install <package>
    
    root@c273da65c52d:/code/mathesar_ui# npm uninstall <package>

Fixing npm audit failures

Fixing npm audit failures is restricted only to maintainers. If you are facing this, please notify the maintainers on our Matrix channels, or raise an issue on GitHub.

If the audit check on your pull request fails, here are the steps to fix it:

  • If the audit failure indicates that the issues are auto-fixable, the following commands need to be run to fix them:

    npm audit fix
    npm install
    

    Please make sure to run these within the container only. If you are running Mathesar locally, without Docker, make sure you use the same node and npm versions.

  • If the issues are non auto-fixable, identify the packages that are vulnerable.

    • If they are directly used packages, update their versions.
    • If they are dependencies of packages used by us (most common), update the parent packages.
    • Most often, newer parent packages may not have been released yet. In which case, we can use the 'resolutions' field in package.json to force the version of packages. Make sure to only update it to the closest non-vulnerable minor release, in this case.
    • Force resolving dependencies to a particular version should only be done when the vulnerabilities are not false positives. This article by Dan Abramov from the React team, gives a good explanation on why most reported vulnerabilities are false positives.