-
Notifications
You must be signed in to change notification settings - Fork 86
POC Report: WebComponents
The main addition of this POC us the use of Web Components on top of services. For the needs of templating and code structure, we choose write Web-Components using a framework: Lit. It is a small footprint library.
Lit (historically called lit-element) is build around the standards of Web Components.
This is well supported in modern frameworks or even native JavaScript. Using such design insures us to get some interoperability for the future and keep the existing code functioning with the rapid evolution of the front-end ecosystem. "More standards, the better".
The attributes are a little bit renamed:
ng-on-close-panel="mainCtrl.ngeoAuthActive = $event.detail"
=> Code called, then the close-panel
event is dispatched from the component.
ng-prop-login_info_message="mainCtrl.loginInfoMessage"
=> set the property loginInfoMessage
of the WebComponent with custom object.
Refer to ngOn and ngProp in the AngularJS documentation for full details.
A store, not a state. Mainly based on https://rxjs.dev/api/index/class/BehaviorSubject, a multicasted observable with a default value. But There are other "Subject" possibilities.
Works well and without bad surprise. Easy to access, easy to set. Light to implement.
The code must be simple. One set function should emit one thing.
The integrity of a stored value must be well checked by the store.
RXJS offer a lot of operators to play with Observable (see https://rxmarbles.com). From experience, it's better not to use too much of them (the code becomes quickly hard to follow and to change).
Using observable allows use to remove our old events and centralize the state of the application.
Evaluate i18next and lit-localize
Choice for i18next because with lit-localize we can’t translate text out of the component, and we need to translate some part of the HTML like the page title.
Translate the tag content in the HTML:
<title data-i18n="Alternative Desktop Application">GeoMapFish</title>
Translate a tag attribute in the HTML:
<button data-i18n="[tooltip]Draw and Measure"/>
Translate just a string in the HTML: Not possible
Translate just a string in the JavaScript(WebComponent):
import i18next from 'i18next';
html`${i18next.t("Loading themes, please wait...")}`
Plural in the JavaScript(WebComponent):
import i18next from 'i18next';
const i18nextConf = {count: themes.length};
html`${i18next.t("Loading {{count}} themes, please wait...", i18nextConf)}`
But this can't be used because it breaks the English translating who actually is in the sources... Same issue for context: https://www.i18next.com/translation-function/context
The configuration will be dispatched by RXJS, and be fully typed, see src/state/config.ts
.
Usage:
import configuration, {Configuration} from 'ngeo/store/config';
this.subscriptions_.push(
configuration.getConfig().subscribe({
next: (configuration: Configuration) => {
// Use the configuration
},
})
);
We choose TypeScript to write the new component as it's largely accepted by lit-element
, lit-element
itself is written in TypeScript.
Other motivation is that we get some limitations in typing in the JSdoc.
Translate JavaScrypt to TypeScript, see: https://github.com/camptocamp/ngeo/blob/poc_2.7/docs/codeshift.md
The principle is to have a class instantiate (a single time) at the end of the file. Here is the canvas of a example service:
export class MyService {
property: string;
constructor() {
this.property = 'something';
}
someFunction(param: string): string {
this.property = param;
return this.property;
}
}
const ngeoMyService = new MyService();
export default ngeoMyService;
As it is instantiate in the service file itself then exported in a ECMAScript module, we can now import and use it in other files as follow:
import ngeoMyService from 'path/file';
ngeoMyService.someFunction('hello world');
We provide the following element throw a Singleton available in window
, then we can access to the in a separate build.
In this way, by default the HTML page will be compiled by the ngeo build (not modifiable in the project).
The states are exposed to be able to use the out of the script (currently: user, config, OpenLayer map).
The goal of the simple application mode is to simplify the migration.
The general idea for the simple application is to completely separate the project and the GeoMapFish code.
For the backend by creating a separate container. For the frontend by providing in the config container
a JavaScript and a CSS, in the addition of what's already in the vars.yaml
in particular the CSS variable.
See: https://github.com/camptocamp/demo_geomapfish/pull/255
The main things that's not possible in this architecture is the authentication (LDAP, ...), and extending the GeoMapFish data model (but it's difficult to maintain). For having a separate class that didn't have to be visible in the admin interface, it can be done in the additional container. But for that, we didn't need to have an advance application we can just extend the image and just override some files.
For the authentication, we can imagine to support SAML 2.0 SSO, and for LDAP we should investigate if it's possible to have a generic implementation.
TODO
Example / StoryBook
We will use StoryBook for the new examples. StoryBook is more than providing the example, with that, we can also do test and review with Chromatic It make a little documentation of a web component. It will also a base for the tests.
Finally, Cypress is implemented. It has a good end-to-end tests as good unit tests possibilities, embeded ui, good cli... and gives a good feeling (Even if the doc is not so clear if you don't use React).
Testing models and services are mandatory.
As we use Storybook, we don't need the (beta) extension to test components, we can use "the standard way to test" on Storybook pages. Using Chromatic, we have a good check on examples. Therefore, testing our component with Cypress is not so useful. But If the evolution of the examples make them more complex, like including a real authentication for the auth component, we should test that with Cypress, via the example. Otherwise, we should use Cypress for more end-to-end tests directly on the apps.
The alternatives would have been Jest, Web Test Runner...