Skip to content

Commit

Permalink
feat(app): create and wire up Not Configured modal (#13751)
Browse files Browse the repository at this point in the history
closes RAUT-699
  • Loading branch information
jerader authored Oct 10, 2023
1 parent f73772c commit e30c1b5
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 4 deletions.
2 changes: 2 additions & 0 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"96_mount": "left + right mount",
"adapter_slot_location_module": "Slot {{slotName}}, {{adapterName}} on {{moduleName}}",
"adapter_slot_location": "Slot {{slotName}}, {{adapterName}}",
"add_fixture_to_deck": "Add this fixture to your deck configuration. It will be referenced during protocol analysis.",
"add_fixture": "Add {{fixtureName}} to deck configuration",
"additional_labware": "{{count}} additional labware",
"additional_off_deck_labware": "Additional Off-Deck Labware",
"attach_gripper_failure_reason": "Attach the required gripper to continue",
Expand Down
1 change: 1 addition & 0 deletions app/src/assets/localization/en/shared.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"a_software_update_is_available": "A software update is available for this robot. Update to run protocols.",
"add": "add",
"alphabetical": "Alphabetical",
"back": "Back",
"before_you_begin": "Before you begin",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useUpdateDeckConfigurationMutation } from '@opentrons/react-api-client/src/deck_configuration'
import {
Flex,
DIRECTION_COLUMN,
TYPOGRAPHY,
SPACING,
JUSTIFY_SPACE_BETWEEN,
COLORS,
BORDERS,
ALIGN_CENTER,
} from '@opentrons/components'
import { FixtureLoadName, getFixtureDisplayName } from '@opentrons/shared-data'
import { TertiaryButton } from '../../../../atoms/buttons/TertiaryButton'
import { Portal } from '../../../../App/portal'
import { LegacyModal } from '../../../../molecules/LegacyModal'
import { StyledText } from '../../../../atoms/text'

interface NotConfiguredModalProps {
onCloseClick: () => void
requiredFixture: FixtureLoadName
cutout: string
}

