piral-core 0.15.0-alpha.4036 → 0.15.0-alpha.4122

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 (284) hide show
  1. package/esm/Piral.d.ts +2 -2
  2. package/esm/Piral.js +14 -12
  3. package/esm/Piral.js.map +1 -1
  4. package/esm/PiralContext.d.ts +21 -0
  5. package/esm/PiralContext.js +34 -0
  6. package/esm/PiralContext.js.map +1 -0
  7. package/esm/actions/app.d.ts +1 -2
  8. package/esm/actions/app.js +0 -3
  9. package/esm/actions/app.js.map +1 -1
  10. package/esm/actions/state.js +5 -1
  11. package/esm/actions/state.js.map +1 -1
  12. package/esm/components/ErrorBoundary.d.ts +4 -0
  13. package/esm/components/ErrorBoundary.js +3 -3
  14. package/esm/components/ErrorBoundary.js.map +1 -1
  15. package/esm/components/ForeignComponentContainer.d.ts +1 -1
  16. package/esm/components/ForeignComponentContainer.js.map +1 -1
  17. package/esm/components/PiralGlobals.d.ts +6 -0
  18. package/esm/components/PiralGlobals.js +13 -0
  19. package/esm/components/PiralGlobals.js.map +1 -0
  20. package/esm/components/PiralSuspense.d.ts +5 -0
  21. package/esm/components/PiralSuspense.js +8 -0
  22. package/esm/components/PiralSuspense.js.map +1 -0
  23. package/esm/components/PiralView.d.ts +10 -1
  24. package/esm/components/PiralView.js +12 -24
  25. package/esm/components/PiralView.js.map +1 -1
  26. package/esm/components/ResponsiveLayout.d.ts +9 -1
  27. package/esm/components/ResponsiveLayout.js +5 -12
  28. package/esm/components/ResponsiveLayout.js.map +1 -1
  29. package/esm/components/components.d.ts +13 -6
  30. package/esm/components/components.js +13 -6
  31. package/esm/components/components.js.map +1 -1
  32. package/esm/components/index.d.ts +2 -12
  33. package/esm/components/index.js +2 -12
  34. package/esm/components/index.js.map +1 -1
  35. package/esm/components/wrapComponent.d.ts +1 -1
  36. package/esm/{components → defaults}/DefaultErrorInfo.d.ts +0 -0
  37. package/esm/{components → defaults}/DefaultErrorInfo.js +1 -2
  38. package/esm/defaults/DefaultErrorInfo.js.map +1 -0
  39. package/esm/{components → defaults}/DefaultLayout.d.ts +0 -0
  40. package/esm/{components → defaults}/DefaultLayout.js +0 -0
  41. package/esm/defaults/DefaultLayout.js.map +1 -0
  42. package/esm/{components/DefaultLoader.d.ts → defaults/DefaultLoadingIndicator.d.ts} +0 -0
  43. package/esm/{components/DefaultLoader.js → defaults/DefaultLoadingIndicator.js} +1 -1
  44. package/esm/defaults/DefaultLoadingIndicator.js.map +1 -0
  45. package/esm/{components → defaults}/DefaultRouteSwitch.d.ts +0 -0
  46. package/esm/{components → defaults}/DefaultRouteSwitch.js +0 -0
  47. package/esm/defaults/DefaultRouteSwitch.js.map +1 -0
  48. package/esm/defaults/DefaultRouter.d.ts +3 -0
  49. package/esm/{components → defaults}/DefaultRouter.js +0 -0
  50. package/esm/defaults/DefaultRouter.js.map +1 -0
  51. package/esm/defaults/index.d.ts +5 -0
  52. package/esm/defaults/index.js +6 -0
  53. package/esm/defaults/index.js.map +1 -0
  54. package/esm/index.d.ts +1 -0
  55. package/esm/index.js +1 -0
  56. package/esm/index.js.map +1 -1
  57. package/esm/{components → setters}/SetComponent.d.ts +0 -0
  58. package/esm/{components → setters}/SetComponent.js +0 -0
  59. package/esm/setters/SetComponent.js.map +1 -0
  60. package/esm/{components → setters}/SetError.d.ts +0 -0
  61. package/esm/{components → setters}/SetError.js +0 -0
  62. package/esm/setters/SetError.js.map +1 -0
  63. package/esm/{components → setters}/SetErrors.d.ts +0 -0
  64. package/esm/{components → setters}/SetErrors.js +0 -0
  65. package/esm/setters/SetErrors.js.map +1 -0
  66. package/esm/{components → setters}/SetLayout.d.ts +0 -0
  67. package/esm/{components → setters}/SetLayout.js +0 -0
  68. package/esm/setters/SetLayout.js.map +1 -0
  69. package/esm/{components → setters}/SetProvider.d.ts +0 -0
  70. package/esm/{components → setters}/SetProvider.js +0 -0
  71. package/esm/setters/SetProvider.js.map +1 -0
  72. package/esm/{components → setters}/SetRedirect.d.ts +0 -0
  73. package/esm/{components → setters}/SetRedirect.js +0 -0
  74. package/esm/setters/SetRedirect.js.map +1 -0
  75. package/esm/{components → setters}/SetRoute.d.ts +0 -0
  76. package/esm/{components → setters}/SetRoute.js +0 -0
  77. package/esm/setters/SetRoute.js.map +1 -0
  78. package/esm/setters/index.d.ts +7 -0
  79. package/esm/setters/index.js +8 -0
  80. package/esm/setters/index.js.map +1 -0
  81. package/esm/state/createGlobalState.js +1 -2
  82. package/esm/state/createGlobalState.js.map +1 -1
  83. package/esm/state/withApi.js.map +1 -1
  84. package/esm/types/components.d.ts +9 -1
  85. package/esm/types/instance.d.ts +10 -3
  86. package/esm/types/state.d.ts +2 -12
  87. package/esm/utils/compare.d.ts +1 -1
  88. package/esm/utils/compare.js +20 -3
  89. package/esm/utils/compare.js.map +1 -1
  90. package/esm/utils/media.js +1 -1
  91. package/esm/utils/media.js.map +1 -1
  92. package/lib/Piral.d.ts +2 -2
  93. package/lib/Piral.js +13 -11
  94. package/lib/Piral.js.map +1 -1
  95. package/lib/PiralContext.d.ts +21 -0
  96. package/lib/PiralContext.js +38 -0
  97. package/lib/PiralContext.js.map +1 -0
  98. package/lib/actions/app.d.ts +1 -2
  99. package/lib/actions/app.js +1 -5
  100. package/lib/actions/app.js.map +1 -1
  101. package/lib/actions/index.js +6 -6
  102. package/lib/actions/index.js.map +1 -1
  103. package/lib/actions/state.js +5 -1
  104. package/lib/actions/state.js.map +1 -1
  105. package/lib/components/ErrorBoundary.d.ts +4 -0
  106. package/lib/components/ErrorBoundary.js +3 -3
  107. package/lib/components/ErrorBoundary.js.map +1 -1
  108. package/lib/components/ForeignComponentContainer.d.ts +1 -1
  109. package/lib/components/ForeignComponentContainer.js.map +1 -1
  110. package/lib/components/PiralGlobals.d.ts +6 -0
  111. package/lib/components/PiralGlobals.js +17 -0
  112. package/lib/components/PiralGlobals.js.map +1 -0
  113. package/lib/components/PiralRoutes.js +1 -1
  114. package/lib/components/PiralRoutes.js.map +1 -1
  115. package/lib/components/PiralSuspense.d.ts +5 -0
  116. package/lib/components/PiralSuspense.js +12 -0
  117. package/lib/components/PiralSuspense.js.map +1 -0
  118. package/lib/components/PiralView.d.ts +10 -1
  119. package/lib/components/PiralView.js +11 -23
  120. package/lib/components/PiralView.js.map +1 -1
  121. package/lib/components/ResponsiveLayout.d.ts +9 -1
  122. package/lib/components/ResponsiveLayout.js +3 -10
  123. package/lib/components/ResponsiveLayout.js.map +1 -1
  124. package/lib/components/components.d.ts +13 -6
  125. package/lib/components/components.js +14 -7
  126. package/lib/components/components.js.map +1 -1
  127. package/lib/components/index.d.ts +2 -12
  128. package/lib/components/index.js +12 -22
  129. package/lib/components/index.js.map +1 -1
  130. package/lib/components/wrapComponent.d.ts +1 -1
  131. package/lib/{components → defaults}/DefaultErrorInfo.d.ts +0 -0
  132. package/lib/{components → defaults}/DefaultErrorInfo.js +2 -3
  133. package/lib/defaults/DefaultErrorInfo.js.map +1 -0
  134. package/lib/{components → defaults}/DefaultLayout.d.ts +0 -0
  135. package/lib/{components → defaults}/DefaultLayout.js +0 -0
  136. package/lib/defaults/DefaultLayout.js.map +1 -0
  137. package/lib/{components/DefaultLoader.d.ts → defaults/DefaultLoadingIndicator.d.ts} +0 -0
  138. package/lib/{components/DefaultLoader.js → defaults/DefaultLoadingIndicator.js} +1 -1
  139. package/lib/defaults/DefaultLoadingIndicator.js.map +1 -0
  140. package/lib/{components → defaults}/DefaultRouteSwitch.d.ts +0 -0
  141. package/lib/{components → defaults}/DefaultRouteSwitch.js +1 -1
  142. package/lib/defaults/DefaultRouteSwitch.js.map +1 -0
  143. package/lib/defaults/DefaultRouter.d.ts +3 -0
  144. package/lib/{components → defaults}/DefaultRouter.js +0 -0
  145. package/lib/defaults/DefaultRouter.js.map +1 -0
  146. package/lib/defaults/index.d.ts +5 -0
  147. package/lib/defaults/index.js +9 -0
  148. package/lib/defaults/index.js.map +1 -0
  149. package/lib/hooks/actions.js +1 -1
  150. package/lib/hooks/actions.js.map +1 -1
  151. package/lib/hooks/index.js +6 -6
  152. package/lib/hooks/index.js.map +1 -1
  153. package/lib/index.d.ts +1 -0
  154. package/lib/index.js +9 -8
  155. package/lib/index.js.map +1 -1
  156. package/lib/modules/index.js +3 -3
  157. package/lib/modules/index.js.map +1 -1
  158. package/lib/{components → setters}/SetComponent.d.ts +0 -0
  159. package/lib/{components → setters}/SetComponent.js +0 -0
  160. package/lib/setters/SetComponent.js.map +1 -0
  161. package/lib/{components → setters}/SetError.d.ts +0 -0
  162. package/lib/{components → setters}/SetError.js +0 -0
  163. package/lib/setters/SetError.js.map +1 -0
  164. package/lib/{components → setters}/SetErrors.d.ts +0 -0
  165. package/lib/{components → setters}/SetErrors.js +0 -0
  166. package/lib/setters/SetErrors.js.map +1 -0
  167. package/lib/{components → setters}/SetLayout.d.ts +0 -0
  168. package/lib/{components → setters}/SetLayout.js +0 -0
  169. package/lib/setters/SetLayout.js.map +1 -0
  170. package/lib/{components → setters}/SetProvider.d.ts +0 -0
  171. package/lib/{components → setters}/SetProvider.js +0 -0
  172. package/lib/setters/SetProvider.js.map +1 -0
  173. package/lib/{components → setters}/SetRedirect.d.ts +0 -0
  174. package/lib/{components → setters}/SetRedirect.js +0 -0
  175. package/lib/setters/SetRedirect.js.map +1 -0
  176. package/lib/{components → setters}/SetRoute.d.ts +0 -0
  177. package/lib/{components → setters}/SetRoute.js +0 -0
  178. package/lib/setters/SetRoute.js.map +1 -0
  179. package/lib/setters/index.d.ts +7 -0
  180. package/lib/setters/index.js +11 -0
  181. package/lib/setters/index.js.map +1 -0
  182. package/lib/state/createGlobalState.js +6 -7
  183. package/lib/state/createGlobalState.js.map +1 -1
  184. package/lib/state/index.js +4 -4
  185. package/lib/state/index.js.map +1 -1
  186. package/lib/state/withApi.js.map +1 -1
  187. package/lib/types/components.d.ts +9 -1
  188. package/lib/types/index.js +12 -12
  189. package/lib/types/index.js.map +1 -1
  190. package/lib/types/instance.d.ts +10 -3
  191. package/lib/types/state.d.ts +2 -12
  192. package/lib/utils/compare.d.ts +1 -1
  193. package/lib/utils/compare.js +22 -5
  194. package/lib/utils/compare.js.map +1 -1
  195. package/lib/utils/helpers.js +1 -1
  196. package/lib/utils/helpers.js.map +1 -1
  197. package/lib/utils/index.js +10 -10
  198. package/lib/utils/index.js.map +1 -1
  199. package/lib/utils/media.js +1 -1
  200. package/lib/utils/media.js.map +1 -1
  201. package/package.json +33 -8
  202. package/src/Piral.test.tsx +3 -3
  203. package/src/Piral.tsx +18 -14
  204. package/src/PiralContext.tsx +43 -0
  205. package/src/RootListener.test.tsx +7 -5
  206. package/src/actions/app.test.ts +1 -19
  207. package/src/actions/app.ts +0 -8
  208. package/src/actions/state.ts +6 -1
  209. package/src/components/ErrorBoundary.tsx +7 -3
  210. package/src/components/ForeignComponentContainer.test.tsx +25 -16
  211. package/src/components/ForeignComponentContainer.tsx +1 -2
  212. package/src/components/PiralGlobals.tsx +16 -0
  213. package/src/components/PiralRoutes.test.tsx +1 -1
  214. package/src/components/PiralSuspense.tsx +19 -0
  215. package/src/components/PiralView-server.test.tsx +27 -26
  216. package/src/components/PiralView.test.tsx +1 -0
  217. package/src/components/PiralView.tsx +28 -47
  218. package/src/components/ResponsiveLayout.test.tsx +14 -43
  219. package/src/components/ResponsiveLayout.tsx +18 -15
  220. package/src/components/components.tsx +13 -6
  221. package/src/components/index.ts +2 -12
  222. package/src/components/wrapComponent.tsx +1 -1
  223. package/src/{components → defaults}/DefaultErrorInfo.test.tsx +0 -0
  224. package/src/{components → defaults}/DefaultErrorInfo.tsx +1 -2
  225. package/src/{components → defaults}/DefaultLayout.test.tsx +0 -0
  226. package/src/{components → defaults}/DefaultLayout.tsx +1 -1
  227. package/src/{components/DefaultLoader.test.tsx → defaults/DefaultLoadingIndicator.test.tsx} +2 -2
  228. package/src/{components/DefaultLoader.tsx → defaults/DefaultLoadingIndicator.tsx} +0 -0
  229. package/src/{components → defaults}/DefaultRouteSwitch.tsx +0 -0
  230. package/src/{components → defaults}/DefaultRouter.tsx +2 -1
  231. package/src/defaults/index.ts +5 -0
  232. package/src/hooks/setter.test.ts +3 -2
  233. package/src/index.tsx +1 -0
  234. package/src/{components → setters}/SetComponent.test.tsx +0 -0
  235. package/src/{components → setters}/SetComponent.tsx +0 -0
  236. package/src/{components → setters}/SetError.test.tsx +0 -0
  237. package/src/{components → setters}/SetError.tsx +0 -0
  238. package/src/{components → setters}/SetErrors.test.tsx +0 -0
  239. package/src/{components → setters}/SetErrors.tsx +0 -0
  240. package/src/{components → setters}/SetLayout.test.tsx +0 -0
  241. package/src/{components → setters}/SetLayout.tsx +0 -0
  242. package/src/{components → setters}/SetProvider.test.tsx +0 -0
  243. package/src/{components → setters}/SetProvider.tsx +0 -0
  244. package/src/{components → setters}/SetRedirect.test.tsx +0 -0
  245. package/src/{components → setters}/SetRedirect.tsx +0 -0
  246. package/src/{components → setters}/SetRoute.test.tsx +0 -0
  247. package/src/{components → setters}/SetRoute.tsx +0 -0
  248. package/src/setters/index.ts +7 -0
  249. package/src/state/createGlobalState.test.ts +1 -10
  250. package/src/state/createGlobalState.ts +2 -3
  251. package/src/state/withApi.tsx +2 -3
  252. package/src/types/components.ts +11 -2
  253. package/src/types/instance.ts +11 -3
  254. package/src/types/state.ts +2 -12
  255. package/src/utils/compare.test.ts +15 -15
  256. package/src/utils/compare.ts +23 -3
  257. package/src/utils/foreign.test.ts +1 -1
  258. package/src/utils/media.ts +1 -1
  259. package/esm/components/DefaultErrorInfo.js.map +0 -1
  260. package/esm/components/DefaultLayout.js.map +0 -1
  261. package/esm/components/DefaultLoader.js.map +0 -1
  262. package/esm/components/DefaultRouteSwitch.js.map +0 -1
  263. package/esm/components/DefaultRouter.d.ts +0 -2
  264. package/esm/components/DefaultRouter.js.map +0 -1
  265. package/esm/components/SetComponent.js.map +0 -1
  266. package/esm/components/SetError.js.map +0 -1
  267. package/esm/components/SetErrors.js.map +0 -1
  268. package/esm/components/SetLayout.js.map +0 -1
  269. package/esm/components/SetProvider.js.map +0 -1
  270. package/esm/components/SetRedirect.js.map +0 -1
  271. package/esm/components/SetRoute.js.map +0 -1
  272. package/lib/components/DefaultErrorInfo.js.map +0 -1
  273. package/lib/components/DefaultLayout.js.map +0 -1
  274. package/lib/components/DefaultLoader.js.map +0 -1
  275. package/lib/components/DefaultRouteSwitch.js.map +0 -1
  276. package/lib/components/DefaultRouter.d.ts +0 -2
  277. package/lib/components/DefaultRouter.js.map +0 -1
  278. package/lib/components/SetComponent.js.map +0 -1
  279. package/lib/components/SetError.js.map +0 -1
  280. package/lib/components/SetErrors.js.map +0 -1
  281. package/lib/components/SetLayout.js.map +0 -1
  282. package/lib/components/SetProvider.js.map +0 -1
  283. package/lib/components/SetRedirect.js.map +0 -1
  284. package/lib/components/SetRoute.js.map +0 -1
