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(app): wire up location conflict modal for ODD #13797

Merged
merged 5 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"configured": "configured",
"confirm_heater_shaker_module_modal_description": "Before the run begins, module should have both anchors fully extended for a firm attachment. The thermal adapter should be attached to the module. ",
"confirm_heater_shaker_module_modal_title": "Confirm Heater-Shaker Module is attached",
"confirm_removal": "Confirm removal",
"connect_all_hardware": "Connect and calibrate all hardware first",
"connect_all_mod": "Connect all modules first",
"connection_info_not_available": "Connection info not available once run has started",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
JUSTIFY_END,
ALIGN_CENTER,
Box,
JUSTIFY_SPACE_BETWEEN,
BORDERS,
} from '@opentrons/components'
import {
getFixtureDisplayName,
Expand All @@ -26,6 +28,8 @@ import {
import { Portal } from '../../../../App/portal'
import { LegacyModal } from '../../../../molecules/LegacyModal'
import { StyledText } from '../../../../atoms/text'
import { Modal } from '../../../../molecules/Modal'
import { SmallButton } from '../../../../atoms/buttons/SmallButton'

import type {
Cutout,
Expand All @@ -39,12 +43,19 @@ interface LocationConflictModalProps {
cutout: Cutout
requiredFixture?: FixtureLoadName
requiredModule?: ModuleModel
isOnDevice?: boolean
}

export const LocationConflictModal = (
props: LocationConflictModalProps
): JSX.Element => {
const { onCloseClick, cutout, requiredFixture, requiredModule } = props
const {
onCloseClick,
cutout,
requiredFixture,
requiredModule,
isOnDevice = false,
} = props
const { t, i18n } = useTranslation(['protocol_setup', 'shared'])
const deckConfig = useDeckConfigurationQuery().data ?? []
const { updateDeckConfiguration } = useUpdateDeckConfigurationMutation()
Expand Down Expand Up @@ -73,93 +84,192 @@ export const LocationConflictModal = (

return (
<Portal level="top">
<LegacyModal
title={
<Flex
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing10}
alignItems={ALIGN_CENTER}
>
<Icon name="ot-alert" size="1rem" color={COLORS.warningEnabled} />
<StyledText as="h3" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('deck_conflict')}
</StyledText>
</Flex>
}
onClose={onCloseClick}
width="27.75rem"
>
<Flex flexDirection={DIRECTION_COLUMN}>
<Trans
t={t}
i18nKey="deck_conflict_info"
values={{
currentFixture: currentFixtureDisplayName,
cutout,
}}
components={{
block: <StyledText fontSize={TYPOGRAPHY.fontSizeH4} />,
strong: <strong />,
}}
/>
<Flex paddingY={SPACING.spacing16} flexDirection={DIRECTION_COLUMN}>
<StyledText
fontSize={TYPOGRAPHY.fontSizeH4}
fontWeight={TYPOGRAPHY.fontWeightBold}
>
{t('slot_location', { slotName: cutout })}
</StyledText>
{isOnDevice ? (
<Modal
onOutsideClick={onCloseClick}
header={{
title: t('deck_conflict'),
hasExitIcon: true,
onClick: onCloseClick,
iconName: 'ot-alert',
iconColor: COLORS.warningEnabled,
}}
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing32}>
<Trans
t={t}
i18nKey="deck_conflict_info"
values={{
currentFixture: currentFixtureDisplayName,
cutout,
}}
components={{
block: <StyledText as="p" />,
strong: <strong />,
}}
/>
<Flex flexDirection={DIRECTION_COLUMN}>
<StyledText
as="p"
fontWeight={TYPOGRAPHY.fontWeightBold}
paddingBottom={SPACING.spacing8}
>
{t('slot_location', { slotName: cutout })}
</StyledText>
<Flex
flexDirection={DIRECTION_COLUMN}
paddingTop={SPACING.spacing8}
gridGap={SPACING.spacing8}
>
<Flex
padding={SPACING.spacing24}
backgroundColor={COLORS.light1}
flexDirection={DIRECTION_ROW}
alignItems={ALIGN_CENTER}
justifyContent={JUSTIFY_SPACE_BETWEEN}
borderRadius={BORDERS.borderRadiusSize3}
>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('protocol_specifies')}
</StyledText>

<StyledText as="p">
{requiredFixture != null &&
getFixtureDisplayName(requiredFixture)}
{requiredModule != null &&
getModuleDisplayName(requiredModule)}
</StyledText>
</Flex>
<Flex
padding={SPACING.spacing24}
backgroundColor={COLORS.light1}
flexDirection={DIRECTION_ROW}
justifyContent={JUSTIFY_SPACE_BETWEEN}
alignItems={ALIGN_CENTER}
borderRadius={BORDERS.borderRadiusSize3}
>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('currently_configured')}
</StyledText>

<StyledText as="p">{currentFixtureDisplayName}</StyledText>
</Flex>
</Flex>
</Flex>
<Flex
flexDirection={DIRECTION_COLUMN}
paddingTop={SPACING.spacing8}
flexDirection={DIRECTION_ROW}
justifyContent={JUSTIFY_SPACE_BETWEEN}
gridGap={SPACING.spacing8}
>
<Flex
padding={SPACING.spacing8}
backgroundColor={COLORS.fundamentalsBackground}
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing20}
alignItems={ALIGN_CENTER}
<SmallButton
buttonType="secondary"
onClick={onCloseClick}
buttonText={i18n.format(t('shared:cancel'), 'capitalize')}
width="100%"
/>
<SmallButton
onClick={handleUpdateDeck}
buttonText={i18n.format(t('confirm_removal'), 'capitalize')}
width="100%"
/>
</Flex>
</Flex>
</Modal>
) : (
<LegacyModal
title={
<Flex
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing10}
alignItems={ALIGN_CENTER}
>
<Icon name="ot-alert" size="1rem" color={COLORS.warningEnabled} />
<StyledText as="h3" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('deck_conflict')}
</StyledText>
</Flex>
}
onClose={onCloseClick}
width="27.75rem"
>
<Flex flexDirection={DIRECTION_COLUMN}>
<Trans
t={t}
i18nKey="deck_conflict_info"
values={{
currentFixture: currentFixtureDisplayName,
cutout,
}}
components={{
block: <StyledText fontSize={TYPOGRAPHY.fontSizeH4} />,
strong: <strong />,
}}
/>
<Flex paddingY={SPACING.spacing16} flexDirection={DIRECTION_COLUMN}>
<StyledText
fontSize={TYPOGRAPHY.fontSizeH4}
fontWeight={TYPOGRAPHY.fontWeightBold}
>
<Box width="107px">
<StyledText as="label">{t('protocol_specifies')}</StyledText>
</Box>
<StyledText as="label">
{requiredFixture && getFixtureDisplayName(requiredFixture)}
{requiredModule && getModuleDisplayName(requiredModule)}
</StyledText>
</Flex>
{t('slot_location', { slotName: cutout })}
</StyledText>
<Flex
padding={SPACING.spacing8}
backgroundColor={COLORS.fundamentalsBackground}
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing20}
alignItems={ALIGN_CENTER}
flexDirection={DIRECTION_COLUMN}
paddingTop={SPACING.spacing8}
gridGap={SPACING.spacing8}
>
<Box width="max-content">
<Flex
padding={SPACING.spacing8}
backgroundColor={COLORS.fundamentalsBackground}
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing20}
alignItems={ALIGN_CENTER}
>
<Box width="107px">
<StyledText as="label">
{t('protocol_specifies')}
</StyledText>
</Box>
<StyledText as="label">
{t('currently_configured')}
{requiredFixture != null &&
getFixtureDisplayName(requiredFixture)}
{requiredModule != null &&
getModuleDisplayName(requiredModule)}
</StyledText>
</Box>
<StyledText as="label">{currentFixtureDisplayName}</StyledText>
</Flex>
<Flex
padding={SPACING.spacing8}
backgroundColor={COLORS.fundamentalsBackground}
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing20}
alignItems={ALIGN_CENTER}
>
<Box width="max-content">
<StyledText as="label">
{t('currently_configured')}
</StyledText>
</Box>
<StyledText as="label">
{currentFixtureDisplayName}
</StyledText>
</Flex>
</Flex>
</Flex>
</Flex>

