diff --git a/protocol-designer/src/components/FileSidebar/FileSidebar.tsx b/protocol-designer/src/components/FileSidebar/FileSidebar.tsx index 3049f036b4a..e05a80e3163 100644 --- a/protocol-designer/src/components/FileSidebar/FileSidebar.tsx +++ b/protocol-designer/src/components/FileSidebar/FileSidebar.tsx @@ -129,6 +129,7 @@ function getWarningContent({ const pipettesDetails = pipettesWithoutStep .map(pipette => `${pipette.mount} ${pipette.spec.displayName}`) .join(' and ') + const modulesDetails = modulesWithoutStep .map(moduleOnDeck => t(`modules:module_long_names.${moduleOnDeck.type}`)) .join(' and ') @@ -169,12 +170,14 @@ function getWarningContent({ if (modulesWithoutStep.length) { const moduleCase = modulesWithoutStep.length > 1 ? 'unused_modules' : 'unused_module' + const slotName = modulesWithoutStep.map(module => module.slot) return { content: ( <>

{t(`export_warnings.${moduleCase}.body1`, { modulesDetails, + slotName: slotName, })}

{t(`export_warnings.${moduleCase}.body2`)}

diff --git a/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx b/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx index ebe86be63a7..a9d2978b981 100644 --- a/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx +++ b/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx @@ -1,7 +1,11 @@ import * as React from 'react' import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest' import { fireEvent, screen, cleanup } from '@testing-library/react' -import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' +import { + FLEX_ROBOT_TYPE, + LabwareDefinition2, + fixtureTiprack300ul, +} from '@opentrons/shared-data' import { renderWithProviders } from '../../../__testing-utils__' import { createFile, getRobotType } from '../../../file-data/selectors' import { @@ -17,11 +21,8 @@ import { import { toggleNewProtocolModal } from '../../../navigation/actions' import { getHasUnsavedChanges } from '../../../load-file/selectors' import { useBlockingHint } from '../../Hints/useBlockingHint' -import { - getUnusedEntities, - getUnusedStagingAreas, - getUnusedTrash, -} from '../utils' +import { getUnusedStagingAreas } from '../utils/getUnusedStagingAreas' +import { getUnusedTrash } from '../utils/getUnusedTrash' import { FileSidebar } from '../FileSidebar' vi.mock('../../../step-forms/selectors') @@ -30,15 +31,14 @@ vi.mock('../../../navigation/actions') vi.mock('../../../navigation/selectors') vi.mock('../../../file-data/selectors') vi.mock('../../Hints/useBlockingHint') -vi.mock('../utils') - +vi.mock('../utils/getUnusedStagingAreas') +vi.mock('../utils/getUnusedTrash') const render = () => { return renderWithProviders(, { i18nInstance: i18n })[0] } describe('FileSidebar', () => { beforeEach(() => { - vi.mocked(getUnusedEntities).mockReturnValue([]) vi.mocked(getUnusedStagingAreas).mockReturnValue([]) vi.mocked(getUnusedTrash).mockReturnValue({ trashBinUnused: false, @@ -91,19 +91,54 @@ describe('FileSidebar', () => { fireEvent.click(screen.getByRole('button', { name: 'Export' })) screen.getByText('Your protocol has no steps') }) - it('renders the unused pipette and module warning', () => { - vi.mocked(getUnusedEntities).mockReturnValue([ - { - mount: 'left', - name: 'p1000_96', - id: 'pipetteId', - tiprackDefURI: 'mockURI', - spec: { - name: 'mock pip name', - displayName: 'mock display name', + it('renders the unused pipette warning', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: {}, + pipettes: { + pipetteId: { + mount: 'left', + name: 'p1000_96', + id: 'pipetteId', + tiprackLabwareDef: [fixtureTiprack300ul as LabwareDefinition2], + tiprackDefURI: ['mockDefUri'], + spec: { + displayName: 'mock display name', + } as any, + }, + }, + additionalEquipmentOnDeck: {}, + labware: {}, + }) + render() + fireEvent.click(screen.getByRole('button', { name: 'Export' })) + screen.getByText('Unused pipette') + }) + it('renders the unused pieptte and module warning', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + moduleId: { + slot: 'A1', + moduleState: {} as any, + id: 'moduleId', + type: 'temperatureModuleType', + model: 'temperatureModuleV2', + }, + }, + pipettes: { + pipetteId: { + mount: 'left', + name: 'p1000_96', + id: 'pipetteId', + tiprackLabwareDef: [fixtureTiprack300ul as LabwareDefinition2], + tiprackDefURI: ['mockDefUri'], + spec: { + displayName: 'mock display name', + } as any, }, }, - ]) + additionalEquipmentOnDeck: {}, + labware: {}, + }) render() fireEvent.click(screen.getByRole('button', { name: 'Export' })) screen.getByText('Unused pipette and module') @@ -140,4 +175,55 @@ describe('FileSidebar', () => { fireEvent.click(screen.getByRole('button', { name: 'Export' })) screen.getByText('Unused gripper') }) + it('renders the unused module warning', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + moduleId: { + slot: 'A1', + moduleState: {} as any, + id: 'moduleId', + type: 'temperatureModuleType', + model: 'temperatureModuleV2', + }, + }, + pipettes: {}, + additionalEquipmentOnDeck: {}, + labware: {}, + }) + render() + fireEvent.click(screen.getByRole('button', { name: 'Export' })) + screen.getByText('Unused module') + screen.getByText( + 'The Temperature module specified in your protocol in Slot A1 is not currently used in any step. In order to run this protocol you will need to power up and connect the module to your robot.' + ) + }) + it('renders the unused modules warning', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + moduleId: { + slot: 'A1', + moduleState: {} as any, + id: 'moduleId', + type: 'temperatureModuleType', + model: 'temperatureModuleV2', + }, + moduleId2: { + slot: 'B1', + moduleState: {} as any, + id: 'moduleId2', + type: 'temperatureModuleType', + model: 'temperatureModuleV2', + }, + }, + pipettes: {}, + additionalEquipmentOnDeck: {}, + labware: {}, + }) + render() + fireEvent.click(screen.getByRole('button', { name: 'Export' })) + screen.getByText('Unused modules') + screen.getByText( + 'One or more modules specified in your protocol in Slot(s) A1,B1 are not currently used in any step. In order to run this protocol you will need to power up and connect the modules to your robot.' + ) + }) }) diff --git a/protocol-designer/src/localization/en/alert.json b/protocol-designer/src/localization/en/alert.json index 272e51a9363..4548d19e57c 100644 --- a/protocol-designer/src/localization/en/alert.json +++ b/protocol-designer/src/localization/en/alert.json @@ -49,6 +49,10 @@ "title": "Missing labware", "body": "Your module has no labware on it. We recommend you add labware before proceeding." }, + "multiple_modules_without_labware": { + "title": "Missing labware", + "body": "One or more module has no labware on it. We recommend you add labware before proceeding" + }, "export_v8_protocol_7_1": { "title": "Robot and app update may be required", "body1": "This protocol can only run on app and robot server version", @@ -256,12 +260,12 @@ }, "unused_module": { "heading": "Unused module", - "body1": "The {{modulesDetails}} specified in your protocol are not currently used in any step. In order to run this protocol you will need to power up and connect the module to your robot.", + "body1": "The {{modulesDetails}} specified in your protocol in Slot {{slotName}} is not currently used in any step. In order to run this protocol you will need to power up and connect the module to your robot.", "body2": "If you don't intend to use the module, please consider removing it from your protocol." }, "unused_modules": { "heading": "Unused modules", - "body1": "The {{modulesDetails}} specified in your protocol are not currently used in any step. In order to run this protocol you will need to power up and connect the modules to your robot.", + "body1": "One or more modules specified in your protocol in Slot(s) {{slotName}} are not currently used in any step. In order to run this protocol you will need to power up and connect the modules to your robot.", "body2": "If you don't intend to use these modules, please consider removing them from your protocol." }, "unused_gripper": {