@genesislcap/blank-app-seed 5.22.1 → 5.23.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 (191) hide show
  1. package/.genx/configure.js +11 -1
  2. package/.genx/package.json +1 -1
  3. package/.genx/scripts/generate-test-apps.sh +124 -0
  4. package/.genx/templates/angular/chart.hbs +19 -7
  5. package/.genx/templates/angular/component/component.column.defs.hbs +16 -4
  6. package/.genx/templates/angular/component/component.create.form.hbs +10 -2
  7. package/.genx/templates/angular/component/component.events.config.hbs +31 -6
  8. package/.genx/templates/angular/component/component.filter.form.hbs +1 -2
  9. package/.genx/templates/angular/component/component.gridOptions.hbs +15 -3
  10. package/.genx/templates/angular/component/component.hbs +77 -46
  11. package/.genx/templates/angular/component/component.index.hbs +1 -1
  12. package/.genx/templates/angular/component/component.update.form.hbs +1 -1
  13. package/.genx/templates/angular/entityManager.hbs +6 -6
  14. package/{client-tmp/angular/src/app/utils/fdc3.ts → .genx/templates/angular/fdc3.ts.hbs} +6 -8
  15. package/.genx/templates/angular/form.hbs +9 -12
  16. package/.genx/templates/angular/grid.hbs +43 -40
  17. package/.genx/templates/angular/route.hbs +8 -2
  18. package/.genx/templates/angular/route.template.hbs +7 -7
  19. package/.genx/templates/angular/slices/eventing.slice.hbs +21 -10
  20. package/.genx/templates/angular/store.hbs +1 -5
  21. package/.genx/templates/angular/tabsPanel.hbs +2 -2
  22. package/.genx/templates/react/chart.hbs +6 -15
  23. package/.genx/templates/react/component/component.column.defs.hbs +16 -10
  24. package/.genx/templates/react/component/component.create.form.hbs +2 -2
  25. package/.genx/templates/react/component/component.events.config.hbs +32 -5
  26. package/.genx/templates/react/component/component.filter.form.hbs +1 -2
  27. package/.genx/templates/react/component/component.gridOptions.hbs +17 -11
  28. package/.genx/templates/react/component/component.hbs +84 -42
  29. package/.genx/templates/react/component/component.update.form.hbs +1 -1
  30. package/.genx/templates/react/entityManager.hbs +8 -16
  31. package/{client-tmp/react/src/utils/fdc3.ts → .genx/templates/react/fdc3.ts.hbs} +7 -8
  32. package/.genx/templates/react/form.hbs +1 -15
  33. package/.genx/templates/react/grid.hbs +11 -11
  34. package/.genx/templates/react/gridLayout.hbs +44 -4
  35. package/.genx/templates/react/horizontalLayout.hbs +10 -1
  36. package/.genx/templates/react/route.hbs +21 -14
  37. package/.genx/templates/react/slices/eventing.slice.hbs +19 -8
  38. package/.genx/templates/react/store.hbs +1 -5
  39. package/.genx/templates/react/tabsLayout.hbs +5 -1
  40. package/.genx/templates/web-components/chart.hbs +25 -25
  41. package/.genx/templates/web-components/component/component.column.defs.hbs +11 -3
  42. package/.genx/templates/web-components/component/component.create.form.hbs +11 -4
  43. package/.genx/templates/web-components/component/component.events.config.hbs +33 -6
  44. package/.genx/templates/web-components/component/component.filter.form.hbs +1 -2
  45. package/.genx/templates/web-components/component/component.gridOptions.hbs +12 -3
  46. package/.genx/templates/web-components/component/component.hbs +19 -20
  47. package/.genx/templates/web-components/component/component.index.hbs +1 -1
  48. package/.genx/templates/web-components/component/component.template.hbs +9 -13
  49. package/.genx/templates/web-components/component/component.update.form.hbs +1 -1
  50. package/.genx/templates/web-components/entityManager.hbs +92 -73
  51. package/{client-tmp/web-components/src/utils/fdc3.ts → .genx/templates/web-components/fdc3.ts.hbs} +6 -7
  52. package/.genx/templates/web-components/form.hbs +16 -15
  53. package/.genx/templates/web-components/grid.hbs +49 -41
  54. package/.genx/templates/web-components/gridLayout.hbs +32 -28
  55. package/.genx/templates/web-components/horizontalLayout.hbs +5 -5
  56. package/.genx/templates/web-components/route.template.hbs +4 -6
  57. package/.genx/templates/web-components/slices/eventing.slice.hbs +23 -15
  58. package/.genx/templates/web-components/store.hbs +1 -9
  59. package/.genx/templates/web-components/tabsLayout.hbs +5 -5
  60. package/.genx/tests/fixtures/routes-full.json +299 -0
  61. package/.genx/tests/fixtures/testapp/client/src/pages/Home/Home.tsx +12 -0
  62. package/.genx/utils/formatJSONValue.js +38 -2
  63. package/.genx/utils/formatRouteData.js +67 -18
  64. package/.genx/utils/generateTile.js +10 -1
  65. package/.genx/utils/gridSerializers.js +63 -23
  66. package/.genx/utils/registerPartials.js +15 -12
  67. package/.genx/utils/toJsStringLiteral.js +37 -0
  68. package/.github/workflows/build.yml +22 -1
  69. package/.github/workflows/release.yaml +96 -0
  70. package/.husky/pre-commit +8 -0
  71. package/.releaserc +1 -1
  72. package/CHANGELOG.md +23 -0
  73. package/client-tmp/angular/.oxfmtrc.json +32 -0
  74. package/client-tmp/angular/.oxlintrc.json +4 -0
  75. package/client-tmp/angular/README.md +4 -4
  76. package/client-tmp/angular/jest.config.js +1 -1
  77. package/client-tmp/angular/lint-css.js +24 -3
  78. package/client-tmp/angular/oxlint-staged.js +45 -0
  79. package/client-tmp/angular/package.json +19 -9
  80. package/client-tmp/angular/playwright.config.ts +12 -11
  81. package/client-tmp/angular/src/app/app.component.html +0 -1
  82. package/client-tmp/angular/src/app/app.component.ts +8 -9
  83. package/client-tmp/angular/src/app/app.config.ts +2 -2
  84. package/client-tmp/angular/src/app/app.module.ts +6 -5
  85. package/client-tmp/angular/src/app/components/error-message/error-message.component.html +29 -15
  86. package/client-tmp/angular/src/app/components/error-message/error-message.component.spec.ts +2 -2
  87. package/client-tmp/angular/src/app/components/error-message/error-message.component.ts +3 -5
  88. package/client-tmp/angular/src/app/directive/app-lazy-load.directive.ts +1 -0
  89. package/client-tmp/angular/src/app/guards/chained.guard.ts +19 -19
  90. package/client-tmp/angular/src/app/guards/connection.guard.ts +12 -12
  91. package/client-tmp/angular/src/app/layouts/blank/blank.layout.spec.ts +1 -1
  92. package/client-tmp/angular/src/app/layouts/blank/blank.layout.ts +1 -5
  93. package/client-tmp/angular/src/app/layouts/default/default.layout.html +3 -4
  94. package/client-tmp/angular/src/app/layouts/default/default.layout.spec.ts +4 -4
  95. package/client-tmp/angular/src/app/layouts/default/default.layout.ts +1 -1
  96. package/client-tmp/angular/src/app/pages/auth-login/auth-login.component.ts +1 -2
  97. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.html +3 -3
  98. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.scss +1 -1
  99. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.spec.ts +5 -5
  100. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.ts +3 -4
  101. package/client-tmp/angular/src/app/services/route.service.ts +79 -74
  102. package/client-tmp/angular/src/app/share/foundation-auth.ts +7 -1
  103. package/client-tmp/angular/src/app/share/genesis-components.ts +66 -68
  104. package/client-tmp/angular/src/app/utils/index.ts +3 -1
  105. package/client-tmp/angular/src/app/utils/permissions.ts +7 -7
  106. package/client-tmp/angular/src/declaration.d.ts +1 -1
  107. package/client-tmp/angular/src/environments/environment.ts +1 -1
  108. package/client-tmp/angular/src/jest.setup.ts +8 -0
  109. package/client-tmp/angular/src/main.ts +2 -2
  110. package/client-tmp/angular/src/pbc/container.ts +28 -26
  111. package/client-tmp/angular/src/pbc/elementsRenderer.ts +49 -39
  112. package/client-tmp/angular/src/pbc/utils.ts +61 -60
  113. package/client-tmp/angular/src/styles/_mixins.scss +7 -8
  114. package/client-tmp/angular/src/styles/styles.css +6 -6
  115. package/client-tmp/angular/test/e2e/fixture.ts +2 -2
  116. package/client-tmp/angular/test/e2e/flows/001-protected.e2e.ts +1 -1
  117. package/client-tmp/angular/tsconfig.app.json +2 -8
  118. package/client-tmp/angular/tsconfig.json +1 -4
  119. package/client-tmp/angular/tsconfig.spec.json +4 -11
  120. package/client-tmp/angular/webpack.dev.config.js +3 -4
  121. package/client-tmp/angular/webpack.prod.config.js +3 -4
  122. package/client-tmp/angular/webpack.shared.config.js +20 -24
  123. package/client-tmp/react/.oxfmtrc.json +30 -0
  124. package/client-tmp/react/.oxlintrc.json +4 -0
  125. package/client-tmp/react/README.md +4 -4
  126. package/client-tmp/react/jest.config.ts +8 -4
  127. package/client-tmp/react/jest.setup.ts +85 -1
  128. package/client-tmp/react/oxlint-staged.js +45 -0
  129. package/client-tmp/react/package.json +24 -9
  130. package/client-tmp/react/playwright.config.ts +11 -10
  131. package/client-tmp/react/src/App.tsx +27 -29
  132. package/client-tmp/react/src/__mocks__/fileMock.js +1 -0
  133. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.test.tsx +16 -4
  134. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.tsx +32 -29
  135. package/client-tmp/react/src/components/error-boundary/ErrorBoundary.tsx +15 -6
  136. package/client-tmp/react/src/components/routes/AppRoutes.tsx +1 -1
  137. package/client-tmp/react/src/components/single-component/registry.ts +4 -0
  138. package/client-tmp/react/src/custom-elements.d.ts +4 -2
  139. package/client-tmp/react/src/helpers/hasPermissionHelper.tsx +1 -1
  140. package/client-tmp/react/src/helpers/isAuthenticatedHelper.tsx +1 -1
  141. package/client-tmp/react/src/helpers/isConnectedHelper.tsx +3 -3
  142. package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +7 -11
  143. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +8 -8
  144. package/client-tmp/react/src/main.tsx +11 -11
  145. package/client-tmp/react/src/pages/AuthPage/AuthPage.test.tsx +13 -2
  146. package/client-tmp/react/src/pages/AuthPage/AuthPage.tsx +14 -13
  147. package/client-tmp/react/src/pages/NotFoundPage/NotFoundPage.tsx +2 -7
  148. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +8 -4
  149. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +1 -1
  150. package/client-tmp/react/src/pbc/container.tsx +9 -4
  151. package/client-tmp/react/src/pbc/elementsRenderer.tsx +13 -7
  152. package/client-tmp/react/src/pbc/utils.ts +60 -59
  153. package/client-tmp/react/src/services/auth.service.ts +1 -1
  154. package/client-tmp/react/src/services/store.service.ts +5 -3
  155. package/client-tmp/react/src/share/foundation-login.ts +2 -2
  156. package/client-tmp/react/src/share/genesis-components.ts +46 -47
  157. package/client-tmp/react/src/store/AuthContext.tsx +2 -10
  158. package/client-tmp/react/src/store/RoutesContext.test.tsx +99 -0
  159. package/client-tmp/react/src/store/RoutesContext.tsx +25 -5
  160. package/client-tmp/react/src/styles/styles.css +7 -7
  161. package/client-tmp/react/src/types/LayoutName.ts +1 -1
  162. package/client-tmp/react/src/utils/getLayoutNameByRoute.ts +1 -1
  163. package/client-tmp/react/src/utils/index.ts +2 -0
  164. package/client-tmp/react/src/utils/layout.ts +8 -1
  165. package/client-tmp/react/src/utils/permissions.ts +7 -7
  166. package/client-tmp/react/src/utils/useDocumentTitle.ts +5 -6
  167. package/client-tmp/react/test/e2e/fixture.ts +5 -2
  168. package/client-tmp/react/test/e2e/flows/001-protected.e2e.ts +3 -1
  169. package/client-tmp/react/tsconfig.app.json +1 -1
  170. package/client-tmp/react/tsconfig.json +1 -5
  171. package/client-tmp/web-components/.oxfmtrc.json +30 -0
  172. package/client-tmp/web-components/.oxlintrc.json +4 -0
  173. package/client-tmp/web-components/oxlint-staged.js +45 -0
  174. package/client-tmp/web-components/package.json +14 -22
  175. package/client-tmp/web-components/src/components/components.ts +9 -7
  176. package/client-tmp/web-components/src/main/main.css +1 -2
  177. package/client-tmp/web-components/src/main/main.ts +1 -1
  178. package/client-tmp/web-components/src/routes/config.ts +4 -0
  179. package/client-tmp/web-components/src/utils/index.ts +2 -0
  180. package/client-tmp/web-components/tsconfig.json +1 -4
  181. package/package.json +1 -1
  182. package/.genx/templates/angular/gridLayout.hbs +0 -30
  183. package/.genx/templates/angular/horizontalLayout.hbs +0 -7
  184. package/.genx/templates/angular/tabsLayout.hbs +0 -7
  185. package/.genx/tests/fixtures/testapp/client/src/routes/home/home.template.ts +0 -6
  186. package/.github/workflows/release.yml +0 -50
  187. package/client-tmp/react/.babelrc +0 -10
  188. package/client-tmp/react/.eslintrc.cjs +0 -21
  189. package/client-tmp/react/git +0 -0
  190. package/client-tmp/react/webpack.config.js +0 -121
  191. package/client-tmp/web-components/.prettierignore +0 -14
