@servicetitan/web-components 23.0.0 → 23.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.
Files changed (94) hide show
  1. package/dist/__mocks__/index.d.ts +3 -0
  2. package/dist/__mocks__/index.d.ts.map +1 -0
  3. package/dist/__mocks__/index.js +3 -0
  4. package/dist/__mocks__/index.js.map +1 -0
  5. package/dist/__mocks__/register-component.d.ts +4 -0
  6. package/dist/__mocks__/register-component.d.ts.map +1 -0
  7. package/dist/__mocks__/register-component.js +14 -0
  8. package/dist/__mocks__/register-component.js.map +1 -0
  9. package/dist/__mocks__/test-component.d.ts +3 -0
  10. package/dist/__mocks__/test-component.d.ts.map +1 -0
  11. package/dist/__mocks__/test-component.js +14 -0
  12. package/dist/__mocks__/test-component.js.map +1 -0
  13. package/dist/contexts/tests/mfe-data-context.test.js +1 -1
  14. package/dist/contexts/tests/mfe-data-context.test.js.map +1 -1
  15. package/dist/contexts/tests/mfe-metadata-context.test.js +1 -1
  16. package/dist/contexts/tests/mfe-metadata-context.test.js.map +1 -1
  17. package/dist/loader.d.ts +7 -17
  18. package/dist/loader.d.ts.map +1 -1
  19. package/dist/loader.js +74 -224
  20. package/dist/loader.js.map +1 -1
  21. package/dist/register.d.ts +4 -1
  22. package/dist/register.d.ts.map +1 -1
  23. package/dist/register.js +26 -13
  24. package/dist/register.js.map +1 -1
  25. package/dist/render.d.ts +11 -0
  26. package/dist/render.d.ts.map +1 -0
  27. package/dist/render.js +39 -0
  28. package/dist/render.js.map +1 -0
  29. package/dist/utils/get-bundle-info.d.ts +24 -0
  30. package/dist/utils/get-bundle-info.d.ts.map +1 -0
  31. package/dist/utils/get-bundle-info.js +98 -0
  32. package/dist/utils/get-bundle-info.js.map +1 -0
  33. package/dist/utils/get-entry-points.d.ts +6 -0
  34. package/dist/utils/get-entry-points.d.ts.map +1 -0
  35. package/dist/utils/get-entry-points.js +24 -0
  36. package/dist/utils/get-entry-points.js.map +1 -0
  37. package/dist/{get-mfe-supports-rerendering.d.ts → utils/get-mfe-supports-rerendering.d.ts} +1 -1
  38. package/dist/utils/get-mfe-supports-rerendering.d.ts.map +1 -0
  39. package/dist/utils/get-mfe-supports-rerendering.js.map +1 -0
  40. package/dist/{utils.d.ts → utils/get-version-mismatches.d.ts} +2 -3
  41. package/dist/utils/get-version-mismatches.d.ts.map +1 -0
  42. package/dist/{utils.js → utils/get-version-mismatches.js} +2 -10
  43. package/dist/utils/get-version-mismatches.js.map +1 -0
  44. package/dist/utils/index.d.ts +9 -0
  45. package/dist/utils/index.d.ts.map +1 -0
  46. package/dist/utils/index.js +9 -0
  47. package/dist/utils/index.js.map +1 -0
  48. package/dist/utils/is-versioned-url.d.ts +2 -0
  49. package/dist/utils/is-versioned-url.d.ts.map +1 -0
  50. package/dist/utils/is-versioned-url.js +9 -0
  51. package/dist/utils/is-versioned-url.js.map +1 -0
  52. package/dist/utils/load-scripts.d.ts +3 -0
  53. package/dist/utils/load-scripts.d.ts.map +1 -0
  54. package/dist/utils/load-scripts.js +31 -0
  55. package/dist/utils/load-scripts.js.map +1 -0
  56. package/dist/utils/validate-bundle-info.d.ts +7 -0
  57. package/dist/utils/validate-bundle-info.d.ts.map +1 -0
  58. package/dist/utils/validate-bundle-info.js +10 -0
  59. package/dist/utils/validate-bundle-info.js.map +1 -0
  60. package/dist/utils/web-component.d.ts +17 -0
  61. package/dist/utils/web-component.d.ts.map +1 -0
  62. package/dist/utils/web-component.js +54 -0
  63. package/dist/utils/web-component.js.map +1 -0
  64. package/package.json +17 -18
  65. package/src/__mocks__/index.ts +2 -0
  66. package/src/__mocks__/register-component.tsx +20 -0
  67. package/src/__mocks__/test-component.tsx +30 -0
  68. package/src/__tests__/event-bus.test.ts +3 -2
  69. package/src/__tests__/loader.test.tsx +17 -11
  70. package/src/__tests__/register-legacy-root.test.tsx +36 -0
  71. package/src/__tests__/register.test.tsx +16 -81
  72. package/src/contexts/tests/mfe-data-context.test.tsx +4 -4
  73. package/src/contexts/tests/mfe-metadata-context.test.tsx +4 -4
  74. package/src/loader.tsx +129 -374
  75. package/src/register.tsx +25 -14
  76. package/src/render.ts +62 -0
  77. package/src/{__tests__ → utils/__tests__}/get-mfe-supports-rerendering.test.ts +1 -1
  78. package/src/utils/__tests__/get-version-mismatches.test.ts +76 -0
  79. package/src/utils/__tests__/is-versioned-url.test.ts +18 -0
  80. package/src/utils/get-bundle-info.ts +121 -0
  81. package/src/utils/get-entry-points.ts +23 -0
  82. package/src/{get-mfe-supports-rerendering.ts → utils/get-mfe-supports-rerendering.ts} +1 -1
  83. package/src/{utils.ts → utils/get-version-mismatches.ts} +1 -12
  84. package/src/utils/index.ts +8 -0
  85. package/src/utils/is-versioned-url.ts +11 -0
  86. package/src/utils/load-scripts.ts +38 -0
  87. package/src/utils/validate-bundle-info.ts +15 -0
  88. package/src/utils/web-component.tsx +96 -0
  89. package/dist/get-mfe-supports-rerendering.d.ts.map +0 -1
  90. package/dist/get-mfe-supports-rerendering.js.map +0 -1
  91. package/dist/utils.d.ts.map +0 -1
  92. package/dist/utils.js.map +0 -1
  93. package/src/__tests__/utils.test.ts +0 -95
  94. /package/dist/{get-mfe-supports-rerendering.js → utils/get-mfe-supports-rerendering.js} +0 -0
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment, useMemo, useState } from 'react';
3
+ import { useOptionalDependencies } from '@servicetitan/react-ioc';
4
+ import { HistoryManager } from '../history-manager';
5
+ export function webComponent(props) {
6
+ const { basename, bundleInfo, eventBus, exposedDependencies, logService, LoadingFallback } = props;
7
+ const { WebComponent, disposer, mfeSupportsRerendering } = bundleInfo;
8
+ return (props) => {
9
+ const [isLoading, setIsLoading] = useState(true);
10
+ const [historyManager] = useOptionalDependencies(HistoryManager);
11
+ const handleRef = (webComponent) => {
12
+ webComponent === null || webComponent === void 0 ? void 0 : webComponent.provide({
13
+ logService,
14
+ historyManager,
15
+ exposedDependencies,
16
+ eventBus,
17
+ basename,
18
+ onReady: () => {
19
+ setIsLoading(false);
20
+ },
21
+ onDispose: () => {
22
+ disposer();
23
+ },
24
+ });
25
+ };
26
+ const stringifiedData = useMemo(() => JSON.stringify(props.data), [props.data]);
27
+ /*
28
+ * Set key for rendering the web component so that if the data from the Host change,
29
+ * and the MFE's version of startup doesn't support watching for data changes, then
30
+ * the web component will be re-mounted whenever the data changes.
31
+ * This is only used as a fallback when the MFE's version is older.
32
+ */
33
+ const key = mfeSupportsRerendering ? undefined : stringifiedData;
34
+ const dataAttrs = useMemo(() => composeDataAttributes(props.data), [props.data]);
35
+ const styleUrls = getStyleUrls(bundleInfo);
36
+ return (_jsxs(Fragment, { children: [isLoading && _jsx(LoadingFallback, {}), _jsx(WebComponent, Object.assign({ ref: handleRef, class: props.className }, (styleUrls && { 'style-urls': styleUrls }), dataAttrs, { "data-mfe-data": stringifiedData }), key)] }));
37
+ };
38
+ }
39
+ /*
40
+ * Convert camelCase data props to kebab-case data attributes, prepended with data-
41
+ * Ex: fooBar -> data-foo-bar
42
+ */
43
+ function composeDataAttributes(data = {}) {
44
+ return Object.fromEntries(Object.entries(data).map(([key, value]) => [
45
+ 'data-' + key.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase()),
46
+ value,
47
+ ]));
48
+ }
49
+ function getStyleUrls({ urls }) {
50
+ if (urls.css.length) {
51
+ return encodeURIComponent(JSON.stringify(urls.css));
52
+ }
53
+ }
54
+ //# sourceMappingURL=web-component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-component.js","sourceRoot":"","sources":["../../src/utils/web-component.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAMlE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAYpD,MAAM,UAAU,YAAY,CAAC,KAAwB;IACjD,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,GACtF,KAAK,CAAC;IACV,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,sBAAsB,EAAE,GAAG,UAAU,CAAC;IAEtE,OAAO,CAAC,KAAsB,EAAE,EAAE;QAC9B,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,cAAc,CAAC,GAAG,uBAAuB,CAAC,cAAc,CAAC,CAAC;QAEjE,MAAM,SAAS,GAAG,CAAC,YAAkC,EAAE,EAAE;YACrD,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,OAAO,CAAC;gBAClB,UAAU;gBACV,cAAc;gBACd,mBAAmB;gBACnB,QAAQ;gBACR,QAAQ;gBACR,OAAO,EAAE,GAAG,EAAE;oBACV,YAAY,CAAC,KAAK,CAAC,CAAC;gBACxB,CAAC;gBACD,SAAS,EAAE,GAAG,EAAE;oBACZ,QAAQ,EAAE,CAAC;gBACf,CAAC;aACJ,CAAC,CAAC;QACP,CAAC,CAAC;QAEF,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAEhF;;;;;WAKG;QACH,MAAM,GAAG,GAAG,sBAAsB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC;QACjE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACjF,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QAE3C,OAAO,CACH,MAAC,QAAQ,eACJ,SAAS,IAAI,KAAC,eAAe,KAAG,EACjC,KAAC,YAAY,kBAET,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,KAAK,CAAC,SAAS,IAClB,CAAC,SAAS,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,EAK1C,SAAS,qBAEE,eAAe,KAVzB,GAAG,CAWV,IACK,CACd,CAAC;IACN,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,OAAuC,EAAE;IACpE,OAAO,MAAM,CAAC,WAAW,CACrB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;QACvC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACrE,KAAK;KACR,CAAC,CACL,CAAC;AACN,CAAC;AAED,SAAS,YAAY,CAAC,EAAE,IAAI,EAA4B;IACpD,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE;QACjB,OAAO,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;KACvD;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/web-components",
3
- "version": "23.0.0",
3
+ "version": "23.2.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,36 +15,35 @@
15
15
  "src"
