Skip to content

Commit

Permalink
Merge pull request #751 from garden-io/info-pane-refactor
Browse files Browse the repository at this point in the history
(dashboard) Info pane refactor
  • Loading branch information
eysi09 authored May 6, 2019
2 parents 54d6cd2 + 8bc67d0 commit 9ec4e7e
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 588 deletions.
13 changes: 5 additions & 8 deletions dashboard/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import cls from "classnames"
import { css } from "emotion/macro"
import React, { useContext } from "react"
import styled from "@emotion/styled/macro"
Expand Down Expand Up @@ -112,13 +111,11 @@ const App = () => {
`}
>
<div
className={cls(
css`
background-color: ${colors.grayLight}
flex-grow: 1;
padding: 1rem 1rem 1rem 3rem;
`,
)}
className={css`
background-color: ${colors.grayLight}
flex-grow: 1;
padding: 1rem 1rem 1rem 3rem;
`}
>
<Route exact path="/" component={Overview} />
<Route path="/logs/" component={Logs} />
Expand Down
59 changes: 59 additions & 0 deletions dashboard/src/components/RefreshButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 Garden Technologies, Inc. <[email protected]>
*
* 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 styled from "@emotion/styled/macro"
import React from "react"

import { colors } from "../styles/variables"

interface Props {
onClick: () => void
loading: boolean
}

const Button = styled.div`
padding: 0.3em;
border-radius: 10%;
cursor: pointer;
:active {
opacity: 0.5;
}
`

const Icon = styled.i`
color: ${colors.gardenGray};
font-size: 1.25rem;
:hover {
color: ${colors.gardenPink}
}
:active {
opacity: 0.5;
}
`

const IconLoading = styled(Icon)`
animation spin 0.5s infinite linear;
@keyframes spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
`

export const RefreshButton: React.FC<Props> = ({ loading, onClick }) => {
const IconComp = loading ? IconLoading : Icon

return (
<Button onClick={onClick}>
<IconComp className={"fas fa-redo-alt"} />
</Button>
)
}
7 changes: 2 additions & 5 deletions dashboard/src/components/graph/graph.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}

.node:hover rect, .node.selected rect {
stroke-width: 4.5px;
stroke-width: 4.5px;
}

.edgePath path {
Expand All @@ -52,11 +52,8 @@
background-size: 4rem;
border-radius: 5px;
border:none;
cursor: pointer;

&--run,
&--test {
cursor: pointer;
}
.type {
color: gray;
padding-bottom: 0.2rem;
Expand Down
186 changes: 186 additions & 0 deletions dashboard/src/components/info-pane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright (C) 2018 Garden Technologies, Inc. <[email protected]>
*
* 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 from "react"
import cls from "classnames"
import { capitalize } from "lodash"
import { css } from "emotion/macro"
import styled from "@emotion/styled/macro"
import Card from "../components/card"
import { colors } from "../styles/variables"
import { RenderedNode } from "garden-cli/src/config-graph"
import { RefreshButton } from "./RefreshButton"

export const ErrorTxt = styled.div`
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
position: relative;
padding: 0.75rem 1.25rem;
border: 1px solid transparent;
border-radius: 0.25rem;
`

const Term = styled.div`
background-color: ${colors.gardenBlack};
color: white;
border-radius: 2px;
max-height: 45rem;
overflow-y: auto;
padding: 1rem;
`
const Code = styled.code`
font-size: 0.8rem;
white-space: pre-wrap;
`

const ClosePaneContainer = styled.div`
display: flex;
margin-left: auto;
`
const ClosePane = styled.div`
cursor: pointer;
background-size: contain;
width: 2rem;
height: 2rem;
`

const IconContainer = styled.span`
display: inline-block;
width: 2rem;
height: 2rem;
background-size: contain;
vertical-align: text-top;
background-repeat: no-repeat;
vertical-align: top;
`
interface Props {
node: RenderedNode
clearGraphNodeSelection: () => void
onRefresh?: () => void
loading?: boolean
output?: string | null
startedAt?: string | null
completedAt?: string | null
duration?: string | null
}

const Key = ({ text }) => (
<div
className={cls(css`
font-weight: bold;
`,
"col-xs-5 col-lg-3 pr-1")}
>
{text}
</div>
)

// TODO: Split up into something InfoPane and InfoPaneWithResults. Props are kind of messy.
export const InfoPane: React.FC<Props> = ({
clearGraphNodeSelection,
loading,
onRefresh,
node,
output,
startedAt,
completedAt,
duration,
}) => {
const { name, moduleName, type } = node
let outputEl: React.ReactNode = null

if (output) {
outputEl = (
<Term>
<Code>{output}</Code>
</Term>
)
} else if (output === null) {
// Output explictly set to null means that the data was fetched but the result was empty
outputEl = <ErrorTxt>No test output</ErrorTxt>
}

return (
<Card>
<div className="p-1">
<div className="row">
<div>
<IconContainer className={cls(`garden-icon`, `garden-icon--${type}`)} />
</div>
<div
className={css`
padding-left: 0.5rem;
`}
>
<h2
className={css`
margin-block-end: 0;
`}
>
{name}
</h2>
</div>

<ClosePaneContainer>
{onRefresh && (
<div className={css`margin-right: 1rem;`}>
<RefreshButton onClick={onRefresh} loading={loading || false} />
</div>
)}
<ClosePane
onClick={clearGraphNodeSelection}
className="garden-icon garden-icon--close"
/>
</ClosePaneContainer>
</div>

<div className="row pt-2">
<Key text="Type" />
<div className="col-xs col-lg">
{capitalize(type)}
</div>
</div>

<div className="row pt-1">
<Key text="Module" />
<div className="col-xs col-lg">{moduleName}</div>
</div>

{duration && (
<div className="row pt-1">
<Key text="Duration" />
<div className="col-xs col-lg">{duration}</div>
</div>
)}

{startedAt && (
<div className="row pt-1">
<Key text="Started At" />
<div className="col-xs col-lg">{startedAt}</div>
</div>
)}

{completedAt && (
<div className="row pt-1">
<Key text="Completed At" />
<div className="col-xs col-lg">{completedAt}</div>
</div>
)}

{(type === "test" || type === "run") && (
<div className="row pt-1">
<div className="col-xs-12">
{outputEl}
</div>
</div>
)}
</div>
</Card>
)
}
33 changes: 0 additions & 33 deletions dashboard/src/components/load-wrapper.tsx

This file was deleted.

41 changes: 2 additions & 39 deletions dashboard/src/components/logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { getServiceNames } from "../util/helpers"

import { ServiceLogEntry } from "garden-cli/src/types/plugin/outputs"
import { ConfigDump } from "garden-cli/src/garden"
import { RefreshButton } from "./RefreshButton"

interface Props {
config: ConfigDump
Expand All @@ -39,40 +40,6 @@ const Header = styled.div`
align-items: center;
`

const Button = styled.div`
padding: 0.3em;
border-radius: 10%;
border: 2px solid ${colors.gardenGrayLight};
cursor: pointer;
:hover {
border: 2px solid ${colors.gardenGray};
transition: all 0.3s ease-out;
}
:active {
opacity: 0.5;
}
`

const Icon = styled.i`
color: ${colors.gardenGray};
font-size: 1.25rem;
:active {
opacity: 0.5;
}
`

const IconLoading = styled(Icon)`
animation spin 0.5s infinite linear;
@keyframes spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
`

// TODO: Roll our own Select component instead of using react-select, it's an overkill.
const selectStyles = {
control: (base, state) => ({
Expand Down Expand Up @@ -133,8 +100,6 @@ class Logs extends Component<Props, State> {
const title = value === "all" ? label : `${label} logs`
const filteredLogs = value === "all" ? logs : logs.filter(l => l.serviceName === value)

const IconComp = loading ? IconLoading : Icon

return (
<div>
<div
Expand All @@ -154,9 +119,7 @@ class Logs extends Component<Props, State> {
<div>
<Header className="p-1">
<CardTitle>{title}</CardTitle>
<Button onClick={this.refresh}>
<IconComp className={"fas fa-redo-alt"} />
</Button>
<RefreshButton onClick={this.refresh} loading={loading} />
</Header>
<Terminal
entries={filteredLogs}
Expand Down
Loading

0 comments on commit 9ec4e7e

Please sign in to comment.