datajunction-ui 0.0.1-rc.1 → 0.0.1-rc.11

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.
Files changed (58) hide show
  1. package/.github/workflows/ci.yml +3 -3
  2. package/.prettierignore +3 -1
  3. package/package.json +16 -8
  4. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +67 -25
  5. package/src/app/components/NamespaceHeader.jsx +4 -13
  6. package/src/app/components/QueryInfo.jsx +109 -0
  7. package/src/app/components/Tab.jsx +1 -8
  8. package/src/app/components/ToggleSwitch.jsx +17 -0
  9. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
  10. package/src/app/components/djgraph/Collapse.jsx +46 -0
  11. package/src/app/components/djgraph/DJNode.jsx +56 -80
  12. package/src/app/components/djgraph/DJNodeColumns.jsx +68 -0
  13. package/src/app/components/djgraph/DJNodeDimensions.jsx +69 -0
  14. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +82 -43
  15. package/src/app/icons/CollapsedIcon.jsx +15 -0
  16. package/src/app/icons/ExpandedIcon.jsx +15 -0
  17. package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
  18. package/src/app/icons/InvalidIcon.jsx +14 -0
  19. package/src/app/icons/PythonIcon.jsx +52 -0
  20. package/src/app/icons/TableIcon.jsx +14 -0
  21. package/src/app/icons/ValidIcon.jsx +14 -0
  22. package/src/app/index.tsx +28 -15
  23. package/src/app/pages/NamespacePage/Explorer.jsx +57 -0
  24. package/src/app/pages/NamespacePage/Loadable.jsx +9 -7
  25. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +39 -17
  26. package/src/app/pages/NamespacePage/index.jsx +90 -28
  27. package/src/app/pages/NodePage/ClientCodePopover.jsx +30 -0
  28. package/src/app/pages/NodePage/Loadable.jsx +9 -7
  29. package/src/app/pages/NodePage/NodeColumnTab.jsx +36 -20
  30. package/src/app/pages/NodePage/NodeGraphTab.jsx +83 -54
  31. package/src/app/pages/NodePage/NodeHistory.jsx +71 -0
  32. package/src/app/pages/NodePage/NodeInfoTab.jsx +132 -49
  33. package/src/app/pages/NodePage/NodeMaterializationTab.jsx +151 -0
  34. package/src/app/pages/NodePage/NodeSQLTab.jsx +100 -0
  35. package/src/app/pages/NodePage/NodeStatus.jsx +14 -20
  36. package/src/app/pages/NodePage/index.jsx +49 -13
  37. package/src/app/pages/Root/index.tsx +5 -0
  38. package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
  39. package/src/app/pages/SQLBuilderPage/index.jsx +344 -0
  40. package/src/app/providers/djclient.jsx +5 -0
  41. package/src/app/services/DJService.js +125 -1
  42. package/src/styles/dag.css +111 -5
  43. package/src/styles/index.css +343 -25
  44. package/tsconfig.json +1 -1
  45. package/webpack.config.js +22 -6
  46. package/.babelrc +0 -4
  47. package/.env.local +0 -4
  48. package/.env.production +0 -1
  49. package/.vscode/extensions.json +0 -7
  50. package/.vscode/launch.json +0 -15
  51. package/.vscode/settings.json +0 -25
  52. package/Dockerfile +0 -7
  53. package/dist/5fa71a03d45dc2e3d61447f3013a303d.png +0 -0
  54. package/dist/index.html +0 -26
  55. package/dist/main.js +0 -23303
  56. package/dist/vendor.js +0 -281
  57. package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
  58. package/src/app/pages/ListNamespacesPage/index.jsx +0 -52
@@ -2,9 +2,9 @@ name: CI/CD
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [master]
5
+ branches: [main]
6
6
  pull_request:
7
- branches: [master]
7
+ branches: [main]
8
8
 
9
9
  jobs:
10
10
  build:
@@ -19,7 +19,7 @@ jobs:
19
19
  uses: actions/checkout@v2
20
20
 
21
21
  - name: Set up Node.js ${{ matrix.node-version }}
22
- uses: actions/setup-node@v3.6.
22
+ uses: actions/setup-node@v3
23
23
  with:
24
24
  node-version: ${{ matrix.node-version }}
25
25
 
package/.prettierignore CHANGED
@@ -1,4 +1,6 @@
1
1
  build/
2
2
  node_modules/
