@opentrons/components 0.1.7-alpha.1 → 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/cssModuleSideEffect.ts +22 -0
  2. package/lib/assets/localization/en/index.d.ts +12 -0
  3. package/lib/assets/localization/index.d.ts +12 -0
  4. package/lib/atoms/AnimationVideo/index.d.ts +13 -0
  5. package/lib/atoms/InlineNotification/index.d.ts +1 -1
  6. package/lib/atoms/ListButton/ListButtonChildren/index.d.ts +0 -1
  7. package/lib/atoms/StyledText/PlaceholderStyledText.d.ts +14 -0
  8. package/lib/atoms/StyledText/index.d.ts +1 -0
  9. package/lib/atoms/buttons/BasicButton.d.ts +12 -0
  10. package/lib/atoms/buttons/index.d.ts +1 -0
  11. package/lib/atoms/index.d.ts +1 -0
  12. package/lib/hardware-sim/BaseDeck/BaseDeck.d.ts +10 -8
  13. package/lib/hardware-sim/Deck/FlexTrash.d.ts +4 -1
  14. package/lib/hardware-sim/Deck/MoveLabwareOnDeck.d.ts +3 -2
  15. package/lib/hardware-sim/Deck/RobotCoordsForeignDiv.d.ts +6 -5
  16. package/lib/hardware-sim/Deck/RobotCoordsForeignObject.d.ts +8 -5
  17. package/lib/hardware-sim/Deck/RobotCoordsText.d.ts +11 -1
  18. package/lib/hardware-sim/Deck/RobotWorkSpace.d.ts +30 -14
  19. package/lib/hardware-sim/Deck/__tests__/resolveLabwareLocation.test.d.ts +1 -0
  20. package/lib/hardware-sim/Deck/resolveLabwareLocation.d.ts +20 -0
  21. package/lib/hardware-sim/DeckConfigurator/AbsorbanceReaderItem.d.ts +11 -0
  22. package/lib/hardware-sim/DeckConfigurator/EmptyConfigItem.d.ts +11 -0
  23. package/lib/hardware-sim/DeckConfigurator/FlexStackerItem.d.ts +12 -0
  24. package/lib/hardware-sim/DeckConfigurator/HeaterShakerItem.d.ts +11 -0
  25. package/lib/hardware-sim/DeckConfigurator/MagneticBlockItem.d.ts +12 -0
  26. package/lib/hardware-sim/DeckConfigurator/StagingAreaConfigItem.d.ts +11 -0
  27. package/lib/hardware-sim/DeckConfigurator/{StaticFixture.d.ts → StaticItem.d.ts} +1 -1
  28. package/lib/hardware-sim/DeckConfigurator/TemperatureModuleItem.d.ts +11 -0
  29. package/lib/hardware-sim/DeckConfigurator/ThermocyclerItem.d.ts +11 -0
  30. package/lib/hardware-sim/DeckConfigurator/TrashBinConfigItem.d.ts +11 -0
  31. package/lib/hardware-sim/DeckConfigurator/WasteChuteConfigItem.d.ts +11 -0
  32. package/lib/hardware-sim/DeckConfigurator/constants.d.ts +5 -8
  33. package/lib/hardware-sim/DeckConfigurator/index.d.ts +4 -3
  34. package/lib/hardware-sim/Labware/CalibrationBlockRender.d.ts +15 -0
  35. package/lib/hardware-sim/Labware/Labware.d.ts +7 -8
  36. package/lib/hardware-sim/Labware/LabwareAdapter/index.d.ts +2 -2
  37. package/lib/hardware-sim/Labware/LabwareRender.d.ts +46 -5
  38. package/lib/hardware-sim/Labware/index.d.ts +1 -0
  39. package/lib/hardware-sim/Labware/labwareInternals/FilledWells.d.ts +3 -4
  40. package/lib/hardware-sim/Labware/labwareInternals/LabwareOutline.d.ts +20 -9
  41. package/lib/hardware-sim/Labware/labwareInternals/LabwareWellLabels.d.ts +2 -2
  42. package/lib/hardware-sim/Labware/labwareInternals/StaticLabware.d.ts +2 -2
  43. package/lib/hardware-sim/Labware/labwareInternals/StrokedWells.d.ts +2 -2
  44. package/lib/hardware-sim/Labware/labwareInternals/StyledWells.d.ts +2 -2
  45. package/lib/hardware-sim/Labware/labwareInternals/Well.d.ts +1 -2
  46. package/lib/hardware-sim/Labware/labwareInternals/WellLabels.d.ts +2 -2
  47. package/lib/hardware-sim/Module/index.d.ts +24 -2
  48. package/lib/hardware-sim/Pipette/PipetteRender.d.ts +2 -2
  49. package/lib/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpace.d.ts +5 -3
  50. package/lib/hocs/withStyleProps.d.ts +26 -0
  51. package/lib/hooks/useConditionalConfirm.d.ts +4 -0
  52. package/lib/icons/Icon.d.ts +7 -7
  53. package/lib/icons/ModuleIcon.d.ts +4 -3
  54. package/lib/instrument/index.d.ts +0 -2
  55. package/lib/legacy-hardware-sim/index.d.ts +0 -1
  56. package/lib/lists/index.d.ts +0 -1
  57. package/lib/molecules/CustomizeExpandButton/__tests__/CustomizeExpandButton.test.d.ts +1 -0
  58. package/lib/molecules/CustomizeExpandButton/index.d.ts +29 -0
  59. package/lib/molecules/DeckInfoLabel/index.d.ts +8 -16
  60. package/lib/molecules/index.d.ts +2 -1
  61. package/lib/organisms/CommandText/index.d.ts +2 -2
  62. package/lib/organisms/CommandText/useCommandTextString/index.d.ts +2 -2
  63. package/lib/organisms/CommandText/useCommandTextString/utils/getLabwareDefinitionsFromCommands.d.ts +2 -2
  64. package/lib/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.d.ts +3 -3
  65. package/lib/organisms/CommandText/useCommandTextString/utils/getLabwareName.d.ts +2 -2
  66. package/lib/organisms/FixtureOption/index.d.ts +2 -2
  67. package/lib/organisms/ProtocolDeck/index.d.ts +10 -0
  68. package/lib/organisms/ProtocolTimelineScrubber/CommandItem.d.ts +2 -2
  69. package/lib/organisms/ProtocolTimelineScrubber/utils.d.ts +2 -2
  70. package/lib/organisms/index.d.ts +1 -0
  71. package/lib/primitives/ForeignObject.d.ts +8 -18
  72. package/lib/primitives/style-props.d.ts +1 -1
  73. package/lib/structure/index.d.ts +2 -3
  74. package/package.json +6 -3
  75. package/src/alerts/alerts.module.css +2 -2
  76. package/src/assets/localization/en/deck_configuration.json +12 -0
  77. package/src/assets/localization/en/index.ts +2 -0
  78. package/src/assets/localization/en/protocol_command_text.json +1 -1
  79. package/src/atoms/AnimationVideo/__tests__/AnimationVideo.test.tsx +40 -0
  80. package/src/atoms/AnimationVideo/index.tsx +44 -0
  81. package/src/atoms/CheckboxField/__tests__/CheckboxField.test.tsx +0 -3
  82. package/src/atoms/CheckboxField/index.tsx +1 -1
  83. package/src/atoms/InlineNotification/index.tsx +86 -70
  84. package/src/atoms/ListButton/ListButton.stories.tsx +21 -7
  85. package/src/atoms/ListButton/ListButtonChildren/index.ts +0 -1
  86. package/src/atoms/ListItem/ListItemChildren/ListItemDescriptor.tsx +2 -2
  87. package/src/atoms/MenuList/OverflowBtn.tsx +39 -41
  88. package/src/atoms/Snackbar/Snackbar.stories.tsx +59 -23
  89. package/src/atoms/Snackbar/index.tsx +48 -38
  90. package/src/atoms/StyledText/PlaceholderStyledText.tsx +46 -0
  91. package/src/atoms/StyledText/index.ts +1 -0
  92. package/src/atoms/StyledText/placeholderstyledtext.module.css +25 -0
  93. package/src/atoms/buttons/BasicButton.tsx +100 -0
  94. package/src/atoms/buttons/SecondaryButton.tsx +5 -2
  95. package/src/atoms/buttons/__tests__/BasicButton.test.tsx +87 -0
  96. package/src/atoms/buttons/buttons.stories.tsx +91 -55
  97. package/src/atoms/buttons/index.ts +1 -0
  98. package/src/atoms/index.ts +1 -0
  99. package/src/buttons/buttons.module.css +92 -92
  100. package/src/controls/styles.module.css +15 -15
  101. package/src/forms/Select.module.css +23 -23
  102. package/src/forms/forms.module.css +38 -37
  103. package/src/hardware-sim/BaseDeck/BaseDeck.stories.tsx +7 -7
  104. package/src/hardware-sim/BaseDeck/BaseDeck.tsx +98 -61
  105. package/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx +22 -28
  106. package/src/hardware-sim/BaseDeck/basedeck.module.css +20 -0
  107. package/src/hardware-sim/Deck/FlexTrash.tsx +50 -30
  108. package/src/hardware-sim/Deck/MoveLabwareOnDeck.stories.tsx +2 -2
  109. package/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx +122 -156
  110. package/src/hardware-sim/Deck/RobotCoordsForeignDiv.tsx +35 -27
  111. package/src/hardware-sim/Deck/RobotCoordsForeignObject.tsx +32 -29
  112. package/src/hardware-sim/Deck/RobotCoordsText.tsx +12 -11
  113. package/src/hardware-sim/Deck/RobotWorkSpace.tsx +74 -54
  114. package/src/hardware-sim/Deck/SlotLabels.tsx +51 -51
  115. package/src/hardware-sim/Deck/__tests__/resolveLabwareLocation.test.ts +138 -0
  116. package/src/hardware-sim/Deck/deck.module.css +54 -0
  117. package/src/hardware-sim/Deck/resolveLabwareLocation.ts +154 -0
  118. package/src/hardware-sim/DeckConfigurator/{FlexStackerFixture.tsx → AbsorbanceReaderItem.tsx} +34 -31
  119. package/src/hardware-sim/DeckConfigurator/{EmptyConfigFixture.tsx → EmptyConfigItem.tsx} +72 -44
  120. package/src/hardware-sim/DeckConfigurator/{AbsorbanceReaderFixture.tsx → FlexStackerItem.tsx} +46 -27
  121. package/src/hardware-sim/DeckConfigurator/{HeaterShakerFixture.tsx → HeaterShakerItem.tsx} +40 -28
  122. package/src/hardware-sim/DeckConfigurator/{MagneticBlockFixture.tsx → MagneticBlockItem.tsx} +41 -58
  123. package/src/hardware-sim/DeckConfigurator/{StagingAreaConfigFixture.tsx → StagingAreaConfigItem.tsx} +41 -23
  124. package/src/hardware-sim/DeckConfigurator/{StaticFixture.tsx → StaticItem.tsx} +16 -9
  125. package/src/hardware-sim/DeckConfigurator/{TemperatureModuleFixture.tsx → TemperatureModuleItem.tsx} +42 -26
  126. package/src/hardware-sim/DeckConfigurator/{ThermocyclerFixture.tsx → ThermocyclerItem.tsx} +31 -21
  127. package/src/hardware-sim/DeckConfigurator/{TrashBinConfigFixture.tsx → TrashBinConfigItem.tsx} +42 -27
  128. package/src/hardware-sim/DeckConfigurator/{WasteChuteConfigFixture.tsx → WasteChuteConfigItem.tsx} +38 -27
  129. package/src/hardware-sim/DeckConfigurator/constants.ts +5 -9
  130. package/src/hardware-sim/DeckConfigurator/index.tsx +236 -206
  131. package/src/hardware-sim/Labware/CalibrationBlockRender.stories.tsx +49 -0
  132. package/src/hardware-sim/Labware/CalibrationBlockRender.tsx +172 -0
  133. package/src/hardware-sim/Labware/Labware.tsx +30 -15
  134. package/src/hardware-sim/Labware/LabwareAdapter/index.tsx +2 -2
  135. package/src/hardware-sim/Labware/LabwareRender.stories.tsx +36 -21
  136. package/src/hardware-sim/Labware/LabwareRender.tsx +70 -15
  137. package/src/hardware-sim/Labware/__tests__/Labware.test.tsx +9 -4
  138. package/src/hardware-sim/Labware/__tests__/LabwareRender.test.tsx +14 -5
  139. package/src/hardware-sim/Labware/index.ts +1 -0
  140. package/src/hardware-sim/Labware/labware.module.css +8 -0
  141. package/src/hardware-sim/Labware/labwareInternals/FilledWells.tsx +3 -4
  142. package/src/hardware-sim/Labware/labwareInternals/LabwareOutline.tsx +48 -23
  143. package/src/hardware-sim/Labware/labwareInternals/LabwareWellLabels.tsx +3 -3
  144. package/src/hardware-sim/Labware/labwareInternals/StaticLabware.tsx +2 -2
  145. package/src/hardware-sim/Labware/labwareInternals/StrokedWells.tsx +2 -2
  146. package/src/hardware-sim/Labware/labwareInternals/StyledWells.tsx +2 -2
  147. package/src/hardware-sim/Labware/labwareInternals/Well.tsx +1 -2
  148. package/src/hardware-sim/Labware/labwareInternals/WellLabels.tsx +10 -11
  149. package/src/hardware-sim/Labware/labwareInternals/__tests__/LabwareWellLabels.test.tsx +2 -2
  150. package/src/hardware-sim/Labware/labwareInternals/__tests__/StrokedWells.test.tsx +2 -2
  151. package/src/hardware-sim/Labware/labwareInternals/__tests__/WellLabels.test.tsx +2 -2
  152. package/src/hardware-sim/Module/Module.stories.tsx +45 -15
  153. package/src/hardware-sim/Module/Thermocycler/index.tsx +2 -2
  154. package/src/hardware-sim/Module/index.tsx +41 -18
  155. package/src/hardware-sim/Pipette/PipetteRender.stories.tsx +2 -2
  156. package/src/hardware-sim/Pipette/PipetteRender.tsx +5 -7
  157. package/src/hardware-sim/Pipette/__tests__/PipetteRender.test.tsx +2 -2
  158. package/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpace.tsx +15 -14
  159. package/src/hardware-sim/RobotCoordinateSpace/robotcoordinatespace.module.css +3 -0
  160. package/src/hocs/withStyleProps.ts +53 -0
  161. package/src/hooks/useConditionalConfirm.ts +15 -5
  162. package/src/icons/Icon.tsx +38 -29
  163. package/src/icons/ModuleIcon.tsx +5 -3
  164. package/src/icons/icon-data.ts +26 -2
  165. package/src/icons/icon.module.css +10 -0
  166. package/src/index.module.css +1 -0
  167. package/src/instrument/InstrumentDiagram.tsx +0 -2
  168. package/src/instrument/PipetteSelect.module.css +3 -3
  169. package/src/instrument/index.ts +0 -2
  170. package/src/legacy-hardware-sim/LabwareNameOverlay.module.css +5 -5
  171. package/src/legacy-hardware-sim/ModuleItem.module.css +19 -19
  172. package/src/legacy-hardware-sim/index.ts +0 -1
  173. package/src/lists/index.ts +0 -1
  174. package/src/lists/lists.module.css +23 -23
  175. package/src/modals/modals.module.css +55 -55
  176. package/src/molecules/CustomizeExpandButton/__tests__/CustomizeExpandButton.test.tsx +88 -0
  177. package/src/molecules/CustomizeExpandButton/index.tsx +189 -0
  178. package/src/molecules/DeckInfoLabel/DeckInfoLabel.stories.tsx +10 -6
  179. package/src/molecules/DeckInfoLabel/__tests__/DeckInfoLabel.test.tsx +15 -11
  180. package/src/molecules/DeckInfoLabel/deckinfolabel.module.css +42 -0
  181. package/src/molecules/DeckInfoLabel/index.tsx +36 -62
  182. package/src/molecules/DropdownMenu/index.tsx +17 -13
  183. package/src/molecules/InfoScreen/index.tsx +6 -2
  184. package/src/molecules/ReleaseNotes/styles.module.css +15 -15
  185. package/src/molecules/index.ts +2 -1
  186. package/src/nav/SidePanel.module.css +6 -6
  187. package/src/organisms/CommandText/__tests__/CommandText.test.tsx +12 -12
  188. package/src/organisms/CommandText/index.tsx +2 -2
  189. package/src/organisms/CommandText/useCommandTextString/index.ts +2 -2
  190. package/src/organisms/CommandText/useCommandTextString/utils/__tests__/getFinalLabwareLocation.test.ts +4 -4
  191. package/src/organisms/CommandText/useCommandTextString/utils/commandText/__tests__/getPipettingCommandText.test.tsx +3 -3
  192. package/src/organisms/CommandText/useCommandTextString/utils/commandText/getFlexStackerCommandText.ts +2 -4
  193. package/src/organisms/CommandText/useCommandTextString/utils/commandText/getMoveToWellCommandText.ts +19 -3
  194. package/src/organisms/CommandText/useCommandTextString/utils/commandText/getPipettingCommandText.ts +19 -10
  195. package/src/organisms/CommandText/useCommandTextString/utils/getLabwareDefinitionsFromCommands.ts +9 -9
  196. package/src/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.tsx +3 -3
  197. package/src/organisms/CommandText/useCommandTextString/utils/getLabwareName.ts +2 -2
  198. package/src/organisms/DeckLabelSet/index.tsx +1 -3
  199. package/src/organisms/FixtureOption/__tests__/FixtureOption.test.tsx +0 -2
  200. package/src/organisms/FixtureOption/index.tsx +42 -71
  201. package/src/organisms/ProtocolDeck/index.tsx +118 -0
  202. package/src/organisms/ProtocolTimelineScrubber/CommandItem.tsx +2 -2
  203. package/src/organisms/ProtocolTimelineScrubber/PipetteVisuals.tsx +1 -1
  204. package/src/organisms/ProtocolTimelineScrubber/utils.ts +3 -3
  205. package/src/organisms/index.ts +1 -0
  206. package/src/primitives/ForeignObject.tsx +20 -61
  207. package/src/primitives/style-props.ts +1 -0
  208. package/src/slotmap/styles.module.css +1 -1
  209. package/src/structure/Pill.module.css +1 -1
  210. package/src/structure/Splash.module.css +1 -1
  211. package/src/structure/index.ts +2 -4
  212. package/src/structure/structure.module.css +11 -11
  213. package/src/styles/colors.module.css +1 -0
  214. package/src/styles/global.css +179 -0
  215. package/src/styles/index.module.css +1 -1
  216. package/src/styles/typography.module.css +1 -0
  217. package/src/testing/utils/renderWithProviders.tsx +3 -6
  218. package/src/tooltips/tooltips.module.css +14 -14
  219. package/vite.config.mts +6 -3
  220. package/lib/atoms/ListButton/ListButtonChildren/ListButtonRadioButton.d.ts +0 -14
  221. package/lib/hardware-sim/DeckConfigurator/AbsorbanceReaderFixture.d.ts +0 -10
  222. package/lib/hardware-sim/DeckConfigurator/EmptyConfigFixture.d.ts +0 -8
  223. package/lib/hardware-sim/DeckConfigurator/FlexStackerFixture.d.ts +0 -11
  224. package/lib/hardware-sim/DeckConfigurator/HeaterShakerFixture.d.ts +0 -10
  225. package/lib/hardware-sim/DeckConfigurator/MagneticBlockFixture.d.ts +0 -11
  226. package/lib/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.d.ts +0 -10
  227. package/lib/hardware-sim/DeckConfigurator/TemperatureModuleFixture.d.ts +0 -10
  228. package/lib/hardware-sim/DeckConfigurator/ThermocyclerFixture.d.ts +0 -10
  229. package/lib/hardware-sim/DeckConfigurator/TrashBinConfigFixture.d.ts +0 -10
  230. package/lib/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.d.ts +0 -11
  231. package/lib/hardware-sim/utils.d.ts +0 -3
  232. package/lib/instrument/InstrumentGroup.d.ts +0 -11
  233. package/lib/instrument/InstrumentInfo.d.ts +0 -18
  234. package/lib/legacy-hardware-sim/ModuleItem.d.ts +0 -17
  235. package/lib/lists/TitledList.d.ts +0 -44
  236. package/lib/structure/Card.d.ts +0 -18
  237. package/src/atoms/ListButton/ListButtonChildren/ListButtonRadioButton.tsx +0 -109
  238. package/src/atoms/ListButton/__tests__/ListButtonRadioButton.test.tsx +0 -28
  239. package/src/hardware-sim/utils.ts +0 -18
  240. package/src/instrument/InstrumentDiagram.stories.tsx +0 -43
  241. package/src/instrument/InstrumentGroup.stories.tsx +0 -89
  242. package/src/instrument/InstrumentGroup.tsx +0 -35
  243. package/src/instrument/InstrumentInfo.tsx +0 -106
  244. package/src/instrument/__tests__/InstrumentInfo.test.tsx +0 -58
  245. package/src/instrument/instrument.module.css +0 -45
  246. package/src/legacy-hardware-sim/ModuleItem.tsx +0 -145
  247. package/src/lists/TitledList.tsx +0 -154
  248. package/src/structure/Card.stories.tsx +0 -29
  249. package/src/structure/Card.tsx +0 -65
  250. /package/lib/atoms/{ListButton/__tests__/ListButtonRadioButton.test.d.ts → AnimationVideo/__tests__/AnimationVideo.test.d.ts} +0 -0
  251. /package/lib/{instrument/__tests__/InstrumentInfo.test.d.ts → atoms/buttons/__tests__/BasicButton.test.d.ts} +0 -0
