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(protocol-designer): deleting staging area also deletes 4th colum… #16701

Merged
merged 3 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"show_default_tips": "Show default tips",
"show_tips": "Show incompatible tips",
"slots_limit_reached": "Slots limit reached",
"staging_area_has_labware": "This staging area slot has labware",
"staging_area_will_delete_labware": "The staging area slot that you are about to delete has labware placed on it. If you make these changes to your protocol starting deck, the labware will be deleted as well.",
"stagingArea": "Staging area",
"swap_pipettes": "Swap pipettes",
"tell_us": "Tell us about your protocol",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { fireEvent, screen } from '@testing-library/react'
import { describe, it, beforeEach, vi, expect } from 'vitest'
import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../assets/localization'
import { ConfirmDeleteStagingAreaModal } from '..'

jerader marked this conversation as resolved.
Show resolved Hide resolved
const render = (
props: React.ComponentProps<typeof ConfirmDeleteStagingAreaModal>
jerader marked this conversation as resolved.
Show resolved Hide resolved
) => {
return renderWithProviders(<ConfirmDeleteStagingAreaModal {...props} />, {
i18nInstance: i18n,
})[0]
}

describe('ConfirmDeleteStagingAreaModal', () => {
let props: React.ComponentProps<typeof ConfirmDeleteStagingAreaModal>
jerader marked this conversation as resolved.
Show resolved Hide resolved

beforeEach(() => {
props = {
onClose: vi.fn(),
onConfirm: vi.fn(),
}
})
it('renders the text and buttons work as expected', () => {
render(props)
screen.getByText('This staging area slot has labware')
screen.getByText(
'The staging area slot that you are about to delete has labware placed on it. If you make these changes to your protocol starting deck, the labware will be deleted as well.'
)
fireEvent.click(screen.getByText('Cancel'))
expect(props.onClose).toHaveBeenCalled()
fireEvent.click(screen.getByText('Continue'))
expect(props.onConfirm).toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useTranslation } from 'react-i18next'
import { createPortal } from 'react-dom'
import {
Flex,
JUSTIFY_END,
Modal,
PrimaryButton,
SecondaryButton,
SPACING,
StyledText,
} from '@opentrons/components'
import { getTopPortalEl } from '../../components/portals/TopPortal'
import { HandleEnter } from '../../atoms/HandleEnter'

interface ConfirmDeleteStagingAreaModalProps {
onClose: () => void
onConfirm: () => void
}
export function ConfirmDeleteStagingAreaModal(
props: ConfirmDeleteStagingAreaModalProps
): JSX.Element {
const { onClose, onConfirm } = props
const { t, i18n } = useTranslation(['create_new_protocol', 'shared'])

return createPortal(
<HandleEnter onEnter={onConfirm}>
<Modal
zIndexOverlay={11}
title={t('staging_area_has_labware')}
type="info"
onClose={onClose}
footer={
<Flex
justifyContent={JUSTIFY_END}
gridGap={SPACING.spacing8}
padding={SPACING.spacing24}
>
<SecondaryButton
onClick={() => {
onClose()
}}
>
{t('shared:cancel')}
</SecondaryButton>
<PrimaryButton onClick={onConfirm}>
{i18n.format(t('shared:continue'), 'capitalize')}
</PrimaryButton>
</Flex>
}
>
<StyledText desktopStyle="bodyDefaultRegular">
{t('staging_area_will_delete_labware')}
</StyledText>
</Modal>
</HandleEnter>,
getTopPortalEl()
)
}
1 change: 1 addition & 0 deletions protocol-designer/src/organisms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './Alerts'
export * from './AnnouncementModal'
export * from './AssignLiquidsModal'
export * from './BlockingHintModal'
export * from './ConfirmDeleteStagingAreaModal'
export * from './DefineLiquidsModal'
export * from './EditInstrumentsModal'
export * from './EditNickNameModal'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const OT2_STANDARD_DECK_VIEW_LAYER_BLOCK_LIST: string[] = [
'fixedTrash',
]
export const lightFill = COLORS.grey35
const darkFill = COLORS.grey60
export const darkFill = COLORS.grey60

export function DeckSetupContainer(props: DeckSetupTabType): JSX.Element {
const { tab } = props
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import values from 'lodash/values'
import { Fragment, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'

import { Module } from '@opentrons/components'
Expand Down Expand Up @@ -28,7 +28,9 @@ import { DeckItemHover } from './DeckItemHover'
import { SlotOverflowMenu } from './SlotOverflowMenu'
import { HoveredItems } from './HoveredItems'
import { SelectedHoveredItems } from './SelectedHoveredItems'
import { getAdjacentLabware } from './utils'

import type { ComponentProps, Dispatch, SetStateAction } from 'react'
import type { ModuleTemporalProperties } from '@opentrons/step-generation'
import type {
AddressableArea,
Expand All @@ -55,7 +57,7 @@ interface DeckSetupDetailsProps extends DeckSetupTabType {
hoveredFixture: Fixture | null
hoveredLabware: string | null
hoveredModule: ModuleModel | null
setHover: React.Dispatch<React.SetStateAction<string | null>>
setHover: Dispatch<SetStateAction<string | null>>
showGen1MultichannelCollisionWarnings: boolean
stagingAreaCutoutIds: CutoutId[]
selectedZoomInSlot?: DeckSlotId
Expand Down Expand Up @@ -83,9 +85,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
)
const selectedSlotInfo = useSelector(selectors.getZoomedInSlotInfo)
const { selectedSlot } = selectedSlotInfo
const [menuListId, setShowMenuListForId] = React.useState<DeckSlotId | null>(
null
)
const [menuListId, setShowMenuListForId] = useState<DeckSlotId | null>(null)
const dispatch = useDispatch<any>()

const {
Expand All @@ -100,7 +100,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
deckDef,
})
// initiate the slot's info
React.useEffect(() => {
useEffect(() => {
dispatch(
editSlotInfo({
createdNestedLabwareForSlot,
Expand Down Expand Up @@ -132,6 +132,15 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
? getSlotsWithCollisions(deckDef, allModules)
: []

const adjacentLabware =
preSelectedFixture != null && selectedSlot.cutout != null
? getAdjacentLabware(
preSelectedFixture,
selectedSlot.cutout,
activeDeckSetup.labware
)
: null

return (
<>
{/* all modules */}
Expand All @@ -146,7 +155,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
const moduleDef = getModuleDef2(moduleOnDeck.model)
const getModuleInnerProps = (
moduleState: ModuleTemporalProperties['moduleState']
): React.ComponentProps<typeof Module>['innerProps'] => {
): ComponentProps<typeof Module>['innerProps'] => {
if (moduleState.type === THERMOCYCLER_MODULE_TYPE) {
let lidMotorState = 'unknown'
if (tab === 'startingDeck' || moduleState.lidOpen) {
Expand Down Expand Up @@ -186,7 +195,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
zDimension: labwareLoadedOnModule?.def.dimensions.zDimension ?? 0,
}
return moduleOnDeck.slot !== selectedSlot.slot ? (
<React.Fragment key={moduleOnDeck.id}>
<Fragment key={moduleOnDeck.id}>
<Module
key={moduleOnDeck.id}
x={slotPosition[0]}
Expand Down Expand Up @@ -238,7 +247,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
/>
) : null}
</Module>
</React.Fragment>
</Fragment>
) : null
})}

Expand Down Expand Up @@ -276,7 +285,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
})
.map(addressableArea => {
return (
<React.Fragment key={addressableArea.id}>
<Fragment key={addressableArea.id}>
<DeckItemHover
isSelected={selectedZoomInSlot != null}
hover={hover}
Expand All @@ -291,18 +300,18 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
itemId={addressableArea.id}
tab={tab}
/>
</React.Fragment>
</Fragment>
)
})}
{/* all labware on deck NOT those in modules */}
{allLabware.map(labware => {
if (
labware.slot === 'offDeck' ||
allModules.some(m => m.id === labware.slot) ||
allLabware.some(lab => lab.id === labware.slot)
allLabware.some(lab => lab.id === labware.slot) ||
labware.id === adjacentLabware?.id
)
return null

const slotPosition = getPositionFromSlotId(labware.slot, deckDef)
const slotBoundingBox = getAddressableAreaFromSlotId(
labware.slot,
Expand All @@ -313,7 +322,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
return null
}
return labware.slot !== selectedSlot.slot ? (
<React.Fragment key={labware.id}>
<Fragment key={labware.id}>
<LabwareOnDeck
x={slotPosition[0]}
y={slotPosition[1]}
Expand All @@ -331,7 +340,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
itemId={labware.slot}
tab={tab}
/>
</React.Fragment>
</Fragment>
) : null
})}

Expand Down Expand Up @@ -376,7 +385,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
? slotForOnTheDeck
: allModules.find(module => module.id === slotForOnTheDeck)?.slot
return (
<React.Fragment key={labware.id}>
<Fragment key={labware.id}>
<LabwareOnDeck
x={slotPosition[0]}
y={slotPosition[1]}
Expand All @@ -394,7 +403,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
itemId={slotOnDeck ?? ''}
tab={tab}
/>
</React.Fragment>
</Fragment>
)
})}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
MAGNETIC_MODULE_TYPE,
MAGNETIC_MODULE_V1,
MAGNETIC_MODULE_V2,
MODULE_MODELS,
OT2_ROBOT_TYPE,
} from '@opentrons/shared-data'

Expand All @@ -46,6 +47,7 @@ import { selectors } from '../../../labware-ingred/selectors'
import { useKitchen } from '../../../organisms/Kitchen/hooks'
import { getDismissedHints } from '../../../tutorial/selectors'
import { createContainerAboveModule } from '../../../step-forms/actions/thunks'
import { ConfirmDeleteStagingAreaModal } from '../../../organisms'
import { FIXTURES, MOAM_MODELS } from './constants'
import { getSlotInformation } from '../utils'
import { getModuleModelsBySlot, getDeckErrors } from './utils'
Expand All @@ -71,6 +73,9 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
const { makeSnackbar } = useKitchen()
const selectedSlotInfo = useSelector(selectors.getZoomedInSlotInfo)
const robotType = useSelector(getRobotType)
const [showDeleteLabwareModal, setShowDeleteLabwareModal] = useState<
ModuleModel | 'clear' | null
>(null)
const isDismissedModuleHint = useSelector(getDismissedHints).includes(
'change_magnet_module_model'
)
Expand Down Expand Up @@ -154,6 +159,7 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
createdModuleForSlot,
createdLabwareForSlot,
createFixtureForSlots,
matchingLabwareFor4thColumn,
} = getSlotInformation({ deckSetup, slot })

let fixtures: Fixture[] = []
Expand Down Expand Up @@ -218,6 +224,10 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
if (createdNestedLabwareForSlot != null) {
dispatch(deleteContainer({ labwareId: createdNestedLabwareForSlot.id }))
}
// clear labware on staging area 4th column slot
if (matchingLabwareFor4thColumn != null) {
dispatch(deleteContainer({ labwareId: matchingLabwareFor4thColumn.id }))
}
}
handleResetToolbox()
setSelectedHardware(null)
Expand Down Expand Up @@ -278,6 +288,26 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
}
return (
<>
{showDeleteLabwareModal != null ? (
<ConfirmDeleteStagingAreaModal
onClose={() => {
setShowDeleteLabwareModal(null)
}}
onConfirm={() => {
if (showDeleteLabwareModal === 'clear') {
handleClear()
handleResetToolbox()
} else if (MODULE_MODELS.includes(showDeleteLabwareModal)) {
setSelectedHardware(showDeleteLabwareModal)
dispatch(selectFixture({ fixture: null }))
dispatch(selectModule({ moduleModel: showDeleteLabwareModal }))
dispatch(selectLabware({ labwareDefUri: null }))
dispatch(selectNestedLabware({ nestedLabwareDefUri: null }))
}
setShowDeleteLabwareModal(null)
}}
/>
) : null}
{changeModuleWarning}
<Toolbox
height="calc(100vh - 64px)"
Expand All @@ -302,8 +332,12 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
</StyledText>
}
onCloseClick={() => {
handleClear()
handleResetToolbox()
if (matchingLabwareFor4thColumn != null) {
setShowDeleteLabwareModal('clear')
} else {
handleClear()
handleResetToolbox()
}
}}
onConfirmClick={() => {
handleConfirm()
Expand Down Expand Up @@ -407,6 +441,12 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
!isDismissedModuleHint
) {
displayModuleWarning(true)
} else if (
selectedFixture === 'stagingArea' ||
(selectedFixture === 'wasteChuteAndStagingArea' &&
matchingLabwareFor4thColumn != null)
) {
setShowDeleteLabwareModal(model)
} else {
setSelectedHardware(model)
dispatch(selectFixture({ fixture: null }))
Expand Down
Loading
Loading