3
3
  package-lock.json
4
- yarn.lock
4
+ yarn.lock
5
+ dist/
6
+ coverage/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datajunction-ui",
3
- "version": "0.0.1-rc.1",
3
+ "version": "0.0.1-rc.11",
4
4
  "description": "DataJunction Metrics Platform UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -32,20 +32,21 @@
32
32
  "@types/react": "^18.0.20",
33
33
  "@types/react-dom": "^18.0.6",
34
34
  "@types/react-redux": "^7.1.24",
35
+ "@types/react-select": "5.0.1",
35
36
  "@types/react-test-renderer": "^18.0.0",
36
37
  "@types/rimraf": "^3.0.2",
37
38
  "@types/shelljs": "^0.8.11",
38
39
  "@types/testing-library__jest-dom": "^5.14.5",
39
40
  "@types/webpack": "^5.28.0",
40
41
  "@types/webpack-env": "^1.18.0",
42
+ "babel-loader": "9.1.2",
41
43
  "chalk": "4.1.2",
44
+ "cronstrue": "2.27.0",
42
45
  "cross-env": "7.0.3",
46
+ "css-loader": "6.7.3",
43
47
  "dagre": "^0.8.5",
44
- "eslint-config-prettier": "8.5.0",
45
- "eslint-plugin-prettier": "4.2.1",
46
- "eslint-plugin-react-hooks": "4.6.0",
47
- "fbemitter": "3.0.0",
48
- "flux": "4.0.4",
48
+ "datajunction": "0.0.1-rc.0",
49
+ "file-loader": "6.2.0",
49
50
  "fontfaceobserver": "2.3.0",
50
51
  "husky": "8.0.1",
51
52
  "i18next": "21.9.2",
@@ -66,6 +67,7 @@
66
67
  "react-redux": "7.2.8",
67
68
  "react-router-dom": "6.3.0",
68
69
  "react-scripts": "5.0.1",
70
+ "react-select": "5.7.3",
69
71
  "react-syntax-highlighter": "^15.5.0",
70
72
  "react-test-renderer": "18.2.0",
71
73
  "reactflow": "^11.7.0",
@@ -76,6 +78,7 @@
76
78
  "serve": "14.0.1",
77
79
  "shelljs": "0.8.5",
78
80
  "sql-formatter": "^12.2.0",
81
+ "style-loader": "3.3.2",
79
82
  "stylelint": "14.12.0",
80
83
  "stylelint-config-recommended": "9.0.0",
81
84
  "ts-loader": "9.4.2",
@@ -83,7 +86,8 @@
83
86
  "typescript": "4.6.4",
84
87
  "web-vitals": "2.1.4",
85
88
  "webpack": "5.81.0",
86
- "webpack-cli": "5.0.2"
89
+ "webpack-cli": "5.0.2",
90
+ "webpack-dev-server": "4.13.3"
87
91
  },
88
92
  "scripts": {
89
93
  "webpack-start": "webpack-dev-server --open",
@@ -102,7 +106,8 @@
102
106
  "generate": "plop --plopfile internals/generators/plopfile.ts",
103
107
  "cleanAndSetup": "ts-node ./internals/scripts/clean.ts",
104
108
  "prettify": "prettier --write",
105
- "extract-messages": "i18next-scanner --config=internals/extractMessages/i18next-scanner.config.js"
109
+ "extract-messages": "i18next-scanner --config=internals/extractMessages/i18next-scanner.config.js",
110
+ "prepublishOnly": "webpack --mode=production"
106
111
  },
107
112
  "eslintConfig": {
108
113
  "extends": "react-app"
@@ -150,6 +155,9 @@
150
155
  },
