Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): Add attached modules card UI to instrument settings page #1854

Merged
merged 4 commits into from
Jul 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions app/src/components/InstrumentSettings/AttachedModulesCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,40 @@ import * as React from 'react'
import {connect} from 'react-redux'

import type {State} from '../../types'

import type {Module} from '../../http-api-client'
import {Card} from '@opentrons/components'
import {getModulesOn} from '../../config'
import ModulesCardContents from './ModulesCardContents'

type Module = {
slot: number,
name: string,
status: string,
serial: number,
fw_version: string
}

type SP = {
modulesFlag: ?boolean,
inProgress?: ?boolean,
modules?: Array<Module>, // modulesBySlot?
modules?: Array<Module>,
fetchModules?: () => mixed
}

type Props = SP

const TITLE = 'Modules'

const STUBBED_MODULE_DATA = [
{
name: 'temp_deck',
model: 'temp_deck',
serial: '123123124',
fwVersion: '1.2.13',
status: '86',
displayName: 'Temperature Module'
},
{
name: 'mag_deck',
model: 'mag_deck',
serial: '123123124',
fwVersion: '1.2.13',
status: 'disengaged',
displayName: 'Magnetic Bead Module'
}
]
export default connect(mapStateToProps, null)(AttachedModulesCard)

// TODO (ka 2018-6-29): change this to a refresh card once we have endpoints
Expand All @@ -38,7 +48,7 @@ function AttachedModulesCard (props: Props) {
title={TITLE}
column
>
<ModulesCardContents />
<ModulesCardContents {...props}/>
</Card>
)
}
Expand All @@ -47,6 +57,7 @@ function AttachedModulesCard (props: Props) {

function mapStateToProps (state: State): SP {
return {
modulesFlag: getModulesOn(state)
modulesFlag: getModulesOn(state),
modules: STUBBED_MODULE_DATA
}
}
24 changes: 14 additions & 10 deletions app/src/components/InstrumentSettings/ModulesCardContents.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
// @flow
import * as React from 'react'
import styles from './styles.css'
import type {Module} from '../../http-api-client'
import ModuleItem, {NoModulesMessage} from '../ModuleItem'

type Props = {
modules?: any
modules: Array<Module>,
}

export default function ModulesCardContents (props: Props) {
return (
<React.Fragment>
<p className={styles.modules_description}>No modules detected.</p>

<p className={styles.modules_description}>Connect a module to your robot via USB, then power it on.
Press the refresh icon to the top to detect your module.</p>
</React.Fragment>
)
const modulesFound = props.modules[0]
if (modulesFound) {
return (
<React.Fragment>
{props.modules.map((mod, index) => (
<ModuleItem {...mod} key={index}/>
))}
</React.Fragment>
)
}
return (<NoModulesMessage />)
}
6 changes: 0 additions & 6 deletions app/src/components/InstrumentSettings/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,3 @@
order: 3;
}
}

.modules_description {
@apply --font-body-2-dark;

margin: 0.5rem 0;
}
27 changes: 27 additions & 0 deletions app/src/components/ModuleItem/ModuleImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @flow
import * as React from 'react'

import styles from './styles.css'

type Props = {
name: string
}

export default function ModuleImage (props: Props) {
const imgSrc = getModuleImg(props.name)
return (
<div className={styles.module_image_wrapper}>
<img src={imgSrc} className={styles.module_image}/>
</div>
)
}

function getModuleImg (name: string) {
return MODULE_IMGS[name]
}

// TODO (ka 2018-7-10): replace with design assets onces available
const MODULE_IMGS = {
'temp_deck': require('./images/[email protected]'),
'mag_deck': require('./images/[email protected]')
}
23 changes: 23 additions & 0 deletions app/src/components/ModuleItem/ModuleInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @flow
import * as React from 'react'

import type {Module} from '../../http-api-client'

import {LabeledValue} from '@opentrons/components'

