Fluent Commerce Logo
Docs
Sign In

Registering SDK Components

Topic

Author:

Fluent Commerce

Changed on:

6 Sept 2024

Overview

Developers can add new components to OMX via the registries.

There are three available:

  • Component Registry - for standard in-page components
  • Field Registry - for interactive form field components
  • Template Registry - for adding new template helpers

Registering SDK Components

Author:

Fluent Commerce

Changed on:

6 Sept 2024

Overview

Developers can add new components to OMX via the registries.

There are three available:

  • Component Registry - for standard in-page components
  • Field Registry - for interactive form field components
  • Template Registry - for adding new template helpers

Key points

  • Component Registry
  • Field Registry
  • Template Registry

Component Registry

Components are the building blocks of an OMX web app.

SDK developers can build and register new components with the Component Registry, which allows them to be referenced in a web app manifest just like out-of-the-box components.

Naming a component

Component names don't need to follow any strict rules, but in general should be as simple as possible, while leaving room to expand later on.

The typical Fluent convention is a period-separated hierarchy of:

  • account namespace of the author (
    `fc.`
     in our case, for "Fluent Commerce")
  • the type of component (e.g. 
    `card.`
    )
  • and finally the variant (e.g. 
    `map`
    )

So in all, a card that shows a map would be called 

`fc.card.map`
.

Sometimes you might have variants of variants, in which case it's fine to add levels as we have with 

`fc.button.print.download`
 and 
`fc.button.print.inline`
.

Registering a component

New components can be installed via the ComponentRegistry.

After writing a custom component, rather than rendering it you simply give it a name in the ComponentRegistry...

1const HelloComponent: FC<HelloComponentProps> = ({label}) => {
2    return (<h2>{label}</h2>);
3}
4ComponentRegistry.register(['ACME.HelloWorld'], HelloComponent);

Language: java

Name: Example

Description:

[Warning: empty required content area]

...and it becomes reference-able in a manifest document:

1routes: [
2  { path:'hello', component:'Page', decendants:[
3    { component:'ACME.HelloWorld', props:{ 'label':'Hello World!' } }
4  ]}
5]

Language: json

Name: Example

Description:

[Warning: empty required content area]

The ComponentRegistry also provide a custom extension of PropTypes that allows the framework to capture more information about the new component to do things like validating a manifest and (later) providing a visual app builder experience. The Typescript types for React's prop-types provides an InferProps interface to avoid needing to duplicate prop configuration, e.g.

1const myProps = {
2    a: PropTypes.string(),
3    b: PropTypes.number().isRequired,
4    c: PropTypes.arrayOf.object(ColourPicker).isRequired,
5};
6const HelloProps: FC<InferProps<typeof myProps>> = ({b /* inferred as 'number' */, c /* inferred as custom type 'ColourPicker[]' */}) => {
7    return <div />;
8};
9BasicComponent.displayName = 'HelloProps';
10BasicComponent.propTypes = myProps;

Language: java

Name: Example

Description:

[Warning: empty required content area]

Retrieving a component from the registry

To use a component from the registry you can retrieve it using the provided 

`get`
 method.

1const Comp = ComponentRegistry.get('acme.card.custom');
2return Comp ? (<Comp data={data} width={6} />) : null;

Language: java

Name: Example

Description:

[Warning: empty required content area]

You can also ask for a set of component names and the Component Registry will return the first match. This is especially useful for allowing other SDK developers to provide implementations for a part of your component.

For example, in the filter component we use 

`ComponentRegistry.get()`
 to attempt to load a series of fallbacks to represent the filter label. The order is most specific to least, allowing developers to override at the level they need to. If none are overridden, a basic 
`span`
 is returned.

1const Comp = ComponentRegistry.get([
2    `fc.filter.${entityType}.${fieldName}.label`,
3    `fc.filter.${entityType}.${fieldType}.label`,
4    `fc.filter.${fieldName}.label`,
5    `fc.filter.${fieldType}.label`,
6], { noLog:true });
7
8return Comp ? (<Comp data={filter} />) : (<span>{filter.value.toString()}</span>);

Language: java

Name: Example

Description:

[Warning: empty required content area]

Field Registry

New form fields are installed via the Field Registry.