@@ -1,13 +1,13 @@
1
- import React from 'react'
2
- import ReactDOM from 'react-dom/client'
3
- import App from './App.tsx'
4
- import { AppErrorBoundary } from './components/error-boundary/ErrorBoundary'
5
-
6
- import { registerPBCs } from './pbc/utils';
7
1
  import { createLogger } from '@genesislcap/foundation-logger';
8
2
  import { installRapidFlexLayoutReactStyles } from '@genesislcap/rapid-design-system';
3
+ import React from 'react';
4
+ import ReactDOM from 'react-dom/client';
5
+ import App from './App.tsx';
6
+ import { AppErrorBoundary } from './components/error-boundary/ErrorBoundary';
7
+
8
+ import { registerPBCs } from './pbc/utils';
9
9
 
10
- import './styles/styles.css'
10
+ import './styles/styles.css';
11
11
 
12
12
  // Single source of truth for the flexlayout-react theme lives in the platform (@genesislcap/rapid-design-system).
13
13
  installRapidFlexLayoutReactStyles();
@@ -23,11 +23,11 @@ function bootstrapApp() {
23
23
  <App rootElement={rootEelement} />
24
24
  </AppErrorBoundary>
25
25
  </React.StrictMode>,
26
- )
26
+ );
27
27
  }