16
16
  ],
17
17
  "dependencies": {
18
- "@servicetitan/error-boundary": "23.0.0",
18
+ "@servicetitan/error-boundary": "23.2.0",
19
19
  "lodash.get": "~4.4.2",
20
20
  "mitt": "~3.0.1",
21
21
  "semver": "~7.5.4"
22
22
  },
23
23
  "devDependencies": {
24
- "@servicetitan/design-system": "~12.6.2",
25
- "@servicetitan/log-service": "23.0.0",
26
- "@servicetitan/react-ioc": "23.0.0",
27
- "@testing-library/dom": "^8.20.1",
24
+ "@servicetitan/design-system": "~14.0.0",
25
+ "@servicetitan/log-service": "23.2.0",
26
+ "@servicetitan/react-ioc": "23.2.0",
27
+ "@testing-library/dom": "^9.0.0",
28
28
  "@testing-library/jest-dom": "^6.4.2",
29
- "@testing-library/react": "^12.1.5",
30
- "@testing-library/react-hooks": "^8.0.1",
29
+ "@testing-library/react": "^14.2.1",
31
30
  "@types/history": "~4.7.10",
32
31
  "@types/lodash.get": "~4.4.6",
33
- "@types/react": "~17.0.38",
34
- "@types/react-dom": "~17.0.11",
32
+ "@types/react": "~18.2.55",
33
+ "@types/react-dom": "~18.2.19",
35
34
  "@types/semver": "~7.5.0",
36
35
  "history": "~4.10.1",
37
- "react": "~17.0.2",
38
- "react-dom": "~17.0.2",
36
+ "react": "~18.2.0",
37
+ "react-dom": "~18.2.0",
39
38
  "shadow-dom-testing-library": "^1.11.0"
40
39
  },