<Flex
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing8}
justifyContent={JUSTIFY_END}
>
<SecondaryButton onClick={onCloseClick}>
{i18n.format(t('shared:cancel'), 'capitalize')}
</SecondaryButton>
<PrimaryButton onClick={handleUpdateDeck}>
{t('update_deck')}
</PrimaryButton>
<Flex
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing8}
justifyContent={JUSTIFY_END}
>
<SecondaryButton onClick={onCloseClick}>
{i18n.format(t('shared:cancel'), 'capitalize')}
</SecondaryButton>
<PrimaryButton onClick={handleUpdateDeck}>
{t('update_deck')}
</PrimaryButton>
</Flex>
</Flex>
</Flex>
</LegacyModal>
</LegacyModal>
)}
</Portal>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,21 @@ describe('LocationConflictModal', () => {
getByRole('button', { name: 'Update deck' }).click()
expect(mockUpdate).toHaveBeenCalled()
})
it('should render correct info for a odd', () => {
props = {
...props,
isOnDevice: true,
}
const { getByText, getAllByText } = render(props)
getByText('Deck location conflict')
getByText('Slot B3')
getByText('Protocol specifies')
getByText('Currently configured')
getAllByText('Staging Area Slot')
getByText('Trash Bin')
getByText('Cancel').click()
expect(props.onCloseClick).toHaveBeenCalled()
getByText('Confirm removal').click()
expect(mockUpdate).toHaveBeenCalled()
})
})
4 changes: 2 additions & 2 deletions app/src/organisms/ProtocolSetupLabware/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import { Modal } from '../../molecules/Modal'
import { useMostRecentCompletedAnalysis } from '../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { getLabwareSetupItemGroups } from '../../pages/Protocols/utils'
import { getProtocolModulesInfo } from '../Devices/ProtocolRun/utils/getProtocolModulesInfo'
import { getAttachedProtocolModuleMatches } from '../ProtocolSetupModules/utils'
import { getAttachedProtocolModuleMatches } from '../ProtocolSetupModulesAndDeck/utils'
import { getLabwareRenderInfo } from '../Devices/ProtocolRun/utils/getLabwareRenderInfo'
import {
getNestedLabwareInfo,
Expand All @@ -68,7 +68,7 @@ import type { HeaterShakerModule, Modules } from '@opentrons/api-client'
import type { LabwareSetupItem } from '../../pages/Protocols/utils'
import type { ModalHeaderBaseProps } from '../../molecules/Modal/types'
import type { SetupScreens } from '../../pages/OnDeviceDisplay/ProtocolSetup'
import type { AttachedProtocolModuleMatch } from '../ProtocolSetupModules/utils'
import type { AttachedProtocolModuleMatch } from '../ProtocolSetupModulesAndDeck/utils'

const OT3_STANDARD_DECK_VIEW_LAYER_BLOCK_LIST: string[] = [
'DECK_BASE',
Expand Down
Loading