diff --git a/dashboard/src/app.tsx b/dashboard/src/app.tsx index a6227455c0..3f5d7fe46d 100644 --- a/dashboard/src/app.tsx +++ b/dashboard/src/app.tsx @@ -24,6 +24,15 @@ import "flexboxgrid/dist/flexboxgrid.min.css" import "./styles/padding-margin-mixin.scss" import { EventProvider } from "./context/events" import { DataProvider } from "./context/data" +import { NavLink } from "./components/links" + +import logo from "./assets/logo.png" + +// Style and align properly +const Logo = styled.img` + height: auto; + width: 80%; +` const SidebarWrapper = styled.div` border-right: 1px solid ${colors.border}; @@ -44,6 +53,11 @@ const App = () => ( overflow-y: hidden; `}> +
+ + + +
+ * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { ChangeEvent } from "react" + +interface Props { + name: string + checked?: boolean + onChange: (event: ChangeEvent) => void +} + +const CheckBox: React.SFC = ({ name, onChange, checked = false }) => { + return ( + + ) +} + +export default CheckBox diff --git a/dashboard/src/components/graph/graph.scss b/dashboard/src/components/graph/graph.scss index 4e19d7eff6..7d4e16e04b 100644 --- a/dashboard/src/components/graph/graph.scss +++ b/dashboard/src/components/graph/graph.scss @@ -2,6 +2,13 @@ g.taskPending > rect { stroke: #f17fcd; stroke-dasharray: 8; + animation: dash 15s linear infinite; + } + + @keyframes dash { + to { + stroke-dashoffset: 1000; + } } g.taskComplete > rect { @@ -27,4 +34,17 @@ stroke: rgba(0,0,0,0.32); stroke-width: 1.5px; } + + div.label-wrap { + text-align: center; + + span.name { + font-weight: bold; + } + span.type { + display: inline-block; + margin-top: 0.5em + } + } + } diff --git a/dashboard/src/components/graph/index.tsx b/dashboard/src/components/graph/index.tsx index a4c7247c6a..b1391cce0b 100644 --- a/dashboard/src/components/graph/index.tsx +++ b/dashboard/src/components/graph/index.tsx @@ -8,8 +8,9 @@ import cls from "classnames" import { css } from "emotion/macro" -import React, { Component } from "react" +import React, { Component, ChangeEvent } from "react" import styled from "@emotion/styled/macro" +import { capitalize, uniq } from "lodash" import * as d3 from "d3" import dagreD3 from "dagre-d3" @@ -59,6 +60,7 @@ function drawChart(graph: Graph, width: number, height: number) { label: node.label, class: "", id: node.id, + labelType: "html", }) } @@ -129,6 +131,7 @@ interface Props { } interface State { + filters: { [key: string]: boolean } nodes: Node[] edges: Edge[] } @@ -146,13 +149,15 @@ const getIdFromTaskKey = (key: string) => { return makeId(name, type) } +// Renders as HTML const makeLabel = (name: string, type: string) => { const nameParts = name.split(".") // test names look like: name.test-name.type if (type === "test") { type += ` (${nameParts[1]})` } - return `${nameParts[0]}\n${type}` + return "
" + + nameParts[0] + "
" + type + "
" } const Span = styled.span` @@ -175,17 +180,34 @@ class Chart extends Component { _edges: Edge[] _chartRef: React.RefObject + state = { + nodes: [], + edges: [], + filters: {}, + } + constructor(props) { super(props) this._chartRef = React.createRef() + this.onCheckboxChange = this.onCheckboxChange.bind(this) + + const taskTypes = uniq(this.props.graph.nodes.map(n => n.type)) + const filters = taskTypes.reduce((acc, type) => { + acc[type] = false + return acc + }, {}) + this.state = { + ...this.state, + filters, + } } componentDidMount() { this.drawChart() // Re-draw graph on **end** of window resize event (hence the timer) - let resizeTimer + let resizeTimer: NodeJS.Timeout window.onresize = () => { clearTimeout(resizeTimer) resizeTimer = setTimeout(() => { @@ -194,6 +216,12 @@ class Chart extends Component { } } + onCheckboxChange({ target }: ChangeEvent) { + this.setState({ + filters: { ...this.state.filters, [target.name]: !target.checked }, + }) + } + drawChart() { const graph = this.makeGraph() this._nodes = graph.nodes @@ -204,30 +232,38 @@ class Chart extends Component { } makeGraph() { - const nodes: Node[] = this.props.graph.nodes.map(n => { - return { - id: makeId(n.name, n.type), - name: n.name, - label: makeLabel(n.name, n.type), - } - }) - const edges: Edge[] = this.props.graph.relationships.map(r => { - const source = r.dependency - const target = r.dependant - return { - source: makeId(source.name, source.type), - target: makeId(target.name, target.type), - type: source.type, - } - }) + const { filters } = this.state + const nodes: Node[] = this.props.graph.nodes + .filter(n => !filters[n.type]) + .map(n => { + return { + id: makeId(n.name, n.type), + name: n.name, + label: makeLabel(n.name, n.type), + } + }) + const edges: Edge[] = this.props.graph.relationships + .filter(n => !filters[n.dependant.type] && !filters[n.dependency.type]) + .map(r => { + const source = r.dependency + const target = r.dependant + return { + source: makeId(source.name, source.type), + target: makeId(target.name, target.type), + type: source.type, + } + }) return { edges, nodes } } - componentDidUpdate(_prevProps: Props) { - const { message } = this.props + componentDidUpdate(_prevProps, prevState: State) { + const message = this.props.message if (message && message.type === "event") { this.updateNodeClass(message) } + if (prevState.filters !== this.state.filters) { + this.drawChart() + } } clearClasses(el: HTMLElement) { @@ -256,13 +292,16 @@ class Chart extends Component { render() { const { message } = this.props + const taskTypes = uniq(this.props.graph.nodes.map(n => n.type)) const chartHeightEstimate = `100vh - 15rem` + let spinner = null let status = "Ready" if (message && message.name !== "taskGraphComplete") { status = "Processing..." spinner = } + return (
@@ -272,14 +311,27 @@ class Chart extends Component { -- Pending Error

+
+ {taskTypes.map(type => ( + + ))} +
+ height: calc(${chartHeightEstimate}); + `} ref={this._chartRef} id="chart">
+ display: flex; + `, "ml-1 pb-1")}> {status} {spinner}
diff --git a/dashboard/src/components/sidebar.tsx b/dashboard/src/components/sidebar.tsx index 1ffc963416..69e37cfcc8 100644 --- a/dashboard/src/components/sidebar.tsx +++ b/dashboard/src/components/sidebar.tsx @@ -14,7 +14,6 @@ import { NavLink } from "./links" import { Page } from "../containers/sidebar" import { colors, fontMedium } from "../styles/variables" -import logo from "../assets/logo.png" interface Props { pages: Page[] @@ -48,12 +47,6 @@ const linkStyle = ` const A = styled.a(linkStyle) const Link = styled(NavLink)(linkStyle) -// Style and align properly -const Logo = styled.img` - height: auto; - width: 80%; -` - class Sidebar extends Component { constructor(props) { @@ -68,11 +61,6 @@ class Sidebar extends Component { render() { return (
-
- - - -