Skip to content

Commit

Permalink
feat(protocol-designer): off-deck labware view (#16140)
Browse files Browse the repository at this point in the history
closes AUTH-686 AUTH-740, partially fixes AUTH-738
  • Loading branch information
jerader authored Aug 28, 2024
1 parent 392bf1c commit f2ff007
Show file tree
Hide file tree
Showing 17 changed files with 555 additions and 204 deletions.
16 changes: 13 additions & 3 deletions protocol-designer/src/Bouncing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ interface Location {
y: number
}

const initialPosition = {
x: 100,
y: 100,
}

const initialVelocity = {
x: 2,
y: 2,
}

export const Bouncing = (): JSX.Element => {
const [isDragging, setIsDragging] = React.useState<boolean>(false)
const [position, setPosition] = React.useState<Location>({ x: 100, y: 100 })
const [velocity, setVelocity] = React.useState<Location>({ x: 2, y: 2 })
const [position, setPosition] = React.useState<Location>(initialPosition)
const [velocity, setVelocity] = React.useState<Location>(initialVelocity)
const [isPaused, setIsPaused] = React.useState<boolean>(false)
const [isStopped, setIsStopped] = React.useState<boolean>(false)
const [isStopped, setIsStopped] = React.useState<boolean>(true)
const [showFeatureFlags, setShowFeatureFlags] = React.useState<boolean>(false)

const divSize = 50
Expand Down
1 change: 1 addition & 0 deletions protocol-designer/src/assets/localization/en/shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"heatershakermoduletype": "Heater-shaker Module",
"labware_name_conflict": "Duplicate labware name",
"labware": "Labware",
"labware_detail": "Labware detail",
"left_right": "Left+Right",
"left": "Left",
"liquid": "Liquid",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
"deck_hardware": "Deck hardware",
"done": "Done",
"duplicate": "Duplicate",
"edit_protocol": "Edit protocol",
"edit_labware": "Edit labware",
"edit_protocol": "Edit protocol",
"edit_slot": "Edit slot",
"edit": "Edit",
"heater_shaker_adjacent_to": "A module is adjacent to this slot. The Heater-Shaker cannot be placed next to a module",
"heater_shaker_adjacent": "A Heater-Shaker is adjacent to this slot. Modules cannot be placed next to a Heater-Shaker",
"heater_shaker_trash": "The heater-shaker cannot be next to the trash bin",
"labware": "Labware",
"liquids": "Liquids",
"off_deck_labware": "Off-deck Labware",
"offDeck": "Off-deck",
"onDeck": "On deck",
"one_item": "No more than 1 {{hardware}} allowed on the deck at one time",
"protocol_starting_deck": "Protocol starting deck",
"rename_lab": "Rename labware",
Expand Down
39 changes: 27 additions & 12 deletions protocol-designer/src/organisms/SlotDetailsContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ import { SlotInformation } from '../../organisms/SlotInformation'
import { getYPosition } from './utils'

import type { DeckSlotId, RobotType } from '@opentrons/shared-data'
import type { ContentsByWell } from '../../labware-ingred/types'

interface SlotDetailContainerProps {
robotType: RobotType
slot: DeckSlotId | null
offDeckLabwareId?: string
}

export function SlotDetailsContainer(
props: SlotDetailContainerProps
): JSX.Element | null {
const { robotType, slot } = props
const { robotType, slot, offDeckLabwareId } = props
const { t } = useTranslation('shared')
const location = useLocation()
const deckSetup = useSelector(getDeckSetupForActiveItem)
Expand All @@ -29,15 +31,21 @@ export function SlotDetailsContainer(
)
const allIngredNamesIds = useSelector(selectors.allIngredientNamesIds)

if (slot == null) {
if (slot == null || (slot === 'offDeck' && offDeckLabwareId == null)) {
return null
}

const {
modules: deckSetupModules,
labware: deckSetupLabwares,
additionalEquipmentOnDeck,
} = deckSetup

const offDeckLabwareDisplayName =
offDeckLabwareId != null
? deckSetupLabwares[offDeckLabwareId].def.metadata.displayName
: null

const moduleOnSlot = Object.values(deckSetupModules).find(
module => module.slot === slot
)
Expand All @@ -62,10 +70,13 @@ export function SlotDetailsContainer(

const liquidsLabware =
nestedLabwareOnSlot != null ? nestedLabwareOnSlot : labwareOnSlot
const wellContents =
allWellContentsForActiveItem && liquidsLabware != null
? allWellContentsForActiveItem[liquidsLabware.id]
: null

let wellContents: ContentsByWell | null = null
if (offDeckLabwareId != null && allWellContentsForActiveItem != null) {
wellContents = allWellContentsForActiveItem[offDeckLabwareId]
} else if (allWellContentsForActiveItem != null && liquidsLabware != null) {
wellContents = allWellContentsForActiveItem[liquidsLabware.id]
}

const liquids =
wellContents != null
Expand All @@ -84,14 +95,18 @@ export function SlotDetailsContainer(
.filter(Boolean)

const labwares: string[] = []
if (labwareOnSlotDisplayName != null) {
labwares.push(labwareOnSlotDisplayName)
}
if (nestedLabwareOnSlotDisplayName != null) {
labwares.push(nestedLabwareOnSlotDisplayName)
if (offDeckLabwareDisplayName != null) {
labwares.push(offDeckLabwareDisplayName)
} else {
if (labwareOnSlotDisplayName != null) {
labwares.push(labwareOnSlotDisplayName)
}
if (nestedLabwareOnSlotDisplayName != null) {
labwares.push(nestedLabwareOnSlotDisplayName)
}
}

return location.pathname === '/designer' ? (
return location.pathname === '/designer' && slot !== 'offDeck' ? (
<RobotCoordsForeignObject
width="15.8125rem"
height="26.75rem"
Expand Down
11 changes: 7 additions & 4 deletions protocol-designer/src/organisms/SlotInformation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@ export const SlotInformation: React.FC<SlotInformationProps> = ({
fixtures = [],
}) => {
const { t } = useTranslation('shared')
const isOffDeck = location === 'offDeck'
return (
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing12}
width="100%"
>
<Flex gridGap={SPACING.spacing8} alignItems={ALIGN_CENTER}>
<DeckInfoLabel deckLabel={location} />
{isOffDeck ? null : <DeckInfoLabel deckLabel={location} />}
<StyledText desktopStyle="headingSmallBold">
{t('slot_detail')}
{t(isOffDeck ? 'labware_detail' : 'slot_detail')}
</StyledText>
</Flex>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing4}>
Expand All @@ -57,8 +58,10 @@ export const SlotInformation: React.FC<SlotInformationProps> = ({
<StackInfoList title={t('liquid')} items={liquids} />
)}
<StackInfoList title={t('labware')} items={labwares} />
<StackInfoList title={t('module')} items={modules} />
{robotType === FLEX_ROBOT_TYPE ? (
{isOffDeck ? null : (
<StackInfoList title={t('module')} items={modules} />
)}
{robotType === FLEX_ROBOT_TYPE && !isOffDeck ? (
<StackInfoList title={t('fixtures')} items={fixtures} />
) : null}
</Flex>
Expand Down
54 changes: 30 additions & 24 deletions protocol-designer/src/pages/Designer/DeckSetup/DeckItemHover.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { css } from 'styled-components'
import {
ALIGN_CENTER,
Expand All @@ -8,47 +9,58 @@ import {
DISPLAY_FLEX,
Flex,
JUSTIFY_CENTER,
Link,
POSITION_ABSOLUTE,
PRODUCT,
RobotCoordsForeignDiv,
StyledText,
} from '@opentrons/components'
import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations'
import { START_TERMINAL_ITEM_ID } from '../../../steplist'
import { SlotOverflowMenu } from './SlotOverflowMenu'

import type { CoordinateTuple, Dimensions } from '@opentrons/shared-data'
import type {
CoordinateTuple,
DeckSlotId,
Dimensions,
} from '@opentrons/shared-data'
import type { TerminalItemId } from '../../../steplist'

interface DeckItemHoverProps {
addEquipment: (slotId: string) => void
hover: string | null
setHover: React.Dispatch<React.SetStateAction<string | null>>
slotBoundingBox: Dimensions
slotId: string
// can be slotId or labwareId (for off-deck labware)
itemId: string
slotPosition: CoordinateTuple | null
setShowMenuListForId: React.Dispatch<React.SetStateAction<string | null>>
menuListId: DeckSlotId | null
selectedTerminalItemId?: TerminalItemId | null
}

export function DeckItemHover(props: DeckItemHoverProps): JSX.Element | null {
const {
addEquipment,
hover,
selectedTerminalItemId,
setHover,
slotBoundingBox,
slotId,
itemId,
setShowMenuListForId,
menuListId,
slotPosition,
} = props
const { t } = useTranslation('starting_deck_state')
const [showMenuList, setShowMenuList] = React.useState<boolean>(false)

const deckSetup = useSelector(getDeckSetupForActiveItem)
const offDeckLabware = Object.values(deckSetup.labware).find(
lw => lw.id === itemId
)
if (
selectedTerminalItemId !== START_TERMINAL_ITEM_ID ||
slotPosition === null
)
return null

const hoverOpacity = hover != null && hover === slotId ? '1' : '0'
const hoverOpacity =
(hover != null && hover === itemId) || menuListId === itemId ? '1' : '0'

return (
<RobotCoordsForeignDiv
Expand All @@ -74,13 +86,13 @@ export function DeckItemHover(props: DeckItemHoverProps): JSX.Element | null {
borderRadius: BORDERS.borderRadius8,
},
onMouseEnter: () => {
setHover(slotId)
setHover(itemId)
},
onMouseLeave: () => {
setHover(null)
},
onClick: () => {
setShowMenuList(true)
setShowMenuListForId(itemId)
},
}}
>
Expand All @@ -91,25 +103,19 @@ export function DeckItemHover(props: DeckItemHoverProps): JSX.Element | null {
opacity: ${hoverOpacity};
`}
>
<a
<Link
role="button"
onClick={() => {
setShowMenuList(true)
setShowMenuListForId(itemId)
}}
>
<StyledText desktopStyle="bodyDefaultSemiBold">
{slotId === 'offDeck' ? t('edit_labware') : t('edit_slot')}
{offDeckLabware?.slot === 'offDeck'
? t('edit_labware')
: t('edit_slot')}
</StyledText>
</a>
</Link>
</Flex>
{showMenuList ? (
<SlotOverflowMenu
slot={slotId}
addEquipment={addEquipment}
setShowMenuList={setShowMenuList}
xSlotPosition={slotPosition[0]}
ySlotPosition={slotPosition[1]}
/>
) : null}
</RobotCoordsForeignDiv>
)
}
Loading

0 comments on commit f2ff007

Please sign in to comment.