@genesislcap/blank-app-seed 5.22.2-prerelease.2 → 5.23.1

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 (194) 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 +23 -16
  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/.genx/versions.json +3 -3
  69. package/.github/workflows/build.yml +22 -1
  70. package/.husky/pre-commit +8 -0
  71. package/CHANGELOG.md +97 -23
  72. package/bdd-tests/gradle/wrapper/gradle-wrapper.properties +1 -1
  73. package/bdd-tests/gradle.properties +0 -1
  74. package/client-tmp/angular/.oxfmtrc.json +32 -0
  75. package/client-tmp/angular/.oxlintrc.json +4 -0
  76. package/client-tmp/angular/README.md +4 -4
  77. package/client-tmp/angular/jest.config.js +1 -1
  78. package/client-tmp/angular/lint-css.js +24 -3
  79. package/client-tmp/angular/oxlint-staged.js +45 -0
  80. package/client-tmp/angular/package.json +19 -9
  81. package/client-tmp/angular/playwright.config.ts +12 -11
  82. package/client-tmp/angular/src/app/app.component.html +0 -1
  83. package/client-tmp/angular/src/app/app.component.ts +8 -9
  84. package/client-tmp/angular/src/app/app.config.ts +2 -2
  85. package/client-tmp/angular/src/app/app.module.ts +6 -5
  86. package/client-tmp/angular/src/app/components/error-message/error-message.component.html +29 -15
  87. package/client-tmp/angular/src/app/components/error-message/error-message.component.spec.ts +2 -2
  88. package/client-tmp/angular/src/app/components/error-message/error-message.component.ts +3 -5
  89. package/client-tmp/angular/src/app/directive/app-lazy-load.directive.ts +1 -0
  90. package/client-tmp/angular/src/app/guards/chained.guard.ts +19 -19
  91. package/client-tmp/angular/src/app/guards/connection.guard.ts +12 -12
  92. package/client-tmp/angular/src/app/layouts/blank/blank.layout.spec.ts +1 -1
  93. package/client-tmp/angular/src/app/layouts/blank/blank.layout.ts +1 -5
  94. package/client-tmp/angular/src/app/layouts/default/default.layout.html +3 -4
  95. package/client-tmp/angular/src/app/layouts/default/default.layout.spec.ts +4 -4
  96. package/client-tmp/angular/src/app/layouts/default/default.layout.ts +1 -1
  97. package/client-tmp/angular/src/app/pages/auth-login/auth-login.component.ts +1 -2
  98. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.html +3 -3
  99. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.scss +1 -1
  100. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.spec.ts +5 -5
  101. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.ts +3 -4
  102. package/client-tmp/angular/src/app/services/route.service.ts +79 -74
  103. package/client-tmp/angular/src/app/share/foundation-auth.ts +7 -1
  104. package/client-tmp/angular/src/app/share/genesis-components.ts +66 -68
  105. package/client-tmp/angular/src/app/utils/index.ts +3 -1
  106. package/client-tmp/angular/src/app/utils/permissions.ts +7 -7
  107. package/client-tmp/angular/src/declaration.d.ts +1 -1
  108. package/client-tmp/angular/src/environments/environment.ts +1 -1
  109. package/client-tmp/angular/src/jest.setup.ts +8 -0
  110. package/client-tmp/angular/src/main.ts +2 -2
  111. package/client-tmp/angular/src/pbc/container.ts +28 -26
  112. package/client-tmp/angular/src/pbc/elementsRenderer.ts +49 -39
  113. package/client-tmp/angular/src/pbc/utils.ts +61 -60
  114. package/client-tmp/angular/src/styles/_mixins.scss +7 -8
  115. package/client-tmp/angular/src/styles/styles.css +6 -6
  116. package/client-tmp/angular/test/e2e/fixture.ts +2 -2
  117. package/client-tmp/angular/test/e2e/flows/001-protected.e2e.ts +1 -1
  118. package/client-tmp/angular/tsconfig.app.json +2 -8
  119. package/client-tmp/angular/tsconfig.json +1 -4
  120. package/client-tmp/angular/tsconfig.spec.json +4 -11
  121. package/client-tmp/angular/webpack.dev.config.js +3 -4
  122. package/client-tmp/angular/webpack.prod.config.js +3 -4
  123. package/client-tmp/angular/webpack.shared.config.js +20 -24
  124. package/client-tmp/react/.oxfmtrc.json +30 -0
  125. package/client-tmp/react/.oxlintrc.json +4 -0
  126. package/client-tmp/react/README.md +4 -4
  127. package/client-tmp/react/jest.config.ts +8 -4
  128. package/client-tmp/react/jest.setup.ts +85 -1
  129. package/client-tmp/react/oxlint-staged.js +45 -0
  130. package/client-tmp/react/package.json +24 -9
  131. package/client-tmp/react/playwright.config.ts +11 -10
  132. package/client-tmp/react/src/App.tsx +27 -29
  133. package/client-tmp/react/src/__mocks__/fileMock.js +1 -0
  134. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.test.tsx +16 -4
  135. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.tsx +32 -29
  136. package/client-tmp/react/src/components/error-boundary/ErrorBoundary.tsx +15 -6
  137. package/client-tmp/react/src/components/routes/AppRoutes.tsx +1 -1
  138. package/client-tmp/react/src/components/single-component/registry.ts +4 -0
  139. package/client-tmp/react/src/custom-elements.d.ts +4 -2
  140. package/client-tmp/react/src/helpers/hasPermissionHelper.tsx +1 -1
  141. package/client-tmp/react/src/helpers/isAuthenticatedHelper.tsx +1 -1
  142. package/client-tmp/react/src/helpers/isConnectedHelper.tsx +3 -3
  143. package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +7 -11
  144. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +8 -8
  145. package/client-tmp/react/src/main.tsx +11 -11
  146. package/client-tmp/react/src/pages/AuthPage/AuthPage.test.tsx +13 -2
  147. package/client-tmp/react/src/pages/AuthPage/AuthPage.tsx +14 -13
  148. package/client-tmp/react/src/pages/NotFoundPage/NotFoundPage.tsx +2 -7
  149. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +8 -4
  150. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +1 -1
  151. package/client-tmp/react/src/pbc/container.tsx +9 -4
  152. package/client-tmp/react/src/pbc/elementsRenderer.tsx +18 -9
  153. package/client-tmp/react/src/pbc/utils.ts +60 -59
  154. package/client-tmp/react/src/services/auth.service.ts +1 -1
  155. package/client-tmp/react/src/services/store.service.ts +5 -3
  156. package/client-tmp/react/src/share/foundation-login.ts +2 -2
  157. package/client-tmp/react/src/share/genesis-components.ts +46 -47
  158. package/client-tmp/react/src/store/AuthContext.tsx +2 -10
  159. package/client-tmp/react/src/store/RoutesContext.test.tsx +99 -0
  160. package/client-tmp/react/src/store/RoutesContext.tsx +25 -5
  161. package/client-tmp/react/src/styles/styles.css +7 -7
  162. package/client-tmp/react/src/types/LayoutName.ts +1 -1
  163. package/client-tmp/react/src/utils/getLayoutNameByRoute.ts +1 -1
  164. package/client-tmp/react/src/utils/index.ts +2 -0
  165. package/client-tmp/react/src/utils/layout.ts +8 -1
  166. package/client-tmp/react/src/utils/permissions.ts +7 -7
  167. package/client-tmp/react/src/utils/useDocumentTitle.ts +5 -6
  168. package/client-tmp/react/test/e2e/fixture.ts +5 -2
  169. package/client-tmp/react/test/e2e/flows/001-protected.e2e.ts +3 -1
  170. package/client-tmp/react/tsconfig.app.json +1 -1
  171. package/client-tmp/react/tsconfig.json +1 -5
  172. package/client-tmp/web-components/.oxfmtrc.json +30 -0
  173. package/client-tmp/web-components/.oxlintrc.json +4 -0
  174. package/client-tmp/web-components/oxlint-staged.js +45 -0
  175. package/client-tmp/web-components/package.json +14 -22
  176. package/client-tmp/web-components/src/components/components.ts +9 -7
  177. package/client-tmp/web-components/src/main/main.css +1 -2
  178. package/client-tmp/web-components/src/main/main.ts +1 -1
  179. package/client-tmp/web-components/src/routes/config.ts +4 -0
  180. package/client-tmp/web-components/src/utils/index.ts +2 -0
  181. package/client-tmp/web-components/tsconfig.json +1 -4
  182. package/gradle/wrapper/gradle-wrapper.properties +1 -1
  183. package/package.json +1 -1
  184. package/server/gradle/wrapper/gradle-wrapper.properties +1 -1
  185. package/server/gradle.properties +0 -2
  186. package/.genx/templates/angular/gridLayout.hbs +0 -30
  187. package/.genx/templates/angular/horizontalLayout.hbs +0 -7
  188. package/.genx/templates/angular/tabsLayout.hbs +0 -7
  189. package/.genx/tests/fixtures/testapp/client/src/routes/home/home.template.ts +0 -6
  190. package/client-tmp/react/.babelrc +0 -10
  191. package/client-tmp/react/.eslintrc.cjs +0 -21
  192. package/client-tmp/react/git +0 -0
  193. package/client-tmp/react/webpack.config.js +0 -121
  194. package/client-tmp/web-components/.prettierignore +0 -14
