Skip to main content
Version: 13.x (Current)

Card

<bk-card></bk-card>

bk-card

The Card read-only visualizer for objects, arrays of objects, images.

It is not suited for editing. That role should be delegated to another component, such as the Dynamic Form Card component.

The Card is made by blocks and is recursive, which means that cards can be embedded inside cards.

The Card is made of 3 HTML5 tag

  1. header
  2. main
  3. footer

If either of these keys is absent from the Card configuration, it does not appear in the Card shadow DOM and it does not clutter its internal structure.

How to configure

To configure the Card, the cardSchema property is needed:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"header": {
"title": {"en": "User", "it": "Utente"}
},
"main": {
"dataSchema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"role": {"type": "string", "enum": ["Admin", "Developer", "Guest"]},
"dateOfBirth": {"type": "string", "format": "date"},
}
}
},
"footer": {
"subtitle": {"en": "User details", "it": "Dettagli utente"}
}
}
}
}

cardSchema control the layout of the Card, and accepts an object with keys: header, main, footer.

Roles

The Card can be assigned a property role, which define a color code

  • default - white background and no border
  • info - white background, grey border and grey font color
  • success - white background, primary-color border and primary-color font color
  • error - red background, red border and red main title.

Dynamic Context

Several properties of the Table allow dynamic configurations. By default, such properties are parsed with handlebars, injecting the the following data as context:

  • data: the whole datum associated to the Card
  • arraySource: array data source associated to the Card
  • buttonSource: object data source associated to the Card
  • args: an array contianing
    • the datum of a specific field (depending on the property being interpolated)
    • the whole datum associated to the Card

All the above parameters can be dynamically interpolated into properties that allow dynamic configurations through handlebars.

Layout

Property cardSchema is available to control how each section of the Card. cardSchema accepts an object with keys: header, main, footer.

The simplest card layout is an informative card, which is obtained by combining header and footer.

Header supports:

  • title (h1)
  • subtitle (h2)
  • badge
  • icon

Each one of them is optional and the layout is left-float icon + title + badge and a second line with the subtitle.

header-example

Title, subtitle and badge can be internationalized using LocalizedText which is either a string or an object with language support.

Icons are dynamically imported to reduce bundle size. Icons are not downloaded unless actually used.

Available icons are @ant-design/icons or any fontawesome public solid or regular icon.

{
"cardSchema": {
"header": {
"icon": "fas fa-building",
"title": {"en": "Conversation", "it": "Conversazione"},
"badge": {"en": "Awaiting", "it": "In Attesa"}
}
}
}

Footer encapsulates actions and can mount an unlimited number of buttons. Its configuration supports

  • title
  • subtitle
  • subsubtitle
  • buttons

The former three are similar to the header properties.

buttons key instead takes either an object or an array of objects that can contain the key tag.

When tag is not specified it defaults to button. Footer will render the given tag and it will apply any other property of the corresponding configuration object as vanilla JS property on an HTML5 tag.

A user-agent browser default button can be achieved as:

{
"children": {
"Click me!"
}
}

but more often a Button component is useful.

{
"footer": {
"buttons": {
"tag": "bk-button",
"content": {
"it": "Scarica PDF autorizzazione",
"en": "Download authorization PDF"
},
"iconId": "DownloadOutlined",
"type": "link"
}
}
}

Multiple buttons can be specified, and support two layouts: horizontal and vertical (default), which can be controlled with footer property buttonsLayout.

Footer supports dynamic configurations via handlebars notation. The context provided by the Card can be utilized inside handlebars.

{
"role": "info",
"cardSchema": {
"footer": {
"subtitle": "{{data.name}}"
}
}
}

The Card supports all dynamic configurations helpers, with the limitation that template-configMap pair can be applied to keys at the first level of the footer object, and to keys at the first level of footer.buttons.

Main

Main is the core part of the Card and the most widely customizable section. It roughly accepts configuration for 3 different modes (all of which can be combined at will in a single card).

  • object-mode: displays data from an object data source
  • array-mode: displays data from an array data source
  • image-mode: display an image from a URL
  • recursive mode: displays embedded Cards

The Card cannot operate under multiple modes at the same time. For instance, whenever the recursive-mode field is specified, it is not possible to view data from a data source besides the nested Cards. The only exception is object and array modes. It is indeed possible to visualize both object and array fields of a data item.

Visualize data

object-mode and array-mode can be accessed by specifying a data-schema to the main section of the Card.

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
}
}
}
}

object-mode and array-mode allow to describe a data item by displaying its fields accordingly with the provided data-schema.

In this mode, whenever data are received (upon listening to display-data events), the first entry is visualized inside the card main section according with the data-schema. lookup-data events are also handled to resolve lookup data.

Mount web-components in Card body

When provided with a data-schema, the Card supports visualization-options.

In particular, the Card allows to insert a generic web-component into its body in place of a field by defining, in the data-schema description of the corresponding field, all the web-components properties inside the visualization-options.

