The Webcomponent Manifest
Any webcomponent is or aims to be:
- an HTML tag
- a CSS encapsulated environment
- a JS business logic unit
As HTML tag, a custom webcomponent has attributes and properties. Moreover a pair attribute and property can be coupled by reflecting changes: a change on the former is mirrored on the latter, and viceversa.
Basics
The Configurator layout section queries the webcomponents to discover their properties/attributes using a static getter promise called a Manifest.
You can use the JSON schema to check your components manifests.
The __manifest static getter must return a JavaScript object that has a key type which must be object (to be JSON schema compatible) and a map of properties.
const manifest = {
type: 'object',
properties: {
// list of properties
}
}
A custom button might look like:
// my-button.ts
import { LitElement } from 'lit'
class MyButton extends LitElement {
static get __manifest() {
return import('./manifest').then(({default: manifest}) => manifest)
}
@property() hidden?: boolean
}
and thus will instruct the Configurator preview section with the following manifest
// manifest.ts
import type { Manifest } from '@micro-lc/compose-toolkit'
const manifest = {
type: 'object',
properties: {
hidden: {
type: 'boolean'
}
}
}
export default manifest
In the outlined example, the Configurator layout section will provide its configuration form with a boolean toggle for the hidden property.
Types can be almost anything that JSON schema provides:
booleanstringnumberobjectarray- a
oneOfarray of primitives
For each of these, the Configurator layout will provide a consistent form input to edit the property.
Trivially, primitive types boolean, string, and number are simple to edit from a form input.
Complex properties such as objects and arrays are also handled in a no-code fashion so far the manifest is precise in describing their nested properties.
The most basic visualization for an object without a schema is an IDE-like editor, with basic JSON validation capabilities. Likewise an array has a no-code item selector, which again, without schema will spawn an IDE-like editor for each one of its items.
The owner/developer of custom webcomponents can enforce no-code configurability by nesting the component manifest.
For instance:
// my-button.ts
import { LitElement } from 'lit'
interface Action {
type: 'http-request' | 'file-upload'
url: string
}
class MyButton extends LitElement {
static get __manifest() {
return import('./manifest').then(({default: manifest}) => manifest)
}
@property() action?: Action
}
can be described by the following manifest
// manifest.ts
import type { Manifest } from '@micro-lc/compose-toolkit'
const manifest: Manifest = {
type: 'object',
properties: {
action: {
type: 'object',
properties: {
type: {type: 'string', enum: ['http-request', 'file-upload']},
url: {type: 'string'}
}
}
}
}
export default manifest
Despite the action being an object, the Configurator layout section will spawn a modal (which can have potentially infinite levels of nesting) to configure type as a string with at most 2 fixed values and url as a string.
Mia's Configuration Advanced
The Webcomponent manifest is a superset of a compliant draft-07 JSON schema. The Configurator guarantees to display a no-code comfortable version of each property.
Beside this specification, Configurator can enforce some extra logic using a special property, available to any webcomponent property or nested property: __mia_configuration.
Let's consider a custom button
// my-button.ts
import { LitElement } from 'lit'
class MyButton extends LitElement {
static get __manifest() {
return import('./manifest').then(({default: manifest}) => manifest)
}
@property() hidden?: boolean
}
with manifest
// manifest.ts
import type { Manifest } from '@micro-lc/compose-toolkit'
const manifest = {
type: 'object',
properties: {
hidden: {
type: 'boolean'
__mia_configuration: {
// mia specific configurations
}
}
}
}
export default manifest
The __mia_configuration object targets the following use cases:
deprecated▶️ mark a property for deprecationlabel▶️ The label to show on the form input/editor used to configure the given property. This label supports i18n by supporting both astringand a dictionary where the key is the language 2 letters code and the translation.description▶️ helpful when the property configuration is complicated or nested: provides a description tooltip to help the user.docLink▶️ replaces thedescriptiontooltip with a link to external documentationoneOfGuard▶️ see refoneOfDefault▶️ allows to select adefaultbranch of a JSON schemaoneOfsection.priority▶️ groups properties in 3 levels inside the Configurator layout section form. Helpful when multiple personas are interacting with the configuration by highlighting those properties which are most likely to be tuned.attribute▶️ instructs the Configurator layout section that the property is mirrored by an HTML attribute (NOT USED ATM).schema-hint▶️ Configurator layout section knows some often used property schemas and provides labels to select them instead of writing the whole property JSON schema.shared-key▶️ Configurator allows to share JSON schema definitions by resolving in-place their references.enumLabels▶️ provides the capability to i18n-ify string enums
Summarizing the __mia_configuration property must comply with the following type:
/**
* This interface was referenced by `MiaSchema`'s JSON-Schema
* via the `definition` "__mia_configuration".
*/
export interface MiaConfiguration {
deprecated?:
| boolean
| {
since?: string
description?: LocalizedText
[k: string]: unknown
}
label?: LocalizedText
description?: LocalizedText
docLink?: string
oneOfGuard?: string
oneOfDefault?: number
priority?: "high" | "medium" | "low"
attribute?: boolean | string
"schema-hint"?:
| "localized-text"
| "dynamic-icon"
| "on-off-toggle"
| "color"
| "event"
| "mia/endpoints"
| "mia/endpoints/crud"
| "mia/endpoints/crud-and-generate-data-schema"
| "micro-lc/applications"
"shared-key"?: "back-kit/data-schema" | string
enumLabels?: {
[k: string]: LocalizedText
}
}
The oneOfGuard key
Suppose your property is a JSON oneOf an there's a guard key which allows to distinguish non-overlapping types. For instance:
// manifest.ts
import type { Manifest } from '@micro-lc/compose-toolkit'
const manifest = {
type: 'object',
properties: {
action: {
type: 'object',
oneOf: [
{
properties: {
type: {const: 'http-post'},
url: {type: 'string'},
payload: {type: 'string'}
}
},
{
properties: {
type: {const: 'event'},
payload: {type: 'object'}
}
}
]
__mia_configuration: {
oneOfGuard: 'type'
}
}
}
}
export default manifest
By using oneOfGuard set to type Configurator layout section is able to provide a no-code configuration to the property action by requesting the user to select a type between http-post and event and then the rest of the object.
If the user selects http-post then 2 string input will appear in order to configure url and payload, otherwise an IDE-like editor will allow to type directly the payload property since no schema was provided.
The schema-hint key
Configurator provides some types that are well known and often used in order to avoid writing down a repeating JSON schema multiple times.
- A
localized-texthints for a string or a dictionary of translations - A
dynamic-iconis a property that complies with the@micro-lc/iconicicon interface - An
on-off-toggleis a number, either0and1which will be rendered as a boolean toggle - A
colorspawns a color picker - An
eventexpects an object with at least 2 properties:- a
labelwhich must be astring - and a
payloadwhich must be anobject
- a
- A
mia/endpointsspawns a selection with a fixed list of options which are Mia Platform's Console currently available http endpoints. - A
mia/endpoints/crudis the list ofmia/endpointscoming from aCRUD Servicemicroservice. - A
mia/endpoints/data-sourceis the list ofmia/endpointscoming from aCRUD Servicemicroservice, a Mongo View, a Fast Data projection, or a Fast Data Single View. - A
micro-lc/applicationsis the list of currently configured applications in the Configurator initial section.
The shared-key key
JSON schema supports referencing of property definitions. Despite not being a fixed pattern there's a recommendation for draft-07 which suggests to use the key definitions at the first level of your JSON configuration. In the most recent drafts it will be substituted by the $defs keyword.
The following example shows how it works:
{
"definitions": {
"propertyUsedMultipleTimes": {
// property schema
}
},
"type": "object",
"properties": {
"first": {
"$ref": "#/definitions/propertyUsedMultipleTimes"
},
"second": {
"$ref": "#/definitions/propertyUsedMultipleTimes"
}
}
}
The shared-key property suggests to the Configurator how to group properties using JSON definitions (refer to the dedicated documentation for more information).