From 2cf1b4db78b3c5fdf64a43295b3fddd671853435 Mon Sep 17 00:00:00 2001 From: Katie Adee Date: Wed, 18 Jul 2018 16:02:10 -0400 Subject: [PATCH] feat(app): Add and implement module selectors in calibration (#1895) --- app/src/components/Page/RefreshWrapper.js | 20 ++++++ app/src/components/Page/index.js | 3 +- app/src/components/UploadStatus/Status.js | 2 - app/src/components/nav-bar/NavButton.js | 18 +---- app/src/pages/Calibrate/Labware.js | 83 ++++++++++++++++------- app/src/pages/Calibrate/Pipettes.js | 50 +++++++++++--- app/src/robot/api-client/client.js | 8 +++ app/src/robot/selectors.js | 19 +++++- app/src/robot/test/selectors.test.js | 42 +++++++++++- 9 files changed, 190 insertions(+), 55 deletions(-) create mode 100644 app/src/components/Page/RefreshWrapper.js diff --git a/app/src/components/Page/RefreshWrapper.js b/app/src/components/Page/RefreshWrapper.js new file mode 100644 index 00000000000..26f77407852 --- /dev/null +++ b/app/src/components/Page/RefreshWrapper.js @@ -0,0 +1,20 @@ +// @flow +import * as React from 'react' + +type Props = { + refresh: () => mixed, + children: React.Node, +} +export default class RefreshWrapper extends React.Component { + render () { + const {children} = this.props + return ( + + {children} + + ) + } + componentDidMount () { + this.props.refresh() + } +} diff --git a/app/src/components/Page/index.js b/app/src/components/Page/index.js index c035f89e06c..54f74f850e6 100644 --- a/app/src/components/Page/index.js +++ b/app/src/components/Page/index.js @@ -4,6 +4,7 @@ import * as React from 'react' import styles from './styles.css' import {TitleBar, type TitleBarProps} from '@opentrons/components' import PageWrapper from './PageWrapper' +import RefreshWrapper from './RefreshWrapper' type Props = { titleBarProps?: TitleBarProps, @@ -22,4 +23,4 @@ export default function Page (props: Props) { ) } -export {PageWrapper} +export {PageWrapper, RefreshWrapper} diff --git a/app/src/components/UploadStatus/Status.js b/app/src/components/UploadStatus/Status.js index b34f2692f9b..76cd1a4c4de 100644 --- a/app/src/components/UploadStatus/Status.js +++ b/app/src/components/UploadStatus/Status.js @@ -46,8 +46,6 @@ function UploadResults (props: Props) { let instructions - console.log(props) - if (uploadError) { // instructions for an unsuccessful upload instructions = ( diff --git a/app/src/components/nav-bar/NavButton.js b/app/src/components/nav-bar/NavButton.js index 27fa12246f5..01ddff3ab10 100644 --- a/app/src/components/nav-bar/NavButton.js +++ b/app/src/components/nav-bar/NavButton.js @@ -11,7 +11,7 @@ import { selectors as robotSelectors, constants as robotConstants } from '../../robot' -import {getAnyRobotUpdateAvailable, fetchPipettes} from '../../http-api-client' +import {getAnyRobotUpdateAvailable} from '../../http-api-client' import {getShellUpdate} from '../../shell' import {NavButton} from '@opentrons/components' @@ -26,9 +26,7 @@ type SP = Props & { _robot: ?RobotService, } -type DP = {dispatch: Dispatch} - -export default withRouter(connect(mapStateToProps, null, mergeProps)(NavButton)) +export default withRouter(connect(mapStateToProps)(NavButton)) function mapStateToProps (state: State, ownProps: OP): SP { const {name} = ownProps @@ -78,15 +76,3 @@ function mapStateToProps (state: State, ownProps: OP): SP { return {...NAV_ITEM_BY_NAME[name], _robot} } - -function mergeProps (stateProps: SP, dispatchProps: DP, ownProps: OP): Props { - const {dispatch} = dispatchProps - const {_robot, url, disabled} = stateProps - let props: Props = {...ownProps, ...stateProps} - - if (_robot && url === '/calibrate' && !disabled) { - props = {...props, onClick: () => dispatch(fetchPipettes(_robot))} - } - - return props -} diff --git a/app/src/pages/Calibrate/Labware.js b/app/src/pages/Calibrate/Labware.js index d77c07d0eac..17a06139a2d 100644 --- a/app/src/pages/Calibrate/Labware.js +++ b/app/src/pages/Calibrate/Labware.js @@ -2,41 +2,59 @@ // setup instruments page import * as React from 'react' import {connect} from 'react-redux' -import {Route, Redirect, withRouter, type ContextRouter, type Match} from 'react-router' +import {Route, Redirect, withRouter, type Match} from 'react-router' import {push} from 'react-router-redux' -import type {State} from '../../types' -import { - selectors as robotSelectors, - type Labware -} from '../../robot' -import {makeGetRobotSettings} from '../../http-api-client' -import Page from '../../components/Page' +import type {State, Dispatch} from '../../types' +import type {Labware, Robot, StateModule} from '../../robot' +import {selectors as robotSelectors} from '../../robot' +import {getModulesOn} from '../../config' +import type {Module} from '../../http-api-client' +import {makeGetRobotSettings, makeGetRobotModules, fetchModules} from '../../http-api-client' +import Page, {RefreshWrapper} from '../../components/Page' import CalibrateLabware from '../../components/CalibrateLabware' import SessionHeader from '../../components/SessionHeader' import ReviewDeckModal from '../../components/ReviewDeckModal' import ConfirmModal from '../../components/CalibrateLabware/ConfirmModal' -type OwnProps = { +type OP = { match: Match } -type StateProps = { +type SP = { deckPopulated: boolean, labware: ?Labware, - calibrateToBottom: boolean + calibrateToBottom: boolean, + robot: Robot, + modulesFlag: ?boolean, + modules: Array, + actualModules: ?Array, } -type DispatchProps = {onBackClick: () => void} +type DP = { + dispatch: Dispatch +} -type Props = ContextRouter & StateProps & OwnProps & DispatchProps +type Props = SP & OP & { + onBackClick: () => void, + fetchModules: () => mixed, +} -export default withRouter(connect(makeMapStateToProps, mapDispatchToProps)(SetupDeckPage)) +export default withRouter(connect(makeMapStateToProps, null, mergeProps)(SetupDeckPage)) function SetupDeckPage (props: Props) { - const {calibrateToBottom, labware, deckPopulated, onBackClick, match: {url, params: {slot}}} = props + const { + calibrateToBottom, + labware, + deckPopulated, + onBackClick, + fetchModules, + match: {url, params: {slot}} + } = props return ( - + )}} > @@ -56,36 +74,55 @@ function SetupDeckPage (props: Props) { ) }} /> - + ) } -function makeMapStateToProps (): (state: State, ownProps: OwnProps) => StateProps { +function makeMapStateToProps (): (state: State, ownProps: OP) => SP { const getRobotSettings = makeGetRobotSettings() + const getRobotModules = makeGetRobotModules() return (state, ownProps) => { const {match: {url, params: {slot}}} = ownProps const labware = robotSelectors.getLabware(state) const currentLabware = labware.find((lw) => lw.slot === slot) const name = robotSelectors.getConnectedRobotName(state) - const response = getRobotSettings(state, {name}).response - const settings = response && response.settings + const robot = robotSelectors.getConnectedRobot(state) + + const settingsResponse = getRobotSettings(state, {name}).response + const settings = settingsResponse && settingsResponse.settings const flag = !!settings && settings.find((s) => s.id === 'calibrateToBottom') const calibrateToBottom = !!flag && flag.value + const modules = robotSelectors.getModules(state) + const modulesCall = getRobotModules(state, robot) + const modulesResponse = modulesCall.response + const actualModules = modulesResponse && modulesResponse.modules + return { deckPopulated: !!robotSelectors.getDeckPopulated(state), labware: currentLabware, slot, url, - calibrateToBottom + calibrateToBottom, + robot, + modulesFlag: getModulesOn(state), + modules, + actualModules } } } -function mapDispatchToProps (dispatch, ownProps: OwnProps): DispatchProps { +function mergeProps (stateProps: SP, dispatchProps: DP, ownProps: OP): Props { const {match: {url}} = ownProps + const {dispatch} = dispatchProps + const {robot} = stateProps return { - onBackClick: () => { dispatch(push(url)) } + ...stateProps, + ...ownProps, + onBackClick: () => { dispatch(push(url)) }, + fetchModules: () => { + dispatch(fetchModules(robot)) + } } } diff --git a/app/src/pages/Calibrate/Pipettes.js b/app/src/pages/Calibrate/Pipettes.js index 2fe8ed6d9a1..7fd3891fe8e 100644 --- a/app/src/pages/Calibrate/Pipettes.js +++ b/app/src/pages/Calibrate/Pipettes.js @@ -2,33 +2,45 @@ // setup pipettes component import * as React from 'react' import {connect} from 'react-redux' -import {Route, Redirect, type ContextRouter} from 'react-router' +import {Route, Redirect, type Match} from 'react-router' import type {State} from '../../types' -import type {Pipette} from '../../robot' +import type {Pipette, Robot} from '../../robot' import {selectors as robotSelectors} from '../../robot' -import {makeGetRobotPipettes} from '../../http-api-client' +import {makeGetRobotPipettes, fetchPipettes} from '../../http-api-client' -import Page from '../../components/Page' +import Page, {RefreshWrapper} from '../../components/Page' import TipProbe from '../../components/TipProbe' import ConfirmTipProbeModal from '../../components/ConfirmTipProbeModal' import {PipetteTabs, Pipettes} from '../../components/calibrate-pipettes' import SessionHeader from '../../components/SessionHeader' -type StateProps = { +type SP = { pipettes: Array, - currentPipette: ?Pipette + currentPipette: ?Pipette, + robot: Robot, } -type OwnProps = ContextRouter +type DP = {dispatch: Dispatch} -type Props = StateProps & OwnProps +type OP = { + match: Match +} + +type Props = SP & OP & { + fetchPipettes: () => mixed +} -export default connect(makeMapStateToProps)(CalibratePipettesPage) +export default connect(makeMapStateToProps, null, mergeProps)(CalibratePipettesPage) function CalibratePipettesPage (props: Props) { - const {pipettes, currentPipette, match: {url, params}} = props + const { + pipettes, + currentPipette, + fetchPipettes, + match: {url, params} + } = props const confirmTipProbeUrl = `${url}/confirm-tip-probe` // redirect back to mountless route if mount doesn't exist @@ -37,6 +49,9 @@ function CalibratePipettesPage (props: Props) { } return ( + )}} > @@ -57,22 +72,35 @@ function CalibratePipettesPage (props: Props) { )} /> )} + ) } -function makeMapStateToProps (): (State, OwnProps) => StateProps { +function makeMapStateToProps (): (State, OP) => SP { const getCurrentPipette = robotSelectors.makeGetCurrentPipette() const getAttachedPipettes = makeGetRobotPipettes() return (state, props) => { const name = robotSelectors.getConnectedRobotName(state) + const robot = robotSelectors.getConnectedRobot(state) const pipettesResponse = getAttachedPipettes(state, {name}) return { name, + robot, pipettes: robotSelectors.getPipettes(state), currentPipette: getCurrentPipette(state, props), actualPipettes: pipettesResponse.response } } } + +function mergeProps (stateProps: SP, dispatchProps: DP, ownProps: OP): Props { + const {dispatch} = dispatchProps + const {robot} = stateProps + return { + ...stateProps, + ...ownProps, + fetchPipettes: () => { dispatch(fetchPipettes(robot)) } + } +} diff --git a/app/src/robot/api-client/client.js b/app/src/robot/api-client/client.js index f0d2dae42da..caf4a186425 100755 --- a/app/src/robot/api-client/client.js +++ b/app/src/robot/api-client/client.js @@ -341,6 +341,14 @@ export default function client (dispatch) { if (apiSession.modules) { update.modulesBySlot = {} + // TODO (ka 2018-7-17): MOCKED MODULES by slot here instead of session.py uncomment below to test + // update.modulesBySlot = { + // '1': { + // id: '4374062089', + // name: 'magdeck', + // slot: '1' + // } + // } apiSession.modules.forEach(addApiModuleToModules) } diff --git a/app/src/robot/selectors.js b/app/src/robot/selectors.js index 326141b0fcc..9830bba6769 100644 --- a/app/src/robot/selectors.js +++ b/app/src/robot/selectors.js @@ -15,7 +15,8 @@ import type { LabwareCalibrationStatus, LabwareType, Robot, - SessionStatus + SessionStatus, + StateModule } from './types' import { @@ -313,6 +314,22 @@ export const getPipettesCalibrated = createSelector( ) ) +export function getModulesBySlot (state: State) { + return session(state).modulesBySlot +} + +export const getModules = createSelector( + getModulesBySlot, + (modules): ?StateModule[] => { + return Object.keys(modules) + .filter(isSlot) + .map((slot) => { + const module = modules[slot] + return {...module} + }) + } +) + export function getLabwareBySlot (state: State) { return session(state).labwareBySlot } diff --git a/app/src/robot/test/selectors.test.js b/app/src/robot/test/selectors.test.js index d1375ed536d..d0a4262b8b0 100644 --- a/app/src/robot/test/selectors.test.js +++ b/app/src/robot/test/selectors.test.js @@ -26,7 +26,9 @@ const { getUnconfirmedTipracks, getUnconfirmedLabware, getNextLabware, - makeGetCurrentPipette + makeGetCurrentPipette, + getModulesBySlot, + getModules } = selectors describe('robot selectors', () => { @@ -506,6 +508,44 @@ describe('robot selectors', () => { expect(getPipettesCalibrated(onePipetteCalibrated)).toBe(true) }) + describe('module selectors', () => { + let state + + beforeEach(() => { + state = makeState({ + session: { + modulesBySlot: { + 1: { + _id: 1, + slot: '1', + name: 'tempdeck' + } + } + } + }) + }) + + test('get modules by slot', () => { + expect(getModulesBySlot(state)).toEqual({ + 1: { + _id: 1, + slot: '1', + name: 'tempdeck' + } + }) + }) + + test('get modules', () => { + expect(getModules(state)).toEqual([ + { + _id: 1, + slot: '1', + name: 'tempdeck' + } + ]) + }) + }) + describe('labware selectors', () => { let state