41
40
  "peerDependencies": {
42
- "@servicetitan/design-system": "~12.6.2",
43
- "@servicetitan/log-service": "23.0.0",
44
- "@servicetitan/react-ioc": "23.0.0",
41
+ "@servicetitan/design-system": ">=12.6.2",
42
+ "@servicetitan/log-service": "23.2.0",
43
+ "@servicetitan/react-ioc": "23.2.0",
45
44
  "history": "~4.10.1",
46
- "react": "~17.0.2",
47
- "react-dom": "~17.0.2"
45
+ "react": ">=17.0.2",
46
+ "react-dom": ">=17.0.2"
48
47
  },
49
48
  "publishConfig": {
50
49
  "access": "public"
@@ -52,5 +51,5 @@
52
51
  "cli": {
53
52
  "webpack": false
54
53
  },
55
- "gitHead": "45876ed36415ad78d3ec4ef3783d6df1430504d3"
54
+ "gitHead": "4b72bab5ab6cb52a688d9329c7fbaa702ae6780c"
56
55
  }
@@ -0,0 +1,2 @@
1
+ export * from './register-component';
2
+ export * from './test-component';
@@ -0,0 +1,20 @@
1
+ import { FunctionComponent } from 'react';
2
+ import { RegisterOptions } from '../register';
3
+
4
+ export function registerComponent(
5
+ TestComponent: FunctionComponent,
6
+ componentName: string | undefined,
7
+ options?: RegisterOptions
8
+ ) {
9
+ /*
10
+ * This register can only happen once, unfortunately. So the TestComponent needs to
11
+ * work for all tests. If ran more than once, window.customElements.define will throw
12
+ * the error: "NotSupportedError: This name has already been registered in the registry."
13
+ *
14
+ * Also note that there is not way to "unregister" a web component, so an afterAll
15
+ * cannot be used.
16
+ */
17
+ (global as any).WEB_COMPONENT_NAME = componentName;
18
+ const { register } = require('../register');
19
+ register(TestComponent, true, options);
20
+ }
@@ -0,0 +1,30 @@
1
+ import { FC, useContext } from 'react';
2
+ import { TablePopupPropsContext } from '@servicetitan/design-system';
3
+ import { useMFEDataContext, useMFEMetadataContext } from '../contexts';
4
+
5
+ export const MockComponent: FC<Record<string, any>> = props => {
6
+ const data = useMFEDataContext();
7
+ const metadata = useMFEMetadataContext();
8
+ const popupProps = useContext(TablePopupPropsContext);
9
+
10
+ return (
11
+ <div>
12
+ {Object.entries(props).map(([name, value]) => (
13
+ <span key={name}>{`Prop: ${name}, Value: ${stringifyValue(value)}`}</span>
14
+ ))}
15
+ {Object.entries(data).map(([name, value]) => (
16
+ <span key={name}>{`Data: ${name}, Value: ${stringifyValue(value)}`}</span>
17
+ ))}
18
+ {Object.entries(metadata).map(([name, value]) => (
19
+ <span key={name}>{`Metadata: ${name}, Value: ${stringifyValue(value)}`}</span>
20
+ ))}
21
+ <span>TablePopupPropsContext appendTo: {String(popupProps({}).appendTo)}</span>
22
+ </div>
23
+ );
24
+ };
25
+
26
+ function stringifyValue(value: any) {
27
+ return JSON.stringify(value, (_key, value) =>
28
+ value instanceof ShadowRoot ? 'ShadowRoot' : value
29
+ );
30
+ }
@@ -1,5 +1,5 @@
1
1
  import * as mitt from 'mitt';
