Skip to content

Commit

Permalink
feat(protocol-designer): allow user to delete entire liquid groups
Browse files Browse the repository at this point in the history
* refactor liquid deletion actions/reducers
* show confirmation modal when deleting a liquid that
was placed on the deck

Closes #2437
  • Loading branch information
IanLondon committed Oct 22, 2018
1 parent 2c48c09 commit 49e6823
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 85 deletions.
13 changes: 0 additions & 13 deletions protocol-designer/src/components/IngredientPropertiesForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ const makeInputField = (args: {setSubstate: SetSubstate, getSubstate: GetSubstat
type Props = {
onSave: (EditIngredientPayload) => mixed,
onCancel: () => mixed,
onDelete: (groupId: string) => mixed,

selectedWells: Array<string>,
selectedWellsMaxVolume: number,
Expand Down Expand Up @@ -201,18 +200,6 @@ class IngredientPropertiesForm extends React.Component<Props, State> {
}))
}

handleDelete = (e: SyntheticEvent<*>) => {
const {onDelete} = this.props

const groupToDelete = null // TODO: Ian 2018-06-07 allow deleting ingreds
if (groupToDelete) {
window.confirm('Are you sure you want to delete all ingredients in this group?') &&
onDelete(groupToDelete)
} else {
console.warn('Tried to delete in IngredientPropertiesForm, with no groupId to delete!')
}
}

