datajunction-ui 0.0.1-a1 → 0.0.1-a101
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/Makefile +7 -1
- package/package.json +18 -7
- package/public/index.html +1 -1
- package/src/app/components/AddNodeDropdown.jsx +44 -0
- package/src/app/components/ListGroupItem.jsx +2 -1
- package/src/app/components/NodeListActions.jsx +69 -0
- package/src/app/components/NodeMaterializationDelete.jsx +80 -0
- package/src/app/components/QueryInfo.jsx +96 -1
- package/src/app/components/Search.jsx +94 -0
- package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
- package/src/app/components/__tests__/Search.test.jsx +63 -0
- package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +5 -3
- package/src/app/components/djgraph/Collapse.jsx +3 -2
- package/src/app/components/djgraph/DJNode.jsx +1 -1
- package/src/app/components/djgraph/DJNodeColumns.jsx +5 -1
- package/src/app/components/djgraph/LayoutFlow.jsx +5 -3
- package/src/app/components/forms/Action.jsx +8 -0
- package/src/app/components/forms/NodeNameField.jsx +64 -0
- package/src/app/components/search.css +17 -0
- package/src/app/icons/AddItemIcon.jsx +16 -0
- package/src/app/icons/CommitIcon.jsx +45 -0
- package/src/app/icons/DiffIcon.jsx +63 -0
- package/src/app/icons/EyeIcon.jsx +20 -0
- package/src/app/icons/FilterIcon.jsx +7 -0
- package/src/app/icons/JupyterExportIcon.jsx +25 -0
- package/src/app/icons/LoadingIcon.jsx +10 -10
- package/src/app/icons/PythonIcon.jsx +6 -44
- package/src/app/index.tsx +24 -0
- package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
- package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
- package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
- package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
- package/src/app/pages/AddEditNodePage/FormikSelect.jsx +5 -0
- package/src/app/pages/AddEditNodePage/FullNameField.jsx +3 -2
- package/src/app/pages/AddEditNodePage/Loadable.jsx +6 -2
- package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
- package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
- package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
- package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
- package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +8 -3
- package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
- package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
- package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +15 -9
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +167 -24
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +55 -25
- package/src/app/pages/AddEditNodePage/index.jsx +275 -194
- package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +154 -0
- package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
- package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +77 -0
- package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +405 -0
- package/src/app/pages/CubeBuilderPage/index.jsx +267 -0
- package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +5 -5
- package/src/app/pages/NamespacePage/Explorer.jsx +6 -2
- package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
- package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
- package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
- package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +98 -19
- package/src/app/pages/NamespacePage/index.jsx +272 -89
- package/src/app/pages/NodePage/AddBackfillPopover.jsx +60 -61
- package/src/app/pages/NodePage/AddMaterializationPopover.jsx +104 -51
- package/src/app/pages/NodePage/ClientCodePopover.jsx +73 -25
- package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
- package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
- package/src/app/pages/NodePage/LinkDimensionPopover.jsx +38 -23
- package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
- package/src/app/pages/NodePage/NodeColumnTab.jsx +183 -113
- package/src/app/pages/NodePage/NodeDependenciesTab.jsx +153 -0
- package/src/app/pages/NodePage/NodeGraphTab.jsx +56 -29
- package/src/app/pages/NodePage/NodeHistory.jsx +165 -161
- package/src/app/pages/NodePage/NodeInfoTab.jsx +148 -14
- package/src/app/pages/NodePage/NodeMaterializationTab.jsx +201 -104
- package/src/app/pages/NodePage/NodeStatus.jsx +96 -21
- package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
- package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
- package/src/app/pages/NodePage/PartitionColumnPopover.jsx +3 -5
- package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
- package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
- package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
- package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +13 -4
- package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
- package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
- package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
- package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +10 -14
- package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
- package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +151 -0
- package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +6 -2
- package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +3 -2
- package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +148 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +159 -57
- package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +2 -386
- package/src/app/pages/NodePage/index.jsx +94 -57
- package/src/app/pages/Root/__tests__/index.test.jsx +3 -1
- package/src/app/pages/Root/index.tsx +62 -12
- package/src/app/services/DJService.js +587 -55
- package/src/app/services/__tests__/DJService.test.jsx +382 -45
- package/src/index.tsx +1 -0
- package/src/mocks/mockNodes.jsx +265 -227
- package/src/styles/dag.css +4 -2
- package/src/styles/index.css +474 -10
- package/src/styles/loading.css +1 -1
- package/src/styles/node-creation.scss +84 -5
- package/src/styles/node-list.css +4 -0
- package/src/styles/sorted-table.css +15 -0
- package/src/app/components/DeleteNode.jsx +0 -55
- package/src/app/components/__tests__/DeleteNode.test.jsx +0 -53
- package/src/app/pages/NodePage/NodeSQLTab.jsx +0 -82
- package/src/app/pages/NodePage/__tests__/ClientCodePopover.test.jsx +0 -49
|
@@ -19,9 +19,11 @@ exports[`<ListGroupItem /> should render and match the snapshot 1`] = `
|
|
|
19
19
|
className="mb-0 opacity-75"
|
|
20
20
|
role="dialog"
|
|
21
21
|
>
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
<Markdown>
|
|
23
|
+
<span>
|
|
24
|
+
Something
|
|
25
|
+
</span>
|
|
26
|
+
</Markdown>
|
|
25
27
|
</p>
|
|
26
28
|
</div>
|
|
27
29
|
</div>
|
|
@@ -5,6 +5,7 @@ import { DJNodeColumns } from './DJNodeColumns';
|
|
|
5
5
|
export default function Collapse({ collapsed, text, data }) {
|
|
6
6
|
const [isCollapsed, setIsCollapsed] = React.useState(collapsed);
|
|
7
7
|
|
|
8
|
+
const limit = 5;
|
|
8
9
|
return (
|
|
9
10
|
<>
|
|
10
11
|
<div className="collapse">
|
|
@@ -26,11 +27,11 @@ export default function Collapse({ collapsed, text, data }) {
|
|
|
26
27
|
>
|
|
27
28
|
{data.type !== 'metric'
|
|
28
29
|
? isCollapsed
|
|
29
|
-
? DJNodeColumns({ data: data, limit:
|
|
30
|
+
? DJNodeColumns({ data: data, limit: limit })
|
|
30
31
|
: DJNodeColumns({ data: data, limit: 100 })
|
|
31
32
|
: DJNodeDimensions(data)}
|
|
32
33
|
</div>
|
|
33
|
-
{data.type !== 'metric' && data.column_names.length >
|
|
34
|
+
{data.type !== 'metric' && data.column_names.length > limit ? (
|
|
34
35
|
<button
|
|
35
36
|
className="collapse-button"
|
|
36
37
|
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
@@ -68,7 +68,7 @@ export function DJNode({ id, data }) {
|
|
|
68
68
|
{data.type === 'source' ? data.table : data.display_name}
|
|
69
69
|
</a>
|
|
70
70
|
<Collapse
|
|
71
|
-
collapsed={true}
|
|
71
|
+
collapsed={data.is_current && data.type != 'metric' ? false : true}
|
|
72
72
|
text={data.type !== 'metric' ? 'columns' : 'dimensions'}
|
|
73
73
|
data={data}
|
|
74
74
|
/>
|
|
@@ -33,7 +33,11 @@ export function DJNodeColumns({ data, limit }) {
|
|
|
33
33
|
};
|
|
34
34
|
return data.column_names.slice(0, limit).map(col => (
|
|
35
35
|
<div
|
|
36
|
-
className={
|
|
36
|
+
className={
|
|
37
|
+
'custom-node-subheader node_type__' +
|
|
38
|
+
data.type +
|
|
39
|
+
(col.order <= 0 ? ' custom-node-emphasis' : '')
|
|
40
|
+
}
|
|
37
41
|
key={`${data.name}.${col.name}`}
|
|
38
42
|
>
|
|
39
43
|
<div style={handleWrapperStyle}>
|
|
@@ -32,8 +32,10 @@ const getLayoutedElements = (
|
|
|
32
32
|
const nodeHeightTracker = {};
|
|
33
33
|
|
|
34
34
|
nodes.forEach(node => {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const minColumnsLength = node.data.column_names.filter(
|
|
36
|
+
col => col.order > 0,
|
|
37
|
+
).length;
|
|
38
|
+
nodeHeightTracker[node.id] = Math.min(minColumnsLength, 5) * 40 + 250;
|
|
37
39
|
dagreGraph.setNode(node.id, {
|
|
38
40
|
width: nodeWidth,
|
|
39
41
|
height: nodeHeightTracker[node.id],
|
|
@@ -52,7 +54,7 @@ const getLayoutedElements = (
|
|
|
52
54
|
node.sourcePosition = isHorizontal ? 'right' : 'bottom';
|
|
53
55
|
node.position = {
|
|
54
56
|
x: nodeWithPosition.x - nodeWidth / 2,
|
|
55
|
-
y: nodeWithPosition.y - nodeHeightTracker[node.id] /
|
|
57
|
+
y: nodeWithPosition.y - nodeHeightTracker[node.id] / 3,
|
|
56
58
|
};
|
|
57
59
|
node.width = nodeWidth;
|
|
58
60
|
node.height = nodeHeightTracker[node.id];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ErrorMessage, Field } from 'formik';
|
|
2
|
+
import { FormikSelect } from '../../pages/AddEditNodePage/FormikSelect';
|
|
3
|
+
import { FullNameField } from '../../pages/AddEditNodePage/FullNameField';
|
|
4
|
+
import React, { useContext, useEffect, useState } from 'react';
|
|
5
|
+
import DJClientContext from '../../providers/djclient';
|
|
6
|
+
import { useParams } from 'react-router-dom';
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* This component creates the namespace selector, display name input, and
|
|
10
|
+
* derived name fields in a form. It can be reused any time we need to create
|
|
11
|
+
* a new node.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default function NodeNameField() {
|
|
15
|
+
const [namespaces, setNamespaces] = useState([]);
|
|
16
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
17
|
+
let { initialNamespace } = useParams();
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const fetchData = async () => {
|
|
21
|
+
const namespaces = await djClient.namespaces();
|
|
22
|
+
setNamespaces(
|
|
23
|
+
namespaces.map(m => ({
|
|
24
|
+
value: m['namespace'],
|
|
25
|
+
label: m['namespace'],
|
|
26
|
+
})),
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
fetchData().catch(console.error);
|
|
30
|
+
}, [djClient, djClient.metrics]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<div className="NamespaceInput">
|
|
35
|
+
<ErrorMessage name="namespace" component="span" />
|
|
36
|
+
<label htmlFor="namespace">Namespace *</label>
|
|
37
|
+
<FormikSelect
|
|
38
|
+
selectOptions={namespaces}
|
|
39
|
+
formikFieldName="namespace"
|
|
40
|
+
placeholder="Choose Namespace"
|
|
41
|
+
defaultValue={{
|
|
42
|
+
value: initialNamespace,
|
|
43
|
+
label: initialNamespace,
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
<div className="DisplayNameInput NodeCreationInput">
|
|
48
|
+
<ErrorMessage name="display_name" component="span" />
|
|
49
|
+
<label htmlFor="displayName">Display Name *</label>
|
|
50
|
+
<Field
|
|
51
|
+
type="text"
|
|
52
|
+
name="display_name"
|
|
53
|
+
id="displayName"
|
|
54
|
+
placeholder="Human readable display name"
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="FullNameInput NodeCreationInput">
|
|
58
|
+
<ErrorMessage name="name" component="span" />
|
|
59
|
+
<label htmlFor="FullName">Full Name</label>
|
|
60
|
+
<FullNameField type="text" name="name" />
|
|
61
|
+
</div>
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.search-box {
|
|
2
|
+
display: flex;
|
|
3
|
+
height: 50%;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.search-results {
|
|
7
|
+
position: absolute;
|
|
8
|
+
z-index: 1000;
|
|
9
|
+
width: 75%;
|
|
10
|
+
background-color: rgba(244, 244, 244, 0.8);
|
|
11
|
+
border-radius: 1rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.search-result-item {
|
|
15
|
+
text-decoration: wavy;
|
|
16
|
+
padding: 0.5rem;
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const AddItemIcon = props => (
|
|
2
|
+
<svg
|
|
3
|
+
enableBackground="new 0 0 512 512"
|
|
4
|
+
height="20px"
|
|
5
|
+
id="Layer_1"
|
|
6
|
+
version="1.1"
|
|
7
|
+
viewBox="0 0 512 512"
|
|
8
|
+
width="20px"
|
|
9
|
+
xmlSpace="preserve"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
12
|
+
>
|
|
13
|
+
<path d="M256,512C114.625,512,0,397.391,0,256C0,114.609,114.625,0,256,0c141.391,0,256,114.609,256,256 C512,397.391,397.391,512,256,512z M256,64C149.969,64,64,149.969,64,256s85.969,192,192,192c106.047,0,192-85.969,192-192 S362.047,64,256,64z M288,384h-64v-96h-96v-64h96v-96h64v96h96v64h-96V384z" />
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
export default AddItemIcon;
|
|
@@ -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;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const EyeIcon = props => (
|
|
2
|
+
<svg
|
|
3
|
+
className="feather feather-eye"
|
|
4
|
+
fill="none"
|
|
5
|
+
height="24"
|
|
6
|
+
width="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
stroke="currentColor"
|
|
9
|
+
strokeWidth="2"
|
|
10
|
+
strokeLinecap="round"
|
|
11
|
+
strokeLinejoin="round"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
16
|
+
<circle cx="12" cy="12" r="3" />
|
|
17
|
+
</svg>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default EyeIcon;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const JupyterExportIcon = props => (
|
|
2
|
+
<svg
|
|
3
|
+
className="feather feather-jupyter-export"
|
|
4
|
+
fill="none"
|
|
5
|
+
height="24"
|
|
6
|
+
width="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
stroke="currentColor"
|
|
9
|
+
strokeWidth="2"
|
|
10
|
+
strokeLinecap="round"
|
|
11
|
+
strokeLinejoin="round"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
{/* Notebook outline */}
|
|
16
|
+
<rect x="3" y="2" width="14" height="20" rx="2" ry="2" />
|
|
17
|
+
|
|
18
|
+
{/* Notebook lines */}
|
|
19
|
+
<line x1="7" y1="6" x2="13" y2="6" />
|
|
20
|
+
<line x1="7" y1="10" x2="13" y2="10" />
|
|
21
|
+
<line x1="7" y1="14" x2="13" y2="14" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export default JupyterExportIcon;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import '../../styles/loading.css';
|
|
2
2
|
|
|
3
|
-
export default function LoadingIcon() {
|
|
4
|
-
|
|
5
|
-
<
|
|
6
|
-
<div
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</div>
|
|
12
|
-
</center>
|
|
3
|
+
export default function LoadingIcon({ centered = true }) {
|
|
4
|
+
const content = (
|
|
5
|
+
<div className="lds-ring">
|
|
6
|
+
<div></div>
|
|
7
|
+
<div></div>
|
|
8
|
+
<div></div>
|
|
9
|
+
<div></div>
|
|
10
|
+
</div>
|
|
13
11
|
);
|
|
12
|
+
|
|
13
|
+
return centered ? <center>{content}</center> : content;
|
|
14
14
|
}
|
|
@@ -1,51 +1,13 @@
|
|
|
1
1
|
const PythonIcon = props => (
|
|
2
2
|
<svg
|
|
3
|
-
width="45px"
|
|
4
|
-
height="45px"
|
|
5
|
-
viewBox="0 0 64 64"
|
|
6
|
-
fill="none"
|
|
7
3
|
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
x="0px"
|
|
5
|
+
y="0px"
|
|
6
|
+
width="24"
|
|
7
|
+
height="24"
|
|
8
|
+
viewBox="0 0 48 48"
|
|
8
9
|
>
|
|
9
|
-
<
|
|
10
|
-
<g
|
|
11
|
-
id="SVGRepo_tracerCarrier"
|
|
12
|
-
strokeLinecap="round"
|
|
13
|
-
strokeLinejoin="round"
|
|
14
|
-
></g>
|
|
15
|
-
<g id="SVGRepo_iconCarrier">
|
|
16
|
-
<path
|
|
17
|
-
d="M31.885 16c-8.124 0-7.617 3.523-7.617 3.523l.01 3.65h7.752v1.095H21.197S16 23.678 16 31.876c0 8.196 4.537 7.906 4.537 7.906h2.708v-3.804s-.146-4.537 4.465-4.537h7.688s4.32.07 4.32-4.175v-7.019S40.374 16 31.885 16zm-4.275 2.454c.771 0 1.395.624 1.395 1.395s-.624 1.395-1.395 1.395a1.393 1.393 0 0 1-1.395-1.395c0-.771.624-1.395 1.395-1.395z"
|
|
18
|
-
fill="url(#a)"
|
|
19
|
-
></path>
|
|
20
|
-
<path
|
|
21
|
-
d="M32.115 47.833c8.124 0 7.617-3.523 7.617-3.523l-.01-3.65H31.97v-1.095h10.832S48 40.155 48 31.958c0-8.197-4.537-7.906-4.537-7.906h-2.708v3.803s.146 4.537-4.465 4.537h-7.688s-4.32-.07-4.32 4.175v7.019s-.656 4.247 7.833 4.247zm4.275-2.454a1.393 1.393 0 0 1-1.395-1.395c0-.77.624-1.394 1.395-1.394s1.395.623 1.395 1.394c0 .772-.624 1.395-1.395 1.395z"
|
|
22
|
-
fill="url(#b)"
|
|
23
|
-
></path>
|
|
24
|
-
<defs>
|
|
25
|
-
<linearGradient
|
|
26
|
-
id="a"
|
|
27
|
-
x1="19.075"
|
|
28
|
-
y1="18.782"
|
|
29
|
-
x2="34.898"
|
|
30
|
-
y2="34.658"
|
|
31
|
-
gradientUnits="userSpaceOnUse"
|
|
32
|
-
>
|
|
33
|
-
<stop stopColor="#387EB8"></stop>
|
|
34
|
-
<stop offset="1" stopColor="#366994"></stop>
|
|
35
|
-
</linearGradient>
|
|
36
|
-
<linearGradient
|
|
37
|
-
id="b"
|
|
38
|
-
x1="28.809"
|
|
39
|
-
y1="28.882"
|
|
40
|
-
x2="45.803"
|
|
41
|
-
y2="45.163"
|
|
42
|
-
gradientUnits="userSpaceOnUse"
|
|
43
|
-
>
|
|
44
|
-
<stop stopColor="#FFE052"></stop>
|
|
45
|
-
<stop offset="1" stopColor="#FFC331"></stop>
|
|
46
|
-
</linearGradient>
|
|
47
|
-
</defs>
|
|
48
|
-
</g>
|
|
10
|
+
<path d="M 24 3 C 20.271429 3 18.240267 3.9470561 16.792969 4.5742188 L 16.791016 4.5742188 C 15.488673 5.1421213 14.704771 6.3187748 14.365234 7.4296875 C 14.025697 8.5406002 14 9.6506515 14 10.640625 L 14 14 L 10.640625 14 C 9.6506515 14 8.5406002 14.0257 7.4296875 14.365234 C 6.3187748 14.704771 5.1421212 15.488626 4.5742188 16.791016 L 4.5742188 16.792969 C 3.947056 18.24022 3 20.271429 3 24 C 3 27.728571 3.9470561 29.759733 4.5742188 31.207031 L 4.5742188 31.208984 C 5.1421212 32.511374 6.3187748 33.295229 7.4296875 33.634766 C 8.5406002 33.974256 9.6506515 34 10.640625 34 L 14 34 L 14 37.359375 C 14 38.349349 14.0257 39.459401 14.365234 40.570312 C 14.704771 41.681225 15.488626 42.857879 16.791016 43.425781 L 16.792969 43.425781 C 18.24022 44.052944 20.271429 45 24 45 C 27.728571 45 29.759733 44.052944 31.207031 43.425781 L 31.208984 43.425781 C 32.511374 42.857879 33.295229 41.681225 33.634766 40.570312 C 33.974256 39.459401 34 38.349349 34 37.359375 L 34 34 L 37.359375 34 C 38.349349 34 39.459401 33.9743 40.570312 33.634766 C 41.681225 33.295229 42.857879 32.511374 43.425781 31.208984 L 43.425781 31.207031 C 44.052944 29.75978 45 27.728571 45 24 C 45 20.271429 44.052944 18.240267 43.425781 16.792969 L 43.425781 16.791016 C 42.857879 15.488673 41.681225 14.704771 40.570312 14.365234 C 39.459401 14.025697 38.349349 14 37.359375 14 L 34 14 L 34 10.640625 C 34 9.6506515 33.974303 8.5406002 33.634766 7.4296875 C 33.295229 6.3187748 32.511374 5.1421213 31.208984 4.5742188 L 31.207031 4.5742188 C 29.75978 3.9470561 27.728571 3 24 3 z M 24 6 C 27.268623 6 28.459017 6.6519922 30.009766 7.3242188 C 30.427376 7.5063161 30.590162 7.7325533 30.765625 8.3066406 C 30.941088 8.8807279 31 9.7405985 31 10.640625 L 31 15.253906 A 1.50015 1.50015 0 0 0 31 15.740234 L 31 19 C 31 20.950062 29.450062 22.5 27.5 22.5 L 20.5 22.5 C 16.928062 22.5 14 25.428062 14 29 L 14 31 L 10.640625 31 C 9.7405985 31 8.8807279 30.941088 8.3066406 30.765625 C 7.7325533 30.590162 7.5063162 30.427376 7.3242188 30.009766 C 6.6519921 28.459017 6 27.268623 6 24 C 6 20.731377 6.6519921 19.540983 7.3242188 17.990234 C 7.5063161 17.572624 7.7325533 17.409838 8.3066406 17.234375 C 8.8807279 17.058912 9.7405985 17 10.640625 17 L 23.5 17 A 1.50015 1.50015 0 1 0 23.5 14 L 17 14 L 17 10.640625 C 17 9.7405985 17.058912 8.8807279 17.234375 8.3066406 C 17.409838 7.7325533 17.572624 7.5063162 17.990234 7.3242188 C 19.540983 6.6519921 20.731377 6 24 6 z M 20.5 9 A 1.5 1.5 0 0 0 20.5 12 A 1.5 1.5 0 0 0 20.5 9 z M 34 17 L 37.359375 17 C 38.259401 17 39.119272 17.05891 39.693359 17.234375 C 40.267447 17.409838 40.493684 17.572624 40.675781 17.990234 C 41.348008 19.540983 42 20.731377 42 24 C 42 27.268623 41.348008 28.459017 40.675781 30.009766 C 40.493684 30.427376 40.267447 30.590162 39.693359 30.765625 C 39.119272 30.941088 38.259401 31 37.359375 31 L 24.5 31 A 1.50015 1.50015 0 1 0 24.5 34 L 31 34 L 31 37.359375 C 31 38.259401 30.94109 39.119272 30.765625 39.693359 C 30.590162 40.267447 30.427376 40.493684 30.009766 40.675781 C 28.459017 41.348008 27.268623 42 24 42 C 20.731377 42 19.540983 41.348008 17.990234 40.675781 C 17.572624 40.493684 17.409838 40.267447 17.234375 39.693359 C 17.058912 39.119272 17 38.259401 17 37.359375 L 17 29 C 17 27.049938 18.549938 25.5 20.5 25.5 L 27.5 25.5 C 31.071938 25.5 34 22.571938 34 19 L 34 17 z M 27.5 36 A 1.5 1.5 0 0 0 27.5 39 A 1.5 1.5 0 0 0 27.5 36 z"></path>
|
|
49
11
|
</svg>
|
|
50
12
|
);
|
|
51
13
|
|
package/src/app/index.tsx
CHANGED
|
@@ -9,7 +9,9 @@ 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';
|
|
14
|
+
import { CubeBuilderPage } from './pages/CubeBuilderPage/Loadable';
|
|
13
15
|
import { TagPage } from './pages/TagPage/Loadable';
|
|
14
16
|
import { AddEditNodePage } from './pages/AddEditNodePage/Loadable';
|
|
15
17
|
import { AddEditTagPage } from './pages/AddEditTagPage/Loadable';
|
|
@@ -52,6 +54,16 @@ export function App() {
|
|
|
52
54
|
key="edit"
|
|
53
55
|
element={<AddEditNodePage />}
|
|
54
56
|
/>
|
|
57
|
+
<Route
|
|
58
|
+
path=":name/edit-cube"
|
|
59
|
+
key="edit-cube"
|
|
60
|
+
element={<CubeBuilderPage />}
|
|
61
|
+
/>
|
|
62
|
+
<Route
|
|
63
|
+
path=":name/revisions/:revision"
|
|
64
|
+
element={<RevisionDiff />}
|
|
65
|
+
/>
|
|
66
|
+
<Route path=":name/:tab" element={<NodePage />} />
|
|
55
67
|
</Route>
|
|
56
68
|
|
|
57
69
|
<Route path="/" element={<NamespacePage />} key="index" />
|
|
@@ -72,6 +84,18 @@ export function App() {
|
|
|
72
84
|
key="register"
|
|
73
85
|
element={<RegisterTablePage />}
|
|
74
86
|
></Route>
|
|
87
|
+
<Route path="/create/cube">
|
|
88
|
+
<Route
|
|
89
|
+
path=":initialNamespace"
|
|
90
|
+
key="create"
|
|
91
|
+
element={<CubeBuilderPage />}
|
|
92
|
+
/>
|
|
93
|
+
<Route
|
|
94
|
+
path=""
|
|
95
|
+
key="create"
|
|
96
|
+
element={<CubeBuilderPage />}
|
|
97
|
+
/>
|
|
98
|
+
</Route>
|
|
75
99
|
<Route path="create/:nodeType">
|
|
76
100
|
<Route
|
|
77
101
|
path=":initialNamespace"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component for selecting node columns based on the current form state
|
|
3
|
+
*/
|
|
4
|
+
import { ErrorMessage, useFormikContext } from 'formik';
|
|
5
|
+
import { useContext, useMemo, useState, useEffect } from 'react';
|
|
6
|
+
import DJClientContext from '../../providers/djclient';
|
|
7
|
+
import { FormikSelect } from './FormikSelect';
|
|
8
|
+
|
|
9
|
+
export const ColumnsSelect = ({
|
|
10
|
+
defaultValue,
|
|
11
|
+
fieldName,
|
|
12
|
+
label,
|
|
13
|
+
labelStyle = {},
|
|
14
|
+
isMulti = false,
|
|
15
|
+
isClearable = true,
|
|
16
|
+
}) => {
|
|
17
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
18
|
+
|
|
19
|
+
// Used to pull out current form values for node validation
|
|
20
|
+
const { values } = useFormikContext();
|
|
21
|
+
|
|
22
|
+
// The available columns, determined from validating the node query
|
|
23
|
+
const [availableColumns, setAvailableColumns] = useState([]);
|
|
24
|
+
const selectableOptions = useMemo(() => {
|
|
25
|
+
if (availableColumns && availableColumns.length > 0) {
|
|
26
|
+
return availableColumns;
|
|
27
|
+
}
|
|
28
|
+
}, [availableColumns]);
|
|
29
|
+
|
|
30
|
+
// Fetch columns by validating the latest node query
|
|
31
|
+
const fetchColumns = async () => {
|
|
32
|
+
try {
|
|
33
|
+
const { status, json } = await djClient.validateNode(
|
|
34
|
+
values.type,
|
|
35
|
+
values.name,
|
|
36
|
+
values.display_name,
|
|
37
|
+
values.description,
|
|
38
|
+
values.query,
|
|
39
|
+
);
|
|
40
|
+
if (json?.columns) {
|
|
41
|
+
setAvailableColumns(
|
|
42
|
+
json.columns.map(col => ({ value: col.name, label: col.name })),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Error fetching columns:', error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
fetchColumns();
|
|
52
|
+
}, [values.type, values.name, values.query]);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="CubeCreationInput">
|
|
56
|
+
<ErrorMessage name={fieldName} component="span" />
|
|
57
|
+
<label htmlFor={fieldName} style={labelStyle}>
|
|
58
|
+
{label}
|
|
59
|
+
</label>
|
|
60
|
+
<span data-testid={`select-${fieldName}`}>
|
|
61
|
+
<FormikSelect
|
|
62
|
+
className={isMulti ? 'MultiSelectInput' : 'SelectInput'}
|
|
63
|
+
defaultValue={
|
|
64
|
+
isMulti
|
|
65
|
+
? defaultValue.map(val => {
|
|
66
|
+
return {
|
|
67
|
+
value: val,
|
|
68
|
+
label: val,
|
|
69
|
+
};
|
|
70
|
+
})
|
|
71
|
+
: defaultValue
|
|
72
|
+
? { value: defaultValue, label: defaultValue }
|
|
73
|
+
: null
|
|
74
|
+
}
|
|
75
|
+
selectOptions={selectableOptions}
|
|
76
|
+
formikFieldName={fieldName}
|
|
77
|
+
onFocus={event => fetchColumns(event)}
|
|
78
|
+
isMulti={isMulti}
|
|
79
|
+
isClearable={isClearable}
|
|
80
|
+
/>
|
|
81
|
+
</span>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ErrorMessage, Field } from 'formik';
|
|
2
|
+
|
|
3
|
+
export const DescriptionField = () => {
|
|
4
|
+
return (
|
|
5
|
+
<div className="DescriptionInput NodeCreationInput">
|
|
6
|
+
<ErrorMessage name="description" component="span" />
|
|
7
|
+
<label htmlFor="Description">Description</label>
|
|
8
|
+
<Field
|
|
9
|
+
type="textarea"
|
|
10
|
+
as="textarea"
|
|
11
|
+
name="description"
|
|
12
|
+
id="Description"
|
|
13
|
+
placeholder="Describe your node"
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ErrorMessage, Field } from 'formik';
|
|
2
|
+
|
|
3
|
+
export const DisplayNameField = () => {
|
|
4
|
+
return (
|
|
5
|
+
<div className="DisplayNameInput NodeCreationInput">
|
|
6
|
+
<ErrorMessage name="display_name" component="span" />
|
|
7
|
+
<label htmlFor="displayName">Display Name *</label>
|
|
8
|
+
<Field
|
|
9
|
+
type="text"
|
|
10
|
+
name="display_name"
|
|
11
|
+
id="displayName"
|
|
12
|
+
placeholder="Human readable display name"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -12,6 +12,8 @@ export const FormikSelect = ({
|
|
|
12
12
|
style,
|
|
13
13
|
className = 'SelectInput',
|
|
14
14
|
isMulti = false,
|
|
15
|
+
isClearable = false,
|
|
16
|
+
onFocus = event => {},
|
|
15
17
|
}) => {
|
|
16
18
|
// eslint-disable-next-line no-unused-vars
|
|
17
19
|
const [field, _, helpers] = useField(formikFieldName);
|
|
@@ -37,6 +39,9 @@ export const FormikSelect = ({
|
|
|
37
39
|
onChange={selected => setValue(getValue(selected))}
|
|
38
40
|
styles={style}
|
|
39
41
|
isMulti={isMulti}
|
|
42
|
+
isClearable={isClearable}
|
|
43
|
+
onFocus={event => onFocus(event)}
|
|
44
|
+
id={field.name}
|
|
40
45
|
/>
|
|
41
46
|
);
|
|
42
47
|
};
|
|
@@ -11,12 +11,13 @@ export const FullNameField = props => {
|
|
|
11
11
|
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
// Set the value of the node's full name based on its namespace and display name
|
|
14
|
-
if (values.namespace
|
|
14
|
+
if (values.namespace || values.display_name) {
|
|
15
15
|
setFieldValue(
|
|
16
16
|
props.name,
|
|
17
17
|
`${values.namespace}.${values.display_name
|
|
18
18
|
.toLowerCase()
|
|
19
|
-
.replace(/ /g, '_')
|
|
19
|
+
.replace(/ /g, '_')
|
|
20
|
+
.replace(/[^a-zA-Z0-9_]/g, '')}` || '',
|
|
20
21
|
);
|
|
21
22
|
}
|
|
22
23
|
}, [setFieldValue, props.name, values]);
|