2
- import { renderHook } from '@testing-library/react-hooks';
2
+ import { renderHook } from '@testing-library/react';
3
3
  import { useEventBus, EventBus } from '../event-bus';
4
4
 
5
5
  jest.mock('mitt', () => jest.fn());
@@ -138,9 +138,10 @@ describe(`[web-components] ${useEventBus.name}`, () => {
138
138
  test('when the component using useEventBus is re-rendered, useEventBus provides the same event bus instance', () => {
139
139
  const { result, rerender } = subject();
140
140
 
141
+ const firstInstance = result.current;
141
142
  rerender();
142
143
 
143
- const [firstInstance, secondInstance] = result.all;
144
+ const secondInstance = result.current;
144
145
  expect(firstInstance).toBe(secondInstance);
145
146
  });
146
147
  });
@@ -1,14 +1,20 @@
1
- import { Metadata, Loader, LoaderProps } from '../loader';
2
- import { act, render, screen, waitFor } from '@testing-library/react';
3
1
  import '@testing-library/jest-dom';
4
- import { Entries, EXPOSED_DEPENDENCIES_TOKEN } from '../common';
5
- import * as common from '../common';
6
- import * as utils from '../utils';
7
- import { FC } from 'react';
8
- import { Provider, ProviderProps } from '@servicetitan/react-ioc';
2
+ import { act, render, screen, waitFor } from '@testing-library/react';
9
3
  import { Log } from '@servicetitan/log-service';
