The TypeScript App that runs in the browser.
This is the documentation for InseeFrLab/onyxia -> web/.
src/ui
contains the React application, it's the UI of the app.
src/core
contains the π§ of the app.
Nothing in the src/core
directory should relate to React. A concept like react hooks for example is out of scope for the src/core directory.
src/core
should never import anything from src/ui
, even types.
It should be possible for example to port onyxia-web to Vue.js or React Native without changing anything to the src/core
directory.
The goal of src/core
is to expose an API that serves the UI.
The API exposed should be reactive. We should not expose to the UI functions that returns promises, instead, the functions we expose should update states and the UI should react to these states updates.
Whenever we need to interact with the infrastructure we define a port in src/core/port
. A port is only a type definition. In our case the infrastructure is: the Keycloak server, the Vault server, the Minio server and a Kubernetes API (Onyxia-API).
In src/core/adapters
are the implementations of the ports. For each port we should have at least two implementations, a dummy and a real one. It enabled the app to still run, be it in degraded mode, if one piece of the infrastructure is missing. Say we donβt have a Vault server we should still be able to launch containers.
In src/lib/usecases
we expose APIs for the UI to consume.
Let's say we want to create a new page in onyxia-web where users can type in a repo name and get the current number of stars the repo has on GitHub.
UPDATE: This video remain relevant but please not that the clean archi setup have been considerably improved in latest releases. A dedicated repo have been created to explain it in detail.
Main take-way is that app
have been renamed ui
and lib
have been renamed core
.
You might wonder why some values, instead of being redux state, are returned by thunks functions.
For example, it might seem more natural to do:
Instead of what we actually do, which is:
However the rule is to never store as a redux state, values that are not susceptible to change. Redux states are values that we observe, any redux state changes should trigger a re-render of the React components that uses them. Conversely, there is no need to observe a value that will never change. We can get it once and never again, get it in a callback or wherever.
But, you may object, users do login and logout, isUserLoggedIn
is not a constant!
Actually, from the standpoint of the web app, it is. When a user that isn't authenticated click on the login button, it is being redirected away. When he returns to the app everything is reloaded from scratch.
Now let's say we want the search to be restricted to a given GitHub organization. (Example: InseeFrLab.) The GitHub organization should be specified as an environment variable by the person in charge of deploying Onyxia. e.g.:
If no ORG_NAME
is provided by the administrator, the app should always show 999 stars for any repo name queried.
Currently users can save their GitHub Personal access token in their Onyxia account but not yet their GitLab token. Let's see how we would implement that.
The easy action to take when the user selects another project is to simply reload the page (windows.location.reload()
). We want to avoid doing this to enable what we call "hot projet swiping":
To implement this behavior you have to leverage the evtAction middleware from clean-redux. It enabled to register functions to be run when certain actions are dispatched.
Unlike the other video, the following one is voiced. Find the relevant code here.
Technologies at play in Onyxia-web
To find your way in Onyxia, the best approach is to start by getting a surface-level understanding of the libraries that are leveraged in the project.
Modules marked by π are our own.
We are fully committed on keeping everything type safe. If you are a seasoned developer but not fully comfortable with TypeScript yet a good way to get you quickly up to speed is to go through the of the official website.
You can skip anything related to class
we don't do OOP in the project.
We try, whenever we see an opportunity for it, to publish as standalone NPM module chunks of the code we write for Onyxia-web. It help keep the complexity in check. We use TS-CI as a starter for everything we publish on NPM.
If you want to test some changes made to onyxia-ui in onyxia-web before releasing a new version of onyxia-ui to NPM you can link locally onyxia-ui in onyxia-web.
Now you can make changes in ~/github/onyxia/ui/
and see the live updates.
If you want to install/update some dependencies, you must remove the node_modules, do you updates, then link again.
The library we use for styling.
Rules of thumbs when it comes to styling:
Onyxia is mostly used on desktop computer screens. It's not worth the effort to create a fully flege responsive design for the UI. screen-scaler enables us to design for a sigle canonical screen size. The library take charge of scaling/shrinking the image. depending on the real size of the screen. It also asks to rotate the screen when the app is rendered in protrait mode.
To launch Storybook locally run the following command:
We need to be able to do:
Then, somehow, access OIDC_URL
in the code like process.env["OIDC_URL"]
.
It enables to run onyxia-web again a specific infrastructure while keeping the app docker image generic.
It's a collection general purpose react hooks. Let's document the few use cases you absolutely need to understand:
It's a build tool that enables to implement the login and register pages that users see when they are redirected to Keycloak for authentication.
For internalization and translation.
The framework used to implement strict separation of concern betwen the UI and the Core and high modularity of the code.
A lot of the things we do is powered under the hood by EVT. You don't need to know EVT to work on onyxia-web however, in order to demystify the parts of the codes that involve it, here are the key ideas to take away:
We also heavily rely on . It's a collection of utilities that help write cleaner TypeScript code. It is crutial to understand at least , , and to be able to contribute on the codebase.
Anything contained in the directory.
The UI toolkit used in the project, you can find the setup of in onyxia-web here: .
is fully compatible with .
Onyxia-UI offers but you can also use components in the project, their aspect will automatically be adapted to blend in with the theme.
We currently offers builtin support for :
France:
Ultraviolet:
Verdant:
Onyxia (default):
You can also .
The fonts are loaded in the . It's important to keep it that way for Keycloakify.
To release a new version of . You just need to bump the and push. will automate publish .
Every component should acceptprop it should always .
A component should not size or position itself. It should always be the responsibility of the parent component to do it. In other words, you should never have height
, width
, top
, left
, right
, bottom
or margin
in of your components.
You should never have a color or a dimension hardcoded elsewhere than in . Use theme.spacing()
(, , ) and .
It enables us to test the graphical components in isolation. .
In theory it shouldn't be possible, onyxia-web is an SPA, it is just static JS/CSS/HTML. If we want to bundle values in the code, we should have to recompile. But this is where comes into play.
Checkout :
All the accepted environment variables are defined here: . They are all prefixed with REACT_APP_
to be compatible . Default values are defined in this file.
Only in development (yarn start
) is also loaded and have priority over .env
Then, in the code the variable can be accessed .
Please try not to access the environment variable to liberally through out the code. In principle they should only be accessed . We try to keep things as much as possible.
For the sake of performance we enforce that every component be wrapped into . It makes that a component only re-render if one of their prop has changed.
However if you use inline functions or as callbacks props your components will re-render every time anyway:
We always use for callback props. And for callback prop in lists.
It is very handy to be able to get the height and the width of components dynamically. It prevents from having to hardcode dimension when we donβt need to. For that we use ``
If the app is being run on Keycloak the isn't undefined
and it means shat we should render the login/register pages.
If you want to test, uncomment and run yarn start
. You can also test the login pages in a local keycloak container by running yarn keycloak
. All the instructions will be printed on the console.
The keycloak-theme.jar
file is automatically and by the CI.
The library we use for routing. It's like but type safe.
We plane to move to Vite when will support it.
The project is a non-ejected using (you can find the template repo that was used as a base for this project).
We use instead of the default react-scripts
to be able to use custom Webpack plugins without having to eject the App. The custom webpack plugins that we use are defined here . Currently we only one we use is .
Anything contained in the directory.
EVT is an event management library (like is).
If we need to perform particular actions when a value gets changed, we use.
We use Ctx
to detaches event handlers when we no longer need them. (See line 108 on )
In React, we use the hook to work with DOM events.