Skip to content

Commit

Permalink
add disabled condition buttons in Robot Settings Advanced tab
Browse files Browse the repository at this point in the history
  • Loading branch information
koji committed Dec 18, 2023
1 parent a8bb298 commit c3b1398
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import { useLEDLights } from '../../hooks'

interface EnableStatusLightProps {
robotName: string
isEstopNotDisengaged: boolean
}
export function EnableStatusLight({
robotName,
isEstopNotDisengaged,
}: EnableStatusLightProps): JSX.Element {
const { t } = useTranslation('device_settings')
const { lightsEnabled, toggleLights } = useLEDLights(robotName)
Expand Down Expand Up @@ -46,6 +48,7 @@ export function EnableStatusLight({
toggledOn={lightsEnabled}
onClick={toggleLights}
id="RobotSettings_enableStatusLightToggleButton"
disabled={isEstopNotDisengaged}
/>
</Flex>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ const JUPYTER_NOTEBOOK_LINK =

export interface OpenJupyterControlProps {
robotIp: string
isEstopNotDisengaged: boolean
}

export function OpenJupyterControl({
robotIp,
isEstopNotDisengaged,
}: OpenJupyterControlProps): JSX.Element {
const { t } = useTranslation('device_settings')
const href = `http://${robotIp}:48888`
Expand All @@ -51,8 +53,8 @@ export function OpenJupyterControl({
</ExternalLink>
</Box>
<TertiaryButton
disabled={isEstopNotDisengaged}
onClick={() => trackEvent(EVENT_JUPYTER_OPEN)}
as={Link}
href={href}
marginLeft={SPACING.spacing32}
external
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import type { IconProps } from '@opentrons/components'

interface TroubleshootingProps {
robotName: string
isEstopNotDisengaged: boolean
}

export function Troubleshooting({
robotName,
isEstopNotDisengaged,
}: TroubleshootingProps): JSX.Element {
const { t } = useTranslation('device_settings')
const robot = useRobot(robotName)
Expand Down Expand Up @@ -125,7 +127,10 @@ export function Troubleshooting({
</Box>
<TertiaryButton
disabled={
controlDisabled || logsAvailable == null || isDownloadingRobotLogs
controlDisabled ||
logsAvailable == null ||
isDownloadingRobotLogs ||
isEstopNotDisengaged
}
marginLeft={SPACING_AUTO}
onClick={handleClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('EnableStatusLight', () => {
beforeEach(() => {
props = {
robotName: ROBOT_NAME,
isEstopNotDisengaged: false,
}
mockUseLEDLights.mockReturnValue({
lightsEnabled: false,
Expand All @@ -47,4 +48,13 @@ describe('EnableStatusLight', () => {
getByLabelText('enable_status_light').click()
expect(mockToggleLights).toHaveBeenCalled()
})

it('shoud make toggle button disabled when e-stop is pressed', () => {
props = {
...props,
isEstopNotDisengaged: true,
}
const [{ getByLabelText }] = render(props)
expect(getByLabelText('enable_status_light')).toBeDisabled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ const mockIpAddress = '1.1.1.1'
const mockLink = `http://${mockIpAddress}:48888`
const trackEvent = jest.fn()

const render = () => {
const render = (props: React.ComponentProps<typeof OpenJupyterControl>) => {
return renderWithProviders(
<MemoryRouter>
<OpenJupyterControl robotIp={mockIpAddress} />
<OpenJupyterControl {...props} />
</MemoryRouter>,
{ i18nInstance: i18n }
)
}

describe('RobotSettings OpenJupyterControl', () => {
let props: React.ComponentProps<typeof OpenJupyterControl>
beforeEach(() => {
props = {
robotIp: mockIpAddress,
isEstopNotDisengaged: false,
}
mockUseTrackEvent.mockReturnValue(trackEvent)
})

Expand All @@ -36,33 +41,39 @@ describe('RobotSettings OpenJupyterControl', () => {
})

it('should render title, description and button', () => {
const [{ getByText, getByRole }] = render()
const [{ getByText, getByRole }] = render(props)
getByText('Jupyter Notebook')
getByText(
'Open the Jupyter Notebook running on this robot in the web browser. This is an experimental feature.'
)
getByText('Learn more about using Jupyter notebook')
getByText('Launch Jupyter Notebook')
expect(
getByRole('link', { name: 'Launch Jupyter Notebook' })
getByRole('button', { name: 'Launch Jupyter Notebook' })
).toBeInTheDocument()
})

it('should render jupyter notebook link', () => {
const [{ getByText }] = render()
const link = getByText('Launch Jupyter Notebook')
expect(link.closest('a')).toHaveAttribute('href', mockLink)
expect(link.closest('a')).toHaveAttribute('target', '_blank')
expect(link.closest('a')).toHaveAttribute('rel', 'noopener noreferrer')
const [{ getByRole }] = render(props)
const link = getByRole('button', { name: 'Launch Jupyter Notebook' })
console.log(link)
expect(link).toHaveAttribute('href', mockLink)
})

it('should send and analytics event on link click', () => {
const [{ getByRole }] = render()
const button = getByRole('link', { name: 'Launch Jupyter Notebook' })
const [{ getByRole }] = render(props)
const button = getByRole('button', { name: 'Launch Jupyter Notebook' })
fireEvent.click(button)
expect(trackEvent).toHaveBeenCalledWith({
name: ANALYTICS_JUPYTER_OPEN,
properties: {},
})
})

it('should render disabled button when e-stop button is pressed', () => {
props = { ...props, isEstopNotDisengaged: true }
const [{ getByRole }] = render(props)
const button = getByRole('button', { name: 'Launch Jupyter Notebook' })
expect(button).toBeDisabled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useRobot } from '../../../hooks'
import { Troubleshooting } from '../Troubleshooting'

import type { HostConfig } from '@opentrons/api-client'
import { ToasterContextType } from '../../../../ToasterOven/ToasterContext'
import type { ToasterContextType } from '../../../../ToasterOven/ToasterContext'

jest.mock('@opentrons/react-api-client')
jest.mock('../../../../../organisms/ToasterOven')
Expand All @@ -32,18 +32,25 @@ const HOST_CONFIG: HostConfig = { hostname: 'localhost' }
const MOCK_MAKE_TOAST = jest.fn()
const MOCK_EAT_TOAST = jest.fn()

const render = (robotName = ROBOT_NAME) => {
const render = (props: React.ComponentProps<typeof Troubleshooting>) => {
return renderWithProviders(
<MemoryRouter>
<Troubleshooting robotName={robotName} />
<Troubleshooting {...props} />
</MemoryRouter>,
{ i18nInstance: i18n }
)
}

describe('RobotSettings Troubleshooting', () => {
let props: React.ComponentProps<typeof Troubleshooting>
beforeEach(() => {
when(mockUseRobot).calledWith('otie').mockReturnValue(mockConnectableRobot)
props = {
robotName: ROBOT_NAME,
isEstopNotDisengaged: false,
}
when(mockUseRobot)
.calledWith(ROBOT_NAME)
.mockReturnValue(mockConnectableRobot)
when(mockUseHost).calledWith().mockReturnValue(HOST_CONFIG)
when(mockUseToaster)
.calledWith()
Expand All @@ -58,21 +65,21 @@ describe('RobotSettings Troubleshooting', () => {
cleanup()
})
it('should render title, description, and button', () => {
const [{ getByText, getByRole, getByTestId }] = render()
const [{ getByText, getByRole, getByTestId }] = render(props)
getByText('Troubleshooting')
getByTestId('RobotSettings_Troubleshooting')
getByRole('button', { name: 'Download logs' })
})

it('should be disabled when logs are not available', () => {
when(mockUseRobot).calledWith('otie').mockReturnValue(mockUnreachableRobot)
const [{ getByRole }] = render()
const [{ getByRole }] = render(props)
const downloadLogsButton = getByRole('button', { name: 'Download logs' })
expect(downloadLogsButton).toBeDisabled()
})

it('should initiate log download when clicking Download logs button', async () => {
const [{ getByRole, queryByText }] = render()
const [{ getByRole, queryByText }] = render(props)
const downloadLogsButton = getByRole('button', { name: 'Download logs' })
act(() => {
downloadLogsButton.click()
Expand All @@ -89,4 +96,10 @@ describe('RobotSettings Troubleshooting', () => {
expect(downloadLogsButton).not.toBeDisabled()
})
})

it('should make donwload button disabled when e-stop is pressed', () => {
props = { ...props, isEstopNotDisengaged: true }
const [{ getByRole }] = render(props)
expect(getByRole('button', { name: 'Download logs' })).toBeDisabled()
})
})
35 changes: 23 additions & 12 deletions app/src/organisms/Devices/RobotSettings/RobotSettingsAdvanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { DeviceResetModal } from './AdvancedTab/AdvancedTabSlideouts/DeviceReset
import { handleUpdateBuildroot } from './UpdateBuildroot'
import { UNREACHABLE } from '../../../redux/discovery'
import { Portal } from '../../../App/portal'
import { useIsEstopNotDisengaged } from '../../../resources/devices/hooks/useIsEstopNotDisengaged'

import type { State, Dispatch } from '../../../redux/types'
import type {
Expand Down Expand Up @@ -72,6 +73,7 @@ export function RobotSettingsAdvanced({
] = React.useState<boolean>(false)

const isRobotBusy = useIsRobotBusy({ poll: true })
const isEstopNotDisengaged = useIsEstopNotDisengaged(robotName)

const robot = useRobot(robotName)
const isFlex = useIsFlex(robotName)
Expand Down Expand Up @@ -149,7 +151,7 @@ export function RobotSettingsAdvanced({
<DisplayRobotName
robotName={robotName}
updateIsExpanded={updateIsExpanded}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
<Divider marginY={SPACING.spacing16} />
<RobotServerVersion robotName={robotName} />
Expand All @@ -161,62 +163,71 @@ export function RobotSettingsAdvanced({
<UsageSettings
settings={findSettings('enableDoorSafetySwitch')}
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
</>
)}
<Divider marginY={SPACING.spacing16} />
<GantryHoming
settings={findSettings('disableHomeOnBoot')}
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>

{isFlex ? (
<>
<Divider marginY={SPACING.spacing16} />
<EnableStatusLight robotName={robotName} />
<EnableStatusLight
robotName={robotName}
isEstopNotDisengaged={isEstopNotDisengaged}
/>
</>
) : null}
<Divider marginY={SPACING.spacing16} />
<OpenJupyterControl robotIp={ipAddress} />
<OpenJupyterControl
robotIp={ipAddress}
isEstopNotDisengaged={isEstopNotDisengaged}
/>
<Divider marginY={SPACING.spacing16} />
<UpdateRobotSoftware
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
onUpdateStart={() => handleUpdateBuildroot(robot)}
/>
<Troubleshooting robotName={robotName} />
<Troubleshooting
robotName={robotName}
isEstopNotDisengaged={isEstopNotDisengaged}
/>
<Divider marginY={SPACING.spacing16} />
<DeviceReset
updateIsExpanded={updateIsExpanded}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
{isFlex ? null : (
<>
<Divider marginY={SPACING.spacing16} />
<UseOlderProtocol
settings={findSettings('disableFastProtocolUpload')}
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
<Divider marginY={SPACING.spacing16} />
<LegacySettings
settings={findSettings('deckCalibrationDots')}
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
<Divider marginY={SPACING.spacing16} />
<ShortTrashBin
settings={findSettings('shortFixedTrash')}
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
<Divider marginY={SPACING.spacing16} />
<UseOlderAspirateBehavior
settings={findSettings('useOldAspirationFunctions')}
robotName={robotName}
isRobotBusy={isRobotBusy}
isRobotBusy={isRobotBusy || isEstopNotDisengaged}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const mockGetRequestById = RobotApi.getRequestById as jest.MockedFunction<
typeof RobotApi.getRequestById
>

describe('<TemporarySelectNetwork />', () => {
describe('SelectNetwork', () => {
let dispatch: any
let mockStore: any

Expand Down Expand Up @@ -102,7 +102,11 @@ describe('<TemporarySelectNetwork />', () => {

render = () => {
return mount(
<SelectNetwork robotName={mockRobotName} isRobotBusy={false} />,
<SelectNetwork
robotName={mockRobotName}
isRobotBusy={false}
isEstopNotDisengaged={false}
/>,
{
wrappingComponent: Provider,
wrappingComponentProps: { store: mockStore },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ const mockCalibratedModule = {
},
}

const ROBOT_NAME = 'mockRobot'

const render = (
props: React.ComponentProps<typeof ModuleCalibrationItems>
): ReturnType<typeof renderWithProviders> => {
Expand All @@ -71,6 +73,7 @@ describe('ModuleCalibrationItems', () => {
attachedModules: mockFetchModulesSuccessActionPayloadModules,
updateRobotStatus: jest.fn(),
formattedPipetteOffsetCalibrations: [],
robotName: ROBOT_NAME,
}
mockModuleCalibrationOverflowMenu.mockReturnValue(
<div>mock ModuleCalibrationOverflowMenu</div>
Expand Down
Loading

0 comments on commit c3b1398

Please sign in to comment.