datajunction-ui 0.0.1-a45 → 0.0.1-a45.dev5
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/package.json +3 -1
- package/src/app/components/NodeListActions.jsx +69 -0
- package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
- package/src/app/icons/CommitIcon.jsx +45 -0
- package/src/app/icons/DiffIcon.jsx +63 -0
- package/src/app/index.tsx +5 -0
- package/src/app/pages/NamespacePage/index.jsx +2 -6
- package/src/app/pages/NodePage/NodeHistory.jsx +150 -172
- package/src/app/pages/NodePage/RevisionDiff.jsx +200 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +2 -8
- package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +179 -0
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +0 -100
- package/src/app/services/__tests__/DJService.test.jsx +51 -0
- package/src/index.tsx +1 -0
- package/src/mocks/mockNodes.jsx +47 -0
- package/src/styles/index.css +89 -0
- package/src/app/components/DeleteNode.jsx +0 -55
- package/src/app/components/__tests__/DeleteNode.test.jsx +0 -53
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datajunction-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1-a45.dev5",
|
|
4
4
|
"description": "DataJunction Metrics Platform UI",
|
|
5
5
|
"module": "src/index.tsx",
|
|
6
6
|
"repository": {
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"react": "18.2.0",
|
|
70
70
|
"react-app-polyfill": "3.0.0",
|
|
71
71
|
"react-cookie": "4.1.1",
|
|
72
|
+
"react-diff-view": "3.2.1",
|
|
72
73
|
"react-dom": "18.2.0",
|
|
73
74
|
"react-helmet-async": "1.3.0",
|
|
74
75
|
"react-i18next": "11.18.6",
|
|
@@ -96,6 +97,7 @@
|
|
|
96
97
|
"ts-loader": "9.4.2",
|
|
97
98
|
"ts-node": "10.9.1",
|
|
98
99
|
"typescript": "4.6.4",
|
|
100
|
+
"unidiff": "1.0.4",
|
|
99
101
|
"web-vitals": "2.1.4",
|
|
100
102
|
"webpack": "5.81.0",
|
|
101
103
|
"webpack-cli": "5.0.2",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import DJClientContext from '../providers/djclient';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import DeleteIcon from '../icons/DeleteIcon';
|
|
4
|
+
import EditIcon from '../icons/EditIcon';
|
|
5
|
+
import { Form, Formik } from 'formik';
|
|
6
|
+
import { useContext } from 'react';
|
|
7
|
+
import { displayMessageAfterSubmit } from '../../utils/form';
|
|
8
|
+
|
|
9
|
+
export default function NodeListActions({ nodeName }) {
|
|
10
|
+
const [editButton, setEditButton] = React.useState(<EditIcon />);
|
|
11
|
+
const [deleteButton, setDeleteButton] = React.useState(<DeleteIcon />);
|
|
12
|
+
|
|
13
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
14
|
+
const deleteNode = async (values, { setStatus }) => {
|
|
15
|
+
if (
|
|
16
|
+
!window.confirm('Deleting node ' + values.nodeName + '. Are you sure?')
|
|
17
|
+
) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { status, json } = await djClient.deactivate(values.nodeName);
|
|
21
|
+
if (status === 200 || status === 201 || status === 204) {
|
|
22
|
+
setStatus({
|
|
23
|
+
success: <>Successfully deleted node {values.nodeName}</>,
|
|
24
|
+
});
|
|
25
|
+
setEditButton(''); // hide the Edit button
|
|
26
|
+
setDeleteButton(''); // hide the Delete button
|
|
27
|
+
} else {
|
|
28
|
+
setStatus({
|
|
29
|
+
failure: `${json.message}`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const initialValues = {
|
|
35
|
+
nodeName: nodeName,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div>
|
|
40
|
+
<a href={`/nodes/${nodeName}/edit`} style={{ marginLeft: '0.5rem' }}>
|
|
41
|
+
{editButton}
|
|
42
|
+
</a>
|
|
43
|
+
<Formik initialValues={initialValues} onSubmit={deleteNode}>
|
|
44
|
+
{function Render({ status, setFieldValue }) {
|
|
45
|
+
return (
|
|
46
|
+
<Form className="deleteNode">
|
|
47
|
+
{displayMessageAfterSubmit(status)}
|
|
48
|
+
{
|
|
49
|
+
<>
|
|
50
|
+
<button
|
|
51
|
+
type="submit"
|
|
52
|
+
style={{
|
|
53
|
+
marginLeft: 0,
|
|
54
|
+
all: 'unset',
|
|
55
|
+
color: '#005c72',
|
|
56
|
+
cursor: 'pointer',
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{deleteButton}
|
|
60
|
+
</button>
|
|
61
|
+
</>
|
|
62
|
+
}
|
|
63
|
+
</Form>
|
|
64
|
+
);
|
|
65
|
+
}}
|
|
66
|
+
</Formik>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import fetchMock from 'jest-fetch-mock';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { render } from '../../../setupTests';
|
|
6
|
+
import DJClientContext from '../../providers/djclient';
|
|
7
|
+
import NodeListActions from '../NodeListActions';
|
|
8
|
+
|
|
9
|
+
describe('<NodeListActions />', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
fetchMock.resetMocks();
|
|
12
|
+
jest.clearAllMocks();
|
|
13
|
+
window.scrollTo = jest.fn();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const renderElement = djClient => {
|
|
17
|
+
return render(
|
|
18
|
+
<DJClientContext.Provider value={djClient}>
|
|
19
|
+
<NodeListActions nodeName="default.hard_hat" />
|
|
20
|
+
</DJClientContext.Provider>,
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const initializeMockDJClient = () => {
|
|
25
|
+
return {
|
|
26
|
+
DataJunctionAPI: {
|
|
27
|
+
deactivate: jest.fn(),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
it('deletes a node when clicked', async () => {
|
|
33
|
+
global.confirm = () => true;
|
|
34
|
+
const mockDjClient = initializeMockDJClient();
|
|
35
|
+
mockDjClient.DataJunctionAPI.deactivate.mockReturnValue({
|
|
36
|
+
status: 204,
|
|
37
|
+
json: { name: 'source.warehouse.schema.some_table' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
renderElement(mockDjClient);
|
|
41
|
+
|
|
42
|
+
await userEvent.click(screen.getByRole('button'));
|
|
43
|
+
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalled();
|
|
46
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalledWith(
|
|
47
|
+
'default.hard_hat',
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
expect(
|
|
51
|
+
screen.getByText('Successfully deleted node default.hard_hat'),
|
|
52
|
+
).toBeInTheDocument();
|
|
53
|
+
}, 60000);
|
|
54
|
+
|
|
55
|
+
it('skips a node deletion during confirm', async () => {
|
|
56
|
+
global.confirm = () => false;
|
|
57
|
+
const mockDjClient = initializeMockDJClient();
|
|
58
|
+
mockDjClient.DataJunctionAPI.deactivate.mockReturnValue({
|
|
59
|
+
status: 204,
|
|
60
|
+
json: { name: 'source.warehouse.schema.some_table' },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
renderElement(mockDjClient);
|
|
64
|
+
|
|
65
|
+
await userEvent.click(screen.getByRole('button'));
|
|
66
|
+
|
|
67
|
+
await waitFor(() => {
|
|
68
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).not.toBeCalled();
|
|
69
|
+
});
|
|
70
|
+
}, 60000);
|
|
71
|
+
|
|
72
|
+
it('fail deleting a node when clicked', async () => {
|
|
73
|
+
global.confirm = () => true;
|
|
74
|
+
const mockDjClient = initializeMockDJClient();
|
|
75
|
+
mockDjClient.DataJunctionAPI.deactivate.mockReturnValue({
|
|
76
|
+
status: 777,
|
|
77
|
+
json: { message: 'source.warehouse.schema.some_table' },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
renderElement(mockDjClient);
|
|
81
|
+
|
|
82
|
+
await userEvent.click(screen.getByRole('button'));
|
|
83
|
+
|
|
84
|
+
await waitFor(() => {
|
|
85
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalled();
|
|
86
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalledWith(
|
|
87
|
+
'default.hard_hat',
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
expect(
|
|
91
|
+
screen.getByText('source.warehouse.schema.some_table'),
|
|
92
|
+
).toBeInTheDocument();
|
|
93
|
+
}, 60000);
|
|
94
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
const CommitIcon = props => (
|
|
4
|
+
<svg
|
|
5
|
+
width="2em"
|
|
6
|
+
height="2em"
|
|
7
|
+
viewBox="0 0 256 256"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<rect fill="none" height="256" width="256" />
|
|
11
|
+
<circle
|
|
12
|
+
cx="128"
|
|
13
|
+
cy="128"
|
|
14
|
+
fill="none"
|
|
15
|
+
r="52"
|
|
16
|
+
stroke="#000"
|
|
17
|
+
strokeLinecap="round"
|
|
18
|
+
strokeLinejoin="round"
|
|
19
|
+
strokeWidth="12"
|
|
20
|
+
/>
|
|
21
|
+
<line
|
|
22
|
+
fill="none"
|
|
23
|
+
stroke="#000"
|
|
24
|
+
strokeLinecap="round"
|
|
25
|
+
strokeLinejoin="round"
|
|
26
|
+
strokeWidth="12"
|
|
27
|
+
x1="8"
|
|
28
|
+
x2="76"
|
|
29
|
+
y1="128"
|
|
30
|
+
y2="128"
|
|
31
|
+
/>
|
|
32
|
+
<line
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke="#000"
|
|
35
|
+
strokeLinecap="round"
|
|
36
|
+
strokeLinejoin="round"
|
|
37
|
+
strokeWidth="12"
|
|
38
|
+
x1="180"
|
|
39
|
+
x2="248"
|
|
40
|
+
y1="128"
|
|
41
|
+
y2="128"
|
|
42
|
+
/>
|
|
43
|
+
</svg>
|
|
44
|
+
);
|
|
45
|
+
export default CommitIcon;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const DiffIcon = props => (
|
|
2
|
+
<svg
|
|
3
|
+
width="2em"
|
|
4
|
+
height="2em"
|
|
5
|
+
viewBox="0 0 256 256"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
>
|
|
8
|
+
<rect fill="none" height="256" width="256" />
|
|
9
|
+
<circle
|
|
10
|
+
cx="196"
|
|
11
|
+
cy="188"
|
|
12
|
+
fill="none"
|
|
13
|
+
r="28"
|
|
14
|
+
stroke="#000"
|
|
15
|
+
strokeLinecap="round"
|
|
16
|
+
strokeLinejoin="round"
|
|
17
|
+
strokeWidth="12"
|
|
18
|
+
/>
|
|
19
|
+
<path
|
|
20
|
+
d="M196,160V119.9a48.2,48.2,0,0,0-14.1-34L144,48"
|
|
21
|
+
fill="none"
|
|
22
|
+
stroke="#000"
|
|
23
|
+
strokeLinecap="round"
|
|
24
|
+
strokeLinejoin="round"
|
|
25
|
+
strokeWidth="12"
|
|
26
|
+
/>
|
|
27
|
+
<polyline
|
|
28
|
+
fill="none"
|
|
29
|
+
points="144 88 144 48 184 48"
|
|
30
|
+
stroke="#000"
|
|
31
|
+
strokeLinecap="round"
|
|
32
|
+
strokeLinejoin="round"
|
|
33
|
+
strokeWidth="12"
|
|
34
|
+
/>
|
|
35
|
+
<circle
|
|
36
|
+
cx="60"
|
|
37
|
+
cy="68"
|
|
38
|
+
fill="none"
|
|
39
|
+
r="28"
|
|
40
|
+
stroke="#000"
|
|
41
|
+
strokeLinecap="round"
|
|
42
|
+
strokeLinejoin="round"
|
|
43
|
+
strokeWidth="12"
|
|
44
|
+
/>
|
|
45
|
+
<path
|
|
46
|
+
d="M60,96v40.1a48.2,48.2,0,0,0,14.1,34L112,208"
|
|
47
|
+
fill="none"
|
|
48
|
+
stroke="#000"
|
|
49
|
+
strokeLinecap="round"
|
|
50
|
+
strokeLinejoin="round"
|
|
51
|
+
strokeWidth="12"
|
|
52
|
+
/>
|
|
53
|
+
<polyline
|
|
54
|
+
fill="none"
|
|
55
|
+
points="112 168 112 208 72 208"
|
|
56
|
+
stroke="#000"
|
|
57
|
+
strokeLinecap="round"
|
|
58
|
+
strokeLinejoin="round"
|
|
59
|
+
strokeWidth="12"
|
|
60
|
+
/>
|
|
61
|
+
</svg>
|
|
62
|
+
);
|
|
63
|
+
export default DiffIcon;
|
package/src/app/index.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
|
|
9
9
|
|
|
10
10
|
import { NamespacePage } from './pages/NamespacePage/Loadable';
|
|
11
11
|
import { NodePage } from './pages/NodePage/Loadable';
|
|
12
|
+
import RevisionDiff from './pages/NodePage/RevisionDiff';
|
|
12
13
|
import { SQLBuilderPage } from './pages/SQLBuilderPage/Loadable';
|
|
13
14
|
import { CubeBuilderPage } from './pages/CubeBuilderPage/Loadable';
|
|
14
15
|
import { TagPage } from './pages/TagPage/Loadable';
|
|
@@ -58,6 +59,10 @@ export function App() {
|
|
|
58
59
|
key="edit-cube"
|
|
59
60
|
element={<CubeBuilderPage />}
|
|
60
61
|
/>
|
|
62
|
+
<Route
|
|
63
|
+
path=":name/revisions/:revision"
|
|
64
|
+
element={<RevisionDiff />}
|
|
65
|
+
/>
|
|
61
66
|
<Route path=":name/:tab" element={<NodePage />} />
|
|
62
67
|
</Route>
|
|
63
68
|
|
|
@@ -4,8 +4,7 @@ import { useContext, useEffect, useState } from 'react';
|
|
|
4
4
|
import NodeStatus from '../NodePage/NodeStatus';
|
|
5
5
|
import DJClientContext from '../../providers/djclient';
|
|
6
6
|
import Explorer from '../NamespacePage/Explorer';
|
|
7
|
-
import
|
|
8
|
-
import DeleteNode from '../../components/DeleteNode';
|
|
7
|
+
import NodeListActions from '../../components/NodeListActions';
|
|
9
8
|
import AddNamespacePopover from './AddNamespacePopover';
|
|
10
9
|
|
|
11
10
|
export function NamespacePage() {
|
|
@@ -106,10 +105,7 @@ export function NamespacePage() {
|
|
|
106
105
|
</span>
|
|
107
106
|
</td>
|
|
108
107
|
<td>
|
|
109
|
-
<
|
|
110
|
-
<EditIcon />
|
|
111
|
-
</a>
|
|
112
|
-
<DeleteNode nodeName={node?.name} />
|
|
108
|
+
<NodeListActions nodeName={node?.name} />
|
|
113
109
|
</td>
|
|
114
110
|
</tr>
|
|
115
111
|
));
|