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
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 (in our case, for "Fluent Commerce")
`fc.`
- 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`
`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`
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()`
`span`
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`
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 hook inside a filter component to pre-fetch a list of valid type or status value from a retailer or account setting
`useSetting`
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`
`data`
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.
- (full path and field name)
`fc.mutation.order.attribute.value`
- (full path and field type)
`fc.mutation.order.attribute.json`
- (entity type and field name)
`fc.mutation.attribute.value`
- (entity type and field type)
`fc.mutation.attribute.json`
- (field name)
`fc.mutation.value`
- (field type)
`fc.mutation.json`
This allows SDK developers to override the default field implementation either across the board (e.g. all
`string`
`value`
`order attributes`
`FieldRegistry`
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.