@servicetitan/docs-uikit 31.0.0 → 31.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/launchdarkly-service.mdx +24 -0
- package/docs/startup/start.mdx +1 -1
- package/docs/startup/startup.mdx +6 -2
- package/docs/web-components/headless-loader.mdx +67 -0
- package/docs/web-components/loader.mdx +235 -0
- package/docs/web-components/use-mfe-data-context.mdx +17 -0
- package/docs/web-components/use-mfe-metadata-context.mdx +20 -0
- package/docs/web-components/web-components.mdx +7 -265
- package/package.json +2 -2
|
@@ -140,6 +140,30 @@ export const HostApp: FC = () => {
|
|
|
140
140
|
};
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
### LDService
|
|
144
|
+
|
|
145
|
+
The `LDService` interface enables [headless bundles](./web-components/headless-loader#headlesscallback) to create and reuse client connections for LaunchDarkly projects.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
export interface LDService {
|
|
149
|
+
getClient: (config: ClientConfig) => LDClient;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface ClientConfig {
|
|
153
|
+
clientSideID: string;
|
|
154
|
+
context?: LDContext;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### getClient
|
|
159
|
+
|
|
160
|
+
Returns an `LDClient` instance configured with the following parameters.
|
|
161
|
+
|
|
162
|
+
| Name | Type | Description |
|
|
163
|
+
| :------------- | :------- | :------------------------------------------------------------------ |
|
|
164
|
+
| `clientSideID` | `string` | The LaunchDarkly client-side ID. |
|
|
165
|
+
| `context` | `object` | (Optional) Context for targeting rules. Defaults to anonymous user. |
|
|
166
|
+
|
|
143
167
|
### FeatureFlagStore
|
|
144
168
|
|
|
145
169
|
`LDProvider` automatically provides a `FeatureFlagStore` that allows you to access
|
package/docs/startup/start.mdx
CHANGED
|
@@ -15,4 +15,4 @@ Runs package in the development mode. Applications will be hosted on sequential
|
|
|
15
15
|
|
|
16
16
|
The `start` command executes the same steps as the `build` command, then watches for changes and automatically reruns each step as needed.
|
|
17
17
|
|
|
18
|
-
See [Build Steps](/docs/startup/build#build-steps).
|
|
18
|
+
See [Build Steps](/docs/frontend/uikit/startup/build#build-steps).
|
package/docs/startup/startup.mdx
CHANGED
|
@@ -189,7 +189,7 @@ Use package-specific `tsconfig.json` and `tsconfig.build.json` files to set or o
|
|
|
189
189
|
}
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
-
`tsconfig.build.json` takes precedence over `tsconfig.json`. Use `tsconfig.build.json` that extends from `tsconfig.json` to exclude tests and mocks from type checking during development. E.g.,
|
|
192
|
+
`tsconfig.build.json` takes precedence over `tsconfig.json`. Use `tsconfig.build.json` that extends from `tsconfig.json` to exclude tests, stories, and mocks from type checking during development. E.g.,
|
|
193
193
|
|
|
194
194
|
:::caution
|
|
195
195
|
If your `tsconfig.json` configures project references they must be duplicated in `tsconfig.build.json`.
|
|
@@ -200,7 +200,7 @@ See [references are not inherited in tsconfig.json](https://github.com/microsoft
|
|
|
200
200
|
```json title="tsconfig.build.json"
|
|
201
201
|
{
|
|
202
202
|
"extends": "./tsconfig.json",
|
|
203
|
-
"exclude": ["**/__tests__/*", "**/*.test.*", "**/__mocks__/*"],
|
|
203
|
+
"exclude": ["**/__tests__/*", "**/*.test.*", "**/__mocks__/*", "**/*.stories.*"],
|
|
204
204
|
"references": [
|
|
205
205
|
{ "path": "../feature-a" },
|
|
206
206
|
{ "path": "../feature-b" },
|
|
@@ -284,6 +284,10 @@ Or you can use relative path to a file that exports an object with detailed conf
|
|
|
284
284
|
|
|
285
285
|
See [MFE configuration](/docs/frontend/micro-frontends/#mfe-configuration) for detailed instructions on configuring MFE applications.
|
|
286
286
|
|
|
287
|
+
###### Headless bundle
|
|
288
|
+
|
|
289
|
+
If a file named `headless.ts` is present in the source folder of an MFE package, `startup` automatically generates a headless bundle alongside the `light` and `full` bundles. See [web-components documentation](../web-components/headless-loader) for information on the headless bundle.
|
|
290
|
+
|
|
287
291
|
#### Webpack
|
|
288
292
|
|
|
289
293
|
Use `cli.webpack` to set or override Webpack options.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: HeadlessLoader
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The headless bundle is a tiny bundle that allows a host to preload configuration or setup information without having to load the entire MFE. The headless bundle does not utilize React, and cannot use any modules that require React.
|
|
6
|
+
|
|
7
|
+
## Generating headless bundle
|
|
8
|
+
|
|
9
|
+
To generate a headless bundle, create a file named `headless.ts` in the root directory of the MFE's source folder. When this file is present, startup generates a `headless` bundle alongside the `light` and `full` bundles. Only what is imported in `headless.ts` will be included in the bundle.
|
|
10
|
+
|
|
11
|
+
`headless.ts` should contain exported functions named `connectedCallback` and/or `disconnectedCallback`.
|
|
12
|
+
|
|
13
|
+
### `connectedCallback`
|
|
14
|
+
|
|
15
|
+
`connectedCallback` will be called when the bundle is loaded in the host. It should be typed as `HeadlessCallback<T>`.
|
|
16
|
+
|
|
17
|
+
```ts title="src/headless.ts"
|
|
18
|
+
import type { HeadlessCallback } from '@servicetitan/web-components';
|
|
19
|
+
export const connectedCallback: HeadlessCallback<ExampleData> = ({ mfeData, eventBus }) => {
|
|
20
|
+
doSomethingWithMfeData(mfeData);
|
|
21
|
+
eventBus?.emit('example-module:status', { status: 'connected', timestamp: Date.now() });
|
|
22
|
+
};
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### `disconnectedCallback`
|
|
27
|
+
|
|
28
|
+
`disconnectedCallback` will be called when the headless MFE is unmounted. It should be typed as `HeadlessCallback<T>`.
|
|
29
|
+
|
|
30
|
+
```ts title="src/headless.ts"
|
|
31
|
+
import type { HeadlessCallback } from '@servicetitan/web-components';
|
|
32
|
+
export const disconnectedCallback: HeadlessCallback = ({ eventBus }) => {
|
|
33
|
+
eventBus?.emit('example-module:status', { status: 'disconnected', timestamp: Date.now() });
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### `HeadlessCallback<T>`
|
|
38
|
+
|
|
39
|
+
The `HeadlessCallback<T>` type is a function that receives an object with the below properties:
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Description |
|
|
42
|
+
| ------------ | ----------------- | ---------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `ldService` | `LDService` | The [LaunchDarkly LDService instance](../launchdarkly-service#ldservice) passed from the Host. |
|
|
44
|
+
| `logService` | `Log` | The [Log instance](../log-service#log) passed from the Host. |
|
|
45
|
+
| `eventBus` | `EventBus` | The [EventBus instance](./event-bus) passed from the Host. |
|
|
46
|
+
| `mfeData` | `Serializable<T>` | The [data passed from the host into `HeadlessLoader`](#headless-loader-props), typed as `T`. |
|
|
47
|
+
|
|
48
|
+
## HeadlessLoader
|
|
49
|
+
|
|
50
|
+
Use the `HeadlessLoader` component to load this bundle from within a host application.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { HeadlessLoader } from '@servicetitan/web-components';
|
|
54
|
+
|
|
55
|
+
export const Foo = () => {
|
|
56
|
+
return <HeadlessLoader src="https://unpkg.servicetitan.com/{package_name}@{semver_range}" />;
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Props {#headless-loader-props}
|
|
61
|
+
|
|
62
|
+
| Name | Description |
|
|
63
|
+
| :------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| `src` | url for the MFE's package |
|
|
65
|
+
| `fallbackSrc` | optional alternative url for the MFE's package (if request for src returns error) |
|
|
66
|
+
| `data` | additional data passed to an MFE as an object, where values are serializable, and preferably memoized ([see Loader](./loader#passing-data-from-host-to-mfe)) |
|
|
67
|
+
| `cache` | optional cache strategy for the MFE [(see Loader)](./loader#cache-strategy). Defaults to `-1`. |
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Loader
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
import Tabs from '@theme/Tabs';
|
|
6
|
+
import TabItem from '@theme/TabItem';
|
|
7
|
+
|
|
8
|
+
## Loader
|
|
9
|
+
|
|
10
|
+
The `Loader` component is used to load an MFE into a host application. See [Microfrontends (MFEs)](/docs/frontend/micro-frontends) for more information about how MFEs are injected.
|
|
11
|
+
|
|
12
|
+
### Usage
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Loader } from '@servicetitan/web-components';
|
|
16
|
+
|
|
17
|
+
export const Foo = () => {
|
|
18
|
+
return <Loader src="https://unpkg.servicetitan.com/{package_name}@{semver_range}" />;
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Props
|
|
23
|
+
|
|
24
|
+
| Name | Description |
|
|
25
|
+
| :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `src` | url for the MFE's package |
|
|
27
|
+
| `fallbackSrc` | optional alternative url for the MFE's package (if request for src returns error) |
|
|
28
|
+
| `data` | additional data passed to an MFE as an object, where values are serializable, and preferably memoized ([see below](#passing-data-from-host-to-mfe)) |
|
|
29
|
+
| `basename` | prefix for all MFE URLs, you should inject it by the `BASENAME_TOKEN` and pass to the appropriate router property |
|
|
30
|
+
| `loadingFallback` | optional ReactNode to render when the MFE is loading |
|
|
31
|
+
| `loadingFallbackDelayed` | controls whether to render the loading fallback immediately, or after a short delay [(see below)](#fallback-delay) |
|
|
32
|
+
| `errorFallback` | optional ReactNode to render when the MFE fails to load |
|
|
33
|
+
| `className` | additional CSS classes for the MFE web component element |
|
|
34
|
+
| `cache` | optional cache strategy for the MFE [(see below)](#cache-strategy). Defaults to `-1`. |
|
|
35
|
+
|
|
36
|
+
### Loading fallback delay {#fallback-delay}
|
|
37
|
+
|
|
38
|
+
To prevent the loading fallback from flickering on then off when an MFE loads quickly, `Loader` does not render the fallback immediately. It waits a moment (currently 200ms) then renders the fallback only if the MFE hasn't finished loading. Set `loadingFallbackDelayed` to `false` to always render the fallback, regardless how quickly the MFE loads.
|
|
39
|
+
|
|
40
|
+
### Cache strategy {#cache-strategy}
|
|
41
|
+
|
|
42
|
+
When an MFE is loaded, `Loader` fetches the version and bundle data from the `src` (or `fallbackSrc`) endpoint and stores the information in a cache. Then, if the MFE is unloaded and subsequently reloaded, it reuses the cached information. Use `cache` to control whether or how long `Loader` caches an MFE's data:
|
|
43
|
+
|
|
44
|
+
- `true`: cache the data for the standard interval (currently 15 minutes).
|
|
45
|
+
- `false`: do not cache the data; (re)fetch it every time the MFE is loaded
|
|
46
|
+
- `-1`: cache the data indefinitely (i.e., for the duration of the user's session). This is the default value.
|
|
47
|
+
- _<number>_: cache the data for the specified _<number>_ of milliseconds
|
|
48
|
+
|
|
49
|
+
### Passing data from Host to MFE
|
|
50
|
+
|
|
51
|
+
Use `data` to pass data from your host application to the MFE. The data should be an object where keys are strings and the values are any serializable data.
|
|
52
|
+
|
|
53
|
+
To avoid performance issues with re-rendering and re-serializing the value, we recommend memoizing the `data` object passed to `Loader`.
|
|
54
|
+
|
|
55
|
+
**Reminder:** Don't casually share data between the host and MFEs. When possible, MFEs should load the data they need instead of relying on the host to pass it.
|
|
56
|
+
|
|
57
|
+
:::note
|
|
58
|
+
Before version v22.12.0 of `@servicetitan/web-components` package, the `data` prop only supported data of type `Record<string, string>`
|
|
59
|
+
:::
|
|
60
|
+
|
|
61
|
+
```tsx title="Host Application"
|
|
62
|
+
import { useMemo } from 'react';
|
|
63
|
+
import { Loader } from '@servicetitan/web-components';
|
|
64
|
+
...
|
|
65
|
+
export const HostApp = () => {
|
|
66
|
+
...
|
|
67
|
+
const data = useMemo(() => {
|
|
68
|
+
foo: ['one', 'two', 'three'],
|
|
69
|
+
bar: dependency,
|
|
70
|
+
}, [dependency]);
|
|
71
|
+
return (
|
|
72
|
+
...
|
|
73
|
+
<Loader
|
|
74
|
+
src="https://unpkg.servicetitan.com/{package_name}@{semver_range}"
|
|
75
|
+
data={data}
|
|
76
|
+
/>
|
|
77
|
+
...
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Data can be accessed within the MFE using props.
|
|
83
|
+
|
|
84
|
+
```tsx title="MFE Application"
|
|
85
|
+
interface MFEAppData {
|
|
86
|
+
foo: string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const MFEApp = (props: MFEAppData) => {
|
|
90
|
+
const { foo } = props;
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Data must be serializable, due to the nature of how it is passed through a web component to reach your MFE component. This means that you cannot pass functions or other non-serializable data types.
|
|
95
|
+
|
|
96
|
+
```tsx title="Host Application"
|
|
97
|
+
...
|
|
98
|
+
<Loader data={{ date: new Date("2020-05-12T23:50:21.817Z") }} />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```tsx title="MFE Application"
|
|
102
|
+
...
|
|
103
|
+
export const MFEApp = (props: MyType) => {
|
|
104
|
+
props.date; // '2020-05-12T23:50:21.817Z'
|
|
105
|
+
};
|
|
106
|
+
// RESULTS IN A STRING, NOT A DATE OBJECT
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
As of version v22.12.0 of `@servicetitan/web-components`, if data passed into the `data` prop of `Loader` changes during runtime, the data will re-render within your MFE component where appropriate. Previous versions would re-mounting the entire MFE application when data changed.
|
|
110
|
+
|
|
111
|
+
### Using EventBus to exchanges messages
|
|
112
|
+
|
|
113
|
+
Use [EventBus](./event-bus) to exchange messages between the host and MFEs.
|
|
114
|
+
When an EventBus is provided via `EVENT_BUS_TOKEN`, `Loader` automatically passes it through to MFEs.
|
|
115
|
+
For example,
|
|
116
|
+
|
|
117
|
+
<Tabs
|
|
118
|
+
defaultValue="host"
|
|
119
|
+
values={[
|
|
120
|
+
{ label: 'Host', value: 'host' },
|
|
121
|
+
{ label: 'MFE', value: 'mfe'},
|
|
122
|
+
{ label: 'Types', value: 'types' },
|
|
123
|
+
]}
|
|
124
|
+
>
|
|
125
|
+
<TabItem value="types">
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { EventBus, typedEventBusToken } from '@servicetitan/web-components';
|
|
129
|
+
|
|
130
|
+
export enum Events {
|
|
131
|
+
example = 'example:event',
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ExampleEvents {
|
|
135
|
+
[Events.example]: undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type ExampleEventBus = EventBus<ExampleEvents>;
|
|
139
|
+
|
|
140
|
+
export const EXAMPLE_EVENT_BUS_TOKEN = typedEventBusToken<ExampleEvents>();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
</TabItem>
|
|
144
|
+
<TabItem value="host">
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { provide, useDependencies } from '@servicetitan/react-ioc';
|
|
148
|
+
import { EVENT_BUS_TOKEN, EventBus, Loader } from '@servicetitan/web-components';
|
|
149
|
+
import { FC, useCallback, useEffect } from 'react';
|
|
150
|
+
import { Events, EXAMPLE_EVENT_BUS_TOKEN } from './types';
|
|
151
|
+
|
|
152
|
+
const mfeUrl = 'https://unpkg.servicetitan.com/@servicetitan/examples';
|
|
153
|
+
|
|
154
|
+
export const App: FC = provide({
|
|
155
|
+
singletons: [{ provide: EVENT_BUS_TOKEN, useValue: new EventBus() }],
|
|
156
|
+
})(() => {
|
|
157
|
+
const [eventBus] = useDependencies(EXAMPLE_EVENT_BUS_TOKEN);
|
|
158
|
+
const handleExampleEvent = useCallback(() => {
|
|
159
|
+
/* Handle event */
|
|
160
|
+
}, []);
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
eventBus.on(Events.example, handleExampleEvent);
|
|
164
|
+
return () => {
|
|
165
|
+
eventBus.off(Events.example, handleExampleEvent);
|
|
166
|
+
};
|
|
167
|
+
}, [eventBus, handleExampleEvent]);
|
|
168
|
+
|
|
169
|
+
// Loader passes provided EventBus through to MFE
|
|
170
|
+
return <Loader src={mfeUrl} />;
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
</TabItem>
|
|
175
|
+
<TabItem value="mfe">
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { Button } from '@servicetitan/anvil2';
|
|
179
|
+
import { useDependencies } from '@servicetitan/react-ioc';
|
|
180
|
+
import { useCallback } from 'react';
|
|
181
|
+
import { Events, EXAMPLE_EVENT_BUS_TOKEN } from './types';
|
|
182
|
+
|
|
183
|
+
export const App = () => {
|
|
184
|
+
// Loader passes through provided EventBus
|
|
185
|
+
const [eventBus] = useDependencies(EXAMPLE_EVENT_BUS_TOKEN);
|
|
186
|
+
|
|
187
|
+
const handleClick = useCallback(() => {
|
|
188
|
+
eventBus.emit(Events.example);
|
|
189
|
+
}, [eventBus]);
|
|
190
|
+
|
|
191
|
+
return <Button onClick={handleClick}>Click me</Button>;
|
|
192
|
+
};
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
</TabItem>
|
|
196
|
+
</Tabs>
|
|
197
|
+
|
|
198
|
+
See [typedEventBusToken](./event-bus#typedeventbustoken) for a detailed explanation
|
|
199
|
+
of the types used in these examples.
|
|
200
|
+
|
|
201
|
+
#### Using legacy ref parameter
|
|
202
|
+
|
|
203
|
+
For backward compatibility, when no `EVENT_BUS_TOKEN` is provided, `Loader`
|
|
204
|
+
creates a private EventBus and returns it via the `ref` parameter.
|
|
205
|
+
This old approach should not be used in new code.
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { FC, useCallback, useEffect, useState } from 'react';
|
|
209
|
+
import { Loader } from '@servicetitan/web-components';
|
|
210
|
+
import { Events, ExampleEventBus } from './types';
|
|
211
|
+
|
|
212
|
+
const mfeUrl = 'https://unpkg.servicetitan.com/@servicetitan/examples';
|
|
213
|
+
|
|
214
|
+
export const Component: FC = () => {
|
|
215
|
+
const [eventBus, setEventBus] = useState<ExampleEventBus | null>(null);
|
|
216
|
+
const handleExampleEvent = useCallback(() => {
|
|
217
|
+
/* Handle event */
|
|
218
|
+
}, []);
|
|
219
|
+
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
eventBus?.on(Events.example, handleExampleEvent);
|
|
222
|
+
return () => {
|
|
223
|
+
eventBus?.off(Events.example, handleExampleEvent);
|
|
224
|
+
};
|
|
225
|
+
}, [eventBus, handleExampleEvent]);
|
|
226
|
+
|
|
227
|
+
// Loader provides private EventBus and returns it via "ref"
|
|
228
|
+
return <Loader ref={ref => setEventBus(ref)} src={mfeUrl} />;
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
:::caution
|
|
233
|
+
Note, if an `EVENT_BUS_TOKEN` is provided, `Loader` returns the provide value in `ref`
|
|
234
|
+
and doesn't create a private EventBus.
|
|
235
|
+
:::
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useMFEDataContext
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
If data is provided to an MFE via `<Loader>`, the data can be accessed within the MFE using the `useMFEDataContext` hook. You must provide the type of the data you expect to receive.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { useMFEDataContext } from '@servicetitan/web-components';
|
|
9
|
+
...
|
|
10
|
+
interface MFEAppData {
|
|
11
|
+
bar: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const MFEApp = () => {
|
|
15
|
+
const { bar } = useMFEDataContext<MFEAppData>();
|
|
16
|
+
};
|
|
17
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useMFEMetadataContext
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Metadata, such as `shadowRoot` and `portalShadowRoot`, is provided to MFEs. It is accessible through the `useMFEMetadataContext` hook.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { useMFEMetadataContext } from '@servicetitan/web-components';
|
|
9
|
+
...
|
|
10
|
+
export const MFEApp = () => {
|
|
11
|
+
const { shadowRoot, portalShadowRoot } = useMFEMetadataContext();
|
|
12
|
+
};
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The following metadata about an MFE is available from `useMFEMetadataContext` hook.
|
|
16
|
+
|
|
17
|
+
| Name | Type | Description |
|
|
18
|
+
| :----------------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------ |
|
|
19
|
+
| `shadowRoot` | ShadowRoot | ShadowRoot root node of which the MFE is rendered within |
|
|
20
|
+
| `portalShadowRoot` | ShadowRoot | ShadowRoot root node of the "portal" tied to the MFE, which is used to render elements in a div directly under the body element |
|
|
@@ -2,273 +2,15 @@
|
|
|
2
2
|
title: Web Components
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
import Tabs from '@theme/Tabs';
|
|
6
|
-
import TabItem from '@theme/TabItem';
|
|
7
|
-
|
|
8
5
|
#### [CHANGELOG (@servicetitan/web-components)](https://github.com/servicetitan/uikit/blob/master/packages/web-components/CHANGELOG.md)
|
|
9
6
|
|
|
10
7
|
`@servicetitan/web-components` is used to build, register, and load Microfrontends (MFEs) into host applications.
|
|
11
8
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
The `Loader` component is used to load an MFE into a host application. See [Microfrontends (MFEs)](/docs/frontend/micro-frontends) for more information about how MFEs are injected.
|
|
15
|
-
|
|
16
|
-
### Usage
|
|
17
|
-
|
|
18
|
-
```tsx
|
|
19
|
-
import { Loader } from '@servicetitan/web-components';
|
|
20
|
-
|
|
21
|
-
export const Foo = () => {
|
|
22
|
-
return <Loader src="https://unpkg.servicetitan.com/{package_name}@{semver_range}" />;
|
|
23
|
-
};
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Props
|
|
27
|
-
|
|
28
|
-
| Name | Description |
|
|
29
|
-
| :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
-
| `src` | url for the MFE's package |
|
|
31
|
-
| `fallbackSrc` | optional alternative url for the MFE's package (if request for src returns error) |
|
|
32
|
-
| `data` | additional data passed to an MFE as an object, where values are serializable, and preferably memoized ([see below](#passing-data-from-host-to-mfe)) |
|
|
33
|
-
| `basename` | prefix for all MFE URLs, you should inject it by the `BASENAME_TOKEN` and pass to the appropriate router property |
|
|
34
|
-
| `loadingFallback` | optional ReactNode to render when the MFE is loading |
|
|
35
|
-
| `loadingFallbackDelayed` | controls whether to render the loading fallback immediately, or after a short delay [(see below)](#fallback-delay) |
|
|
36
|
-
| `errorFallback` | optional ReactNode to render when the MFE fails to load |
|
|
37
|
-
| `className` | additional CSS classes for the MFE web component element |
|
|
38
|
-
| `cache` | optional cache strategy for the MFE [(see below)](#cache-strategy) |
|
|
39
|
-
|
|
40
|
-
### Loading fallback delay {#fallback-delay}
|
|
41
|
-
|
|
42
|
-
To prevent the loading fallback from flickering on then off when an MFE loads quickly, `Loader` does not render the fallback immediately. It waits a moment (currently 200ms) then renders the fallback only if the MFE hasn't finished loading. Set `loadingFallbackDelayed` to `false` to always render the fallback, regardless how quickly the MFE loads.
|
|
43
|
-
|
|
44
|
-
### Cache strategy {#cache-strategy}
|
|
45
|
-
|
|
46
|
-
When an MFE is loaded, `Loader` fetches the version and bundle data from the `src` (or `fallbackSrc`) endpoint and stores the information in a cache. Then, if the MFE is unloaded and subsequently reloaded, it reuses the cached information. Use `cache` to control whether or how long `Loader` caches an MFE's data:
|
|
47
|
-
|
|
48
|
-
- `true`: cache the data for the standard interval (currently 15 minutes). This is the default value.
|
|
49
|
-
- `false`: do not cache the data; (re)fetch it every time the MFE is loaded
|
|
50
|
-
- `-1`: cache the data indefinitely (i.e., for the duration of the user's session)
|
|
51
|
-
- _<number>_: cache the data for the specified _<number>_ of milliseconds
|
|
52
|
-
|
|
53
|
-
### Passing data from Host to MFE
|
|
54
|
-
|
|
55
|
-
Use `data` to pass data from your host application to the MFE. The data should be an object where keys are strings and the values are any serializable data.
|
|
56
|
-
|
|
57
|
-
To avoid performance issues with re-rendering and re-serializing the value, we recommend memoizing the `data` object passed to `Loader`.
|
|
58
|
-
|
|
59
|
-
**Reminder:** Don't casually share data between the host and MFEs. When possible, MFEs should load the data they need instead of relying on the host to pass it.
|
|
60
|
-
|
|
61
|
-
:::note
|
|
62
|
-
Before version v22.12.0 of `@servicetitan/web-components` package, the `data` prop only supported data of type `Record<string, string>`
|
|
63
|
-
:::
|
|
64
|
-
|
|
65
|
-
```tsx title="Host Application"
|
|
66
|
-
import { useMemo } from 'react';
|
|
67
|
-
import { Loader } from '@servicetitan/web-components';
|
|
68
|
-
...
|
|
69
|
-
export const HostApp = () => {
|
|
70
|
-
...
|
|
71
|
-
const data = useMemo(() => {
|
|
72
|
-
foo: ['one', 'two', 'three'],
|
|
73
|
-
bar: dependency,
|
|
74
|
-
}, [dependency]);
|
|
75
|
-
return (
|
|
76
|
-
...
|
|
77
|
-
<Loader
|
|
78
|
-
src="https://unpkg.servicetitan.com/{package_name}@{semver_range}"
|
|
79
|
-
data={data}
|
|
80
|
-
/>
|
|
81
|
-
...
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Data can be accessed within the MFE using props.
|
|
87
|
-
|
|
88
|
-
```tsx title="MFE Application"
|
|
89
|
-
interface MFEAppData {
|
|
90
|
-
foo: string[];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export const MFEApp = (props: MFEAppData) => {
|
|
94
|
-
const { foo } = props;
|
|
95
|
-
};
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Data must be serializable, due to the nature of how it is passed through a web component to reach your MFE component. This means that you cannot pass functions or other non-serializable data types.
|
|
99
|
-
|
|
100
|
-
```tsx title="Host Application"
|
|
101
|
-
...
|
|
102
|
-
<Loader data={{ date: new Date("2020-05-12T23:50:21.817Z") }} />
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
```tsx title="MFE Application"
|
|
106
|
-
...
|
|
107
|
-
export const MFEApp = (props: MyType) => {
|
|
108
|
-
props.date; // '2020-05-12T23:50:21.817Z'
|
|
109
|
-
};
|
|
110
|
-
// RESULTS IN A STRING, NOT A DATE OBJECT
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
As of version v22.12.0 of `@servicetitan/web-components`, if data passed into the `data` prop of `Loader` changes during runtime, the data will re-render within your MFE component where appropriate. Previous versions would re-mounting the entire MFE application when data changed.
|
|
114
|
-
|
|
115
|
-
### Using EventBus to exchanges messages
|
|
116
|
-
|
|
117
|
-
Use [EventBus](./event-bus) to exchange messages between the host and MFEs.
|
|
118
|
-
When an EventBus is provided via `EVENT_BUS_TOKEN`, `Loader` automatically passes it through to MFEs.
|
|
119
|
-
For example,
|
|
120
|
-
|
|
121
|
-
<Tabs
|
|
122
|
-
defaultValue="host"
|
|
123
|
-
values={[
|
|
124
|
-
{ label: 'Host', value: 'host' },
|
|
125
|
-
{ label: 'MFE', value: 'mfe'},
|
|
126
|
-
{ label: 'Types', value: 'types' },
|
|
127
|
-
]}
|
|
128
|
-
>
|
|
129
|
-
<TabItem value="types">
|
|
130
|
-
|
|
131
|
-
```tsx
|
|
132
|
-
import { EventBus, typedEventBusToken } from '@servicetitan/web-components';
|
|
133
|
-
|
|
134
|
-
export enum Events {
|
|
135
|
-
example = 'example:event',
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export interface ExampleEvents {
|
|
139
|
-
[Events.example]: undefined;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export type ExampleEventBus = EventBus<ExampleEvents>;
|
|
143
|
-
|
|
144
|
-
export const EXAMPLE_EVENT_BUS_TOKEN = typedEventBusToken<ExampleEvents>();
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
</TabItem>
|
|
148
|
-
<TabItem value="host">
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
import { provide, useDependencies } from '@servicetitan/react-ioc';
|
|
152
|
-
import { EVENT_BUS_TOKEN, EventBus, Loader } from '@servicetitan/web-components';
|
|
153
|
-
import { FC, useCallback, useEffect } from 'react';
|
|
154
|
-
import { Events, EXAMPLE_EVENT_BUS_TOKEN } from './types';
|
|
155
|
-
|
|
156
|
-
const mfeUrl = 'https://unpkg.servicetitan.com/@servicetitan/examples';
|
|
157
|
-
|
|
158
|
-
export const App: FC = provide({
|
|
159
|
-
singletons: [{ provide: EVENT_BUS_TOKEN, useValue: new EventBus() }],
|
|
160
|
-
})(() => {
|
|
161
|
-
const [eventBus] = useDependencies(EXAMPLE_EVENT_BUS_TOKEN);
|
|
162
|
-
const handleExampleEvent = useCallback(() => {
|
|
163
|
-
/* Handle event */
|
|
164
|
-
}, []);
|
|
165
|
-
|
|
166
|
-
useEffect(() => {
|
|
167
|
-
eventBus.on(Events.example, handleExampleEvent);
|
|
168
|
-
return () => {
|
|
169
|
-
eventBus.off(Events.example, handleExampleEvent);
|
|
170
|
-
};
|
|
171
|
-
}, [eventBus, handleExampleEvent]);
|
|
172
|
-
|
|
173
|
-
// Loader passes provided EventBus through to MFE
|
|
174
|
-
return <Loader src={mfeUrl} />;
|
|
175
|
-
});
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
</TabItem>
|
|
179
|
-
<TabItem value="mfe">
|
|
180
|
-
|
|
181
|
-
```tsx
|
|
182
|
-
import { Button } from '@servicetitan/anvil2';
|
|
183
|
-
import { useDependencies } from '@servicetitan/react-ioc';
|
|
184
|
-
import { useCallback } from 'react';
|
|
185
|
-
import { Events, EXAMPLE_EVENT_BUS_TOKEN } from './types';
|
|
186
|
-
|
|
187
|
-
export const App = () => {
|
|
188
|
-
// Loader passes through provided EventBus
|
|
189
|
-
const [eventBus] = useDependencies(EXAMPLE_EVENT_BUS_TOKEN);
|
|
190
|
-
|
|
191
|
-
const handleClick = useCallback(() => {
|
|
192
|
-
eventBus.emit(Events.example);
|
|
193
|
-
}, [eventBus]);
|
|
194
|
-
|
|
195
|
-
return <Button onClick={handleClick}>Click me</Button>;
|
|
196
|
-
};
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
</TabItem>
|
|
200
|
-
</Tabs>
|
|
201
|
-
|
|
202
|
-
See [typedEventBusToken](./event-bus#typedeventbustoken) for a detailed explanation
|
|
203
|
-
of the types used in these examples.
|
|
204
|
-
|
|
205
|
-
#### Using legacy ref parameter
|
|
206
|
-
|
|
207
|
-
For backward compatibility, when no `EVENT_BUS_TOKEN` is provided, `Loader`
|
|
208
|
-
creates a private EventBus and returns it via the `ref` parameter.
|
|
209
|
-
This old approach should not be used in new code.
|
|
210
|
-
|
|
211
|
-
```tsx
|
|
212
|
-
import { FC, useCallback, useEffect, useState } from 'react';
|
|
213
|
-
import { Loader } from '@servicetitan/web-components';
|
|
214
|
-
import { Events, ExampleEventBus } from './types';
|
|
215
|
-
|
|
216
|
-
const mfeUrl = 'https://unpkg.servicetitan.com/@servicetitan/examples';
|
|
217
|
-
|
|
218
|
-
export const Component: FC = () => {
|
|
219
|
-
const [eventBus, setEventBus] = useState<ExampleEventBus | null>(null);
|
|
220
|
-
const handleExampleEvent = useCallback(() => {
|
|
221
|
-
/* Handle event */
|
|
222
|
-
}, []);
|
|
223
|
-
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
eventBus?.on(Events.example, handleExampleEvent);
|
|
226
|
-
return () => {
|
|
227
|
-
eventBus?.off(Events.example, handleExampleEvent);
|
|
228
|
-
};
|
|
229
|
-
}, [eventBus, handleExampleEvent]);
|
|
230
|
-
|
|
231
|
-
// Loader provides private EventBus and returns it via "ref"
|
|
232
|
-
return <Loader ref={ref => setEventBus(ref)} src={mfeUrl} />;
|
|
233
|
-
};
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
:::caution
|
|
237
|
-
Note, if an `EVENT_BUS_TOKEN` is provided, `Loader` returns the provide value in `ref`
|
|
238
|
-
and doesn't create a private EventBus.
|
|
239
|
-
:::
|
|
240
|
-
|
|
241
|
-
## useMFEDataContext
|
|
242
|
-
|
|
243
|
-
Data can be also be accessed within the MFE using the `useMFEDataContext` hook. You must provide the type of the data you expect to receive.
|
|
244
|
-
|
|
245
|
-
```tsx
|
|
246
|
-
import { useMFEDataContext } from '@servicetitan/web-components';
|
|
247
|
-
...
|
|
248
|
-
interface MFEAppData {
|
|
249
|
-
bar: string;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export const MFEApp = () => {
|
|
253
|
-
const { bar } = useMFEDataContext<MFEAppData>();
|
|
254
|
-
};
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## useMFEMetadataContext
|
|
258
|
-
|
|
259
|
-
Some metadata is also provided to MFEs, and is accessible through the `useMFEMetadataContext` hook.
|
|
260
|
-
|
|
261
|
-
```tsx
|
|
262
|
-
import { useMFEMetadataContext } from '@servicetitan/web-components';
|
|
263
|
-
...
|
|
264
|
-
export const MFEApp = () => {
|
|
265
|
-
const { shadowRoot, portalShadowRoot } = useMFEMetadataContext();
|
|
266
|
-
};
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
The following metadata about an MFE is available from `useMFEMetadataContext` hook.
|
|
9
|
+
## API
|
|
270
10
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
11
|
+
- [Loader](./loader.mdx) - Use `Loader` to load an MFE into a host application.
|
|
12
|
+
- [HeadlessLoader](./headless-loader.mdx) - Use `HeadlessLoader` to load the headless bundle to perform logic, such as messaging, with no UI, and without requiring the full or light bundle to be loaded.
|
|
13
|
+
- [EventBus](./event-bus.mdx) - Use `EventBus` to exchange messages (aka events) between a host and MFEs, or between concurrently mounted MFEs.
|
|
14
|
+
- [useMFEDataContext](./use-mfe-data-context.mdx) - Use `useMFEDataContext` hook to access data provided to the MFE.
|
|
15
|
+
- [useMFEMetadataContext](./use-mfe-metadata-context.mdx) - Use `useMFEMetadataContext` hook to access metadata about the MFE.
|
|
16
|
+
- [getValueForEnvironment](./get-value-for-environment.mdx) - Use `getValueForEnvironment` to detect the host environment and return a corresponding value.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/docs-uikit",
|
|
3
|
-
"version": "31.
|
|
3
|
+
"version": "31.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,5 +16,5 @@
|
|
|
16
16
|
"cli": {
|
|
17
17
|
"webpack": false
|
|
18
18
|
},
|
|
19
|
-
"gitHead": "
|
|
19
|
+
"gitHead": "d01cfe32c50461245ce52c911710e8eb879290e9"
|
|
20
20
|
}
|