@genesislcap/blank-app-seed 3.29.2 → 3.30.0-prerelease.10
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.
- package/.genx/package.json +1 -1
- package/.genx/scripts/update-versions.js +45 -15
- package/.genx/templates/angular/entityManager.hbs +3 -0
- package/.genx/templates/angular/route.hbs +0 -1
- package/.genx/templates/react/chart.hbs +9 -1
- package/.genx/templates/react/component/component.column.defs.hbs +4 -2
- package/.genx/templates/react/component/component.create.form.hbs +2 -2
- package/.genx/templates/react/component/component.gridOptions.hbs +5 -3
- package/.genx/templates/react/component/component.hbs +65 -20
- package/.genx/templates/react/component/component.index.hbs +1 -1
- package/.genx/templates/react/component/component.update.form.hbs +1 -1
- package/.genx/templates/react/entityManager.hbs +63 -43
- package/.genx/templates/react/form.hbs +9 -1
- package/.genx/templates/react/grid.hbs +23 -7
- package/.genx/templates/react/gridLayout.hbs +28 -28
- package/.genx/templates/react/horizontalLayout.hbs +5 -5
- package/.genx/templates/react/route.hbs +4 -19
- package/.genx/templates/react/tabsLayout.hbs +5 -5
- package/.genx/templates/web-components/entityManager.hbs +3 -0
- package/.genx/utils/formatRouteData.js +1 -1
- package/.genx/utils/generateRoute.js +8 -7
- package/.genx/utils/generateTile.js +31 -15
- package/.genx/versions.json +3 -3
- package/.github/pull_request_template.md +1 -1
- package/.github/workflows/build.yml +3 -2
- package/.github/workflows/slack.yml +1 -1
- package/.github/workflows/upgrade.yml +6 -1
- package/.github/workflows/upgrade_prerelease.yml +1 -0
- package/.idea/modules.xml +6 -6
- package/CHANGELOG.md +165 -4
- package/client-tmp/react/.babelrc +10 -0
- package/client-tmp/react/.eslintrc.cjs +4 -1
- package/client-tmp/react/env.development.json +3 -0
- package/client-tmp/react/index.html +1 -1
- package/client-tmp/react/jest.config.ts +15 -0
- package/client-tmp/react/jest.setup.ts +1 -0
- package/client-tmp/react/lint-css.ts +19 -0
- package/client-tmp/react/package.json +42 -6
- package/client-tmp/react/playwright.config.ts +17 -0
- package/client-tmp/react/src/App.tsx +34 -7
- package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.test.tsx +95 -0
- package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.tsx +67 -0
- package/client-tmp/react/src/config.ts +2 -1
- package/client-tmp/react/src/custom-elements.d.ts +19 -9
- package/client-tmp/react/src/guards/AuthGuard.tsx +1 -1
- package/client-tmp/react/src/guards/PermissionsGuard.tsx +37 -0
- package/client-tmp/react/src/index.ts +21 -0
- package/client-tmp/react/src/pages/AuthPage/AuthPage.test.tsx +10 -0
- package/client-tmp/react/src/pages/{auth/AuthPage.jsx → AuthPage/AuthPage.tsx} +3 -2
- package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.css +0 -0
- package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +17 -0
- package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +14 -0
- package/client-tmp/react/src/{reportWebVitals.js → reportWebVitals.ts} +1 -1
- package/client-tmp/react/src/share/foundation-login.ts +1 -1
- package/client-tmp/react/src/svg-elements.d.ts +1 -1
- package/client-tmp/react/src/utils/fdc3.ts +32 -0
- package/client-tmp/react/src/utils/history.ts +1 -3
- package/client-tmp/react/src/utils/index.ts +4 -0
- package/client-tmp/react/src/utils/permissions.ts +7 -0
- package/client-tmp/react/src/utils/setApiHost.ts +9 -0
- package/client-tmp/react/test/e2e/fixture.ts +26 -0
- package/client-tmp/react/test/e2e/flows/001-protected.e2e.ts +6 -0
- package/client-tmp/react/test/e2e/index.ts +2 -0
- package/client-tmp/react/test/e2e/pages/index.ts +1 -0
- package/client-tmp/react/test/e2e/pages/protected.ts +16 -0
- package/client-tmp/react/tsconfig.app.json +2 -5
- package/client-tmp/react/tsconfig.json +4 -1
- package/client-tmp/react/vite.config.ts +57 -12
- package/package.json +1 -1
- package/server/settings.gradle.kts +2 -2
- package/client-tmp/react/src/index.js +0 -17
- /package/client-tmp/react/src/pages/{auth → AuthPage}/AuthPage.css +0 -0
- /package/client-tmp/react/src/{setupTests.js → setupTests.ts} +0 -0
|
@@ -1,36 +1,59 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import {
|
|
3
2
|
unstable_HistoryRouter as HistoryRouter,
|
|
4
3
|
Routes,
|
|
5
4
|
Route,
|
|
6
5
|
useLocation,
|
|
7
6
|
} from 'react-router-dom';
|
|
8
|
-
import
|
|
7
|
+
import { useEffect } from 'react';
|
|
8
|
+
import { history, setApiHost{{#if FDC3.channels.length}}, listenToChannel{{/if}} } from './utils';
|
|
9
9
|
import LayoutWrapper from './layouts/LayoutWrapper';
|
|
10
|
-
import { routeLayouts } from './config';
|
|
10
|
+
import { AUTH_PATH, NOT_PERMITTED_PATH, routeLayouts } from './config';
|
|
11
11
|
import AuthGuard from './guards/AuthGuard';
|
|
12
|
+
import PermissionsGuard from './guards/PermissionsGuard';
|
|
12
13
|
import { AuthProvider } from './store/AuthContext';
|
|
13
14
|
// Pages Components
|
|
14
|
-
import AuthPage from './pages/
|
|
15
|
+
import AuthPage from './pages/AuthPage/AuthPage';
|
|
16
|
+
import NotPermittedPage from './pages/NotPermittedPage/NotPermittedPage';
|
|
15
17
|
{{#each routes}}
|
|
16
|
-
import {{pascalCase this.name}} from './pages/{{
|
|
18
|
+
import {{pascalCase this.name}} from './pages/{{pascalCase this.name}}/{{pascalCase this.name}}';
|
|
17
19
|
{{/each}}
|
|
20
|
+
|
|
18
21
|
// Genesis Components
|
|
19
22
|
import './share/genesis-components';
|
|
20
23
|
|
|
21
24
|
const LayoutWithLocation = () => {
|
|
22
25
|
const location = useLocation();
|
|
23
26
|
const layout = routeLayouts[location.pathname] || 'default';
|
|
27
|
+
{{#if FDC3.channels.length}}
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
{{#each FDC3.channels}}
|
|
30
|
+
listenToChannel('{{this.name}}', '{{this.type}}', (result) => {
|
|
31
|
+
console.log('Received FDC3 channel message on: {{this.name}} channel, type: {{this.type}}', result);
|
|
32
|
+
// TODO: Add your listener logic here
|
|
33
|
+
// E.g. open a modal or route to specific page: Route.path.push(`[Route name]`);
|
|
34
|
+
});
|
|
35
|
+
{{/each}}
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
console.log('Component is being unmounted');
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
{{/if}}
|
|
24
42
|
|
|
25
43
|
let pageComponent;
|
|
44
|
+
let permissionCode = '{{this.permissions.viewRight}}';
|
|
26
45
|
|
|
27
46
|
switch (location.pathname) {
|
|
28
|
-
case
|
|
47
|
+
case `/${AUTH_PATH}`:
|
|
29
48
|
pageComponent = <AuthPage />;
|
|
30
49
|
break;
|
|
50
|
+
case `/${NOT_PERMITTED_PATH}`:
|
|
51
|
+
pageComponent = <NotPermittedPage />;
|
|
52
|
+
break;
|
|
31
53
|
{{#each routes}}
|
|
32
54
|
case '/{{kebabCase this.name}}':
|
|
33
55
|
pageComponent = <{{pascalCase this.name}} />;
|
|
56
|
+
permissionCode = '{{this.permissions.viewRight}}';
|
|
34
57
|
break;
|
|
35
58
|
{{/each}}
|
|
36
59
|
default:
|
|
@@ -45,13 +68,17 @@ const LayoutWithLocation = () => {
|
|
|
45
68
|
} else {
|
|
46
69
|
return (
|
|
47
70
|
<AuthGuard>
|
|
48
|
-
<
|
|
71
|
+
<PermissionsGuard permissionCode={permissionCode}>
|
|
72
|
+
<LayoutWrapper layout={layout}>{pageComponent}</LayoutWrapper>
|
|
73
|
+
</PermissionsGuard>
|
|
49
74
|
</AuthGuard>
|
|
50
75
|
);
|
|
51
76
|
}
|
|
52
77
|
};
|
|
53
78
|
|
|
54
79
|
const App: React.FC = () => {
|
|
80
|
+
setApiHost();
|
|
81
|
+
|
|
55
82
|
return (
|
|
56
83
|
<AuthProvider>
|
|
57
84
|
<HistoryRouter history={history}>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { default as ErrorMessage, ErrorMessageProps } from './ErrorMessage';
|
|
3
|
+
|
|
4
|
+
describe('ErrorMessage Component', () => {
|
|
5
|
+
const message: string = 'Test Error Message';
|
|
6
|
+
|
|
7
|
+
test('renders the message as a div by default', () => {
|
|
8
|
+
render(<ErrorMessage message={message} />);
|
|
9
|
+
const displayedMessage: HTMLElement = screen.getByText(message);
|
|
10
|
+
expect(displayedMessage.tagName).toBe('DIV');
|
|
11
|
+
expect(displayedMessage).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('renders the message as an h1 element when elementType is "h1"', () => {
|
|
15
|
+
render(<ErrorMessage message={message} elementType="h1" />);
|
|
16
|
+
const displayedMessage: HTMLElement = screen.getByRole('heading', { level: 1 });
|
|
17
|
+
expect(displayedMessage.tagName).toBe('H1');
|
|
18
|
+
expect(displayedMessage).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('renders the message as a p element when elementType is "p"', () => {
|
|
22
|
+
render(<ErrorMessage message={message} elementType="p" />);
|
|
23
|
+
const displayedMessage: HTMLElement = screen.getByText(message);
|
|
24
|
+
expect(displayedMessage.tagName).toBe('P');
|
|
25
|
+
expect(displayedMessage).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('applies the correct styles to the error message wrapper', () => {
|
|
29
|
+
render(<ErrorMessage message={message} />);
|
|
30
|
+
const wrapper: HTMLElement | null = screen.getByText(message).closest('section');
|
|
31
|
+
expect(wrapper).toHaveStyle(`
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
align-items: center;
|
|
36
|
+
height: 100%;
|
|
37
|
+
width: 100%;
|
|
38
|
+
`);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('applies the correct styles to the error message itself', () => {
|
|
42
|
+
render(<ErrorMessage message={message} />);
|
|
43
|
+
const displayedMessage: HTMLElement = screen.getByText(message);
|
|
44
|
+
const messageWrapper: HTMLElement | null = displayedMessage.parentElement;
|
|
45
|
+
|
|
46
|
+
expect(messageWrapper).toBeInTheDocument();
|
|
47
|
+
expect(messageWrapper).toHaveStyle(`
|
|
48
|
+
color: var(--neutral-foreground-rest);
|
|
49
|
+
background-color: var(--neutral-layer-4);
|
|
50
|
+
border-color: var(--error-color);
|
|
51
|
+
border-radius: 7px;
|
|
52
|
+
border-style: solid;
|
|
53
|
+
border-width: 4px;
|
|
54
|
+
padding: 5px;
|
|
55
|
+
margin: 15px;
|
|
56
|
+
text-align: center;
|
|
57
|
+
width: fit-content;
|
|
58
|
+
align-self: center;
|
|
59
|
+
height: auto;
|
|
60
|
+
max-height: 100%;
|
|
61
|
+
`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('renders nothing if the message is an empty string', () => {
|
|
65
|
+
const { container }: { container: HTMLElement } = render(<ErrorMessage message="" />);
|
|
66
|
+
expect(container.firstChild).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('renders the correct element type for various elementType props', () => {
|
|
70
|
+
const elementTypes: ErrorMessageProps['elementType'][] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div'];
|
|
71
|
+
elementTypes.forEach((elementType: ErrorMessageProps['elementType']) => {
|
|
72
|
+
const { container }: { container: HTMLElement } = render(<ErrorMessage message={message} elementType={elementType} />);
|
|
73
|
+
const displayedMessage: HTMLElement | null = container.querySelector(elementType as string);
|
|
74
|
+
expect(displayedMessage).toBeInTheDocument();
|
|
75
|
+
expect(displayedMessage?.tagName).toBe((elementType as string).toUpperCase());
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('renders the message as a div element when elementType is unknown', () => {
|
|
80
|
+
render(<ErrorMessage message={message} />);
|
|
81
|
+
const displayedMessage: HTMLElement = screen.getByText(message);
|
|
82
|
+
expect(displayedMessage.tagName).toBe('DIV');
|
|
83
|
+
expect(displayedMessage).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('renders nothing if the message is null', () => {
|
|
87
|
+
const { container }: { container: HTMLElement } = render(<ErrorMessage />);
|
|
88
|
+
expect(container.firstChild).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('renders nothing if the message is undefined', () => {
|
|
92
|
+
const { container }: { container: HTMLElement } = render(<ErrorMessage message={undefined} />);
|
|
93
|
+
expect(container.firstChild).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
const styles = {
|
|
4
|
+
errorMessageWrapper: {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
flexDirection: 'column' as const,
|
|
7
|
+
justifyContent: 'center',
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
height: '100%',
|
|
10
|
+
width: '100%',
|
|
11
|
+
},
|
|
12
|
+
errorMessage: {
|
|
13
|
+
color: 'var(--neutral-foreground-rest)',
|
|
14
|
+
backgroundColor: 'var(--neutral-layer-4)',
|
|
15
|
+
borderColor: 'var(--error-color)',
|
|
16
|
+
borderRadius: '7px',
|
|
17
|
+
borderStyle: 'solid' as const,
|
|
18
|
+
borderWidth: '4px',
|
|
19
|
+
padding: '5px',
|
|
20
|
+
margin: '15px',
|
|
21
|
+
textAlign: 'center' as const,
|
|
22
|
+
width: 'fit-content',
|
|
23
|
+
alignSelf: 'center',
|
|
24
|
+
height: 'auto',
|
|
25
|
+
maxHeight: '100%'
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface ErrorMessageProps {
|
|
30
|
+
elementType?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';
|
|
31
|
+
message?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ErrorMessage: React.FC<ErrorMessageProps> = ({ elementType = 'div', message = '' }) => {
|
|
35
|
+
const ElementType = elementType;
|
|
36
|
+
|
|
37
|
+
return message && message !== '' && (
|
|
38
|
+
<section style={styles.errorMessageWrapper}>
|
|
39
|
+
<div style={styles.errorMessage}>
|
|
40
|
+
{(() => {
|
|
41
|
+
switch (ElementType) {
|
|
42
|
+
case 'h1':
|
|
43
|
+
return <h1>{message}</h1>;
|
|
44
|
+
case 'h2':
|
|
45
|
+
return <h2>{message}</h2>;
|
|
46
|
+
case 'h3':
|
|
47
|
+
return <h3>{message}</h3>;
|
|
48
|
+
case 'h4':
|
|
49
|
+
return <h4>{message}</h4>;
|
|
50
|
+
case 'h5':
|
|
51
|
+
return <h5>{message}</h5>;
|
|
52
|
+
case 'h6':
|
|
53
|
+
return <h6>{message}</h6>;
|
|
54
|
+
case 'p':
|
|
55
|
+
return <p>{message}</p>;
|
|
56
|
+
case 'span':
|
|
57
|
+
return <span>{message}</span>;
|
|
58
|
+
default:
|
|
59
|
+
return <div>{message}</div>;
|
|
60
|
+
}
|
|
61
|
+
})()}
|
|
62
|
+
</div>
|
|
63
|
+
</section>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default ErrorMessage;
|
|
@@ -9,9 +9,10 @@ export const routeLayouts: RouteLayouts = {
|
|
|
9
9
|
import type { MainMenu } from './types/menu';
|
|
10
10
|
|
|
11
11
|
export const AUTH_PATH = 'auth';
|
|
12
|
+
export const NOT_PERMITTED_PATH = 'not-permitted';
|
|
12
13
|
|
|
13
14
|
export const API_DATA = {
|
|
14
|
-
URL:
|
|
15
|
+
URL: import.meta.env.VITE_API_HOST,
|
|
15
16
|
AUTH: {
|
|
16
17
|
username: '', // provide login to a user in given environment
|
|
17
18
|
password: '', // provide password to a user in given environment
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
interface IntrinsicElements {
|
|
3
|
-
// Wildcard for all webcomponents:
|
|
4
|
-
[elemName: string]: any;
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
import React, { useState, DOMAttributes } from 'react';
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
type CustomElement<T = HTMLElement> = Partial<T & DOMAttributes<T> & { children: any }>;
|
|
4
|
+
|
|
5
|
+
declare module "react/jsx-runtime" {
|
|
6
|
+
namespace JSX {
|
|
7
|
+
interface IntrinsicElements {
|
|
8
|
+
'entity-management': CustomElement;
|
|
9
|
+
'foundation-form': CustomElement;
|
|
10
|
+
'rapid-grid-pro': CustomElement;
|
|
11
|
+
'grid-pro-genesis-datasource': CustomElement;
|
|
12
|
+
'grid-pro-column': CustomElement;
|
|
13
|
+
'rapid-g2plot-chart': CustomElement;
|
|
14
|
+
'chart-datasource': CustomElement;
|
|
15
|
+
'client-app-login': CustomElement;
|
|
16
|
+
'rapid-layout': CustomElement;
|
|
17
|
+
'rapid-layout-region': CustomElement;
|
|
18
|
+
'rapid-layout-item': CustomElement;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
11
21
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useEffect, useState, ReactNode } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import { getUser } from '@genesislcap/foundation-user';
|
|
4
|
+
import { NOT_PERMITTED_PATH } from '../config';
|
|
5
|
+
|
|
6
|
+
interface PermissionsGuardProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
permissionCode?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const PermissionsGuard: React.FC<PermissionsGuardProps> = ({ children, permissionCode }: PermissionsGuardProps) => {
|
|
12
|
+
const navigate = useNavigate();
|
|
13
|
+
const [isAuthorized, setIsAuthorized] = useState(false);
|
|
14
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const checkPermission = async () => {
|
|
18
|
+
const user = getUser();
|
|
19
|
+
if (permissionCode && !user.hasPermission(permissionCode)) {
|
|
20
|
+
navigate(`/${NOT_PERMITTED_PATH}`);
|
|
21
|
+
} else {
|
|
22
|
+
setIsAuthorized(true);
|
|
23
|
+
}
|
|
24
|
+
setIsLoading(false);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
checkPermission();
|
|
28
|
+
}, [navigate, permissionCode]);
|
|
29
|
+
|
|
30
|
+
if (isLoading) {
|
|
31
|
+
return <div>Loading...</div>; // Or some loading component
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return isAuthorized ? <>{children}</> : null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default PermissionsGuard;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom/client';
|
|
3
|
+
import './styles/styles.css';
|
|
4
|
+
import App from './App';
|
|
5
|
+
import reportWebVitals from './reportWebVitals';
|
|
6
|
+
|
|
7
|
+
const rootElement = document.getElementById('root');
|
|
8
|
+
|
|
9
|
+
if (rootElement) {
|
|
10
|
+
const root = ReactDOM.createRoot(rootElement);
|
|
11
|
+
root.render(
|
|
12
|
+
<React.StrictMode>
|
|
13
|
+
<App />
|
|
14
|
+
</React.StrictMode>,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// If you want to start measuring performance in your app, pass a function
|
|
18
|
+
// to log results (for example: reportWebVitals(console.log))
|
|
19
|
+
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
20
|
+
reportWebVitals();
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import AuthPage from './AuthPage';
|
|
3
|
+
|
|
4
|
+
test('renders AuthPage component', () => {
|
|
5
|
+
const { container } = render(<AuthPage />);
|
|
6
|
+
const authPageElement: Element | null = container.querySelector('.auth-page');
|
|
7
|
+
expect(authPageElement).toBeInTheDocument();
|
|
8
|
+
const clientAppLoginElement: Element | null = container.querySelector('client-app-login');
|
|
9
|
+
expect(clientAppLoginElement).toBeInTheDocument();
|
|
10
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import './AuthPage.css';
|
|
2
3
|
|
|
3
|
-
const AuthPage = () => {
|
|
4
|
+
const AuthPage: React.FC = () => {
|
|
4
5
|
return (
|
|
5
6
|
<section className="auth-page">
|
|
6
7
|
<client-app-login></client-app-login>
|
|
@@ -8,4 +9,4 @@ const AuthPage = () => {
|
|
|
8
9
|
);
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
export default AuthPage;
|
|
12
|
+
export default AuthPage;
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import NotPermittedPage from './NotPermittedPage';
|
|
3
|
+
|
|
4
|
+
jest.mock('../../components/ErrorMessage/ErrorMessage', () => {
|
|
5
|
+
return jest.fn((props: { message: string }) => <h1 data-testid="error-message">{props.message}</h1>);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
describe('NotPermittedPage Component', () => {
|
|
9
|
+
test('renders the ErrorMessage component with correct props', () => {
|
|
10
|
+
render(<NotPermittedPage />);
|
|
11
|
+
|
|
12
|
+
const errorMessage: HTMLElement = screen.getByTestId('error-message');
|
|
13
|
+
expect(errorMessage).toBeInTheDocument();
|
|
14
|
+
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.');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ErrorMessage from '../../components/ErrorMessage/ErrorMessage';
|
|
3
|
+
import './NotPermittedPage.css';
|
|
4
|
+
|
|
5
|
+
const NotPermittedPage: React.FC = () => {
|
|
6
|
+
return (
|
|
7
|
+
<ErrorMessage
|
|
8
|
+
elementType="h1"
|
|
9
|
+
message="You do not have permission to access this part of the application, please contact your administrator."
|
|
10
|
+
/>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default NotPermittedPage;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {configure, define} from '@genesislcap/foundation-login';
|
|
2
2
|
import { AUTH_PATH } from '../config';
|
|
3
3
|
import { DI } from '@microsoft/fast-foundation';
|
|
4
|
-
import history from '../utils/history';
|
|
4
|
+
import { history } from '../utils/history';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Configure the micro frontend
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{{#if FDC3.includeDependencies}}
|
|
2
|
+
import { DefaultFDC3 } from '@genesislcap/foundation-fdc3';
|
|
3
|
+
{{/if}}
|
|
4
|
+
export const isFDC3 = (): boolean => !!((window as unknown as { fdc3?: boolean }).fdc3);
|
|
5
|
+
{{#if FDC3.includeDependencies}}
|
|
6
|
+
|
|
7
|
+
export const onFDC3Ready = async (FDC3ReadyCb: () => any): Promise<void> => {
|
|
8
|
+
isFDC3()
|
|
9
|
+
? await FDC3ReadyCb()
|
|
10
|
+
: window.addEventListener('fdc3Ready', async () => {
|
|
11
|
+
await FDC3ReadyCb();
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const listenToChannel = async (
|
|
16
|
+
channelName: string,
|
|
17
|
+
type: string,
|
|
18
|
+
callback: (result: any) => void,
|
|
19
|
+
): Promise<void> => {
|
|
20
|
+
const fdc3Service = new DefaultFDC3();
|
|
21
|
+
fdc3Service.addChannelListener(channelName, type, callback);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const sendEventOnChannel = (channelName: string, type: string) => {
|
|
25
|
+
return async (e: any) => {
|
|
26
|
+
const fdc3Service = new DefaultFDC3();
|
|
27
|
+
// check for ag-grid-specific events, fall back to standard events
|
|
28
|
+
const payload = e.data || e.detail;
|
|
29
|
+
await fdc3Service.broadcastOnChannel(channelName, type, payload);
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
{{/if}}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { base, Page } from '@genesislcap/foundation-testing/e2e';
|
|
2
|
+
import { ProtectedPage } from './pages';
|
|
3
|
+
import { PORT } from '../../playwright.config';
|
|
4
|
+
|
|
5
|
+
export type FixtureConfig = {
|
|
6
|
+
API_HOST: string;
|
|
7
|
+
DEFAULT_USER: string;
|
|
8
|
+
DEFAULT_PASSWORD: string;
|
|
9
|
+
PORT: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Fixture = {
|
|
13
|
+
config: FixtureConfig;
|
|
14
|
+
protectedPage: ProtectedPage;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const API_HOST = process.env['API_HOST'] || 'localhost';
|
|
18
|
+
|
|
19
|
+
export const test = base.extend<Fixture>({
|
|
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>) => {
|
|
22
|
+
const protectedPage = new ProtectedPage(config, page);
|
|
23
|
+
await protectedPage.goto();
|
|
24
|
+
await use(protectedPage);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './protected';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Page } from '@genesislcap/foundation-testing/e2e';
|
|
2
|
+
import type { FixtureConfig } from '../fixture';
|
|
3
|
+
|
|
4
|
+
export class ProtectedPage {
|
|
5
|
+
config: FixtureConfig;
|
|
6
|
+
page: Page;
|
|
7
|
+
|
|
8
|
+
constructor(config: FixtureConfig, page: Page) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.page = page;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async goto() {
|
|
14
|
+
await this.page.goto('/');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
8
8
|
"module": "ESNext",
|
|
9
9
|
"skipLibCheck": true,
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
10
|
"moduleResolution": "bundler",
|
|
13
11
|
"allowImportingTsExtensions": true,
|
|
14
12
|
"resolveJsonModule": true,
|
|
@@ -16,13 +14,12 @@
|
|
|
16
14
|
"moduleDetection": "force",
|
|
17
15
|
"noEmit": true,
|
|
18
16
|
"jsx": "react-jsx",
|
|
19
|
-
|
|
20
|
-
/* Linting */
|
|
21
17
|
"strict": true,
|
|
18
|
+
"esModuleInterop": true,
|
|
22
19
|
"noUnusedLocals": true,
|
|
23
20
|
"noUnusedParameters": true,
|
|
24
21
|
"noFallthroughCasesInSwitch": true,
|
|
25
22
|
"types": ["node", "react"]
|
|
26
23
|
},
|
|
27
|
-
"include": ["src"]
|
|
24
|
+
"include": ["src" , "src/**/*.d.ts"],
|
|
28
25
|
}
|