28
28
  }
29
29
 
30
30
  registerPBCs()
31
- .then(hasAssets => logger.debug(hasAssets ? 'PBCs registered' : 'No PBCs detected'))
32
- .catch((err) => logger.error(err))
33
- .finally(bootstrapApp)
31
+ .then((hasAssets) => logger.debug(hasAssets ? 'PBCs registered' : 'No PBCs detected'))
32
+ .catch((err) => logger.error(err))
33
+ .finally(bootstrapApp);
@@ -1,10 +1,21 @@
1
1
  import { render } from '@testing-library/react';
2
+ import { MemoryRouter } from 'react-router-dom';
2
3
  import AuthPage from './AuthPage';
3
4
 
5
+ // The login module pulls in the whole foundation-auth runtime (design-system registration,
6
+ // fast-router); this test only asserts the page shell renders.
7
+ jest.mock('../../share/foundation-login.ts', () => ({
8
+ configureFoundationLogin: jest.fn(),
9
+ }));
10
+
4
11
  test('renders AuthPage component', () => {
5
- const { container } = render(<AuthPage />);
12
+ const { container } = render(
13
+ <MemoryRouter>
14
+ <AuthPage />
15
+ </MemoryRouter>,
16
+ );
6
17
  const authPageElement: Element | null = container.querySelector('.auth-page');
7
18
  expect(authPageElement).toBeInTheDocument();
8
19
  const clientAppLoginElement: Element | null = container.querySelector('client-app-login');
9
20
  expect(clientAppLoginElement).toBeInTheDocument();
10
- });
21
+ });
@@ -1,21 +1,22 @@
1
- import React, {useEffect} from 'react';
2
- import './AuthPage.css';
3
- import { configureFoundationLogin } from '../../share/foundation-login.ts';
1
+ import React, { useEffect } from 'react';
4
2
  import { useNavigate, useLocation } from 'react-router-dom';