Web-components that are mounted inside the body of the Card through visualization-options support dynamic configurations with partial Card context (keys arraySource and objectSource are not supported):

  • context key args.[0] can be used to access the value of the field being rendered as a web-component,
  • while args.[1] or data provide access to the object representation of the whole datum.
Visualize an image

image-mode can be accessed through setting key img in the main section of the card.

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"main": {
"img": "https://source.unsplash.com/random/300x200"
}
}
}
}

The value of the img field can be furthermore set to a dynamic value using handlebars.

Visualize recursive Cards

Providing a cards key inside the main section of the Card enables recursive-mode. cards should contain an array of cardSchema objects.

recursive is a mode that embeds cards into cards.

{
"cardSchema": {
"main": {
"cards": [
{
"header": {...},
"main": {...}
},
{
"header": {...},
"main": {
"cards": [
{
"header": {...}
}
]
}
}
]
}
}
}

Examples

Example: Text-only Card

A Card that should be composed by text-only content can be achieved by placing the text content in the footer of the card:

{
"tag": "bk-card",
"properties": {
"role": "info",
"cardSchema": {
"footer": {
"subtitle": "Text"
}
}
}
}

A title can be added through the header:

{
"tag": "bk-card",
"properties": {
"role": "info",
"cardSchema": {
"header": {
"title": {"en": "Info", "it": "Info"}
},
"footer": {
"subtitle": "Text"
}
}
}
}

A Card configured like:

{
"tag": "bk-card",
"properties": {
"footer": {
"buttons": {
"tag": "bk-button",
"content": {
"it": "Scarica PDF autorizzazione",
"en": "Download authorization PDF"
},
"iconId": "DownloadOutlined",
"type": "link"
}
}
}
}

mounts a Button in the footer.

Multiple buttons can be specified, and support two layouts: horizontal and vertical (default).

{
"role": "info",
"cardSchema": {
"footer": {
"buttonsLayout": "horizontal",
"buttons": [
{
"tag": "bk-button",
"content": {
"it": "Scarica PDF autorizzazione",
"en": "Download authorization PDF"
},
"iconId": "DownloadOutlined",
"type": "link"
},
{
"tag": "bk-button",
"content": {
"it": "Fai un'altra richiesta",
"en": "Make a new request"
}
}
]
}
}
}

The following configuration for the Card:

{
"tag": "bk-card",
"properties": {
"role": "info",
"cardSchema": {
"footer": {
"subtitle": "{{data.name}}"
}
}
}
}

with a datum such as:

{
"name": "Teresa",
"surname": "Fattorini"
}

displays a Card having footer text equal to "Teresa".

The following configuration for the Card:

{
"role": "info",
"cardSchema": {
"footer": {
"buttons": {
"tag": "bk-button",
"content": "Details",
"action": {
"type": "event",
"config": {
"events": {
"label": "selected-data",
"payload": "{{rawObject data}}"
}
}
}
}
}
}
}

with a datum such as:

{
"name": "Teresa",
"surname": "Fattorini"
}

displays a Card whose footer mounts a Button that, upon click, emits a selected-data event with payload equal to the Card datum.

{
"label": "selected-data",
"payload": {
"name": "Teresa",
"surname": "Fattorini",
}
}
info

rawObject is a helper keyword that prevents "data" from being stringified in the payload of the event.

If is furthermore possible to provide dynamic configurations via a template-configMap pair pair. In such cases, the resulting value is taken from the configMap using the template as key (or $default, if the template does not match any configMap key).

For instance, the following configuration for the Card:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"footer": {
"buttons": {
"tag": "bk-button",
"content": "Cancel",
"disabled": {
"template": "{{data.status}}",
"configMap": {
"active": false,
"$deafult": true
}
}
}
}
}
}
}

with datum:

{
"_id": "product-1",
"status": "pending"
}

resolves to:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"footer": {
"buttons": {
"tag": "bk-button",
"disabled": false
}
}
}
}
}

thus displaying a Card whose footer mounts a disabled Button.

On the other hand, a datum like:

{
"_id": "product-2",
"status": "actinve"
}

resolves to:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"footer": {
"buttons": {
"tag": "bk-button",
"disabled": true
}
}
}
}
}

thus displaying a Card whose footer mounts an active Button.

Example: Handle nested dynamic configuration

A template-configMap interface for dynamic configurations can only be applied to keys at the first level of the footer object, and to keys at the first level of the buttons inside the footer.

For instance, the following is NOT a valid configuration:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"footer": {
"buttons": {
"tag": "bk-button",
"action": {
"type": "event",
"config": {
"events": {
"label": { // <- nested too deeply, `label` is not resolved
"template": "{{data.status}}",
"configMap": {
"active": "selected-data",
"$deafult": "add-new"
}
},
"payload": {}
}
}
}
}
}
}
}
}

since template-configMap pair is not applied to a first-level key of buttons.