getFieldErrors: () => FieldErrors = () => {
const {name, volume} = this.state.input
const {selectedWellsMaxVolume} = this.props
Expand Down
18 changes: 9 additions & 9 deletions protocol-designer/src/components/IngredientsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import styles from './IngredientsList.css'
import type {LiquidGroupsById, LiquidGroup} from '../labware-ingred/types'
import type {SingleLabwareLiquidState} from '../step-generation'

type DeleteIngredient = (args: {|groupId: string, wellName?: string|}) => mixed
type DeleteWellsContents = (args: {|liquidGroupId: string, wells: Array<string>|}) => mixed
type EditModeIngredientGroup = (args: {|groupId: string, wellName: ?string|}) => mixed

// Props used by both IngredientsList and IngredGroupCard
type CommonProps = {|
editModeIngredientGroup: EditModeIngredientGroup,
deleteIngredient: DeleteIngredient,
deleteWellsContents: DeleteWellsContents,
selected?: boolean,
|}

Expand All @@ -45,7 +45,7 @@ class IngredGroupCard extends React.Component<CardProps, CardState> {
const {
ingredGroup,
editModeIngredientGroup,
deleteIngredient,
deleteWellsContents,
selected,
groupId,
labwareWellContents,
Expand Down Expand Up @@ -100,7 +100,7 @@ class IngredGroupCard extends React.Component<CardProps, CardState> {
volume={volume}
groupId={groupId}
editModeIngredientGroup={editModeIngredientGroup}
deleteIngredient={deleteIngredient}
deleteWellsContents={deleteWellsContents}
/>
})}
</PDTitledList>
Expand All @@ -116,7 +116,7 @@ type IndividProps = {|
canDelete: boolean,
groupId: string,
editModeIngredientGroup: EditModeIngredientGroup,
deleteIngredient: DeleteIngredient,
deleteWellsContents: DeleteWellsContents,
|}

function IngredIndividual (props: IndividProps) {
Expand All @@ -127,7 +127,7 @@ function IngredIndividual (props: IndividProps) {
// concentration, // TODO LATER Ian 2018-02-22: concentration is removed from MVP. Remove all traces of it, or add it back in
canDelete,
groupId,
deleteIngredient,
deleteWellsContents,
} = props

return (
Expand All @@ -140,7 +140,7 @@ function IngredIndividual (props: IndividProps) {
name='close'
onClick={
() => window.confirm(`Are you sure you want to delete well ${wellName} ?`) &&
deleteIngredient({wellName, groupId})
deleteWellsContents({liquidGroupId: groupId, wells: [wellName]})
} />}
</PDListItem>
)
Expand All @@ -160,7 +160,7 @@ export default function IngredientsList (props: Props) {
labwareWellContents,
liquidGroupsById,
editModeIngredientGroup,
deleteIngredient,
deleteWellsContents,
selectedIngredientGroupId,
renameLabwareFormMode,
openRenameLabwareForm,
Expand All @@ -180,7 +180,7 @@ export default function IngredientsList (props: Props) {
{Object.keys(liquidGroupsById).map((groupIdForCard) =>
<IngredGroupCard key={groupIdForCard}
editModeIngredientGroup={editModeIngredientGroup}
deleteIngredient={deleteIngredient}
deleteWellsContents={deleteWellsContents}
labwareWellContents={labwareWellContents}
ingredGroup={liquidGroupsById[groupIdForCard]}
groupId={groupIdForCard}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {LiquidGroup} from '../../labware-ingred/types'

type Props = {
...$Exact<LiquidGroup>,
canDelete: boolean,
deleteLiquidGroup: () => mixed,
cancelForm: () => mixed,
saveForm: (LiquidGroup) => mixed,
Expand All @@ -34,7 +35,7 @@ export const liquidEditFormSchema = Yup.object().shape({
})

export default function LiquidEditForm (props: Props) {
const {deleteLiquidGroup, cancelForm, saveForm} = props
const {deleteLiquidGroup, cancelForm, canDelete, saveForm} = props

const initialValues = {
name: props.name,
Expand Down Expand Up @@ -91,7 +92,11 @@ export default function LiquidEditForm (props: Props) {
</section>

<div className={styles.button_row}>
<OutlineButton onClick={deleteLiquidGroup}>{i18n.t('button.delete')}</OutlineButton>
<OutlineButton
onClick={deleteLiquidGroup}
disabled={!canDelete}>
{i18n.t('button.delete')}
</OutlineButton>
<PrimaryButton onClick={cancelForm}>{i18n.t('button.cancel')}</PrimaryButton>
<PrimaryButton
disabled={!dirty}
Expand Down
5 changes: 4 additions & 1 deletion protocol-designer/src/components/LiquidsPage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type SP = {
...LiquidGroup,
_liquidGroupId: ?string,
showForm: boolean,
canDelete: $ElementType<Props, 'canDelete'>,
}

function LiquidEditFormWrapper (props: WrapperProps) {
Expand All @@ -37,6 +38,7 @@ function mapStateToProps (state: BaseState): SP {

return {
_liquidGroupId,
canDelete: _liquidGroupId != null,
showForm,
name: selectedIngredFields.name,
description: selectedIngredFields.description,
Expand All @@ -52,7 +54,8 @@ function mergeProps (stateProps: SP, dispatchProps: {dispatch: ThunkDispatch<*>}
formKey: _liquidGroupId || '__new_form__',
formProps: {
...passThruFormProps,
deleteLiquidGroup: () => window.alert('Deleting liquids is not yet implemented'), // TODO: Ian 2018-10-12 later ticket
deleteLiquidGroup: () => _liquidGroupId &&
dispatch(labwareIngredActions.deleteLiquidGroup(_liquidGroupId)),
cancelForm: () => dispatch(labwareIngredActions.deselectLiquidGroup()),
saveForm: (formData: LiquidGroup) => dispatch(labwareIngredActions.editLiquidGroup({
...formData,
Expand Down
3 changes: 0 additions & 3 deletions protocol-designer/src/containers/IngredientPropertiesForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {connect} from 'react-redux'
import {
editIngredient,
editModeIngredientGroup,
deleteIngredient,
type EditIngredientPayload,
} from '../labware-ingred/actions'
import {selectors} from '../labware-ingred/reducers'
Expand All @@ -18,7 +17,6 @@ type Props = React.ElementProps<typeof IngredientPropertiesForm>
type DP = {
onSave: $PropertyType<Props, 'onSave'>,
onCancel: $PropertyType<Props, 'onCancel'>,
onDelete: $PropertyType<Props, 'onDelete'>,
}

type SP = $Diff<Props, DP>
Expand All @@ -38,7 +36,6 @@ function mapDispatchToProps (dispatch: ThunkDispatch<*>): DP {
return {
onSave: (payload: EditIngredientPayload) => dispatch(editIngredient(payload)),
onCancel: () => dispatch(editModeIngredientGroup(null)),
onDelete: (groupId: string) => dispatch(deleteIngredient({groupId})),
}
}

Expand Down
20 changes: 13 additions & 7 deletions protocol-designer/src/containers/IngredientsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,45 @@ import * as React from 'react'
import {connect} from 'react-redux'
import {selectors} from '../labware-ingred/reducers'
import * as wellSelectionSelectors from '../top-selectors/well-contents'
import {editModeIngredientGroup, deleteIngredient, openRenameLabwareForm} from '../labware-ingred/actions'
import type {BaseState, ThunkDispatch} from '../types'
import {editModeIngredientGroup, deleteWellsContents, openRenameLabwareForm} from '../labware-ingred/actions'
import type {Dispatch} from 'redux'
import type {BaseState} from '../types'

import IngredientsList from '../components/IngredientsList'

type Props = React.ElementProps<typeof IngredientsList>

type DP = {
editModeIngredientGroup: $ElementType<Props, 'editModeIngredientGroup'>,
deleteIngredient: $ElementType<Props, 'deleteIngredient'>,
deleteWellsContents: $ElementType<Props, 'deleteWellsContents'>,
openRenameLabwareForm: $ElementType<Props, 'openRenameLabwareForm'>,
}

type SP = $Diff<Props, DP>
type SP = $Diff<Props, DP> & {_labwareId: ?string}

function mapStateToProps (state: BaseState): SP {
const container = selectors.getSelectedContainer(state)
const _labwareId = container && container.id

return {
renameLabwareFormMode: selectors.getRenameLabwareFormMode(state),
liquidGroupsById: selectors.getLiquidGroupsById(state),
labwareWellContents: (container && selectors.getIngredientLocations(state)[container.id]) || {},
selectedIngredientGroupId: wellSelectionSelectors.getSelectedWellsCommonIngredId(state),
selected: false,
_labwareId,
}
}

function mapDispatchToProps (dispatch: ThunkDispatch<*>): DP {
function mergeProps (stateProps: SP, dispatchProps: {dispatch: Dispatch<*>}): Props {
const {dispatch} = dispatchProps
const {_labwareId, ...passThruProps} = stateProps
return {
...passThruProps,
editModeIngredientGroup: (args) => dispatch(editModeIngredientGroup(args)),
deleteIngredient: (args) => dispatch(deleteIngredient(args)),
deleteWellsContents: (args) => dispatch(deleteWellsContents({...args, labwareId: _labwareId})),
openRenameLabwareForm: () => dispatch(openRenameLabwareForm()),
}
}

export default connect(mapStateToProps, mapDispatchToProps)(IngredientsList)
export default connect(mapStateToProps, null, mergeProps)(IngredientsList)
56 changes: 33 additions & 23 deletions protocol-designer/src/labware-ingred/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,34 +123,44 @@ export const moveLabware = (toSlot: DeckSlot) => (dispatch: Dispatch<MoveLabware
}
}

type DeleteIngredientPrepayload = {
wellName?: string,
groupId: string,
}

export type DeleteIngredient = {|
type: 'DELETE_INGREDIENT',
export type DeleteWellsContents = {
type: 'DELETE_WELLS_CONTENTS',
payload: {
containerId: string,
} & DeleteIngredientPrepayload,
|}
liquidGroupId: string,
labwareId: string,
wells: Array<string>,
},
}

export const deleteIngredient = (payload: DeleteIngredientPrepayload) => (dispatch: Dispatch<DeleteIngredient>, getState: GetState) => {
const container = selectors.getSelectedContainer(getState())
if (!container || !container.id) {
console.warn('Tried to delete ingredient with no selected container')
return null
}
export const deleteWellsContents = (
payload: $PropertyType<DeleteWellsContents, 'payload'>
) => ({
type: 'DELETE_WELLS_CONTENTS',
payload,
})

return dispatch({
type: 'DELETE_INGREDIENT',
payload: {
...payload,
containerId: container.id,
},
})
export type DeleteLiquidGroup = {
type: 'DELETE_LIQUID_GROUP',
payload: string, // liquid group id
}

export const deleteLiquidGroup = (liquidGroupId: string) =>
(dispatch: Dispatch<DeleteLiquidGroup>, getState: GetState) => {
const allLiquidGroupsOnDeck = selectors.getLiquidGroupsOnDeck(getState())
const liquidIsOnDeck = allLiquidGroupsOnDeck.includes(liquidGroupId)
// TODO: Ian 2018-10-22 we will eventually want to replace
// this window.confirm with a modal
const okToDelete = liquidIsOnDeck
? global.confirm('This liquid has been placed on the deck, are you sure you want to delete it?')
: true
if (okToDelete) {
return dispatch({
type: 'DELETE_LIQUID_GROUP',
payload: liquidGroupId,
})
}
}

// TODO test this thunk
export type EditIngredient = {
type: 'EDIT_INGREDIENT',
Expand Down
Loading

0 comments on commit 49e6823

Please sign in to comment.