This folder container the ML Lab web app which is build with JavaScript and React. It communicates with the ML Lab backend (via the contaxy API) and provides the overall UI structure of ML Lab. The ML Lab components are integrated into this UI via iframes.
During development, it can be very helpful to run the web app locally without building the production bundle and putting it into the ML Lab backend docker image.
To do this, execute yarn run start-debug
and open http://localhost:3000 in your browser.
Note that this debug server will automatically reload if the web application is changed.
To test the integration with the ML Lab backend, this debug web app can be connected to any running ML Lab instance.
By default, the ML Lab backend is expected to run at http://localhost:30010/ which is the default when starting ML Lab with docker compose.
In case you want to connect to a different ML Lab instance, adjust the package.json file accordingly.
This project uses React as the main framework. Components should be written as React Hooks instead of the old class-style wherever possible. See Section Components Guide for some more guidelines. For component styling this project uses the styled-components library (see Section Component Styling). As the default design we use Material and, hence, the Material-UI Library.
For code styling, eslint is used for linting and prettier is used for formatting (see this page for learning about linting vs. formatting). The linting rules are listed in .eslintrc.json. The configuration for prettier can be found in .prettierrc. The configurations adhere to Airbnb's JavaScript style guide. For CSS styling, stylelint is used for which the configuration can be found in the .stylelintrc.json file.
Docs should be written in JSDoc format, though overall we advocate self-explanatory code over comments.
It was bootstrapped with Create React App (yarn create react-app react-webapp
) and, thus, uses the pre-configured webpack and babel build tools.
The used package manager for installing packages is yarn.
When contributing code, please try to make the code following the project's codestyle setup as described in the Development summary section. We recommend to use plugins in your IDE to already keep an eye on the style while developing.
After executing yarn install
as they are defined in the ./package.json, you can run style checking like following:
- Formatting:
yarn run prettier <path-to-file>
: this command formats the file and saves it.
- Linting (shows the problems but does not fix them):
python build.py --check
runs all checks as defined in the build.py or manually:yarn run lint:js
: checks the JavaScript files (files ending with.js
/.jsx
).yarn run lint:css
: checks the.css
files.
Sometimes, you have to do something that is not allowed by the linting rules. For example, property spreading in React makes sense sometimes. In this example, you can disable the linter for the specific line by adding // eslint-disable-line react/jsx-props-no-spreading
. Instead of disabling a rule globally, this forces you to think about your decision instead of allowing slopiness by default.
Execute python build.py --make
to build the app. Under the hood, it uses yarn build
for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Add some information here about the structure of the app, what the entrypoint is, and what tools are used.
The project uses follwing structure:
src/
: Contains the source code of the web app, for example the React components. Development usually happens in here. The/src/index.jsx
is the entry point into the application.public/
: Contains the default public resources that must be available such as theindex.html
. It also contains thelocales/
directory for translation files other than English; see the Internationalization Section for more information.build/
: The generated directory that contains the bundled web app. This folder is ignored bygit
and should not be pushednode_modules/
: The installed packages. This folder is ignored bygit
and should not be pushed.
Inside of the src/
folder, there should be following structure (inspired by this blog post):
components/
: Capsulated components appear here. Artifacts that are specific to a component should be packaged together, for examplecomponents/dashboard/
should have all components and styles and images that are just relevant for thedashboard
component (domain-based structure).pages/
: Reflects the routes of the application and is composed of different components. Each component in this folder should have its own route. If a page has specific components that are only used within that page (especially those which are composed of more general components), you can add them in a sub-directory here instead of thecomponents/
directory.utils/
: Functionality that is generally relevant for your application and could be used in multiple places.app/
: Contains app essentials such as routes and the store in form ofstore.js
when using Redux.services/
: Contains JavaScript functions and clients that manage API integrations.assets/
: Should contain style and images and other resources that are generally relevant for your application and not only for a specific component.stories/
: Contains general Storybook files such as introduction and assets that are not directly linked to a specific component. It should not contain the actual component stories.
If files belong together, for example a component has a related .stories.jsx
and .test.jsx
file, put them into an extra folder. Also, when needed add an index.js
file to a component directory to make it easier to import as shown here.
Add Storybook files (see the Storybook Section) next to the components they describe. Story files must follow the <component-name>.stories.jsx
name pattern. For example, if you have a component src/dashboard/Dashboard.jsx
, put the stories file under src/dashboard/Dashboard.stories.jsx
.
Add test files next to the code they are testing (see the Testing Section). Test files must follow the <component-name>.test.jsx
name pattern.
You can find an example of how styled-components can be used here. With the stated motivation of the library, the style of a component should be bundled with the component and, usually, lies in the same file.
Inside of functional components, we prefer arrow functions const foo = () => {}
over function declarations function foo(){}
.
In class components, arrow functions should be defined outside of the render
function to avoid any performance problems. For functional components, define non-component specific functions outside of the component function or use useMemo
/useCallback
functions to avoid re-creation of functions (see this example and the React documentation).
When your component uses components that render often and always with the same props, consider using a memoized version of those components via React.memo
to avoid unnecessary re-renders (check out this blogpost).
We use the react-i18next (GitHub) library for translations. The translation file for languages other than English is loaded dynamically upon need via the i18next-http-backend library. The translation files are placed under ./public/locales. Add a new folder there for extending the languages or edit the languages there to add new translations. The English translation file is pre-bundled with the app which is why it is placed under the src/
directory.
You can find an example of how to use the i18next
in ./src/pages/App/App.jsx.
When we want to document our components further than using JSDoc, we use Storybook. In the Stories directory you can find some example stories (this project was initialized via npx sb init
).
The Storybook server can be started via yarn run storybook
.
This project uses Jest and react-testing-library for testing as it comes pre-bundled with Create React App.
The official Jest documentation recommends to add at least smoke tests to make sure that components render (source), so we go along with this recommendation ;)
Use test()
instead of it's alias it()
(source). See the App.test.jsx file for an example.
To run the tests, execute yarn test
. To see test coverage, execute yarn test -- --coverage
(source).