3
+ import { configureFoundationLogin } from '../../share/foundation-login.ts';
4
+ import './AuthPage.css';
5
5
 
6
6
  const AuthPage: React.FC = () => {
7
- const navigate = useNavigate();
8
- const location = useLocation();
7
+ const navigate = useNavigate();
8
+ const location = useLocation();
9
9
 
10
- useEffect(() => {
11
- configureFoundationLogin({ navigate, location });
12
- }, []);
10
+ useEffect(() => {
11
+ configureFoundationLogin({ navigate, location });
12
+ // oxlint-disable-next-line react-hooks/exhaustive-deps -- run once on mount; re-subscribing on every navigation would re-initialise login
13
+ }, []);
13
14
 
14
- return (
15
- <section className="auth-page">
16
- <client-app-login></client-app-login>
17
- </section>
18
- );
15
+ return (
16
+ <section className="auth-page">
17
+ <client-app-login></client-app-login>
18
+ </section>
19
+ );
19
20
  };
20
21
 
21
22
  export default AuthPage;
@@ -3,12 +3,7 @@ import ErrorMessage from '../../components/ErrorMessage/ErrorMessage';
3
3
  import './NotFoundPage.css';
4
4
 
5
5
  const NotFoundPage: React.FC = () => {
6
- return (
7
- <ErrorMessage
8
- elementType="h1"
9
- message="Page Not Found 404"
10
- />
11
- );
6
+ return <ErrorMessage elementType="h1" message="Page Not Found 404" />;
12
7
  };
13
8
 
14
- export default NotFoundPage;
9
+ export default NotFoundPage;
@@ -2,16 +2,20 @@ import { render, screen } from '@testing-library/react';
2
2
  import NotPermittedPage from './NotPermittedPage';
3
3
 
4
4
  jest.mock('../../components/ErrorMessage/ErrorMessage', () => {
5
- return jest.fn((props: { message: string }) => <h1 data-testid="error-message">{props.message}</h1>);
5
+ return jest.fn((props: { message: string }) => (
6
+ <h1 data-testid="error-message">{props.message}</h1>
7
+ ));
6
8
  });
7
9
 
8
10
  describe('NotPermittedPage Component', () => {
9
11
  test('renders the ErrorMessage component with correct props', () => {
10
12
  render(<NotPermittedPage />);
11
-
13
+
12
14
  const errorMessage: HTMLElement = screen.getByTestId('error-message');
13
15
  expect(errorMessage).toBeInTheDocument();
14
16
  expect(errorMessage.tagName).toBe('H1');
15
- expect(errorMessage).toHaveTextContent('You do not have permission to access this part of the application, please contact your administrator.');
17
+ expect(errorMessage).toHaveTextContent(
18
+ 'You do not have permission to access this part of the application, please contact your administrator.',
19
+ );
16
20
  });
17
- });
21
+ });
@@ -11,4 +11,4 @@ const NotPermittedPage: React.FC = () => {
11
11
  );
12
12
  };
13
13
 
14
- export default NotPermittedPage;
14
+ export default NotPermittedPage;
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
- import { deriveElementTag } from './utils';
3
- import { useRoutesContext } from '../store/RoutesContext';
4
2
  import { useLocation, RouteObject } from 'react-router-dom';
