@startinblox/boilerplate v3.0.6
Startin'blox components repository boilerplate
Introduction
This repository provides a boilerplate for developing Startin'blox web components. It streamlines the creation and deployment of web components, whether used independently or integrated with the Startin'blox ecosystem, including Lit, Startin'blox Core, Startin'blox Store, Startin'blox Router, and Startin'blox Orbit. The primary objective is to enable developers to concentrate on web component logic by abstracting common complexities and providing out-of-the-box functionalities.
Features
This boilerplate includes:
- Localization: Compatibility with platforms like Weblate, powered by
@lit/localize
. - Data Management: A comprehensive wrapper around the Startin'blox Store for simplified property handling (RDF or named).
- Component Awareness: Mechanisms to manage race conditions and inheritance across external components.
- Helper Utilities: Functions for data reactivity, filtering, and sorting.
- Development Tools:
- Storybook: For component documentation and isolated development, reducing the need for a heavy local environment.
- Cypress: For component testing.
- Unpluggin Icons: Access to a vast library of icons from Iconify.
- Lit Integration: Pre-made component classes to accelerate development.
- Vite-powered: Typescript, SASS, PostCSS, Autoprefixer... Focus on your component logic, not the build process.
- BiomeJS: For linting and formatting, preconfigured for Startin'blox JS styling, using the most reasonable one. Use Biome within your preferred editor.
Getting started
Creating a new components repository
To initialize a new component repository from this boilerplate:
- Clone this repository.
Rename the
origin
remote toupstream
:git remote rename origin upstream
Update the
name
,description
, andrepository.url
fields inpackage.json
.- Commit the changes with a
major: Initial commit
message. - Create a new repository on git.startinblox.com/components.
Add the new repository as the
origin
remote:git remote add origin https://git.startinblox.com/components/your-component-name.git
Configure Gitlab settings (branch protection rules, FF merge only, clone instead of fetch).
- Push to
origin master
.
For production usage, components can be served via a CDN like JSDelivr or self-hosted.
<script type="module" src="https://cdn.jsdelivr.net/npm/@startinblox/core@latest/dist/store.js" defer=""></script>
<!--
Or the entire core:
<script type="module" src="https://cdn.jsdelivr.net/npm/@startinblox/core@latest/dist/index.js" defer=""></script>
-->
<script type="module" src="https://cdn.jsdelivr.net/npm/your-component-name@latest" defer=""></script>
<!-- ... -->
<your-component-1 data-src="..."></your-component-1>
<your-component-2>Hello World!</your-component-2>
<!-- etc. -->
Installation
To set up the local development environment with Storybook:
npm install
npm run storybook
Development environment
This boilerplate is designed as a library for multiple web components and intentionally does not include a default index.html
file.
Storybook is the primary development environment for isolated component development and documentation. Cypress is integrated for component testing.
For components intended to be packaged within the broader Startin'blox environment, integrating with a local Orbit instance is recommended.
Updating from the boilerplate
To keep your component repository synchronized with updates from this boilerplate:
- Ensure
upstream
is configured to point to this boilerplate's repository. Pull changes from the boilerplate's
upstream
:git pull upstream master
Resolve any merge conflicts. Changes outside the boilerplate's core code should merge smoothly.
- Push the updates to
origin master
.
Core Concepts
Pre-made component classes
OrbitComponent
: A component designed to display or handle a Resource or Container, optionally extending its functionality for Orbit integration.ObjectsHandler
: A component for rendering an array of objects, typically used within anOrbitComponent
to display a Container.ObjectHandler
: A component for rendering a single object, commonly used within anOrbitComponent
orObjectsHandler
to display a Resource.
Store reactivity
setupCacheInvalidation
: Invalidates the store cache and updates the component when asave
event occurs for resources listed by specified attributes and keywords.setupOnResourceReady
: Invalidates the store cache and updates the component upon aresourceReady
event, independent of attributes.setupOnSaveReset
: Resets form values by calling the_setValue
method (which must be custom-implemented) on asave
event containing specified keywords.
Custom cache invalidation methods can be added:
async _afterAttach() {
this._subscriptions.add(["name-of-the-event", (e) => {
// Logic to execute when `name-of-the-event` occurs
}]);
// Ensure event deduplication before re-subscribing
this._subscribe();
// _afterAttach expects a Promise, you can reject on error
return Promise.resolve();
}
OrbitComponent
default attributes and methods
Attributes:
defaultDataSrc
: (Optional) Used within Lit Tasks to track the original data source when the component'sdataSrc
is rewritten.dataSrc
: (Optional) Specifies the resource to display within a Lit Task.nestedField
: (Optional) Requires manual handling within a Lit Task.uniq
: (Optional) Allows multiple instances of the component within the same DOM.route
: (Optional) Enables router-awareness, preventing rendering/updating when the current route does not match expectations.cherryPickedProperties
: (Required) An array describing the expected RDF or named properties for the component.
Methods:
render
: A Lit method; refer to Lit documentation. Should return a Lit Task ornothing
._afterAttach
: An asynchronous method called after the component is attached to the DOM and fully initialized. Expects a resolved or rejected Promise._navigate(e)
: Facilitates navigation using the router and predefined attributes within arender
method._getProxyValue(dataSrc)
: An asynchronous method typically called within a Lit Task. It retrieves data based oncherryPickedProperties
, is recursive by default, and allowscherryPickedProperties
to be overridden._responseAdaptator(res)
: An asynchronous method called before_getProxyValue
returns its result, allowing for response adaptation (e.g., prefixing aname
property based ontype
).gatekeeper
: A pre-written method for components intended to operate exclusively within Orbit, simplifying gatekeeping logic.
OrbitComponent
inherits methods from ObjectsHandler
.
ObjectsHandler
methods
hasType(type)
: Checks if any object withinthis.objects
shares the requested type, returning a boolean.
ObjectHandler
methods
isType(type)
: Checks if the object shares the requested type, returning a boolean.
Available helpers
The src/helpers
directory contains various utilities:
datas
:dataBuilder
: For creating mock data.filterGenerator
: For seamless object filtering usingfilterObjectByX
methods.sort
: A general-purpose sorting method.
ui
:formatDate
: For date formatting using nativeIntl
methods.lipsum
: For configuring mock word, sentence, or paragraph generation.
utils
:requestNavigation
: A wrapper around the Startin'blox Router for JavaScript-based navigation.uniq
: A unique ID generator.
cherryPickedProperties
explained
The cherryPickedProperties
array within an OrbitComponent
defines the properties expected by the component. Each entry can have up to four attributes:
key
: The original property name or RDF.value
: The name this property will have within the component.cast
: A callback function (e.g.,formatDate
helper) to transform the property's value.expand
: Iftrue
, and the property contains a Resource or Container, it will be recursively expanded using the samecherryPickedProperties
. Exercise caution with circular dependencies;_responseAdaptator
is often more suitable for such cases, allowing_getProxyValue
to be called withrecursive=False
or differentcherryPickedProperties
. Defaults tofalse
.
Example:
cherryPickedProperties: PropertiesPicker[] = [
{ key: "name", value: "name" },
// Using the project property two times: One with a cast to keep its @id, and one expanded giving complete access to its datas
{ key: "project", value: "projectId", cast: (r) => r["@id"] },
// This will expand the `project` property, using the same cherryPickedProperties, so it'll contain any `project.name`, `project.project` and `project.description`
{ key: "project", value: "project", expand: true },
{ key: "description", value: "description" },
// In any case, the `_originalResource`, the `@id`, `@context` and `@type` will be fetched and accessible
];
Development workflow
Localization
This project utilizes @lit/localize
for internationalization.
- Configure
lit-localize.json
, specifically thetargetLocales
key. - Run
npm run locale:extract
to generate initial localization files. - Execute
npm run locale:extract
to update localization files wheneverstr
ormsg
methods from Lit Localize are used. XLIFF files will be generated in thelocales
directory. - Before production deployment, run
npm run locale:build
to generate built localization files.
To change the locale at runtime within an application:
// With Orbit:
window.setLocale.map((setLocale) => setLocale("your-lang-code"));
// Without Orbit:
window.setLocale("your-lang-code");
To retrieve the current locale:
// With Orbit:
window.getLocale.map((locale) => console.log(locale));
// Without Orbit:
console.log(window.getLocale("your-lang-code"));
Testing
# Run all tests
npm run cy:run
# Open Cypress's interface
npm run cy:open
Building for production
# Generate localization files
npm run locale:build
# Generate Storybook documentation
npm run build-storybook
# Build the component repository
npm run build
Orbit integration
Components built with this boilerplate are designed to work seamlessly with Orbit without requiring specific configurations.
To prevent potential race conditions, include import @src/initializer;
at the top of your component's file, before the @customElement
declaration.
Application performance can be optimized by utilizing the gatekeeper
method within your OrbitComponent
's render methods.
Note that without a complete Orbit environment, interactions with external components (e.g., route management, component deduplication, global localization, conditional component display) may be limited.
Best practices
Adhere to general web component usage rules, as outlined by MDN Web Docs or the webcomponents.org guideline.
Tailor web components to their expected application environment. Features like the gatekeeper and localization usage should be standard practice, not exceptions. This approach ensures components are efficient and integrate effectively with the broader application and other components.