@@ -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
+ });
@@ -3,15 +3,23 @@ import {
3
3
  type AppRouteConfig,
4
4
  type PbcRouteInput,
5
5
  } from '@genesislcap/foundation-react-utils/router';
6
- import React, { createContext, useContext, ReactNode } from 'react';
7
6
  import { getApp } from '@genesislcap/foundation-shell/app';
7
+ import React, { createContext, useContext, useMemo, ReactNode } from 'react';
8
+ import { AUTH_PATH, NOT_PERMITTED_PATH } from '../config';
9
+ {{#if routes.[0]}}
10
+ /* eslint-disable import-es/order -- generated route pages follow route-table order, not alphabetical */
8
11
  import AuthPage from '../pages/AuthPage/AuthPage';
9
12
  import NotPermittedPage from '../pages/NotPermittedPage/NotPermittedPage';
10
13
  {{#each routes}}
11
14
  import {{pascalCase this.name}} from '../pages/{{pascalCase this.name}}/{{pascalCase this.name}}';
12
15
  {{/each}}
13
16
  import PBCContainer from '../pbc/container';
14
- import { AUTH_PATH, NOT_PERMITTED_PATH } from '../config';
17
+ /* eslint-enable import-es/order */
18
+ {{else}}
19
+ import AuthPage from '../pages/AuthPage/AuthPage';
20
+ import NotPermittedPage from '../pages/NotPermittedPage/NotPermittedPage';
21
+ import PBCContainer from '../pbc/container';
22
+ {{/if}}
15
23
 
16
24
  /**
17
25
  * Static application routes as a declarative table consumed by `renderAppRoutes`
@@ -46,9 +54,21 @@ const staticRoutes: AppRouteConfig[] = [
46
54
  const RoutesContext = createContext<AppRouteConfig[]>([]);
47
55
 
48
56
  export const RoutesProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
49
- const routes = mergePbcRoutes(staticRoutes, getApp().routes as unknown as PbcRouteInput[], {
50
- renderPbc: () => <PBCContainer />,
51
- });
57
+ const pbcRoutes = getApp().routes as unknown as PbcRouteInput[];
58
+
59
+ /**
60
+ * Memoized so the context value keeps a stable identity across re-renders. Without this,
61
+ * every re-render of this provider hands consumers a brand new array and re-runs the
62
+ * effects keyed on it — `PBCContainer` would tear down and re-create the mounted custom
63
+ * element, and the document title would be re-applied on each pass.
64
+ */
65
+ const routes = useMemo(
66
+ () =>
67
+ mergePbcRoutes(staticRoutes, pbcRoutes, {
68
+ renderPbc: () => <PBCContainer />,
69
+ }),
70
+ [pbcRoutes],
71
+ );
52
72
 
53
73
  return <RoutesContext.Provider value={routes}>{children}</RoutesContext.Provider>;
54
74
  };
@@ -1,25 +1,25 @@
1
1
  {{#if fontData}}
2
2
  {{#each fontData}}
3
3
  @font-face {
4
- font-family: "{{../fontFamily}}";
4
+ font-family: '{{../fontFamily}}';
5
5
  src: url("../public/fonts/{{filename}}") format("{{format}}");
6
6
  font-weight: {{weight}};
7
7
  font-style: normal;
8
8
  font-display: swap;
9
9
  }
10
10
  {{/each}}
11
- {{/if}}
12
11
 
12
+ {{/if}}
13
13
  {{#unless fontFamily}}
14
14
  @font-face {
15
15
  font-family: Segoe UI;
16
16
  font-weight: 300;
17
- src: local("Segoe UI Semilight"), local("Segoe UI");
17
+ src: local('Segoe UI Semilight'), local('Segoe UI');
18
18
  }
19
- {{/unless}}
20
19
 
20
+ {{/unless}}
21
21
  * {
22
- font-family: {{#if fontFamily}}"{{fontFamily}}", {{/if}}"Segoe UI", "Helvetica Neue", Arial, sans-serif;
22
+ font-family: {{#if fontFamily}}'{{fontFamily}}', {{/if}}'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
23
23
  font-style: normal;
24
24
  font-weight: normal;
25
25
  }
@@ -36,7 +36,7 @@ body,
36
36
  --nav-height: 60px;
37
37
  }
38
38
 
39
- body{
39
+ body {
40
40
  overflow: hidden;
41
41
  }
42
42
 
@@ -50,4 +50,4 @@ foundation-router {
50
50
 
51
51
  rapid-design-system-provider {
52
52
  overflow: hidden;
53
- }
53
+ }
@@ -1,3 +1,3 @@
1
1
  type LayoutName = 'blank' | 'default';
2
2
 
3
- export default LayoutName;
3
+ export default LayoutName;
@@ -2,4 +2,4 @@ import { routeLayouts } from '../config';
2
2
 
3
3
  export const getLayoutNameByRoute = (route: string) => {
4
4
  return routeLayouts[route] || 'default';
5
- }
5
+ };
@@ -1,4 +1,6 @@
1
+ {{#if FDC3.includeDependencies}}
1
2
  export * from './fdc3';
3
+ {{/if}}
2
4
  export * from './getLayoutNameByRoute';
3
5
  export * from './layout';
4
6
  export * from './permissions';
@@ -1,10 +1,17 @@
1
1
  import { isDev } from '@genesislcap/foundation-utils';
2
2
  import pkg from '../../package.json';
3
3
 
4
+ // Bump this whenever the persisted flexlayout model changes shape in a way that
5
+ // older saves cannot satisfy — the tab `component` ids are generated, so a change
6
+ // to how they are derived would otherwise restore a layout whose tabs no longer
7
+ // resolve to a component and render as empty panels. A new version simply ignores
8
+ // the stale save once and falls back to the generated default layout.
9
+ const LAYOUT_SCHEMA_VERSION = 'v2';
10
+
4
11
  // Returns null to disable persistence in dev unless PERSIST_LAYOUT_IN_DEV=true
5
12
  export const getFlexLayoutStorageKey = (id: string): string | null => {
6
13
  if (isDev() && (pkg as any)?.config.PERSIST_LAYOUT_IN_DEV !== true) {
7
14
  return null;
8
15
  }
9
- return `flexlayout_${id}`;
16
+ return `flexlayout_${LAYOUT_SCHEMA_VERSION}_${id}`;
10
17
  };
@@ -1,7 +1,7 @@
1
- import { User } from '@genesislcap/foundation-user';
2
-
3
- export const getViewUpdateRightComponent = (
4
- user: User,
5
- right: string,
6
- event: string | boolean = true,
7
- ) => (!right || user.hasPermission(right) ? event : '');
1
+ import { User } from '@genesislcap/foundation-user';
2
+
3
+ export const getViewUpdateRightComponent = (
4
+ user: User,
5
+ right: string,
6
+ event: string | boolean = true,
7
+ ) => (!right || user.hasPermission(right) ? event : '');
@@ -26,21 +26,20 @@ export const useDocumentTitle = () => {
26
26
  return location.pathname === routePath || location.pathname.startsWith(routePath + '/');
27
27
  });
28
28
 
29
- let pageTitle = 'React App';
29
+ let pageTitle = '{{capitalCase appName}}';
30
30
 
31
31
  if (currentRoute) {
32
32
  if (currentRoute.title) {
33
33
  pageTitle = currentRoute.title;
34
- }
35
- else if (currentRoute.data?.navItems && currentRoute.data.navItems.length > 0) {
36
- pageTitle = currentRoute.data.navItems[0].title || 'React App';
34
+ } else if (currentRoute.data?.navItems && currentRoute.data.navItems.length > 0) {
35
+ pageTitle = currentRoute.data.navItems[0].title || '{{capitalCase appName}}';
37
36
  }
38
37
  }
39
38
 
40
39
  document.title = pageTitle;
41
40
 
42
41
  return () => {
43
- document.title = 'React App';
42
+ document.title = '{{capitalCase appName}}';
44
43
  };
45
44
  }, [location.pathname, routes]);
46
- };
45
+ };
@@ -1,6 +1,6 @@
1
1
  import { base, Page } from '@genesislcap/foundation-testing/e2e';
2
- import { ProtectedPage } from './pages';
3
2
  import { PORT } from '../../playwright.config';
3
+ import { ProtectedPage } from './pages';
4
4
 
5
5
  export type FixtureConfig = {
6
6
  API_HOST: string;
@@ -18,7 +18,10 @@ const API_HOST = process.env['API_HOST'] || 'localhost';
18
18
 
19
19
  export const test = base.extend<Fixture>({
20
20
  config: [{ PORT, API_HOST, DEFAULT_PASSWORD: '', DEFAULT_USER: '' }, { option: true }],
21
- protectedPage: async ({ config, page }: { config: FixtureConfig, page: Page }, use: (page: ProtectedPage) => Promise<unknown>) => {
21
+ protectedPage: async (
22
+ { config, page }: { config: FixtureConfig; page: Page },
23
+ use: (page: ProtectedPage) => Promise<unknown>,
24
+ ) => {
22
25
  const protectedPage = new ProtectedPage(config, page);
23
26
  await protectedPage.goto();
24
27
  await use(protectedPage);
@@ -1,6 +1,8 @@
1
1
  import { expect } from '@genesislcap/foundation-testing/e2e';
2
2
  import { test } from '../fixture';
3
3
 
4
- test('expected page title', async ({ page }: { page: any }) => {
4
+ // `protectedPage` looks unused, but destructuring it is what makes Playwright instantiate
5
+ // the fixture — and the fixture's setup is what navigates `page` to the app.
6
+ test('expected page title', async ({ protectedPage, page }) => {
5
7
  await expect(page).toHaveTitle(/{{capitalCase appName}}/);
6
8
  });
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "extends": "./tsconfig.json",
3
- "include": ["src/**/*", "src/**/*.d.ts"],
3
+ "include": ["src/**/*", "src/**/*.d.ts"]
4
4
  }
@@ -24,9 +24,5 @@
24
24
  "experimentalDecorators": true,
25
25
  "emitDecoratorMetadata": true
26
26
  },
27
- "include": [
28
- "src/**/*",
29
- "src/**/*.d.ts",
30
- "src/pbc/**/*.ts",
31
- ],
27
+ "include": ["src/**/*", "src/**/*.d.ts", "src/pbc/**/*.ts"]
32
28
  }
@@ -0,0 +1,30 @@
1
+ {
2
+ "semi": true,
3
+ "tabWidth": 2,
4
+ "singleQuote": true,
5
+ "bracketSpacing": true,
6
+ "printWidth": 100,
7
+ "htmlWhitespaceSensitivity": "ignore",
8
+ "sortPackageJson": false,
9
+ "ignorePatterns": [
10
+ "**/.git",
11
+ "**/.svn",
12
+ "**/.hg",
13
+ "**/node_modules",
14
+ "**/__tests__",
15
+ "**/__test__",
16
+ "**/.tmp",
17
+ "**/bootstrap",
18
+ "**/coverage",
19
+ "**/test-results",
20
+ "**/dist",
21
+ "**/temp",
22
+ "**/storybook-static",
23
+ "**/*.d.ts",
24
+ "**/*.md",
25
+ "**/*.hbs",
26
+ "**/.genx/**",
27
+ "**/src/generated/**",
28
+ "**/index.html"
29
+ ]
30
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "extends": ["./node_modules/@genesislcap/oxlint-config/.oxlintrc.json"]
4
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Lints the staged files, fixing what can be fixed and failing on what cannot.
3
+ *
4
+ * `oxlint --fix` is not single-pass convergent: it applies at most one fix per
5
+ * overlapping span, so a file with several unused specifiers in one import
6
+ * statement still reports the rest afterwards. A single
7
+ * `oxlint --fix --deny-warnings` would therefore reject a commit whose findings
8
+ * are entirely auto-fixable, and lint-staged would revert the partial fix. There
9
+ * is no `--fix-passes` flag (oxlint 1.78), so the loop lives here; it can go
10
+ * back to one plain call once oxlint converges on its own.
11
+ *
12
+ * The fixing passes stay quiet so a remaining finding is reported once, by the
13
+ * final pass, rather than once per attempt.
14
+ */
15
+ const { spawnSync } = require('node:child_process');
16
+
17
+ /** Convergence needs about log2(findings) passes; this is well clear of that. */
18
+ const MAX_PASSES = 10;
19
+
20
+ const files = process.argv.slice(2);
21
+
22
+ const runOxlint = (args, quiet) =>
23
+ spawnSync('oxlint', [...args, ...files], {
24
+ stdio: quiet ? 'ignore' : 'inherit',
25
+ shell: process.platform === 'win32',
26
+ });
27
+
28
+ if (files.length === 0) {
29
+ process.exit(0);
30
+ }
31
+
32
+ for (let pass = 0; pass < MAX_PASSES; pass += 1) {
33
+ const { status, error } = runOxlint(['--fix', '--deny-warnings'], true);
34
+ if (error) {
35
+ // Never fail silently: a missing binary must not look like a lint failure.
36
+ console.error(`oxlint-staged: could not run oxlint (${error.code})`);
37
+ process.exit(1);
38
+ }
39
+ if (status === 0) {
40
+ process.exit(0);
41
+ }
42
+ }
43
+
44
+ // Still not clean: report what is left (without touching the files again) and fail.
45
+ process.exit(runOxlint(['--deny-warnings'], false).status === 0 ? 0 : 1);
@@ -41,10 +41,9 @@
41
41
  "dev:https": "npm run dev -- -- --https",
42
42
  "dev:vite": "genx dev -e API_HOST,ENABLE_SSO -b vite",
43
43
  "dev:webpack": "genx dev -e API_HOST,ENABLE_SSO -b webpack",
44
- "git:setup": "cd .. && npx --yes husky install",
45
- "lint": "genx lint --profile",
46
- "lint:fix": "genx lint --fix",
47
- "lint:eslint": "genx lint -l eslint --profile",
44
+ "git:setup": "cd .. && npx --no --prefix client husky install",
45
+ "lint": "genx lint -l ox",
46
+ "lint:fix": "genx lint -l ox --fix",
48
47
  "lint:stylelint": "genx lint -l stylelint",
49
48
  "serve": "genx serve",
50
49
  "test": "genx test",
@@ -54,43 +53,36 @@
54
53
  "test:coverage": "genx test --coverage",
55
54
  "test:unit:watch": "genx test --watch"
56
55
  },
57
- "husky": {
58
- "hooks": {
59
- "pre-commit": "lint-staged"
60
- }
61
- },
62
56
  "lint-staged": {
63
- "*.{ts,js}": [
64
- "eslint"
57
+ "*.{ts,js,tsx,jsx}": [
58
+ "node oxlint-staged.js",
59
+ "oxfmt --write"
60
+ ],
61
+ "*.{mjs,cjs}": [
62
+ "oxfmt --write"
65
63
  ],
66
64
  "*.styles.ts": [
67
- "stylelint"
65
+ "stylelint --allow-empty-input"
68
66
  ]
69
67
  },
70
- "eslintConfig": {
71
- "extends": "@genesislcap/eslint-config",
72
- "ignorePatterns": ["**/dist/**", "**/public/**", "**/coverage/**", "**/test/**", "playwright.config.ts"]
73
- },
74
68
  "stylelint": {
75
69
  "extends": "@genesislcap/stylelint-config"
76
70
  },
77
- "prettier": "@genesislcap/prettier-config",
78
71
  "devDependencies": {
79
72
  "@genesislcap/design-system-configurator": "{{versions.UI}}",
80
- "@genesislcap/eslint-config": "{{versions.UI}}",
81
73
  "@genesislcap/foundation-testing": "{{versions.UI}}",
82
74
  "@genesislcap/genx": "{{versions.UI}}",
83
75
  "@genesislcap/event-type-codegen": "{{versions.UI}}",
84
- "@genesislcap/eslint-stylelint-builder": "{{versions.UI}}",
76
+ "@genesislcap/oxlint-config": "{{versions.UI}}",
85
77
  "@genesislcap/uvu-playwright-builder": "{{versions.UI}}",
86
78
  "@genesislcap/vite-builder": "{{versions.UI}}",
87
79
  "@genesislcap/webpack-builder": "{{versions.UI}}",
88
- "@genesislcap/prettier-config": "{{versions.UI}}",
89
80
  "@genesislcap/stylelint-config": "{{versions.UI}}",
90
81
  "@types/node": "20.11.5",
91
82
  "husky": "^7.0.4",
92
- "lint-prepush": "^2.2.1",
93
- "lint-staged": "^12.4.1"
83
+ "lint-staged": "^12.4.1",
84
+ "oxfmt": "^0.44.0",
85
+ "oxlint": "^1.77.0"
94
86
  },
95
87
  "dependencies": {
96
88
  "@genesislcap/foundation-auth": "{{versions.UI}}",
@@ -32,13 +32,15 @@ export async function registerComponents() {
32
32
  designSystem: rapidDesignSystem,
33
33
  });
34
34
 
35
- rapidDesignSystem.provideDesignSystem().register(
36
- rapidDesignSystem.baseComponents,
37
- rapidGridProBeta(),
38
- rapidGridComponents,
39
- g2plotChartsComponents,
40
- foundationLayoutComponents,
41
- );
35
+ rapidDesignSystem
36
+ .provideDesignSystem()
37
+ .register(
38
+ rapidDesignSystem.baseComponents,
39
+ rapidGridProBeta(),
40
+ rapidGridComponents,
41
+ g2plotChartsComponents,
42
+ foundationLayoutComponents,
43
+ );
42
44
 
43
45
  configureHeader({
44
46
  templateOptions: {
@@ -15,11 +15,10 @@ foundation-router {
15
15
  :not(:defined) {
16
16
  visibility: hidden;
17
17
  }
18
-
19
18
  {{#if fontData}}
20
19
  {{#each fontData}}
21
20
  @font-face {
22
- font-family: "{{../fontFamily}}";
21
+ font-family: '{{../fontFamily}}';
23
22
  src: url("../../public/fonts/{{filename}}") format("{{format}}");
24
23
  font-weight: {{weight}};
25
24
  font-style: normal;
@@ -110,7 +110,7 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
110
110
  FDC3ReadyHandler = () => {
111
111
  {{#each FDC3.channels}}
112
112
  listenToChannel('{{this.name}}', '{{this.type}}', (result) => {
113
- console.log('Received FDC3 channel message on: {{this.name}} channel, type: {{this.type}}', result);
113
+ console.log('Received FDC3 message on {{this.name}} ({{this.type}})', result);
114
114
  // TODO: Add your listener logic here
115
115
  // E.g. open a modal or route to specific page: Route.path.push(`[Route name]`);
116
116
  });
@@ -3,9 +3,13 @@ import { Auth, Connect } from '@genesislcap/foundation-comms';
3
3
  import { FoundationRouterConfiguration } from '@genesislcap/foundation-ui';
4
4
  import { GENESIS_SOCKET_URL, PUBLIC_PATH } from '@genesislcap/foundation-utils';
5
5
  import { defaultLayout, loginLayout } from '../layouts';
6
+ {{#if routes}}
7
+ /* eslint-disable import-es/order -- generated page imports follow the navigation order */
6
8
  {{#each routes}}
7
9
  import { {{pascalCase this.name}} } from './{{kebabCase this.name}}/{{kebabCase this.name}}';
8
10
  {{/each}}
11
+ /* eslint-enable import-es/order */
12
+ {{/if}}
9
13
  import { NotFound } from './not-found/not-found';
10
14
  import { defaultNotPermittedRoute, NotPermitted } from './not-permitted/not-permitted';
11
15
  import { LoginSettings } from './types';
@@ -1,4 +1,6 @@
1
+ {{#if FDC3.includeDependencies}}
1
2
  export * from './fdc3';
3
+ {{/if}}
2
4
  export * from './layout';
3
5
  export * from './logger';
4
6
  export * from './permissions';
@@ -24,9 +24,6 @@
24
24
  "strictNullChecks": false,
25
25
  "target": "ES2015"
26
26
  },
27
- "include": [
28
- "globals.d.ts" ,
29
- "src/**/*"
30
- ],
27
+ "include": ["globals.d.ts", "src/**/*"],
31
28
  "exclude": ["node_modules", "dist"]
32
29
  }
@@ -1,6 +1,6 @@
1
1
  distributionBase=GRADLE_USER_HOME
2
2
  distributionPath=wrapper/dists
3
- distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
3
+ distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
4
4
  networkTimeout=10000
5
5
  validateDistributionUrl=true
6
6
  zipStoreBase=GRADLE_USER_HOME
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed",
3
3
  "description": "Genesis Blank App Seed",
4
- "version": "5.22.2-prerelease.2",
4
+ "version": "5.23.1",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "release": "semantic-release"
@@ -1,6 +1,6 @@
1
1
  distributionBase=GRADLE_USER_HOME
2
2
  distributionPath=wrapper/dists
3
- distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
3
+ distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
4
4
  networkTimeout=10000
5
5
  validateDistributionUrl=true
6
6
  zipStoreBase=GRADLE_USER_HOME
@@ -19,5 +19,3 @@ ssh-port=1337
19
19
  enableDockerContainerTasks=true
20
20
 
21
21
  compactProcessesStrategy=DEV
22
-
23
- useDevRepo=true
@@ -1,30 +0,0 @@
1
- <rapid-layout-region type="horizontal">
2
- <rapid-layout-region type="vertical">
3
- {{#each route.tiles}}
4
- {{#ifEquals @index 0}}
5
- <rapid-layout-item title="{{this.title}}">
6
- <app-{{kebabCase this.componentName}}></app-{{kebabCase this.componentName}}>
7
- </rapid-layout-item>
8
- {{/ifEquals}}
9
- {{#ifEquals @index 1}}
10
- <rapid-layout-item title="{{this.title}}">
11
- <app-{{kebabCase this.componentName}}></app-{{kebabCase this.componentName}}>
12
- </rapid-layout-item>
13
- {{/ifEquals}}
14
- {{/each}}
15
- </rapid-layout-region>
16
- <rapid-layout-region type="vertical">
17
- {{#each route.tiles}}
18
- {{#ifEquals @index 2}}
19
- <rapid-layout-item title="{{this.title}}">
20
- <app-{{kebabCase this.componentName}}></app-{{kebabCase this.componentName}}>
21
- </rapid-layout-item>
22
- {{/ifEquals}}
23
- {{#ifEquals @index 3}}
24
- <rapid-layout-item title="{{this.title}}">
25
- <app-{{kebabCase this.componentName}}></app-{{kebabCase this.componentName}}>
26
- </rapid-layout-item>
27
- {{/ifEquals}}
28
- {{/each}}
29
- </rapid-layout-region>
30
- </rapid-layout-region>
@@ -1,7 +0,0 @@
1
- <rapid-layout-region>
2
- {{#each route.tiles}}
3
- <rapid-layout-item title="{{this.title}}">
4
- <app-{{kebabCase this.componentName}}></app-{{kebabCase this.componentName}}>
5
- </rapid-layout-item>
6
- {{/each}}
7
- </rapid-layout-region>
@@ -1,7 +0,0 @@
1
- <rapid-layout-region type="tabs">
2
- {{#each route.tiles}}
3
- <rapid-layout-item title="{{this.title}}">
4
- <app-{{kebabCase this.componentName}}></app-{{kebabCase this.componentName}}>
5
- </rapid-layout-item>
6
- {{/each}}
7
- </rapid-layout-region>