The component is defined as a npm package, so it contains a package.json
file but it's set as private
because we don't plan to distribute the single components individually.
Still the @ecl/
namespace should be used consistently throughout all packages for consistency, but only one package will be distributed with the name of @ecl-webcomponents
and with all the needed filesystem on the ECL npm organisation.
Examples:
-
@ecl/webcomponents-menu
- name of the package -
/src/components/ecl-menu/ecl-menu.tsx
- Component in typescript -
/src/components/ecl-menu/src/styles/ecl-menu-ec.scss
- Source sass file for EC -
/src/components/ecl-menu/src/styles/ecl-menu-eu.scss
- Source sass file for EU
There will be only one css file built for EC and EU, stencil is capable of dynamically loading a css file based on a prop, which is called in ECL webcomponent "theme", and it can have ec
or eu
as values.
The configuration for this is in /src/globals/global.ts
This file system can be automatically generated (with additional files) by running:
yarn generate ecl-{component}
yarn generate:template ecl-{component}
The generated files are currently empty, except those created by stencil
styleUrls: {
ec: './build/styles/ecl-accordion-ec.css',
eu: './build/styles/ecl-accordion-eu.css',
}
This is how the multiple css can be defined in the component decorator, ec is used by default but if the value of the theme
attribute is EU the proper style will be loaded.
This is only possible in stencil by defining the styles as:
scoped: true
It is "simulating" shadowDom, scoping the css by adding selectors generated using the Prop to define the different themes to the html generated and the css injected.
This can be tricky do the approach we have about the composability, a nested component style might not work due the mismatch of selectors, the component will generate a sc-ecl-{component}-{theme} class to the the html defined by the component, but a nested component will have a different class which is scoping that component, this is usually solvable by manually adding the:
sc-ecl-{component}-{theme}
class to the nested custom element with a style-class attribute.
An ECL webcomponent is usually styled by compiling the "theme" scss file of EC and EU together with the sources sass file belonging to the ECL vanilla component.
@use '@ecl/theme-ec';
@use '@ecl/vanilla-component-blockquote/blockquote-ec';
This is usually enough, since the inner components potentially used will bring their own style, but there are issues due to the markup differences and the order in which these styles will be loaded, so that the style of a nested component will normally prevail on the component styles, which is problematic. It is then acceptable to fix potential issues with some additional css code to be placed in the source scss files of the web-component.
The components needing javascript are using the vanilla js from the ECL package,
- define a
ecl-builder scripts
script in the component'spackage.json
- add a scripts key as an array in the
ecl-builder.config.js
file, like this:
scripts: [
{
entry: path.resolve(nodeModules, '@ecl/vanilla-component-accordion/accordion.js'),
dest: path.resolve(outputFolder, 'scripts/ecl-accordion-vanilla.js'),
options: {
banner,
moduleName: 'ECL',
includePaths,
sourceMap: false,
},
},
];
Then in the component, define a @Prop eclScript: boolean = false,
prop and use that to determine whether to load the js.
At the moment this property is always defined as false
, so it's opt-in, this could change in the future.
if (this.eclScript) {
const src = getAssetPath('./build/scripts/ecl-accordion-vanilla.js');
if (document.querySelector(`script[src="${src}"]`)) {
document.querySelector(`script[src="${src}"]`).remove();
}
const script = document.createElement('script');
script.src = src;
script.onload = () => {
const accordion = new ECL.Accordion(this.el);
accordion.init();
};
document.body.appendChild(script);
}
This ensures that the script tag is created only if requested and if already present it will be replaced, the 'ECL' global object needs to be defined in the component, like this:
declare const ECL: any;
if we have javascript in a component we also need to import the stencil component Element
and define that as:
@Element() el: HTMLElement;
right above the Props.
We only have for the moment getClass()
, that we use like this, styleClass is a Prop
that we define for each component, can be used in the custom element as an attribute style-class
.
getClass(): string {
return [
`ecl-accordion`,
this.styleClass
].join(' ');
}
Stencil defines additional methods in the lifecycle of a web-component defined by the Wep apis for Custom Elements:
componentWillLoad()
componentDidLoad()
componentWillRender()
componentDidRender()
componentShouldUpdate()
componentWillUpdate()
componentDidUpdate()
Please refer to the stencil documentation for a better understanding of this lifecycle, but at the moment only one method is used, and it's componentDidRender()
which seems the most reliable for the operations we need to perform, loading a js and initialising a component and loading the utils css where potentially needed.