3
+ import { useRoutesContext } from '../store/RoutesContext';
4
+ import { deriveElementTag } from './utils';
5
5
 
6
6
  type ExtendedRouteObject = RouteObject & {
7
7
  data?: {
@@ -9,7 +9,7 @@ type ExtendedRouteObject = RouteObject & {
9
9
  pbcElement?: any;
10
10
  };
11
11
  path: string;
12
- }
12
+ };
13
13
 
14
14
  const PBCContainer: React.FC = () => {
15
15
  const containerRef = useRef<HTMLDivElement>(null);
@@ -36,9 +36,14 @@ const PBCContainer: React.FC = () => {
36
36
  };
37
37
 
38
38
  loadElement();
39
+ // `routes` comes from a memoized context value (see RoutesContext) so its identity is
40
+ // stable; an unstable one would re-run this effect and remount the PBC custom element
41
+ // on every re-render of the provider.
39
42
  }, [location.pathname, routes]);
40
43
 
41
- return <div ref={containerRef} className="container" style=\{{ width: '100%', height: '100%' }}></div>;
44
+ return (
45
+ <div ref={containerRef} className="container" style=\{{ width: '100%', height: '100%' }}></div>
46
+ );
42
47
  };
43
48
 
44
49
  export default PBCContainer;
@@ -1,8 +1,8 @@
1
- import { useEffect, useRef, useState } from 'react';
1
+ import { Connect } from '@genesislcap/foundation-comms';
2
2
  import { AppElementPredicate, AppTargetId } from '@genesislcap/foundation-shell/app';
3
- import { customEventFactory, getTargetElements } from './utils';
4
3
  import { DI } from '@genesislcap/web-core';
5
- import { Connect } from '@genesislcap/foundation-comms';
4
+ import { useEffect, useRef, useState } from 'react';
5
+ import { customEventFactory, getTargetElements } from './utils';
6
6
 
7
7
  const ALWAYS_TRUE_PREDICATE: AppElementPredicate = () => true;
8
8
 
@@ -15,15 +15,21 @@ interface PBCContainerElement extends HTMLDivElement {
15
15
  $emit: (type: string, detail: any) => void;
16
16
  }
17
17
 
18
- const PBCElementsRenderer = ({ target = [], predicate = ALWAYS_TRUE_PREDICATE }: PBCElementsRendererProps) => {
18
+ const PBCElementsRenderer = ({
19
+ target = [],
20
+ predicate = ALWAYS_TRUE_PREDICATE,
21
+ }: PBCElementsRendererProps) => {
19
22
  const containerRef = useRef<PBCContainerElement>(null);
20
23
  const [isConnected, setIsConnected] = useState(false);
21
24
 
22
25
  useEffect(() => {
23
26
  const connect = DI.getOrCreateDOMContainer().get(Connect);
24
27
  setIsConnected(connect.isConnected);
25
- const isConnected$ = (connect as { isConnected$?: { subscribe: (cb: (v: boolean) => void) => { unsubscribe: () => void } } })
26
- ?.isConnected$;
28
+ const isConnected$ = (
29
+ connect as {
30
+ isConnected$?: { subscribe: (cb: (v: boolean) => void) => { unsubscribe: () => void } };
31
+ }
32
+ )?.isConnected$;
27
33
  const sub = isConnected$?.subscribe((connected: boolean) => setIsConnected(connected));
28
34
  return () => sub?.unsubscribe?.();
29
35
  }, []);
@@ -49,7 +55,7 @@ const PBCElementsRenderer = ({ target = [], predicate = ALWAYS_TRUE_PREDICATE }:
49
55
  });
50
56
  }, [target, predicate, isConnected]);
51
57
 
52
- return (<div ref={containerRef} className="container"></div>);
58
+ return <div ref={containerRef} className="container"></div>;
53
59
  };
54
60
 
55
61
  export default PBCElementsRenderer;
@@ -1,15 +1,15 @@
1
1
  import { createLogger } from '@genesislcap/foundation-logger';
2
2
  import {
3
- AppContext,
4
- AppElement,
5
- AppElementPredicate,
6
- AppStyle,
7
- AppStylePredicate,
8
- AppTargetId,
9
- assetPredicate,
10
- getApp,
11
- registrationPredicate,
12
- targetIdPredicate
3
+ AppContext,
4
+ AppElement,
5
+ AppElementPredicate,
6
+ AppStyle,
7
+ AppStylePredicate,
8
+ AppTargetId,
9
+ assetPredicate,
10
+ getApp,
11
+ registrationPredicate,
12
+ targetIdPredicate,
13
13
  } from '@genesislcap/foundation-shell/app';
14
14
  import { importPBCAssets } from '@genesislcap/foundation-shell/pbc';
15
15
  import { toElementStyles } from '@genesislcap/foundation-utils';
@@ -26,10 +26,10 @@ const logger = createLogger('pbc-utils');
26
26
  * @public
27
27
  */
