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

refactor(app): Split RunProgressMeter #15893

Merged
merged 1 commit into from
Aug 5, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
RUN_STATUS_IDLE,
RUN_STATUS_RUNNING,
RUN_STATUS_SUCCEEDED,
RUN_STATUS_STOPPED,
} from '@opentrons/api-client'

import { i18n } from '../../../i18n'
Expand All @@ -34,6 +35,7 @@ import {
import { RunProgressMeter } from '..'
import { renderWithProviders } from '../../../__testing-utils__'
import { useLastRunCommand } from '../../Devices/hooks/useLastRunCommand'
import { useRunningStepCounts } from '../../../resources/protocols/hooks'

import type { RunCommandSummary } from '@opentrons/api-client'
import type * as ApiClient from '@opentrons/react-api-client'
Expand All @@ -52,6 +54,7 @@ vi.mock('../../Devices/hooks')
vi.mock('../../../atoms/ProgressBar')
vi.mock('../../InterventionModal')
vi.mock('../../Devices/hooks/useLastRunCommand')
vi.mock('../../../resources/protocols/hooks')

const render = (props: React.ComponentProps<typeof RunProgressMeter>) => {
return renderWithProviders(<RunProgressMeter {...props} />, {
Expand Down Expand Up @@ -88,6 +91,11 @@ describe('RunProgressMeter', () => {
.thenReturn({ key: NON_DETERMINISTIC_COMMAND_KEY } as RunCommandSummary)

vi.mocked(useNotifyRunQuery).mockReturnValue({ data: null } as any)
vi.mocked(useRunningStepCounts).mockReturnValue({
totalStepCount: null,
currentStepNumber: null,
hasRunDiverged: true,
})

props = {
runId: NON_DETERMINISTIC_RUN_ID,
Expand All @@ -100,7 +108,7 @@ describe('RunProgressMeter', () => {
it('should show only the total count of commands in run and not show the meter when protocol is non-deterministic', () => {
vi.mocked(useCommandQuery).mockReturnValue({ data: null } as any)
render(props)
expect(screen.getByText('Current Step ?/?')).toBeTruthy()
expect(screen.getByText('Current Step ?/?:')).toBeTruthy()
expect(screen.queryByText('MOCK PROGRESS BAR')).toBeFalsy()
})
it('should give the correct info when run status is idle', () => {
Expand Down Expand Up @@ -141,7 +149,19 @@ describe('RunProgressMeter', () => {
it('should render the correct run status when run status is completed', () => {
vi.mocked(useCommandQuery).mockReturnValue({ data: null } as any)
vi.mocked(useRunStatus).mockReturnValue(RUN_STATUS_SUCCEEDED)
vi.mocked(useRunningStepCounts).mockReturnValue({
totalStepCount: 10,
currentStepNumber: 10,
hasRunDiverged: false,
})
render(props)
screen.getByText('Final Step 10/10:')
})

it('should render the correct step info when the run is cancelled before running', () => {
vi.mocked(useCommandQuery).mockReturnValue({ data: null } as any)
vi.mocked(useRunStatus).mockReturnValue(RUN_STATUS_STOPPED)
render(props)
screen.getByText('Final Step ?/?')
screen.getByText('Final Step: N/A')
})
})
15 changes: 15 additions & 0 deletions app/src/organisms/RunProgressMeter/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {
RUN_STATUS_FAILED,
RUN_STATUS_FINISHING,
RUN_STATUS_STOPPED,
RUN_STATUS_SUCCEEDED,
} from '@opentrons/api-client'

import type { RunStatus } from '@opentrons/api-client'

export const TERMINAL_RUN_STATUSES: RunStatus[] = [
RUN_STATUS_STOPPED,
RUN_STATUS_FAILED,
RUN_STATUS_FINISHING,
RUN_STATUS_SUCCEEDED,
]
1 change: 1 addition & 0 deletions app/src/organisms/RunProgressMeter/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useRunProgressCopy'
118 changes: 118 additions & 0 deletions app/src/organisms/RunProgressMeter/hooks/useRunProgressCopy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {
RUN_STATUS_BLOCKED_BY_OPEN_DOOR,
RUN_STATUS_IDLE,
} from '@opentrons/api-client'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { getCommandTextData } from '../../../molecules/Command/utils/getCommandTextData'
import { LegacyStyledText } from '@opentrons/components'
import { CommandText } from '../../../molecules/Command'
import { TERMINAL_RUN_STATUSES } from '../constants'

import type { CommandDetail, RunStatus } from '@opentrons/api-client'
import type {
CompletedProtocolAnalysis,
RobotType,
RunTimeCommand,
} from '@opentrons/shared-data'

interface UseRunProgressResult {
currentStepContents: React.ReactNode
stepCountStr: string | null
progressPercentage: number
}

interface UseRunProgressProps {
runStatus: RunStatus | null
currentStepNumber: number | null
totalStepCount: number | null
analysis: CompletedProtocolAnalysis | null
hasRunDiverged: boolean
runCommandDetails: CommandDetail | null
robotType: RobotType
analysisCommands: RunTimeCommand[]
}

// TODO(jh, 08-05-24): Testing is sufficiently covered by RunProgressMeter, but we should migrate relevant tests to this
// hook after devising a better way to test i18n outside of a component.
export function useRunProgressCopy({
runStatus,
currentStepNumber,
totalStepCount,
hasRunDiverged,
runCommandDetails,
analysisCommands,
robotType,
analysis,
}: UseRunProgressProps): UseRunProgressResult {
const { t } = useTranslation('run_details')

const runHasNotBeenStarted =
(currentStepNumber === 0 &&
runStatus === RUN_STATUS_BLOCKED_BY_OPEN_DOOR) ||
runStatus === RUN_STATUS_IDLE

const currentStepContents = ((): JSX.Element | null => {
if (runHasNotBeenStarted) {
return <LegacyStyledText as="h2">{t('not_started_yet')}</LegacyStyledText>
} else if (analysis != null && !hasRunDiverged) {
return (
<CommandText
commandTextData={getCommandTextData(analysis)}
command={analysisCommands[(currentStepNumber as number) - 1]}
robotType={robotType}
/>
)
} else if (
analysis != null &&
hasRunDiverged &&
runCommandDetails != null
) {
return (
<CommandText
commandTextData={getCommandTextData(analysis)}
command={runCommandDetails.data}
robotType={robotType}
/>
)
} else {
return null
}
})()

const progressPercentage = runHasNotBeenStarted
? 0
: ((currentStepNumber as number) / analysisCommands.length) * 100

const stepCountStr = ((): string | null => {
if (runStatus == null) {
return null
} else {
const isTerminalStatus = TERMINAL_RUN_STATUSES.includes(runStatus)
const stepType = isTerminalStatus ? t('final_step') : t('current_step')

if (runStatus === RUN_STATUS_IDLE) {
return `${stepType}:`
} else if (isTerminalStatus && currentStepNumber == null) {
return `${stepType}: N/A`
} else {
const getCountString = (): string => {
const current = currentStepNumber ?? '?'
const total = totalStepCount ?? '?'

return `${current}/${total}`
}

const countString = getCountString()

return `${stepType} ${countString}:`
}
}
})()

return {
currentStepContents,
stepCountStr,
progressPercentage,
}
}
81 changes: 21 additions & 60 deletions app/src/organisms/RunProgressMeter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,13 @@ import {
import { useCommandQuery } from '@opentrons/react-api-client'
import {
RUN_STATUS_IDLE,
RUN_STATUS_STOPPED,
RUN_STATUS_FAILED,
RUN_STATUS_FINISHING,
RUN_STATUS_SUCCEEDED,
RUN_STATUS_RUNNING,
RUN_STATUS_BLOCKED_BY_OPEN_DOOR,
} from '@opentrons/api-client'

import { useMostRecentCompletedAnalysis } from '../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { getModalPortalEl } from '../../App/portal'
import { Tooltip } from '../../atoms/Tooltip'
import { CommandText } from '../../molecules/Command'
import { useRunStatus } from '../RunTimeControl/hooks'
import { InterventionModal } from '../InterventionModal'
import { ProgressBar } from '../../atoms/ProgressBar'
Expand All @@ -44,17 +39,9 @@ import {
useNotifyRunQuery,
useNotifyAllCommandsQuery,
} from '../../resources/runs'
import { getCommandTextData } from '../../molecules/Command/utils/getCommandTextData'
import { useRunningStepCounts } from '../../resources/protocols/hooks'

import type { RunStatus } from '@opentrons/api-client'

const TERMINAL_RUN_STATUSES: RunStatus[] = [
RUN_STATUS_STOPPED,
RUN_STATUS_FAILED,
RUN_STATUS_FINISHING,
RUN_STATUS_SUCCEEDED,
]
import { TERMINAL_RUN_STATUSES } from './constants'
import { useRunProgressCopy } from './hooks'

interface RunProgressMeterProps {
runId: string
Expand Down Expand Up @@ -104,36 +91,6 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element {

const { downloadRunLog } = useDownloadRunLog(robotName, runId)

const stepCountStr = `${currentStepNumber ?? '?'}/${totalStepCount ?? '?'}`

const runHasNotBeenStarted =
(currentStepNumber === 0 &&
runStatus === RUN_STATUS_BLOCKED_BY_OPEN_DOOR) ||
runStatus === RUN_STATUS_IDLE

let currentStepContents: React.ReactNode = null
if (runHasNotBeenStarted) {
currentStepContents = (
<LegacyStyledText as="h2">{t('not_started_yet')}</LegacyStyledText>
)
} else if (analysis != null && !hasRunDiverged) {
currentStepContents = (
<CommandText
commandTextData={getCommandTextData(analysis)}
command={analysisCommands[(currentStepNumber as number) - 1]}
robotType={robotType}
/>
)
} else if (analysis != null && hasRunDiverged && runCommandDetails != null) {
currentStepContents = (
<CommandText
commandTextData={getCommandTextData(analysis)}
command={runCommandDetails.data}
robotType={robotType}
/>
)
}

React.useEffect(() => {
if (
lastRunCommand != null &&
Expand All @@ -158,6 +115,21 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element {
downloadRunLog()
}

const {
progressPercentage,
stepCountStr,
currentStepContents,
} = useRunProgressCopy({
runStatus,
robotType,
currentStepNumber,
totalStepCount,
analysis,
analysisCommands,
runCommandDetails: runCommandDetails ?? null,
hasRunDiverged,
})

return (
<>
{interventionModalCommandKey != null &&
Expand All @@ -184,15 +156,9 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element {
<LegacyStyledText
as="h2"
fontWeight={TYPOGRAPHY.fontWeightSemiBold}
>{`${
runStatus != null && TERMINAL_RUN_STATUSES.includes(runStatus)
? t('final_step')
: t('current_step')
}${
runStatus === RUN_STATUS_IDLE
? ':'
: ` ${stepCountStr}${currentStepContents != null ? ': ' : ''}`
}`}</LegacyStyledText>
>
{stepCountStr}
</LegacyStyledText>

{currentStepContents}
</Flex>
Expand Down Expand Up @@ -226,12 +192,7 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element {
</Flex>
{!hasRunDiverged ? (
<ProgressBar
percentComplete={
runHasNotBeenStarted
? 0
: ((currentStepNumber as number) / analysisCommands.length) *
100
}
percentComplete={progressPercentage}
outerStyles={css`
height: 0.375rem;
background-color: ${COLORS.grey30};
Expand Down
Loading