@@ -1,16 +1,23 @@
1
- import { css } from 'styled-components'
2
-
3
1
  import type { ComponentProps, ReactNode } from 'react'
4
2
 
5
3
  export interface RobotCoordsTextProps extends ComponentProps<'text'> {
6
4
  x: number
5
+ /** The y-coordinate of the text baseline, in the Opentrons robot orientation, so +y is towards the top of the screen. */
7
6
  y: number
8
7
  children?: ReactNode
9
8
  canHighlight?: boolean
10
9
  }
11
10
 
12
- /** SVG text reflected to use take robot coordinates as props */
13
- // TODO: Ian 2019-05-07 reconcile this with Brian's version
11
+ /**
12
+ * SVG text positioned with Opentrons robot coordinates.
13
+ *
14
+ * This should be used inside a wrapper like RobotWorkSpace that sets up a transformation
15
+ * to orient the SVG with the Opentrons coordinate convention, with +y towards the
16
+ * top of the screen.
17
+ *
18
+ * This component surgically undoes the wrapper's transformation so the child text is
19
+ * displayed right-side-up.
20
+ */
14
21
  export function RobotCoordsText(props: RobotCoordsTextProps): JSX.Element {
15
22
  const { x, y, children, canHighlight = true, ...additionalProps } = props
16
23
  return (
@@ -19,13 +26,7 @@ export function RobotCoordsText(props: RobotCoordsTextProps): JSX.Element {
19
26
  x={x}
20
27
  y={-1 * y}
21
28
  transform="scale(1, -1)"
22
- css={
23
- !canHighlight
24
- ? css`
25
- user-select: none;
26
- `
27
- : undefined
28
- }
29
+ style={{ userSelect: !canHighlight ? 'none' : undefined }}
29
30
  >
30
31
  {children}
31
32
  </text>
@@ -2,80 +2,70 @@ import { useRef } from 'react'
2
2
 
3
3
  import { OT2_ROBOT_TYPE } from '@opentrons/shared-data'
4
4
 
5
- import { Svg } from '../../primitives'
6
5
  import { DeckFromLayers } from './DeckFromLayers'
7
6
 
8
- import type { ReactNode } from 'react'
9
- import type { DeckDefinition, DeckSlot } from '@opentrons/shared-data'
10
- import type { StyleProps } from '../../primitives'
7
+ import type { CSSProperties, ReactNode } from 'react'
8
+ import type { AddressableArea, DeckDefinition } from '@opentrons/shared-data'
11
9
 
12
10
  export interface RobotWorkSpaceRenderProps {
13
- deckSlotsById: { [slotId: string]: DeckSlot }
14
- getRobotCoordsFromDOMCoords: (
15
- domX: number,
16
- domY: number
17
- ) => { x: number; y: number }
11
+ // todo(mm, 2025-06-05): Is this API still worthwhile? The parent of RobotWorkSpace
12
+ // already has access to the full DeckDefinition. Maybe it should iterate over
13
+ // the DeckDefinition's AddressableAreas itself?
14
+ addressableAreasById: { [addressableAreaId: string]: AddressableArea }
18
15
  }
19
16
 
20
- export interface RobotWorkSpaceProps extends StyleProps {
21
- deckDef?: DeckDefinition
22
- viewBox?: string | null
17
+ type BaseProps = {
23
18
  children?: (props: RobotWorkSpaceRenderProps) => ReactNode
24
19
  deckLayerBlocklist?: string[]
25
- // optional boolean to show the OT-2 deck from deck defintion layers
20
+ /** optional boolean to show the OT-2 deck from deck defintion layers */
26
21
  showDeckLayers?: boolean
27
22
  id?: string
28
- }
23
+ } & (
24
+ | // Require at least one of deckDef or viewBox,
25
+ {
26
+ deckDef: DeckDefinition
27
+ }
28
+ | {
29
+ /**
30
+ * The x/y area to show, in standard SVG `x y xDimension yDimension` syntax.
31
+ *
32
+ * Specify coordinates in the Opentrons orientation, not the standard SVG orientation.
33
+ * So (x, y) is the area's front-left, xDimension extends the viewed area to the
34
+ * right, and yDimension extends the viewed area to the back.
35
+ */
36
+ viewBox: string
37
+ }
38
+ )
29
39
 
30
- type GetRobotCoordsFromDOMCoords = RobotWorkSpaceRenderProps['getRobotCoordsFromDOMCoords']
40
+ export type RobotWorkSpaceProps = BaseProps & CSSProperties
31
41
 
32
- export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element | null {
42
+ /**
43
+ * A wrapper for rendering the robot deck, labware, etc. from a top-down perspective.
44
+ *
45
+ * Child SVG components can render themselves in the Opentrons coordinate orientation
46
+ * (+x to the right of the robot, +y to the back of the robot) and this component will
47
+ * SVG-transform them so they're displayed the correct way. This is needed because
48
+ * SVG inverts y compared to Opentrons coordinates.
49
+ */
50
+ export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element {
33
51
  const {
34
52
  children,
35
- deckDef,
36
53
  deckLayerBlocklist = [],
37
54
  showDeckLayers = false,
38
- viewBox,
39
55
  id,
40
56
  ...styleProps
41
57
  } = props
58
+ const activeViewBox = getViewBoxFromProps(props)
59
+ const addressableAreasById = getAddressableAreasByIdFromProps(props)
42
60
  const wrapperRef = useRef<SVGSVGElement>(null)
43
61
 
44
- // NOTE: getScreenCTM in Chrome a DOMMatrix type,
45
- // in Firefox the same fn returns a deprecated SVGMatrix.
46
- // Until Firefox fixes this and conforms to SVG2 draft,
47
- // it will suffer from inverted y behavior (ignores css transform)
48
- const getRobotCoordsFromDOMCoords: GetRobotCoordsFromDOMCoords = (x, y) => {
49
- if (!wrapperRef.current) return { x: 0, y: 0 }
50
-
51
- const cursorPoint = wrapperRef.current.createSVGPoint()
52
-
53
- cursorPoint.x = x
54
- cursorPoint.y = y
55
-
56
- return cursorPoint.matrixTransform(
57
- wrapperRef.current.getScreenCTM()?.inverse()
58
- )
59
- }
60
- if (!deckDef && !viewBox) return null
61
-
62
- let wholeDeckViewBox
63
- let deckSlotsById = {}
64
- if (deckDef != null) {
65
- const [viewBoxOriginX, viewBoxOriginY] = deckDef.cornerOffsetFromOrigin
66
- const [deckXDimension, deckYDimension] = deckDef.dimensions
67
-
68
- deckSlotsById = deckDef.locations.addressableAreas.reduce(
69
- (acc, deckSlot) => ({ ...acc, [deckSlot.id]: deckSlot }),
70
- {}
71
- )
72
- wholeDeckViewBox = `${viewBoxOriginX} ${viewBoxOriginY} ${deckXDimension} ${deckYDimension}`
73
- }
74
-
75
- const activeViewBox = viewBox ?? wholeDeckViewBox
76
-
77
62
  return (
78
- <Svg viewBox={activeViewBox} ref={wrapperRef} id={id} {...styleProps}>
63
+ <svg
64
+ viewBox={activeViewBox}
65
+ ref={wrapperRef}
66
+ id={id}
67
+ style={{ ...styleProps }}
68
+ >
79
69
  <g
80
70
  transform={
81
71
  activeViewBox
@@ -93,8 +83,38 @@ export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element | null {
93
83
  robotType={OT2_ROBOT_TYPE}
94
84
  />
95
85
  ) : null}
96
- {children?.({ deckSlotsById, getRobotCoordsFromDOMCoords })}
86
+ {children?.({ addressableAreasById })}
97
87
  </g>
98
- </Svg>
88
+ </svg>
99
89
  )
100
90
  }
91
+
92
+ function getAddressableAreasByIdFromProps(
93
+ props: RobotWorkSpaceProps
94
+ ): RobotWorkSpaceRenderProps['addressableAreasById'] {
95
+ if ('deckDef' in props) {
96
+ return Object.fromEntries(
97
+ props.deckDef.locations.addressableAreas.map(addressableArea => [
98
+ addressableArea.id,
99
+ addressableArea,
100
+ ])
101
+ )
102
+ } else {
103
+ return {}
104
+ }
105
+ }
106
+
107
+ function getViewBoxFromProps(props: RobotWorkSpaceProps): string {
108
+ if ('viewBox' in props) {
109
+ // An explicitly provided viewBox takes precedence.
110
+ return props.viewBox
111
+ } else {
112
+ return getWholeDeckViewBox(props.deckDef)
113
+ }
114
+ }
115
+
116
+ function getWholeDeckViewBox(deckDef: DeckDefinition): string {
117
+ const [viewBoxOriginX, viewBoxOriginY] = deckDef.cornerOffsetFromOrigin
118
+ const [deckXDimension, deckYDimension] = deckDef.dimensions
119
+ return `${viewBoxOriginX} ${viewBoxOriginY} ${deckXDimension} ${deckYDimension}`
120
+ }
@@ -1,8 +1,9 @@
1
+ import clsx from 'clsx'
2
+
1
3
  import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
2
4
 
3
5
  import { DeckInfoLabel } from '../../molecules'
4
- import { Flex } from '../../primitives'
5
- import { ALIGN_CENTER, DIRECTION_COLUMN, JUSTIFY_CENTER } from '../../styles'
6
+ import styles from './deck.module.css'
6
7
  import { RobotCoordsForeignObject } from './RobotCoordsForeignObject'
7
8
 
8
9
  import type { RobotType } from '@opentrons/shared-data'
@@ -25,6 +26,26 @@ export const SlotLabels = ({
25
26
  const widthSmallRem = 10.5
26
27
  const widthLargeRem = 15.25
27
28
 
29
+ const rowDynamicWidth = show4thColumn
30
+ ? `${widthSmallRem * 2 + widthLargeRem * 2}rem`
31
+ : `${widthSmallRem + widthLargeRem * 2}rem`
32
+
33
+ const itemDynamicWidth = show4thColumn
34
+ ? `${widthSmallRem}rem`
35
+ : `${widthLargeRem}rem`
36
+
37
+ const rowStyle: Record<string, string> = {
38
+ '--dynamic-width': rowDynamicWidth,
39
+ }
40
+ const simpleItemWidthStyle: Record<string, string> = {
41
+ '--dynamic-width': `${widthLargeRem}rem`,
42
+ }
43
+ const nextTo4thWidthStyle: Record<string, string> = {
44
+ '--dynamic-width': itemDynamicWidth,
45
+ }
46
+ const fourthWidthStyle: Record<string, string> = {
47
+ '--dynamic-width': `${widthSmallRem}rem`,
48
+ }
28
49
  return robotType === FLEX_ROBOT_TYPE ? (
29
50
  <>
30
51
  <RobotCoordsForeignObject
@@ -33,26 +54,20 @@ export const SlotLabels = ({
33
54
  x="-147"
34
55
  y="-10"
35
56
  >
36
- <Flex
37
- alignItems={ALIGN_CENTER}
38
- flexDirection={DIRECTION_COLUMN}
39
- flex="1"
40
- height="100%"
41
- width="2.5rem"
42
- >
43
- <Flex alignItems={ALIGN_CENTER} flex="1">
57
+ <div className={styles.deck_labels_column}>
58
+ <div className={styles.deck_label_column_container}>
44
59
  <DeckInfoLabel deckLabel="A" height="max-content" width="100%" />
45
- </Flex>
46
- <Flex alignItems={ALIGN_CENTER} flex="1">
60
+ </div>
61
+ <div className={styles.deck_label_column_container}>
47
62
  <DeckInfoLabel deckLabel="B" height="max-content" width="100%" />
48
- </Flex>
49
- <Flex alignItems={ALIGN_CENTER} flex="1">
63
+ </div>
64
+ <div className={styles.deck_label_column_container}>
50
65
  <DeckInfoLabel deckLabel="C" height="max-content" width="100%" />
51
- </Flex>
52
- <Flex alignItems={ALIGN_CENTER} flex="1">
66
+ </div>
67
+ <div className={styles.deck_label_column_container}>
53
68
  <DeckInfoLabel deckLabel="D" height="max-content" width="100%" />
54
- </Flex>
55
- </Flex>
69
+ </div>
70
+ </div>
56
71
  </RobotCoordsForeignObject>
57
72
  <RobotCoordsForeignObject
58
73
  height="2.5rem"
@@ -64,49 +79,34 @@ export const SlotLabels = ({
64
79
  x="-100"
65
80
  y="-55"
66
81
  >
67
- <Flex
68
- alignItems={ALIGN_CENTER}
69
- flex="1"
70
- width={`${
71
- show4thColumn
72
- ? widthSmallRem * 2 + widthLargeRem * 2
73
- : widthSmallRem + widthLargeRem * 2
74
- }rem`}
75
- height="2.5rem"
76
- >
77
- <Flex
78
- alignItems={ALIGN_CENTER}
79
- justifyContent={JUSTIFY_CENTER}
80
- width={`${widthLargeRem}rem`}
82
+ <div className={clsx(styles.deck_labels_row)} style={rowStyle}>
83
+ <div
84
+ className={styles.deck_label_row_container}
85
+ style={simpleItemWidthStyle}
81
86
  >
82
87
  <DeckInfoLabel deckLabel="1" height="100%" />
83
- </Flex>
84
- <Flex
85
- alignItems={ALIGN_CENTER}
86
- justifyContent={JUSTIFY_CENTER}
87
- width={`${widthSmallRem}rem`}
88
+ </div>
89
+ <div
90
+ className={styles.deck_label_row_container}
91
+ style={simpleItemWidthStyle}
88
92
  >
89
93
  <DeckInfoLabel deckLabel="2" height="100%" />
90
- </Flex>
91
- <Flex
92
- alignItems={ALIGN_CENTER}
93
- justifyContent={JUSTIFY_CENTER}
94
- width={
95
- show4thColumn ? `${widthSmallRem}rem` : `${widthLargeRem}rem`
96
- }
94
+ </div>
95
+ <div
96
+ className={styles.deck_label_row_container}
97
+ style={nextTo4thWidthStyle}
97
98
  >
98
99
  <DeckInfoLabel deckLabel="3" height="100%" />
99
- </Flex>
100
+ </div>
100
101
  {show4thColumn ? (
101
- <Flex
102
- alignItems={ALIGN_CENTER}
103
- justifyContent={JUSTIFY_CENTER}
104
- width={`${widthSmallRem}rem`}
102
+ <div
103
+ className={styles.deck_label_row_container}
104
+ style={fourthWidthStyle}
105
105
  >
106
106
  <DeckInfoLabel deckLabel="4" height="100%" />
107
- </Flex>
107
+ </div>
108
108
  ) : null}
109
- </Flex>
109
+ </div>
110
110
  </RobotCoordsForeignObject>
111
111
  </>
112
112
  ) : null
@@ -0,0 +1,138 @@
1
+ import { expect, it } from 'vitest'
2
+
3
+ import {
4
+ getAllDefinitions,
5
+ getDeckDefinitions,
6
+ getModuleDef,
7
+ } from '@opentrons/shared-data'
8
+
9
+ import { resolveLabwareLocation } from '../resolveLabwareLocation'
10
+
11
+ import type { ModuleModel } from '@opentrons/shared-data'
12
+
13
+ it('should resolve a labware location', () => {
14
+ const labwareAUri = 'opentrons/nest_12_reservoir_15ml/1'
15
+ const labwareBUri = 'opentrons/agilent_1_reservoir_290ml/1'
16
+ const labwareCUri = 'opentrons/opentrons_96_pcr_adapter/1'
17
+ const labwareADefinition = getAllDefinitions()[labwareAUri]
18
+ const labwareBDefinition = getAllDefinitions()[labwareBUri]
19
+ const labwareCDefinition = getAllDefinitions()[labwareCUri]
20
+
21
+ const moduleModel: ModuleModel = 'temperatureModuleV2'
22
+ const moduleDefinition = getModuleDef(moduleModel)
23
+
24
+ const deckDefinition = getDeckDefinitions().ot3_standard
25
+
26
+ const slotId = 'C2'
27
+
28
+ const result = resolveLabwareLocation({
29
+ targetLabwareDef: labwareADefinition,
30
+ targetLabwareLocation: {
31
+ labwareId: 'labware-b-id',
32
+ },
33
+ otherLoadedLabware: [
34
+ {
35
+ id: 'labware-b-id',
36
+ definitionUri: labwareBUri,
37
+ location: {
38
+ labwareId: 'labware-c-id',
39
+ },
40
+ loadName: '',
41
+ },
42
+ {
43
+ id: 'labware-c-id',
44
+ definitionUri: labwareCUri,
45
+ location: {
46
+ moduleId: 'module-id',
47
+ },
48
+ loadName: '',
49
+ },
50
+ ],
51
+ deckDef: deckDefinition,
52
+ otherLabwareDefinitions: [labwareBDefinition, labwareCDefinition],
53
+ loadedModules: [
54
+ {
55
+ id: 'module-id',
56
+ model: moduleModel,
57
+ location: {
58
+ slotName: slotId,
59
+ },
60
+ serialNumber: '',
61
+ },
62
+ ],
63
+ })
64
+
65
+ const expectedResult: typeof result = {
66
+ deckDefinition,
67
+ slotId,
68
+ moduleDefinition,
69
+ labwareDefinitionsBottomToTop: [
70
+ labwareCDefinition,
71
+ labwareBDefinition,
72
+ labwareADefinition,
73
+ ],
74
+ }
75
+
76
+ expect(result).toStrictEqual(expectedResult)
77
+ })
78
+
79
+ it('should return offDeck if the labware is off-deck', () => {
80
+ const labwareAUri = 'opentrons/nest_12_reservoir_15ml/1'
81
+ const labwareBUri = 'opentrons/agilent_1_reservoir_290ml/1'
82
+ const labwareADefinition = getAllDefinitions()[labwareAUri]
83
+ const labwareBDefinition = getAllDefinitions()[labwareBUri]
84
+
85
+ const deckDefinition = getDeckDefinitions().ot3_standard
86
+
87
+ const result = resolveLabwareLocation({
88
+ targetLabwareDef: labwareADefinition,
89
+ targetLabwareLocation: {
90
+ labwareId: 'labware-b-id',
91
+ },
92
+ otherLoadedLabware: [
93
+ {
94
+ id: 'labware-b-id',
95
+ definitionUri: labwareBUri,
96
+ location: 'offDeck',
97
+ loadName: '',
98
+ },
99
+ ],
100
+ deckDef: deckDefinition,
101
+ otherLabwareDefinitions: [labwareBDefinition],
102
+ loadedModules: [],
103
+ })
104
+
105
+ const expectedResult: typeof result = 'offDeck'
106
+
107
+ expect(result).toStrictEqual(expectedResult)
108
+ })
109
+
110
+ it('should return error if something is missing from the input definitions', () => {
111
+ const labwareAUri = 'opentrons/nest_12_reservoir_15ml/1'
112
+ const labwareBUri = 'opentrons/agilent_1_reservoir_290ml/1'
113
+ const labwareADefinition = getAllDefinitions()[labwareAUri]
114
+
115
+ const deckDefinition = getDeckDefinitions().ot3_standard
116
+
117
+ const result = resolveLabwareLocation({
118
+ targetLabwareDef: labwareADefinition,
119
+ targetLabwareLocation: {
120
+ labwareId: 'labware-b-id',
121
+ },
122
+ otherLoadedLabware: [
123
+ {
124
+ id: 'labware-b-id',
125
+ definitionUri: labwareBUri,
126
+ location: 'offDeck',
127
+ loadName: '',
128
+ },
129
+ ],
130
+ deckDef: deckDefinition,
131
+ otherLabwareDefinitions: [], // Missing labware B definition.
132
+ loadedModules: [],
133
+ })
134
+
135
+ const expectedResult: typeof result = 'error'
136
+
137
+ expect(result).toStrictEqual(expectedResult)
138
+ })
@@ -0,0 +1,54 @@
1
+ .deck_labels_column {
2
+ display: flex;
3
+ width: 2.5rem;
4
+ height: 100%;
5
+ flex: 1;
6
+ flex-direction: column;
7
+ align-items: center;
8
+ }
9
+
10
+ .deck_label_column_container {
11
+ display: flex;
12
+ flex: 1;
13
+ align-items: center;
14
+ }
15
+
16
+ .deck_labels_row {
17
+ display: flex;
18
+ width: var(--dynamic-width, 5rem); /* fallback if variable not set */
19
+ height: 2.5rem;
20
+ flex: 1;
21
+ align-items: center;
22
+ }
23
+
24
+ .deck_label_row_container {
25
+ display: flex;
26
+ width: var(--dynamic-width);
27
+ align-items: center;
28
+ justify-content: center;
29
+ }
30
+
31
+ .robot_coords_foreign_object_container {
32
+ display: flex;
33
+ width: 100%;
34
+ height: 100%;
35
+ transform: scale(1, -1);
36
+ }
37
+
38
+ .trash_container {
39
+ display: flex;
40
+ width: 100%;
41
+ height: 100%;
42
+ align-items: center;
43
+ justify-content: center;
44
+ border-radius: var(--border-4);
45
+ gap: var(--spacing-8);
46
+ }
47
+
48
+ .trash_container_highlight {
49
+ border: 3px solid var(--blue-50);
50
+ }
51
+
52
+ .trash_container_rotate_copy {
53
+ transform-origin: center;
54
+ }
@@ -0,0 +1,154 @@
1
+ import { getLabwareDefURI, getModuleDef } from '@opentrons/shared-data'
2
+
3
+ import type {
4
+ ComputeLabwareOriginInput,
5
+ DeckDefinition,
6
+ LabwareDefinition,
7
+ LabwareLocation,
8
+ LoadedLabware,
9
+ LoadedModule,
10
+ ModuleDefinition,
11
+ } from '@opentrons/shared-data'
12
+
13
+ /**
14
+ * Converts a labware location to something that computeLabwareOrigin() can take as
15
+ * input and compute absolute deck coordinates from.
16
+ *
17
+ * This basically just does a lot of tedious lookups to resolve IDs into full definitions.
18
+ *
19
+ * Returns "error" if there was a problem doing the conversion, probably because of
20
+ * some invalid input to this function, like entries missing from `otherLoadedLabware`.
21
+ * Returns "offDeck" if the labware is known to be off-deck, so it doesn't have
22
+ * coordinates.
23
+ */
24
+ export function resolveLabwareLocation({
25
+ deckDef,
26
+ targetLabwareDef,
27
+ targetLabwareLocation,
28
+ loadedModules,
29
+ otherLoadedLabware,
30
+ otherLabwareDefinitions,
31
+ }: {
32
+ deckDef: DeckDefinition
33
+ targetLabwareDef: LabwareDefinition
34
+ targetLabwareLocation: LabwareLocation
35
+ loadedModules: LoadedModule[]
36
+ otherLoadedLabware: LoadedLabware[]
37
+ otherLabwareDefinitions: LabwareDefinition[]
38
+ }): 'error' | 'offDeck' | ComputeLabwareOriginInput {
39
+ const labwareTopToBottom = resolveLabwareStack({
40
+ topLabwareDef: targetLabwareDef,
41
+ topLabwareLocation: targetLabwareLocation,
42
+ otherLoadedLabware,
43
+ otherLabwareDefinitions,
44
+ })
45
+ const labwareBottomToTop =
46
+ labwareTopToBottom !== 'error' ? labwareTopToBottom.toReversed() : 'error'
47
+
48
+ if (labwareBottomToTop === 'error' || labwareBottomToTop.length < 1) {
49
+ return 'error'
50
+ }
51
+
52
+ const bottomLabwareLocation = labwareBottomToTop[0].location
53
+
54
+ if (
55
+ bottomLabwareLocation === 'offDeck' ||
56
+ bottomLabwareLocation === 'systemLocation'
57
+ ) {
58
+ return 'offDeck'
59
+ }
60
+
61
+ const bottom = (():
62
+ | 'error'
63
+ | {
64
+ slotId: string
65
+ moduleDefinition: ModuleDefinition | null
66
+ } => {
67
+ if ('moduleId' in bottomLabwareLocation) {
68
+ const loadedModule = loadedModules.find(
69
+ m => m.id === bottomLabwareLocation.moduleId
70
+ )
71
+ if (loadedModule == null) return 'error'
72
+ const moduleDefinition = getModuleDef(loadedModule.model)
73
+ const modSlot = deckDef.locations.addressableAreas.find(
74
+ s => s.id === loadedModule.location.slotName
75
+ )
76
+ if (modSlot == null) return 'error'
77
+ return { slotId: modSlot.id, moduleDefinition }
78
+ } else if ('slotName' in bottomLabwareLocation) {
79
+ return { slotId: bottomLabwareLocation.slotName, moduleDefinition: null }
80
+ } else if ('addressableAreaName' in bottomLabwareLocation) {
81
+ return {
82
+ slotId: bottomLabwareLocation.addressableAreaName,
83
+ moduleDefinition: null,
84
+ }
85
+ } else {
86
+ // Should not be reachable if resolveLabwareStack() did its job.
87
+ return 'error'
88
+ }
89
+ })()
90
+
91
+ if (bottom === 'error') {
92
+ return 'error'
93
+ }
94
+
95
+ return {
96
+ deckDefinition: deckDef,
97
+ slotId: bottom.slotId,
98
+ moduleDefinition: bottom.moduleDefinition,
99
+ labwareDefinitionsBottomToTop: labwareBottomToTop.map(l => l.definition),
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Return the labware comprising the labware part of a stack. In other words, everything
105
+ * from the top labware down to, and excluding, the underlying module or deck slot.
106
+ */
107
+ function resolveLabwareStack({
108
+ topLabwareDef,
109
+ topLabwareLocation,
110
+ otherLoadedLabware,
111
+ otherLabwareDefinitions,
112
+ }: {
113
+ topLabwareDef: LabwareDefinition
114
+ topLabwareLocation: LabwareLocation
115
+ otherLoadedLabware: LoadedLabware[]
116
+ otherLabwareDefinitions: LabwareDefinition[]
117
+ }):
118
+ | 'error'
119
+ | Array<{
120
+ definition: LabwareDefinition
121
+ location: LabwareLocation
122
+ }> {
123
+ let bottomMostLabwareSoFar = {
124
+ definition: topLabwareDef,
125
+ location: topLabwareLocation,
126
+ }
127
+ const labwareTopToBottomSoFar = [bottomMostLabwareSoFar]
128
+
129
+ while (
130
+ typeof bottomMostLabwareSoFar.location === 'object' &&
131
+ 'labwareId' in bottomMostLabwareSoFar.location
132
+ ) {
133
+ const newBottomLabwareId = bottomMostLabwareSoFar.location.labwareId
134
+ const newBottomLabware = otherLoadedLabware.find(
135
+ l => l.id === newBottomLabwareId
136
+ )
137
+ const newBottomLabwareDefinition = otherLabwareDefinitions.find(
138
+ def => getLabwareDefURI(def) === newBottomLabware?.definitionUri
139
+ )
140
+ if (newBottomLabware == null || newBottomLabwareDefinition == null) {
141
+ console.warn(
142
+ `Expected to find details for labware ID ${newBottomLabwareId} but could not.`
143
+ )
144
+ return 'error'
145
+ }
146
+ bottomMostLabwareSoFar = {
147
+ definition: newBottomLabwareDefinition,
148
+ location: newBottomLabware.location,
149
+ }
150
+ labwareTopToBottomSoFar.push(bottomMostLabwareSoFar)
151
+ }
152
+
153
+ return labwareTopToBottomSoFar
154
+ }