28
28
  export async function registerPBCs(): Promise<boolean> {
29
- const app = getApp();
30
- const pbcAssets = await importPBCAssets();
31
- app.registerAssets(pbcAssets);
32
- return app.hasAssets();
29
+ const app = getApp();
30
+ const pbcAssets = await importPBCAssets();
31
+ app.registerAssets(pbcAssets);
32
+ return app.hasAssets();
33
33
  }
34
34
 
35
35
  /**
@@ -37,76 +37,72 @@ export async function registerPBCs(): Promise<boolean> {
37
37
  * Shared across elements and styles.
38
38
  */
39
39
  const assetFilter = (
40
- asset: AppElement | AppStyle,
41
- targetId: AppTargetId,
42
- predicate: AppElementPredicate | AppStylePredicate = () => true,
43
- context: AppContext,
44
- ) => targetIdPredicate(asset, targetId) &&
45
- assetPredicate(asset, context) &&
46
- registrationPredicate(
47
- asset,
48
- predicate,
49
- context,
50
- );
40
+ asset: AppElement | AppStyle,
41
+ targetId: AppTargetId,
42
+ predicate: AppElementPredicate | AppStylePredicate = () => true,
43
+ context: AppContext,
44
+ ) =>
45
+ targetIdPredicate(asset, targetId) &&
46
+ assetPredicate(asset, context) &&
47
+ registrationPredicate(asset, predicate, context);
51
48
 
52
49
  /**
53
50
  * @public
54
51
  */
