datajunction-ui 0.0.1-a40.dev → 0.0.1-a41
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 +1 -1
- package/src/app/pages/NodePage/AddBackfillPopover.jsx +4 -2
- package/src/app/pages/NodePage/NodeColumnTab.jsx +21 -62
- package/src/app/pages/NodePage/NodeHistory.jsx +1 -12
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +0 -1
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +2 -45
- package/src/app/pages/NodePage/index.jsx +0 -9
- package/src/app/services/DJService.js +0 -7
- package/src/mocks/mockNodes.jsx +0 -1
- package/src/styles/index.css +0 -16
- package/dj.internal.db +0 -0
- package/src/app/pages/NodePage/NodeDimensionsTab.jsx +0 -80
- package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +0 -162
- package/src/app/pages/NodePage/__tests__/NodeDimensionsTab.test.jsx +0 -145
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { useContext, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import DJClientContext from '../../providers/djclient';
|
|
4
|
-
import { Field, Form, Formik } from 'formik';
|
|
5
|
-
import {
|
|
4
|
+
import { ErrorMessage, Field, Form, Formik } from 'formik';
|
|
5
|
+
import { FormikSelect } from '../AddEditNodePage/FormikSelect';
|
|
6
|
+
import EditIcon from '../../icons/EditIcon';
|
|
7
|
+
import { displayMessageAfterSubmit, labelize } from '../../../utils/form';
|
|
6
8
|
|
|
7
9
|
export default function AddBackfillPopover({
|
|
8
10
|
node,
|
|
@@ -5,8 +5,6 @@ import EditColumnPopover from './EditColumnPopover';
|
|
|
5
5
|
import LinkDimensionPopover from './LinkDimensionPopover';
|
|
6
6
|
import { labelize } from '../../../utils/form';
|
|
7
7
|
import PartitionColumnPopover from './PartitionColumnPopover';
|
|
8
|
-
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
9
|
-
import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
|
|
10
8
|
|
|
11
9
|
export default function NodeColumnTab({ node, djClient }) {
|
|
12
10
|
const [attributes, setAttributes] = useState([]);
|
|
@@ -177,65 +175,26 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
177
175
|
};
|
|
178
176
|
|
|
179
177
|
return (
|
|
180
|
-
|
|
181
|
-
<
|
|
182
|
-
<
|
|
183
|
-
<
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
</div>
|
|
202
|
-
<div>
|
|
203
|
-
<h3>Linked Dimensions (Custom Join SQL)</h3>
|
|
204
|
-
<table className="card-inner-table table">
|
|
205
|
-
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
206
|
-
<tr>
|
|
207
|
-
<th className="text-start">Dimension Node</th>
|
|
208
|
-
<th>Join Type</th>
|
|
209
|
-
<th>Join SQL</th>
|
|
210
|
-
<th>Role</th>
|
|
211
|
-
</tr>
|
|
212
|
-
</thead>
|
|
213
|
-
<tbody>
|
|
214
|
-
{node?.dimension_links.map(link => {
|
|
215
|
-
return (
|
|
216
|
-
<tr>
|
|
217
|
-
<td>
|
|
218
|
-
<a href={'/nodes/' + link.dimension.name}>
|
|
219
|
-
{link.dimension.name}
|
|
220
|
-
</a>
|
|
221
|
-
</td>
|
|
222
|
-
<td>{link.join_type.toUpperCase()}</td>
|
|
223
|
-
<td style={{ width: '25rem', maxWidth: 'none' }}>
|
|
224
|
-
<SyntaxHighlighter
|
|
225
|
-
language="sql"
|
|
226
|
-
style={foundation}
|
|
227
|
-
wrapLongLines={true}
|
|
228
|
-
>
|
|
229
|
-
{link.join_sql}
|
|
230
|
-
</SyntaxHighlighter>
|
|
231
|
-
</td>
|
|
232
|
-
<td>{link.role}</td>
|
|
233
|
-
</tr>
|
|
234
|
-
);
|
|
235
|
-
})}
|
|
236
|
-
</tbody>
|
|
237
|
-
</table>
|
|
238
|
-
</div>
|
|
239
|
-
</>
|
|
178
|
+
<div className="table-responsive">
|
|
179
|
+
<table className="card-inner-table table">
|
|
180
|
+
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
181
|
+
<tr>
|
|
182
|
+
<th className="text-start">Column</th>
|
|
183
|
+
<th>Display Name</th>
|
|
184
|
+
<th>Type</th>
|
|
185
|
+
{node?.type !== 'cube' ? (
|
|
186
|
+
<>
|
|
187
|
+
<th>Linked Dimension</th>
|
|
188
|
+
<th>Attributes</th>
|
|
189
|
+
</>
|
|
190
|
+
) : (
|
|
191
|
+
''
|
|
192
|
+
)}
|
|
193
|
+
<th>Partition</th>
|
|
194
|
+
</tr>
|
|
195
|
+
</thead>
|
|
196
|
+
<tbody>{columnList(columns)}</tbody>
|
|
197
|
+
</table>
|
|
198
|
+
</div>
|
|
240
199
|
);
|
|
241
200
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
3
|
-
import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
|
|
4
|
-
import * as React from 'react';
|
|
5
2
|
|
|
6
3
|
export default function NodeHistory({ node, djClient }) {
|
|
7
4
|
const [history, setHistory] = useState([]);
|
|
@@ -178,15 +175,7 @@ export default function NodeHistory({ node, djClient }) {
|
|
|
178
175
|
</td>
|
|
179
176
|
<td>{revision.display_name}</td>
|
|
180
177
|
<td>{revision.description}</td>
|
|
181
|
-
<td>
|
|
182
|
-
<SyntaxHighlighter
|
|
183
|
-
language="sql"
|
|
184
|
-
style={foundation}
|
|
185
|
-
wrapLongLines={true}
|
|
186
|
-
>
|
|
187
|
-
{revision.query}
|
|
188
|
-
</SyntaxHighlighter>
|
|
189
|
-
</td>
|
|
178
|
+
<td>{revision.query}</td>
|
|
190
179
|
<td>{revision.tags}</td>
|
|
191
180
|
</tr>
|
|
192
181
|
));
|
|
@@ -46,53 +46,10 @@ exports[`<NodePage /> renders the NodeHistory tab correctly 1`] = `
|
|
|
46
46
|
Average repair price
|
|
47
47
|
</td>
|
|
48
48
|
<td>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
>
|
|
52
|
-
<code
|
|
53
|
-
class="language-sql"
|
|
54
|
-
style="white-space: pre-wrap;"
|
|
55
|
-
>
|
|
56
|
-
<span>
|
|
57
|
-
<span
|
|
58
|
-
style="color: rgb(0, 153, 153);"
|
|
59
|
-
>
|
|
60
|
-
SELECT
|
|
61
|
-
</span>
|
|
62
|
-
<span>
|
|
63
|
-
|
|
64
|
-
</span>
|
|
65
|
-
<span
|
|
66
|
-
class="hljs-built_in"
|
|
67
|
-
>
|
|
68
|
-
avg
|
|
69
|
-
</span>
|
|
70
|
-
<span>
|
|
71
|
-
(price) default_DOT_avg_repair_price
|
|
72
|
-
|
|
73
|
-
</span>
|
|
74
|
-
</span>
|
|
75
|
-
<span>
|
|
76
|
-
<span>
|
|
77
|
-
|
|
78
|
-
</span>
|
|
79
|
-
<span
|
|
80
|
-
style="color: rgb(0, 153, 153);"
|
|
81
|
-
>
|
|
82
|
-
FROM
|
|
83
|
-
</span>
|
|
84
|
-
<span>
|
|
85
|
-
default.repair_order_details
|
|
49
|
+
SELECT avg(price) default_DOT_avg_repair_price
|
|
50
|
+
FROM default.repair_order_details
|
|
86
51
|
|
|
87
|
-
</span>
|
|
88
|
-
</span>
|
|
89
|
-
<span>
|
|
90
|
-
|
|
91
52
|
|
|
92
|
-
</span>
|
|
93
|
-
<span />
|
|
94
|
-
</code>
|
|
95
|
-
</pre>
|
|
96
53
|
</td>
|
|
97
54
|
<td />
|
|
98
55
|
</tr>
|
|
@@ -15,7 +15,6 @@ import NodesWithDimension from './NodesWithDimension';
|
|
|
15
15
|
import NodeColumnLineage from './NodeLineageTab';
|
|
16
16
|
import EditIcon from '../../icons/EditIcon';
|
|
17
17
|
import AlertIcon from '../../icons/AlertIcon';
|
|
18
|
-
import NodeDimensionsTab from "./NodeDimensionsTab";
|
|
19
18
|
|
|
20
19
|
export function NodePage() {
|
|
21
20
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
@@ -105,11 +104,6 @@ export function NodePage() {
|
|
|
105
104
|
name: 'Lineage',
|
|
106
105
|
display: node?.type === 'metric',
|
|
107
106
|
},
|
|
108
|
-
{
|
|
109
|
-
id: 8,
|
|
110
|
-
name: 'Dimensions',
|
|
111
|
-
display: node?.type !== 'cube',
|
|
112
|
-
},
|
|
113
107
|
];
|
|
114
108
|
};
|
|
115
109
|
|
|
@@ -143,9 +137,6 @@ export function NodePage() {
|
|
|
143
137
|
case 7:
|
|
144
138
|
tabToDisplay = <NodeColumnLineage djNode={node} djClient={djClient} />;
|
|
145
139
|
break;
|
|
146
|
-
case 8:
|
|
147
|
-
tabToDisplay = <NodeDimensionsTab node={node} djClient={djClient} />;
|
|
148
|
-
break;
|
|
149
140
|
default: /* istanbul ignore next */
|
|
150
141
|
tabToDisplay = <NodeInfoTab node={node} />;
|
|
151
142
|
}
|
|
@@ -543,13 +543,6 @@ export const DataJunctionAPI = {
|
|
|
543
543
|
})
|
|
544
544
|
).json();
|
|
545
545
|
},
|
|
546
|
-
nodeDimensions: async function (nodeName) {
|
|
547
|
-
return await (
|
|
548
|
-
await fetch(`${DJ_URL}/nodes/${nodeName}/dimensions`, {
|
|
549
|
-
credentials: 'include',
|
|
550
|
-
})
|
|
551
|
-
).json();
|
|
552
|
-
},
|
|
553
546
|
linkDimension: async function (nodeName, columnName, dimensionName) {
|
|
554
547
|
const response = await fetch(
|
|
555
548
|
`${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
|
package/src/mocks/mockNodes.jsx
CHANGED
package/src/styles/index.css
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
@import url('https://fonts.googleapis.com/css?family=Jost');
|
|
2
2
|
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@300;600&display=swap');
|
|
3
3
|
@import url('https://fonts.googleapis.com/css?family=Lato');
|
|
4
|
-
@import url('https://fonts.googleapis.com/css?family=Consolas');
|
|
5
4
|
|
|
6
5
|
body {
|
|
7
6
|
margin: 0;
|
|
@@ -1092,18 +1091,3 @@ pre {
|
|
|
1092
1091
|
.partitionLink:hover {
|
|
1093
1092
|
text-decoration: none;
|
|
1094
1093
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
.dimensionsList {
|
|
1098
|
-
padding: 12px;
|
|
1099
|
-
opacity: 1;
|
|
1100
|
-
border-radius: 0.5rem;
|
|
1101
|
-
line-height: 1.55rem;
|
|
1102
|
-
font-size: 0.95rem;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
.DimensionAttribute {
|
|
1106
|
-
background: #effcff;
|
|
1107
|
-
padding: 5px;
|
|
1108
|
-
font-family: Consolas, serif;
|
|
1109
|
-
}
|
package/dj.internal.db
DELETED
|
File without changes
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { labelize } from '../../../utils/form';
|
|
4
|
-
|
|
5
|
-
export default function NodeDimensionsTab({ node, djClient }) {
|
|
6
|
-
const [dimensions, setDimensions] = useState([]);
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
const fetchData = async () => {
|
|
9
|
-
if (node) {
|
|
10
|
-
const data = await djClient.nodeDimensions(node.name);
|
|
11
|
-
const grouped = Object.entries(
|
|
12
|
-
data.reduce((group, dimension) => {
|
|
13
|
-
group[dimension.node_name + dimension.path] =
|
|
14
|
-
group[dimension.node_name + dimension.path] ?? [];
|
|
15
|
-
group[dimension.node_name + dimension.path].push(dimension);
|
|
16
|
-
return group;
|
|
17
|
-
}, {}),
|
|
18
|
-
);
|
|
19
|
-
setDimensions(grouped);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
fetchData().catch(console.error);
|
|
23
|
-
}, [djClient, node]);
|
|
24
|
-
|
|
25
|
-
// Builds the block of dimensions selectors, grouped by node name + path
|
|
26
|
-
return (
|
|
27
|
-
<div style={{ padding: '1rem' }}>
|
|
28
|
-
{dimensions.map(grouping => {
|
|
29
|
-
const dimensionsInGroup = grouping[1];
|
|
30
|
-
const role = dimensionsInGroup[0].path
|
|
31
|
-
.map(pathItem => pathItem.split('.').slice(-1))
|
|
32
|
-
.join(' → ');
|
|
33
|
-
const fullPath = dimensionsInGroup[0].path.join(' → ');
|
|
34
|
-
const groupHeader = (
|
|
35
|
-
<h4
|
|
36
|
-
style={{
|
|
37
|
-
fontWeight: 'normal',
|
|
38
|
-
marginBottom: '5px',
|
|
39
|
-
marginTop: '15px',
|
|
40
|
-
}}
|
|
41
|
-
>
|
|
42
|
-
<a href={`/nodes/${dimensionsInGroup[0].node_name}`}>
|
|
43
|
-
<b>{dimensionsInGroup[0].node_display_name}</b>
|
|
44
|
-
</a>{' '}
|
|
45
|
-
with role{' '}
|
|
46
|
-
<span className="HighlightPath">
|
|
47
|
-
<b>{role}</b>
|
|
48
|
-
</span>{' '}
|
|
49
|
-
via <span className="HighlightPath">{fullPath}</span>
|
|
50
|
-
</h4>
|
|
51
|
-
);
|
|
52
|
-
const dimensionGroupOptions = dimensionsInGroup.map(dim => {
|
|
53
|
-
return {
|
|
54
|
-
value: dim.name,
|
|
55
|
-
label:
|
|
56
|
-
labelize(dim.name.split('.').slice(-1)[0]) +
|
|
57
|
-
(dim.is_primary_key ? ' (PK)' : ''),
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
return (
|
|
61
|
-
<>
|
|
62
|
-
{groupHeader}
|
|
63
|
-
<div className="dimensionsList">
|
|
64
|
-
{dimensionGroupOptions.map(dimension => {
|
|
65
|
-
return (
|
|
66
|
-
<div>
|
|
67
|
-
{dimension.label.split('[').slice(0)[0]} ⇢{' '}
|
|
68
|
-
<code className="DimensionAttribute">
|
|
69
|
-
{dimension.value}
|
|
70
|
-
</code>
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
})}
|
|
74
|
-
</div>
|
|
75
|
-
</>
|
|
76
|
-
);
|
|
77
|
-
})}
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, waitFor, screen } from '@testing-library/react';
|
|
3
|
-
import NodeColumnTab from '../NodeColumnTab';
|
|
4
|
-
|
|
5
|
-
describe('<NodeColumnTab />', () => {
|
|
6
|
-
const mockDjClient = {
|
|
7
|
-
node: jest.fn(),
|
|
8
|
-
columns: jest.fn(),
|
|
9
|
-
attributes: jest.fn(),
|
|
10
|
-
dimensions: jest.fn(),
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const mockNodeColumns = [
|
|
14
|
-
{
|
|
15
|
-
name: 'repair_order_id',
|
|
16
|
-
display_name: 'Repair Order Id',
|
|
17
|
-
type: 'int',
|
|
18
|
-
attributes: [],
|
|
19
|
-
dimension: null,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'municipality_id',
|
|
23
|
-
display_name: 'Municipality Id',
|
|
24
|
-
type: 'string',
|
|
25
|
-
attributes: [],
|
|
26
|
-
dimension: null,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'hard_hat_id',
|
|
30
|
-
display_name: 'Hard Hat Id',
|
|
31
|
-
type: 'int',
|
|
32
|
-
attributes: [],
|
|
33
|
-
dimension: null,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'order_date',
|
|
37
|
-
display_name: 'Order Date',
|
|
38
|
-
type: 'date',
|
|
39
|
-
attributes: [],
|
|
40
|
-
dimension: null,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'required_date',
|
|
44
|
-
display_name: 'Required Date',
|
|
45
|
-
type: 'date',
|
|
46
|
-
attributes: [],
|
|
47
|
-
dimension: null,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'dispatched_date',
|
|
51
|
-
display_name: 'Dispatched Date',
|
|
52
|
-
type: 'date',
|
|
53
|
-
attributes: [],
|
|
54
|
-
dimension: null,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'dispatcher_id',
|
|
58
|
-
display_name: 'Dispatcher Id',
|
|
59
|
-
type: 'int',
|
|
60
|
-
attributes: [],
|
|
61
|
-
dimension: null,
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
const mockNode = {
|
|
66
|
-
node_revision_id: 1,
|
|
67
|
-
node_id: 1,
|
|
68
|
-
type: 'source',
|
|
69
|
-
name: 'default.repair_orders',
|
|
70
|
-
display_name: 'Default: Repair Orders',
|
|
71
|
-
version: 'v1.0',
|
|
72
|
-
status: 'valid',
|
|
73
|
-
mode: 'published',
|
|
74
|
-
catalog: {
|
|
75
|
-
id: 1,
|
|
76
|
-
uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
|
|
77
|
-
created_at: '2023-08-21T16:48:51.146121+00:00',
|
|
78
|
-
updated_at: '2023-08-21T16:48:51.146122+00:00',
|
|
79
|
-
extra_params: {},
|
|
80
|
-
name: 'warehouse',
|
|
81
|
-
},
|
|
82
|
-
schema_: 'roads',
|
|
83
|
-
table: 'repair_orders',
|
|
84
|
-
description: 'Repair orders',
|
|
85
|
-
query: null,
|
|
86
|
-
availability: null,
|
|
87
|
-
columns: mockNodeColumns,
|
|
88
|
-
updated_at: '2023-08-21T16:48:52.880498+00:00',
|
|
89
|
-
materializations: [],
|
|
90
|
-
parents: [],
|
|
91
|
-
dimension_links: [
|
|
92
|
-
{
|
|
93
|
-
dimension: {
|
|
94
|
-
name: 'default.contractor',
|
|
95
|
-
},
|
|
96
|
-
join_type: 'left',
|
|
97
|
-
join_sql:
|
|
98
|
-
'default.contractor.contractor_id = default.repair_orders.contractor_id',
|
|
99
|
-
join_cardinality: 'one_to_one',
|
|
100
|
-
role: 'contractor',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const mockAttributes = [
|
|
106
|
-
{
|
|
107
|
-
uniqueness_scope: [],
|
|
108
|
-
namespace: 'system',
|
|
109
|
-
name: 'primary_key',
|
|
110
|
-
description:
|
|
111
|
-
'Points to a column which is part of the primary key of the node',
|
|
112
|
-
allowed_node_types: ['source', 'transform', 'dimension'],
|
|
113
|
-
id: 1,
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
uniqueness_scope: [],
|
|
117
|
-
namespace: 'system',
|
|
118
|
-
name: 'dimension',
|
|
119
|
-
description: 'Points to a dimension attribute column',
|
|
120
|
-
allowed_node_types: ['source', 'transform'],
|
|
121
|
-
id: 2,
|
|
122
|
-
},
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
const mockDimensions = ['default.contractor', 'default.hard_hat'];
|
|
126
|
-
|
|
127
|
-
beforeEach(() => {
|
|
128
|
-
// Reset the mocks before each test
|
|
129
|
-
mockDjClient.node.mockReset();
|
|
130
|
-
mockDjClient.columns.mockReset();
|
|
131
|
-
mockDjClient.attributes.mockReset();
|
|
132
|
-
mockDjClient.dimensions.mockReset();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('renders node columns and dimension links', async () => {
|
|
136
|
-
mockDjClient.node.mockReturnValue(mockNode);
|
|
137
|
-
mockDjClient.columns.mockReturnValue(mockNodeColumns);
|
|
138
|
-
mockDjClient.attributes.mockReturnValue(mockAttributes);
|
|
139
|
-
mockDjClient.dimensions.mockReturnValue(mockDimensions);
|
|
140
|
-
|
|
141
|
-
render(<NodeColumnTab node={mockNode} djClient={mockDjClient} />);
|
|
142
|
-
|
|
143
|
-
await waitFor(() => {
|
|
144
|
-
// Displays the columns
|
|
145
|
-
for (const column of mockNode.columns) {
|
|
146
|
-
expect(screen.getByText(column.name)).toBeInTheDocument();
|
|
147
|
-
expect(screen.getByText(column.display_name)).toBeInTheDocument();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Displays the dimension links
|
|
151
|
-
for (const dimensionLink of mockNode.dimension_links) {
|
|
152
|
-
const link = screen
|
|
153
|
-
.getByText(dimensionLink.dimension.name)
|
|
154
|
-
.closest('a');
|
|
155
|
-
expect(link).toHaveAttribute(
|
|
156
|
-
'href',
|
|
157
|
-
`/nodes/${dimensionLink.dimension.name}`,
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, waitFor, screen } from '@testing-library/react';
|
|
3
|
-
import NodeDimensionsTab from '../NodeDimensionsTab';
|
|
4
|
-
|
|
5
|
-
describe('<NodeDimensionsTab />', () => {
|
|
6
|
-
const mockDjClient = {
|
|
7
|
-
node: jest.fn(),
|
|
8
|
-
nodeDimensions: jest.fn(),
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const mockNode = {
|
|
12
|
-
node_revision_id: 1,
|
|
13
|
-
node_id: 1,
|
|
14
|
-
type: 'source',
|
|
15
|
-
name: 'default.repair_orders',
|
|
16
|
-
display_name: 'Default: Repair Orders',
|
|
17
|
-
version: 'v1.0',
|
|
18
|
-
status: 'valid',
|
|
19
|
-
mode: 'published',
|
|
20
|
-
catalog: {
|
|
21
|
-
id: 1,
|
|
22
|
-
uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
|
|
23
|
-
created_at: '2023-08-21T16:48:51.146121+00:00',
|
|
24
|
-
updated_at: '2023-08-21T16:48:51.146122+00:00',
|
|
25
|
-
extra_params: {},
|
|
26
|
-
name: 'warehouse',
|
|
27
|
-
},
|
|
28
|
-
schema_: 'roads',
|
|
29
|
-
table: 'repair_orders',
|
|
30
|
-
description: 'Repair orders',
|
|
31
|
-
query: null,
|
|
32
|
-
availability: null,
|
|
33
|
-
columns: [
|
|
34
|
-
{
|
|
35
|
-
name: 'repair_order_id',
|
|
36
|
-
type: 'int',
|
|
37
|
-
attributes: [],
|
|
38
|
-
dimension: null,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'municipality_id',
|
|
42
|
-
type: 'string',
|
|
43
|
-
attributes: [],
|
|
44
|
-
dimension: null,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: 'hard_hat_id',
|
|
48
|
-
type: 'int',
|
|
49
|
-
attributes: [],
|
|
50
|
-
dimension: null,
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'order_date',
|
|
54
|
-
type: 'date',
|
|
55
|
-
attributes: [],
|
|
56
|
-
dimension: null,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: 'required_date',
|
|
60
|
-
type: 'date',
|
|
61
|
-
attributes: [],
|
|
62
|
-
dimension: null,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: 'dispatched_date',
|
|
66
|
-
type: 'date',
|
|
67
|
-
attributes: [],
|
|
68
|
-
dimension: null,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'dispatcher_id',
|
|
72
|
-
type: 'int',
|
|
73
|
-
attributes: [],
|
|
74
|
-
dimension: null,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
updated_at: '2023-08-21T16:48:52.880498+00:00',
|
|
78
|
-
materializations: [],
|
|
79
|
-
parents: [],
|
|
80
|
-
dimension_links: [
|
|
81
|
-
{
|
|
82
|
-
dimension: {
|
|
83
|
-
name: 'default.contractor',
|
|
84
|
-
},
|
|
85
|
-
join_type: 'left',
|
|
86
|
-
join_sql:
|
|
87
|
-
'default.contractor.contractor_id = default.repair_orders.contractor_id',
|
|
88
|
-
join_cardinality: 'one_to_one',
|
|
89
|
-
role: 'contractor',
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const mockDimensions = [
|
|
95
|
-
{
|
|
96
|
-
is_primary_key: false,
|
|
97
|
-
name: 'default.dispatcher.company_name',
|
|
98
|
-
node_display_name: 'Default: Dispatcher',
|
|
99
|
-
node_name: 'default.dispatcher',
|
|
100
|
-
path: ['default.repair_orders_fact.dispatcher_id'],
|
|
101
|
-
type: 'string',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
is_primary_key: true,
|
|
105
|
-
name: 'default.dispatcher.dispatcher_id',
|
|
106
|
-
node_display_name: 'Default: Dispatcher',
|
|
107
|
-
node_name: 'default.dispatcher',
|
|
108
|
-
path: ['default.repair_orders_fact.dispatcher_id'],
|
|
109
|
-
type: 'int',
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
is_primary_key: false,
|
|
113
|
-
name: 'default.hard_hat.city',
|
|
114
|
-
node_display_name: 'Default: Hard Hat',
|
|
115
|
-
node_name: 'default.hard_hat',
|
|
116
|
-
path: ['default.repair_orders_fact.hard_hat_id'],
|
|
117
|
-
type: 'string',
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
is_primary_key: true,
|
|
121
|
-
name: 'default.hard_hat.hard_hat_id',
|
|
122
|
-
node_display_name: 'Default: Hard Hat',
|
|
123
|
-
node_name: 'default.hard_hat',
|
|
124
|
-
path: ['default.repair_orders_fact.hard_hat_id'],
|
|
125
|
-
type: 'int',
|
|
126
|
-
},
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
beforeEach(() => {
|
|
130
|
-
// Reset the mocks before each test
|
|
131
|
-
mockDjClient.nodeDimensions.mockReset();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('renders nodes with dimensions', async () => {
|
|
135
|
-
mockDjClient.nodeDimensions.mockReturnValue(mockDimensions);
|
|
136
|
-
render(<NodeDimensionsTab node={mockNode} djClient={mockDjClient} />);
|
|
137
|
-
await waitFor(() => {
|
|
138
|
-
for (const dimension of mockDimensions) {
|
|
139
|
-
const link = screen.getByText(dimension.node_display_name).closest('a');
|
|
140
|
-
expect(link).toHaveAttribute('href', `/nodes/${dimension.node_name}`);
|
|
141
|
-
expect(screen.getByText(dimension.name)).toBeInTheDocument();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|