151
156
  "devDependencies": {
152
157
  "@babel/plugin-proposal-class-properties": "7.18.6",
158
+ "eslint-config-prettier": "8.8.0",
159
+ "eslint-plugin-prettier": "4.2.1",
160
+ "eslint-plugin-react-hooks": "4.6.0",
153
161
  "html-webpack-plugin": "5.5.1",
154
162
  "jest": "^29.5.0",
155
163
  "mini-css-extract-plugin": "2.7.5"
@@ -14,30 +14,72 @@ exports[`<App /> should render and match the snapshot 1`] = `
14
14
  name="description"
15
15
  />
16
16
  </Helmet>
17
- <Routes>
18
- <Route
19
- element={<Unknown />}
20
- path="/"
21
- >
22
- <React.Fragment>
23
- <Route
24
- element={<Unknown />}
25
- path="nodes/:name"
26
- />
27
- <Route
28
- element={<ListNamespacesPage />}
29
- path="namespaces"
30
- />
31
- <Route
32
- element={<Unknown />}
33
- path="namespaces/:namespace"
34
- />
35
- </React.Fragment>
36
- </Route>
37
- <Route
38
- element={<Unknown />}
39
- path="*"
40
- />
41
- </Routes>
17
+ <Context.Provider
18
+ value={
19
+ Object {
20
+ "DataJunctionAPI": Object {
21
+ "clientCode": [Function],
22
+ "columns": [Function],
23
+ "commonDimensions": [Function],
24
+ "compiledSql": [Function],
25
+ "cube": [Function],
26
+ "dag": [Function],
27
+ "data": [Function],
28
+ "downstreams": [Function],
29
+ "history": [Function],
30
+ "lineage": [Function],
31
+ "materializations": [Function],
32
+ "metric": [Function],
33
+ "metrics": [Function],
34
+ "namespace": [Function],
35
+ "namespaces": [Function],
36
+ "node": [Function],
37
+ "node_dag": [Function],
38
+ "revisions": [Function],
39
+ "sql": [Function],
40
+ "sqls": [Function],
41
+ "upstreams": [Function],
42
+ },
43
+ }
44
+ }
45
+ >
46
+ <Routes>
47
+ <Route
48
+ element={<Unknown />}
49
+ path="/"
50
+ >
51
+ <React.Fragment>
52
+ <Route
53
+ path="nodes"
54
+ >
55
+ <Route
56
+ element={<NodePage />}
57
+ path=":name"
58
+ />
59
+ </Route>
60
+ <Route
61
+ element={<NamespacePage />}
62
+ path="/"
63
+ />
64
+ <Route
65
+ path="namespaces"
66
+ >
67
+ <Route
68
+ element={<NamespacePage />}
69
+ path=":namespace"
70
+ />
71
+ </Route>
72
+ <Route
73
+ element={<SQLBuilderPage />}
74
+ path="sql"
75
+ />
76
+ </React.Fragment>
77
+ </Route>
78
+ <Route
79
+ element={<Unknown />}
80
+ path="*"
81
+ />
82
+ </Routes>
83
+ </Context.Provider>
42
84
  </BrowserRouter>
43
85
  `;
@@ -1,4 +1,5 @@
1
1
  import { Component } from 'react';
2
+ import HorizontalHierarchyIcon from '../icons/HorizontalHierarchyIcon';
2
3
 
3
4
  export default class NamespaceHeader extends Component {
4
5
  render() {
@@ -6,7 +7,7 @@ export default class NamespaceHeader extends Component {
6
7
  const namespaceParts = namespace.split('.');
7
8
  const namespaceList = namespaceParts.map((piece, index) => {
8
9
  return (
9
- <li className="breadcrumb-item">
10
+ <li className="breadcrumb-item" key={index}>
10
11
  <a
11
12
  className="link-body-emphasis"
12
13
  href={'/namespaces/' + namespaceParts.slice(0, index + 1).join('.')}
@@ -19,18 +20,8 @@ export default class NamespaceHeader extends Component {
19
20
  return (
20
21
  <ol className="breadcrumb breadcrumb-chevron p-3 bg-body-tertiary rounded-3">
21
22
  <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>
23
+ <a href="/">
24
+ <HorizontalHierarchyIcon />
34
25
  </a>
35
26
  </li>
36
27
  {namespaceList}
@@ -0,0 +1,109 @@
1
+ export default function QueryInfo({
2
+ id,
3
+ state,
4
+ engine_name,
5
+ engine_version,
6
+ errors,
7
+ links,
8
+ output_table,
9
+ scheduled,
10
+ started,
11
+ numRows,
12
+ }) {
13
+ const stateIcon =
14
+ state === 'FINISHED' ? (
15
+ <span className="status__valid status" style={{ alignContent: 'center' }}>
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="25"
19
+ height="25"
20
+ fill="currentColor"
21
+ className="bi bi-check-circle-fill"
22
+ viewBox="0 0 16 16"
23
+ >
24
+ <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
25
+ </svg>
26
+ </span>
27
+ ) : (
28
+ <span
29
+ className="status__invalid status"
30
+ style={{ alignContent: 'center' }}
31
+ >
32
+ <svg
33
+ xmlns="http://www.w3.org/2000/svg"
34
+ width="16"
35
+ height="16"
36
+ fill="currentColor"
37
+ className="bi bi-x-circle-fill"
38
+ viewBox="0 0 16 16"
39
+ >
40
+ <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
41
+ </svg>
42
+ </span>
43
+ );
44
+
45
+ return (
46
+ <div className="table-responsive">
47
+ <table className="card-inner-table table">
48
+ <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
49
+ <tr>
50
+ <th>Query ID</th>
51
+ <th>Engine</th>
52
+ <th>State</th>
53
+ <th>Scheduled</th>
54
+ <th>Started</th>
55
+ <th>Errors</th>
56
+ <th>Links</th>
57
+ <th>Output Table</th>
58
+ <th>Number of Rows</th>
59
+ </tr>
60
+ </thead>
61
+ <tbody>
62
+ <tr>
63
+ <td>
64
+ <span className="rounded-pill badge bg-secondary-soft">{id}</span>
65
+ </td>
66
+ <td>
67
+ <span className="rounded-pill badge bg-secondary-soft">
68
+ {engine_name}
69
+ {' - '}
70
+ {engine_version}
71
+ </span>
72
+ </td>
73
+ <td>{stateIcon}</td>
74
+ <td>{scheduled}</td>
75
+ <td>{started}</td>
76
+ <td>
77
+ {errors.length ? (
78
+ errors.map(e => (
79
+ <p>
80
+ <span className="rounded-pill badge bg-secondary-error">
81
+ {e}
82
+ </span>
83
+ </p>
84
+ ))
85
+ ) : (
86
+ <></>
87
+ )}
88
+ </td>
89
+ <td>
90
+ {links?.length ? (
91
+ links.map(link => (
92
+ <p>
93
+ <a href={link} target="_blank" rel="noreferrer">
94
+ {link}
95
+ </a>
96
+ </p>
97
+ ))
98
+ ) : (
99
+ <></>
100
+ )}
101
+ </td>
102
+ <td>{output_table}</td>
103
+ <td>{numRows}</td>
104
+ </tr>
105
+ </tbody>
106
+ </table>
107
+ </div>
108
+ );
109
+ }
@@ -7,15 +7,8 @@ export default class Tab extends Component {
7
7
  <div className={selectedTab === id ? 'col active' : 'col'}>
8
8
  <div className="header-tabs nav-overflow nav nav-tabs">
9
9
  <div className="nav-item">
10
- <button
11
- id={id}
12
- role="button"
13
- className="nav-link"
14
- tabIndex="0"
15
- onClick={onClick}
16
- >
10
+ <button id={id} className="nav-link" tabIndex="0" onClick={onClick}>
17
11
  {this.props.name}
18
- {/*<span className="rounded-pill badge bg-secondary-soft">823</span>*/}
19
12
  </button>
20
13
  </div>
21
14
  </div>
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ const ToggleSwitch = ({ checked, onChange, toggleName }) => (
4
+ <>
5
+ <input
6
+ id="show-compiled-sql-toggle"
7
+ type="checkbox"
8
+ className="checkbox"
9
+ checked={checked}
10
+ onChange={e => onChange(e.target.checked)}
11
+ />
12
+ <label htmlFor="show-compiled-sql-toggle" className="switch"></label>{' '}
13
+ {toggleName}
14
+ </>
15
+ );
16
+
17
+ export default ToggleSwitch;
@@ -8,25 +8,9 @@ exports[`<NamespaceHeader /> should render and match the snapshot 1`] = `
8
8
  className="breadcrumb-item"
9
9
  >
10
10
  <a
11
- href="/namespaces/"
11
+ href="/"
12
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>
13
+ <HorizontalHierarchyIcon />
30
14
  </a>
31
15
  </li>
32
16
  <li
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { DJNodeDimensions } from './DJNodeDimensions';
3
+ import { DJNodeColumns } from './DJNodeColumns';
4
+
5
+ export default function Collapse({ collapsed, text, data }) {
6
+ const [isCollapsed, setIsCollapsed] = React.useState(collapsed);
7
+
8
+ return (
9
+ <>
10
+ <div className="collapse">
11
+ {data.type === 'metric' ? (
12
+ <button
13
+ className="collapse-button"
14
+ onClick={() => setIsCollapsed(!isCollapsed)}
15
+ >
16
+ {isCollapsed ? '\u25B6 Show' : '\u25BC Hide'} {text}
17
+ </button>
18
+ ) : (
19
+ ''
20
+ )}
21
+ <div
22
+ className={`collapse-content ${
23
+ isCollapsed && data.type === 'metric' ? 'collapsed' : 'expanded'
24
+ }`}
25
+ aria-expanded={isCollapsed}
26
+ >
27
+ {data.type !== 'metric'
28
+ ? isCollapsed
29
+ ? DJNodeColumns({ data: data, limit: 10 })
30
+ : DJNodeColumns({ data: data, limit: 100 })
31
+ : DJNodeDimensions(data)}
32
+ </div>
33
+ {data.type !== 'metric' && data.column_names.length > 10 ? (
34
+ <button
35
+ className="collapse-button"
36
+ onClick={() => setIsCollapsed(!isCollapsed)}
37
+ >
38
+ {isCollapsed ? '\u25B6 More' : '\u25BC Less'} {text}
39
+ </button>
40
+ ) : (
41
+ ''
42
+ )}
43
+ </div>
44
+ </>
45
+ );
46
+ }
@@ -1,81 +1,57 @@
1
1
  import React, { memo } from 'react';
2
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
- }
3
+ import { DJNodeDimensions } from './DJNodeDimensions';
4
+ import Collapse from './Collapse';
20
5
 
21
6
  function capitalize(string) {
22
7
  return string.charAt(0).toUpperCase() + string.slice(1);
23
8
  }
24
9
 
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
10
  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
- ));
11
+ const handleWrapperStyle = {
12
+ display: 'flex',
13
+ position: 'absolute',
14
+ height: '100%',
15
+ flexDirection: 'column',
16
+ top: '50%',
17
+ justifyContent: 'space-between',
18
+ };
19
+ const handleWrapperStyleRight = { ...handleWrapperStyle, ...{ right: 0 } };
70
20
 
21
+ const handleStyle = {
22
+ width: '12px',
23
+ height: '12px',
24
+ borderRadius: '12px',
25
+ background: 'transparent',
26
+ border: '4px solid transparent',
27
+ cursor: 'pointer',
28
+ position: 'absolute',
29
+ top: '0px',
30
+ left: 0,
31
+ };
32
+ const handleStyleLeft = percentage => {
33
+ return {
34
+ ...handleStyle,
35
+ ...{
36
+ transform: 'translate(-' + percentage + '%, -50%)',
37
+ },
38
+ };
39
+ };
40
+ const highlightNodeClass =
41
+ data.is_current === true ? ' dj-node_highlight' : '';
71
42
  return (
72
43
  <>
73
- <Handle
74
- type="target"
75
- position={Position.Left}
76
- style={{ backgroundColor: '#ccc' }}
77
- />
78
- <div className="dj-node__full" style={renderBasedOnDJNodeType(data.type)}>
44
+ <div
45
+ className={'dj-node__full node_type__' + data.type + highlightNodeClass}
46
+ >
47
+ <div style={handleWrapperStyle}>
48
+ <Handle
49
+ type="target"
50
+ id={data.name}
51
+ position={Position.Left}
52
+ style={handleStyleLeft(100)}
53
+ />
54
+ </div>
79
55
  <div className="dj-node__header">
80
56
  <div className="serif">
81
57
  {data.name
@@ -86,24 +62,24 @@ export function DJNode({ id, data }) {
86
62
  </div>
87
63
  <div className="dj-node__body">
88
64
  <b>{capitalize(data.type)}</b>:{' '}
89
- {data.type === 'source' ? data.table : data.display_name}
65
+ <a href={`/nodes/${data.name}`}>
66
+ {data.type === 'source' ? data.table : data.display_name}
67
+ </a>
90
68
  <Collapse
91
69
  collapsed={true}
92
70
  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>
71
+ data={data}
72
+ />
73
+ </div>
74
+ <div style={handleWrapperStyleRight}>
75
+ <Handle
76
+ type="source"
77
+ id={data.name}
78
+ position={Position.Right}
79
+ style={handleStyleLeft(90)}
80
+ />
100
81
  </div>
101
82
  </div>
102
- <Handle
103
- type="source"
104
- position={Position.Right}
105
- style={{ backgroundColor: '#ccc' }}
106
- />
107
83
  </>
108
84
  );
109
85
  }