55
- export function getTargetStyles(
56
- targetId: AppTargetId,
57
- predicate: AppStylePredicate = () => true
58
- ) {
59
- const app = getApp();
60
- return app.styles
61
- .filter(asset => assetFilter(asset, targetId, predicate, app.config.context!))
62
- .map((token) => token.styles)
63
- .flat();
52
+ export function getTargetStyles(targetId: AppTargetId, predicate: AppStylePredicate = () => true) {
53
+ const app = getApp();
54
+ return app.styles
55
+ .filter((asset) => assetFilter(asset, targetId, predicate, app.config.context!))
56
+ .map((token) => token.styles)
57
+ .flat();
64
58
  }
65
59
 
66
-
67
60
  /**
68
61
  * @public
69
62
  */
70
63
  export function getTargetElements(
71
- targetId: AppTargetId,
72
- predicate: AppElementPredicate = () => true
64
+ targetId: AppTargetId,
65
+ predicate: AppElementPredicate = () => true,
73
66
  ) {
74
- const app = getApp();
75
- return app.elements
76
- .filter(asset => assetFilter(asset, targetId, predicate, app.config.context!))
77
- .map((token) => token.elements);
67
+ const app = getApp();
68
+ return app.elements
69
+ .filter((asset) => assetFilter(asset, targetId, predicate, app.config.context!))
70
+ .map((token) => token.elements);
78
71
  }
79
72
 
80
73
  /**
81
74
  * @public
82
75
  */
83
76
  export function registerStylesTarget(nativeElement: HTMLElement, targetId: AppTargetId) {
84
- const styles = getTargetStyles(targetId);
85
- if (!styles || styles.length === 0) {
86
- return;
87
- }
88
- applyDynamicStyles(nativeElement, styles);
77
+ const styles = getTargetStyles(targetId);
78
+ if (!styles || styles.length === 0) {
79
+ return;
80
+ }
81
+ applyDynamicStyles(nativeElement, styles);
89
82
  }
90
83
 
91
84
  /**
92
85
  * @public
93
86
  */
94
- export function applyDynamicStyles(nativeElement: HTMLElement, style: ComposableStyles | ComposableStyles[]) {
95
- const elementStyles = toElementStyles(style);
96
- const styleTarget = nativeElement.shadowRoot ? nativeElement.shadowRoot : document;
97
- elementStyles.addStylesTo(styleTarget);
87
+ export function applyDynamicStyles(
88
+ nativeElement: HTMLElement,
89
+ style: ComposableStyles | ComposableStyles[],
90
+ ) {
91
+ const elementStyles = toElementStyles(style);
92
+ const styleTarget = nativeElement.shadowRoot ? nativeElement.shadowRoot : document;
93
+ elementStyles.addStylesTo(styleTarget);
98
94
  }
99
95
 
100
96
  /**
101
97
  * @public
102
98
  */
103
99
  export function customEventFactory(type: string, detail?: any) {
104
- return new CustomEvent(type, {
105
- bubbles: true,
106
- cancelable: true,
107
- composed: true,
108
- detail,
109
- });
100
+ return new CustomEvent(type, {
101
+ bubbles: true,
102
+ cancelable: true,
103
+ composed: true,
104
+ detail,
105
+ });
110
106
  }
111
107
 
112
108
  /**
@@ -115,7 +111,12 @@ export function customEventFactory(type: string, detail?: any) {
115
111
  * @public
116
112
  */
117
113
  export function deriveElementTag(name: string): string {
118
- const tagName = name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
119
- logger.debug(`Guessing pbc element tag is '${tagName}' based on '${name}'. This may be incorrect, please set pbcElementTag in route data.`);
120
- return tagName;
114
+ const tagName = name
115
+ .replace(/([A-Z])/g, '-$1')
116
+ .toLowerCase()
117
+ .replace(/^-/, '');
118
+ logger.debug(
119
+ `Guessing pbc element tag is '${tagName}' based on '${name}'. This may be incorrect, please set pbcElementTag in route data.`,
120
+ );
121
+ return tagName;
121
122
  }
@@ -1,5 +1,5 @@
1
1
  import { Auth } from '@genesislcap/foundation-comms';
2
- import { DI } from '@microsoft/fast-foundation';
2
+ import { DI } from '@genesislcap/web-core';
3
3
 
4
4
  class AuthService {
5
5
  isAuthenticated = false;
@@ -30,9 +30,11 @@ class DefaultStore extends AbstractStoreRoot<Store, StoreEventDetailMap> impleme
30
30
 
31
31
  setConnected(connected: boolean) {
32
32
  this.connected = connected;
33
- document.dispatchEvent(new CustomEvent('connected-changed', {
34
- detail: { connected }
35
- }));
33
+ document.dispatchEvent(
34
+ new CustomEvent('connected-changed', {
35
+ detail: { connected },
36
+ }),
37
+ );
36
38
  }
37
39
  }
38
40
 
@@ -1,12 +1,12 @@
1
1
  import { configure, defaultAuthConfig } from '@genesislcap/foundation-auth/config';
2
+ import { Connect } from '@genesislcap/foundation-comms';
2
3
  import {
3
4
  buildPostLoginRedirect,
4
5
  type RedirectableLocationState,
5
6
  } from '@genesislcap/foundation-react-utils/router';
6
- import { AUTH_PATH } from '../config';
7
- import { Connect } from '@genesislcap/foundation-comms';
8
7
  import { DI } from '@genesislcap/web-core';
9
8
  import type { NavigateFunction, Location as RouterLocation } from 'react-router-dom';
9
+ import { AUTH_PATH } from '../config';
10
10
 
11
11
  /**
12
12
  * Configure the micro frontend
@@ -1,47 +1,46 @@
1
- import { getApp } from '@genesislcap/foundation-shell/app';
2
- import { g2plotChartsComponents } from '@genesislcap/g2plot-chart';
3
- import * as rapidDesignSystem from '@genesislcap/rapid-design-system';
4
- import { rapidGridComponents, rapidGridProBeta } from '@genesislcap/rapid-grid-pro';
5
- import { FoundationRouter } from '@genesislcap/foundation-ui';
6
- import { avoidTreeShaking } from '@genesislcap/foundation-utils';
7
-
8
- /**
9
- * Ensure tree shaking doesn't remove these.
10
- */
11
- avoidTreeShaking(FoundationRouter);
12
-
13
- /**
14
- * registerComponents.
15
- * @public
16
- */
17
- export async function registerComponents() {
18
- const { configure: configureHeader } = await import('@genesislcap/foundation-header/config');
19
- /**
20
- * Register any PBC components with the design system
21
- */
22
- getApp().registerComponents({
23
- designSystem: rapidDesignSystem,
24
- });
25
-
26
- rapidDesignSystem
27
- .provideDesignSystem()
28
- .register(
29
- rapidDesignSystem.baseComponents,
30
- rapidGridProBeta(),
31
- rapidGridComponents,
32
- g2plotChartsComponents,
33
- );
34
-
35
- configureHeader({
36
- templateOptions: {
37
- provider: 'template',
38
- icon: 'rapid-icon',
39
- button: 'rapid-button',
40
- connectionIndicator: 'rapid-connection-indicator',
41
- select: 'rapid-select',
42
- option: 'rapid-option',
43
- flyout: 'rapid-flyout',
44
- },
45
- });
46
-
47
- }
1
+ import { getApp } from '@genesislcap/foundation-shell/app';
2
+ import { FoundationRouter } from '@genesislcap/foundation-ui';
3
+ import { avoidTreeShaking } from '@genesislcap/foundation-utils';
4
+ import { g2plotChartsComponents } from '@genesislcap/g2plot-chart';
5
+ import * as rapidDesignSystem from '@genesislcap/rapid-design-system';
6
+ import { rapidGridComponents, rapidGridProBeta } from '@genesislcap/rapid-grid-pro';
7
+
8
+ /**
9
+ * Ensure tree shaking doesn't remove these.
10
+ */
11
+ avoidTreeShaking(FoundationRouter);
12
+
13
+ /**
14
+ * registerComponents.
15
+ * @public
16
+ */
17
+ export async function registerComponents() {
18
+ const { configure: configureHeader } = await import('@genesislcap/foundation-header/config');
19
+ /**
20
+ * Register any PBC components with the design system
21
+ */
22
+ getApp().registerComponents({
23
+ designSystem: rapidDesignSystem,
24
+ });
25
+
26
+ rapidDesignSystem
27
+ .provideDesignSystem()
28
+ .register(
29
+ rapidDesignSystem.baseComponents,
30
+ rapidGridProBeta(),
31
+ rapidGridComponents,
32
+ g2plotChartsComponents,
33
+ );
34
+
35
+ configureHeader({
36
+ templateOptions: {
37
+ provider: 'template',
38
+ icon: 'rapid-icon',
39
+ button: 'rapid-button',
40
+ connectionIndicator: 'rapid-connection-indicator',
41
+ select: 'rapid-select',
42
+ option: 'rapid-option',
43
+ flyout: 'rapid-flyout',
44
+ },
45
+ });
46
+ }
@@ -1,10 +1,4 @@
1
- import {
2
- createContext,
3
- useState,
4
- useContext,
5
- ReactNode,
6
- FunctionComponent,
7
- } from 'react';
1
+ import { createContext, useState, useContext, ReactNode, FunctionComponent } from 'react';
8
2
  import { authService } from '../services/auth.service';
9
3
 
10
4
  interface AuthContextType {
@@ -31,9 +25,7 @@ export const useAuth = () => {
31
25
  return context;
32
26
  };
33
27
 
34
- export const AuthProvider: FunctionComponent<AuthProviderProps> = ({
35
- children,
36
- }) => {
28
+ export const AuthProvider: FunctionComponent<AuthProviderProps> = ({ children }) => {
37
29
  const [user, setUser] = useState<User | null>(null);
38
30
 
39
31
  const checkAuthStatus = async () => {
@@ -0,0 +1,99 @@
1
+ import type { AppRouteConfig, PbcRouteInput } from '@genesislcap/foundation-react-utils/router';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { useState } from 'react';
4
+ import { RoutesProvider, useRoutesContext } from './RoutesContext';
5
+
6
+ /**
7
+ * `getApp()` resolves the shell's `App` singleton out of the DI DOM container, which this
8
+ * unit test has no reason to boot. The stand-in mirrors the only thing the provider reads:
9
+ * a plain `routes` array field, stored once by the real `App` and only ever reassigned
10
+ * (registerAssets/deregisterAssets) — so identity behaves here exactly as it does in the app.
11
+ */
12
+ const mockShellApp: { routes: PbcRouteInput[] } = { routes: [] };
13
+
14
+ jest.mock('@genesislcap/foundation-shell/app', () => ({
15
+ getApp: () => mockShellApp,
16
+ }));
17
+
18
+ // Route pages are inert payload for this test — the provider only parks them in the route
19
+ // table as `element` values, never renders them — while dragging in the foundation-auth
20
+ // runtime and, for generated dashboards/charts, chart libraries jest cannot transform.
21
+ // Stubbed here; the route table itself and `mergePbcRoutes` stay real.
22
+ jest.mock('../pages/AuthPage/AuthPage', () => () => null);
23
+ jest.mock('../pages/NotPermittedPage/NotPermittedPage', () => () => null);
24
+ {{#each routes}}
25
+ jest.mock('../pages/{{pascalCase this.name}}/{{pascalCase this.name}}', () => () => null);
26
+ {{/each}}
27
+
28
+ /** Every context value the consumer below saw, in render order. */
29
+ const captured: AppRouteConfig[][] = [];
30
+
31
+ const RoutesConsumer = () => {
32
+ const routes = useRoutesContext();
33
+ captured.push(routes);
34
+ return null;
35
+ };
36
+
37
+ /** A fresh element tree per call, so React cannot bail out of re-rendering the provider. */
38
+ const providerTree = () => (
39
+ <RoutesProvider>
40
+ <RoutesConsumer />
41
+ </RoutesProvider>
42
+ );
43
+
44
+ const expectSingleIdentity = () => {
45
+ // initial render plus at least two re-renders
46
+ expect(captured.length).toBeGreaterThan(2);
47
+ captured.forEach((value) => {
48
+ expect(value).toBe(captured[0]);
49
+ });
50
+ };
51
+
52
+ describe('RoutesProvider', () => {
53
+ beforeEach(() => {
54
+ captured.length = 0;
55
+ mockShellApp.routes = [];
56
+ });
57
+
58
+ test('hands consumers one context value identity across re-renders', () => {
59
+ const { rerender } = render(providerTree());
60
+
61
+ rerender(providerTree());
62
+ rerender(providerTree());
63
+
64
+ expectSingleIdentity();
65
+ });
66
+
67
+ test('hands consumers one context value identity when a parent state change cascades in', () => {
68
+ const Parent = () => {
69
+ const [tick, setTick] = useState(0);
70
+ return (
71
+ <RoutesProvider>
72
+ <button type="button" onClick={() => setTick(tick + 1)}>
73
+ tick {tick}
74
+ </button>
75
+ <RoutesConsumer />
76
+ </RoutesProvider>
77
+ );
78
+ };
79
+
80
+ render(<Parent />);
81
+ const tickButton = screen.getByRole('button');
82
+ fireEvent.click(tickButton);
83
+ fireEvent.click(tickButton);
84
+
85
+ expectSingleIdentity();
86
+ });
87
+
88
+ test('recomputes the context value when the app reassigns its routes', () => {
89
+ const { rerender } = render(providerTree());
90
+ const beforeReassign = captured[captured.length - 1];
91
+
92
+ mockShellApp.routes = [{ path: 'pbc-page', settings: { permissionCode: 'PBC_VIEW' } }];
93
+ rerender(providerTree());
94
+
95
+ const afterReassign = captured[captured.length - 1];
96
+ expect(afterReassign).not.toBe(beforeReassign);
97
+ expect(afterReassign.map((route) => route.path)).toContain('/pbc-page');
98
+ });
99
+ });