Skip to content

POC Report: WebComponents

Laurent Lienher edited this page Oct 8, 2021 · 43 revisions

Lit element / HTML

TODO

Integration with AngularJS

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.

Store RXJS

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.

I18n

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

Config

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
    },
  })
);

TypeScript

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

Service migration

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');

Separate build

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.

Component extension

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.

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...

Clone this wiki locally