@icanbwell/composite
v1.1316.2
Published
@icanbwell/composite. repo version: 0.1597.2
Readme
Composite
Composite embeddable bwell component. The composite can be embedded in either a web or React Native context.
Web
Integration
The composite web component can be integrated via either script tag or npm module
Script
If using the script tag, you'll need to add the below to your web page. This will pull in the required scripts and define the bwell composite web component.
<script src="https://embeddables.prod.icanbwell.com/composite/<VERSION_NUMBER>/loader/index.js"></script>Dynamic loader (no version pinning)
For integrations that should always run the composite version selected by Client Hub (rather than a version baked into the script tag at integration time), use the stable dynamic-loader URL instead:
<script src="https://embeddables.prod.icanbwell.com/composite/dynamic/loader/index.js"></script>The dynamic loader resolves the target version per-request from Client Hub at runtime, so updates roll out to all integrators without a code change on their side. Use the versioned URL above if you need to pin a specific build.
Module
Alternatively, if you use NPM, the bwell composite can be added to your project with the following command.
npm install @icanbwell/composite@<VERSION_NUMBER>Once that's done, make sure to do a direct import of the module somewhere within your appliction.
import '@icanbwell/composite';Usage
In both cases above, replace <VERSION_NUMBER> with the desired version number.
The composite can then be integrated into your web UI as a traditional HTML tag.
<bwell-composite />Initialization
The embeddable is initialized in two steps:
- Initialize the app experience with the client key (
<CLIENT_KEY>in the code below). Think of this as a public SDK/API key. - Initialize the user context with the user token (
<USER_TOKEN>in the code below).
async function onInit() {
await bwell.init(<CLIENT_KEY>)
const userInfo = await bwell.setUserToken(<USER_TOKEN>)
}Updating Document Title
The bwell composite introduces a global bwell object off of the window. Via
this global object, events can be subscribed and unsubscribed. One of these
events is pagetransition and can be used to update your web application's
document title to best reflect the current screen being shown within the
embeddable. An example of such an approach can be seen below.
bwell.on(
'pagetransition',
(pageInfo) => (document.title = pageInfo.pageTitle || ''),
);Embeddable events
Screens inside the composite tell the host application that something happened
through the embeddable event. This is the only screen-to-host channel: the
composite's event map has data (this section) and error. Other events such
as pagetransition and initialized are lifecycle events the composite emits
itself.
bwell.on('embeddable', (event) => {
// event = { id, name, type: 'data', payload }
});Do not key on event.id or event.name. They identify the embeddable instance
and change between builds. Key on event.payload.
Schema for new events. Events added from now on use EmbedEvent (see
libs/shared/src/types/embedEvent.types.ts):
{ event: string, data?: JSON }eventis what happened, namedon*by convention.dataholds the details and is only present when there are some.- Do not use
nameoridas payload fields. The emitter merges the embeddable's ownnameinto every payload and a payloadnameoverrides it, which changes what that field means for the host.
Host rules. The channel is shared, so a listener must filter on the event
name it wants and ignore every other payload, including event names and data
values it does not recognise.
Current events
| event | data | Emitted when | Source |
| ------------ | -------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------- |
| onSubmit | { action: 'dataConnectionComplete' } | The user presses "I'm done connecting" | libs/shared useOnSubmitEmbeddableEvent |
| onNavigate | { url: string } | A screen leaves for a page that cannot render in an iframe (provider sign-in) | libs/shared useOnNavigateEmbeddableEvent |
Legacy payload shapes. These predate the schema and have external consumers,
so they keep their shape. They are not a pattern to copy. The detail wrappers
come from the CustomEvent migration (UP-4517).
| Payload | Emitted by |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| { onNagivateToExternalHash: <href> } (the misspelling is part of the contract) | IAL2 instructions, callback and desktop-to-mobile screens (4 call sites) |
| { onProfileDataReceivedAfterIAL2VerificationInAccountCreation: { detail: { profile } } } | IAL2 legacy intermediate state screen |
| { detail: 'legacyMessagePreferencesSuccessCallback' } | Message preferences screen |
Renaming any event name, action or legacy key is a breaking change for hosts. Where a host is maintained in another repository, change both together.
React Native
The bwell composite also supports integration into React Native via a react
native scoped export. Before integrating, installation requires adding some peer
dependencies of the @icanbwell/composite module.
Installation
Install module plus peer dependencies (only required they are not already installed).
npm install \
react \
react-native \
react-native-webview \
@react-native-async-storage/async-storageOptional Native Dependencies
The following native dependencies are optional and may be required dependent on the features that are enabled for your specific client.
npm install \
react-native-biometrics \
react-native-permissions \
@react-native-camera-roll/camera-roll \
@react-native-camera-roll/camera-roll \
@react-native-documents/picker \
@react-native-community/geolocation \
react-native-printInstall Pods
npx pod-installUsage
// Native Module(s) to inject
import storage from '@react-native-async-storage/async-storage';
import { SafeAreaView } from 'react-native';
import { Composite } from '@icanbwell/composite/native';
export default function App() {
return (
<SafeAreaView style={{ flex: 1 }}>
<Composite
clientKey='<CLIENT_KEY>'
userToken='<USER_TOKEN>'
nativeModules={{
storage,
}}
/>
</SafeAreaView>
);
}Properties
clientKey
required
type: string
Client key.
userToken
required
type: string
User token for SSO.
initialPath (deprecated; prefer path below)
type: string
Optional property that tells the composite what page to start on. Use this to deeplink into the embeddable application.
path
type:
type Path = {
value: string;
onResetValue: () => void;
};Optional property that tells the composite what page to initially render. Use
this to have the Composite render a specific screen. Pass the intended path in
via the value property (for example value: '/intended/path'). Use the
onResetValue callback to clear the value that is passed to path (set it to
an empty string/undefined). onResetValue is called immediately after the
passed in path value is rendered within the Composite component. This is
required in order to accommodate passing in the same path value sequentially
and having the underlying component recognize there's been a change. It is
possible the user navigates elsewhere in between sequential path value
updates. NOTE: Make sure to omit the hash segment of a path if it exists. For
example: #/intended/path would be /intended/path.
onEvent
type:
type OnAuthSuccessEventData = {
user: {
id: string;
};
};
type ErrorEventData = {
name: string;
message: string;
errorCode: string;
};
type EventData =
| {
key: 'onauthsuccess';
data: OnAuthSuccessEventData;
}
| {
key: 'error';
data: ErrorEventData;
};
type OnEventCallback = (event: EventData) => void;Callback property that allows for listening to internal events of the embeddable.
nativeModules
required
type:
import type storage from '@react-native-async-storage/async-storage';
import type * as cameraRoll from '@react-native-camera-roll/camera-roll';
import type geolocation from '@react-native-community/geolocation';
import type biometrics from 'react-native-biometrics';
import type documentPicker from 'react-native-document-picker';
import type * as fileSystem from 'react-native-fs';
import type permissions from 'react-native-permissions';
import type print from 'react-native-print';
type NativeModuleMap = {
biometrics?: typeof biometrics;
permissions?: typeof permissions;
cameraRoll?: typeof cameraRoll;
fileSystem?: typeof fileSystem;
documentPicker?: typeof documentPicker;
geolocation?: typeof geolocation;
print?: typeof print;
storage?: typeof storage;
};Property used to inject native dependencies into the composite. At minimum
storage is required.