package/src/Piral.tsx CHANGED
@@ -1,15 +1,23 @@
1
1
  import * as React from 'react';
2
- import { StateContext } from './state';
2
+ import { StaticRouter } from 'react-router';
3
3
  import { createInstance } from './createInstance';
4
- import { PiralView, Mediator, ResponsiveLayout, PortalRenderer } from './components';
5
- import { RootListener } from './RootListener';
4
+ import { PiralView, RegisteredRouter } from './components';
5
+ import { useGlobalState } from './hooks';
6
+ import { PiralContext } from './PiralContext';
6
7
  import type { PiralProps } from './types';
7
8
 
9
+ const FallbackRouter: React.FC = (props) => {
10
+ const publicPath = useGlobalState((s) => s.app.publicPath);
11
+ return <StaticRouter location="/" {...props} basename={publicPath} />;
12
+ };
13
+
14
+ const Router = typeof window === 'undefined' ? FallbackRouter : RegisteredRouter;
15
+
8
16
  /**
9
17
  * Represents the Piral app shell frame. Use this component together
10
18
  * with an existing instance to render the app shell.
11
- * Includes layout and routing handling. Wires the state container
12
- * to the generated views.
19
+ * Includes layout and routing handling. Connects the Piral context
20
+ * and the React router to the generated views.
13
21
  *
14
22
  * @example
15
23
  ```jsx
@@ -21,14 +29,10 @@ const app = (
21
29
  ```
22
30
  */
