From c57872f13b5e54dc16236b587b38295d10e6a237 Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Thu, 7 Jun 2018 18:29:20 -0400 Subject: [PATCH 1/9] get a button and some more selectors out there --- protocol-designer/src/components/FilePage.css | 4 +++ protocol-designer/src/components/FilePage.js | 12 +++++---- .../src/containers/ConnectedFilePage.js | 11 +++++--- .../src/containers/ConnectedNewFileModal.js | 2 +- protocol-designer/src/file-data/actions.js | 13 ++++++---- .../src/file-data/reducers/index.js | 25 ++++++++++++++----- .../src/file-data/selectors/fileFields.js | 8 +++++- protocol-designer/src/file-data/types.js | 4 +-- 8 files changed, 55 insertions(+), 24 deletions(-) diff --git a/protocol-designer/src/components/FilePage.css b/protocol-designer/src/components/FilePage.css index 7b130d75eb3..4a6d15d8bc0 100644 --- a/protocol-designer/src/components/FilePage.css +++ b/protocol-designer/src/components/FilePage.css @@ -7,3 +7,7 @@ .file_page section { margin: 0 2.5rem; } + +.update_button { + width: 6rem; +} diff --git a/protocol-designer/src/components/FilePage.js b/protocol-designer/src/components/FilePage.js index 824673e5f64..fb5929bf8fd 100644 --- a/protocol-designer/src/components/FilePage.js +++ b/protocol-designer/src/components/FilePage.js @@ -1,19 +1,18 @@ // @flow import * as React from 'react' -import {FormGroup, InputField, InstrumentGroup} from '@opentrons/components' -import type {FilePageFields} from '../file-data' +import {FormGroup, InputField, InstrumentGroup, PrimaryButton} from '@opentrons/components' +import type {FileMetadataFields} from '../file-data' import type {FormConnector} from '../utils' - import styles from './FilePage.css' import formStyles from '../components/forms.css' type Props = { - formConnector: FormConnector, + formConnector: FormConnector, instruments: React.ElementProps } export default function FilePage (props: Props) { - const {formConnector, instruments} = props + const {formConnector, isFormAltered, instruments} = props return (
@@ -34,6 +33,9 @@ export default function FilePage (props: Props) { + + UPDATE +
diff --git a/protocol-designer/src/containers/ConnectedFilePage.js b/protocol-designer/src/containers/ConnectedFilePage.js index d7b5b1b9aa7..7aba4cc923f 100644 --- a/protocol-designer/src/containers/ConnectedFilePage.js +++ b/protocol-designer/src/containers/ConnectedFilePage.js @@ -7,7 +7,7 @@ import type {BaseState} from '../types' import FilePage from '../components/FilePage' import {actions, selectors} from '../file-data' -import type {FilePageFields} from '../file-data' +import type {FileMetadataFields} from '../file-data' import {formConnectorFactory, type FormConnector} from '../utils' export default connect(mapStateToProps, null, mergeProps)(FilePage) @@ -20,10 +20,12 @@ type StateProps = { function mapStateToProps (state: BaseState): StateProps { const formValues = selectors.fileFormValues(state) + const isFormAltered = selectors.isUnsavedMetadatFormAltered(state) const pipetteData = selectors.pipettesForInstrumentGroup(state) return { _values: formValues, + isFormAltered, instruments: { left: pipetteData.find(i => i.mount === 'left'), right: pipetteData.find(i => i.mount === 'right') @@ -35,21 +37,22 @@ function mergeProps ( stateProps: StateProps, dispatchProps: {dispatch: Dispatch<*>} ): Props { - const {instruments, _values} = stateProps + const {instruments, isFormAltered, _values} = stateProps const {dispatch} = dispatchProps const onChange = (accessor) => (e: SyntheticInputEvent<*>) => { if (accessor === 'name' || accessor === 'description' || accessor === 'author') { - dispatch(actions.updateFileFields({[accessor]: e.target.value})) + dispatch(actions.updateFileMetadataFields({[accessor]: e.target.value})) } else { console.warn('Invalid accessor in ConnectedFilePage:', accessor) } } - const formConnector: FormConnector = formConnectorFactory(onChange, _values) + const formConnector: FormConnector = formConnectorFactory(onChange, _values) return { formConnector, + isFormAltered, instruments } } diff --git a/protocol-designer/src/containers/ConnectedNewFileModal.js b/protocol-designer/src/containers/ConnectedNewFileModal.js index abcbfa02f6d..7a22c34faa3 100644 --- a/protocol-designer/src/containers/ConnectedNewFileModal.js +++ b/protocol-designer/src/containers/ConnectedNewFileModal.js @@ -27,7 +27,7 @@ function mapDispatchToProps (dispatch: Dispatch<*>): DispatchProps { return { onCancel: () => dispatch(navigationActions.toggleNewProtocolModal(false)), onSave: fields => { - dispatch(fileActions.updateFileFields({ + dispatch(fileActions.saveFileMetadata({ name: fields.name || '' })) diff --git a/protocol-designer/src/file-data/actions.js b/protocol-designer/src/file-data/actions.js index 461e3c6325a..33453fbfc32 100644 --- a/protocol-designer/src/file-data/actions.js +++ b/protocol-designer/src/file-data/actions.js @@ -1,11 +1,14 @@ // @flow -import type {FilePageFieldAccessors} from './types' +import type {FileMetadataFieldAccessors} from './types' import type {PipetteName} from './pipetteData' -// NOTE: updateFileFields contains pipette name identifiers, though pipettes aren't file fields. -// This is because both pipettes and file fields can get changed in the same place -export const updateFileFields = (payload: {[accessor: FilePageFieldAccessors]: string}) => ({ - type: 'UPDATE_FILE_FIELDS', +export const updateFileMetadataFields = (payload: {[accessor: FileMetadataFieldAccessors]: string}) => ({ + type: 'UPDATE_FILE_METADATA_FIELDS', + payload +}) + +export const saveFileMetadata = (payload: {[accessor: FileMetadataFieldAccessors]: string}) => ({ + type: 'SAVE_FILE_METADATA', payload }) diff --git a/protocol-designer/src/file-data/reducers/index.js b/protocol-designer/src/file-data/reducers/index.js index a838cdddbf4..6910320a672 100644 --- a/protocol-designer/src/file-data/reducers/index.js +++ b/protocol-designer/src/file-data/reducers/index.js @@ -2,12 +2,12 @@ import {combineReducers} from 'redux' import {handleActions, type ActionType} from 'redux-actions' -import {updateFileFields, updatePipettes} from '../actions' +import {updateFileMetadataFields, updatePipettes} from '../actions' import {pipetteDataByName, type PipetteName} from '../pipetteData' import type {Mount} from '@opentrons/components' import type {PipetteData} from '../../step-generation' -import type {FilePageFields} from '../types' +import type {FileMetadataFields} from '../types' const defaultFields = { name: '', @@ -15,8 +15,19 @@ const defaultFields = { description: '' } -const metadataFields = handleActions({ - UPDATE_FILE_FIELDS: (state: FilePageFields, action: ActionType) => ({ +const unsavedMetadataForm = handleActions({ + UPDATE_FILE_METADATA_FIELDS: (state: FileMetadataFields, action: ActionType) => ({ + ...state, + ...action.payload + }), + SAVE_FILE_METADATA: (state: FileMetadataFields, action: ActionType) => ({ + ...state, + ...action.payload + }) +}, defaultFields) + +const fileMetadata = handleActions({ + SAVE_FILE_METADATA: (state: FileMetadataFields, action: ActionType) => ({ ...state, ...action.payload }) @@ -58,12 +69,14 @@ const pipettes = handleActions({ }, {left: null, right: null}) export type RootState = { - metadataFields: FilePageFields, + unsavedMetadataForm: FileMetadataFields, + fileMetadata: FileMetadataFields, pipettes: PipetteState } const _allReducers = { - metadataFields, + unsavedMetadataForm, + fileMetadata, pipettes } diff --git a/protocol-designer/src/file-data/selectors/fileFields.js b/protocol-designer/src/file-data/selectors/fileFields.js index 0b39b9b17ce..7f6a6b6d208 100644 --- a/protocol-designer/src/file-data/selectors/fileFields.js +++ b/protocol-designer/src/file-data/selectors/fileFields.js @@ -1,4 +1,5 @@ // @flow +import _ from 'lodash' import {createSelector} from 'reselect' import type {BaseState} from '../../types' import type {RootState} from '../reducers' @@ -7,5 +8,10 @@ export const rootSelector = (state: BaseState): RootState => state.fileData export const fileFormValues = createSelector( rootSelector, - state => state.metadataFields + state => state.unsavedMetadataForm +) + +export const isUnsavedMetadatFormAltered = createSelector( + rootSelector, + state => (_.isEqual(state.unsavedMetadataForm, state.fileMetadata)) ) diff --git a/protocol-designer/src/file-data/types.js b/protocol-designer/src/file-data/types.js index 1ca61a5745f..f8764f0fb68 100644 --- a/protocol-designer/src/file-data/types.js +++ b/protocol-designer/src/file-data/types.js @@ -2,13 +2,13 @@ import type {DeckSlot, Mount} from '@opentrons/components' import type {Command} from '../step-generation/types' -export type FilePageFields = { +export type FileMetadataFields = { name: string, author: string, description: string } -export type FilePageFieldAccessors = $Keys +export type FileMetadataFieldAccessors = $Keys type MsSinceEpoch = number type VersionString = string // eg '1.0.0' From db9d6a2ab9cf8adf96e1b40766d7c2338a4bae78 Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Fri, 8 Jun 2018 14:10:08 -0400 Subject: [PATCH 2/9] wire up the save --- protocol-designer/src/components/FilePage.css | 8 ++- protocol-designer/src/components/FilePage.js | 69 ++++++++++--------- .../src/containers/ConnectedFilePage.js | 41 ++++++----- .../src/file-data/selectors/fileFields.js | 2 +- 4 files changed, 67 insertions(+), 53 deletions(-) diff --git a/protocol-designer/src/components/FilePage.css b/protocol-designer/src/components/FilePage.css index 4a6d15d8bc0..8ed7a357c06 100644 --- a/protocol-designer/src/components/FilePage.css +++ b/protocol-designer/src/components/FilePage.css @@ -8,6 +8,12 @@ margin: 0 2.5rem; } +.button_row { + display: flex; + justify-content: flex-end; +} + .update_button { - width: 6rem; + width: 8rem; + align-self: flex-start; } diff --git a/protocol-designer/src/components/FilePage.js b/protocol-designer/src/components/FilePage.js index fb5929bf8fd..a9d30098cfd 100644 --- a/protocol-designer/src/components/FilePage.js +++ b/protocol-designer/src/components/FilePage.js @@ -1,6 +1,6 @@ // @flow import * as React from 'react' -import {FormGroup, InputField, InstrumentGroup, PrimaryButton} from '@opentrons/components' +import {FormGroup, InputField, InstrumentGroup, OutlineButton} from '@opentrons/components' import type {FileMetadataFields} from '../file-data' import type {FormConnector} from '../utils' import styles from './FilePage.css' @@ -8,42 +8,45 @@ import formStyles from '../components/forms.css' type Props = { formConnector: FormConnector, - instruments: React.ElementProps + isFormAltered: boolean, + instruments: React.ElementProps, + saveFileMetadata: () => void } -export default function FilePage (props: Props) { - const {formConnector, isFormAltered, instruments} = props - return ( -
-
-

- Information -

+const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}: Props) => ( +
+
+

+ Information +

-
- - - - - - - -
+
+ + + - - + + - +
+ + + + +
+ UPDATE - -
+ +
+
-
-

- Pipettes -

- -
-
- ) -} +
+

+ Pipettes +

+ +
+
+) + +export default FilePage diff --git a/protocol-designer/src/containers/ConnectedFilePage.js b/protocol-designer/src/containers/ConnectedFilePage.js index 7aba4cc923f..28fff4f0321 100644 --- a/protocol-designer/src/containers/ConnectedFilePage.js +++ b/protocol-designer/src/containers/ConnectedFilePage.js @@ -1,6 +1,5 @@ // @flow import * as React from 'react' -import type {Dispatch} from 'redux' import {connect} from 'react-redux' import type {BaseState} from '../types' @@ -10,22 +9,21 @@ import {actions, selectors} from '../file-data' import type {FileMetadataFields} from '../file-data' import {formConnectorFactory, type FormConnector} from '../utils' -export default connect(mapStateToProps, null, mergeProps)(FilePage) - type Props = React.ElementProps -type StateProps = { +type SP = { instruments: $PropertyType, _values: {[string]: string} } +type DP = { + _updateFileMetadataFields: typeof actions.updateFileMetadataFields, + saveFileMetadata: () => void +} -function mapStateToProps (state: BaseState): StateProps { - const formValues = selectors.fileFormValues(state) - const isFormAltered = selectors.isUnsavedMetadatFormAltered(state) +const mapStateToProps = (state: BaseState): SP => { const pipetteData = selectors.pipettesForInstrumentGroup(state) - return { - _values: formValues, - isFormAltered, + _values: selectors.fileFormValues(state), + isFormAltered: selectors.isUnsavedMetadatFormAltered(state), instruments: { left: pipetteData.find(i => i.mount === 'left'), right: pipetteData.find(i => i.mount === 'right') @@ -33,16 +31,19 @@ function mapStateToProps (state: BaseState): StateProps { } } -function mergeProps ( - stateProps: StateProps, - dispatchProps: {dispatch: Dispatch<*>} -): Props { - const {instruments, isFormAltered, _values} = stateProps - const {dispatch} = dispatchProps +const mapDispatchToProps = { + _updateFileMetadataFields: actions.updateFileMetadataFields, + _saveFileMetadata: actions.saveFileMetadata +} +const mergeProps = ( + {instruments, isFormAltered, _values}: SP, + {_updateFileMetadataFields, _saveFileMetadata}: DP, + props: Props +) => { const onChange = (accessor) => (e: SyntheticInputEvent<*>) => { if (accessor === 'name' || accessor === 'description' || accessor === 'author') { - dispatch(actions.updateFileMetadataFields({[accessor]: e.target.value})) + _updateFileMetadataFields({[accessor]: e.target.value}) } else { console.warn('Invalid accessor in ConnectedFilePage:', accessor) } @@ -51,8 +52,12 @@ function mergeProps ( const formConnector: FormConnector = formConnectorFactory(onChange, _values) return { + ...props, formConnector, isFormAltered, - instruments + instruments, + saveFileMetadata: () => _saveFileMetadata(_values) } } + +export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(FilePage) diff --git a/protocol-designer/src/file-data/selectors/fileFields.js b/protocol-designer/src/file-data/selectors/fileFields.js index 7f6a6b6d208..6763a798c3e 100644 --- a/protocol-designer/src/file-data/selectors/fileFields.js +++ b/protocol-designer/src/file-data/selectors/fileFields.js @@ -13,5 +13,5 @@ export const fileFormValues = createSelector( export const isUnsavedMetadatFormAltered = createSelector( rootSelector, - state => (_.isEqual(state.unsavedMetadataForm, state.fileMetadata)) + state => !_.isEqual(state.unsavedMetadataForm, state.fileMetadata) ) From de4b90af0ffdcaa1c5dc28318a446c92bcc85cb1 Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Fri, 8 Jun 2018 14:34:28 -0400 Subject: [PATCH 3/9] move towards correct file name --- protocol-designer/src/containers/ConnectedFilePage.js | 8 ++++---- protocol-designer/src/containers/ConnectedNewFileModal.js | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/protocol-designer/src/containers/ConnectedFilePage.js b/protocol-designer/src/containers/ConnectedFilePage.js index 28fff4f0321..52056a8f51a 100644 --- a/protocol-designer/src/containers/ConnectedFilePage.js +++ b/protocol-designer/src/containers/ConnectedFilePage.js @@ -9,9 +9,9 @@ import {actions, selectors} from '../file-data' import type {FileMetadataFields} from '../file-data' import {formConnectorFactory, type FormConnector} from '../utils' -type Props = React.ElementProps +type OP = React.ElementProps type SP = { - instruments: $PropertyType, + instruments: $PropertyType, _values: {[string]: string} } type DP = { @@ -39,7 +39,7 @@ const mapDispatchToProps = { const mergeProps = ( {instruments, isFormAltered, _values}: SP, {_updateFileMetadataFields, _saveFileMetadata}: DP, - props: Props + ownProps: OP ) => { const onChange = (accessor) => (e: SyntheticInputEvent<*>) => { if (accessor === 'name' || accessor === 'description' || accessor === 'author') { @@ -52,7 +52,7 @@ const mergeProps = ( const formConnector: FormConnector = formConnectorFactory(onChange, _values) return { - ...props, + ...ownProps, formConnector, isFormAltered, instruments, diff --git a/protocol-designer/src/containers/ConnectedNewFileModal.js b/protocol-designer/src/containers/ConnectedNewFileModal.js index 7a22c34faa3..7f74e4f26b8 100644 --- a/protocol-designer/src/containers/ConnectedNewFileModal.js +++ b/protocol-designer/src/containers/ConnectedNewFileModal.js @@ -28,7 +28,8 @@ function mapDispatchToProps (dispatch: Dispatch<*>): DispatchProps { onCancel: () => dispatch(navigationActions.toggleNewProtocolModal(false)), onSave: fields => { dispatch(fileActions.saveFileMetadata({ - name: fields.name || '' + name: fields.name || '', + description: '' })) dispatch(fileActions.updatePipettes({ From 91b684d749983d694e1c45b4300f2ebe253a02ef Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Fri, 8 Jun 2018 16:39:19 -0400 Subject: [PATCH 4/9] enter key press handler --- components/src/forms/InputField.js | 1 + protocol-designer/src/components/FilePage.js | 32 +++++++++------ .../src/components/KeypressHandler.js | 40 +++++++++++++++++++ .../src/containers/ConnectedTitleBar.js | 5 +-- .../src/file-data/selectors/fileFields.js | 2 + 5 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 protocol-designer/src/components/KeypressHandler.js diff --git a/components/src/forms/InputField.js b/components/src/forms/InputField.js index cb3ccb523d4..3ba0ba0e200 100644 --- a/components/src/forms/InputField.js +++ b/components/src/forms/InputField.js @@ -75,6 +75,7 @@ function Input (props: Props) { placeholder={props.placeholder} onChange={props.onChange} onClick={props.onClick} + onKeyDown={props.onKeyDown} readOnly={props.readOnly} /> {props.units &&
{props.units}
} diff --git a/protocol-designer/src/components/FilePage.js b/protocol-designer/src/components/FilePage.js index a9d30098cfd..64490b3cf85 100644 --- a/protocol-designer/src/components/FilePage.js +++ b/protocol-designer/src/components/FilePage.js @@ -5,6 +5,7 @@ import type {FileMetadataFields} from '../file-data' import type {FormConnector} from '../utils' import styles from './FilePage.css' import formStyles from '../components/forms.css' +import KeypressHandler from './KeypressHandler' type Props = { formConnector: FormConnector, @@ -19,22 +20,27 @@ const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}:

Information

+ ( + +
+ + + -
- - - + + + +
- - - -
- - - - + + + +
+ )} />
- + UPDATE
diff --git a/protocol-designer/src/components/KeypressHandler.js b/protocol-designer/src/components/KeypressHandler.js new file mode 100644 index 00000000000..3815eb41ccf --- /dev/null +++ b/protocol-designer/src/components/KeypressHandler.js @@ -0,0 +1,40 @@ +// @flow +import * as React from 'react' +import _ from 'lodash' + +const KEY_CODES = { + enter: 13, + up_arrow: 38, + down_arrow: 40 +} + +const KEY_CODE_NAMES = _.invert(KEY_CODES) + +type KeyHandlers = { [$Keys]: () => void }; + +type RenderParams = { + onKeyDown: (SyntheticKeyboardEvent) => void +} + +type Props = { + keyHandlers: KeyHandlers, + render: (RenderParams) => React.Node, +} + +const makeHandler = (keyHandlers: KeyHandlers) => (event: SyntheticKeyboardEvent) => { + const keyCode = event.keyCode + const handler = keyHandlers[KEY_CODE_NAMES[keyCode]] + + if (handler) { + event.preventDefault() + handler(event) + } +} + +const KeypressHandler = ({ keyHandlers, render }: Props) => { + const WithKeypressHandling = render({ onKeyDown: makeHandler(keyHandlers) }) + + return WithKeypressHandling +} + +export default KeypressHandler diff --git a/protocol-designer/src/containers/ConnectedTitleBar.js b/protocol-designer/src/containers/ConnectedTitleBar.js index 243d4812928..e2838353b59 100644 --- a/protocol-designer/src/containers/ConnectedTitleBar.js +++ b/protocol-designer/src/containers/ConnectedTitleBar.js @@ -7,21 +7,20 @@ import {TitleBar, humanizeLabwareType} from '@opentrons/components' import {selectors as labwareIngredSelectors} from '../labware-ingred/reducers' import {selectors as steplistSelectors} from '../steplist/reducers' +import {selectors as fileDataSelectors} from '../file-data' import {closeIngredientSelector} from '../labware-ingred/actions' import {selectors, type Page} from '../navigation' import type {BaseState} from '../types' type Props = React.ElementProps - type DP = { onBackClick: $PropertyType } - type SP = $Diff & {_page: Page} function mapStateToProps (state: BaseState): SP { const _page = selectors.currentPage(state) // TODO: Ian 2018-02-22 fileName from file - const fileName = 'Protocol Name' + const fileName = fileDataSelectors.protocolName(state) switch (_page) { case 'file-splash': diff --git a/protocol-designer/src/file-data/selectors/fileFields.js b/protocol-designer/src/file-data/selectors/fileFields.js index 6763a798c3e..082d4284256 100644 --- a/protocol-designer/src/file-data/selectors/fileFields.js +++ b/protocol-designer/src/file-data/selectors/fileFields.js @@ -15,3 +15,5 @@ export const isUnsavedMetadatFormAltered = createSelector( rootSelector, state => !_.isEqual(state.unsavedMetadataForm, state.fileMetadata) ) + +export const protocolName = createSelector(rootSelector, state => state.fileMetadata.name) From 2339ddec563815ce322c263155832da450bd4cad Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Fri, 8 Jun 2018 17:30:08 -0400 Subject: [PATCH 5/9] feat(protocol-designer): clarify editing file details Make file metadata form clearer about when you're altering the saved information and when you can update it by submitting. Also added in key press handler render prop component to the component library (used here for handling submission on pressing the enter key). Closes #1504 and #1661 --- .../src/__tests__/__snapshots__/forms.test.js.snap | 1 + .../src/interaction-enhancers}/KeypressHandler.js | 0 components/src/interaction-enhancers/index.js | 4 +++- protocol-designer/src/components/FilePage.js | 11 ++++++++--- 4 files changed, 12 insertions(+), 4 deletions(-) rename {protocol-designer/src/components => components/src/interaction-enhancers}/KeypressHandler.js (100%) diff --git a/components/src/__tests__/__snapshots__/forms.test.js.snap b/components/src/__tests__/__snapshots__/forms.test.js.snap index 9df55c6d546..f4ed7628b8f 100644 --- a/components/src/__tests__/__snapshots__/forms.test.js.snap +++ b/components/src/__tests__/__snapshots__/forms.test.js.snap @@ -235,6 +235,7 @@ exports[`InputField renders correctly 1`] = ` , @@ -41,7 +46,7 @@ const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}: )} />
- UPDATE + {isFormAltered ? 'UPDATE' : 'SAVED'}
From a3c4e578ef839b0383715c4f2df51afa81672187 Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Mon, 11 Jun 2018 13:54:42 -0400 Subject: [PATCH 6/9] onKeyDown flow type --- components/src/forms/InputField.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/src/forms/InputField.js b/components/src/forms/InputField.js index 3ba0ba0e200..bd8c11417bf 100644 --- a/components/src/forms/InputField.js +++ b/components/src/forms/InputField.js @@ -29,6 +29,8 @@ type Props = { type?: 'text' | 'password', /** mouse click handler */ onClick?: (event: SyntheticMouseEvent<*>) => mixed, + /** key press handler */ + onKeyDown?: (event: SyntheticKeyboardEvent<*>) => mixed, /** makes input field read-only */ readOnly?: ?boolean } From 4955d200e13ee019bd8abf01d949b3d14d3544bd Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Mon, 11 Jun 2018 15:38:15 -0400 Subject: [PATCH 7/9] [fix-up] address ian pr comments --- .../__snapshots__/forms.test.js.snap | 1 - components/src/forms/InputField.js | 3 -- .../interaction-enhancers/KeypressHandler.js | 40 ---------------- components/src/interaction-enhancers/index.js | 4 +- protocol-designer/src/components/FilePage.js | 47 +++++++++---------- .../src/containers/ConnectedFilePage.js | 17 +++---- .../src/containers/ConnectedFileSidebar.js | 2 +- .../src/file-data/reducers/index.js | 2 +- .../src/file-data/selectors/fileCreator.js | 10 ++-- .../src/file-data/selectors/fileFields.js | 4 ++ 10 files changed, 40 insertions(+), 90 deletions(-) delete mode 100644 components/src/interaction-enhancers/KeypressHandler.js diff --git a/components/src/__tests__/__snapshots__/forms.test.js.snap b/components/src/__tests__/__snapshots__/forms.test.js.snap index f4ed7628b8f..9df55c6d546 100644 --- a/components/src/__tests__/__snapshots__/forms.test.js.snap +++ b/components/src/__tests__/__snapshots__/forms.test.js.snap @@ -235,7 +235,6 @@ exports[`InputField renders correctly 1`] = ` ) => mixed, - /** key press handler */ - onKeyDown?: (event: SyntheticKeyboardEvent<*>) => mixed, /** makes input field read-only */ readOnly?: ?boolean } @@ -77,7 +75,6 @@ function Input (props: Props) { placeholder={props.placeholder} onChange={props.onChange} onClick={props.onClick} - onKeyDown={props.onKeyDown} readOnly={props.readOnly} /> {props.units &&
{props.units}
} diff --git a/components/src/interaction-enhancers/KeypressHandler.js b/components/src/interaction-enhancers/KeypressHandler.js deleted file mode 100644 index 3815eb41ccf..00000000000 --- a/components/src/interaction-enhancers/KeypressHandler.js +++ /dev/null @@ -1,40 +0,0 @@ -// @flow -import * as React from 'react' -import _ from 'lodash' - -const KEY_CODES = { - enter: 13, - up_arrow: 38, - down_arrow: 40 -} - -const KEY_CODE_NAMES = _.invert(KEY_CODES) - -type KeyHandlers = { [$Keys]: () => void }; - -type RenderParams = { - onKeyDown: (SyntheticKeyboardEvent) => void -} - -type Props = { - keyHandlers: KeyHandlers, - render: (RenderParams) => React.Node, -} - -const makeHandler = (keyHandlers: KeyHandlers) => (event: SyntheticKeyboardEvent) => { - const keyCode = event.keyCode - const handler = keyHandlers[KEY_CODE_NAMES[keyCode]] - - if (handler) { - event.preventDefault() - handler(event) - } -} - -const KeypressHandler = ({ keyHandlers, render }: Props) => { - const WithKeypressHandling = render({ onKeyDown: makeHandler(keyHandlers) }) - - return WithKeypressHandling -} - -export default KeypressHandler diff --git a/components/src/interaction-enhancers/index.js b/components/src/interaction-enhancers/index.js index e3c3e042e4f..d59e36f5c00 100644 --- a/components/src/interaction-enhancers/index.js +++ b/components/src/interaction-enhancers/index.js @@ -1,9 +1,7 @@ // @flow import clickOutside from './clickOutside' -import KeypressHandler from './KeypressHandler' export type {ClickOutsideInterface} from './clickOutside' export { - clickOutside, - KeypressHandler + clickOutside } diff --git a/protocol-designer/src/components/FilePage.js b/protocol-designer/src/components/FilePage.js index 4a1446f6985..4ee0b804ec9 100644 --- a/protocol-designer/src/components/FilePage.js +++ b/protocol-designer/src/components/FilePage.js @@ -4,51 +4,46 @@ import { FormGroup, InputField, InstrumentGroup, - OutlineButton, - KeypressHandler + OutlineButton } from '@opentrons/components' import type {FileMetadataFields} from '../file-data' import type {FormConnector} from '../utils' import styles from './FilePage.css' import formStyles from '../components/forms.css' -type Props = { +export type FilePageProps = { formConnector: FormConnector, isFormAltered: boolean, instruments: React.ElementProps, saveFileMetadata: () => void } -const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}: Props) => ( +const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}: FilePageProps) => (

Information

- ( - -
- - - +
{ console.log('DID IT') }}> +
+ + + - - - -
+ + + +
- - - -
- )} /> -
- - {isFormAltered ? 'UPDATE' : 'SAVED'} - -
+ + + +
+ + {isFormAltered ? 'UPDATE' : 'SAVED'} + +
+
diff --git a/protocol-designer/src/containers/ConnectedFilePage.js b/protocol-designer/src/containers/ConnectedFilePage.js index 52056a8f51a..bd74de6d1ce 100644 --- a/protocol-designer/src/containers/ConnectedFilePage.js +++ b/protocol-designer/src/containers/ConnectedFilePage.js @@ -1,22 +1,21 @@ // @flow -import * as React from 'react' import {connect} from 'react-redux' import type {BaseState} from '../types' - import FilePage from '../components/FilePage' - +import type {FilePageProps} from '../components/FilePage' import {actions, selectors} from '../file-data' import type {FileMetadataFields} from '../file-data' import {formConnectorFactory, type FormConnector} from '../utils' -type OP = React.ElementProps type SP = { - instruments: $PropertyType, + instruments: $PropertyType, + isFormAltered: boolean, _values: {[string]: string} } + type DP = { _updateFileMetadataFields: typeof actions.updateFileMetadataFields, - saveFileMetadata: () => void + _saveFileMetadata: ({[string]: string}) => void } const mapStateToProps = (state: BaseState): SP => { @@ -38,9 +37,8 @@ const mapDispatchToProps = { const mergeProps = ( {instruments, isFormAltered, _values}: SP, - {_updateFileMetadataFields, _saveFileMetadata}: DP, - ownProps: OP -) => { + {_updateFileMetadataFields, _saveFileMetadata}: DP +): FilePageProps => { const onChange = (accessor) => (e: SyntheticInputEvent<*>) => { if (accessor === 'name' || accessor === 'description' || accessor === 'author') { _updateFileMetadataFields({[accessor]: e.target.value}) @@ -52,7 +50,6 @@ const mergeProps = ( const formConnector: FormConnector = formConnectorFactory(onChange, _values) return { - ...ownProps, formConnector, isFormAltered, instruments, diff --git a/protocol-designer/src/containers/ConnectedFileSidebar.js b/protocol-designer/src/containers/ConnectedFileSidebar.js index 5d9e56a81c5..a7bc5b54955 100644 --- a/protocol-designer/src/containers/ConnectedFileSidebar.js +++ b/protocol-designer/src/containers/ConnectedFileSidebar.js @@ -20,7 +20,7 @@ type MP = { export default connect(mapStateToProps, null, mergeProps)(FileSidebar) function mapStateToProps (state: BaseState): SP & MP { - const protocolName = fileDataSelectors.fileFormValues(state).name || 'untitled' + const protocolName = fileDataSelectors.fileMetadata(state).name || 'untitled' const fileData = fileDataSelectors.createFile(state) return { diff --git a/protocol-designer/src/file-data/reducers/index.js b/protocol-designer/src/file-data/reducers/index.js index 6910320a672..6c824e0e728 100644 --- a/protocol-designer/src/file-data/reducers/index.js +++ b/protocol-designer/src/file-data/reducers/index.js @@ -2,7 +2,7 @@ import {combineReducers} from 'redux' import {handleActions, type ActionType} from 'redux-actions' -import {updateFileMetadataFields, updatePipettes} from '../actions' +import {updateFileMetadataFields, updatePipettes, saveFileMetadata} from '../actions' import {pipetteDataByName, type PipetteName} from '../pipetteData' import type {Mount} from '@opentrons/components' diff --git a/protocol-designer/src/file-data/selectors/fileCreator.js b/protocol-designer/src/file-data/selectors/fileCreator.js index 32f3feaab7e..834144bb056 100644 --- a/protocol-designer/src/file-data/selectors/fileCreator.js +++ b/protocol-designer/src/file-data/selectors/fileCreator.js @@ -4,7 +4,7 @@ import mapValues from 'lodash/mapValues' import type {BaseState} from '../../types' import type {ProtocolFile, FilePipette, FileLabware} from '../types' import type {LabwareData, PipetteData} from '../../step-generation' -import {fileFormValues} from './fileFields' +import {fileMetadata} from './fileFields' import {getInitialRobotState, robotStateTimeline} from './commands' // TODO LATER Ian 2018-02-28 deal with versioning @@ -12,12 +12,12 @@ const protocolSchemaVersion = '1.0.0' const applicationVersion = '1.0.0' export const createFile: BaseState => ?ProtocolFile = createSelector( - fileFormValues, + fileMetadata, getInitialRobotState, robotStateTimeline, - (fileFormValues, initialRobotState, _robotStateTimeline) => { - const {author, description} = fileFormValues - const name = fileFormValues.name || 'untitled' + (fileMetadata, initialRobotState, _robotStateTimeline) => { + const {author, description} = fileMetadata + const name = fileMetadata.name || 'untitled' const isValidFile = true // TODO Ian 2018-02-28 this will be its own selector if (!isValidFile) { diff --git a/protocol-designer/src/file-data/selectors/fileFields.js b/protocol-designer/src/file-data/selectors/fileFields.js index 082d4284256..cf538e2104a 100644 --- a/protocol-designer/src/file-data/selectors/fileFields.js +++ b/protocol-designer/src/file-data/selectors/fileFields.js @@ -17,3 +17,7 @@ export const isUnsavedMetadatFormAltered = createSelector( ) export const protocolName = createSelector(rootSelector, state => state.fileMetadata.name) +export const fileMetadata = createSelector( + rootSelector, + state => state.fileMetadata +) From 7c8358516c6b465abdbebde9745966cb975b7768 Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Mon, 11 Jun 2018 17:51:34 -0400 Subject: [PATCH 8/9] [fixup] morgan nit and ian comment addressed --- protocol-designer/src/components/FilePage.js | 73 +++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/protocol-designer/src/components/FilePage.js b/protocol-designer/src/components/FilePage.js index 4ee0b804ec9..d9df095e4b2 100644 --- a/protocol-designer/src/components/FilePage.js +++ b/protocol-designer/src/components/FilePage.js @@ -18,41 +18,48 @@ export type FilePageProps = { saveFileMetadata: () => void } -const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}: FilePageProps) => ( -
-
-

- Information -

-
{ console.log('DID IT') }}> -
- - - +const FilePage = ({formConnector, isFormAltered, instruments, saveFileMetadata}: FilePageProps) => { + const handleSubmit = () => { + // blur focused field on submit + if (document && document.activeElement) document.activeElement.blur() + saveFileMetadata() + } + return ( +
+
+

+ Information +

+ +
+ + + - - - -
+ + + +
- - - -
- - {isFormAltered ? 'UPDATE' : 'SAVED'} - -
- -
+ + + +
+ + {isFormAltered ? 'UPDATE' : 'UPDATED'} + +
+ +
-
-

- Pipettes -

- -
-
-) +
+

+ Pipettes +

+ +
+ + ) +} export default FilePage From 28af4f0f6ea9a0e73481ea8e4a2b21cb5aea478b Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Fri, 15 Jun 2018 11:01:03 -0400 Subject: [PATCH 9/9] remove stale TODO --- protocol-designer/src/containers/ConnectedTitleBar.js | 1 - 1 file changed, 1 deletion(-) diff --git a/protocol-designer/src/containers/ConnectedTitleBar.js b/protocol-designer/src/containers/ConnectedTitleBar.js index e2838353b59..2f755e14e3b 100644 --- a/protocol-designer/src/containers/ConnectedTitleBar.js +++ b/protocol-designer/src/containers/ConnectedTitleBar.js @@ -19,7 +19,6 @@ type SP = $Diff & {_page: Page} function mapStateToProps (state: BaseState): SP { const _page = selectors.currentPage(state) - // TODO: Ian 2018-02-22 fileName from file const fileName = fileDataSelectors.protocolName(state) switch (_page) {