Skip to content

Commit

Permalink
feat(protocol-designer): bring back deck setup drag & drop (#17477)
Browse files Browse the repository at this point in the history
closes AUTH-1276

This PR brings back drag/drop to deck setup with the same functionality
as it had in the pre-redesign. You can only drag top-level labware and
can not drag: modules or adapters. When we add stacking capabilities, we
will need to refactor it to account for that level of stacking.
  • Loading branch information
jerader authored Feb 14, 2025
1 parent ed6400f commit e64ae45
Show file tree
Hide file tree
Showing 22 changed files with 1,365 additions and 365 deletions.
4 changes: 4 additions & 0 deletions components/src/hardware-sim/Deck/RobotCoordsForeignDiv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface RobotCoordsForeignDivProps {
innerDivProps?: ComponentProps<typeof Box>
transformWithSVG?: boolean
extraTransform?: string
/** optional data-testid to test foreignObjects in cypress */
dataTestId?: string
}

export const RobotCoordsForeignDiv = (
Expand All @@ -27,11 +29,13 @@ export const RobotCoordsForeignDiv = (
innerDivProps,
transformWithSVG = false,
extraTransform = '',
dataTestId = '',
} = props

const transform = `scale(1, -1) ${extraTransform}`
return (
<foreignObject
data-testid={dataTestId}
{...{ x, y, height, width, ...outerProps }}
transform={transformWithSVG ? transform : extraTransform}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import type { DeckDefinition, DeckSlot } from '@opentrons/shared-data'

export interface RobotCoordinateSpaceWithRefRenderProps {
deckSlotsById: { [slotId: string]: DeckSlot }
// used for PD's drag/drop DragPreview
getRobotCoordsFromDOMCoords: (
domX: number,
domY: number
) => { x: number; y: number }
}

interface RobotCoordinateSpaceWithRefProps extends ComponentProps<typeof Svg> {
Expand All @@ -20,6 +25,21 @@ export function RobotCoordinateSpaceWithRef(
): JSX.Element | null {
const { children, deckDef, viewBox, zoomed = false, ...restProps } = props
const wrapperRef = useRef<SVGSVGElement>(null)
const getRobotCoordsFromDOMCoords: RobotCoordinateSpaceWithRefRenderProps['getRobotCoordsFromDOMCoords'] = (
x,
y
) => {
if (wrapperRef.current == null) return { x: 0, y: 0 }

const cursorPoint = wrapperRef.current.createSVGPoint()

cursorPoint.x = x
cursorPoint.y = y

return cursorPoint.matrixTransform(
wrapperRef.current.getScreenCTM()?.inverse()
)
}

if (deckDef == null && viewBox == null) return null

Expand All @@ -45,7 +65,7 @@ export function RobotCoordinateSpaceWithRef(
height="100%"
{...restProps}
>
{children?.({ deckSlotsById })}
{children?.({ deckSlotsById, getRobotCoordsFromDOMCoords })}
</Svg>
)
}
5 changes: 5 additions & 0 deletions components/src/icons/icon-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,4 +818,9 @@ export const ICON_DATA_BY_NAME: Record<
'M3.16848 9.22683C4.91915 7.43693 7.33915 6.33359 9.99973 6.33359C12.6604 6.33359 15.0804 7.43697 16.8311 9.22693L17.9996 8.03818C15.9522 5.95529 13.1239 4.66699 9.99973 4.66699C6.87563 4.66699 4.0473 5.95525 2 8.03809L3.16848 9.22683ZM6.1685 12.2783C7.15141 11.2696 8.51069 10.6495 9.99953 10.6495C11.4886 10.6495 12.848 11.2698 13.8309 12.2787L14.9994 11.0899C13.7199 9.78811 11.9521 8.98291 9.99953 8.98291C8.04712 8.98291 6.27954 9.78795 5 11.0895L6.1685 12.2783ZM10.0002 14.9654C9.6831 14.9654 9.38403 15.1024 9.16876 15.3306L8.00012 14.1417C8.51196 13.6209 9.2191 13.2988 10.0002 13.2988C10.7811 13.2988 11.4881 13.6208 11.9999 14.1414L10.8313 15.3303C10.6161 15.1023 10.3171 14.9654 10.0002 14.9654Z',
viewBox: '0 0 20 20',
},
'no-icon': {
path:
'M9.88551 29.8885C12.1813 31.5538 14.9863 32.5294 18 32.5294C25.6771 32.5294 32 26.1981 32 18.2647C32 15.1922 31.0516 12.36 29.4435 10.0429L9.88551 29.8885ZM26.7212 7.10662L7.0116 27.1061C5.1288 24.6828 4 21.6168 4 18.2647C4 10.3313 10.3229 4 18 4C21.2847 4 24.3215 5.15901 26.7212 7.10662ZM18 36.5294C27.9411 36.5294 36 28.352 36 18.2647C36 8.17739 27.9411 0 18 0C8.05887 0 0 8.17739 0 18.2647C0 28.352 8.05887 36.5294 18 36.5294Z',
viewBox: '0 0 36 37',
},
}
51 changes: 23 additions & 28 deletions protocol-designer/cypress/support/SetupSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export enum SetupContent {
ModulePageH = 'Add your modules',
ModulePageB = 'Select modules to use in your protocol.',
EditProtocol = 'Edit protocol',
EditSlot = 'Edit slot',
EditLabware = 'Edit labware',
AddLabwareToDeck = 'Add hardware/labware',
EditHardwareLabwareOnDeck = 'Edit hardware/labware',
LabwareH = 'Labware',
Expand All @@ -126,6 +126,10 @@ export enum SetupContent {
Save = 'Save',
}

export const RegexSetupContent = {
slotText: /Edit (slot|labware)/i,
}

export enum SetupLocators {
Confirm = 'button:contains("Confirm")',
GoBack = 'button:contains("Go back")',
Expand Down Expand Up @@ -167,29 +171,18 @@ const chooseDeckSlot = (
| 'D3',
() => Cypress.Chainable<JQuery<HTMLElement>>
> = {
A1: () =>
cy.contains('foreignObject[x="0"][y="321"]', SetupContent.EditSlot),
A2: () =>
cy.contains('foreignObject[x="164"][y="321"]', SetupContent.EditSlot),
A3: () =>
cy.contains('foreignObject[x="328"][y="321"]', SetupContent.EditSlot),
B1: () =>
cy.contains('foreignObject[x="0"][y="214"]', SetupContent.EditSlot),
B2: () =>
cy.contains('foreignObject[x="164"][y="214"]', SetupContent.EditSlot),
B3: () =>
cy.contains('foreignObject[x="328"][y="214"]', SetupContent.EditSlot),
C1: () =>
cy.contains('foreignObject[x="0"][y="107"]', SetupContent.EditSlot),
C2: () =>
cy.contains('foreignObject[x="164"][y="107"]', SetupContent.EditSlot),
C3: () =>
cy.contains('foreignObject[x="328"][y="107"]', SetupContent.EditSlot),
D1: () => cy.contains('foreignObject[x="0"][y="0"]', SetupContent.EditSlot),
D2: () =>
cy.contains('foreignObject[x="164"][y="0"]', SetupContent.EditSlot),
D3: () =>
cy.contains('foreignObject[x="328"][y="0"]', SetupContent.EditSlot),
A1: () => cy.contains('[data-testid="A1"]', RegexSetupContent.slotText),
A2: () => cy.contains('[data-testid="A2"]', RegexSetupContent.slotText),
A3: () => cy.contains('[data-testid="A3"]', RegexSetupContent.slotText),
B1: () => cy.contains('[data-testid="B1"]', RegexSetupContent.slotText),
B2: () => cy.contains('[data-testid="B2"]', RegexSetupContent.slotText),
B3: () => cy.contains('[data-testid="B3"]', RegexSetupContent.slotText),
C1: () => cy.contains('[data-testid="C1"]', RegexSetupContent.slotText),
C2: () => cy.contains('[data-testid="C2"]', RegexSetupContent.slotText),
C3: () => cy.contains('[data-testid="C3"]', RegexSetupContent.slotText),
D1: () => cy.contains('[data-testid="D1"]', RegexSetupContent.slotText),
D2: () => cy.contains('[data-testid="D2"]', RegexSetupContent.slotText),
D3: () => cy.contains('[data-testid="D3"]', RegexSetupContent.slotText),
}

const slotAction = deckSlots[slot as keyof typeof deckSlots]
Expand Down Expand Up @@ -300,7 +293,10 @@ export const executeSetupSteps = (action: SetupActions): void => {
chooseDeckSlot('B3').click()
break
case SetupActions.ChoseDeckSlotC1:
chooseDeckSlot('C1').click()
chooseDeckSlot('C1')
.find('a[role="button"]')
.contains(RegexSetupContent.slotText)
.click({ force: true })
break
case SetupActions.ChoseDeckSlotC2:
chooseDeckSlot('C2').click()
Expand All @@ -309,7 +305,6 @@ export const executeSetupSteps = (action: SetupActions): void => {
chooseDeckSlot('C3').click()
break
case SetupActions.ChoseDeckSlotD1:
chooseDeckSlot('D1').click()
break
case SetupActions.ChoseDeckSlotD2:
chooseDeckSlot('D2').click()
Expand All @@ -321,7 +316,7 @@ export const executeSetupSteps = (action: SetupActions): void => {
cy.contains(SetupContent.AddLabwareToDeck).click()
break
case SetupActions.EditHardwareLabwareOnDeck:
cy.contains(SetupContent.EditHardwareLabwareOnDeck).click()
cy.contains(SetupContent.EditHardwareLabwareOnDeck).click({ force: true })
break
case SetupActions.ClickLabwareHeader:
cy.contains(SetupContent.LabwareH).click()
Expand All @@ -335,7 +330,7 @@ export const executeSetupSteps = (action: SetupActions): void => {
chooseDeckSlot('C2')
.find('.Box-sc-8ozbhb-0.kIDovv')
.find('a[role="button"]')
.contains(SetupContent.EditSlot)
.contains(RegexSetupContent.slotText)
.click({ force: true })
break
case SetupActions.SelectArmadillo96WellPlate: // New case for selecting Armadillo plate
Expand Down
25 changes: 3 additions & 22 deletions protocol-designer/src/assets/localization/en/deck.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,15 @@
"MODULE_INCOMPATIBLE_LABWARE_SWAP": "Swapping labware not possible due to module incompatibility",
"LABWARE_INCOMPATIBLE_WITH_ADAPTER": "Labware incompatible with this adapter"
},
"header": {
"end": "Click on labware to inspect the result of your protocol"
},
"off_deck": {
"slideout_title": "Off-deck labware",
"slideout_empty_state": "There is currently no off-deck labware in this step of the protocol"
},
"overlay": {
"name_labware": {
"nickname_placeholder": "Add a nickname?",
"add_liquids": "Add Liquids",
"leave_empty": "Leave Empty"
},
"edit": {
"name_and_liquids": "Name & Liquids",
"duplicate": "Duplicate",
"delete": "Delete"
},
"browse": {
"view_liquids": "View Liquids"
},
"slot": {
"add_labware": "Add Labware",
"drag_to_new_slot": "Drag To New Slot",
"place_here": "Place Here",
"add_adapter_or_labware": "Add Labware or Adapter",
"add_adapter": "Add adapter"
"place_here": "Place here",
"swap_labware": "Swap labware"
}
},
"inactive_deck": "hover on a step to see deck state"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ export function DeckSetupContainer(props: DeckSetupTabType): JSX.Element {
zoomed={zoomIn.slot != null}
borderRadius={BORDERS.borderRadius12}
>
{() => (
{({ getRobotCoordsFromDOMCoords }) => (
<>
{robotType === OT2_ROBOT_TYPE ? (
<DeckFromLayers
Expand Down Expand Up @@ -373,6 +373,7 @@ export function DeckSetupContainer(props: DeckSetupTabType): JSX.Element {
)}
<DeckSetupDetails
selectedZoomInSlot={zoomIn.slot ?? undefined}
getRobotCoordsFromDOMCoords={getRobotCoordsFromDOMCoords}
hoveredLabware={hoveredLabware}
hoveredModule={hoveredModule}
hoveredFixture={hoveredFixture}
Expand Down
Loading

0 comments on commit e64ae45

Please sign in to comment.