-
Notifications
You must be signed in to change notification settings - Fork 179
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(app): heater shaker wizard test shake (#9549)
This PR implements the test shake page of the heater shaker wizard.
- Loading branch information
Showing
12 changed files
with
484 additions
and
12 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
app/src/organisms/Devices/HeaterShakerWizard/HeaterShakerModuleCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import * as React from 'react' | ||
import { | ||
Flex, | ||
Text, | ||
Icon, | ||
SIZE_1, | ||
TYPOGRAPHY, | ||
TEXT_TRANSFORM_UPPERCASE, | ||
DIRECTION_ROW, | ||
DIRECTION_COLUMN, | ||
SPACING, | ||
ALIGN_FLEX_START, | ||
COLORS, | ||
} from '@opentrons/components' | ||
import heaterShakerModule from '../../../assets/images/heatershaker_module_transparent.svg' | ||
import { HeaterShakerModuleData } from '../ModuleCard/HeaterShakerModuleData' | ||
|
||
export const HeaterShakerModuleCard = (): JSX.Element | null => { | ||
return ( | ||
<Flex | ||
backgroundColor={COLORS.background} | ||
borderRadius={SPACING.spacing2} | ||
marginBottom={SPACING.spacing3} | ||
marginLeft={SPACING.spacing3} | ||
padding={`${SPACING.spacing4} ${SPACING.spacing3} ${SPACING.spacing4} ${SPACING.spacing3}`} | ||
width={'20rem'} | ||
> | ||
<Flex | ||
flexDirection={DIRECTION_ROW} | ||
paddingRight={SPACING.spacing3} | ||
alignItems={ALIGN_FLEX_START} | ||
> | ||
<img src={heaterShakerModule} alt={'Heater Shaker'} /> | ||
<Flex flexDirection={DIRECTION_COLUMN} paddingLeft={SPACING.spacing3}> | ||
<Text | ||
textTransform={TEXT_TRANSFORM_UPPERCASE} | ||
color={COLORS.darkGreyEnabled} | ||
fontWeight={TYPOGRAPHY.fontWeightRegular} | ||
fontSize={TYPOGRAPHY.fontSizeCaption} | ||
paddingBottom={SPACING.spacing2} | ||
> | ||
{'USB Port'} | ||
</Text> | ||
<Flex paddingBottom={SPACING.spacing2}> | ||
<Icon | ||
name={'heater-shaker'} | ||
size={SIZE_1} | ||
marginRight={SPACING.spacing2} | ||
color={COLORS.darkGreyEnabled} | ||
/> | ||
<Text fontSize={TYPOGRAPHY.fontSizeP}>{'Heater/Shaker GENX'}</Text> | ||
</Flex> | ||
<HeaterShakerModuleData | ||
// TODO(sh, 2022-02-22): replace stubbed out props with actual module values | ||
heaterStatus={'idle'} | ||
shakerStatus={'shaking'} | ||
latchStatus={'Closed and locked'} | ||
targetTemp={0} | ||
currentTemp={0} | ||
targetSpeed={0} | ||
currentSpeed={0} | ||
showTemperatureData={false} | ||
/> | ||
</Flex> | ||
</Flex> | ||
</Flex> | ||
) | ||
} |
136 changes: 134 additions & 2 deletions
136
app/src/organisms/Devices/HeaterShakerWizard/TestShake.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,137 @@ | ||
import React from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import { | ||
ALIGN_CENTER, | ||
ALIGN_FLEX_START, | ||
COLORS, | ||
DIRECTION_COLUMN, | ||
DIRECTION_ROW, | ||
Flex, | ||
Icon, | ||
InputField, | ||
SIZE_AUTO, | ||
SPACING, | ||
Text, | ||
TYPOGRAPHY, | ||
} from '@opentrons/components' | ||
import { RPM } from '@opentrons/shared-data' | ||
import { HeaterShakerModuleCard } from './HeaterShakerModuleCard' | ||
import { TertiaryButton } from '../../../atoms/Buttons' | ||
import { CollapsibleStep } from '../../ProtocolSetup/RunSetupCard/CollapsibleStep' | ||
import { Divider } from '../../../atoms/structure' | ||
|
||
export function TestShake(): JSX.Element { | ||
return <div>TestShake</div> | ||
interface TestShakeProps { | ||
setCurrentPage: React.Dispatch<React.SetStateAction<number>> | ||
} | ||
|
||
export function TestShake(props: TestShakeProps): JSX.Element { | ||
const { setCurrentPage } = props | ||
const { t } = useTranslation('heater_shaker') | ||
|
||
const [isExpanded, setExpanded] = React.useState(false) | ||
|
||
return ( | ||
<Flex flexDirection={DIRECTION_COLUMN}> | ||
<Text | ||
color={COLORS.darkBlack} | ||
fontSize={TYPOGRAPHY.fontSizeH2} | ||
fontWeight={700} | ||
> | ||
{t('step_4_of_4')} | ||
</Text> | ||
<Flex | ||
marginTop={SPACING.spacing3} | ||
marginBottom={SPACING.spacing4} | ||
backgroundColor={COLORS.background} | ||
paddingTop={SPACING.spacing4} | ||
paddingLeft={SPACING.spacing4} | ||
flexDirection={DIRECTION_ROW} | ||
data-testid={'test_shake_banner_info'} | ||
> | ||
<Flex | ||
size={SPACING.spacing6} | ||
color={COLORS.darkGreyEnabled} | ||
paddingBottom={SPACING.spacing4} | ||
> | ||
<Icon name="information" aria-label="information" /> | ||
</Flex> | ||
<Flex | ||
flexDirection={DIRECTION_COLUMN} | ||
paddingLeft={SPACING.spacing3} | ||
fontSize={TYPOGRAPHY.fontSizeP} | ||
paddingBottom={SPACING.spacing4} | ||
> | ||
<Text fontWeight={TYPOGRAPHY.fontWeightRegular}> | ||
{/* TODO(sh, 2022-02-22): Dynamically render this text if a labware/protocol exists */} | ||
{t('test_shake_banner_information')} | ||
</Text> | ||
</Flex> | ||
</Flex> | ||
<Flex | ||
alignSelf={ALIGN_CENTER} | ||
flexDirection={DIRECTION_COLUMN} | ||
fontSize={TYPOGRAPHY.fontSizeCaption} | ||
> | ||
<HeaterShakerModuleCard /> | ||
<TertiaryButton marginLeft={SIZE_AUTO} marginTop={SPACING.spacing4}> | ||
{t('open_labware_latch')} | ||
</TertiaryButton> | ||
<Flex | ||
flexDirection={DIRECTION_ROW} | ||
marginY={SPACING.spacingL} | ||
alignItems={ALIGN_FLEX_START} | ||
> | ||
<Flex flexDirection={DIRECTION_COLUMN} maxWidth={'6.25rem'}> | ||
<Text fontSize={TYPOGRAPHY.fontSizeCaption}> | ||
{t('set_shake_speed')} | ||
</Text> | ||
{/* TODO(sh, 2022-02-22): Wire up input when end points are updated */} | ||
<InputField units={RPM} value={'1000'} readOnly /> | ||
<Text fontSize={TYPOGRAPHY.fontSizeCaption}> | ||
{t('min_max_rpm', { min: '200', max: '1800' })} | ||
</Text> | ||
</Flex> | ||
<TertiaryButton | ||
fontSize={TYPOGRAPHY.fontSizeCaption} | ||
marginLeft={SIZE_AUTO} | ||
marginTop={SPACING.spacing3} | ||
> | ||
{t('start_shaking')} | ||
</TertiaryButton> | ||
</Flex> | ||
</Flex> | ||
<Divider marginY={SPACING.spacing4} /> | ||
<CollapsibleStep | ||
expanded={isExpanded} | ||
title={t('troubleshooting')} | ||
toggleExpanded={() => setExpanded(!isExpanded)} | ||
> | ||
<Flex | ||
flexDirection={DIRECTION_ROW} | ||
alignItems={ALIGN_FLEX_START} | ||
marginY={SPACING.spacing6} | ||
> | ||
<Text width={'22rem'}>{t('troubleshoot_step1_description')}</Text> | ||
<TertiaryButton | ||
fontSize={TYPOGRAPHY.fontSizeCaption} | ||
marginLeft={SIZE_AUTO} | ||
onClick={() => setCurrentPage(2)} | ||
> | ||
{t('go_to_step_1')} | ||
</TertiaryButton> | ||
</Flex> | ||
<Flex flexDirection={DIRECTION_ROW} alignItems={ALIGN_FLEX_START}> | ||
<Text width={'22rem'}>{t('troubleshoot_step2_description')}</Text> | ||
<TertiaryButton | ||
fontSize={TYPOGRAPHY.fontSizeCaption} | ||
marginLeft={SIZE_AUTO} | ||
onClick={() => setCurrentPage(3)} | ||
> | ||
{t('go_to_step_2')} | ||
</TertiaryButton> | ||
</Flex> | ||
</CollapsibleStep> | ||
<Divider marginTop={SPACING.spacing4} marginBottom={SPACING.spacingXL} /> | ||
</Flex> | ||
) | ||
} |
86 changes: 86 additions & 0 deletions
86
app/src/organisms/Devices/HeaterShakerWizard/__tests__/TestShake.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import * as React from 'react' | ||
import { renderWithProviders } from '@opentrons/components' | ||
import { i18n } from '../../../../i18n' | ||
import { TestShake } from '../TestShake' | ||
import { HeaterShakerModuleCard } from '../HeaterShakerModuleCard' | ||
import { fireEvent } from '@testing-library/react' | ||
|
||
jest.mock('../HeaterShakerModuleCard') | ||
|
||
const mockHeaterShakerModuleCard = HeaterShakerModuleCard as jest.MockedFunction< | ||
typeof HeaterShakerModuleCard | ||
> | ||
|
||
const render = (props: React.ComponentProps<typeof TestShake>) => { | ||
return renderWithProviders(<TestShake {...props} />, { | ||
i18nInstance: i18n, | ||
})[0] | ||
} | ||
|
||
describe('TestShake', () => { | ||
let props: React.ComponentProps<typeof TestShake> | ||
beforeEach(() => { | ||
props = { | ||
setCurrentPage: jest.fn(), | ||
} | ||
mockHeaterShakerModuleCard.mockReturnValue( | ||
<div>Mock Heater Shaker Module Card</div> | ||
) | ||
}) | ||
it('renders the correct title', () => { | ||
const { getByText } = render(props) | ||
getByText('Step 4 of 4: Test shake') | ||
}) | ||
|
||
it('renders the information banner icon and description', () => { | ||
const { getByText, getByLabelText } = render(props) | ||
getByLabelText('information') | ||
getByText( | ||
'If you want to add labware to the module before doing a test shake, you can use the labware latch controls to hold the latches open.' | ||
) | ||
}) | ||
|
||
it('renders a heater shaker module card', () => { | ||
const { getByText } = render(props) | ||
|
||
getByText('Mock Heater Shaker Module Card') | ||
}) | ||
|
||
it('renders the open labware latch button and is enabled', () => { | ||
const { getByRole } = render(props) | ||
const button = getByRole('button', { name: /Open Labware Latch/i }) | ||
expect(button).toBeEnabled() | ||
}) | ||
|
||
it('renders the start shaking button and is enabled', () => { | ||
const { getByRole } = render(props) | ||
const button = getByRole('button', { name: /Start Shaking/i }) | ||
expect(button).toBeEnabled() | ||
}) | ||
|
||
it('renders an input field for speed setting', () => { | ||
const { getByText, getByRole } = render(props) | ||
|
||
getByText('Set shake speed') | ||
getByRole('textbox') | ||
}) | ||
|
||
it('renders troubleshooting accordion and contents', () => { | ||
const { getByText, getByRole } = render(props) | ||
|
||
const troubleshooting = getByText('Troubleshooting') | ||
fireEvent.click(troubleshooting) | ||
|
||
getByText( | ||
'Return to Step 1 to see instructions for securing the module to the deck.' | ||
) | ||
const buttonStep1 = getByRole('button', { name: /Go to Step 1/i }) | ||
expect(buttonStep1).toBeEnabled() | ||
|
||
getByText( | ||
'Return to Step 2 to see instructions for securing the thermal adapter to the module.' | ||
) | ||
const buttonStep2 = getByRole('button', { name: /Go to Step 2/i }) | ||
expect(buttonStep2).toBeEnabled() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.