This allows developers to add new field components to UX forms generated either from Rubix user actions (to match the data types expected by custom Rules) or GraphQL mutations (to improve the UX of previously purely auto-generated forms).

1FieldRegistry.register(['string', 'text', 'input'], TextInput);

Language: java

Name: Example

Description:

[Warning: empty required content area]

While rendering a form, the Form component will check for field implementations to match the user action attribute type, or GQL field type, and use that component in that position in the form.

The form component is responsible for implementing the necessary interface to make it work within a form, which are enforced by the Typescript interfaces the developer must inherit from to register the component in the first place. See the TSDocs for more details.

Extending Filters

The standard 

`fc.list`
 component has a filter bar driven by the GraphQL schema.

Using the Field Registry, you can implement custom UX for the configuration and display of these filters.

Adding a new filter type

New filter types can be registered via the FieldRegistry. The field component should behave the same way as a normal form field (i.e. render a control and produce values via onChange), but it will be shown inside a modal when a matching filter is selected from the filters dropdown.

Common use-cases include:

  • implementing a UX for a GraphQL type that doesn't currently exist
  • tailoring the UX of an existing filter for a special case
  • using the 
    `useSetting`
     hook inside a filter component to pre-fetch a list of valid type or status value from a retailer or account setting

The UI component that's chosen to represent a query parameter follows a fallback pattern based on the field type and name in the GraphQL schema, from most specific to least. So in the case of an Order status filter, the following field aliases will be checked in order:

  • `fc.filter.order.status`
  • `fc.filter.order.string`
  • `fc.filter.status`
  • `fc.filter.string`

Using the OMX SDK, simply register a field component by the right name to add or replace a filter.

Custom filter labels

When a filter produces a non-string result it might not look right in the filter chip that is produced on submit.

For these cases, you can use the ComponentRegistry to provide a view component that will be embedded within the chip, following the same fallback logic:

  • `fc.filter.order.status.label`
  • `fc.filter.order.string.label`
  • `fc.filter.status.label`
  • `fc.filter.string.label`

Filter label components will receive the value of the filter (the one produced by the 

`onChange`
 event in the field) as the 
`data`
 prop, and should be registered with the ComponentRegistry:

1ComponentRegistry.register(['fc.filter.order.status.label'], OrderStatusWithIcon, {category:'filter'});

Language: java

Name: Example

Description:

[Warning: empty required content area]

Extending Mutations

When a form is generated for a GraphQL mutation, Mystique chooses fields based on a similar specificity-based fallback strategy.

  • `fc.mutation.order.attribute.value`
     (full path and field name)
  • `fc.mutation.order.attribute.json`
     (full path and field type)
  • `fc.mutation.attribute.value`
     (entity type and field name)
  • `fc.mutation.attribute.json`
     (entity type and field type)
  • `fc.mutation.value`
     (field name)
  • `fc.mutation.json`
     (field type)

This allows SDK developers to override the default field implementation either across the board (e.g. all 

`string`
 fields) down to very specifically (e.g. the 
`value`
 field of 
`order attributes`
) by registering a new field with the 
`FieldRegistry`
 at the desired level.

Template Registry

Many OMX components use template strings to allow customisation of components via the manifest.

1{ "value": "{{order.status}}" } // = "BOOKED"

Language: java

Name: Example

Description:

[Warning: empty required content area]

Template helpers allow manifest configurers to transform data in different ways, like changing uppercase text to mixed case, or converting an absolute timestamp to a relative one.

1{ "value": "{{dateRelative order.createdOn}}" } // = "5 days ago"

Language: java

Name: Example

Description:

[Warning: empty required content area]

The Template Registry allows developers to add to the standard list of helpers available.

1TemplateRegistry.register(['uppercase', 'toUpperCase'], function(str) {
2    return str.toUpperCase();
3});

Language: java

Name: Example

Description:

[Warning: empty required content area]

Helper functions should be in the format defined by the Handlebars framework documentation.

Fluent Commerce

Fluent Commerce

Copyright © 2024 Fluent Retail Pty Ltd (trading as Fluent Commerce). All rights reserved. No materials on this docs.fluentcommerce.com site may be used in any way and/or for any purpose without prior written authorisation from Fluent Commerce. Current customers and partners shall use these materials strictly in accordance with the terms and conditions of their written agreements with Fluent Commerce or its affiliates.

Fluent Logo