export const NotConfiguredModal = (
props: NotConfiguredModalProps
): JSX.Element => {
const { onCloseClick, cutout, requiredFixture } = props
const { t, i18n } = useTranslation(['protocol_setup', 'shared'])
const { updateDeckConfiguration } = useUpdateDeckConfigurationMutation()

const handleUpdateDeck = (): void => {
updateDeckConfiguration({
fixtureLocation: cutout,
loadName: requiredFixture,
})
onCloseClick()
}

return (
<Portal level="top">
<LegacyModal
title={
<StyledText as="h3" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('add_fixture', {
fixtureName: getFixtureDisplayName(requiredFixture),
})}
</StyledText>
}
onClose={onCloseClick}
width="27.75rem"
>
<Flex flexDirection={DIRECTION_COLUMN}>
<StyledText as="p">{t('add_fixture_to_deck')}</StyledText>
<Flex paddingTop={SPACING.spacing16} flexDirection={DIRECTION_COLUMN}>
<Flex
padding={`${SPACING.spacing8} ${SPACING.spacing16}`}
backgroundColor={COLORS.medGreyEnabled}
borderRadius={BORDERS.radiusSoftCorners}
alignItems={ALIGN_CENTER}
justifyContent={JUSTIFY_SPACE_BETWEEN}
>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{getFixtureDisplayName(requiredFixture)}
</StyledText>
<TertiaryButton onClick={handleUpdateDeck}>
{i18n.format(t('add'), 'capitalize')}
</TertiaryButton>
</Flex>
</Flex>
</Flex>
</LegacyModal>
</Portal>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { StyledText } from '../../../../atoms/text'
import { StatusLabel } from '../../../../atoms/StatusLabel'
import { TertiaryButton } from '../../../../atoms/buttons/TertiaryButton'
import { LocationConflictModal } from './LocationConflictModal'
import { NotConfiguredModal } from './NotConfiguredModal'
import { getFixtureImage } from './utils'

import type { LoadedFixturesBySlot } from '@opentrons/api-client'
Expand Down Expand Up @@ -151,9 +152,20 @@ export function FixtureListItem({
showLocationConflictModal,
setShowLocationConflictModal,
] = React.useState<boolean>(false)
const [
showNotConfiguredModal,
setShowNotConfiguredModal,
] = React.useState<boolean>(false)

return (
<>
{showNotConfiguredModal ? (
<NotConfiguredModal
onCloseClick={() => setShowNotConfiguredModal(false)}
cutout={cutout}
requiredFixture={loadName}
/>
) : null}
{showLocationConflictModal ? (
<LocationConflictModal
onCloseClick={() => setShowLocationConflictModal(false)}
Expand Down Expand Up @@ -217,7 +229,7 @@ export function FixtureListItem({
onClick={() =>
configurationStatus === CONFLICTING
? setShowLocationConflictModal(true)
: console.log('wire this up')
: setShowNotConfiguredModal(true)
}
>
<StyledText as="label" cursor="pointer">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react'
import { renderWithProviders } from '@opentrons/components'
import { TRASH_BIN_LOAD_NAME } from '@opentrons/shared-data'
import { useUpdateDeckConfigurationMutation } from '@opentrons/react-api-client/src/deck_configuration'
import { i18n } from '../../../../../i18n'
import { NotConfiguredModal } from '../NotConfiguredModal'

jest.mock('@opentrons/react-api-client/src/deck_configuration')

const mockUseUpdateDeckConfigurationMutation = useUpdateDeckConfigurationMutation as jest.MockedFunction<
typeof useUpdateDeckConfigurationMutation
>

const render = (props: React.ComponentProps<typeof NotConfiguredModal>) => {
return renderWithProviders(<NotConfiguredModal {...props} />, {
i18nInstance: i18n,
})[0]
}

describe('NotConfiguredModal', () => {
let props: React.ComponentProps<typeof NotConfiguredModal>
const mockUpdate = jest.fn()
beforeEach(() => {
props = {
onCloseClick: jest.fn(),
cutout: 'B3',
requiredFixture: TRASH_BIN_LOAD_NAME,
}
mockUseUpdateDeckConfigurationMutation.mockReturnValue({
updateDeckConfiguration: mockUpdate,
} as any)
})
it('renders the correct text and button works as expected', () => {
const { getByText, getByRole } = render(props)
getByText('Add Trash Bin to deck configuration')
getByText(
'Add this fixture to your deck configuration. It will be referenced during protocol analysis.'
)
getByText('Trash Bin')
getByRole('button', { name: 'Add' }).click()
expect(mockUpdate).toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@ import {
import { i18n } from '../../../../../i18n'
import { useLoadedFixturesConfigStatus } from '../../../../../resources/deck_configuration/hooks'
import { SetupFixtureList } from '../SetupFixtureList'
import { NotConfiguredModal } from '../NotConfiguredModal'
import { LocationConflictModal } from '../LocationConflictModal'
import type { LoadedFixturesBySlot } from '@opentrons/api-client'

jest.mock('../../../../../resources/deck_configuration/hooks')
jest.mock('../LocationConflictModal')
jest.mock('../NotConfiguredModal')

const mockUseLoadedFixturesConfigStatus = useLoadedFixturesConfigStatus as jest.MockedFunction<
typeof useLoadedFixturesConfigStatus
>
const mockLocationConflictModal = LocationConflictModal as jest.MockedFunction<
typeof LocationConflictModal
>
const mockNotConfiguredModal = NotConfiguredModal as jest.MockedFunction<
typeof NotConfiguredModal
>
const mockLoadedFixture = {
id: 'stubbed_load_fixture',
commandType: 'loadFixture',
Expand Down Expand Up @@ -58,6 +64,7 @@ describe('SetupFixtureList', () => {
mockLocationConflictModal.mockReturnValue(
<div>mock location conflict modal</div>
)
mockNotConfiguredModal.mockReturnValue(<div>mock not configured modal</div>)
})

it('should render the headers and a fixture with configured status', () => {
Expand Down Expand Up @@ -92,6 +99,6 @@ describe('SetupFixtureList', () => {
const { getByText, getByRole } = render(props)[0]
getByText('Not configured')
getByRole('button', { name: 'Update deck' }).click()
// TODO(Jr, 10/5/23): add test coverage for button
getByText('mock not configured modal')
})
})
11 changes: 9 additions & 2 deletions app/src/resources/deck_configuration/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useDeckConfigurationQuery } from '@opentrons/react-api-client'

import type { Fixture, LoadFixtureRunTimeCommand } from '@opentrons/shared-data'
import {
Fixture,
LoadFixtureRunTimeCommand,
STANDARD_SLOT_LOAD_NAME,
} from '@opentrons/shared-data'

export const CONFIGURED = 'configured'
export const CONFLICTING = 'conflicting'
Expand Down Expand Up @@ -32,9 +36,12 @@ export function useLoadedFixturesConfigStatus(
deckConfigurationAtLocation.loadName === loadedFixture.params.loadName
) {
configurationStatus = CONFIGURED
// special casing this for now until we know what the backend will give us. It is only
// conflicting if the current deck configuration fixture is not the desired or standard slot
} else if (
deckConfigurationAtLocation != null &&
deckConfigurationAtLocation.loadName !== loadedFixture.params.loadName
deckConfigurationAtLocation.loadName !== loadedFixture.params.loadName &&
deckConfigurationAtLocation.loadName !== STANDARD_SLOT_LOAD_NAME
) {
configurationStatus = CONFLICTING
}
Expand Down

0 comments on commit e30c1b5

Please sign in to comment.