datajunction-ui 0.0.1-rc.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.
- package/.env +1 -0
- package/.env.local +4 -0
- package/.env.production +1 -0
- package/.eslintrc.js +20 -0
- package/.gitattributes +201 -0
- package/.github/pull_request_template.md +11 -0
- package/.github/workflows/ci.yml +33 -0
- package/.husky/pre-commit +6 -0
- package/.nvmrc +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +9 -0
- package/.stylelintrc +7 -0
- package/.vscode/extensions.json +8 -0
- package/.vscode/launch.json +15 -0
- package/.vscode/settings.json +25 -0
- package/Dockerfile +6 -0
- package/LICENSE +22 -0
- package/README.md +10 -0
- package/internals/testing/loadable.mock.tsx +6 -0
- package/package.json +150 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +29 -0
- package/public/manifest.json +15 -0
- package/public/robots.txt +3 -0
- package/src/app/__tests__/__snapshots__/index.test.tsx.snap +45 -0
- package/src/app/__tests__/index.test.tsx +14 -0
- package/src/app/components/ListGroupItem.jsx +17 -0
- package/src/app/components/NamespaceHeader.jsx +40 -0
- package/src/app/components/Tab.jsx +26 -0
- package/src/app/components/__tests__/ListGroupItem.test.tsx +16 -0
- package/src/app/components/__tests__/NamespaceHeader.test.jsx +14 -0
- package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +26 -0
- package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +63 -0
- package/src/app/components/djgraph/DJNode.jsx +111 -0
- package/src/app/components/djgraph/__tests__/DJNode.test.tsx +24 -0
- package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +73 -0
- package/src/app/index.tsx +53 -0
- package/src/app/pages/ListNamespacesPage/Loadable.jsx +23 -0
- package/src/app/pages/ListNamespacesPage/index.jsx +53 -0
- package/src/app/pages/NamespacePage/Loadable.jsx +23 -0
- package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +45 -0
- package/src/app/pages/NamespacePage/__tests__/index.test.tsx +14 -0
- package/src/app/pages/NamespacePage/index.jsx +93 -0
- package/src/app/pages/NodePage/Loadable.jsx +23 -0
- package/src/app/pages/NodePage/NodeColumnTab.jsx +44 -0
- package/src/app/pages/NodePage/NodeGraphTab.jsx +160 -0
- package/src/app/pages/NodePage/NodeInfoTab.jsx +87 -0
- package/src/app/pages/NodePage/NodeStatus.jsx +34 -0
- package/src/app/pages/NodePage/index.jsx +100 -0
- package/src/app/pages/NotFoundPage/Loadable.tsx +14 -0
- package/src/app/pages/NotFoundPage/P.ts +8 -0
- package/src/app/pages/NotFoundPage/__tests__/__snapshots__/index.test.tsx.snap +61 -0
- package/src/app/pages/NotFoundPage/__tests__/index.test.tsx +21 -0
- package/src/app/pages/NotFoundPage/index.tsx +45 -0
- package/src/app/pages/Root/Loadable.tsx +23 -0
- package/src/app/pages/Root/assets/dj-logo.png +0 -0
- package/src/app/pages/Root/index.tsx +42 -0
- package/src/app/services/DJService.js +124 -0
- package/src/index.tsx +47 -0
- package/src/react-app-env.d.ts +4 -0
- package/src/reportWebVitals.ts +15 -0
- package/src/setupTests.ts +8 -0
- package/src/styles/dag-styles.ts +117 -0
- package/src/styles/global-styles.ts +588 -0
- package/src/styles/index.css +546 -0
- package/src/utils/__tests__/__snapshots__/loadable.test.tsx.snap +17 -0
- package/src/utils/__tests__/loadable.test.tsx +53 -0
- package/src/utils/__tests__/request.test.ts +82 -0
- package/src/utils/loadable.tsx +30 -0
- package/src/utils/request.ts +54 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
|
|
3
|
+
export default class ListGroupItem extends Component {
|
|
4
|
+
render() {
|
|
5
|
+
const { label, value } = this.props;
|
|
6
|
+
return (
|
|
7
|
+
<div className="list-group-item d-flex">
|
|
8
|
+
<div className="d-flex gap-2 w-100 justify-content-between py-3">
|
|
9
|
+
<div>
|
|
10
|
+
<h6 className="mb-0 w-100">{label}</h6>
|
|
11
|
+
<p className="mb-0 opacity-75">{value}</p>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
|
|
3
|
+
export default class NamespaceHeader extends Component {
|
|
4
|
+
render() {
|
|
5
|
+
const { namespace } = this.props;
|
|
6
|
+
const namespaceParts = namespace.split('.');
|
|
7
|
+
const namespaceList = namespaceParts.map((piece, index) => {
|
|
8
|
+
return (
|
|
9
|
+
<li className="breadcrumb-item">
|
|
10
|
+
<a
|
|
11
|
+
className="link-body-emphasis"
|
|
12
|
+
href={'/namespaces/' + namespaceParts.slice(0, index + 1).join('.')}
|
|
13
|
+
>
|
|
14
|
+
{piece}
|
|
15
|
+
</a>
|
|
16
|
+
</li>
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
return (
|
|
20
|
+
<ol className="breadcrumb breadcrumb-chevron p-3 bg-body-tertiary rounded-3">
|
|
21
|
+
<li className="breadcrumb-item">
|
|
22
|
+
<a href="/namespaces/">
|
|
23
|
+
<svg
|
|
24
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
25
|
+
width="16"
|
|
26
|
+
height="16"
|
|
27
|
+
fill="currentColor"
|
|
28
|
+
className="bi bi-house-door-fill"
|
|
29
|
+
viewBox="0 0 16 16"
|
|
30
|
+
style={{ paddingBottom: '0.2rem' }}
|
|
31
|
+
>
|
|
32
|
+
<path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z" />
|
|
33
|
+
</svg>
|
|
34
|
+
</a>
|
|
35
|
+
</li>
|
|
36
|
+
{namespaceList}
|
|
37
|
+
</ol>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
|
|
3
|
+
export default class Tab extends Component {
|
|
4
|
+
render() {
|
|
5
|
+
const { id, onClick, selectedTab } = this.props;
|
|
6
|
+
return (
|
|
7
|
+
<div className={selectedTab === id ? 'col active' : 'col'}>
|
|
8
|
+
<div className="header-tabs nav-overflow nav nav-tabs">
|
|
9
|
+
<div className="nav-item">
|
|
10
|
+
<a
|
|
11
|
+
id={id}
|
|
12
|
+
role="button"
|
|
13
|
+
className="nav-link"
|
|
14
|
+
tabIndex="0"
|
|
15
|
+
href="#/"
|
|
16
|
+
onClick={onClick}
|
|
17
|
+
>
|
|
18
|
+
{this.props.name}
|
|
19
|
+
{/*<span className="rounded-pill badge bg-secondary-soft">823</span>*/}
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createRenderer } from 'react-test-renderer/shallow';
|
|
3
|
+
|
|
4
|
+
import ListGroupItem from '../ListGroupItem';
|
|
5
|
+
|
|
6
|
+
const renderer = createRenderer();
|
|
7
|
+
|
|
8
|
+
describe('<ListGroupItem />', () => {
|
|
9
|
+
it('should render and match the snapshot', () => {
|
|
10
|
+
renderer.render(
|
|
11
|
+
<ListGroupItem label="Name" value={<span>Something</span>} />,
|
|
12
|
+
);
|
|
13
|
+
const renderedOutput = renderer.getRenderOutput();
|
|
14
|
+
expect(renderedOutput).toMatchSnapshot();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createRenderer } from 'react-test-renderer/shallow';
|
|
3
|
+
|
|
4
|
+
import NamespaceHeader from '../NamespaceHeader';
|
|
5
|
+
|
|
6
|
+
const renderer = createRenderer();
|
|
7
|
+
|
|
8
|
+
describe('<NamespaceHeader />', () => {
|
|
9
|
+
it('should render and match the snapshot', () => {
|
|
10
|
+
renderer.render(<NamespaceHeader namespace="shared.dimensions.accounts" />);
|
|
11
|
+
const renderedOutput = renderer.getRenderOutput();
|
|
12
|
+
expect(renderedOutput).toMatchSnapshot();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<ListGroupItem /> should render and match the snapshot 1`] = `
|
|
4
|
+
<div
|
|
5
|
+
className="list-group-item d-flex"
|
|
6
|
+
>
|
|
7
|
+
<div
|
|
8
|
+
className="d-flex gap-2 w-100 justify-content-between py-3"
|
|
9
|
+
>
|
|
10
|
+
<div>
|
|
11
|
+
<h6
|
|
12
|
+
className="mb-0 w-100"
|
|
13
|
+
>
|
|
14
|
+
Name
|
|
15
|
+
</h6>
|
|
16
|
+
<p
|
|
17
|
+
className="mb-0 opacity-75"
|
|
18
|
+
>
|
|
19
|
+
<span>
|
|
20
|
+
Something
|
|
21
|
+
</span>
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
`;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<NamespaceHeader /> should render and match the snapshot 1`] = `
|
|
4
|
+
<ol
|
|
5
|
+
className="breadcrumb breadcrumb-chevron p-3 bg-body-tertiary rounded-3"
|
|
6
|
+
>
|
|
7
|
+
<li
|
|
8
|
+
className="breadcrumb-item"
|
|
9
|
+
>
|
|
10
|
+
<a
|
|
11
|
+
href="/namespaces/"
|
|
12
|
+
>
|
|
13
|
+
<svg
|
|
14
|
+
className="bi bi-house-door-fill"
|
|
15
|
+
fill="currentColor"
|
|
16
|
+
height="16"
|
|
17
|
+
style={
|
|
18
|
+
Object {
|
|
19
|
+
"paddingBottom": "0.2rem",
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
viewBox="0 0 16 16"
|
|
23
|
+
width="16"
|
|
24
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
25
|
+
>
|
|
26
|
+
<path
|
|
27
|
+
d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z"
|
|
28
|
+
/>
|
|
29
|
+
</svg>
|
|
30
|
+
</a>
|
|
31
|
+
</li>
|
|
32
|
+
<li
|
|
33
|
+
className="breadcrumb-item"
|
|
34
|
+
>
|
|
35
|
+
<a
|
|
36
|
+
className="link-body-emphasis"
|
|
37
|
+
href="/namespaces/shared"
|
|
38
|
+
>
|
|
39
|
+
shared
|
|
40
|
+
</a>
|
|
41
|
+
</li>
|
|
42
|
+
<li
|
|
43
|
+
className="breadcrumb-item"
|
|
44
|
+
>
|
|
45
|
+
<a
|
|
46
|
+
className="link-body-emphasis"
|
|
47
|
+
href="/namespaces/shared.dimensions"
|
|
48
|
+
>
|
|
49
|
+
dimensions
|
|
50
|
+
</a>
|
|
51
|
+
</li>
|
|
52
|
+
<li
|
|
53
|
+
className="breadcrumb-item"
|
|
54
|
+
>
|
|
55
|
+
<a
|
|
56
|
+
className="link-body-emphasis"
|
|
57
|
+
href="/namespaces/shared.dimensions.accounts"
|
|
58
|
+
>
|
|
59
|
+
accounts
|
|
60
|
+
</a>
|
|
61
|
+
</li>
|
|
62
|
+
</ol>
|
|
63
|
+
`;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { Handle, Position } from 'reactflow';
|
|
3
|
+
|
|
4
|
+
function renderBasedOnDJNodeType(param) {
|
|
5
|
+
switch (param) {
|
|
6
|
+
case 'source':
|
|
7
|
+
return { backgroundColor: '#7EB46150', color: '#7EB461' };
|
|
8
|
+
case 'transform':
|
|
9
|
+
return { backgroundColor: '#6DAAA750', color: '#6DAAA7' };
|
|
10
|
+
case 'dimension':
|
|
11
|
+
return { backgroundColor: '#CF7D2950', color: '#CF7D29' };
|
|
12
|
+
case 'metric':
|
|
13
|
+
return { backgroundColor: '#A27E8650', color: '#A27E86' };
|
|
14
|
+
case 'cube':
|
|
15
|
+
return { backgroundColor: '#C2180750', color: '#C21807' };
|
|
16
|
+
default:
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function capitalize(string) {
|
|
22
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const Collapse = ({ collapsed, text, children }) => {
|
|
26
|
+
const [isCollapsed, setIsCollapsed] = React.useState(collapsed);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
<div className="collapse">
|
|
31
|
+
<button
|
|
32
|
+
className="collapse-button"
|
|
33
|
+
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
34
|
+
>
|
|
35
|
+
{isCollapsed ? '\u25B6 Show' : '\u25BC Hide'} {text}
|
|
36
|
+
</button>
|
|
37
|
+
<div
|
|
38
|
+
className={`collapse-content ${
|
|
39
|
+
isCollapsed ? 'collapsed' : 'expanded'
|
|
40
|
+
}`}
|
|
41
|
+
aria-expanded={isCollapsed}
|
|
42
|
+
>
|
|
43
|
+
{children}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export function DJNode({ id, data }) {
|
|
51
|
+
const columnsRenderer = data =>
|
|
52
|
+
data.column_names.map(col => (
|
|
53
|
+
<tr>
|
|
54
|
+
<td>
|
|
55
|
+
{data.primary_key.includes(col.name) ? (
|
|
56
|
+
<b>{col.name} (PK)</b>
|
|
57
|
+
) : (
|
|
58
|
+
<>{col.name}</>
|
|
59
|
+
)}
|
|
60
|
+
</td>
|
|
61
|
+
<td style={{ textAlign: 'right' }}>{col.type}</td>
|
|
62
|
+
</tr>
|
|
63
|
+
));
|
|
64
|
+
// const dimensionsRenderer = data =>
|
|
65
|
+
// data.dimensions.map(dim => (
|
|
66
|
+
// <tr>
|
|
67
|
+
// <td>{dim}</td>
|
|
68
|
+
// </tr>
|
|
69
|
+
// ));
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<Handle
|
|
74
|
+
type="target"
|
|
75
|
+
position={Position.Left}
|
|
76
|
+
style={{ backgroundColor: '#ccc' }}
|
|
77
|
+
/>
|
|
78
|
+
<div className="dj-node__full" style={renderBasedOnDJNodeType(data.type)}>
|
|
79
|
+
<div className="dj-node__header">
|
|
80
|
+
<div className="serif">
|
|
81
|
+
{data.name
|
|
82
|
+
.split('.')
|
|
83
|
+
.slice(0, data.name.split('.').length - 1)
|
|
84
|
+
.join(' \u25B6 ')}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div className="dj-node__body">
|
|
88
|
+
<b>{capitalize(data.type)}</b>:{' '}
|
|
89
|
+
{data.type === 'source' ? data.table : data.display_name}
|
|
90
|
+
<Collapse
|
|
91
|
+
collapsed={true}
|
|
92
|
+
text={data.type !== 'metric' ? 'columns' : 'dimensions'}
|
|
93
|
+
>
|
|
94
|
+
<div className="dj-node__metadata">
|
|
95
|
+
{
|
|
96
|
+
data.type !== 'metric' ? columnsRenderer(data) : '' // dimensionsRenderer(data)
|
|
97
|
+
}
|
|
98
|
+
</div>
|
|
99
|
+
</Collapse>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<Handle
|
|
103
|
+
type="source"
|
|
104
|
+
position={Position.Right}
|
|
105
|
+
style={{ backgroundColor: '#ccc' }}
|
|
106
|
+
/>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default memo(DJNode);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createRenderer } from 'react-test-renderer/shallow';
|
|
3
|
+
|
|
4
|
+
import { DJNode } from '../DJNode';
|
|
5
|
+
|
|
6
|
+
const renderer = createRenderer();
|
|
7
|
+
|
|
8
|
+
describe('<DJNode />', () => {
|
|
9
|
+
it('should render and match the snapshot', () => {
|
|
10
|
+
renderer.render(
|
|
11
|
+
<DJNode
|
|
12
|
+
id="1"
|
|
13
|
+
data={{
|
|
14
|
+
name: 'shared.dimensions.accounts',
|
|
15
|
+
column_names: ['a'],
|
|
16
|
+
type: 'source',
|
|
17
|
+
primary_key: ['id'],
|
|
18
|
+
}}
|
|
19
|
+
/>,
|
|
20
|
+
);
|
|
21
|
+
const renderedOutput = renderer.getRenderOutput();
|
|
22
|
+
expect(renderedOutput).toMatchSnapshot();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<DJNode /> should render and match the snapshot 1`] = `
|
|
4
|
+
<React.Fragment>
|
|
5
|
+
<Memo(Handle)
|
|
6
|
+
position="left"
|
|
7
|
+
style={
|
|
8
|
+
Object {
|
|
9
|
+
"backgroundColor": "#ccc",
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
type="target"
|
|
13
|
+
/>
|
|
14
|
+
<div
|
|
15
|
+
className="dj-node__full"
|
|
16
|
+
style={
|
|
17
|
+
Object {
|
|
18
|
+
"backgroundColor": "#7EB46150",
|
|
19
|
+
"color": "#7EB461",
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
className="dj-node__header"
|
|
25
|
+
>
|
|
26
|
+
<div
|
|
27
|
+
className="serif"
|
|
28
|
+
>
|
|
29
|
+
shared ▶ dimensions
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div
|
|
33
|
+
className="dj-node__body"
|
|
34
|
+
>
|
|
35
|
+
<b>
|
|
36
|
+
Source
|
|
37
|
+
</b>
|
|
38
|
+
:
|
|
39
|
+
|
|
40
|
+
<Collapse
|
|
41
|
+
collapsed={true}
|
|
42
|
+
text="columns"
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
className="dj-node__metadata"
|
|
46
|
+
>
|
|
47
|
+
<tr>
|
|
48
|
+
<td>
|
|
49
|
+
<React.Fragment />
|
|
50
|
+
</td>
|
|
51
|
+
<td
|
|
52
|
+
style={
|
|
53
|
+
Object {
|
|
54
|
+
"textAlign": "right",
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/>
|
|
58
|
+
</tr>
|
|
59
|
+
</div>
|
|
60
|
+
</Collapse>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<Memo(Handle)
|
|
64
|
+
position="right"
|
|
65
|
+
style={
|
|
66
|
+
Object {
|
|
67
|
+
"backgroundColor": "#ccc",
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
type="source"
|
|
71
|
+
/>
|
|
72
|
+
</React.Fragment>
|
|
73
|
+
`;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This component is the skeleton around the actual pages, and only contains
|
|
3
|
+
* components that should be seen on all pages, like the logo or navigation bar.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import { Helmet } from 'react-helmet-async';
|
|
8
|
+
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
|
9
|
+
|
|
10
|
+
import { GlobalStyle } from 'styles/global-styles';
|
|
11
|
+
import { DAGStyle } from '../styles/dag-styles';
|
|
12
|
+
|
|
13
|
+
import { NamespacePage } from './pages/NamespacePage/Loadable';
|
|
14
|
+
import { NodePage } from './pages/NodePage/Loadable';
|
|
15
|
+
import { NotFoundPage } from './pages/NotFoundPage/Loadable';
|
|
16
|
+
import { Root } from './pages/Root/Loadable';
|
|
17
|
+
import { ListNamespacesPage } from './pages/ListNamespacesPage';
|
|
18
|
+
|
|
19
|
+
export function App() {
|
|
20
|
+
return (
|
|
21
|
+
<BrowserRouter>
|
|
22
|
+
<Helmet
|
|
23
|
+
titleTemplate="DataJunction: %s"
|
|
24
|
+
defaultTitle="DataJunction: A Metrics Platform"
|
|
25
|
+
>
|
|
26
|
+
<meta
|
|
27
|
+
name="description"
|
|
28
|
+
content="DataJunction serves as a semantic layer to help manage metrics"
|
|
29
|
+
/>
|
|
30
|
+
</Helmet>
|
|
31
|
+
|
|
32
|
+
<Routes>
|
|
33
|
+
<Route
|
|
34
|
+
path="/"
|
|
35
|
+
element={<Root />}
|
|
36
|
+
children={
|
|
37
|
+
<>
|
|
38
|
+
<Route path="/nodes/:name" element={<NodePage />} />
|
|
39
|
+
<Route
|
|
40
|
+
path="/namespaces/:namespace"
|
|
41
|
+
element={<NamespacePage />}
|
|
42
|
+
/>
|
|
43
|
+
<Route path="/namespaces/" element={<ListNamespacesPage />} />
|
|
44
|
+
</>
|
|
45
|
+
}
|
|
46
|
+
/>
|
|
47
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
48
|
+
</Routes>
|
|
49
|
+
<GlobalStyle />
|
|
50
|
+
<DAGStyle />
|
|
51
|
+
</BrowserRouter>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asynchronously loads the component for namespaces node-viewing page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { lazyLoad } from 'utils/loadable';
|
|
7
|
+
import styled from 'styled-components/macro';
|
|
8
|
+
|
|
9
|
+
const LoadingWrapper = styled.div`
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100vh;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
export const ListNamespacesPage = lazyLoad(
|
|
18
|
+
() => import('./index'),
|
|
19
|
+
module => module.ListNamespacesPage,
|
|
20
|
+
{
|
|
21
|
+
fallback: <LoadingWrapper></LoadingWrapper>,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import NamespaceHeader from '../../components/NamespaceHeader';
|
|
4
|
+
const datajunction = require('datajunction');
|
|
5
|
+
const dj = new datajunction.DJClient('http://localhost:8000');
|
|
6
|
+
|
|
7
|
+
export function ListNamespacesPage() {
|
|
8
|
+
const [state, setState] = useState({
|
|
9
|
+
namespaces: [],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const fetchData = async () => {
|
|
14
|
+
const namespaces = await dj.namespaces.get();
|
|
15
|
+
setState({
|
|
16
|
+
namespaces: namespaces,
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
fetchData().catch(console.error);
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
const namespacesList = state.namespaces.map(node => (
|
|
23
|
+
<tr>
|
|
24
|
+
<td>
|
|
25
|
+
<a href={'/namespaces/' + node.namespace}>{node.namespace}</a>
|
|
26
|
+
</td>
|
|
27
|
+
<td>5</td>
|
|
28
|
+
</tr>
|
|
29
|
+
));
|
|
30
|
+
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<div className="mid">
|
|
35
|
+
<NamespaceHeader namespace="" />
|
|
36
|
+
<div className="card">
|
|
37
|
+
<div className="card-header">
|
|
38
|
+
<h2>Namespaces</h2>
|
|
39
|
+
<div className="table-responsive">
|
|
40
|
+
<table className="card-table table">
|
|
41
|
+
<thead>
|
|
42
|
+
<th>Namespace</th>
|
|
43
|
+
<th>Node Count</th>
|
|
44
|
+
</thead>
|
|
45
|
+
{namespacesList}
|
|
46
|
+
</table>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asynchronously loads the component for namespaces node-viewing page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { lazyLoad } from 'utils/loadable';
|
|
7
|
+
import styled from 'styled-components/macro';
|
|
8
|
+
|
|
9
|
+
const LoadingWrapper = styled.div`
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100vh;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
export const NamespacePage = lazyLoad(
|
|
18
|
+
() => import('./index'),
|
|
19
|
+
module => module.NamespacePage,
|
|
20
|
+
{
|
|
21
|
+
fallback: <LoadingWrapper></LoadingWrapper>,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<NamespacePage /> should render and match the snapshot 1`] = `
|
|
4
|
+
<div
|
|
5
|
+
className="mid"
|
|
6
|
+
>
|
|
7
|
+
<NamespaceHeader />
|
|
8
|
+
<div
|
|
9
|
+
className="card"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
className="card-header"
|
|
13
|
+
>
|
|
14
|
+
<h2>
|
|
15
|
+
Nodes
|
|
16
|
+
</h2>
|
|
17
|
+
<div
|
|
18
|
+
className="table-responsive"
|
|
19
|
+
>
|
|
20
|
+
<table
|
|
21
|
+
className="card-table table"
|
|
22
|
+
>
|
|
23
|
+
<thead>
|
|
24
|
+
<th>
|
|
25
|
+
Namespace
|
|
26
|
+
</th>
|
|
27
|
+
<th>
|
|
28
|
+
Name
|
|
29
|
+
</th>
|
|
30
|
+
<th>
|
|
31
|
+
Type
|
|
32
|
+
</th>
|
|
33
|
+
<th>
|
|
34
|
+
Status
|
|
35
|
+
</th>
|
|
36
|
+
<th>
|
|
37
|
+
Mode
|
|
38
|
+
</th>
|
|
39
|
+
</thead>
|
|
40
|
+
</table>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createRenderer } from 'react-test-renderer/shallow';
|
|
3
|
+
|
|
4
|
+
import { NamespacePage } from '../index';
|
|
5
|
+
|
|
6
|
+
const renderer = createRenderer();
|
|
7
|
+
|
|
8
|
+
describe('<NamespacePage />', () => {
|
|
9
|
+
it('should render and match the snapshot', () => {
|
|
10
|
+
renderer.render(<NamespacePage />);
|
|
11
|
+
const renderedOutput = renderer.getRenderOutput();
|
|
12
|
+
expect(renderedOutput).toMatchSnapshot();
|
|
13
|
+
});
|
|
14
|
+
});
|