10
- import { HistoryManager } from '../history-manager';
4
+ import { Provider, ProviderProps } from '@servicetitan/react-ioc';
5
+ import { FC } from 'react';
6
+
7
+ import * as common from '../common';
8
+ import { Entries, EXPOSED_DEPENDENCIES_TOKEN } from '../common';
11
9
  import { EVENT_BUS_TOKEN } from '../event-bus';
10
+ import { HistoryManager } from '../history-manager';
11
+ import { Loader, LoaderProps } from '../loader';
12
+ import { Metadata } from '../utils/get-bundle-info';
13
+ import { getVersionMismatches } from '../utils/get-version-mismatches';
14
+
15
+ jest.mock('../utils/get-version-mismatches', () => ({
16
+ getVersionMismatches: jest.fn(),
17
+ }));
12
18
 
13
19
  describe('[web-components] Loader', () => {
14
20
  const baseUrl = 'http://localhost/mfe';
@@ -108,7 +114,7 @@ describe('[web-components] Loader', () => {
108
114
  }
109
115
  return getJsonMockFns[url]();
110
116
  });
111
- jest.spyOn(utils, 'getVersionMismatches').mockReturnValue({});
117
+ jest.mocked(getVersionMismatches).mockReturnValue({});
112
118
  });
113
119
 