An analogous but correct configuration for the above example is:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"footer": {
"buttons": {
"tag": "bk-button",
"action": { // <- first level of nesting, `action` is resolved
"template": "{{data.status}}",
"configMap": {
"active": {
"type": "event",
"config": {
"events": {
"label": "selected-data",
"payload": {}
}
}
},
"$default": {
"type": "event",
"config": {
"events": {
"label": "add-new",
"payload": {}
}
}
}
}
}
}
}
}
}
}

Example: Display data item

The following configuration encompasses both object and array mode of the the Card component.

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"main": {
"dataSchema": {
"type": "object",
"properties": {
"status": {
"label": "Status",
"type": "string"
},
"liv1": {
"type": "array",
"label": "First Floor",
"dataSchema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"surname": {
"type": "string"
}
}
}
},
"notification": {
"label": "Notifications",
"type": "object",
"format": "localized-text"
},
"riderId": {
"label": "Rider",
"type": "string",
"format": "lookup"
},
"customerId": {
"label": "Customer",
"type": "string",
"format": "lookup"
}
}
}
}
}
}
}

The Card visualizes:

  • a status string field
  • lookups riderId, customerId
  • a nested array liv1
  • a composite object notification

Example: Display an image

The following configuration renders a Card that operates under image-mode.

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"main": {
"img": "https://source.unsplash.com/random/300x200"
}
}
}
}

The Card visualizes an image retrieved from a specific URL.

Example: Recursive Cards

recursive is a mode that embeds cards into cards. The very same structure described here can be nested by using

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"main": {
"cards": [
{
"header": {
"title": "Card 1"
},
"footer": {
"subTitle": "Information 1"
}
},
{
"header": {
"title": "Card 2"
},
"footer": {
"subTitle": "Information 2"
}
}
]
}
}
}
}

Example: Mount a web-component dynamically

The following configuration renders a Card mounting a div component in place of the standard view for a field:

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"main": {
"dataSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"visualizationOptions": {
"tag": "div",
"properties": {
"textContent": "The name is: {{args.[0]}}"
}
}
}
}
}
}
}
}
}

args.[0] refers to the value of the corresponding field (in this case, name). args.[1] is also available, referencing the full data of the card.

Example: Forward data to nested cards

Specifying a data-schema is useful in recursive-mode, to inject data to the nested Cards configurations.

Whenever a Card is provided with a data-schema, it handles display-data events by storing internally the first received data item.

Specifying cards property in the Card main section prevents such data-item from being visualized, but still grants access to it through handlebars syntax in dynamic-configurations.

{
"tag": "bk-card",
"properties": {
"cardSchema": {
"header": {
"title": "Images"
},
"main": {
"dataSchema": {
"type": "object",
"properties": {
"images": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"cards": [
{
"cardSchema": {
"main": {
"img": "{{arraySource.images.[0]}}"
}
}
},
{
"cardSchema": {
"main": {
"img": "{{arraySource.images.[1]}}"
}
}
}
]
}
}
}
}

As deducible from the data-schema, the Card stores an array of strings in field images. Embedded cards are responsible for displaying images using the entries from the array.

API

Properties & Attributes

propertyattributetypedefaultdescription
arraySource-ArraySource{}property to inject array source from an external source. This is overridden by the display-data event
cardSchema-CardSchema{}schema that describes the card layout, role and type/style
containerStyle (deprecated)-CSSProperties-React-like CSS properties to decorate card container
customMessageOnAbsentDatum-LocalizedText-when datum reaches the card without an expected value, a custom message can replace it
customMessageOnAbsentLookup-LocalizedText-overrides lookup value in case it is not resolved
objectSource-ObjectSource{}property to inject object source from an external source. This is overridden by the display-data event
role-"default"|"info"|"success"|"error""default"card role to select color schema

ArraySource


type ArraySource = {
[property: string]: {
[field: string] string | TaggableCustom
}[]
}

type TaggableCustom = {
value: string
tag: string
properties: Record<string, any>
data: Record<string, any>
}

ObjectSource


type ArraySource = {
[property: string]: {
[field: string] string | TaggableCustom
}
}

type TaggableCustom = {
value: string
tag: string
properties: Record<string, any>
data: Record<string, any>
}

CardSchema

type CardSchema = {
header?: {
icon?: string
title?: LocalizedText
badge?: LocalizedText
subtitle?: LocalizedText
}
main?: {
dataSchema?: DataSchema
cards?: CardSchema | CardSchema[]
img?: string | URL
}
footer?: {
title?: LocalizedText
subtitle?: LocalizedText
subsubtitle?: LocalizedText
buttons?: TaggableCustom | TaggableCustom[]
buttonsLayout?: 'horizontal' | 'vertical'
}
}

type TaggableCustom = {
tag: string
} & {
[x: property]: any
}

where LocalizedText is either a string or an object mapping language acronyms to strings

Listens to

eventaction
display-datadisplays the first item of the payload according to the card main dataSchema
lookup-dataupdates data with resolved lookups

Emits

eventaction
configurable eventfooter buttons or custom web-components can be used to emit custom events