Skip to content

Commit 9480db5

Browse files
authored
Top-down DAG layout with opinionated zoom, jump bar, styled right sidebar (#222)
* Initial pass at vertical DAG * Update graphQL definitions * Remove align option to get vertically centered DAG * Add compact rendering for nodes with > 4 inputs or outputs * Revert settings.json changes * Update jest tests * Center the minified input/output blocks * Replace panzoom with our own library, basic pan + double click to zoom at two distinct levels * Distinct zoom levels, nav in top bar, detail view on right side * Fix the escape hotkey, detection of current text field input * Clean up React hierarchy, address initial feedback * More cleanup * Fix typescript error with new optional config * Update snapshot tests
1 parent ad149be commit 9480db5

27 files changed

+1253
-1020
lines changed

python_modules/dagit/dagit/webapp/package.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"dependencies": {
66
"@blueprintjs/core": "^3.0.1",
77
"@blueprintjs/icons": "^3.0.0",
8+
"@blueprintjs/select": "^3.2.0",
89
"@types/dagre": "^0.7.40",
910
"@types/prop-types": "^15.5.4",
1011
"@types/react-router": "^4.0.30",
@@ -16,11 +17,11 @@
1617
"@vx/responsive": "^0.0.165",
1718
"@vx/scale": "^0.0.165",
1819
"@vx/shape": "^0.0.170",
20+
"amator": "^1.1.0",
1921
"apollo-boost": "^0.1.12",
2022
"d3-hierarchy": "^1.1.6",
2123
"dagre": "^0.8.2",
2224
"graphql-tag": "^2.9.2",
23-
"panzoom": "^6.1.3",
2425
"react": "^16.4.2",
2526
"react-apollo": "^2.1.9",
2627
"react-dom": "^16.4.2",
@@ -61,12 +62,20 @@
6162
"watch-types": "apollo codegen:generate --queries \"./src/**/*.tsx\" --target typescript types --schema ./src/schema.json --watch"
6263
},
6364
"jest": {
64-
"roots": ["<rootDir>/src"],
65+
"roots": [
66+
"<rootDir>/src"
67+
],
6568
"transform": {
6669
"^.+\\.(ts|tsx)$": "ts-jest",
6770
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/jest/fileTransformer.js"
6871
},
69-
"moduleFileExtensions": ["js", "jsx", "json", "ts", "tsx"],
72+
"moduleFileExtensions": [
73+
"js",
74+
"jsx",
75+
"json",
76+
"ts",
77+
"tsx"
78+
],
7079
"testRegex": "/__tests__/.*\\.test\\.(ts|tsx)$",
7180
"testURL": "http://localhost"
7281
}

python_modules/dagit/dagit/webapp/src/App.tsx

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,80 @@ import gql from "graphql-tag";
33
import { Query, QueryResult } from "react-apollo";
44
import { Route } from "react-router";
55
import { BrowserRouter } from "react-router-dom";
6-
import Page from "./Page";
76
import Loading from "./Loading";
8-
import Pipelines from "./Pipelines";
7+
import PipelineExplorer from "./PipelineExplorer";
8+
import PipelineJumpBar from "./PipelineJumpBar";
99
import { AppQuery } from "./types/AppQuery";
1010

11+
import { Alignment, Navbar, NonIdealState } from "@blueprintjs/core";
12+
import navBarImage from "./images/nav-logo.png";
13+
1114
export default class App extends React.Component {
1215
public render() {
1316
return (
14-
<BrowserRouter>
15-
<Route path="/:pipeline">
16-
{({ match, history }) => (
17-
<Query query={APP_QUERY}>
18-
{(queryResult: QueryResult<AppQuery, any>) => (
19-
<Page>
20-
<Loading queryResult={queryResult}>
21-
{data => (
22-
<Pipelines
23-
selectedPipeline={match ? match.params.pipeline : ""}
24-
pipelines={data.pipelines}
25-
history={history}
26-
/>
27-
)}
28-
</Loading>
29-
</Page>
30-
)}
31-
</Query>
32-
)}
33-
</Route>
34-
</BrowserRouter>
17+
<Query query={APP_QUERY}>
18+
{(queryResult: QueryResult<AppQuery, any>) => (
19+
<Loading queryResult={queryResult}>
20+
{data => (
21+
<BrowserRouter>
22+
<Route path="/:pipeline/:solid?">
23+
{({ match, history }) => {
24+
const selectedPipeline = data.pipelines.find(
25+
p => (match ? p.name === match.params.pipeline : false)
26+
);
27+
const selectedSolid =
28+
selectedPipeline &&
29+
selectedPipeline.solids.find(
30+
s => (match ? s.name === match.params.solid : false)
31+
);
32+
33+
return (
34+
<>
35+
<Navbar>
36+
<Navbar.Group align={Alignment.LEFT}>
37+
<Navbar.Heading>
38+
<img src={navBarImage} style={{ height: 34 }} />
39+
</Navbar.Heading>
40+
<Navbar.Divider />
41+
<PipelineJumpBar
42+
selectedPipeline={selectedPipeline}
43+
selectedSolid={selectedSolid}
44+
pipelines={data.pipelines}
45+
history={history}
46+
/>
47+
</Navbar.Group>
48+
</Navbar>
49+
{selectedPipeline ? (
50+
<PipelineExplorer
51+
pipeline={selectedPipeline}
52+
solid={selectedSolid}
53+
history={history}
54+
/>
55+
) : (
56+
<NonIdealState
57+
title="No pipeline selected"
58+
description="Select a pipeline in the sidebar on the left"
59+
/>
60+
)}
61+
</>
62+
);
63+
}}
64+
</Route>
65+
</BrowserRouter>
66+
)}
67+
</Loading>
68+
)}
69+
</Query>
3570
);
3671
}
3772
}
3873

3974
export const APP_QUERY = gql`
4075
query AppQuery {
4176
pipelines {
42-
...PipelinesFragment
77+
...PipelineFragment
4378
}
4479
}
4580
46-
${Pipelines.fragments.PipelinesFragment}
81+
${PipelineExplorer.fragments.PipelineFragment}
4782
`;

python_modules/dagit/dagit/webapp/src/Breadcrumbs.tsx

Lines changed: 0 additions & 45 deletions
This file was deleted.

python_modules/dagit/dagit/webapp/src/Config.tsx

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import * as React from "react";
22
import gql from "graphql-tag";
33
import styled from "styled-components";
4-
import { UL, H6, Colors } from "@blueprintjs/core";
5-
import SpacedCard from "./SpacedCard";
4+
import { UL, Colors } from "@blueprintjs/core";
65
import TypeWithTooltip from "./TypeWithTooltip";
76
import Description from "./Description";
87
import { ConfigFragment } from "./types/ConfigFragment";
98

109
interface ConfigProps {
11-
config: ConfigFragment | null;
10+
config: ConfigFragment;
1211
}
1312

1413
export default class Config extends React.Component<ConfigProps, {}> {
@@ -61,27 +60,16 @@ export default class Config extends React.Component<ConfigProps, {}> {
6160
}
6261

6362
public render() {
64-
if (this.props.config) {
65-
return (
66-
<ConfigCard elevation={3}>
67-
<H6>Config</H6>
68-
<TypeWithTooltip type={this.props.config.type} />
69-
{this.renderFields(this.props.config)}
70-
</ConfigCard>
71-
);
72-
} else {
73-
return null;
74-
}
63+
return (
64+
<div>
65+
<TypeWithTooltip type={this.props.config.type} />
66+
{this.renderFields(this.props.config)}
67+
</div>
68+
);
7569
}
7670
}
7771

78-
const ConfigCard = styled(SpacedCard)`
79-
width: 400px;
80-
margin-bottom: 10px;
81-
`;
82-
8372
const DescriptionWrapper = styled.div`
84-
max-width: 400px;
8573
margin-top: 10px;
8674
margin-bottom: 10px;
8775
color: ${Colors.GRAY2};

python_modules/dagit/dagit/webapp/src/Description.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import * as React from "react";
2-
import gql from "graphql-tag";
32
import styled from "styled-components";
4-
import { H1, H2, H3, H4, H5, H6, Text, Code, UL, Pre } from "@blueprintjs/core";
53
import * as ReactMarkdown from "react-markdown";
64

75
interface IDescriptionProps {

python_modules/dagit/dagit/webapp/src/Page.tsx

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import * as React from "react";
2+
import styled from "styled-components";
3+
import { Colors } from "@blueprintjs/core";
4+
5+
interface IDividerProps {
6+
onMove: (vw: number) => void;
7+
}
8+
interface IDividerState {
9+
down: boolean;
10+
}
11+
12+
export class PanelDivider extends React.Component<
13+
IDividerProps,
14+
IDividerState
15+
> {
16+
state = {
17+
down: false
18+
};
19+
20+
onMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
21+
this.setState({ down: true });
22+
const onMouseMove = (event: MouseEvent) => {
23+
let vx = (event.clientX * 100) / window.innerWidth;
24+
this.props.onMove(vx);
25+
};
26+
const onMouseUp = (event: MouseEvent) => {
27+
this.setState({ down: false });
28+
document.removeEventListener("mousemove", onMouseMove);
29+
document.removeEventListener("mouseup", onMouseUp);
30+
};
31+
document.addEventListener("mousemove", onMouseMove);
32+
document.addEventListener("mouseup", onMouseUp);
33+
};
34+
35+
render() {
36+
return (
37+
<DividerWrapper down={this.state.down}>
38+
<DividerHitArea onMouseDown={this.onMouseDown} />
39+
</DividerWrapper>
40+
);
41+
}
42+
}
43+
44+
const DividerWrapper = styled.div<{ down: boolean }>`
45+
width: 4px;
46+
background: ${Colors.WHITE};
47+
border-left: 1px solid ${p => (p.down ? Colors.GRAY5 : Colors.LIGHT_GRAY2)};
48+
border-right: 1px solid ${p => (p.down ? Colors.GRAY3 : Colors.GRAY5)};
49+
overflow: visible;
50+
position: relative;
51+
`;
52+
53+
const DividerHitArea = styled.div`
54+
width: 17px;
55+
height: 100%;
56+
z-index: 2;
57+
cursor: ew-resize;
58+
position: relative;
59+
left: -8px;
60+
`;

0 commit comments

Comments
 (0)