diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index 919c22212ec..e38936a3d7e 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -138,6 +138,7 @@ "recalibrate_pipette": "Recalibrate pipette", "recent_protocol_runs": "Recent Protocol Runs", "rerun_now": "Rerun protocol now", + "rerun_loading": "Protocol re-run is disabled until data connection fully loads", "reset_all": "Reset all", "reset_estop": "Reset E-stop", "resume_operation": "Resume operation", diff --git a/app/src/organisms/Devices/HistoricalProtocolRunOverflowMenu.tsx b/app/src/organisms/Devices/HistoricalProtocolRunOverflowMenu.tsx index c4fd49158e1..5632df49161 100644 --- a/app/src/organisms/Devices/HistoricalProtocolRunOverflowMenu.tsx +++ b/app/src/organisms/Devices/HistoricalProtocolRunOverflowMenu.tsx @@ -134,7 +134,7 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element { } const trackEvent = useTrackEvent() const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName) - const { reset } = useRunControls(runId, onResetSuccess) + const { reset, isRunControlLoading } = useRunControls(runId, onResetSuccess) const { deleteRun } = useDeleteRunMutation() const robot = useRobot(robotName) const robotSerialNumber = @@ -184,7 +184,9 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element { {t('rerun_now')} @@ -194,6 +196,11 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element { {t('shared:a_software_update_is_available')} )} + {isRunControlLoading && ( + + {t('rerun_loading')} + + )} { isStopRunActionLoading: false, isResetRunLoading: false, isResumeRunFromRecoveryActionLoading: false, + isRunControlLoading: false, }) when(vi.mocked(useRunStatus)).calledWith(RUN_ID).thenReturn(RUN_STATUS_IDLE) when(vi.mocked(useRunTimestamps)).calledWith(RUN_ID).thenReturn({ @@ -848,6 +849,7 @@ describe('ProtocolRunHeader', () => { isStopRunActionLoading: false, isResetRunLoading: true, isResumeRunFromRecoveryActionLoading: false, + isRunControlLoading: false, }) render() diff --git a/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx b/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx index 8abe226ead3..d0bc2384f91 100644 --- a/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx +++ b/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx @@ -87,6 +87,7 @@ describe('HistoricalProtocolRunOverflowMenu', () => { isStopRunActionLoading: false, isResetRunLoading: false, isResumeRunFromRecoveryActionLoading: false, + isRunControlLoading: false, }) when(useNotifyAllCommandsQuery) .calledWith( @@ -154,6 +155,32 @@ describe('HistoricalProtocolRunOverflowMenu', () => { expect(rerunBtn).toBeDisabled() }) + it('disables the rerun protocol menu item if run data is loading', () => { + when(useRunControls) + .calledWith(RUN_ID, expect.anything()) + .thenReturn({ + play: () => {}, + pause: () => {}, + stop: () => {}, + reset: () => {}, + resumeFromRecovery: () => {}, + isPlayRunActionLoading: false, + isPauseRunActionLoading: false, + isStopRunActionLoading: false, + isResetRunLoading: false, + isResumeRunFromRecoveryActionLoading: false, + isRunControlLoading: true, + }) + render(props) + const btn = screen.getByRole('button') + fireEvent.click(btn) + screen.getByRole('button', { + name: 'View protocol run record', + }) + const rerunBtn = screen.getByRole('button', { name: 'Rerun protocol now' }) + expect(rerunBtn).toBeDisabled() + }) + it('should make overflow menu disabled when e-stop is pressed', () => { when(useIsEstopNotDisengaged).calledWith(ROBOT_NAME).thenReturn(true) render(props) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx index d9d4959814d..3044b359f12 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx @@ -160,7 +160,8 @@ describe('RecentRunProtocolCard', () => { }) vi.mocked(useCloneRun).mockReturnValue({ cloneRun: mockCloneRun, - isLoading: false, + isLoadingRun: false, + isCloning: false, }) }) diff --git a/app/src/organisms/ProtocolUpload/hooks/useCloneRun.ts b/app/src/organisms/ProtocolUpload/hooks/useCloneRun.ts index 41277f47e86..d15bee033ed 100644 --- a/app/src/organisms/ProtocolUpload/hooks/useCloneRun.ts +++ b/app/src/organisms/ProtocolUpload/hooks/useCloneRun.ts @@ -15,7 +15,8 @@ import type { Run } from '@opentrons/api-client' interface UseCloneRunResult { cloneRun: () => void - isLoading: boolean + isLoadingRun: boolean + isCloning: boolean } export function useCloneRun( @@ -25,10 +26,9 @@ export function useCloneRun( ): UseCloneRunResult { const host = useHost() const queryClient = useQueryClient() - const { data: runRecord } = useNotifyRunQuery(runId) + const { data: runRecord, isLoading: isLoadingRun } = useNotifyRunQuery(runId) const protocolKey = runRecord?.data.protocolId ?? null - - const { createRun, isLoading } = useCreateRunMutation({ + const { createRun, isLoading: isCloning } = useCreateRunMutation({ onSuccess: response => { const invalidateRuns = queryClient.invalidateQueries([host, 'runs']) const invalidateProtocols = queryClient.invalidateQueries([ @@ -80,5 +80,5 @@ export function useCloneRun( } } - return { cloneRun, isLoading } + return { cloneRun, isLoadingRun, isCloning } } diff --git a/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx b/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx index 8107a236383..04bff11ab8f 100644 --- a/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx +++ b/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx @@ -59,9 +59,11 @@ describe('useRunControls hook', () => { isStopRunActionLoading: false, isResumeRunFromRecoveryActionLoading: false, }) - when(useCloneRun) - .calledWith(mockPausedRun.id, undefined, true) - .thenReturn({ cloneRun: mockCloneRun, isLoading: false }) + when(useCloneRun).calledWith(mockPausedRun.id, undefined, true).thenReturn({ + cloneRun: mockCloneRun, + isCloning: false, + isLoadingRun: false, + }) const { result } = renderHook(() => useRunControls(mockPausedRun.id)) diff --git a/app/src/organisms/RunTimeControl/hooks.ts b/app/src/organisms/RunTimeControl/hooks.ts index 606e5852f36..f6be73a0706 100644 --- a/app/src/organisms/RunTimeControl/hooks.ts +++ b/app/src/organisms/RunTimeControl/hooks.ts @@ -34,6 +34,7 @@ export interface RunControls { isStopRunActionLoading: boolean isResumeRunFromRecoveryActionLoading: boolean isResetRunLoading: boolean + isRunControlLoading: boolean } export function useRunControls( @@ -51,11 +52,11 @@ export function useRunControls( isResumeRunFromRecoveryActionLoading, } = useRunActionMutations(runId as string) - const { cloneRun, isLoading: isResetRunLoading } = useCloneRun( - runId ?? null, - onCloneRunSuccess, - true - ) + const { + cloneRun, + isLoadingRun: isRunControlLoading, + isCloning: isResetRunLoading, + } = useCloneRun(runId ?? null, onCloneRunSuccess, true) return { play: playRun, @@ -67,6 +68,7 @@ export function useRunControls( isPauseRunActionLoading, isStopRunActionLoading, isResumeRunFromRecoveryActionLoading, + isRunControlLoading, isResetRunLoading, } } diff --git a/app/src/pages/ProtocolSetup/__tests__/ProtocolSetup.test.tsx b/app/src/pages/ProtocolSetup/__tests__/ProtocolSetup.test.tsx index 04f3c2d8e77..b3df47e8ede 100644 --- a/app/src/pages/ProtocolSetup/__tests__/ProtocolSetup.test.tsx +++ b/app/src/pages/ProtocolSetup/__tests__/ProtocolSetup.test.tsx @@ -257,6 +257,7 @@ describe('ProtocolSetup', () => { isStopRunActionLoading: false, isResetRunLoading: false, isResumeRunFromRecoveryActionLoading: false, + isRunControlLoading: false, }) when(vi.mocked(useRunStatus)).calledWith(RUN_ID).thenReturn(RUN_STATUS_IDLE) vi.mocked(useProtocolAnalysisAsDocumentQuery).mockReturnValue({