@servicetitan/docs-uikit 32.1.0 → 32.2.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.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: BREAKING CHANGES
3
+ sidebar_position: 0
3
4
  ---
4
5
 
5
6
  ## v32.0.0
@@ -142,7 +142,7 @@ export const HostApp: FC = () => {
142
142
 
143
143
  ### LDService
144
144
 
145
- The `LDService` interface enables [headless bundles](./web-components/headless-loader#headlesscallback) to create and reuse client connections for LaunchDarkly projects.
145
+ The `LDService` interface enables [headless bundles](./microfrontends/developing/headless-bundle) to create and reuse client connections for LaunchDarkly projects.
146
146
 
147
147
  ```ts
148
148
  export interface LDService {
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Authentication
3
+ ---
4
+
5
+ To develop against backend APIs that require authentication, you can configure [proxies](https://webpack.js.org/configuration/dev-server/#devserverproxy) that add cookies to local requests.
6
+
7
+ ## Create proxy script
8
+
9
+ Create a script that generates a `webpack-dev-server` proxy configuration from a local `config.js` file that maps URLs to cookies. For example,
10
+
11
+ ```js title="local/proxy-settings.js"
12
+ const proxy = [];
13
+
14
+ try {
15
+ /**
16
+ * Here map environments to the endpoints (prefixes) that require authentication
17
+ * (if all do, use the ** wildcard). If all requests are sent to the host,
18
+ * remove or comment out the "ms" (microservice) environment.
19
+ */
20
+ const environments = {
21
+ mainApp: ['/app/api'],
22
+ ms: ['**'],
23
+ };
24
+
25
+ // Read local config
26
+ const config = require('./config');
27
+
28
+ // Add proxy for each URL and cookie in local config
29
+ Object.entries(environments).forEach(([name, prefixes]) => {
30
+ const { cookie, url } = config[name] || {};
31
+ if (url && cookie) {
32
+ proxy.push(createProxyConfig({ target: url, cookie, context: prefixes }));
33
+ }
34
+ });
35
+ } catch {
36
+ // ignore error
37
+ }
38
+
39
+ function createProxyConfig({ context, cookie, target }) {
40
+ return {
41
+ context,
42
+ target,
43
+ changeOrigin: true,
44
+ secure: false,
45
+ onProxyReq: req => {
46
+ if (cookie) {
47
+ req.setHeader('Cookie', cookie);
48
+ }
49
+ },
50
+ };
51
+ }
52
+
53
+ module.exports = proxy.length ? proxy : undefined;
54
+ ```
55
+
56
+ ## Put cookies in local configuration file
57
+
58
+ Login to the application and use the web browser's Developer Tools to copy the cookies from backend requests to `config.js`.
59
+
60
+ ```js title="local/config.js"
61
+ module.exports = {
62
+ mainApp: {
63
+ url: 'https://go.servicetitan.com/',
64
+ cookie: '', // actual cookie goes here
65
+ },
66
+ ms: {
67
+ url: 'http://localhost:8888',
68
+ cookie: '', // actual cookie goes here
69
+ },
70
+ };
71
+ ```
72
+
73
+ :::caution
74
+ Add `config.js` to `.gitignore` so the cookies are not exposed. And, note that
75
+ you will have to replace cookies when they expire.
76
+ :::
77
+
78
+ ## Configure `webpack-dev-server`
79
+
80
+ Set `cli.webpack.proxy` to the relative path to the proxy script.
81
+
82
+ ```json title="packages/mfe/package.json"
83
+ {
84
+ "cli": {
85
+ "webpack": {
86
+ "port": 8888,
87
+ "proxy": "../../local/proxy-settings.js"
88
+ }
89
+ }
90
+ }
91
+ ```
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: Developing
3
+ ---
4
+
5
+ An MFE is a React component that is bundled in such a way that it renders inside an isolated host container.
6
+
7
+ A best practice is to use different MFEs for different purposes. Creating multi-tasking MFEs that serve multiple, unrelated purposes is usually a suboptimal approach.
8
+ Components that conditionally render different behaviors based on inputs from the host should probably be implemented as separate MFEs.
9
+
10
+ To create an MFE, create a file named `app.tsx` in the root directory of a package's source folder (the `rootDir` in `tsconfig.json`) that exports a component named `App`. The `App` component will be the MFE's entry point.
11
+
12
+ Remove any existing "index" file from the root directory and, if the MFE uses `@servicetitan/design-system`, remove all imports of design system styles from the code (`startup` automatically imports design system styles when needed).
13
+
14
+ :::tip
15
+
16
+ - If the MFE is large, use [`lazyModule`](../../lazy-module) to split it into smaller modules that are loaded on demand.
17
+ - See [Headless Bundle](./headless-bundle) for how to create a separate bundle for preloading setup or configuration information.
18
+
19
+ :::
20
+
21
+ ## Configuration
22
+
23
+ In the MFE's `package.json`, set,
24
+
25
+ - `private` to `true`
26
+ - `cli.web-component` to `true`,
27
+ - `cli.webpack.port` to a free port from where to serve the MFE locally
28
+ - `publishConfig.access` to `"private"`
29
+
30
+ E.g.,
31
+
32
+ ```json title="package.json"
33
+ {
34
+ "private": true,
35
+ "cli": {
36
+ "web-component": true,
37
+ "webpack": {
38
+ "port": 8888
39
+ }
40
+ },
41
+ "publishConfig": {
42
+ "access": "restricted"
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Required dependencies
48
+
49
+ Add these required **dependencies** (include other dependencies as needed):
50
+
51
+ - `@servicetitan/log-service`
52
+ - `@servicetitan/react-ioc`
53
+ - `@servicetitan/web-components`
54
+ - `history`
55
+ - `react`
56
+ - `react-dom`
57
+
58
+ ## Running
59
+
60
+ Use [startup start](../../startup/start) to run the MFE. It will serve it from the port specified [above](#configuration).
61
+
62
+ :::tip
63
+ See [Authentication](./authentication) for how to develop against backend APIs that require authentication.
64
+ :::
65
+
66
+ ### Inside host application
67
+
68
+ To run the MFE in a local host, pass [Loader](../../web-components/loader) the MFE's local URL. For example,
69
+
70
+ ```tsx title="Local Host Application"
71
+ import { Loader } from '@servicetitan/web-components';
72
+
73
+ export function MyMfe() {
74
+ return <Loader src="http://localhost:8888" />;
75
+ }
76
+ ```
77
+
78
+ #### Using `getValueForEnvironment` in the Monolith
79
+
80
+ In the Monolith, you can use [getValueForEnvironment](../../web-components/get-value-for-environment) to automatically use the MFE's local URL in development. E.g.,
81
+
82
+ ```tsx title="Monolith Host Application"
83
+ import { Loader, getValueForEnvironment } from '@servicetitan/web-components';
84
+
85
+ const baseUrl = 'https://unpkg.servicetitan.com/@servicetitan/my-mfe';
86
+
87
+ export function MyMfe() {
88
+ return <Loader src={getMfeUrl()} />;
89
+ }
90
+
91
+ function getMfeUrl() {
92
+ return (
93
+ getValueForEnvironment({
94
+ dev: 'http://localhost:8888',
95
+ qa: `${baseUrl}@qa`,
96
+ stage: `${baseUrl}@stage`,
97
+ go: `${baseUrl}@prod`,
98
+ next: `${baseUrl}@prod`,
99
+ }) ?? ''
100
+ );
101
+ }
102
+ ```
103
+
104
+ ### Without start
105
+
106
+ To run the MFE without `start`, [build](#building) it, then point any static http server to the MFE's root folder, where [Loader](../../web-components/loader) will find the `dist` directory. For example, to serve with [`http-server`](https://www.npmjs.com/package/http-server) on port 8888,
107
+
108
+ ```sh
109
+ npx --yes http-server packages/mfe -p 8888 --cors
110
+ ```
111
+
112
+ ## Building
113
+
114
+ Use [startup build](../../startup/build) to build the MFE.
115
+
116
+ ## See Also
117
+
118
+ - [Authentication](./authentication) - how to use backend API's that require authentication
119
+ - [Example MFE](https://github.com/servicetitan/frontend-example/tree/master/packages/mfe)
120
+ - [Headless Bundle](./headless-bundle) - how to preloading setup or configuration information
121
+ - [Hosting](../hosting) - how to load MFEs
122
+ - [Publishing](../publishing) - how to publish MFEs
123
+ - [Sharing Dependencies](../sharing-dependencies) - how to customize shared dependencies
124
+ - [Web Components](../../web-components) - how to use `Loader`, `getValueForEnvironment` and other runtime APIs
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: Headless Bundle
3
+ ---
4
+
5
+ A headless bundle is a tiny bundle that allows hosts to preload configuration or setup information without having to load the entire MFE. Headless bundles are not rendered into or associated with DOM elements so they cannot use React.
6
+
7
+ To generate a headless bundle, create a file named `headless.ts` in the root directory of the MFE's source folder (the `rootDir` in `tsconfig.json`) and export a function named `connectedCallback`.
8
+
9
+ :::info
10
+ `Startup` includes everything imported by `headless.ts` in the bundle.
11
+ Headless bundles are wholly self-contained and cannot [share dependencies](../sharing-dependencies) with hosts.
12
+ :::
13
+
14
+ ## connectedCallback
15
+
16
+ The host calls `connectedCallback` when the bundle is loaded.
17
+
18
+ ```ts title="src/headless.ts"
19
+ import type { HeadlessCallback } from '@servicetitan/web-components';
20
+
21
+ export const connectedCallback: HeadlessCallback<ExampleData> = ({ mfeData, eventBus }) => {
22
+ // Process host data
23
+ doSomethingWithMfeData(mfeData);
24
+
25
+ // Send message to host
26
+ eventBus?.emit('example-module:status', { status: 'connected', timestamp: Date.now() });
27
+ };
28
+ ```
29
+
30
+ See [HeadlessCallback](#headlesscallback) for a detailed description of `connectedCallback`s parameters.
31
+
32
+ ## disconnectedCallback
33
+
34
+ If `headless.ts` exports a function named `disconnectedCallback`, the host calls it when the bundle is unmounted.
35
+
36
+ ```ts title="src/headless.ts"
37
+ import type { HeadlessCallback } from '@servicetitan/web-components';
38
+
39
+ export const disconnectedCallback: HeadlessCallback = ({ eventBus }) => {
40
+ // Send message to host
41
+ eventBus?.emit('example-module:status', { status: 'disconnected', timestamp: Date.now() });
42
+ };
43
+ ```
44
+
45
+ See [HeadlessCallback](#headlesscallback) for a detailed description of `disconnectedCallback`s parameters.
46
+
47
+ ## HeadlessCallback
48
+
49
+ `HeadlessCallback<TMfeData = any, TEventBus extends EventBus = EventBus>`
50
+
51
+ The `connectedCallback` and `disconnectedCallback` functions are passed a single argument that is an object with the following fields:
52
+
53
+ | Name | Type | Description |
54
+ | ------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
55
+ | `ldService` | `LDService` | The [LaunchDarkly LDService instance](../../launchdarkly-service#ldservice) passed from the Host. |
56
+ | `logService` | `Log` | The [Log instance](../../log-service#log) passed from the Host. |
57
+ | `eventBus` | `TEventBus` | The [EventBus instance](../../web-components/event-bus) passed from the Host, typed as `TEventBus`. |
58
+ | `mfeData` | `Serializable<TMfeData>` | The [data passed from the host to `HeadlessLoader`](../../web-components/headless-loader#headless-loader-props), typed as `TMfeData`. |
59
+
60
+ ## Loading
61
+
62
+ Use [HeadlessLoader](../../web-components/headless-loader) to load headless bundles.
@@ -0,0 +1,84 @@
1
+ ---
2
+ title: Hosting
3
+ ---
4
+
5
+ A host is an application, or MFE, that uses [`Loader`](../web-components/loader) to dynamically fetch and render MFEs.
6
+
7
+ ## Configuration
8
+
9
+ Set `cli.webpack.expose-shared-dependencies` to `true` in the application's `package.json`.
10
+
11
+ :::caution
12
+ `expose-shared-dependencies` only applies to applications. MFEs that load other MFEs do not need this setting or any additional configuration.
13
+ :::
14
+
15
+ ```json title="package.json"
16
+ {
17
+ "cli": {
18
+ "webpack": {
19
+ "expose-shared-dependencies": true
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ ### Design system
26
+
27
+ If the host uses `@servicetitan/design-system` remove all imports of design system styles form the code; `startup` handles that automatically. Alternatively, to customize the design system styles, move the imports to a file named `design-system.css` in the root directory of the application's source folder (the `rootDir` in `tsconfig.json`). E.g.,
28
+
29
+ ```css title="design-system.css"
30
+ @import '@servicetitan/tokens/dist/tokens.css';
31
+ @import '@servicetitan/anvil-fonts/dist/css/anvil-fonts.css';
32
+ @import '@servicetitan/design-system/dist/system.min.css';
33
+ ```
34
+
35
+ ## Loading MFEs
36
+
37
+ ServiceTitan serves MFEs from a private UNPKG instance at https://unpkg.servicetitan.com.
38
+ To load an MFE, pass [`Loader`](../web-components/loader) its UNPKG url. For example,
39
+
40
+ ```tsx
41
+ import { Loader } from '@servicetitan/web-components';
42
+
43
+ const mfeUrl = 'https://unpkg.servicetitan.com/@servicetitan/example-mfe';
44
+
45
+ export function ExampleMfe() {
46
+ return <Loader src={mfeUrl} />;
47
+ }
48
+ ```
49
+
50
+ ### Using tags
51
+
52
+ In practice hosts usually append a tag of the form `@{version}`to the URL to specify which version to load. Tags can be an exact version (e.g., **@0.0.0-next.152efeb**) or an release tag like **@prod**.
53
+
54
+ In the Monolith, you can use [getValueForEnvironment](../web-components/get-value-for-environment) to determine the release tag from the host's environment,
55
+
56
+ ```tsx
57
+ import { Loader, getValueForEnvironment } from '@servicetitan/web-components';
58
+
59
+ const baseUrl = 'https://unpkg.servicetitan.com/@servicetitan/example-mfe';
60
+
61
+ export function ExampleMfe() {
62
+ return <Loader src={getMfeUrl()} />;
63
+ }
64
+
65
+ function getMfeUrl() {
66
+ return (
67
+ getValueForEnvironment({
68
+ dev: 'http://localhost:8888',
69
+ qa: `${baseUrl}@qa`,
70
+ stage: `${baseUrl}@stage`,
71
+ go: `${baseUrl}@prod`,
72
+ next: `${baseUrl}@prod`,
73
+ }) ?? ''
74
+ );
75
+ }
76
+ ```
77
+
78
+ ## See Also
79
+
80
+ - [Developing](./developing) - how to create MFEs
81
+ - [Example host application](https://github.com/servicetitan/frontend-example/tree/master/packages/application)
82
+ - [Publishing](./publishing) - how to publish MFEs
83
+ - [Sharing Dependencies](./sharing-dependencies) - how to customize shared dependencies
84
+ - [Web Components](../web-components) - how to use `Loader`, `getValueForEnvironment` and other runtime APIs.
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Microfrontends (MFEs)
3
+ sidebar_position: 1
4
+ ---
5
+
6
+ Microfrontends (MFEs) break a large frontend application into independently developed, deployed, and versioned pieces so teams can move faster and reduce cross-team coupling.
7
+
8
+ :::info
9
+ ServiceTitan's MFE solution predates Webpack's module federation.
10
+ For a discussion of the pros and cons and why we continue to use ServiceTitan's solution see this [document](https://github.com/servicetitan/dispatch-docs/blob/5fd062ec6a7f292be593484a633c892ca34c8de7/architecture-decisions/0007-mfe-monolith-shared-code-and-dependencies.md).
11
+ :::
12
+
13
+ ## Benefits
14
+
15
+ - Enable independent release cadence and ownership
16
+ - Let teams choose and upgrade dependencies independently
17
+ - Improve fault isolation and reliability at scale
18
+ - Allow reuse of UI pieces that are standalone
19
+ - Maintain performance by reusing compatible dependencies
20
+
21
+ ## Restrictions
22
+
23
+ - Stores or services from the host application cannot be shared with MFEs
24
+ - Passing data via `window` or global variables is discouraged
25
+ - Use [Loader's data prop](../web-components/loader#passing-data-from-host-to-mfe) or [`EventBus`](../web-components/event-bus) to exchange data between hosts and MFEs
26
+ - Data must be serializable
27
+ - Changes must be backward compatible, because hosts and MFEs release independently
28
+
29
+ ## Design
30
+
31
+ [`Startup`](../startup/) builds MFEs. It outputs two bundles, and metadata about the MFE's capabilities and dependencies. The **full** bundle is self-contained and includes all code required by the MFE. The **light** bundle contains only private dependencies that aren't shared with hosts.
32
+
33
+ Teams publish MFE's to ServiceTitan private [Verdaccio + UNPKG](/docs/frontend/verdaccio-unpkg) infrastructure.
34
+
35
+ At runtime, [`Loader`](../web-components/loader) fetches the MFE's metadata, selects either the **light** or **full** bundle depending on whether the host's dependencies are compatible, then renders the MFE inside a Shadow DOM that minimizes conflicts.
36
+
37
+ ## Topics
38
+
39
+ See the following topics for how to build, deploy and use MFEs:
40
+
41
+ - [Developing](./developing/) - how to create MFEs
42
+ - [Hosting](./hosting) - how to load MFEs
43
+ - [Publishing](./publishing) - how to publish MFEs
44
+ - [Sharing Dependencies](./sharing-dependencies) - how to customize shared dependencies
45
+ - [Troubleshooting](./troubleshooting) - how to fix common issues
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Publishing
3
+ ---
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
7
+
8
+ MFEs must be published before they can be loaded in production.
9
+
10
+ :::note
11
+ This document describes using UNPKG + Verdaccio, the recommended way to publish MFEs. There are alternative ways of serving MFEs (e.g., from a microservice) that are not recommended and are not described in this document.
12
+ :::
13
+
14
+ ## Publishing Locally
15
+
16
+ Before you can publish locally, run the following command to add an authorization token for Verdaccio to the local NPM configuration:
17
+
18
+ ```sh
19
+ npx verdaccio-okta-oauth@latest --registry https://verdaccio.servicetitan.com
20
+ ```
21
+
22
+ [Build](../startup/build) the MFE, then to publish it run:
23
+
24
+ ```sh
25
+ npx startup mfe-publish
26
+ ```
27
+
28
+ :::info
29
+ If the project contains multiple MFEs `startup` publishes all of them.
30
+ :::
31
+
32
+ ## Publishing From CI
33
+
34
+ To publish an MFE from a Github or Teamcity workflow, use the `ST_VERDACCIO_PUBLISH_TOKEN` organization secret to authorize access to Verdaccio. For example,
35
+
36
+ <Tabs
37
+ defaultValue="github"
38
+ values={[{ label: "Github Action", value: "github"}, {label: "Dockerfile", value: "docker"}]}>
39
+
40
+ <TabItem value="github">
41
+
42
+ ```yml
43
+ - name: Build project
44
+ run: npm run build
45
+
46
+ - name: Configure Verdaccio token
47
+ run: npm config set --location=project "//verdaccio.servicetitan.com/:_authToken"="${{ secrets.ST_VERDACCIO_PUBLISH_TOKEN }}"
48
+
49
+ - name: Publish MFE
50
+ run: npm run mfe-publish
51
+ ```
52
+
53
+ </TabItem>
54
+ <TabItem value="docker">
55
+
56
+ ```dockerfile
57
+ ARG ST_VERDACCIO_PUBLISH_TOKEN
58
+
59
+ RUN npm run build
60
+ RUN npm config set --location=project "//verdaccio.servicetitan.com/:_authToken=$ST_VERDACCIO_PUBLISH_TOKEN"
61
+ RUN npm run mfe-publish
62
+ ```
63
+
64
+ </TabItem>
65
+ </Tabs>
66
+
67
+ ## Managing Releases With Tags
68
+
69
+ Release tags like **dev**, **next**, and **prod** are a lightweight way to control which MFE bundle consumers load, without changing host code. You can use tags to promote, test, or roll back releases independently from the host application.
70
+
71
+ ### Key concepts
72
+
73
+ - A release tag points to a specific published package version.
74
+ - Hosts pass a tag instead of an exact version to [Loader](../web-components/loader).
75
+ - Switching the tag to a different version changes what hosts load.
76
+
77
+ :::caution
78
+ When a tag is switched to a new version, it could take ServiceTitan's infrastructure up to fifteen minutes to deliver the new version to consumers.
79
+ :::
80
+
81
+ ### Common workflows
82
+
83
+ - **Canary testing**: publish MFE as `0.0.0-develop.{hash}` and tag as **dev** for early testing.
84
+ - **Staging promotion**: point **next** to the version used to use in pre-production environments.
85
+ - **Production promotion**: point **prod** to the release that passed staging.
86
+ - **Rollback**: repoint a tag to the previous good version to revert consumers.
87
+
88
+ ## See Also
89
+
90
+ - [Example `mfe-publish` workflow](https://github.com/servicetitan/frontend-example/blob/master/.github/workflows/mfe-publish.yml)
91
+ - [startup mfe-publish](../startup/mfe-publish) -- how to tag releases
92
+ - [Using tags](./hosting#using-tags) -- how to load MFEs with tags
93
+ - [Verdaccio + UNPKG](/docs/frontend/verdaccio-unpkg) -- publishing infrastructure
@@ -0,0 +1,96 @@
1
+ ---
2
+ title: Sharing Dependencies
3
+ ---
4
+
5
+ When `startup` builds an MFE, it creates two bundles, named **full** and **light**:[^headless]
6
+
7
+ - The **full** bundle is completely self-contained and includes all code required by the MFE.
8
+ - The **light** bundle is smaller and faster because it omits dependencies that already exist in the host (e.g., `react`, `@servicetitan/design-system`).
9
+
10
+ At runtime, the host loads the **light** bundle when its shared dependencies are compatible with the MFE's, else it loads the **full** bundle.
11
+
12
+ To be compatible, the host's version of a dependency must satisfy the MFE's [semver](https://semver.org) range. To allow the widest compatibility MFEs should use `^` instead of `~` for shared dependencies. For example, a host using `react` `18.3.1` will consider itself compatible with an MFEs that depends on `^18.2.0`, and incompatible with an MFE that depends on `~18.2.0`.
13
+
14
+ By default, MFEs and hosts share the following dependencies (see [here](https://github.com/search?q=repo%3Aservicetitan%2Fuikit+getDefaultSharedDependencies&type=code) for the most up-to-date list):
15
+
16
+ - @servicetitan/anvil2
17
+ - @servicetitan/design-system
18
+ - classnames
19
+ - formstate
20
+ - mobx
21
+ - mobx-react
22
+ - mobx-utils
23
+ - react
24
+ - react-dom
25
+
26
+ :::info
27
+ MFE's are not required to use all the default dependencies; `startup` and `Loader` ignore any that aren't used.
28
+ :::
29
+
30
+ To customize which dependencies are shared, set `cli.webpack.shared-dependencies` to an object that maps dependencies to `SharedDependencies.{name}`. By convention `{name}` is the camel-cased package name.
31
+
32
+ - Use the special **defaults** key with the empty string to incorporate the default list.
33
+ - To exclude a default dependency, map it to the empty string.
34
+
35
+ ## Adding a Shared Dependency
36
+
37
+ To add a dependency, use the special **defaults** key to first include the defaults. For example, to add `lodash` as a shared dependency,
38
+
39
+ ```json title="package.json"
40
+ {
41
+ "cli": {
42
+ "webpack": {
43
+ "shared-dependencies": {
44
+ "defaults": "", // add default list
45
+ "lodash": "SharedDependencies.Lodash" // add lodash
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ :::caution
53
+ When you add a shared dependency its code is completely omitted from the **light** bundle. The host must include and share the same dependency, else it will always use the MFE's **full** bundle.
54
+ :::
55
+
56
+ ## Omitting a Default Dependency
57
+
58
+ :::tip
59
+ Because monorepos's hoist dependencies into the project's root `package.json`, if a default shared dependency exists in the root `package.json`, `startup` assumes it is used by the MFE.
60
+ If the MFE doesn't actually use a hoisted dependency, customize its shared dependencies to omit it.
61
+ :::
62
+
63
+ To omit an default dependency, map it to the empty string. For example, to omit `@servicetitan/design-system`:
64
+
65
+ ```json title="package.json"
66
+ {
67
+ "cli": {
68
+ "webpack": {
69
+ "shared-dependencies": {
70
+ "defaults": "", // add default list
71
+ "@servicetitan/design-system": "" // drop @servicetitan/design-system
72
+ }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ## Specifying All Dependencies
79
+
80
+ To completely customize the list of shared dependencies, omit the **defaults** key. For example, to only share Anvil2 and React,
81
+
82
+ ```json title="package.json"
83
+ {
84
+ "cli": {
85
+ "webpack": {
86
+ "shared-dependencies": {
87
+ "@servicetitan/anvil2": "SharedDependencies.ServiceTitan.Anvil2",
88
+ "react": "SharedDependencies.React",
89
+ "react-dom": "SharedDependencies.ReactDOM"
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ [^headless]: `startup` can also created **headless** bundles for preloading configuration or setup information. Headless bundles never share dependencies with hosts. See [Headless Bundle](./developing/headless-bundle) for more information.
@@ -0,0 +1,55 @@
1
+ ---
2
+ title: Troubleshooting
3
+ ---
4
+
5
+ ## Styled-components
6
+
7
+ We advise against using `styled-components` in MFEs. If you do, you may encounter an issue where styles are not applied because they are not injected properly.
8
+ To fix, use `StyleSheetManager` to inject the styles into the MFE's ShadowDOM. For example,
9
+
10
+ ```tsx
11
+ import { useMFEMetadataContext } from '@servicetitan/web-components';
12
+ import styled, { StyleSheetManager } from 'styled-components';
13
+
14
+ const Title = styled.h1`
15
+ color: #bf4f74;
16
+ `;
17
+
18
+ export const MFETitle: FC = () => {
19
+ const { shadowRoot } = useMFEMetadataContext();
20
+ return (
21
+ <StyleSheetManager target={shadowRoot}>
22
+ <Title>MFE Title</Title>
23
+ </StyleSheetManager>
24
+ );
25
+ };
26
+ ```
27
+
28
+ For styled components inside portalled elements (e.g., Popover), use `StyleSheetManager` to inject the styles into the MFE's portal ShadowDOM. For example,
29
+
30
+ ```tsx
31
+ import { Popover } from '@servicetitan/anvil2';
32
+ import { useMFEMetadataContext } from '@servicetitan/web-components';
33
+ import styled, { StyleSheetManager } from 'styled-components';
34
+
35
+ const PopoverTitle = styled.h2`
36
+ color: #bf4f75;
37
+ `;
38
+
39
+ export const MFEPopover: FC = () => {
40
+ const { portalShadowRoot } = useMFEMetadataContext();
41
+
42
+ return (
43
+ <Popover>
44
+ <Popover.Button>Click me!</Popover.Button>
45
+ <Popover.Content>
46
+ <StyleSheetManager target={portalShadowRoot}>
47
+ <PopoverTitle>Title</PopoverTitle>
48
+ </StyleSheetManager>
49
+ </Popover.Content>
50
+ </Popover>
51
+ );
52
+ };
53
+ ```
54
+
55
+ See [useMFEMetadataContext](../web-components/use-mfe-metadata-context) for more information about accessing an MFE's ShadowDOM elements.