114
120
  function subject() {
@@ -249,7 +255,7 @@ describe('[web-components] Loader', () => {
249
255
 
250
256
  test('dependencies are pulled from package.json', async () => {
251
257
  await scriptLoadedSetup();
252
- expect(utils.getVersionMismatches).toHaveBeenCalledWith(
258
+ expect(getVersionMismatches).toHaveBeenCalledWith(
253
259
  expect.anything(),
254
260
  dependencies,
255
261
  expect.anything()
@@ -586,7 +592,7 @@ describe('[web-components] Loader', () => {
586
592
  };
587
593
 
588
594
  beforeEach(() => {
589
- jest.spyOn(utils, 'getVersionMismatches').mockReturnValue(mismatchedDeps);
595
+ jest.mocked(getVersionMismatches).mockReturnValue(mismatchedDeps);
590
596
  });
591
597
 
592
598
  itAddsJsEntrypointToWebComponent(
@@ -0,0 +1,36 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render } from '@testing-library/react';
3
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
+ // @ts-ignore because Jest mistakenly complains that "'shadow-dom-testing-library' cannot be imported using this construct".
5
+ import { screen } from 'shadow-dom-testing-library';
6
+
7
+ import { MockComponent, registerComponent } from '../__mocks__';
8
+
9
+ describe('[web-components] register', () => {
10
+ const componentName = 'mock-legacy-root-app';
11
+
12
+ beforeAll(() => {
13
+ registerComponent(MockComponent, componentName, { legacyRoot: true });
14
+ });
15
+
16
+ const subject = () => {
17
+ const Component: any = componentName;
18
+ return render(<Component />);
19
+ };
20
+
21
+ describe('when the web component data attribute is changed', () => {
22
+ const setup = () => {
23
+ subject();
24
+
25
+ const webComponent = document.getElementsByTagName(componentName);
26
+ webComponent[0]?.setAttribute('data-mfe-data', JSON.stringify({ string: 'baz' }));
27
+ };
28
+
29
+ test('useMFEDataContext provides updated data', () => {
30
+ setup();
31
+
32
+ expect(screen.getByShadowText(`Prop: string, Value: "baz"`)).toBeInTheDocument();
33
+ expect(screen.getByShadowText(`Data: string, Value: "baz"`)).toBeInTheDocument();
34
+ });
35
+ });
36
+ });
@@ -1,16 +1,13 @@
1
- import { FC, useContext } from 'react';
2
- import { TablePopupPropsContext } from '@servicetitan/design-system';
3
- import { render } from '@testing-library/react';
1
+ import '@testing-library/jest-dom';
2
+ import { act, render } from '@testing-library/react';
4
3
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
5
4
  // @ts-ignore because Jest mistakenly complains that "'shadow-dom-testing-library' cannot be imported using this construct".
6
5
  import { screen } from 'shadow-dom-testing-library';
7
- import '@testing-library/jest-dom';
8
6
 
9
7
  import { IWebComponent } from '../common';
10
- import { useMFEDataContext, useMFEMetadataContext } from '../contexts';
8
+ import { MockComponent, registerComponent } from '../__mocks__';
11
9
 
12
10
  describe('[web-components] register', () => {
13
- let onloadRefs: Function[];
14
11
  const componentName = 'mock-app';
15
12
  const handleRef = (webComponent: IWebComponent | null) => {
16
13
  webComponent?.provide({
@@ -18,31 +15,7 @@ describe('[web-components] register', () => {
18
15
  onDispose: jest.fn(),
19
16
  });
20
17
  };
21
- function stringifyValue(value: any) {
22
- return JSON.stringify(value, (_key, value) =>
23
- value instanceof ShadowRoot ? 'ShadowRoot' : value
24
- );
25
- }
26
- const TestComponent: FC<Record<string, any>> = props => {
27
- const data = useMFEDataContext();
28
- const metadata = useMFEMetadataContext();
29
- const popupProps = useContext(TablePopupPropsContext);
30
-
31
- return (
32
- <div>
33
- {Object.entries(props).map(([name, value]) => (
34
- <span key={name}>{`Prop: ${name}, Value: ${stringifyValue(value)}`}</span>
35
- ))}
36
- {Object.entries(data).map(([name, value]) => (
37
- <span key={name}>{`Data: ${name}, Value: ${stringifyValue(value)}`}</span>
38
- ))}
39
- {Object.entries(metadata).map(([name, value]) => (
40
- <span key={name}>{`Metadata: ${name}, Value: ${stringifyValue(value)}`}</span>
41
- ))}
42
- <span>TablePopupPropsContext appendTo: {String(popupProps({}).appendTo)}</span>
43
- </div>
44
- );
45
- };
18
+ let onloadRefs: Function[];
46
19
 
47
20
  class Page {
48
21
  static get componentShadowRoot() {
@@ -55,18 +28,7 @@ describe('[web-components] register', () => {
55
28
  }
56
29
 
57
30
  beforeAll(() => {
58
- /*
59
- * This register can only happen once, unfortunately. So the TestComponent needs to
60
- * work for all tests. If ran more than once, window.customElements.define will throw
61
- * the error: "NotSupportedError: This name has already been registered in the registry."
62
- *
63
- * Also note that there is not way to "unregister" a web component, so an afterAll
64
- * cannot be used.
65
- */
66
- (global as any).WEB_COMPONENT_NAME = componentName;
67
- const { register } = require('../register');
68
- register(TestComponent, true);
69
-
31
+ registerComponent(MockComponent, componentName);
70
32
  /*
71
33
  * mock <link>'s onload so that we can call it directly from the test,
72
34
  * because it won't call itself in the test environment
@@ -91,9 +53,11 @@ describe('[web-components] register', () => {
91
53
  const Component: any = componentName;
92
54
  const renderResult = render(<Component ref={handleRef} {...data} {...dataAttrs} />);
93
55
 
94
- for (const onload of onloadRefs) {
95
- onload();
96
- }
56
+ act(() => {
57
+ for (const onload of onloadRefs) {
58
+ onload();
59
+ }
60
+ });
97
61
 
98
62
  return renderResult;
99
63
  };
@@ -200,49 +164,20 @@ describe('[web-components] register', () => {
200
164
  ).toBeInTheDocument()
201
165
  );
202
166
  });
203
-
204
- describe('when the web component data attr is changed', () => {
205
- const setup = () => {
206
- subject(props);
207
-
208
- const webComponent = document.getElementsByTagName(componentName);
209
- webComponent[0]?.setAttribute(
210
- 'data-mfe-data',
211
- JSON.stringify({ ...props, string: 'baz' })
212
- );
213
- };
214
-
215
- test('useMFEDataContext provides updated data', () => {
216
- setup();
217
-
218
- expect(screen.getByShadowText(`Prop: string, Value: "baz"`)).toBeInTheDocument();
219
- expect(screen.getByShadowText(`Data: string, Value: "baz"`)).toBeInTheDocument();
220
- });
221
- });
222
167
  });
223
168
 
224
- describe('when WEB_COMPONENT_NAME is not set', () => {
225
- let register: typeof import('../register').register;
226
-
227
- beforeEach(() => {
228
- jest.isolateModules(() => {
229
- (global as any).WEB_COMPONENT_NAME = undefined;
230
- register = require('../register').register;
231
- });
232
- jest.spyOn(console, 'error').mockImplementation(jest.fn());
233
- });
234
-
235
- test('logs an error', () => {
236
- register(TestComponent, true);
237
- // eslint-disable-next-line no-console
238
- expect(console.error).toHaveBeenCalledWith('"WEB_COMPONENT_NAME" is not defined!');
169
+ test('logs an error when WEB_COMPONENT_NAME is not set', () => {
170
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn());
171
+ jest.isolateModules(() => {
172
+ registerComponent(MockComponent, undefined);
239
173
  });
174
+ expect(consoleSpy).toHaveBeenCalledWith('"WEB_COMPONENT_NAME" is not defined!');
240
175
  });
241
176
 
242
177
  test('configures the shadow root in PopupPropsContext', () => {
243
178
  subject();
244
179
  expect(screen.getByShadowText(/TablePopupPropsContext appendTo:/)).toHaveTextContent(
245
- /[object ShadowRoot]/
180
+ /\[object ShadowRoot\]/
246
181
  );
247
182
  });
248
183
  });
@@ -1,5 +1,5 @@
1
- import { FC } from 'react';
2
- import { renderHook } from '@testing-library/react-hooks';
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { renderHook } from '@testing-library/react';
3
3
  import { MFEDataContext, useMFEDataContext } from '../mfe-data-context';
4
4
 
5
5
  describe(`[web-components] ${useMFEDataContext.name}`, () => {
@@ -7,7 +7,7 @@ describe(`[web-components] ${useMFEDataContext.name}`, () => {
7
7
 
8
8
  describe('when the MFEDataContext is provided by a parent', () => {
9
9
  const data = { foo: 'bar' };
10
- const Wrapper: FC = ({ children }) => {
10
+ const Wrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
11
11
  return <MFEDataContext.Provider value={data}>{children}</MFEDataContext.Provider>;
12
12
  };
13
13
 
@@ -17,7 +17,7 @@ describe(`[web-components] ${useMFEDataContext.name}`, () => {
17
17
  });
18
18
 
19
19
  describe('when the MFEDataContext is not provided by a parent', () => {
20
- const Wrapper: FC = ({ children }) => {
20
+ const Wrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
21
21
  return <div>{children}</div>;
22
22
  };
23
23
 
@@ -1,5 +1,5 @@
1
- import { FC } from 'react';
2
- import { renderHook } from '@testing-library/react-hooks';
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { renderHook } from '@testing-library/react';
3
3
  import { MFEMetadataContext, useMFEMetadataContext } from '../mfe-metadata-context';
4
4
 
5
5
  describe(`[web-components] ${useMFEMetadataContext.name}`, () => {
@@ -9,7 +9,7 @@ describe(`[web-components] ${useMFEMetadataContext.name}`, () => {
9
9
  const shadowRoot = document.createElement('div').attachShadow({ mode: 'open' });
10
10
  const portalShadowRoot = document.createElement('span').attachShadow({ mode: 'open' });
11
11
  const metadata = { shadowRoot, portalShadowRoot };
12
- const Wrapper: FC = ({ children }) => {
12
+ const Wrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
13
13
  return (
14
14
  <MFEMetadataContext.Provider value={metadata}>
15
15
  {children}
@@ -23,7 +23,7 @@ describe(`[web-components] ${useMFEMetadataContext.name}`, () => {
23
23
  });
24
24
 
25
25
  describe('when the MFEMetadataContext is not provided by a parent', () => {
26
- const Wrapper: FC = ({ children }) => {
26
+ const Wrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
27
27
  return <div>{children}</div>;
28
28
  };
29
29