import styles from './styles.css'

export default function ModuleInfo (props: Module) {
return (
<div className={styles.module_info}>
<div className={styles.grid_50} >
<LabeledValue label='Name' value={props.displayName} />
<LabeledValue label='Serial' value={props.serial} />
</div>
<div className={styles.grid_50} >
<LabeledValue label='Status' value={props.status} />
<LabeledValue label='Firmware Version' value={props.fwVersion} />
</div>
</div>
)
}
26 changes: 26 additions & 0 deletions app/src/components/ModuleItem/ModuleUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @flow
import * as React from 'react'

import {OutlineButton} from '@opentrons/components'

import styles from './styles.css'

type Props = {
availableUpdate?: ?string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see availableUpdate in the stubbed modules. Is it a version string in the case that there is an available update and undefined if not? If that's the case, could it be called availableUpdateVersion?

Copy link
Contributor Author

@Kadee80 Kadee80 Jul 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, everything is just stubbed out. Not sure where the firmware update will come from yet. In robot it was a ?string with the version if a new one is available

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

availableUpdate will likely come from a selector based on whatever firmware files we have bundled in that version of the app

}

export default function ModuleUpdate (props: Props) {
const {availableUpdate} = props
const buttonText = availableUpdate
? 'update'
: 'updated'
return (
<div className={styles.module_update}>
<OutlineButton
disabled={!availableUpdate}
>
{buttonText}
</OutlineButton>
</div>
)
}
14 changes: 14 additions & 0 deletions app/src/components/ModuleItem/NoModulesMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @flow
import * as React from 'react'
import styles from './styles.css'

export default function NoModulesMessage () {
return (
<React.Fragment>
<p className={styles.modules_description}>No modules detected.</p>

<p className={styles.modules_description}>Connect a module to your robot via USB, then power it on.
Press the refresh icon to the top to detect your module.</p>
</React.Fragment>
)
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions app/src/components/ModuleItem/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @flow
import * as React from 'react'

import type {Module} from '../../http-api-client'

import ModuleImage from './ModuleImage'
import ModuleInfo from './ModuleInfo'
import ModuleUpdate from './ModuleUpdate'
import NoModulesMessage from './NoModulesMessage'

import styles from './styles.css'

type Props = Module & {
availableUpdate?: ?string
}

export default function ModuleItem (props: Props) {
return (
<div className={styles.module_item}>
<ModuleImage name={props.name}/>
<ModuleInfo {...props}/>
<ModuleUpdate availableUpdate={props.availableUpdate}/>
</div>
)
}

export {NoModulesMessage}
48 changes: 48 additions & 0 deletions app/src/components/ModuleItem/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* style sheet for module settings card on instrument settings page */
@import '@opentrons/components';

.modules_description {
@apply --font-body-2-dark;

margin: 0.5rem 0;
}

.module_item {
width: 100%;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
}

.module_image_wrapper {
flex-basis: 25%;
padding-right: 1rem;
}

.module_image {
width: 100%;
}

.module_info {
flex-basis: 55%;
}

.grid_50 {
display: inline-block;
vertical-align: top;
width: calc(50% - 2rem);
margin: 0 1rem;

& > div {
margin-bottom: 2rem;
}
}

.module_update {
flex-basis: 20%;
padding-top: 4.5rem;

& > button {
width: 8rem;
}
}
5 changes: 3 additions & 2 deletions app/src/components/layout/CardColumn.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import * as React from 'react'
import styles from './styles.css'

type Props = {
children: React.Node,
children: React.Node
}
export default function CardRow (props: Props) {

export default function CardColumn (props: Props) {
return (
<div className={styles.column_50}>
{props.children}
Expand Down
1 change: 1 addition & 0 deletions app/src/http-api-client/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {getRobotApiState} from './reducer'
import client from './client'

export type Module = {
name: string,
model: string,
serial: string,
fwVersion: string,
Expand Down