23
31
  export const Piral: React.FC<PiralProps> = ({ instance = createInstance(), breakpoints, children }) => (
24
- <StateContext.Provider value={instance.context}>
25
- <ResponsiveLayout breakpoints={breakpoints} />
26
- <Mediator options={instance.options} key={instance.id} />
27
- <RootListener />
28
- <PiralView>
29
- <PortalRenderer id="root" />
30
- {children}
31
- </PiralView>
32
- </StateContext.Provider>
32
+ <PiralContext instance={instance}>
33
+ <Router>
34
+ <PiralView breakpoints={breakpoints}>{children}</PiralView>
35
+ </Router>
36
+ </PiralContext>
33
37
  );
34
38
  Piral.displayName = 'Piral';
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { StateContext } from './state';
3
+ import { createInstance } from './createInstance';
4
+ import { Mediator } from './components';
5
+ import { useGlobalState } from './hooks';
6
+ import { RootListener } from './RootListener';
7
+ import type { PiralContextProps } from './types';
8
+
9
+ interface PiralProviderProps {
10
+ children: React.ReactNode;
11
+ }
12
+
13
+ const PiralProvider: React.FC<PiralProviderProps> = ({ children }) => {
14
+ const Provider = useGlobalState((m) => m.provider || React.Fragment);
15
+ return <Provider>{children}</Provider>;
16
+ };
17
+
18
+ /**
19
+ * Represents the Piral app shell frame. Use this component together
20
+ * with an existing instance to render components from micro frontends
21
+ * in your app.
22
+ * Wires the state container together with the global providers.
23
+ *
24
+ * @example
25
+ ```jsx
26
+ const app = (
27
+ <MyRouter>
28
+ <PiralContext instance={yourPiralInstance}>
29
+ <PiralGlobals />
30
+ <MyAppContent />
31
+ </PiralContext>
32
+ </MyRouter>
33
+ );
34
+ ```
35
+ */
36
+ export const PiralContext: React.FC<PiralContextProps> = ({ instance = createInstance(), children }) => (
37
+ <StateContext.Provider value={instance.context}>
38
+ <Mediator options={instance.options} key={instance.id} />
39
+ <RootListener />
40
+ <PiralProvider>{children}</PiralProvider>
41
+ </StateContext.Provider>
42
+ );
43
+ PiralContext.displayName = 'PiralContext';
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { render, unmountComponentAtNode } from 'react-dom';
2
+ import { createRoot } from 'react-dom/client';
3
3
  import { act } from 'react-dom/test-utils';
