Skip to content

Commit

Permalink
Merge pull request #1102 from garden-io/dashboard-normalize-store-v2
Browse files Browse the repository at this point in the history
Normalize Dashboard data store
  • Loading branch information
eysi09 authored Sep 17, 2019
2 parents 6c27c6c + e9be60e commit ce9a2fb
Show file tree
Hide file tree
Showing 35 changed files with 1,692 additions and 1,044 deletions.
6 changes: 2 additions & 4 deletions dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,12 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo

### Structure

The app is structured into presentational components (`src/components`), container components (`src/container`), and provider/consumer components (`src/context`).
The app is structured into presentational components (`src/components`), container components (`src/container`), and context components (`src/contexts`).

**Presentational components:** These are re-usable UI components. They receive outside data as props and have minimal state.

**Container components:** These load data and pass to the presentational components. A container might call the API directly or obtain the data from a consumer component (or both).

**Provider/consumer components:** These are re-usable components that contain "global" data that needs to be accessible by many (presentational) components in the tree. The provider/consumer pattern is a part of the new [React context API](https://reactjs.org/docs/context.html).
**Context components:** [A Context](https://reactjs.org/docs/context.html) contains "global" state that needs to be accessible by other components down the tree. The context components use the [React Hooks API](https://reactjs.org/docs/hooks-intro.html) to create actions and manage the state that gets passed down.

Maintaining this separation will make it easier to migrate to different state management patterns/tools as the app evolves.

We also use the new [React Hooks API](https://reactjs.org/docs/hooks-intro.html) to manage data and state.
13 changes: 9 additions & 4 deletions dashboard/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"flexboxgrid-helpers": "^1.1.3",
"garden-service": "^0.10.9",
"http-proxy-middleware": "^0.19.1",
"immer": "^3.1.3",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"node-sass": "^4.10.0",
Expand All @@ -43,7 +44,7 @@
"uuid": "^3.3.2"
},
"scripts": {
"start": "react-scripts start",
"start": "CI=true react-scripts start",
"build": "react-scripts build && ./bin/copy-to-static",
"dev": "npm run start",
"test-watch": "react-scripts test",
Expand Down
34 changes: 22 additions & 12 deletions dashboard/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ import { TestResultOutput } from "garden-service/build/src/commands/get/get-test
import { ServiceLogEntry } from "garden-service/build/src/types/plugin/service/getServiceLogs"
import { CommandResult } from "garden-service/build/src/commands/base"
import { ConfigDump } from "garden-service/build/src/garden"
import { AllEnvironmentStatus } from "garden-service/build/src/actions"
import { StatusCommandResult } from "garden-service/build/src/commands/get/get-status"

export interface ApiRequest {
command: string
parameters: {}
}
export type FetchLogsParam = string[]
export type FetchTaskResultParam = { name: string }
export type FetchTestResultParam = { name: string, module: string }

const MAX_LOG_LINES = 5000

Expand All @@ -35,20 +32,33 @@ export async function fetchGraph() {
}

export async function fetchStatus() {
return apiPost<AllEnvironmentStatus>("get.status", { output: "json" })
return apiPost<StatusCommandResult>("get.status", { output: "json" })
}

export async function fetchLogs(services: FetchLogsParam) {
const tail = Math.floor(MAX_LOG_LINES / services.length)
return apiPost<ServiceLogEntry[]>("logs", { services, tail })
export interface FetchLogsParams {
serviceNames: string[]
}

export async function fetchTaskResult(params: FetchTaskResultParam) {
export async function fetchLogs({ serviceNames }: FetchLogsParams) {
const tail = Math.floor(MAX_LOG_LINES / serviceNames.length)
return apiPost<ServiceLogEntry[]>("logs", { services: serviceNames, tail })
}

export interface FetchTaskResultParams {
name: string
}

export async function fetchTaskResult(params: FetchTaskResultParams) {
return apiPost<TaskResultOutput>("get.task-result", params)
}

export async function fetchTestResult(params: FetchTestResultParam) {
return apiPost<TestResultOutput>("get.test-result", params)
export interface FetchTestResultParams {
name: string
moduleName: string
}

export async function fetchTestResult({ name, moduleName }: FetchTestResultParams) {
return apiPost<TestResultOutput>("get.test-result", { name, module: moduleName })
}

async function apiPost<T>(command: string, parameters: {} = {}): Promise<T> {
Expand All @@ -64,7 +74,7 @@ async function apiPost<T>(command: string, parameters: {} = {}): Promise<T> {
}

if (!res.data.result) {
throw new Error("result is empty")
throw new Error("Empty response from server")
}

return res.data.result
Expand Down
21 changes: 9 additions & 12 deletions dashboard/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { css } from "emotion"
import React, { useContext } from "react"
import React from "react"
import styled from "@emotion/styled"
import { Route } from "react-router-dom"

Expand All @@ -23,15 +23,14 @@ import "./styles/padding-margin-mixin.scss"
import "./styles/custom-flexboxgrid.scss"
import "./styles/icons.scss"

import { EventProvider } from "./context/events"
import { DataProvider } from "./context/data"
import { NavLink } from "./components/links"

import logo from "./assets/logo.png"
import { ReactComponent as OpenSidebarIcon } from "./assets/open-pane.svg"
import { ReactComponent as CloseSidebarIcon } from "./assets/close-pane.svg"

import { UiStateProvider, UiStateContext } from "./context/ui"
import { UiStateProvider, useUiState } from "./contexts/ui"
import { ApiProvider } from "./contexts/api"

// Style and align properly
const Logo = styled.img`
Expand Down Expand Up @@ -67,13 +66,11 @@ const SidebarToggleButton = styled.div`
const AppContainer = () => {
return (
<div>
<DataProvider>
<EventProvider>
<UiStateProvider>
<App />
</UiStateProvider>
</EventProvider>
</DataProvider>
<ApiProvider>
<UiStateProvider>
<App />
</UiStateProvider>
</ApiProvider>
</div>
)
}
Expand All @@ -82,7 +79,7 @@ const App = () => {
const {
state: { isSidebarOpen },
actions: { toggleSidebar },
} = useContext(UiStateContext)
} = useUiState()

return (
<div
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import React, { ReactNode } from "react"
import styled from "@emotion/styled"
import { Entity } from "../containers/overview"
import { colors } from "../styles/variables"
import { Facebook as ContentLoader } from "react-content-loader"
import { colors } from "../../styles/variables"

interface EntityCardProps {
type: EntityType
}
const EntityCard = styled.div<EntityCardProps>`
export const EntityCardWrap = styled.div`
max-height: 13rem;
background-color: ${props => (props && props.type && colors.cardTypes[props.type] || "white")};
background-color: white;
margin-right: 1rem;
box-shadow: 2px 2px 9px rgba(0,0,0,0.14);
border-radius: 4px;
Expand All @@ -34,13 +28,13 @@ const EntityCard = styled.div<EntityCardProps>`
}
`

const Header = styled.div`
export const Header = styled.div`
width: 100%;
display:flex;
justify-content: space-between;
`

const Content = styled.div`
export const Content = styled.div`
width: 100%;
position: relative;
max-height: 10rem;
Expand All @@ -51,10 +45,11 @@ const Content = styled.div`
}
`

type StateContainerProps = {
type StateLabelProps = {
state: string,
}
const StateContainer = styled.div<StateContainerProps>`

export const StateLabel = styled.div<StateLabelProps>`
padding: 0 .5rem;
margin-left: auto;
background-color: ${props => (props && props.state ? colors.state[props.state] : colors.gardenGrayLight)};
Expand All @@ -70,7 +65,7 @@ const StateContainer = styled.div<StateContainerProps>`
height: 1rem;
`

const Tag = styled.div`
export const Label = styled.div`
display: flex;
align-items: center;
font-weight: 500;
Expand All @@ -81,46 +76,62 @@ const Tag = styled.div`
color: #90A0B7;
`

const Name = styled.div`
export const Name = styled.div`
font-size: 0.9375rem;
font-weight: 500;
color: rgba(0, 0, 0, .87);
padding-top: 0.125rem;
`

type EntityType = "service" | "test" | "task"
type FieldWrapProps = {
visible: boolean,
}

export const FieldWrap = styled.div<FieldWrapProps>`
display: ${props => (props.visible ? `block` : "none")};
animation: fadein .5s;
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`

interface Props {
type: EntityType
children: ReactNode
entity: Entity
type FieldProps = {
inline?: boolean,
visible: boolean,
}

export default ({
children,
type,
entity: { name, isLoading, state },
}: Props) => {

return (
<EntityCard type={type}>
<Header>
<div>
<Tag>{type.toUpperCase()}</Tag>
<Name>{name}</Name>
</div>
{state && (
<StateContainer state={state}>
{state}
</StateContainer>
)}
</Header>
<Content>
{isLoading && (
<ContentLoader height={100} />
)}
{!isLoading && children}
</Content>
</EntityCard>
)
export const Field = styled.div<FieldProps>`
display: ${props => (props.visible ? (props.inline ? "flex" : "block") : "none")};
flex-direction: row;
`

type FieldGroupProps = {
visible: boolean,
}

export const FieldGroup = styled.div<FieldGroupProps>`
display: ${props => (props.visible ? "flex" : "none")};
flex-direction: row;
padding-top: .25rem;
`

export const Key = styled.div`
padding-right: .25rem;
font-size: 0.8125rem;
line-height: 1.1875rem;
letter-spacing: 0.01em;
color: #4C5862;
opacity: 0.5;
`

export const Value = styled.div`
padding-right: .5rem;
font-size: 0.8125rem;
line-height: 1.1875rem;
letter-spacing: 0.01em;
`
Loading

0 comments on commit ce9a2fb

Please sign in to comment.