diff --git a/app/src/assets/localization/en/labware_position_check.json b/app/src/assets/localization/en/labware_position_check.json
index 9f6194717df..2e35b9f3572 100644
--- a/app/src/assets/localization/en/labware_position_check.json
+++ b/app/src/assets/localization/en/labware_position_check.json
@@ -99,6 +99,7 @@
"stored_offsets_for_this_protocol": "Stored Labware Offset data that applies to this protocol",
"table_view": "Table View",
"tip_rack": "tip rack",
+ "view_current_offsets": "view current offsets",
"view_data": "View data",
"what_is_labware_offset_data": "What is labware offset data?"
}
diff --git a/app/src/assets/localization/en/protocol_setup.json b/app/src/assets/localization/en/protocol_setup.json
index 8bb213e4b6f..ba8af45d0dd 100644
--- a/app/src/assets/localization/en/protocol_setup.json
+++ b/app/src/assets/localization/en/protocol_setup.json
@@ -125,7 +125,7 @@
"must_have_labware_and_pip": "Protocol must load labware and a pipette",
"n_a": "N/A",
"no_data": "no data",
- "no_labware_offset_data": "No Labware Offset Data yet",
+ "no_labware_offset_data": "no labware offset data yet",
"no_modules_specified": "no modules are specified for this protocol.",
"no_modules_used_in_this_protocol": "No modules used in this protocol",
"no_tiprack_loaded": "Protocol must load a tip rack",
diff --git a/app/src/molecules/WizardRequiredEquipmentList/index.tsx b/app/src/molecules/WizardRequiredEquipmentList/index.tsx
index 467fd749b66..35b87750b4b 100644
--- a/app/src/molecules/WizardRequiredEquipmentList/index.tsx
+++ b/app/src/molecules/WizardRequiredEquipmentList/index.tsx
@@ -49,7 +49,6 @@ export function WizardRequiredEquipmentList(
>
{t('you_will_need')}
-
) : (
<>
-
+
{t('you_will_need')}
-
- {equipmentList.map(requiredEquipmentProps => (
+ {equipmentList.length > 1 ? : null}
+ {equipmentList.map((requiredEquipmentProps, index) => (
))}
{footer != null ? (
@@ -106,12 +110,13 @@ interface RequiredEquipmentCardProps {
loadName: string
displayName: string
subtitle?: string
+ bottomDivider?: boolean
}
function RequiredEquipmentCard(props: RequiredEquipmentCardProps): JSX.Element {
- const { loadName, displayName, subtitle } = props
+ const { loadName, displayName, subtitle, bottomDivider = true } = props
- let imageSrc: string = labwareImages.generic_custom_tiprack
+ let imageSrc: string | null = null
if (loadName in labwareImages) {
imageSrc = labwareImages[loadName as keyof typeof labwareImages]
} else if (loadName in equipmentImages) {
@@ -125,24 +130,26 @@ function RequiredEquipmentCard(props: RequiredEquipmentCardProps): JSX.Element {
alignItems={ALIGN_CENTER}
width="100%"
>
-
-
-
+ {imageSrc != null ? (
+
+
+
+ ) : null}
-
+ {bottomDivider ? : null}
>
)
}
diff --git a/app/src/organisms/Devices/ProtocolRun/ProtocolRunSetup.tsx b/app/src/organisms/Devices/ProtocolRun/ProtocolRunSetup.tsx
index 951bcd88be0..36f14a0a7ff 100644
--- a/app/src/organisms/Devices/ProtocolRun/ProtocolRunSetup.tsx
+++ b/app/src/organisms/Devices/ProtocolRun/ProtocolRunSetup.tsx
@@ -8,6 +8,11 @@ import {
COLORS,
DIRECTION_COLUMN,
SPACING,
+ Icon,
+ SIZE_1,
+ DIRECTION_ROW,
+ TYPOGRAPHY,
+ Link,
} from '@opentrons/components'
import { Line } from '../../../atoms/structure'
@@ -20,6 +25,7 @@ import {
useRunHasStarted,
useProtocolAnalysisErrors,
useStoredProtocolAnalysis,
+ ProtocolCalibrationStatus,
} from '../hooks'
import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { SetupLabware } from './SetupLabware'
@@ -29,6 +35,7 @@ import { SetupModules } from './SetupModules'
import { SetupStep } from './SetupStep'
import { SetupLiquids } from './SetupLiquids'
import { EmptySetupStep } from './EmptySetupStep'
+import { HowLPCWorksModal } from './SetupLabwarePositionCheck/HowLPCWorksModal'
const ROBOT_CALIBRATION_STEP_KEY = 'robot_calibration_step' as const
const MODULE_SETUP_KEY = 'module_setup_step' as const
@@ -207,10 +214,10 @@ export function ProtocolRunSetup({
? setExpandedStepKey(null)
: setExpandedStepKey(stepKey)
}
- calibrationStatusComplete={
- stepKey === ROBOT_CALIBRATION_STEP_KEY && !runHasStarted
- ? calibrationStatus.complete
- : null
+ rightElement={
+
}
>
{StepDetailMap[stepKey].stepInternals}
@@ -231,3 +238,70 @@ export function ProtocolRunSetup({
)
}
+
+interface StepRightElementProps {
+ stepKey: StepKey
+ calibrationStatus: ProtocolCalibrationStatus
+ runHasStarted: boolean
+}
+function StepRightElement(props: StepRightElementProps): JSX.Element | null {
+ const { stepKey, calibrationStatus, runHasStarted } = props
+ const { t } = useTranslation('protocol_setup')
+
+ if (stepKey === ROBOT_CALIBRATION_STEP_KEY && !runHasStarted) {
+ return (
+
+
+
+ {calibrationStatus.complete
+ ? t('calibration_ready')
+ : t('calibration_needed')}
+
+
+ )
+ } else if (stepKey === LPC_KEY) {
+ return
+ } else {
+ return null
+ }
+}
+
+function LearnAboutLPC(): JSX.Element {
+ const { t } = useTranslation('protocol_setup')
+ const [showLPCHelpModal, setShowLPCHelpModal] = React.useState(false)
+ return (
+ <>
+ {
+ // clicking link shouldn't toggle step expanded state
+ e.preventDefault()
+ e.stopPropagation()
+ setShowLPCHelpModal(true)
+ }}
+ >
+ {t('learn_how_it_works')}
+
+ {showLPCHelpModal ? (
+ setShowLPCHelpModal(false)} />
+ ) : null}
+ >
+ )
+}
diff --git a/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/index.tsx b/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/index.tsx
index 038db0c26c7..65ce62c90e1 100644
--- a/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/index.tsx
+++ b/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/index.tsx
@@ -7,11 +7,11 @@ import {
DIRECTION_COLUMN,
ALIGN_CENTER,
TYPOGRAPHY,
- Link,
TOOLTIP_LEFT,
useHoverTooltip,
SecondaryButton,
PrimaryButton,
+ COLORS,
} from '@opentrons/components'
import { useRunQuery } from '@opentrons/react-api-client'
import { useMostRecentCompletedAnalysis } from '../../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
@@ -20,7 +20,6 @@ import { Tooltip } from '../../../../atoms/Tooltip'
import { useLPCDisabledReason, useStoredProtocolAnalysis } from '../../hooks'
import { CurrentOffsetsTable } from './CurrentOffsetsTable'
import { useLaunchLPC } from '../../../LabwarePositionCheck/useLaunchLPC'
-import { HowLPCWorksModal } from './HowLPCWorksModal'
import { StyledText } from '../../../../atoms/text'
interface SetupLabwarePositionCheckProps {
@@ -33,7 +32,7 @@ export function SetupLabwarePositionCheck(
props: SetupLabwarePositionCheckProps
): JSX.Element {
const { robotName, runId, expandLabwareStep } = props
- const { t } = useTranslation('protocol_setup')
+ const { t, i18n } = useTranslation('protocol_setup')
const { data: runRecord } = useRunQuery(runId, { staleTime: Infinity })
const currentOffsets = runRecord?.data?.labwareOffsets ?? []
@@ -41,7 +40,6 @@ export function SetupLabwarePositionCheck(
const robotProtocolAnalysis = useMostRecentCompletedAnalysis(runId)
const storedProtocolAnalysis = useStoredProtocolAnalysis(runId)
const protocolData = robotProtocolAnalysis ?? storedProtocolAnalysis
- const [showHelpModal, setShowHelpModal] = React.useState(false)
const [targetProps, tooltipProps] = useHoverTooltip({
placement: TOOLTIP_LEFT,
})
@@ -56,18 +54,27 @@ export function SetupLabwarePositionCheck(
marginTop={SPACING.spacing16}
gridGap={SPACING.spacing16}
>
-
- setShowHelpModal(true)}
+ {currentOffsets.length > 0 ? (
+
+ ) : (
+
- {t('learn_how_it_works')}
-
+
+ {i18n.format(t('no_labware_offset_data'), 'capitalize')}
+
+
+ )}
+
{lpcDisabledReason}
) : null}
-
- {currentOffsets.length > 0 ? (
-
- ) : (
-
- {t('no_labware_offset_data')}
-
- )}
-
-
{LPCWizard}
- {showHelpModal ? (
- setShowHelpModal(false)} />
- ) : null}
)
}
diff --git a/app/src/organisms/Devices/ProtocolRun/SetupStep.tsx b/app/src/organisms/Devices/ProtocolRun/SetupStep.tsx
index c7808bb373b..dbf0d910c36 100644
--- a/app/src/organisms/Devices/ProtocolRun/SetupStep.tsx
+++ b/app/src/organisms/Devices/ProtocolRun/SetupStep.tsx
@@ -1,5 +1,4 @@
import * as React from 'react'
-import { useTranslation } from 'react-i18next'
import { css } from 'styled-components'
import {
@@ -11,7 +10,6 @@ import {
DIRECTION_COLUMN,
DIRECTION_ROW,
JUSTIFY_SPACE_BETWEEN,
- SIZE_1,
COLORS,
SPACING,
TYPOGRAPHY,
@@ -20,13 +18,20 @@ import {
import { StyledText } from '../../../atoms/text'
interface SetupStepProps {
+ /** whether or not to show the full contents of the step */
expanded: boolean
+ /** always shown text name of the step */
title: React.ReactNode
+ /** always shown text that provides a one sentence explanation of the contents */
description: string
+ /** always shown text that sits above title of step (used for step number) */
label: string
+ /** callback that should toggle the expanded state (managed by parent) */
toggleExpanded: () => void
+ /** contents to be shown only when expanded */
children: React.ReactNode
- calibrationStatusComplete: boolean | null
+ /** element to be shown (right aligned) regardless of expanded state */
+ rightElement: React.ReactNode
}
const EXPANDED_STYLE = css`
@@ -57,10 +62,8 @@ export function SetupStep({
label,
toggleExpanded,
children,
- calibrationStatusComplete,
+ rightElement,
}: SetupStepProps): JSX.Element {
- const { t } = useTranslation('protocol_setup')
-
return (
@@ -100,34 +103,7 @@ export function SetupStep({
- {calibrationStatusComplete !== null ? (
-
-
-
- {calibrationStatusComplete
- ? t('calibration_ready')
- : t('calibration_needed')}
-
-
- ) : null}
+ {rightElement}
{
title = 'stub title',
description = 'stub description',
label = 'stub label',
- calibrationStatusComplete = null,
toggleExpanded = toggleExpandedMock,
children = ,
+ rightElement = right element
,
}: Partial> = {}) => {
return renderWithProviders(
{
label,
toggleExpanded,
children,
- calibrationStatusComplete,
+ rightElement,
}}
/>,
{ i18nInstance: i18n }
@@ -55,5 +55,6 @@ describe('SetupStep', () => {
getByText('stub label')
getByText('stub title')
queryAllByText('stub description')
+ queryAllByText('right element')
})
})
diff --git a/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx b/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx
index c2fb104b14b..8bba4649b69 100644
--- a/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx
+++ b/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx
@@ -1,6 +1,9 @@
import * as React from 'react'
import { Trans, useTranslation } from 'react-i18next'
-import { CompletedProtocolAnalysis } from '@opentrons/shared-data'
+import {
+ CompletedProtocolAnalysis,
+ LabwareDefinition2,
+} from '@opentrons/shared-data'
import { StyledText } from '../../../atoms/text'
import { RobotMotionLoader } from '../RobotMotionLoader'
import { getPrepCommands } from './getPrepCommands'
@@ -8,11 +11,34 @@ import { useChainRunCommands } from '../../../resources/runs/hooks'
import type { RegisterPositionAction } from '../types'
import type { Jog } from '../../../molecules/JogControls'
import { WizardRequiredEquipmentList } from '../../../molecules/WizardRequiredEquipmentList'
-import { GenericWizardTile } from '../../../molecules/GenericWizardTile'
import { getIsOnDevice } from '../../../redux/config'
+import { NeedHelpLink } from '../../CalibrationPanels'
import { useSelector } from 'react-redux'
+import { TwoUpTileLayout } from '../TwoUpTileLayout'
+import {
+ ALIGN_CENTER,
+ Box,
+ Btn,
+ COLORS,
+ DIRECTION_COLUMN,
+ Flex,
+ Icon,
+ JUSTIFY_SPACE_BETWEEN,
+ PrimaryButton,
+ SPACING,
+ TYPOGRAPHY,
+} from '@opentrons/components'
+import { LabwareOffset } from '@opentrons/api-client'
+import { css } from 'styled-components'
+import { Portal } from '../../../App/portal'
+import { LegacyModalShell } from '../../../molecules/LegacyModal'
+import { SmallButton } from '../../../atoms/buttons'
+import { TerseOffsetTable } from '../ResultsSummary'
+import { getLabwareDefinitionsFromCommands } from '../utils/labware'
export const INTERVAL_MS = 3000
+
+// TODO(BC, 09/01/23): replace updated support article link for LPC on OT-2/Flex
const SUPPORT_PAGE_URL = 'https://support.opentrons.com/s/ot2-calibration'
export const IntroScreen = (props: {
@@ -23,6 +49,7 @@ export const IntroScreen = (props: {
handleJog: Jog
setFatalError: (errorMessage: string) => void
isRobotMoving: boolean
+ existingOffsets: LabwareOffset[]
}): JSX.Element | null => {
const {
proceed,
@@ -30,9 +57,10 @@ export const IntroScreen = (props: {
chainRunCommands,
isRobotMoving,
setFatalError,
+ existingOffsets,
} = props
const isOnDevice = useSelector(getIsOnDevice)
- const { t } = useTranslation(['labware_position_check', 'shared'])
+ const { t, i18n } = useTranslation(['labware_position_check', 'shared'])
const handleClickStartLPC = (): void => {
const prepCommands = getPrepCommands(protocolData)
chainRunCommands(prepCommands, false)
@@ -50,17 +78,16 @@ export const IntroScreen = (props: {
)
}
return (
- }}
/>
}
- rightHandBody={
+ rightElement={
}
- proceedButtonText={t('shared:get_started')}
- proceed={handleClickStartLPC}
+ footer={
+
+ {isOnDevice ? (
+
+ ) : (
+
+ )}
+ {isOnDevice ? (
+
+ ) : (
+
+ {i18n.format(t('shared:get_started'), 'capitalize')}
+
+ )}
+
+ }
/>
)
}
+
+const VIEW_OFFSETS_BUTTON_STYLE = css`
+ ${TYPOGRAPHY.pSemiBold};
+ color: ${COLORS.darkBlackEnabled};
+ font-size: ${TYPOGRAPHY.fontSize22};
+ &:hover {
+ opacity: 100%;
+ }
+ &:active {
+ opacity: 70%;
+ }
+`
+interface ViewOffsetsProps {
+ existingOffsets: LabwareOffset[]
+ labwareDefinitions: LabwareDefinition2[]
+}
+function ViewOffsets(props: ViewOffsetsProps): JSX.Element {
+ const { existingOffsets, labwareDefinitions } = props
+ const { t, i18n } = useTranslation('labware_position_check')
+ const [showOffsetsTable, setShowOffsetsModal] = React.useState(false)
+ return existingOffsets.length > 0 ? (
+ <>
+ setShowOffsetsModal(true)}
+ css={VIEW_OFFSETS_BUTTON_STYLE}
+ aria-label="show labware offsets"
+ >
+
+
+ {i18n.format(t('view_current_offsets'), 'capitalize')}
+
+
+ {showOffsetsTable ? (
+
+
+ {i18n.format(t('labware_offset_data'), 'capitalize')}
+
+ }
+ footer={
+ setShowOffsetsModal(false)}
+ />
+ }
+ >
+
+
+
+
+
+ ) : null}
+ >
+ ) : (
+
+ )
+}
diff --git a/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx b/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx
index 9bcdfb1644f..6ca827aae9d 100644
--- a/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx
+++ b/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx
@@ -263,7 +263,9 @@ export const LabwarePositionCheckComponent = (
/>
)
} else if (currentStep.section === 'BEFORE_BEGINNING') {
- modalContent =
+ modalContent = (
+
+ )
} else if (
currentStep.section === 'CHECK_POSITIONS' ||
currentStep.section === 'CHECK_TIP_RACKS' ||
diff --git a/app/src/organisms/LabwarePositionCheck/TwoUpTileLayout.tsx b/app/src/organisms/LabwarePositionCheck/TwoUpTileLayout.tsx
new file mode 100644
index 00000000000..44ee775f25a
--- /dev/null
+++ b/app/src/organisms/LabwarePositionCheck/TwoUpTileLayout.tsx
@@ -0,0 +1,66 @@
+import * as React from 'react'
+import styled, { css } from 'styled-components'
+import {
+ DIRECTION_COLUMN,
+ Flex,
+ SPACING,
+ JUSTIFY_SPACE_BETWEEN,
+ DIRECTION_ROW,
+ TYPOGRAPHY,
+ JUSTIFY_CENTER,
+ RESPONSIVENESS,
+ DISPLAY_INLINE_BLOCK,
+} from '@opentrons/components'
+
+const Title = styled.h1`
+ ${TYPOGRAPHY.h1Default};
+ margin-bottom: ${SPACING.spacing8};
+ @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
+ ${TYPOGRAPHY.level4HeaderSemiBold};
+ margin-bottom: 0;
+ height: ${SPACING.spacing40};
+ display: ${DISPLAY_INLINE_BLOCK};
+ }
+`
+
+const TILE_CONTAINER_STYLE = css`
+ flex-direction: ${DIRECTION_COLUMN};
+ justify-content: ${JUSTIFY_SPACE_BETWEEN};
+ padding: ${SPACING.spacing32};
+ height: 24.625rem;
+ @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
+ height: 29.5rem;
+ }
+`
+export interface TwoUpTileLayoutProps {
+ /** main header text on left half */
+ title: string
+ /** paragraph text below title on left half */
+ body: React.ReactNode
+ /** entire contents of the right half */
+ rightElement: React.ReactNode
+ /** footer underneath both halves of content */
+ footer: React.ReactNode
+}
+
+export function TwoUpTileLayout(props: TwoUpTileLayoutProps): JSX.Element {
+ const { title, body, rightElement, footer } = props
+ return (
+
+
+
+ {title}
+ {body}
+
+
+ {rightElement}
+
+
+ {footer}
+
+ )
+}