Skip to content

Commit

Permalink
feat(app): add Deck configuration page component (#13784)
Browse files Browse the repository at this point in the history
* feat(app): add Deck configuration page component
  • Loading branch information
koji authored Oct 17, 2023
1 parent 02190d6 commit 3d559e2
Show file tree
Hide file tree
Showing 16 changed files with 446 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,4 @@ opentrons-robot-app.tar.gz
*.swp

# asdf versions file
*.tool-versions
.tool-versions
7 changes: 7 additions & 0 deletions app/src/App/OnDeviceDisplayApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { InstrumentsDashboard } from '../pages/OnDeviceDisplay/InstrumentsDashbo
import { InstrumentDetail } from '../pages/OnDeviceDisplay/InstrumentDetail'
import { Welcome } from '../pages/OnDeviceDisplay/Welcome'
import { InitialLoadingScreen } from '../pages/OnDeviceDisplay/InitialLoadingScreen'
import { DeckConfiguration } from '../pages/DeckConfiguration'
import { PortalRoot as ModalPortalRoot } from './portal'
import { getOnDeviceDisplaySettings, updateConfigValue } from '../redux/config'
import { updateBrightness } from '../redux/shell'
Expand Down Expand Up @@ -183,6 +184,12 @@ export const onDeviceDisplayRoutes: RouteProps[] = [
name: 'Emergency Stop',
path: '/emergency-stop',
},
{
Component: DeckConfiguration,
exact: true,
name: 'Deck Configuration',
path: '/deck-configuration',
},
{
Component: () => (
<>
Expand Down
11 changes: 11 additions & 0 deletions app/src/App/__tests__/OnDeviceDisplayApp.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Welcome } from '../../pages/OnDeviceDisplay/Welcome'
import { NameRobot } from '../../pages/OnDeviceDisplay/NameRobot'
import { InitialLoadingScreen } from '../../pages/OnDeviceDisplay/InitialLoadingScreen'
import { EmergencyStop } from '../../pages/EmergencyStop'
import { DeckConfiguration } from '../../pages/DeckConfiguration'
import { getOnDeviceDisplaySettings } from '../../redux/config'
import { getIsShellReady } from '../../redux/shell'
import { getLocalRobot } from '../../redux/discovery'
Expand All @@ -46,6 +47,7 @@ jest.mock('../../pages/OnDeviceDisplay/RunSummary')
jest.mock('../../pages/OnDeviceDisplay/NameRobot')
jest.mock('../../pages/OnDeviceDisplay/InitialLoadingScreen')
jest.mock('../../pages/EmergencyStop')
jest.mock('../../pages/DeckConfiguration')
jest.mock('../../redux/config')
jest.mock('../../redux/shell')
jest.mock('../../redux/discovery')
Expand Down Expand Up @@ -100,6 +102,9 @@ const mockNameRobot = NameRobot as jest.MockedFunction<typeof NameRobot>
const mockEmergencyStop = EmergencyStop as jest.MockedFunction<
typeof EmergencyStop
>
const mockDeckConfiguration = DeckConfiguration as jest.MockedFunction<
typeof DeckConfiguration
>
const mockGetOnDeviceDisplaySettings = getOnDeviceDisplaySettings as jest.MockedFunction<
typeof getOnDeviceDisplaySettings
>
Expand Down Expand Up @@ -149,6 +154,7 @@ describe('OnDeviceDisplayApp', () => {
mockNameRobot.mockReturnValue(<div>Mock NameRobot</div>)
mockInitialLoadingScreen.mockReturnValue(<div>Mock Loading</div>)
mockEmergencyStop.mockReturnValue(<div>Mock EmergencyStop</div>)
mockDeckConfiguration.mockReturnValue(<div>Mock DeckConfiguration</div>)
mockUseCurrentRunRoute.mockReturnValue(null)
mockGetLocalRobot.mockReturnValue(mockConnectedRobot)
})
Expand Down Expand Up @@ -227,6 +233,11 @@ describe('OnDeviceDisplayApp', () => {
const [{ getByText }] = render('/emergency-stop')
getByText('Mock EmergencyStop')
})
it('renders DeckConfiguration component from /deck-configuration', () => {
mockUseCurrentRunRoute.mockReturnValue('/deck-configuration')
const [{ getByText }] = render('/deck-configuration')
getByText('Mock DeckConfiguration')
})
it('renders protocol receipt toasts', () => {
render('/')
expect(mockUseProtocolReceiptToasts).toHaveBeenCalled()
Expand Down
1 change: 0 additions & 1 deletion app/src/DesignTokens/BorderRadius/BorderRadius.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const Template: Story<BorderRadiusStorybookProps> = args => {
const targetBorderRadiuses = args.borderRadius.filter(s =>
s[0].includes('borderRadiusSize')
)
console.log(targetBorderRadiuses)
return (
<Flex
flexDirection={DIRECTION_COLUMN}
Expand Down
16 changes: 16 additions & 0 deletions app/src/organisms/ChildNavigation/ChildNavigation.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react'
import { touchScreenViewport } from '../../DesignTokens/constants'
import { SmallButton } from '../../atoms/buttons'
import { ChildNavigation } from '.'
import type { Story, Meta } from '@storybook/react'

Expand Down Expand Up @@ -32,3 +33,18 @@ TitleWithLinkButton.args = {
iconPlacement: 'startIcon',
onClickButton: () => {},
}

export const TitleWithTwoButtons = Template.bind({})
const secondaryButtonProps: React.ComponentProps<typeof SmallButton> = {
onClick: () => {},
buttonText: 'Setup Instructions',
buttonType: 'tertiaryLowLight',
iconName: 'information',
iconPlacement: 'startIcon',
}
TitleWithTwoButtons.args = {
header: 'Header',
buttonText: 'ButtonText',
onClickButton: () => {},
secondaryButtonProps,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from 'react'

import { renderWithProviders } from '@opentrons/components'
import { SmallButton } from '../../../atoms/buttons'
import { ChildNavigation } from '..'

const render = (props: React.ComponentProps<typeof ChildNavigation>) =>
renderWithProviders(<ChildNavigation {...props} />)

const mockOnClickBack = jest.fn()
const mockOnClickButton = jest.fn()
const mockOnClickSecondaryButton = jest.fn()

const mockSecondaryButtonProps: React.ComponentProps<typeof SmallButton> = {
onClick: mockOnClickSecondaryButton,
buttonText: 'Setup Instructions',
buttonType: 'tertiaryLowLight',
iconName: 'information',
iconPlacement: 'startIcon',
}

describe('ChildNavigation', () => {
let props: React.ComponentProps<typeof ChildNavigation>

beforeEach(() => {
props = {
header: 'mock header',
onClickBack: mockOnClickBack,
}
})

it('should render text and back button', () => {
const [{ getByText, getByTestId }] = render(props)
getByText('mock header')
getByTestId('ChildNavigation_Back_Button')
})

it('should call a mock function when tapping the back button', () => {
const [{ getByTestId }] = render(props)
getByTestId('ChildNavigation_Back_Button').click()
expect(mockOnClickBack).toHaveBeenCalled()
})

it('should render text, back button and small button', () => {
props = {
...props,
buttonText: 'mock button',
onClickButton: mockOnClickButton,
}
const [{ getByText, getByTestId }] = render(props)
getByText('mock header')
getByTestId('ChildNavigation_Back_Button')
const mockButton = getByText('mock button')
mockButton.click()
expect(mockOnClickButton).toHaveBeenCalled()
})

it('should render text, back button and 2 buttons', () => {
props = {
...props,
buttonText: 'mock button',
onClickButton: mockOnClickButton,
secondaryButtonProps: mockSecondaryButtonProps,
}
const [{ getByText, getByTestId }] = render(props)
getByText('mock header')
getByTestId('ChildNavigation_Back_Button')
getByText('mock button')
const secondaryButton = getByText('Setup Instructions')
secondaryButton.click()
expect(mockOnClickSecondaryButton).toHaveBeenCalled()
})
})
30 changes: 21 additions & 9 deletions app/src/organisms/ChildNavigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styled from 'styled-components'
import {
ALIGN_CENTER,
COLORS,
DIRECTION_ROW,
Flex,
Icon,
JUSTIFY_FLEX_START,
Expand Down Expand Up @@ -35,6 +36,7 @@ interface ChildNavigationProps {
buttonType?: SmallButtonTypes
iconName?: IconName
iconPlacement?: IconPlacement
secondaryButtonProps?: React.ComponentProps<typeof SmallButton>
}

export function ChildNavigation({
Expand All @@ -46,6 +48,7 @@ export function ChildNavigation({
buttonType = 'primary',
iconName,
iconPlacement,
secondaryButtonProps,
}: ChildNavigationProps): JSX.Element {
return (
<Flex
Expand All @@ -61,22 +64,31 @@ export function ChildNavigation({
backgroundColor={COLORS.white}
>
<Flex gridGap={SPACING.spacing16} justifyContent={JUSTIFY_FLEX_START}>
<IconButton onClick={onClickBack}>
<IconButton
onClick={onClickBack}
data-testid="ChildNavigation_Back_Button"
>
<Icon name="back" size="3rem" color={COLORS.darkBlack100} />
</IconButton>
<StyledText as="h2" fontWeight={TYPOGRAPHY.fontWeightBold}>
{header}
</StyledText>
</Flex>
{onClickButton != null && buttonText != null ? (
<SmallButton
buttonType={buttonType}
buttonCategory="rounded"
buttonText={buttonText}
onClick={onClickButton}
iconName={iconName}
iconPlacement={iconPlacement}
/>
<Flex flexDirection={DIRECTION_ROW} gridGap={SPACING.spacing8}>
{secondaryButtonProps != null ? (
<SmallButton {...secondaryButtonProps} />
) : null}

<SmallButton
buttonType={buttonType}
buttonCategory={buttonType === 'primary' ? 'rounded' : 'default'}
buttonText={buttonText}
onClick={onClickButton}
iconName={iconName}
iconPlacement={iconPlacement}
/>
</Flex>
) : null}
{inlineNotification != null ? (
<InlineNotification
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { css } from 'styled-components'

import {
ALIGN_CENTER,
BORDERS,
Expand All @@ -17,7 +18,6 @@ import { useUpdateDeckConfigurationMutation } from '@opentrons/react-api-client'
import {
getFixtureDisplayName,
STAGING_AREA_LOAD_NAME,
// STANDARD_SLOT_LOAD_NAME,
TRASH_BIN_LOAD_NAME,
WASTE_CHUTE_LOAD_NAME,
} from '@opentrons/shared-data'
Expand All @@ -38,9 +38,6 @@ interface AddDeckConfigurationModalProps {
isOnDevice?: boolean
}

// ToDo (kk:09/29/2023)
// update this component when Deck configuration component is ready
// Need to use getFixtureDisplayName
export function AddDeckConfigurationModal({
fixtureLocation,
setShowAddFixtureModal,
Expand All @@ -51,6 +48,7 @@ export function AddDeckConfigurationModal({
const modalHeader: ModalHeaderBaseProps = {
title: t('add_to_slot', { slotName: fixtureLocation }),
hasExitIcon: true,
onClick: () => setShowAddFixtureModal(false),
}

const modalProps: LegacyModalProps = {
Expand Down Expand Up @@ -86,19 +84,21 @@ export function AddDeckConfigurationModal({
return (
<>
{isOnDevice ? (
<Modal header={modalHeader}>
<Modal
header={modalHeader}
onOutsideClick={() => setShowAddFixtureModal(false)}
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing32}>
<StyledText as="p">{t('add_to_slot_description')}</StyledText>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
{/* ToDo (kk:10/05/2023) I will update this part later */}
{/* {availableFixtures.map((fixture, index) => (
<React.Fragment key={`fixture_${index}`}>
<AddFixtureButton fixtureLoadName={fixture} />
{availableFixtures.map(fixture => (
<React.Fragment key={fixture}>
<AddFixtureButton
fixtureLoadName={fixture}
handleClickAdd={handleClickAdd}
/>
</React.Fragment>
))} */}
<AddFixtureButton fixtureLoadName={t('staging_area_slot')} />
<AddFixtureButton fixtureLoadName={t('trash')} />
<AddFixtureButton fixtureLoadName={t('waste_chute')} />
))}
</Flex>
</Flex>
</Modal>
Expand All @@ -108,7 +108,7 @@ export function AddDeckConfigurationModal({
<StyledText as="p">{t('add_fixture_description')}</StyledText>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
{availableFixtures.map(fixture => (
<React.Fragment key={`${fixture}`}>
<React.Fragment key={fixture}>
<Flex
flexDirection={DIRECTION_ROW}
alignItems={ALIGN_CENTER}
Expand All @@ -135,34 +135,34 @@ export function AddDeckConfigurationModal({
}

interface AddFixtureButtonProps {
fixtureLoadName: string
fixtureLoadName: FixtureLoadName
handleClickAdd: (fixtureLoadName: FixtureLoadName) => void
}
function AddFixtureButton({
fixtureLoadName,
handleClickAdd,
}: AddFixtureButtonProps): JSX.Element {
const { t } = useTranslation('device_details')

// ToDo (kk:10/02/2023)
// Need to update a function for onClick
return (
<Btn
onClick={() => {}}
onClick={() => handleClickAdd(fixtureLoadName)}
display="flex"
justifyContent={JUSTIFY_SPACE_BETWEEN}
flexDirection={DIRECTION_ROW}
alignItems={ALIGN_CENTER}
padding={`${SPACING.spacing16} ${SPACING.spacing24}`}
css={FIXTIRE_BUTTON_STYLE}
css={FIXTURE_BUTTON_STYLE}
>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{fixtureLoadName}
{getFixtureDisplayName(fixtureLoadName)}
</StyledText>
<StyledText as="p">{t('add')}</StyledText>
</Btn>
)
}

const FIXTIRE_BUTTON_STYLE = css`
const FIXTURE_BUTTON_STYLE = css`
background-color: ${COLORS.light1};
cursor: default;
border-radius: ${BORDERS.borderRadiusSize3};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function DeckFixtureSetupInstructionsModal({
iconName: 'information',
iconColor: COLORS.darkBlack100,
hasExitIcon: true,
onClick: () => setShowSetupInstructionsModal(false),
}

const modalProps: LegacyModalProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ describe('Touchscreen AddDeckConfigurationModal', () => {
getByText(
'Choose a fixture below to add to your deck configuration. It will be referenced during protocol analysis.'
)
getByText('Staging area slot')
getByText('Trash')
getByText('Waste chute')
getByText('Staging Area Slot')
getByText('Trash Bin')
getByText('Waste Chute')
expect(getAllByText('Add').length).toBe(3)
})

Expand Down
Loading

0 comments on commit 3d559e2

Please sign in to comment.