4
4
  import { RootListener } from './RootListener';
5
5
 
@@ -15,7 +15,8 @@ describe('RootListener Component', () => {
15
15
  const removed = jest.fn();
16
16
  document.body.appendChild(element);
17
17
  const container = document.body.appendChild(document.createElement('div'));
18
- render(<RootListener />, container);
18
+ const root = createRoot(container);
19
+ root.render(<RootListener />);
19
20
  document.body.removeEventListener = removed;
20
21
  await act(() => {
21
22
  const event = new CustomEvent('render-html', {
@@ -23,7 +24,7 @@ describe('RootListener Component', () => {
23
24
  detail: {
24
25
  target: element,
25
26
  props: {},
26
- }
27
+ },
27
28
  });
28
29
  element.dispatchEvent(event);
29
30
  return Promise.resolve();
@@ -35,10 +36,11 @@ describe('RootListener Component', () => {
35
36
  it('removes the RootListener successfully', async () => {
36
37
  const container = document.body.appendChild(document.createElement('div'));
37
38
  const removed = jest.fn();
38
- render(<RootListener />, container);
39
+ const root = createRoot(container);
40
+ root.render(<RootListener />);
39
41
  document.body.removeEventListener = removed;
40
42
  await act(() => Promise.resolve());
41
- unmountComponentAtNode(container);
43
+ root.unmount();
42
44
  await act(() => Promise.resolve());
43
45
  expect(removed).toHaveBeenCalled();
44
46
  });
@@ -1,8 +1,8 @@
1
+ import { mount } from 'enzyme';
1
2
  import { createElement } from 'react';
2
3
  import { Atom, deref } from '@dbeining/react-atom';
3
4
  import { createListener, Pilet } from 'piral-base';
4
5
  import {
5
- changeLayout,
6
6
  includeProvider,
7
7
  initialize,
8
8
  injectPilet,
@@ -12,7 +12,6 @@ import {
12
12
  setRoute,
13
13
  } from './app';
14
14
  import { createActions } from '../state';
15
- import { mount } from 'enzyme';
16
15
  import { RootListener } from '../RootListener';
17
16
 
18
17
  const pilet: Pilet = {
@@ -23,23 +22,6 @@ const pilet: Pilet = {
23
22
  };
24
23
 
25
24
  describe('App Actions Module', () => {
26
- it('changeLayout changes the current layout', () => {
27
- const state = Atom.of({
28
- foo: 5,
29
- app: {
30
- layout: 'tablet',
31
- },
32
- });
33
- const ctx = createActions(state, createListener({}));
34
- changeLayout(ctx, 'mobile');
35
- expect(deref(state)).toEqual({
36
- foo: 5,
37
- app: {
38
- layout: 'mobile',
39
- },
40
- });
41
- });
42
-
43
25
  it('initialize initializes state data', () => {
44
26
  const state = Atom.of({
45
27
  app: {},
@@ -3,7 +3,6 @@ import { RouteComponentProps } from 'react-router';
3
3
  import { runPilet } from 'piral-base';
4
4
  import { withKey, replaceOrAddItem, removeNested, withProvider, withRoute, noop } from '../utils';
5
5
  import {
6
- LayoutType,
7
6
  ComponentsState,
8
7
  ErrorComponentsState,
9
8
  BaseRegistration,
@@ -13,13 +12,6 @@ import {
13
12
  PiletEntry,
14
13
  } from '../types';
15
14
 
16
- export function changeLayout(ctx: GlobalStateContext, current: LayoutType) {
17
- ctx.dispatch((state) => ({
18
- ...state,
19
- app: withKey(state.app, 'layout', current),
20
- }));
21
- }
22
-
23
15
  export function initialize(ctx: GlobalStateContext, loading: boolean, error: Error | undefined, modules: Array<Pilet>) {
24
16
  ctx.dispatch((state) => ({
25
17
  ...state,
@@ -1,8 +1,13 @@
1
1
  import { swap, deref } from '@dbeining/react-atom';
2
+ import { isSame } from '../utils';
2
3
  import { GlobalState, GlobalStateContext } from '../types';
3
4
 
5
+ function onlyChangedState(oldState: GlobalState, newState: GlobalState) {
6
+ return isSame(oldState, newState) ? oldState : newState;
7
+ }
8
+
4
9
  export function dispatch(ctx: GlobalStateContext, update: (state: GlobalState) => GlobalState) {
5
- swap(ctx.state, update);
10
+ swap(ctx.state, oldState => onlyChangedState(oldState, update(oldState)));
6
11
  }
7
12
 
8
13
  export function readState<S>(ctx: GlobalStateContext, read: (state: GlobalState) => S) {
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { PiralError, PiralLoadingIndicator } from './components';
2
+ import { RegisteredErrorInfo, RegisteredLoadingIndicator } from './components';
3
3
  import { Errors, PiletApi } from '../types';
4
4
 
5
5
  export interface ErrorBoundaryProps {
@@ -11,6 +11,10 @@ export interface ErrorBoundaryProps {
11
11
  * The associated pilet api for the metadata.
12
12
  */
13
13
  piral: PiletApi;
14
+ /**
15
+ * The content to render (i.e., where to apply the boundary to).
16
+ */
17
+ children: React.ReactNode;
14
18
  }
15
19
 
16
20
  export interface ErrorBoundaryState {
@@ -45,9 +49,9 @@ export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoun
45
49
 
46
50
  if (error) {
47
51
  const pilet = piral.meta.name;
48
- return <PiralError type={errorType} error={error} pilet={pilet} {...rest} />;
52
+ return <RegisteredErrorInfo type={errorType} error={error} pilet={pilet} {...rest} />;
49
53
  }
50
54
 
51
- return <React.Suspense fallback={<PiralLoadingIndicator />}>{children}</React.Suspense>;
55
+ return <React.Suspense fallback={<RegisteredLoadingIndicator />}>{children}</React.Suspense>;
52
56
  }
53
57
  }
@@ -1,13 +1,21 @@
1
1
  import * as React from 'react';
2
- import { render, unmountComponentAtNode } from 'react-dom';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { act } from 'react-dom/test-utils';
3
4
  import { ForeignComponentContainer } from './ForeignComponentContainer';
4
5
 
6
+ async function render(element: any, container: Element) {
7
+ const root = createRoot(container);
8
+ root.render(element);
9
+ await act(() => Promise.resolve());
10
+ return root;
11
+ }
12
+
5
13
  describe('ForeignComponentContainer component', () => {
6
- it('mounts an HTML component', () => {
14
+ it('mounts an HTML component', async () => {
7
15
  const container = document.body.appendChild(document.createElement('div'));
8
16
  const mount = jest.fn();
9
17
  const component = { mount };
10
- render(
18
+ await render(
11
19
  <ForeignComponentContainer $component={component} $context={undefined} $portalId="foo" innerProps={{}} />,
12
20
  container,
13
21
  );
@@ -15,28 +23,28 @@ describe('ForeignComponentContainer component', () => {
15
23
  container.remove();
16
24
  });
17
25
 
18
- it('unmounts an HTML component', () => {
26
+ it('unmounts an HTML component', async () => {
19
27
  const container = document.body.appendChild(document.createElement('div'));
20
28
  const mount = jest.fn();
21
29
  const unmount = jest.fn();
22
30
  const component = { mount, unmount };
23
- render(
31
+ const root = await render(
24
32
  <ForeignComponentContainer $component={component} $context={undefined} $portalId="foo" innerProps={{}} />,
25
33
  container,
26
34
  );
27
35
  expect(mount).toHaveBeenCalled();
28
36
  expect(unmount).not.toHaveBeenCalled();
29
- unmountComponentAtNode(container);
37
+ root.unmount();
30
38
  expect(unmount).toHaveBeenCalled();
31
39
  container.remove();
32
40
  });
33
41
 
34
- it('updates an HTML component', () => {
42
+ it('updates an HTML component', async () => {
35
43
  const container = document.body.appendChild(document.createElement('div'));
36
44
  const mount = jest.fn();
37
45
  const update = jest.fn();
38
46
  const component = { mount, update };
39
- render(
47
+ const root = await render(
40
48
  <ForeignComponentContainer
41
49
  $component={component}
42
50
  $context={undefined}
@@ -47,20 +55,20 @@ describe('ForeignComponentContainer component', () => {
47
55
  );
48
56
  expect(mount).toHaveBeenCalled();
49
57
  expect(update).not.toHaveBeenCalled();
50
- render(
58
+ root.render(
51
59
  <ForeignComponentContainer
52
60
  $component={component}
53
61
  $context={undefined}
54
62
  $portalId="foo"
55
63
  innerProps={{ a: 'foo' }}
56
64
  />,
57
- container,
58
65
  );
66
+ await act(() => Promise.resolve());
59
67
  expect(update).toHaveBeenCalled();
60
68
  container.remove();
61
69
  });
62
70
 
63
- it('forces re-rendering of an HTML component', () => {
71
+ it('forces re-rendering of an HTML component', async () => {
64
72
  const container = document.body.appendChild(document.createElement('div'));
65
73
  const componentDidMount = ForeignComponentContainer.prototype.componentDidMount;
66
74
  ForeignComponentContainer.prototype.componentDidMount = function () {
@@ -73,7 +81,7 @@ describe('ForeignComponentContainer component', () => {
73
81
  const update = jest.fn();
74
82
  const unmount = jest.fn();
75
83
  const component = { mount, update, unmount };
76
- render(
84
+ const root = await render(
77
85
  <ForeignComponentContainer
78
86
  $component={component}
79
87
  $context={undefined}
@@ -85,27 +93,27 @@ describe('ForeignComponentContainer component', () => {
85
93
  expect(mount).toHaveBeenCalled();
86
94
  expect(unmount).not.toHaveBeenCalled();
87
95
  expect(update).not.toHaveBeenCalled();
88
- render(
96
+ root.render(
89
97
  <ForeignComponentContainer
90
98
  $component={component}
91
99
  $context={undefined}
92
100
  $portalId="foo"
93
101
  innerProps={{ a: 'foo' }}
94
102
  />,
95
- container,
96
103
  );
104
+ await act(() => Promise.resolve());
97
105
  expect(update).not.toHaveBeenCalled();
98
106
  expect(unmount).toHaveBeenCalled();
99
107
  container.remove();
100
108
  });
101
109
 
102
- it('listens to render-html', () => {
110
+ it('listens to render-html', async () => {
103
111
  const container = document.body.appendChild(document.createElement('div'));
104
112
  const mount = jest.fn();
105
113
  const renderHtmlExtension = jest.fn();
106
114
  const component = { mount };
107
115
  const props = { piral: { renderHtmlExtension }, meta: {} };
108
- render(
116
+ await render(
109
117
  <ForeignComponentContainer $component={component} $context={undefined} $portalId="foo" innerProps={props} />,
110
118
  container,
111
119
  );
@@ -113,6 +121,7 @@ describe('ForeignComponentContainer component', () => {
113
121
  const node = document.querySelector('[data-portal-id=foo]');
114
122
  expect(renderHtmlExtension).not.toHaveBeenCalled();
115
123
  node.dispatchEvent(new CustomEvent('render-html', { detail: {} }));
124
+ await act(() => Promise.resolve());
116
125
  expect(renderHtmlExtension).toHaveBeenCalled();
117
126
  container.remove();
118
127
  });
@@ -1,7 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { isfunc } from 'piral-base';
3
- import { __RouterContext } from 'react-router';
4
- import { ForeignComponent, BaseComponentProps, ComponentContext } from '../types';
3
+ import type { ForeignComponent, BaseComponentProps, ComponentContext } from '../types';
5
4
 
6
5
  interface ForeignComponentContainerProps<T> {
7
6
  $portalId: string;
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ import { PortalRenderer } from './PortalRenderer';
3
+ import { RegisteredDebug } from './components';
4
+
5
+ /**
6
+ * Integrates the global portal renderer and the debug utilities
7
+ * (if registered).
8
+ */
9
+ export const PiralGlobals: React.FC = () => {
10
+ return (
11
+ <>
12
+ <PortalRenderer id="root" />
13
+ <RegisteredDebug />
14
+ </>
15
+ );
16
+ };
@@ -2,8 +2,8 @@ import * as React from 'react';
2
2
  import * as hooks from '../hooks';
3
3
  import { MemoryRouter } from 'react-router';
4
4
  import { mount } from 'enzyme';
5
- import { DefaultRouteSwitch } from './DefaultRouteSwitch';
6
5
  import { PiralRoutes } from './PiralRoutes';
6
+ import { DefaultRouteSwitch } from '../defaults';
7
7
 
8
8
  const mountWithRouter = (node, url = '/') =>
9
9
  mount(
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import { RegisteredErrorInfo, RegisteredLoadingIndicator } from './components';
3
+ import { useGlobalState } from '../hooks';
4
+
5
+ export interface PiralSuspenseProps {
6
+ children?: React.ReactNode;
7
+ }
8
+
9
+ export const PiralSuspense: React.FC<PiralSuspenseProps> = ({ children }) => {
10
+ const { error, loading } = useGlobalState((m) => m.app);
11
+
12
+ return error ? (
13
+ <RegisteredErrorInfo type="loading" error={error} />
14
+ ) : loading ? (
15
+ <RegisteredLoadingIndicator />
16
+ ) : (
17
+ <>{children}</>
18
+ );
19
+ };
@@ -17,44 +17,45 @@ StubErrorInfo.displayName = 'StubErrorInfo';
17
17
  const StubLoader: React.FC = () => <div />;
18
18
  StubLoader.displayName = 'StubLoader';
19
19
 
20
- const StubRouter: React.FC = ({ children }) => <div>{children}</div>;
20
+ const StubRouter: React.FC<React.PropsWithChildren<{}>> = ({ children }) => <div>{children}</div>;
21
21
  StubRouter.displayName = 'StubRouter';
22
22
 
23
- const StubLayout: React.FC = ({ children }) => <div>{children}</div>;
23
+ const StubLayout: React.FC<React.PropsWithChildren<{}>> = ({ children }) => <div>{children}</div>;
24
24
  StubLayout.displayName = 'StubLayout';
25
25
 
26
26
  jest.mock('../hooks');
27
27
  jest.mock('./PiralRoutes');
28
28
 
29
29
  const state = {
30
- app: {
31
- error: undefined,
32
- loading: true,
33
- },
34
- components: {
35
- ErrorInfo: StubErrorInfo,
36
- LoadingIndicator: StubLoader,
37
- Router: StubRouter,
38
- Layout: StubLayout,
39
- },
40
- registry: {
41
- pages: {},
42
- extensions: {},
43
- },
44
- routes: {},
45
- provider: undefined,
30
+ app: {
31
+ error: undefined,
32
+ loading: true,
33
+ },
34
+ components: {
35
+ ErrorInfo: StubErrorInfo,
36
+ LoadingIndicator: StubLoader,
37
+ Router: StubRouter,
38
+ Layout: StubLayout,
39
+ },
40
+ registry: {
41
+ pages: {},
42
+ extensions: {},
43
+ },
44
+ portals: {},
45
+ routes: {},
46
+ provider: undefined,
46
47
  };
47
48
 
48
49
  (hooks as any).useGlobalState = (select: any) => select(state);
49
50
 
50
- (routes as any).PiralRoutes = ({ }) => <StubDashboard />;
51
+ (routes as any).PiralRoutes = ({}) => <StubDashboard />;
51
52
 
52
53
  describe('Portal Module', () => {
53
- it('In this test window should be undefined', () => {
54
- state.app.loading = false;
55
- state.app.error = undefined;
56
- const node = render(<PiralView />);
57
- expect(typeof window).toBe("undefined")
58
- expect(node.length).toBe(1);
59
- });
54
+ it('In this test window should be undefined', () => {
55
+ state.app.loading = false;
56
+ state.app.error = undefined;
57
+ const node = render(<PiralView children={undefined} />);
58
+ expect(typeof window).toBe('undefined');
59
+ expect(node.length).toBe(1);
60
+ });
60
61
  });
@@ -33,6 +33,7 @@ const state = {
33
33
  Router: StubRouter,
34
34
  Layout: StubLayout,
35
35
  },
36
+ portals: {},
36
37
  registry: {
37
38
  pages: {},
38
39
  extensions: {},
@@ -1,60 +1,41 @@
1
1
  import * as React from 'react';
2
- import { RouteComponentProps, StaticRouter } from 'react-router';
2
+ import { RouteComponentProps } from 'react-router';
3
+ import { PiralGlobals } from './PiralGlobals';
3
4
  import { PiralRoutes } from './PiralRoutes';
4
- import {
5
- PiralError,
6
- PiralRouter,
7
- PiralLoadingIndicator,
8
- PiralRouteSwitch,
9
- PiralLayout,
10
- PiralDebug,
11
- } from './components';
12
- import { useGlobalState } from '../hooks';
5
+ import { PiralSuspense } from './PiralSuspense';
6
+ import { ResponsiveLayout } from './ResponsiveLayout';
7
+ import { RegisteredErrorInfo, RegisteredRouteSwitch, RegisteredLayout } from './components';
8
+ import { LayoutBreakpoints } from '../types';
13
9
 
14
- const NotFound: React.FC<RouteComponentProps> = (props) => <PiralError type="not_found" {...props} />;
15
-
16
- const PiralContent: React.FC = () => {
17
- const { error, loading, layout } = useGlobalState((m) => m.app);
18
-
19
- return error ? (
20
- <PiralError type="loading" error={error} />
21
- ) : loading ? (
22
- <PiralLoadingIndicator />
23
- ) : (
24
- <PiralLayout currentLayout={layout}>
25
- <PiralRoutes NotFound={NotFound} RouteSwitch={PiralRouteSwitch} />
26
- </PiralLayout>
27
- );
28
- };
29
-
30
- const FallbackRouter: React.FC = (props) => {
31
- const publicPath = useGlobalState((s) => s.app.publicPath);
32
- return <StaticRouter location="/" {...props} basename={publicPath} />;
33
- };
34
-
35
- const Router = typeof window === 'undefined' ? FallbackRouter : PiralRouter;
36
-
37
- const PiralProvider: React.FC = ({ children }) => {
38
- const provider = useGlobalState((m) => m.provider) || React.Fragment;
39
- return React.createElement(provider, undefined, children);
40
- };
10
+ const NotFound: React.FC<RouteComponentProps> = (props) => <RegisteredErrorInfo type="not_found" {...props} />;
41
11
 
42
12
  /**
43
13
  * The props for the PiralView component.
44
14
  */
45
- export interface PiralViewProps { }
15
+ export interface PiralViewProps {
16
+ /**
17
+ * The custom breakpoints for the different layout modi.
18
+ */
19
+ breakpoints?: LayoutBreakpoints;
20
+ /**
21
+ * The extra content.
22
+ */
23
+ children: React.ReactNode;
24
+ }
46
25
 
47
26
  /**
48
27
  * The component responsible for the generic view of the application.
49
- * This includes the global providers, the used Router, the current content and some convenience.
28
+ * This includes the used the current content and some convenience.
50
29
  */
51
- export const PiralView: React.FC<PiralViewProps> = ({ children }) => (
52
- <PiralProvider>
53
- <Router>
54
- <PiralContent />
55
- {children}
56
- <PiralDebug />
57
- </Router>
58
- </PiralProvider>
30
+ export const PiralView: React.FC<PiralViewProps> = ({ breakpoints, children }) => (
31
+ <>
32
+ <PiralGlobals />
33
+ <PiralSuspense>
34
+ <ResponsiveLayout breakpoints={breakpoints} Layout={RegisteredLayout}>
35
+ <PiralRoutes NotFound={NotFound} RouteSwitch={RegisteredRouteSwitch} />
36
+ </ResponsiveLayout>
37
+ </PiralSuspense>
38
+ {children}
39
+ </>
59
40
  );
60
41
  PiralView.displayName = 'PiralView';