From bda54409dc44e2bddfbfade2655b1168bc615e8e Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 7 Apr 2023 15:51:42 -0400 Subject: [PATCH 01/28] Add RecentRun card --- .../MiniCardButton/MiniCardButton.stories.tsx | 35 ------ .../__tests__/MiniCardButton.test.tsx | 44 -------- app/src/molecules/MiniCardButton/index.tsx | 62 ---------- .../pages/OnDeviceDisplay/RobotDashboard.tsx | 106 ++++++++++-------- .../src/ui-style-constants/typography.ts | 4 +- 5 files changed, 60 insertions(+), 191 deletions(-) delete mode 100644 app/src/molecules/MiniCardButton/MiniCardButton.stories.tsx delete mode 100644 app/src/molecules/MiniCardButton/__tests__/MiniCardButton.test.tsx delete mode 100644 app/src/molecules/MiniCardButton/index.tsx diff --git a/app/src/molecules/MiniCardButton/MiniCardButton.stories.tsx b/app/src/molecules/MiniCardButton/MiniCardButton.stories.tsx deleted file mode 100644 index be46fcf99de..00000000000 --- a/app/src/molecules/MiniCardButton/MiniCardButton.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as React from 'react' -import { MemoryRouter } from 'react-router-dom' -import { Flex, SPACING } from '@opentrons/components' -import { GlobalStyle } from '../../atoms/GlobalStyle' -import { MiniCardButton } from '.' - -import type { Story, Meta } from '@storybook/react' - -export default { - title: 'Odd/Molecules/MiniCardButton', - component: MiniCardButton, - decorators: [ - Story => ( - <> - - - - ), - ], -} as Meta - -const Template: Story> = args => ( - - - - - -) - -export const Primary = Template.bind({}) -Primary.args = { - iconName: 'wifi', - cardName: 'Settings', - destinationPath: '/app-molecules-minicardbutton--primary', -} diff --git a/app/src/molecules/MiniCardButton/__tests__/MiniCardButton.test.tsx b/app/src/molecules/MiniCardButton/__tests__/MiniCardButton.test.tsx deleted file mode 100644 index 6d189f33415..00000000000 --- a/app/src/molecules/MiniCardButton/__tests__/MiniCardButton.test.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import * as React from 'react' -import { fireEvent } from '@testing-library/react' -import { renderWithProviders } from '@opentrons/components' - -import { MiniCardButton } from '..' - -const mockPush = jest.fn() - -jest.mock('react-router-dom', () => { - const reactRouterDom = jest.requireActual('react-router-dom') - return { - ...reactRouterDom, - useHistory: () => ({ push: mockPush } as any), - } -}) - -const render = (props: React.ComponentProps) => { - return renderWithProviders() -} - -describe('MiniCardButton', () => { - let props: React.ComponentProps - - beforeEach(() => { - props = { - iconName: 'play', - cardName: 'mockMiniCard', - destinationPath: '/mockPath', - } - }) - - it('should render text and icon', () => { - const [{ getByText, getByTestId }] = render(props) - getByText('mockMiniCard') - getByTestId('miniCardButton_play') - }) - - it('should call mock function when tapping MiniCardButton', () => { - const [{ getByRole }] = render(props) - const button = getByRole('button') - fireEvent.click(button) - expect(mockPush).toHaveBeenCalledWith('/mockPath') - }) -}) diff --git a/app/src/molecules/MiniCardButton/index.tsx b/app/src/molecules/MiniCardButton/index.tsx deleted file mode 100644 index 0053a38750b..00000000000 --- a/app/src/molecules/MiniCardButton/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react' -import { useHistory } from 'react-router-dom' - -import { - Btn, - SPACING, - COLORS, - Icon, - DIRECTION_COLUMN, - ALIGN_FLEX_START, - BORDERS, -} from '@opentrons/components' - -import { StyledText } from '../../atoms/text' - -import type { IconName } from '@opentrons/components' - -// Note: kj width & height default values would be changed in hi-fi -export interface MiniCardButtonProps { - width?: string - height?: string - iconName: IconName - cardName: string - destinationPath: string -} - -export function MiniCardButton({ - width = '19rem', - height = '9.375rem', - iconName, - cardName, - destinationPath, -}: MiniCardButtonProps): JSX.Element { - const history = useHistory() - return ( - history.push(`${destinationPath}`)} - backgroundColor={COLORS.lightGreyPressed} - width={width} - height={height} - > - - - {cardName} - - - ) -} diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index c82633e244f..16ff6e40018 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -11,42 +11,21 @@ import { TYPOGRAPHY, ALIGN_CENTER, ALIGN_FLEX_END, + BORDERS, } from '@opentrons/components' import { StyledText } from '../../atoms/text' -import { TertiaryButton } from '../../atoms/buttons' -import { MiniCardButton } from '../../molecules/MiniCardButton' +import { Chip } from '../../atoms/Chip' import { Navigation } from '../../organisms/OnDeviceDisplay/Navigation' import { onDeviceDisplayRoutes } from '../../App/OnDeviceDisplayApp' import abstractImage from '../../assets/images/odd/abstract@x2.png' -import type { MiniCardButtonProps } from '../../molecules/MiniCardButton' - -// TODO: kj 12/0/7/2022 this part will be update when hi-fi is ready -const DASHBOARD_ITEMS: MiniCardButtonProps[] = [ - { - iconName: 'wifi', - cardName: 'Run a protocol', - destinationPath: '/protocols', - }, - { - iconName: 'wifi', - cardName: 'Instrument + Module Hub', - destinationPath: '/attach-instruments', - }, - { - iconName: 'wifi', - cardName: 'Settings', - destinationPath: '/robot-settings', - }, -] +export const MAXIMUM_RECENT_RUN_PROTOCOLS = 8 // This might be changed export function RobotDashboard(): JSX.Element { const { t } = useTranslation('device_details') - // ToDo kj 12/07/2022 get protocol runs and add conditional rendering - // if there is no run data, shows the following return ( - - {t('run_again')} - - + + {t('run_again')} + + {/* {t('have_not_run_description')} + */} + + + + - - {DASHBOARD_ITEMS.map((card, index) => ( - - ))} + + ) +} + +interface RecentRunCardProps { + moduleStatus: string // also need to pass the module status type + protocolName: string // need to change + lastRun: string +} + +function RecentRunCard(): JSX.Element { + // function LastRunCard({ moduleStatus, protocolName, lastRun }): JSX.Element { + return ( + + {/* marginLeft is needed to cancel chip's padding */} + + - {/* temp button to robot dashboard until we can detect setup status */} - - - To ODD Menu - + + + {'Covid-19 qPCR Prep (Station C)'} + + + {'Last run 1 min ago'} + ) } diff --git a/components/src/ui-style-constants/typography.ts b/components/src/ui-style-constants/typography.ts index 5305f01e5d5..67c944e64b7 100644 --- a/components/src/ui-style-constants/typography.ts +++ b/components/src/ui-style-constants/typography.ts @@ -3,6 +3,7 @@ import { COLORS } from './' // Font Sizes export const fontSize38 = '2.375rem' // 38px +export const fontSize32 = '2rem' // 32px export const fontSize28 = '1.75rem' // 28px export const fontSize22 = '1.375rem' // 22px export const fontSize20 = '1.25rem' // 20px @@ -25,13 +26,14 @@ export const fontWeightLight = 300 // Line Heights export const lineHeight48 = '3rem' // 48px +export const lineHeight42 = '2.625rem' // 42px export const lineHeight36 = '2.25rem' // 36px export const lineHeight28 = '1.75rem' // 28px export const lineHeight24 = '1.5rem' // 24px export const lineHeight20 = '1.25rem' // 20px +export const lineHeight18 = '1.125rem' // 18px export const lineHeight16 = '1rem' // 16px export const lineHeight12 = '.75rem' // 12px -export const lineHeight18 = '1.125rem' // 18px // font styles export const fontStyleNormal = 'normal' From 75ae485af6bcaa9b3275d523b9610861bbe4447a Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 7 Apr 2023 18:32:35 -0400 Subject: [PATCH 02/28] Add EmptyRecentRun component - create EmptyRecentRun component - change the folder name under asset from odd to OnDeviceDisplay - update paths in components --- .../OnDeviceDisplay/Illustration@x2.png | Bin 0 -> 8876 bytes .../{odd => OnDeviceDisplay}/abstract@x2.png | Bin .../odd_abstract@x2.png | Bin .../opentrons_logo.png | Bin .../{odd => OnDeviceDisplay}/usb@x2.png | Bin .../localization/en/device_details.json | 4 +- .../organisms/NameRobot/ConfirmRobotName.tsx | 2 +- .../RobotDashboard/EmptyRecentRun.tsx | 60 ++++++++++ .../organisms/OnDeviceDisplay/SleepScreen.tsx | 2 +- .../pages/OnDeviceDisplay/ConnectViaUSB.tsx | 2 +- .../ProtocolDashboard/index.tsx | 2 +- .../pages/OnDeviceDisplay/RobotDashboard.tsx | 103 +++++++++--------- app/src/pages/OnDeviceDisplay/Welcome.tsx | 2 +- 13 files changed, 116 insertions(+), 61 deletions(-) create mode 100644 app/src/assets/images/OnDeviceDisplay/Illustration@x2.png rename app/src/assets/images/{odd => OnDeviceDisplay}/abstract@x2.png (100%) rename app/src/assets/images/{odd => OnDeviceDisplay}/odd_abstract@x2.png (100%) rename app/src/assets/images/{odd => OnDeviceDisplay}/opentrons_logo.png (100%) rename app/src/assets/images/{odd => OnDeviceDisplay}/usb@x2.png (100%) create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx diff --git a/app/src/assets/images/OnDeviceDisplay/Illustration@x2.png b/app/src/assets/images/OnDeviceDisplay/Illustration@x2.png new file mode 100644 index 0000000000000000000000000000000000000000..64be75a60e34ecc4aae717432814e7fe7ca82eda GIT binary patch literal 8876 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 + + Robot Dashboard no protocol run data + + + + {t('no_recent_runs')} + + + {t('no_recent_runs_description')} + + + + ) +} diff --git a/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx b/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx index cf2d9c8da9d..5ff59b1a239 100644 --- a/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx +++ b/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import logo from '../../assets/images/odd/opentrons_logo.png' +import logo from '../../assets/images/OnDeviceDisplay/opentrons_logo.png' export function SleepScreen(): JSX.Element { const speed = 50 diff --git a/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx b/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx index 38938128531..69621906e90 100644 --- a/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx +++ b/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx @@ -18,7 +18,7 @@ import { import { StyledText } from '../../atoms/text' import { StepMeter } from '../../atoms/StepMeter' // import { PrimaryButton } from '../../atoms/buttons' -import usbImage from '../../assets/images/odd/usb@x2.png' +import usbImage from '../../assets/images/OnDeviceDisplay/usb@x2.png' // Note: kj 12/06/2022 The commented-out lines will be activated when the check function is ready export function ConnectViaUSB(): JSX.Element { diff --git a/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx b/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx index e05ae6b3277..54865dce24d 100644 --- a/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx +++ b/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx @@ -34,7 +34,7 @@ import { PinnedProtocol } from './PinnedProtocol' import { ProtocolRow } from './ProtocolRow' import { sortProtocols } from './utils' -import imgSrc from '../../../assets/images/odd/abstract@x2.png' +import imgSrc from '../../../assets/images/OnDeviceDisplay/abstract@x2.png' import type { Dispatch } from '../../../redux/types' import type { ProtocolsOnDeviceSortKey } from '../../../redux/config/types' diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index 16ff6e40018..cdd0e7b1285 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -1,6 +1,5 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Link } from 'react-router-dom' import { Flex, @@ -9,23 +8,44 @@ import { SPACING, COLORS, TYPOGRAPHY, - ALIGN_CENTER, - ALIGN_FLEX_END, BORDERS, } from '@opentrons/components' +import { + useAllProtocolsQuery, + useAllRunsQuery, +} from '@opentrons/react-api-client' import { StyledText } from '../../atoms/text' import { Chip } from '../../atoms/Chip' import { Navigation } from '../../organisms/OnDeviceDisplay/Navigation' import { onDeviceDisplayRoutes } from '../../App/OnDeviceDisplayApp' - -import abstractImage from '../../assets/images/odd/abstract@x2.png' +import { sortProtocols } from './ProtocolDashboard/utils' +import { EmptyRecentRun } from '../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun' export const MAXIMUM_RECENT_RUN_PROTOCOLS = 8 // This might be changed +const SORT_KEY = 'recentRun' export function RobotDashboard(): JSX.Element { const { t } = useTranslation('device_details') + const protocols = useAllProtocolsQuery() + const runs = useAllRunsQuery() + const protocolsData = protocols.data?.data != null ? protocols.data?.data : [] + const runData = runs.data?.data != null ? runs.data?.data : [] + + /** Currently the max number of displaying recent run protocol is 8 */ + const sortedProtocols = sortProtocols(SORT_KEY, protocolsData, runData).slice( + 0, + MAXIMUM_RECENT_RUN_PROTOCOLS + ) + + console.table(sortedProtocols) + + // lastRun + // const lastRun = runs.data?.data.find( + // run => run.protocolId === protocol.id + // )?.createdAt + return ( - - {t('run_again')} - - {/* - Robot Dashboard no protocol run data - - {t('have_not_run')} - - - {t('have_not_run_description')} - - */} - - - - + {sortProtocols.length !== 0 ? ( + <> + + + ) : ( + <> + + {t('run_again')} + + + + + + + )} ) } -interface RecentRunCardProps { - moduleStatus: string // also need to pass the module status type - protocolName: string // need to change - lastRun: string -} +// interface RecentRunCardProps { +// moduleStatus: string // also need to pass the module status type +// protocolName: string // need to change +// lastRun: string +// } function RecentRunCard(): JSX.Element { // function LastRunCard({ moduleStatus, protocolName, lastRun }): JSX.Element { diff --git a/app/src/pages/OnDeviceDisplay/Welcome.tsx b/app/src/pages/OnDeviceDisplay/Welcome.tsx index 7db1692c91a..708ba1544c7 100644 --- a/app/src/pages/OnDeviceDisplay/Welcome.tsx +++ b/app/src/pages/OnDeviceDisplay/Welcome.tsx @@ -13,7 +13,7 @@ import { } from '@opentrons/components' import { StyledText } from '../../atoms/text' -import screenImage from '../../assets/images/odd/odd_abstract@x2.png' +import screenImage from '../../assets/images/OnDeviceDisplay/odd_abstract@x2.png' const IMAGE_ALT = 'Get started setting up a robot' From 18e312ac248ce8a04b3b451f0e1d49e5601c39dd Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 7 Apr 2023 18:40:24 -0400 Subject: [PATCH 03/28] Update EmptyRecentRun.tsx --- .../OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx index 7c8befc7707..86cdd94b466 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx @@ -8,7 +8,6 @@ import { COLORS, SPACING, TYPOGRAPHY, - JUSTIFY_CENTER, } from '@opentrons/components' import { StyledText } from '../../../atoms/text' @@ -24,9 +23,9 @@ export function EmptyRecentRun(): JSX.Element { backgroundColor={`${COLORS.darkBlackEnabled}${COLORS.opacity15HexCode}`} flexDirection={DIRECTION_COLUMN} alignItems={ALIGN_CENTER} - gridGap={SPACING.spacing4} + padding="5.25rem 3.75rem" > - + Robot Dashboard no protocol run data Date: Wed, 12 Apr 2023 14:44:18 -0400 Subject: [PATCH 04/28] change alt --- .../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx | 2 +- app/src/pages/OnDeviceDisplay/RobotDashboard.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx index 86cdd94b466..6eefebfc22d 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun.tsx @@ -28,7 +28,7 @@ export function EmptyRecentRun(): JSX.Element { Robot Dashboard no protocol run data diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index cdd0e7b1285..e8bfdae8ccd 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -55,7 +55,7 @@ export function RobotDashboard(): JSX.Element { > - {sortProtocols.length !== 0 ? ( + {sortProtocols.length === 0 ? ( <> From f4ac08e56cf1ec360ee1695153d5cfc6c77a0d14 Mon Sep 17 00:00:00 2001 From: Jethary Date: Wed, 12 Apr 2023 16:00:33 -0400 Subject: [PATCH 05/28] create useMissingProtocolHardware --- .../localization/en/device_details.json | 3 +- .../pages/OnDeviceDisplay/RobotDashboard.tsx | 50 ++++++++++++------- app/src/pages/Protocols/hooks/index.ts | 9 ++++ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index f6122d3bfbf..5cd6e974015 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -124,5 +124,6 @@ "ready": "Ready", "run_again": "Run again", "no_recent_runs": "No recent runs", - "no_recent_runs_description": "After you run some protocols, they will appear here." + "no_recent_runs_description": "After you run some protocols, they will appear here.", + "last_run_time": "last run {{number}} ago" } diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index cdd0e7b1285..0bc3ab50fc3 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -1,5 +1,6 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' +import { useParams } from 'react-router-dom' import { Flex, @@ -21,31 +22,34 @@ import { Navigation } from '../../organisms/OnDeviceDisplay/Navigation' import { onDeviceDisplayRoutes } from '../../App/OnDeviceDisplayApp' import { sortProtocols } from './ProtocolDashboard/utils' import { EmptyRecentRun } from '../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun' +import { useMissingProtocolHardware } from '../Protocols/hooks' + +import type { OnDeviceRouteParams } from '../../App/types' export const MAXIMUM_RECENT_RUN_PROTOCOLS = 8 // This might be changed const SORT_KEY = 'recentRun' export function RobotDashboard(): JSX.Element { const { t } = useTranslation('device_details') - const protocols = useAllProtocolsQuery() + const { protocolId } = useParams() + const missingProtocolHardware = useMissingProtocolHardware(protocolId) const runs = useAllRunsQuery() const protocolsData = protocols.data?.data != null ? protocols.data?.data : [] const runData = runs.data?.data != null ? runs.data?.data : [] - + const lastRun = runs.data?.data.find(run => run.protocolId === protocolId) + ?.createdAt /** Currently the max number of displaying recent run protocol is 8 */ const sortedProtocols = sortProtocols(SORT_KEY, protocolsData, runData).slice( 0, MAXIMUM_RECENT_RUN_PROTOCOLS ) - + const missingProtocolHardwareType = missingProtocolHardware.map(hardware => { + hardware.hardwareType + }) + console.log(missingProtocolHardwareType) console.table(sortedProtocols) - // lastRun - // const lastRun = runs.data?.data.find( - // run => run.protocolId === protocol.id - // )?.createdAt - return ( - - + {sortedProtocols.map((protocol, id) => { + ; + + + })} )} @@ -79,14 +90,15 @@ export function RobotDashboard(): JSX.Element { ) } -// interface RecentRunCardProps { -// moduleStatus: string // also need to pass the module status type -// protocolName: string // need to change -// lastRun: string -// } +interface RecentRunCardProps { + missingHardwareTypes: 'pipette' | 'module'[] + protocolName: string // need to change + lastRun: string +} -function RecentRunCard(): JSX.Element { - // function LastRunCard({ moduleStatus, protocolName, lastRun }): JSX.Element { +function RecentRunCard(props: RecentRunCardProps): JSX.Element { + const { t, i18n } = useTranslation('device_details') + const { protocolName, lastRun } = props return ( - {'Covid-19 qPCR Prep (Station C)'} + {protocolName} - {'Last run 1 min ago'} + {i18n.format(t('last_run_time', { number: lastRun }), 'capitalize')} ) diff --git a/app/src/pages/Protocols/hooks/index.ts b/app/src/pages/Protocols/hooks/index.ts index bf0f18bd081..ca15693930d 100644 --- a/app/src/pages/Protocols/hooks/index.ts +++ b/app/src/pages/Protocols/hooks/index.ts @@ -85,3 +85,12 @@ export const useRequiredProtocolLabware = ( const { onDeckItems, offDeckItems } = getLabwareSetupItemGroups(commands) return [...onDeckItems, ...offDeckItems] } + +export const useMissingProtocolHardware = ( + protocolId: string +): ProtocolHardware[] => { + const requiredProtocolHardware = useRequiredProtocolHardware(protocolId) + return requiredProtocolHardware.filter(hardware => { + hardware.connected === true + }) +} From 2220a2730ef69fab07d78bc780c9656969460125 Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 13 Apr 2023 09:15:10 -0400 Subject: [PATCH 06/28] feat(odd): robot dashboard recently run protocols closes RCORE-559 RCORE-586 --- .../localization/en/device_details.json | 6 +- .../__tests__/EmptyRecentRun.test.tsx | 26 +++ .../pages/OnDeviceDisplay/RobotDashboard.tsx | 113 ++++++++---- .../__tests__/RobotDashboard.test.tsx | 168 +++++++++++++++++- .../Protocols/hooks/__tests__/hooks.test.tsx | 87 +++++++++ app/src/pages/Protocols/hooks/index.ts | 6 +- 6 files changed, 369 insertions(+), 37 deletions(-) create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx create mode 100644 app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index 5cd6e974015..47c3a7fb74b 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -125,5 +125,9 @@ "run_again": "Run again", "no_recent_runs": "No recent runs", "no_recent_runs_description": "After you run some protocols, they will appear here.", - "last_run_time": "last run {{number}} ago" + "last_run_time": "last run {{number}}", + "ready_to_run": "ready to run", + "missing_modules": "Missing {{num}} modules", + "missing_pipettes": "Missing {{num}} pipettes", + "missing_both": "Missing {{numMod}} modules and {{numPip}} pipettes" } diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx new file mode 100644 index 00000000000..833890de549 --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx @@ -0,0 +1,26 @@ +import * as React from 'react' +import { MemoryRouter } from 'react-router-dom' + +import { renderWithProviders } from '@opentrons/components' +import { i18n } from '../../../../i18n' +import { EmptyRecentRun } from '../EmptyRecentRun' + +const render = () => { + return renderWithProviders( + + + , + { + i18nInstance: i18n, + } + ) +} + +describe('EmptyRecentRun', () => { + it('should render image and text', () => { + const [{ getByText, getByAltText }] = render() + getByAltText('RobotDashboard no recent run protocols') + getByText('No recent runs') + getByText('After you run some protocols, they will appear here.') + }) +}) diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index e153fd8cf34..ca25edb0703 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' -import { useParams } from 'react-router-dom' +import { formatDistance } from 'date-fns' import { Flex, @@ -15,16 +15,15 @@ import { useAllProtocolsQuery, useAllRunsQuery, } from '@opentrons/react-api-client' +import { css } from 'styled-components' import { StyledText } from '../../atoms/text' import { Chip } from '../../atoms/Chip' import { Navigation } from '../../organisms/OnDeviceDisplay/Navigation' import { onDeviceDisplayRoutes } from '../../App/OnDeviceDisplayApp' -import { sortProtocols } from './ProtocolDashboard/utils' import { EmptyRecentRun } from '../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun' import { useMissingProtocolHardware } from '../Protocols/hooks' - -import type { OnDeviceRouteParams } from '../../App/types' +import { sortProtocols } from './ProtocolDashboard/utils' export const MAXIMUM_RECENT_RUN_PROTOCOLS = 8 // This might be changed const SORT_KEY = 'recentRun' @@ -32,29 +31,20 @@ const SORT_KEY = 'recentRun' export function RobotDashboard(): JSX.Element { const { t } = useTranslation('device_details') const protocols = useAllProtocolsQuery() - const { protocolId } = useParams() - const missingProtocolHardware = useMissingProtocolHardware(protocolId) const runs = useAllRunsQuery() const protocolsData = protocols.data?.data != null ? protocols.data?.data : [] const runData = runs.data?.data != null ? runs.data?.data : [] - const lastRun = runs.data?.data.find(run => run.protocolId === protocolId) - ?.createdAt + /** Currently the max number of displaying recent run protocol is 8 */ const sortedProtocols = sortProtocols(SORT_KEY, protocolsData, runData).slice( 0, MAXIMUM_RECENT_RUN_PROTOCOLS ) - const missingProtocolHardwareType = missingProtocolHardware.map(hardware => { - hardware.hardwareType - }) - console.log(missingProtocolHardwareType) - console.table(sortedProtocols) - return ( - {sortProtocols.length === 0 ? ( + {sortedProtocols.length === 0 ? ( <> @@ -68,14 +58,22 @@ export function RobotDashboard(): JSX.Element { {t('run_again')} - {sortedProtocols.map((protocol, id) => { - ; - - + {sortedProtocols.map(protocol => { + const protocolId = protocol.id + const lastRun = runs.data?.data.find( + run => run.protocolId === protocolId + )?.createdAt + return ( + + + + ) })} @@ -86,26 +84,76 @@ export function RobotDashboard(): JSX.Element { } interface RecentRunCardProps { - missingHardwareTypes: 'pipette' | 'module'[] - protocolName: string // need to change - lastRun: string + protocolName: string + protocolId: string + lastRun?: string } function RecentRunCard(props: RecentRunCardProps): JSX.Element { const { t, i18n } = useTranslation('device_details') - const { protocolName, lastRun } = props + const { protocolName, protocolId, lastRun } = props + const missingProtocolHardware = useMissingProtocolHardware(protocolId) + const isSuccess = missingProtocolHardware.length === 0 + + const CARD_STYLE = css` + &:active { + background-color: ${isSuccess + ? COLORS.green_three_pressed + : COLORS.yellow_three_pressed}; + } + &:focus-visible { + box-shadow: 0 0 0 ${SPACING.spacing1} ${COLORS.fundamentalsFocus}; + } + ` + + const missingProtocolHardwareType = missingProtocolHardware.map( + hardware => hardware.hardwareType + ) + const missingProtocolPipetteType = missingProtocolHardwareType.filter( + type => type === 'pipette' + ) + const missingProtocolModuleType = missingProtocolHardwareType.filter( + type => type === 'module' + ) + + let chipText: string = t('ready_to_run') + if ( + missingProtocolPipetteType.length === 0 && + missingProtocolModuleType.length > 0 + ) { + chipText = t('missing_modules', { num: missingProtocolModuleType.length }) + } else if ( + missingProtocolPipetteType.length > 0 && + missingProtocolModuleType.length === 0 + ) { + chipText = t('missing_pipettes', { num: missingProtocolPipetteType.length }) + } else if ( + missingProtocolPipetteType.length > 0 && + missingProtocolModuleType.length > 0 + ) { + chipText = t('missing_both', { + numMod: missingProtocolModuleType.length, + numPip: missingProtocolPipetteType.length, + }) + } return ( {/* marginLeft is needed to cancel chip's padding */} - + - {i18n.format(t('last_run_time', { number: lastRun }), 'capitalize')} + {i18n.format(t('last_run_time'), 'capitalize')}{' '} + {lastRun != null + ? formatDistance(new Date(lastRun), new Date(), { + addSuffix: true, + }).replace('about ', '') + : ''} ) diff --git a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx index 52676af23c7..4511e928ec7 100644 --- a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx +++ b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx @@ -1,15 +1,38 @@ import * as React from 'react' import { MemoryRouter } from 'react-router-dom' -import { renderWithProviders } from '@opentrons/components' +import { COLORS, renderWithProviders } from '@opentrons/components' +import { + useAllProtocolsQuery, + useAllRunsQuery, +} from '@opentrons/react-api-client' import { i18n } from '../../../i18n' +import { EmptyRecentRun } from '../../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun' import { Navigation } from '../../../organisms/OnDeviceDisplay/Navigation' +import { useMissingProtocolHardware } from '../../Protocols/hooks' import { RobotDashboard } from '../RobotDashboard' +import type { ProtocolResource } from '@opentrons/shared-data' + +jest.mock('@opentrons/react-api-client') +jest.mock('../../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun') jest.mock('../../../organisms/OnDeviceDisplay/Navigation') +jest.mock('../../Protocols/hooks') const mockNavigation = Navigation as jest.MockedFunction +const mockUseAllProtocolsQuery = useAllProtocolsQuery as jest.MockedFunction< + typeof useAllProtocolsQuery +> +const mockUseAllRunsQuery = useAllRunsQuery as jest.MockedFunction< + typeof useAllRunsQuery +> +const mockEmptyRecentRun = EmptyRecentRun as jest.MockedFunction< + typeof EmptyRecentRun +> +const mockUseMissingProtocolHardware = useMissingProtocolHardware as jest.MockedFunction< + typeof useMissingProtocolHardware +> const render = () => { return renderWithProviders( @@ -21,21 +44,160 @@ const render = () => { } ) } +const mockProtocol: ProtocolResource = { + id: 'mockProtocol1', + createdAt: '2022-05-03T21:36:12.494778+00:00', + protocolType: 'json', + metadata: { + protocolName: 'yay mock protocol', + author: 'engineering', + description: 'A short mock protocol', + created: 1606853851893, + tags: ['unitTest'], + }, + analysisSummaries: [], + files: [], + key: '26ed5a82-502f-4074-8981-57cdda1d066d', +} +const mockRunData = { + id: 'mockProtocol1', + createdAt: '2022-05-03T21:36:12.494778+00:00', + completedAt: 'thistime', + startedAt: 'thistime', + protocolId: 'mockProtocol1', +} as any describe('RobotDashboard', () => { beforeEach(() => { + mockEmptyRecentRun.mockReturnValue(
mock EmptyRecentRun
) mockNavigation.mockReturnValue(
mock Navigation
) + mockUseAllProtocolsQuery.mockReturnValue({} as any) + mockUseAllRunsQuery.mockReturnValue({} as any) + mockUseMissingProtocolHardware.mockReturnValue([]) }) afterEach(() => { jest.resetAllMocks() }) - it('should render text, image, and buttons', () => { + it('should render empty recent run image and buttons', () => { const [{ getByText }] = render() getByText('mock Navigation') + getByText('mock EmptyRecentRun') + }) + it('should render a recent protocol with all the required info to run', () => { + mockUseAllProtocolsQuery.mockReturnValue({ + data: { + data: [mockProtocol], + }, + } as any) + mockUseAllRunsQuery.mockReturnValue({ + data: { data: [mockRunData] }, + } as any) + const [{ getByText, getByLabelText }] = render() + getByText('mock Navigation') + getByText('Ready to run') + getByText('Run again') + getByText('yay mock protocol') + getByLabelText('icon_Ready to run') + expect(getByLabelText('RecentRunCard')).toHaveStyle( + `background-color: ${COLORS.green_three}` + ) + }) + it('should render a recent protocol with required modules', () => { + mockUseAllProtocolsQuery.mockReturnValue({ + data: { + data: [mockProtocol], + }, + } as any) + mockUseAllRunsQuery.mockReturnValue({ + data: { data: [mockRunData] }, + } as any) + mockUseMissingProtocolHardware.mockReturnValue([ + { + hardwareType: 'module', + slot: '1', + connected: false, + moduleModel: 'heaterShakerModuleV1', + }, + { + hardwareType: 'module', + slot: '3', + connected: false, + moduleModel: 'magneticModuleV1', + }, + ]) + + const [{ getByText, getByLabelText }] = render() + getByText('mock Navigation') getByText('Run again') + getByText('yay mock protocol') + getByText('Missing 2 modules') + getByLabelText('icon_Missing 2 modules') + expect(getByLabelText('RecentRunCard')).toHaveStyle( + `background-color: ${COLORS.yellow_three}` + ) }) + it('should render a recent protocol with required pipette', () => { + mockUseAllProtocolsQuery.mockReturnValue({ + data: { + data: [mockProtocol], + }, + } as any) + mockUseAllRunsQuery.mockReturnValue({ + data: { data: [mockRunData] }, + } as any) + mockUseMissingProtocolHardware.mockReturnValue([ + { + hardwareType: 'pipette', + connected: false, + mount: 'left', + pipetteName: 'p1000_multi_gen3', + }, + ]) - // Note test cases will be added when RobotDashboard screen is ready + const [{ getByText, getByLabelText }] = render() + getByText('mock Navigation') + getByText('Run again') + getByText('yay mock protocol') + getByText('Missing 1 pipettes') + getByLabelText('icon_Missing 1 pipettes') + expect(getByLabelText('RecentRunCard')).toHaveStyle( + `background-color: ${COLORS.yellow_three}` + ) + }) + it('should render a recent protocol with required pipette and modules', () => { + mockUseAllProtocolsQuery.mockReturnValue({ + data: { + data: [mockProtocol], + }, + } as any) + mockUseAllRunsQuery.mockReturnValue({ + data: { data: [mockRunData] }, + } as any) + mockUseMissingProtocolHardware.mockReturnValue([ + { + hardwareType: 'pipette', + connected: false, + mount: 'left', + pipetteName: 'p1000_multi_gen3', + }, + { + hardwareType: 'module', + slot: '3', + connected: false, + moduleModel: 'magneticModuleV1', + }, + ]) + + const [{ getByText, getByLabelText }] = render() + getByText('mock Navigation') + getByText('Run again') + getByText('yay mock protocol') + getByText('Missing 1 modules and 1 pipettes') + getByLabelText('icon_Missing 1 modules and 1 pipettes') + expect(getByLabelText('RecentRunCard')).toHaveStyle( + `background-color: ${COLORS.yellow_three}` + ) + }) }) diff --git a/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx b/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx new file mode 100644 index 00000000000..557bf14b4b7 --- /dev/null +++ b/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx @@ -0,0 +1,87 @@ +import * as React from 'react' +import { UseQueryResult } from 'react-query' +import { renderHook } from '@testing-library/react-hooks' +import { useProtocolAnalysesQuery } from '@opentrons/react-api-client' +import { + useAttachedModules, + useAttachedPipettes, +} from '../../../../organisms/Devices/hooks' +import { mockHeaterShaker } from '../../../../redux/modules/__fixtures__' +import { useMissingProtocolHardware } from '..' +import type { ProtocolAnalyses } from '@opentrons/api-client' + +jest.mock('@opentrons/react-api-client') +jest.mock('../../../../organisms/Devices/hooks') + +const mockUseProtocolAnalysesQuery = useProtocolAnalysesQuery as jest.MockedFunction< + typeof useProtocolAnalysesQuery +> +const mockUseAttachedModules = useAttachedModules as jest.MockedFunction< + typeof useAttachedModules +> +const mockUseAttachedPipettes = useAttachedPipettes as jest.MockedFunction< + typeof useAttachedPipettes +> +const PROTOCOL_ANALYSIS = { + id: 'fake analysis', + status: 'completed', + labware: [], + pipettes: [{ id: 'pipId', pipetteName: 'p1000_multi_gen3', mount: 'left' }], + modules: [ + { + id: 'modId', + model: 'heaterShakerModuleV1', + location: { slotName: '1' }, + serialNumber: 'serialNum', + }, + ], +} as any + +describe('useMissingProtocolHardware', () => { + let wrapper: React.FunctionComponent<{}> + beforeEach(() => { + mockUseAttachedPipettes.mockReturnValue({ left: {}, right: {} } as any) + mockUseAttachedModules.mockReturnValue([]) + mockUseProtocolAnalysesQuery.mockReturnValue({ + data: { data: [PROTOCOL_ANALYSIS as any] }, + } as UseQueryResult) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + it('should return 1 pipette and 1 module', () => { + const { result } = renderHook( + () => useMissingProtocolHardware(PROTOCOL_ANALYSIS.id), + { wrapper } + ) + expect(result.current).toEqual([ + { + hardwareType: 'pipette', + pipetteName: 'p1000_multi_gen3', + mount: 'left', + connected: false, + }, + { + hardwareType: 'module', + moduleModel: 'heaterShakerModuleV1', + slot: '1', + connected: false, + }, + ]) + }) + it('should return empty array when the correct modules and pipettes are attached', () => { + mockUseAttachedPipettes.mockReturnValue({ + left: { + name: 'p1000_multi_gen3', + }, + right: {}, + } as any) + mockUseAttachedModules.mockReturnValue([mockHeaterShaker]) + const { result } = renderHook( + () => useMissingProtocolHardware(PROTOCOL_ANALYSIS.id), + { wrapper } + ) + expect(result.current).toEqual([]) + }) +}) diff --git a/app/src/pages/Protocols/hooks/index.ts b/app/src/pages/Protocols/hooks/index.ts index ca15693930d..b8b44a00dfe 100644 --- a/app/src/pages/Protocols/hooks/index.ts +++ b/app/src/pages/Protocols/hooks/index.ts @@ -90,7 +90,7 @@ export const useMissingProtocolHardware = ( protocolId: string ): ProtocolHardware[] => { const requiredProtocolHardware = useRequiredProtocolHardware(protocolId) - return requiredProtocolHardware.filter(hardware => { - hardware.connected === true - }) + return requiredProtocolHardware.filter( + hardware => hardware.connected === false + ) } From 8fd78a648a67823c64675d8a53ec55c2a63cab2f Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 13 Apr 2023 10:03:06 -0400 Subject: [PATCH 07/28] add plurals to i18n --- app/src/assets/localization/en/device_details.json | 8 +++++--- app/src/pages/OnDeviceDisplay/RobotDashboard.tsx | 10 ++++++++-- .../OnDeviceDisplay/__tests__/RobotDashboard.test.tsx | 8 ++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index 47c3a7fb74b..172f3d70b53 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -127,7 +127,9 @@ "no_recent_runs_description": "After you run some protocols, they will appear here.", "last_run_time": "last run {{number}}", "ready_to_run": "ready to run", - "missing_modules": "Missing {{num}} modules", - "missing_pipettes": "Missing {{num}} pipettes", - "missing_both": "Missing {{numMod}} modules and {{numPip}} pipettes" + "missing_module": "Missing {{num}} module", + "missing_module_plural": "Missing {{num}} modules", + "missing_pipette": "Missing {{num}} pipette", + "missing_pipettes_plural": "Missing {{num}} pipettes", + "missing_both": "Missing {{numMod}} module(s) and {{numPip}} pipette(s)" } diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index ca25edb0703..2f80839e92c 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -121,12 +121,18 @@ function RecentRunCard(props: RecentRunCardProps): JSX.Element { missingProtocolPipetteType.length === 0 && missingProtocolModuleType.length > 0 ) { - chipText = t('missing_modules', { num: missingProtocolModuleType.length }) + chipText = t('missing_module', { + num: missingProtocolModuleType.length, + count: missingProtocolModuleType.length, + }) } else if ( missingProtocolPipetteType.length > 0 && missingProtocolModuleType.length === 0 ) { - chipText = t('missing_pipettes', { num: missingProtocolPipetteType.length }) + chipText = t('missing_pipette', { + num: missingProtocolPipetteType.length, + count: missingProtocolPipetteType.length, + }) } else if ( missingProtocolPipetteType.length > 0 && missingProtocolModuleType.length > 0 diff --git a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx index 4511e928ec7..3e541a1c228 100644 --- a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx +++ b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx @@ -160,8 +160,8 @@ describe('RobotDashboard', () => { getByText('mock Navigation') getByText('Run again') getByText('yay mock protocol') - getByText('Missing 1 pipettes') - getByLabelText('icon_Missing 1 pipettes') + getByText('Missing 1 pipette') + getByLabelText('icon_Missing 1 pipette') expect(getByLabelText('RecentRunCard')).toHaveStyle( `background-color: ${COLORS.yellow_three}` ) @@ -194,8 +194,8 @@ describe('RobotDashboard', () => { getByText('mock Navigation') getByText('Run again') getByText('yay mock protocol') - getByText('Missing 1 modules and 1 pipettes') - getByLabelText('icon_Missing 1 modules and 1 pipettes') + getByText('Missing 1 module(s) and 1 pipette(s)') + getByLabelText('icon_Missing 1 module(s) and 1 pipette(s)') expect(getByLabelText('RecentRunCard')).toHaveStyle( `background-color: ${COLORS.yellow_three}` ) From 19c9c728e6888adfbbf88513821bf4d58c8f39db Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 13 Apr 2023 10:06:08 -0400 Subject: [PATCH 08/28] remove capitalization since we are using the i18n format function --- app/src/assets/localization/en/device_details.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index 172f3d70b53..33d59008c03 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -127,9 +127,9 @@ "no_recent_runs_description": "After you run some protocols, they will appear here.", "last_run_time": "last run {{number}}", "ready_to_run": "ready to run", - "missing_module": "Missing {{num}} module", - "missing_module_plural": "Missing {{num}} modules", - "missing_pipette": "Missing {{num}} pipette", - "missing_pipettes_plural": "Missing {{num}} pipettes", - "missing_both": "Missing {{numMod}} module(s) and {{numPip}} pipette(s)" + "missing_module": "missing {{num}} module", + "missing_module_plural": "missing {{num}} modules", + "missing_pipette": "missing {{num}} pipette", + "missing_pipettes_plural": "missing {{num}} pipettes", + "missing_both": "missing {{numMod}} module(s) and {{numPip}} pipette(s)" } From 31dc1c1e69484a4564476a2bb28c0b483581085c Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 10:19:56 -0400 Subject: [PATCH 09/28] update test case --- .../__tests__/EmptyRecentRun.test.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx index 833890de549..031e3e8aec0 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx @@ -1,26 +1,24 @@ import * as React from 'react' -import { MemoryRouter } from 'react-router-dom' import { renderWithProviders } from '@opentrons/components' import { i18n } from '../../../../i18n' import { EmptyRecentRun } from '../EmptyRecentRun' +const PNG_FILE_NAME = 'Illustration@x2.png' + const render = () => { - return renderWithProviders( - - - , - { - i18nInstance: i18n, - } - ) + return renderWithProviders(, { + i18nInstance: i18n, + }) } describe('EmptyRecentRun', () => { it('should render image and text', () => { - const [{ getByText, getByAltText }] = render() + const [{ getByText, getByAltText, getByRole }] = render() getByAltText('RobotDashboard no recent run protocols') getByText('No recent runs') getByText('After you run some protocols, they will appear here.') + const image = getByRole('img') + expect(image.getAttribute('src')).toEqual(PNG_FILE_NAME) }) }) From 5cb1e0d2388b1ba8d8df9e9b668d20b01cb5cf39 Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 13 Apr 2023 10:21:48 -0400 Subject: [PATCH 10/28] add onClick functionality to recentRunCard --- .../pages/OnDeviceDisplay/RobotDashboard.tsx | 7 +++++++ .../__tests__/RobotDashboard.test.tsx | 20 ++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index 2f80839e92c..50f4b4bfc89 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -1,5 +1,6 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' +import { useHistory } from 'react-router-dom' import { formatDistance } from 'date-fns' import { @@ -93,6 +94,7 @@ function RecentRunCard(props: RecentRunCardProps): JSX.Element { const { t, i18n } = useTranslation('device_details') const { protocolName, protocolId, lastRun } = props const missingProtocolHardware = useMissingProtocolHardware(protocolId) + const history = useHistory() const isSuccess = missingProtocolHardware.length === 0 const CARD_STYLE = css` @@ -142,6 +144,10 @@ function RecentRunCard(props: RecentRunCardProps): JSX.Element { numPip: missingProtocolPipetteType.length, }) } + const handleCardClick = (): void => { + history.push(`protocols/${protocolId}`) + } + return ( {/* marginLeft is needed to cancel chip's padding */} diff --git a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx index 3e541a1c228..cb83924fa60 100644 --- a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx +++ b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx @@ -1,4 +1,5 @@ import * as React from 'react' +import { fireEvent } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { COLORS, renderWithProviders } from '@opentrons/components' @@ -15,6 +16,15 @@ import { RobotDashboard } from '../RobotDashboard' import type { ProtocolResource } from '@opentrons/shared-data' +const mockPush = jest.fn() + +jest.mock('react-router-dom', () => { + const reactRouterDom = jest.requireActual('react-router-dom') + return { + ...reactRouterDom, + useHistory: () => ({ push: mockPush } as any), + } +}) jest.mock('@opentrons/react-api-client') jest.mock('../../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun') jest.mock('../../../organisms/OnDeviceDisplay/Navigation') @@ -33,7 +43,6 @@ const mockEmptyRecentRun = EmptyRecentRun as jest.MockedFunction< const mockUseMissingProtocolHardware = useMissingProtocolHardware as jest.MockedFunction< typeof useMissingProtocolHardware > - const render = () => { return renderWithProviders( @@ -85,7 +94,7 @@ describe('RobotDashboard', () => { getByText('mock Navigation') getByText('mock EmptyRecentRun') }) - it('should render a recent protocol with all the required info to run', () => { + it('should render a recent protocol with all the required info to run, clicking card goes to run history', () => { mockUseAllProtocolsQuery.mockReturnValue({ data: { data: [mockProtocol], @@ -100,9 +109,10 @@ describe('RobotDashboard', () => { getByText('Run again') getByText('yay mock protocol') getByLabelText('icon_Ready to run') - expect(getByLabelText('RecentRunCard')).toHaveStyle( - `background-color: ${COLORS.green_three}` - ) + const recentRunCard = getByLabelText('RecentRunCard') + expect(recentRunCard).toHaveStyle(`background-color: ${COLORS.green_three}`) + fireEvent.click(recentRunCard) + expect(mockPush).toHaveBeenCalledWith('protocols/mockProtocol1') }) it('should render a recent protocol with required modules', () => { mockUseAllProtocolsQuery.mockReturnValue({ From f45889bc75343e2dcb9fa102885cd1161f299603 Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 14:28:24 -0400 Subject: [PATCH 11/28] move, rename and add test for component --- .../RobotDashboard/RecentProtocolRunCard.tsx | 146 +++++++++++ .../__tests__/RecentProtocolRunCard.test.tsx | 111 +++++++++ .../OnDeviceDisplay/RobotDashboard/index.ts | 2 + .../pages/OnDeviceDisplay/RobotDashboard.tsx | 230 +++++++++--------- app/src/pages/Protocols/hooks/index.ts | 4 +- 5 files changed, 377 insertions(+), 116 deletions(-) create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx new file mode 100644 index 00000000000..340ab06cb00 --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -0,0 +1,146 @@ +import * as React from 'react' +import { css } from 'styled-components' +import { useTranslation } from 'react-i18next' +import { useHistory } from 'react-router-dom' +import { formatDistance } from 'date-fns' + +import { + Flex, + COLORS, + SPACING, + TYPOGRAPHY, + DIRECTION_COLUMN, + BORDERS, +} from '@opentrons/components' + +import { StyledText } from '../../../atoms/text' +import { Chip } from '../../../atoms/Chip' +import { useMissingProtocolHardware } from '../../../pages/Protocols/hooks' + +interface RecentProtocolRunCardProps { + /** protocol name that was run recently */ + protocolName: string + /** protocol id */ + protocolId: string + /** last run */ + lastRun?: string +} + +export function RecentProtocolRunCard({ + protocolName, + protocolId, + lastRun, +}: RecentProtocolRunCardProps): JSX.Element { + const { t, i18n } = useTranslation('device_details') + const missingProtocolHardware = useMissingProtocolHardware(protocolId) + const history = useHistory() + const isSuccess = missingProtocolHardware.length === 0 + + console.log(protocolId) + console.log(missingProtocolHardware) + console.log(missingProtocolHardware.length) + + const CARD_STYLE = css` + &:active { + background-color: ${isSuccess + ? COLORS.green_three_pressed + : COLORS.yellow_three_pressed}; + } + &:focus-visible { + box-shadow: 0 0 0 ${SPACING.spacing1} ${COLORS.fundamentalsFocus}; + } + ` + + const PROTOCOL_TEXT_STYLE = css` + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 5; + overflow: hidden; + ` + + const missingProtocolHardwareType = missingProtocolHardware.map( + hardware => hardware.hardwareType + ) + const missingProtocolPipetteType = missingProtocolHardwareType.filter( + type => type === 'pipette' + ) + const missingProtocolModuleType = missingProtocolHardwareType.filter( + type => type === 'module' + ) + + let chipText: string = t('ready_to_run') + if ( + missingProtocolPipetteType.length === 0 && + missingProtocolModuleType.length > 0 + ) { + chipText = t('missing_module', { + num: missingProtocolModuleType.length, + count: missingProtocolModuleType.length, + }) + } else if ( + missingProtocolPipetteType.length > 0 && + missingProtocolModuleType.length === 0 + ) { + chipText = t('missing_pipette', { + num: missingProtocolPipetteType.length, + count: missingProtocolPipetteType.length, + }) + } else if ( + missingProtocolPipetteType.length > 0 && + missingProtocolModuleType.length > 0 + ) { + chipText = t('missing_both', { + numMod: missingProtocolModuleType.length, + numPip: missingProtocolPipetteType.length, + }) + } + const handleCardClick = (): void => { + history.push(`protocols/${protocolId}`) + } + + return ( + + {/* marginLeft is needed to cancel chip's padding */} + + + + + + {protocolName} + + + + {i18n.format(t('last_run_time'), 'capitalize')}{' '} + {lastRun != null + ? formatDistance(new Date(lastRun), new Date(), { + addSuffix: true, + }).replace('about ', '') + : ''} + + + ) +} diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx new file mode 100644 index 00000000000..fd1d8dba850 --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx @@ -0,0 +1,111 @@ +import * as React from 'react' +import { when, resetAllWhenMocks } from 'jest-when' +import { fireEvent } from '@testing-library/react' +import { formatDistance } from 'date-fns' + +import { renderWithProviders } from '@opentrons/components' + +import { i18n } from '../../../../i18n' +import { useMissingProtocolHardware } from '../../../../pages/Protocols/hooks' +import { RecentProtocolRunCard } from '../' + +import type { ProtocolHardware } from '../../../../pages/Protocols/hooks' + +const mockProtocolName = 'mockProtocol' +const mockProtocolId = 'mockProtocolId' +const mockLastRun = '2023-04-12T21:30:49.124108+00:00' + +const mockPipette = [ + { + hardwareType: 'pipette', + pipetteName: 'p1000_single_gen3', + mount: 'left', + connected: true, + }, +] as ProtocolHardware[] +// missing hardware pipette | module +const mockMissingPipette = [ + { + hardwareType: 'pipette', + pipetteName: 'p1000_single_gen3', + mount: 'left', + connected: false, + }, +] as ProtocolHardware[] + +const mockMissingModule = [ + { + hardwareType: 'module', + moduleModel: 'temperatureModuleV2', + slot: '1', + connected: false, + }, +] as ProtocolHardware[] + +const mockPush = jest.fn() +jest.mock('react-router-dom', () => { + const reactRouterDom = jest.requireActual('react-router-dom') + return { + ...reactRouterDom, + useHistory: () => ({ push: mockPush } as any), + } +}) + +const mockUseMissingProtocolHardware = useMissingProtocolHardware as jest.MockedFunction< + typeof useMissingProtocolHardware +> + +const render = (props: React.ComponentProps) => { + return renderWithProviders(, { + i18nInstance: i18n, + }) +} + +describe('RecentProtocolRunCard', () => { + let props: React.ComponentProps + + beforeEach(() => { + props = { + protocolName: mockProtocolName, + protocolId: mockProtocolId, + lastRun: mockLastRun, + } + when(mockUseMissingProtocolHardware) + .calledWith(mockProtocolId) + .mockReturnValue(mockPipette) + }) + + afterEach(() => { + jest.clearAllMocks() + resetAllWhenMocks() + }) + + it('should render text', () => { + const [{ getByText }] = render(props) + const lastRunTime = formatDistance(new Date(mockLastRun), new Date(), { + addSuffix: true, + }).replace('about ', '') + getByText('Ready to run') + getByText('mockProtocol') + getByText(`Last run ${lastRunTime}`) + }) + + // it('should render missing chip when missing a pipette', () => { + // mockUseMissingProtocolHardware.mockReturnValue(mockMissingPipette) + // const [{ getByText }] = render(props) + // getByText('Missing 1 pipette(s)') + // }) + + // it('should render missing chip when missing a module', () => { + // mockUseMissingProtocolHardware.mockReturnValue(mockMissingModule) + // const [{ getByText }] = render(props) + // getByText('Missing 1 module(s)') + // }) + + // it('when tapping a card, a mock function is called', () => { + // const [{ getByLabelText }] = render(props) + // const button = getByLabelText('RecentRunCard') + // fireEvent.click(button) + // expect(mockPush).toHaveBeenCalledWith(`protocols/${props.protocolId}`) + // }) +}) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts b/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts new file mode 100644 index 00000000000..09fc7870aaf --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts @@ -0,0 +1,2 @@ +export * from './EmptyRecentRun' +export * from './RecentProtocolRunCard' diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index 50f4b4bfc89..8e53d8911d7 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -1,29 +1,25 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' -import { useHistory } from 'react-router-dom' -import { formatDistance } from 'date-fns' import { Flex, DIRECTION_COLUMN, DIRECTION_ROW, SPACING, - COLORS, TYPOGRAPHY, - BORDERS, } from '@opentrons/components' import { useAllProtocolsQuery, useAllRunsQuery, } from '@opentrons/react-api-client' -import { css } from 'styled-components' import { StyledText } from '../../atoms/text' -import { Chip } from '../../atoms/Chip' import { Navigation } from '../../organisms/OnDeviceDisplay/Navigation' import { onDeviceDisplayRoutes } from '../../App/OnDeviceDisplayApp' -import { EmptyRecentRun } from '../../organisms/OnDeviceDisplay/RobotDashboard/EmptyRecentRun' -import { useMissingProtocolHardware } from '../Protocols/hooks' +import { + EmptyRecentRun, + RecentProtocolRunCard, +} from '../../organisms/OnDeviceDisplay/RobotDashboard' import { sortProtocols } from './ProtocolDashboard/utils' export const MAXIMUM_RECENT_RUN_PROTOCOLS = 8 // This might be changed @@ -66,7 +62,7 @@ export function RobotDashboard(): JSX.Element { )?.createdAt return ( - hardware.hardwareType - ) - const missingProtocolPipetteType = missingProtocolHardwareType.filter( - type => type === 'pipette' - ) - const missingProtocolModuleType = missingProtocolHardwareType.filter( - type => type === 'module' - ) +// const PROTOCOL_TEXT_STYLE = css` +// display: -webkit-box; +// -webkit-box-orient: vertical; +// -webkit-line-clamp: 5; +// overflow: hidden; +// ` - let chipText: string = t('ready_to_run') - if ( - missingProtocolPipetteType.length === 0 && - missingProtocolModuleType.length > 0 - ) { - chipText = t('missing_module', { - num: missingProtocolModuleType.length, - count: missingProtocolModuleType.length, - }) - } else if ( - missingProtocolPipetteType.length > 0 && - missingProtocolModuleType.length === 0 - ) { - chipText = t('missing_pipette', { - num: missingProtocolPipetteType.length, - count: missingProtocolPipetteType.length, - }) - } else if ( - missingProtocolPipetteType.length > 0 && - missingProtocolModuleType.length > 0 - ) { - chipText = t('missing_both', { - numMod: missingProtocolModuleType.length, - numPip: missingProtocolPipetteType.length, - }) - } - const handleCardClick = (): void => { - history.push(`protocols/${protocolId}`) - } +// const missingProtocolHardwareType = missingProtocolHardware.map( +// hardware => hardware.hardwareType +// ) +// const missingProtocolPipetteType = missingProtocolHardwareType.filter( +// type => type === 'pipette' +// ) +// const missingProtocolModuleType = missingProtocolHardwareType.filter( +// type => type === 'module' +// ) - return ( - - {/* marginLeft is needed to cancel chip's padding */} - - - - - - {protocolName} - - - - {i18n.format(t('last_run_time'), 'capitalize')}{' '} - {lastRun != null - ? formatDistance(new Date(lastRun), new Date(), { - addSuffix: true, - }).replace('about ', '') - : ''} - - - ) -} +// let chipText: string = t('ready_to_run') +// if ( +// missingProtocolPipetteType.length === 0 && +// missingProtocolModuleType.length > 0 +// ) { +// chipText = t('missing_module', { +// num: missingProtocolModuleType.length, +// count: missingProtocolModuleType.length, +// }) +// } else if ( +// missingProtocolPipetteType.length > 0 && +// missingProtocolModuleType.length === 0 +// ) { +// chipText = t('missing_pipette', { +// num: missingProtocolPipetteType.length, +// count: missingProtocolPipetteType.length, +// }) +// } else if ( +// missingProtocolPipetteType.length > 0 && +// missingProtocolModuleType.length > 0 +// ) { +// chipText = t('missing_both', { +// numMod: missingProtocolModuleType.length, +// numPip: missingProtocolPipetteType.length, +// }) +// } +// const handleCardClick = (): void => { +// history.push(`protocols/${protocolId}`) +// } + +// return ( +// +// {/* marginLeft is needed to cancel chip's padding */} +// +// +// +// +// +// {protocolName} +// +// +// +// {i18n.format(t('last_run_time'), 'capitalize')}{' '} +// {lastRun != null +// ? formatDistance(new Date(lastRun), new Date(), { +// addSuffix: true, +// }).replace('about ', '') +// : ''} +// +// +// ) +// } diff --git a/app/src/pages/Protocols/hooks/index.ts b/app/src/pages/Protocols/hooks/index.ts index b8b44a00dfe..465c488442e 100644 --- a/app/src/pages/Protocols/hooks/index.ts +++ b/app/src/pages/Protocols/hooks/index.ts @@ -90,7 +90,5 @@ export const useMissingProtocolHardware = ( protocolId: string ): ProtocolHardware[] => { const requiredProtocolHardware = useRequiredProtocolHardware(protocolId) - return requiredProtocolHardware.filter( - hardware => hardware.connected === false - ) + return requiredProtocolHardware.filter(hardware => !hardware.connected) } From 0e89a0d5ea1e94496fde737a49791b3dd4d656ab Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 15:29:43 -0400 Subject: [PATCH 12/28] update test --- .../RobotDashboard/RecentProtocolRunCard.tsx | 2 +- .../__tests__/RecentProtocolRunCard.test.tsx | 7 ++++--- app/src/pages/Protocols/hooks/index.ts | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index 340ab06cb00..4e4cbc523b7 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -37,7 +37,7 @@ export function RecentProtocolRunCard({ const isSuccess = missingProtocolHardware.length === 0 console.log(protocolId) - console.log(missingProtocolHardware) + console.log('missingProtocolHardware', missingProtocolHardware) console.log(missingProtocolHardware.length) const CARD_STYLE = css` diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx index fd1d8dba850..20507660efe 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx @@ -11,6 +11,9 @@ import { RecentProtocolRunCard } from '../' import type { ProtocolHardware } from '../../../../pages/Protocols/hooks' +jest.mock('../../../../pages/Protocols/hooks') +jest.mock('../../../../organisms/Devices/hooks') + const mockProtocolName = 'mockProtocol' const mockProtocolId = 'mockProtocolId' const mockLastRun = '2023-04-12T21:30:49.124108+00:00' @@ -70,9 +73,6 @@ describe('RecentProtocolRunCard', () => { protocolId: mockProtocolId, lastRun: mockLastRun, } - when(mockUseMissingProtocolHardware) - .calledWith(mockProtocolId) - .mockReturnValue(mockPipette) }) afterEach(() => { @@ -81,6 +81,7 @@ describe('RecentProtocolRunCard', () => { }) it('should render text', () => { + mockUseMissingProtocolHardware.mockReturnValue(mockPipette) const [{ getByText }] = render(props) const lastRunTime = formatDistance(new Date(mockLastRun), new Date(), { addSuffix: true, diff --git a/app/src/pages/Protocols/hooks/index.ts b/app/src/pages/Protocols/hooks/index.ts index 465c488442e..b8b44a00dfe 100644 --- a/app/src/pages/Protocols/hooks/index.ts +++ b/app/src/pages/Protocols/hooks/index.ts @@ -90,5 +90,7 @@ export const useMissingProtocolHardware = ( protocolId: string ): ProtocolHardware[] => { const requiredProtocolHardware = useRequiredProtocolHardware(protocolId) - return requiredProtocolHardware.filter(hardware => !hardware.connected) + return requiredProtocolHardware.filter( + hardware => hardware.connected === false + ) } From 02f3244b3b36418ef9f2cbc7a22b2642d4559d87 Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 15:32:34 -0400 Subject: [PATCH 13/28] Update RecentProtocolRunCard.test.tsx --- .../RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx index 20507660efe..10bd2e3a756 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx @@ -73,6 +73,7 @@ describe('RecentProtocolRunCard', () => { protocolId: mockProtocolId, lastRun: mockLastRun, } + mockUseMissingProtocolHardware.mockReturnValue(mockPipette) }) afterEach(() => { @@ -81,7 +82,6 @@ describe('RecentProtocolRunCard', () => { }) it('should render text', () => { - mockUseMissingProtocolHardware.mockReturnValue(mockPipette) const [{ getByText }] = render(props) const lastRunTime = formatDistance(new Date(mockLastRun), new Date(), { addSuffix: true, From e6df766be0eee2f87be44c7279be46d6c4a4e883 Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 15:56:15 -0400 Subject: [PATCH 14/28] Update RecentProtocolRunCard.tsx --- .../RobotDashboard/RecentProtocolRunCard.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index 4e4cbc523b7..c8ec2bf40b1 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -39,6 +39,7 @@ export function RecentProtocolRunCard({ console.log(protocolId) console.log('missingProtocolHardware', missingProtocolHardware) console.log(missingProtocolHardware.length) + console.log('isSuccess', isSuccess) const CARD_STYLE = css` &:active { @@ -58,9 +59,23 @@ export function RecentProtocolRunCard({ overflow: hidden; ` + // switch below + // const countHardwareType = (hwType: 'pipette' | 'module'): number => { + // return missingProtocolHardwareType.reduce((acc, hardwareType) => { + // if (hardwareType === hwType) { + // return acc + 1 + // } + // return acc + // }, 0) + // } + + // const missingPipettes = countHardwareType('pipette') + // const missing = countHardwareType('pipette') + const missingProtocolHardwareType = missingProtocolHardware.map( hardware => hardware.hardwareType ) + const missingProtocolPipetteType = missingProtocolHardwareType.filter( type => type === 'pipette' ) From af04dda141962dd2c1cae29020b65c92991e8ae5 Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 17:30:44 -0400 Subject: [PATCH 15/28] fix test cases --- .../RobotDashboard/RecentProtocolRunCard.tsx | 64 +++++++----------- .../__tests__/RecentProtocolRunCard.test.tsx | 66 +++++++++++-------- 2 files changed, 60 insertions(+), 70 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index c8ec2bf40b1..ffa14d7fa1e 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -36,11 +36,6 @@ export function RecentProtocolRunCard({ const history = useHistory() const isSuccess = missingProtocolHardware.length === 0 - console.log(protocolId) - console.log('missingProtocolHardware', missingProtocolHardware) - console.log(missingProtocolHardware.length) - console.log('isSuccess', isSuccess) - const CARD_STYLE = css` &:active { background-color: ${isSuccess @@ -59,54 +54,39 @@ export function RecentProtocolRunCard({ overflow: hidden; ` - // switch below - // const countHardwareType = (hwType: 'pipette' | 'module'): number => { - // return missingProtocolHardwareType.reduce((acc, hardwareType) => { - // if (hardwareType === hwType) { - // return acc + 1 - // } - // return acc - // }, 0) - // } - - // const missingPipettes = countHardwareType('pipette') - // const missing = countHardwareType('pipette') - const missingProtocolHardwareType = missingProtocolHardware.map( hardware => hardware.hardwareType ) - const missingProtocolPipetteType = missingProtocolHardwareType.filter( - type => type === 'pipette' - ) - const missingProtocolModuleType = missingProtocolHardwareType.filter( - type => type === 'module' - ) + // Note(kj:04/13/2023) This component only check the type and count the number + // If we need to display any specific information, we will need to use filter + const countMissingHardwareType = (hwType: 'pipette' | 'module'): number => { + return missingProtocolHardwareType.reduce((acc, hardwareType) => { + if (hardwareType === hwType) { + return acc + 1 + } + return acc + }, 0) + } + + const countMissingPipettes = countMissingHardwareType('pipette') + const countMissingModules = countMissingHardwareType('module') let chipText: string = t('ready_to_run') - if ( - missingProtocolPipetteType.length === 0 && - missingProtocolModuleType.length > 0 - ) { + if (countMissingPipettes === 0 && countMissingModules > 0) { chipText = t('missing_module', { - num: missingProtocolModuleType.length, - count: missingProtocolModuleType.length, + num: countMissingModules, + count: countMissingModules, }) - } else if ( - missingProtocolPipetteType.length > 0 && - missingProtocolModuleType.length === 0 - ) { + } else if (countMissingPipettes > 0 && countMissingModules === 0) { chipText = t('missing_pipette', { - num: missingProtocolPipetteType.length, - count: missingProtocolPipetteType.length, + num: countMissingPipettes, + count: countMissingPipettes, }) - } else if ( - missingProtocolPipetteType.length > 0 && - missingProtocolModuleType.length > 0 - ) { + } else if (countMissingPipettes > 0 && countMissingModules > 0) { chipText = t('missing_both', { - numMod: missingProtocolModuleType.length, - numPip: missingProtocolPipetteType.length, + numMod: countMissingModules, + numPip: countMissingPipettes, }) } const handleCardClick = (): void => { diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx index 10bd2e3a756..3a193bd8733 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx @@ -1,5 +1,4 @@ import * as React from 'react' -import { when, resetAllWhenMocks } from 'jest-when' import { fireEvent } from '@testing-library/react' import { formatDistance } from 'date-fns' @@ -18,25 +17,31 @@ const mockProtocolName = 'mockProtocol' const mockProtocolId = 'mockProtocolId' const mockLastRun = '2023-04-12T21:30:49.124108+00:00' -const mockPipette = [ +const mockMissingPipette = [ { hardwareType: 'pipette', pipetteName: 'p1000_single_gen3', mount: 'left', - connected: true, + connected: false, }, ] as ProtocolHardware[] -// missing hardware pipette | module -const mockMissingPipette = [ + +const mockMissingModule = [ + { + hardwareType: 'module', + moduleModel: 'temperatureModuleV2', + slot: '1', + connected: false, + }, +] as ProtocolHardware[] + +const missingBoth = [ { hardwareType: 'pipette', pipetteName: 'p1000_single_gen3', mount: 'left', connected: false, }, -] as ProtocolHardware[] - -const mockMissingModule = [ { hardwareType: 'module', moduleModel: 'temperatureModuleV2', @@ -73,12 +78,11 @@ describe('RecentProtocolRunCard', () => { protocolId: mockProtocolId, lastRun: mockLastRun, } - mockUseMissingProtocolHardware.mockReturnValue(mockPipette) + mockUseMissingProtocolHardware.mockReturnValue([]) }) afterEach(() => { jest.clearAllMocks() - resetAllWhenMocks() }) it('should render text', () => { @@ -91,22 +95,28 @@ describe('RecentProtocolRunCard', () => { getByText(`Last run ${lastRunTime}`) }) - // it('should render missing chip when missing a pipette', () => { - // mockUseMissingProtocolHardware.mockReturnValue(mockMissingPipette) - // const [{ getByText }] = render(props) - // getByText('Missing 1 pipette(s)') - // }) - - // it('should render missing chip when missing a module', () => { - // mockUseMissingProtocolHardware.mockReturnValue(mockMissingModule) - // const [{ getByText }] = render(props) - // getByText('Missing 1 module(s)') - // }) - - // it('when tapping a card, a mock function is called', () => { - // const [{ getByLabelText }] = render(props) - // const button = getByLabelText('RecentRunCard') - // fireEvent.click(button) - // expect(mockPush).toHaveBeenCalledWith(`protocols/${props.protocolId}`) - // }) + it('should render missing chip when missing a pipette', () => { + mockUseMissingProtocolHardware.mockReturnValue(mockMissingPipette) + const [{ getByText }] = render(props) + getByText('Missing 1 pipette') + }) + + it('should render missing chip when missing a module', () => { + mockUseMissingProtocolHardware.mockReturnValue(mockMissingModule) + const [{ getByText }] = render(props) + getByText('Missing 1 module') + }) + + it('should render missing chip (module and pipette) when missing a pipette and a module', () => { + mockUseMissingProtocolHardware.mockReturnValue(missingBoth) + const [{ getByText }] = render(props) + getByText('Missing 1 module(s) and 1 pipette(s)') + }) + + it('when tapping a card, a mock function is called', () => { + const [{ getByLabelText }] = render(props) + const button = getByLabelText('RecentRunCard') + fireEvent.click(button) + expect(mockPush).toHaveBeenCalledWith(`protocols/${props.protocolId}`) + }) }) From 5f1d1ed5b07a9a5db79beb89509cabca75938539 Mon Sep 17 00:00:00 2001 From: koji Date: Thu, 13 Apr 2023 17:40:44 -0400 Subject: [PATCH 16/28] simplify the condition --- app/src/pages/Protocols/hooks/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/pages/Protocols/hooks/index.ts b/app/src/pages/Protocols/hooks/index.ts index b8b44a00dfe..465c488442e 100644 --- a/app/src/pages/Protocols/hooks/index.ts +++ b/app/src/pages/Protocols/hooks/index.ts @@ -90,7 +90,5 @@ export const useMissingProtocolHardware = ( protocolId: string ): ProtocolHardware[] => { const requiredProtocolHardware = useRequiredProtocolHardware(protocolId) - return requiredProtocolHardware.filter( - hardware => hardware.connected === false - ) + return requiredProtocolHardware.filter(hardware => !hardware.connected) } From 3b73a3e19a4ca6931f8149edefe841085c4d673e Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 13:48:44 -0400 Subject: [PATCH 17/28] add test for Carousel --- .../RecentRunProtocolCarousel.tsx | 91 +++++++++++++++++++ .../RecentRunProtocolCarousel.test.tsx | 85 +++++++++++++++++ .../OnDeviceDisplay/RobotDashboard/index.ts | 1 + .../pages/OnDeviceDisplay/RobotDashboard.tsx | 64 +++++-------- 4 files changed, 202 insertions(+), 39 deletions(-) create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx create mode 100644 app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx new file mode 100644 index 00000000000..f7a934b3949 --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx @@ -0,0 +1,91 @@ +import * as React from 'react' +import { + ALIGN_FLEX_START, + DIRECTION_ROW, + Flex, + OVERFLOW_HIDDEN, + SPACING, + useSwipe, +} from '@opentrons/components' +import { useAllRunsQuery } from '@opentrons/react-api-client' +import { RecentProtocolRunCard } from './RecentProtocolRunCard' + +import type { ProtocolResource } from '@opentrons/shared-data' + +interface RecentRunProtocolCarouselProps { + sortedProtocols: ProtocolResource[] +} + +export function RecentRunProtocolCarousel({ + sortedProtocols, +}: RecentRunProtocolCarouselProps): JSX.Element { + const runs = useAllRunsQuery() + const swipe = useSwipe() + + const slider = (swipe.ref.current as unknown) as HTMLDivElement + let isDown = false + let startX = 0 + let scrollLeft = 0 + + const handleMouseDown = (e: React.MouseEvent): void => { + isDown = true + startX = e.pageX - slider.offsetLeft + scrollLeft = slider.scrollLeft + } + + const handleMouseLeave = (): void => { + isDown = false + } + + const handleMouseUp = (): void => { + isDown = false + } + + const handleMouseMove = (e: React.MouseEvent): void => { + if (!isDown) return + e.preventDefault() + const x = e.pageX - slider.offsetLeft + const walk = (x - startX) * 3 + slider.scrollLeft = scrollLeft - walk + } + + if (swipe.swipeType === 'swipe-left' || swipe.swipeType === 'swipe-right') { + const walk = swipe.swipeType === 'swipe-left' ? -800 : 800 + slider.scrollLeft = slider.scrollLeft - walk + swipe.setSwipeType('') + } + + return ( + + + {sortedProtocols.map((protocol: ProtocolResource) => { + const lastRun = runs.data?.data.find( + run => run.protocolId === protocol.id + )?.createdAt + const protocolId = protocol.id + const protocolName = + protocol.metadata.protocolName ?? protocol.files[0].name + return ( + + + + ) + })} + + + ) +} diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx new file mode 100644 index 00000000000..1aa3c0d429e --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx @@ -0,0 +1,85 @@ +import * as React from 'react' +import { UseQueryResult } from 'react-query' + +import { renderWithProviders } from '@opentrons/components' +import { useAllRunsQuery } from '@opentrons/react-api-client' + +import { RecentProtocolRunCard, RecentRunProtocolCarousel } from '..' + +import type { ProtocolResource } from '@opentrons/shared-data' +import type { Runs } from '@opentrons/api-client' + +jest.mock('@opentrons/react-api-client') +jest.mock('../RecentProtocolRunCard') + +const mockSortedProtocol = [ + { + analysisSummaries: [], + createdAt: '2023-04-11T00:44:20.669922+00:00', + files: [ + { + name: 'Mock Protocol', + role: '', + }, + ], + id: 'mockSortedProtocolID', + key: 'mockKey', + metadata: { + author: 'New API User', + description: 'mock protocol', + name: 'Mock Protocol', + }, + protocolType: 'python', + }, +] as ProtocolResource[] + +const mockRun = { + actions: [], + completedAt: '2023-04-12T15:14:13.811757+00:00', + createdAt: '2023-04-12T15:13:52.110602+00:00', + current: false, + errors: [], + id: '853a3fae-8043-47de-8f03-5d28b3ee3d35', + labware: [], + labwareOffsets: [], + liquids: [], + modules: [], + pipettes: [], + protocolId: 'mockSortedProtocolID', + status: 'stopped', +} + +const mockRecentProtocolRunCard = RecentProtocolRunCard as jest.MockedFunction< + typeof RecentProtocolRunCard +> +const mockUseAllRunsQuery = useAllRunsQuery as jest.MockedFunction< + typeof useAllRunsQuery +> + +const render = ( + props: React.ComponentProps +) => { + return renderWithProviders() +} + +describe('RecentRunProtocolCarousel', () => { + let props: React.ComponentProps + + beforeEach(() => { + props = { + sortedProtocols: mockSortedProtocol, + } + mockRecentProtocolRunCard.mockReturnValue( +
mock RecentProtocolRunCard
+ ) + mockUseAllRunsQuery.mockReturnValue({ data: { data: [mockRun] } } as any) + }) + + it('should render RecentRunProtocolCard', () => { + const [{ getByText }] = render(props) + getByText('mock RecentProtocolRunCard') + }) + + // Note(kj:04/14/2023) still looking for a way to test swipe gesture in a unit test + it.todo('test swipe gesture') +}) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts b/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts index 09fc7870aaf..7c88091b5e7 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts @@ -1,2 +1,3 @@ export * from './EmptyRecentRun' export * from './RecentProtocolRunCard' +export * from './RecentRunProtocolCarousel' diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index f080b705ee1..090334a2914 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next' import { Flex, DIRECTION_COLUMN, - DIRECTION_ROW, SPACING, TYPOGRAPHY, } from '@opentrons/components' @@ -18,7 +17,7 @@ import { Navigation } from '../../organisms/OnDeviceDisplay/Navigation' import { onDeviceDisplayRoutes } from '../../App/OnDeviceDisplayApp' import { EmptyRecentRun, - RecentProtocolRunCard, + RecentRunProtocolCarousel, } from '../../organisms/OnDeviceDisplay/RobotDashboard' import { sortProtocols } from './ProtocolDashboard/utils' @@ -38,44 +37,31 @@ export function RobotDashboard(): JSX.Element { MAXIMUM_RECENT_RUN_PROTOCOLS ) return ( - + <> - - {sortedProtocols.length === 0 ? ( - <> - - - ) : ( - <> - - {t('run_again')} - - - {sortedProtocols.map(protocol => { - const protocolId = protocol.id - const lastRun = runs.data?.data.find( - run => run.protocolId === protocolId - )?.createdAt - return ( - - - - ) - })} - - - )} + + + {sortedProtocols.length === 0 ? ( + <> + + + ) : ( + <> + + {t('run_again')} + + + + )} + - + ) } From b02d395e80ddda87511acf37ceabb8865a796f6a Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 14:02:20 -0400 Subject: [PATCH 18/28] fix lint-js errors --- .../RobotDashboard/RecentProtocolRunCard.tsx | 6 +++++- .../__tests__/RecentRunProtocolCarousel.test.tsx | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index ffa14d7fa1e..da5a918997d 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -37,6 +37,10 @@ export function RecentProtocolRunCard({ const isSuccess = missingProtocolHardware.length === 0 const CARD_STYLE = css` + /* display: -webkit-box; */ + -webkit-box-orient: vertical; + -webkit-line-clamp: 5; + overflow: hidden; &:active { background-color: ${isSuccess ? COLORS.green_three_pressed @@ -48,7 +52,7 @@ export function RecentProtocolRunCard({ ` const PROTOCOL_TEXT_STYLE = css` - display: -webkit-box; + /* display: -webkit-box; */ -webkit-box-orient: vertical; -webkit-line-clamp: 5; overflow: hidden; diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx index 1aa3c0d429e..c5a39e443f0 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx @@ -1,5 +1,4 @@ import * as React from 'react' -import { UseQueryResult } from 'react-query' import { renderWithProviders } from '@opentrons/components' import { useAllRunsQuery } from '@opentrons/react-api-client' @@ -7,7 +6,6 @@ import { useAllRunsQuery } from '@opentrons/react-api-client' import { RecentProtocolRunCard, RecentRunProtocolCarousel } from '..' import type { ProtocolResource } from '@opentrons/shared-data' -import type { Runs } from '@opentrons/api-client' jest.mock('@opentrons/react-api-client') jest.mock('../RecentProtocolRunCard') From 7bb0fbaf05dcb686561d5b4183f7395485711123 Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 15:00:55 -0400 Subject: [PATCH 19/28] modify text styling --- .../RobotDashboard/RecentProtocolRunCard.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index da5a918997d..8b33bf8bfad 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -37,10 +37,6 @@ export function RecentProtocolRunCard({ const isSuccess = missingProtocolHardware.length === 0 const CARD_STYLE = css` - /* display: -webkit-box; */ - -webkit-box-orient: vertical; - -webkit-line-clamp: 5; - overflow: hidden; &:active { background-color: ${isSuccess ? COLORS.green_three_pressed @@ -52,10 +48,12 @@ export function RecentProtocolRunCard({ ` const PROTOCOL_TEXT_STYLE = css` - /* display: -webkit-box; */ + overflow-wrap: break-word; + display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5; overflow: hidden; + width: 100%; ` const missingProtocolHardwareType = missingProtocolHardware.map( From 3f2dee0a980b6a817d941af45410cdddb5e81f2e Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 15:14:11 -0400 Subject: [PATCH 20/28] Update RobotDashboard.tsx --- .../pages/OnDeviceDisplay/RobotDashboard.tsx | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx index 090334a2914..517ede83db4 100644 --- a/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx +++ b/app/src/pages/OnDeviceDisplay/RobotDashboard.tsx @@ -37,31 +37,29 @@ export function RobotDashboard(): JSX.Element { MAXIMUM_RECENT_RUN_PROTOCOLS ) return ( - <> + - - - {sortedProtocols.length === 0 ? ( - <> - - - ) : ( - <> - - {t('run_again')} - - - - )} - + + {sortedProtocols.length === 0 ? ( + <> + + + ) : ( + <> + + {t('run_again')} + + + + )} - + ) } From 3beee2e0bcf2e1ec6796febfbc6a9395ba239719 Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 17:07:35 -0400 Subject: [PATCH 21/28] add test for useRequiredProtocolLabware --- .../Protocols/hooks/__tests__/hooks.test.tsx | 89 ++++++++++++++++++- app/src/pages/Protocols/hooks/index.ts | 20 +++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx b/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx index 557bf14b4b7..cdcb39d1af9 100644 --- a/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx +++ b/app/src/pages/Protocols/hooks/__tests__/hooks.test.tsx @@ -1,18 +1,25 @@ import * as React from 'react' import { UseQueryResult } from 'react-query' import { renderHook } from '@testing-library/react-hooks' +import { when, resetAllWhenMocks } from 'jest-when' + import { useProtocolAnalysesQuery } from '@opentrons/react-api-client' import { useAttachedModules, useAttachedPipettes, } from '../../../../organisms/Devices/hooks' import { mockHeaterShaker } from '../../../../redux/modules/__fixtures__' -import { useMissingProtocolHardware } from '..' +import { useRequiredProtocolLabware, useMissingProtocolHardware } from '..' +import fixture_tiprack_300_ul from '@opentrons/shared-data/labware/fixtures/2/fixture_tiprack_300_ul.json' + import type { ProtocolAnalyses } from '@opentrons/api-client' +import type { LabwareDefinition2 } from '@opentrons/shared-data' jest.mock('@opentrons/react-api-client') jest.mock('../../../../organisms/Devices/hooks') +const PROTOCOL_ID = 'fake_protocol_id' + const mockUseProtocolAnalysesQuery = useProtocolAnalysesQuery as jest.MockedFunction< typeof useProtocolAnalysesQuery > @@ -22,6 +29,8 @@ const mockUseAttachedModules = useAttachedModules as jest.MockedFunction< const mockUseAttachedPipettes = useAttachedPipettes as jest.MockedFunction< typeof useAttachedPipettes > + +const mockLabwareDef = fixture_tiprack_300_ul as LabwareDefinition2 const PROTOCOL_ANALYSIS = { id: 'fake analysis', status: 'completed', @@ -35,8 +44,86 @@ const PROTOCOL_ANALYSIS = { serialNumber: 'serialNum', }, ], + commands: [ + { + key: 'CommandKey0', + commandType: 'loadLabware', + params: { + labwareId: 'firstLabwareId', + location: { slotName: '1' }, + displayName: 'first labware nickname', + }, + result: { + labwareId: 'firstLabwareId', + definition: mockLabwareDef, + offset: { x: 0, y: 0, z: 0 }, + }, + id: 'CommandId0', + status: 'succeeded', + error: null, + createdAt: 'fakeCreatedAtTimestamp', + startedAt: 'fakeStartedAtTimestamp', + completedAt: 'fakeCompletedAtTimestamp', + }, + ], } as any +const NULL_COMMAND = { + id: '97ba49a5-04f6-4f91-986a-04a0eb632882', + createdAt: '2022-09-07T19:47:42.781065+00:00', + commandType: 'loadPipette', + key: '0feeecaf-3895-46d7-ab71-564601265e35', + status: 'succeeded', + params: { + pipetteName: 'p20_single_gen2', + mount: 'left', + pipetteId: '90183a18-a1df-4fd6-9636-be3bcec63fe4', + }, + result: { + pipetteId: '90183a18-a1df-4fd6-9636-be3bcec63fe4', + }, + startedAt: '2022-09-07T19:47:42.782665+00:00', + completedAt: '2022-09-07T19:47:42.785061+00:00', +} +const NULL_PROTOCOL_ANALYSIS = { + ...PROTOCOL_ANALYSIS, + commands: [NULL_COMMAND], +} as any + +describe('useRequiredProtocolLabware', () => { + beforeEach(() => { + when(mockUseProtocolAnalysesQuery) + .calledWith(PROTOCOL_ID, { staleTime: Infinity }) + .mockReturnValue({ + data: { data: [PROTOCOL_ANALYSIS as any] }, + } as UseQueryResult) + }) + + afterEach(() => { + resetAllWhenMocks() + }) + + it('should return LabwareSetupItem array', () => { + const { result } = renderHook(() => useRequiredProtocolLabware(PROTOCOL_ID)) + expect(result.current.length).toBe(1) + expect(result.current[0].nickName).toEqual('first labware nickname') + expect(result.current[0].definition.dimensions.xDimension).toBe(127.76) + expect(result.current[0].definition.metadata.displayName).toEqual( + '300ul Tiprack FIXTURE' + ) + }) + + it('should return empty array when there is no match with protocol id', () => { + when(mockUseProtocolAnalysesQuery) + .calledWith(PROTOCOL_ID, { staleTime: Infinity }) + .mockReturnValue({ + data: { data: [NULL_PROTOCOL_ANALYSIS as any] }, + } as UseQueryResult) + const { result } = renderHook(() => useRequiredProtocolLabware(PROTOCOL_ID)) + expect(result.current.length).toBe(0) + }) +}) + describe('useMissingProtocolHardware', () => { let wrapper: React.FunctionComponent<{}> beforeEach(() => { diff --git a/app/src/pages/Protocols/hooks/index.ts b/app/src/pages/Protocols/hooks/index.ts index 465c488442e..c9c8b81ded1 100644 --- a/app/src/pages/Protocols/hooks/index.ts +++ b/app/src/pages/Protocols/hooks/index.ts @@ -32,6 +32,12 @@ interface ProtocolModule { export type ProtocolHardware = ProtocolPipette | ProtocolModule +/** + * Returns an array of ProtocolHardware objects that are required by the given protocol ID. + * + * @param {string} protocolId The ID of the protocol for which required hardware is being retrieved. + * @returns {ProtocolHardware[]} An array of ProtocolHardware objects that are required by the given protocol ID. + */ export const useRequiredProtocolHardware = ( protocolId: string ): ProtocolHardware[] => { @@ -73,6 +79,12 @@ export const useRequiredProtocolHardware = ( return [...requiredPipettes, ...requiredModules] } +/** + * Returns an array of LabwareSetupItem objects that are required by the given protocol ID. + * + * @param {string} protocolId The ID of the protocol for which required labware setup items are being retrieved. + * @returns {LabwareSetupItem[]} An array of LabwareSetupItem objects that are required by the given protocol ID. + */ export const useRequiredProtocolLabware = ( protocolId: string ): LabwareSetupItem[] => { @@ -86,6 +98,14 @@ export const useRequiredProtocolLabware = ( return [...onDeckItems, ...offDeckItems] } +/** + * Returns an array of ProtocolHardware objects that are required by the given protocol ID, + * but not currently connected. + * + * @param {string} protocolId The ID of the protocol for which required but missing hardware is being retrieved. + * @returns {ProtocolHardware[]} An array of ProtocolHardware objects that are required by the given protocol ID, + * but not currently connected. + */ export const useMissingProtocolHardware = ( protocolId: string ): ProtocolHardware[] => { From 39b6e1b8363621110c2c8785f32d04863fe6dda5 Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 17:09:05 -0400 Subject: [PATCH 22/28] update the text when missing pipette and module --- app/src/assets/localization/en/device_details.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index 21aca6fcb0a..62ccc43f430 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -131,7 +131,7 @@ "missing_module_plural": "missing {{num}} modules", "missing_pipette": "missing {{num}} pipette", "missing_pipettes_plural": "missing {{num}} pipettes", - "missing_both": "missing {{numMod}} module(s) and {{numPip}} pipette(s)", + "missing_both": "missing hardware", "have_not_run": "No recent runs", "have_not_run_description": "After you run some protocols, they will appear here." } From e110f099fb3b9ffbe9d0bd98653dbe5b0ad04c61 Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 17:25:19 -0400 Subject: [PATCH 23/28] update the conditions for each cases --- .../RobotDashboard/RecentProtocolRunCard.tsx | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index 8b33bf8bfad..d9842cdd91e 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -76,20 +76,27 @@ export function RecentProtocolRunCard({ let chipText: string = t('ready_to_run') if (countMissingPipettes === 0 && countMissingModules > 0) { - chipText = t('missing_module', { - num: countMissingModules, - count: countMissingModules, - }) + if (countMissingModules === 1) { + chipText = t('missing_module', { + num: countMissingModules, + }) + } else { + chipText = t('missing_module_plural', { + num: countMissingModules, + }) + } } else if (countMissingPipettes > 0 && countMissingModules === 0) { - chipText = t('missing_pipette', { - num: countMissingPipettes, - count: countMissingPipettes, - }) + if (countMissingPipettes === 1) { + chipText = t('missing_pipette', { + num: countMissingPipettes, + }) + } else { + chipText = t('missing_pipettes_plural', { + num: countMissingPipettes, + }) + } } else if (countMissingPipettes > 0 && countMissingModules > 0) { - chipText = t('missing_both', { - numMod: countMissingModules, - numPip: countMissingPipettes, - }) + chipText = t('missing_both') } const handleCardClick = (): void => { history.push(`protocols/${protocolId}`) From 8f5ee441e6efa827fb458ad0fcd5784bb8cc96d8 Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Apr 2023 19:54:16 -0400 Subject: [PATCH 24/28] fix test errors --- .../pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx index cb83924fa60..2b0d91f850f 100644 --- a/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx +++ b/app/src/pages/OnDeviceDisplay/__tests__/RobotDashboard.test.tsx @@ -204,8 +204,8 @@ describe('RobotDashboard', () => { getByText('mock Navigation') getByText('Run again') getByText('yay mock protocol') - getByText('Missing 1 module(s) and 1 pipette(s)') - getByLabelText('icon_Missing 1 module(s) and 1 pipette(s)') + getByText('Missing hardware') + getByLabelText('icon_Missing hardware') expect(getByLabelText('RecentRunCard')).toHaveStyle( `background-color: ${COLORS.yellow_three}` ) From 9ad5d8ea43775ff9d9cd622fdb1e3ae6492e2e65 Mon Sep 17 00:00:00 2001 From: koji Date: Mon, 17 Apr 2023 10:59:16 -0400 Subject: [PATCH 25/28] fix test issue --- .../RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx index 3a193bd8733..4dfa8662206 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentProtocolRunCard.test.tsx @@ -110,7 +110,7 @@ describe('RecentProtocolRunCard', () => { it('should render missing chip (module and pipette) when missing a pipette and a module', () => { mockUseMissingProtocolHardware.mockReturnValue(missingBoth) const [{ getByText }] = render(props) - getByText('Missing 1 module(s) and 1 pipette(s)') + getByText('Missing hardware') }) it('when tapping a card, a mock function is called', () => { From 25fd796b24df75d58a12b4b46bc1f2ed510ff357 Mon Sep 17 00:00:00 2001 From: koji Date: Mon, 17 Apr 2023 17:16:41 -0400 Subject: [PATCH 26/28] address comments --- .../abstract@x2.png | Bin .../empty_recent_protocol_run.png} | Bin .../odd_abstract@x2.png | Bin .../opentrons_logo.png | Bin .../usb@x2.png | Bin .../localization/en/device_details.json | 4 ++-- app/src/atoms/Chip/index.tsx | 6 +++-- .../organisms/NameRobot/ConfirmRobotName.tsx | 2 +- .../RobotDashboard/EmptyRecentRun.tsx | 4 ++-- .../RobotDashboard/RecentProtocolRunCard.tsx | 22 +++++++++++------- .../__tests__/EmptyRecentRun.test.tsx | 4 ++-- .../organisms/OnDeviceDisplay/SleepScreen.tsx | 2 +- .../pages/OnDeviceDisplay/ConnectViaUSB.tsx | 2 +- .../ProtocolDashboard/index.tsx | 2 +- app/src/pages/OnDeviceDisplay/Welcome.tsx | 2 +- 15 files changed, 28 insertions(+), 22 deletions(-) rename app/src/assets/images/{OnDeviceDisplay => on-device-display}/abstract@x2.png (100%) rename app/src/assets/images/{OnDeviceDisplay/Illustration@x2.png => on-device-display/empty_recent_protocol_run.png} (100%) rename app/src/assets/images/{OnDeviceDisplay => on-device-display}/odd_abstract@x2.png (100%) rename app/src/assets/images/{OnDeviceDisplay => on-device-display}/opentrons_logo.png (100%) rename app/src/assets/images/{OnDeviceDisplay => on-device-display}/usb@x2.png (100%) diff --git a/app/src/assets/images/OnDeviceDisplay/abstract@x2.png b/app/src/assets/images/on-device-display/abstract@x2.png similarity index 100% rename from app/src/assets/images/OnDeviceDisplay/abstract@x2.png rename to app/src/assets/images/on-device-display/abstract@x2.png diff --git a/app/src/assets/images/OnDeviceDisplay/Illustration@x2.png b/app/src/assets/images/on-device-display/empty_recent_protocol_run.png similarity index 100% rename from app/src/assets/images/OnDeviceDisplay/Illustration@x2.png rename to app/src/assets/images/on-device-display/empty_recent_protocol_run.png diff --git a/app/src/assets/images/OnDeviceDisplay/odd_abstract@x2.png b/app/src/assets/images/on-device-display/odd_abstract@x2.png similarity index 100% rename from app/src/assets/images/OnDeviceDisplay/odd_abstract@x2.png rename to app/src/assets/images/on-device-display/odd_abstract@x2.png diff --git a/app/src/assets/images/OnDeviceDisplay/opentrons_logo.png b/app/src/assets/images/on-device-display/opentrons_logo.png similarity index 100% rename from app/src/assets/images/OnDeviceDisplay/opentrons_logo.png rename to app/src/assets/images/on-device-display/opentrons_logo.png diff --git a/app/src/assets/images/OnDeviceDisplay/usb@x2.png b/app/src/assets/images/on-device-display/usb@x2.png similarity index 100% rename from app/src/assets/images/OnDeviceDisplay/usb@x2.png rename to app/src/assets/images/on-device-display/usb@x2.png diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index 62ccc43f430..9090f0c4bb1 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -128,9 +128,9 @@ "last_run_time": "last run {{number}}", "ready_to_run": "ready to run", "missing_module": "missing {{num}} module", - "missing_module_plural": "missing {{num}} modules", + "missing_module_plural": "missing {{count}} modules", "missing_pipette": "missing {{num}} pipette", - "missing_pipettes_plural": "missing {{num}} pipettes", + "missing_pipettes_plural": "missing {{count}} pipettes", "missing_both": "missing hardware", "have_not_run": "No recent runs", "have_not_run_description": "After you run some protocols, they will appear here." diff --git a/app/src/atoms/Chip/index.tsx b/app/src/atoms/Chip/index.tsx index 09cd774cf64..eeee2bce063 100644 --- a/app/src/atoms/Chip/index.tsx +++ b/app/src/atoms/Chip/index.tsx @@ -13,11 +13,11 @@ import { import { StyledText } from '../text' -import type { IconName } from '@opentrons/components' +import type { IconName, StyleProps } from '@opentrons/components' export type ChipType = 'basic' | 'success' | 'warning' | 'neutral' -interface ChipProps { +interface ChipProps extends StyleProps { /** Display background color? */ background?: boolean /** Chip icon */ @@ -70,6 +70,7 @@ export function Chip({ iconName, type, text, + ...styleProps }: ChipProps): JSX.Element { const backgroundColor = background === false && type !== 'basic' @@ -85,6 +86,7 @@ export function Chip({ padding={`${SPACING.spacing3} ${SPACING.spacing4}`} gridGap={SPACING.spacing3} data-testid={`Chip_${type}`} + {...styleProps} > {type !== 'basic' && ( RobotDashboard no recent run protocols diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index d9842cdd91e..c3e4a1170b2 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -20,9 +20,9 @@ import { useMissingProtocolHardware } from '../../../pages/Protocols/hooks' interface RecentProtocolRunCardProps { /** protocol name that was run recently */ protocolName: string - /** protocol id */ + /** protocol id that was run recently */ protocolId: string - /** last run */ + /** the time that this recent run was created */ lastRun?: string } @@ -34,11 +34,11 @@ export function RecentProtocolRunCard({ const { t, i18n } = useTranslation('device_details') const missingProtocolHardware = useMissingProtocolHardware(protocolId) const history = useHistory() - const isSuccess = missingProtocolHardware.length === 0 + const isReadyToBeReRun = missingProtocolHardware.length === 0 const CARD_STYLE = css` &:active { - background-color: ${isSuccess + background-color: ${isReadyToBeReRun ? COLORS.green_three_pressed : COLORS.yellow_three_pressed}; } @@ -82,7 +82,7 @@ export function RecentProtocolRunCard({ }) } else { chipText = t('missing_module_plural', { - num: countMissingModules, + count: countMissingModules, }) } } else if (countMissingPipettes > 0 && countMissingModules === 0) { @@ -92,7 +92,7 @@ export function RecentProtocolRunCard({ }) } else { chipText = t('missing_pipettes_plural', { - num: countMissingPipettes, + count: countMissingPipettes, }) } } else if (countMissingPipettes > 0 && countMissingModules > 0) { @@ -109,15 +109,19 @@ export function RecentProtocolRunCard({ flexDirection={DIRECTION_COLUMN} padding={SPACING.spacing5} gridGap={SPACING.spacing5} - backgroundColor={isSuccess ? COLORS.green_three : COLORS.yellow_three} + backgroundColor={ + isReadyToBeReRun ? COLORS.green_three : COLORS.yellow_three + } width="25.8125rem" borderRadius={BORDERS.size_four} onClick={handleCardClick} > {/* marginLeft is needed to cancel chip's padding */} - + {/* */} + diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx index 031e3e8aec0..130e30ed974 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/EmptyRecentRun.test.tsx @@ -4,7 +4,7 @@ import { renderWithProviders } from '@opentrons/components' import { i18n } from '../../../../i18n' import { EmptyRecentRun } from '../EmptyRecentRun' -const PNG_FILE_NAME = 'Illustration@x2.png' +const PNG_FILE_NAME = 'empty_recent_protocol_run.png' const render = () => { return renderWithProviders(, { @@ -15,7 +15,7 @@ const render = () => { describe('EmptyRecentRun', () => { it('should render image and text', () => { const [{ getByText, getByAltText, getByRole }] = render() - getByAltText('RobotDashboard no recent run protocols') + getByAltText('There is no recent run protocol') getByText('No recent runs') getByText('After you run some protocols, they will appear here.') const image = getByRole('img') diff --git a/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx b/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx index 5ff59b1a239..808a8ec0ef2 100644 --- a/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx +++ b/app/src/organisms/OnDeviceDisplay/SleepScreen.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import logo from '../../assets/images/OnDeviceDisplay/opentrons_logo.png' +import logo from '../../assets/images/on-device-display/opentrons_logo.png' export function SleepScreen(): JSX.Element { const speed = 50 diff --git a/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx b/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx index 69621906e90..f1049439c28 100644 --- a/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx +++ b/app/src/pages/OnDeviceDisplay/ConnectViaUSB.tsx @@ -18,7 +18,7 @@ import { import { StyledText } from '../../atoms/text' import { StepMeter } from '../../atoms/StepMeter' // import { PrimaryButton } from '../../atoms/buttons' -import usbImage from '../../assets/images/OnDeviceDisplay/usb@x2.png' +import usbImage from '../../assets/images/on-device-display/usb@x2.png' // Note: kj 12/06/2022 The commented-out lines will be activated when the check function is ready export function ConnectViaUSB(): JSX.Element { diff --git a/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx b/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx index d521f01eacb..be7bb3e4b60 100644 --- a/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx +++ b/app/src/pages/OnDeviceDisplay/ProtocolDashboard/index.tsx @@ -33,7 +33,7 @@ import { LongPressModal } from './LongPressModal' import { PinnedProtocolCarousel } from './PinnedProtocolCarousel' import { sortProtocols } from './utils' -import imgSrc from '../../../assets/images/OnDeviceDisplay/abstract@x2.png' +import imgSrc from '../../../assets/images/on-device-display/abstract@x2.png' import type { Dispatch } from '../../../redux/types' import type { ProtocolsOnDeviceSortKey } from '../../../redux/config/types' diff --git a/app/src/pages/OnDeviceDisplay/Welcome.tsx b/app/src/pages/OnDeviceDisplay/Welcome.tsx index 708ba1544c7..03dd393bfab 100644 --- a/app/src/pages/OnDeviceDisplay/Welcome.tsx +++ b/app/src/pages/OnDeviceDisplay/Welcome.tsx @@ -13,7 +13,7 @@ import { } from '@opentrons/components' import { StyledText } from '../../atoms/text' -import screenImage from '../../assets/images/OnDeviceDisplay/odd_abstract@x2.png' +import screenImage from '../../assets/images/on-device-display/odd_abstract@x2.png' const IMAGE_ALT = 'Get started setting up a robot' From 234473f6505c485f4026e170998e2a6ab42b1821 Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 20 Apr 2023 08:11:59 -0400 Subject: [PATCH 27/28] fix truncated protocol name by adding a height of max-content --- .../OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx index c3e4a1170b2..c0a9afd7f7c 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx @@ -54,6 +54,7 @@ export function RecentProtocolRunCard({ -webkit-line-clamp: 5; overflow: hidden; width: 100%; + height: max-content; ` const missingProtocolHardwareType = missingProtocolHardware.map( @@ -102,6 +103,7 @@ export function RecentProtocolRunCard({ history.push(`protocols/${protocolId}`) } + console.log(protocolName) return ( Date: Thu, 20 Apr 2023 08:32:09 -0400 Subject: [PATCH 28/28] update css and rename file name --- ...otocolRunCard.tsx => RecentRunProtocolCard.tsx} | 9 ++++----- .../RobotDashboard/RecentRunProtocolCarousel.tsx | 4 ++-- .../__tests__/RecentProtocolRunCard.test.tsx | 10 +++++----- .../__tests__/RecentRunProtocolCarousel.test.tsx | 14 +++++++------- .../OnDeviceDisplay/RobotDashboard/index.ts | 2 +- 5 files changed, 19 insertions(+), 20 deletions(-) rename app/src/organisms/OnDeviceDisplay/RobotDashboard/{RecentProtocolRunCard.tsx => RecentRunProtocolCard.tsx} (96%) diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx similarity index 96% rename from app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx rename to app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx index c0a9afd7f7c..e75dfdf0445 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentProtocolRunCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx @@ -17,7 +17,7 @@ import { StyledText } from '../../../atoms/text' import { Chip } from '../../../atoms/Chip' import { useMissingProtocolHardware } from '../../../pages/Protocols/hooks' -interface RecentProtocolRunCardProps { +interface RecentRunProtocolCardProps { /** protocol name that was run recently */ protocolName: string /** protocol id that was run recently */ @@ -26,11 +26,11 @@ interface RecentProtocolRunCardProps { lastRun?: string } -export function RecentProtocolRunCard({ +export function RecentRunProtocolCard({ protocolName, protocolId, lastRun, -}: RecentProtocolRunCardProps): JSX.Element { +}: RecentRunProtocolCardProps): JSX.Element { const { t, i18n } = useTranslation('device_details') const missingProtocolHardware = useMissingProtocolHardware(protocolId) const history = useHistory() @@ -48,12 +48,11 @@ export function RecentProtocolRunCard({ ` const PROTOCOL_TEXT_STYLE = css` - overflow-wrap: break-word; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5; overflow: hidden; - width: 100%; + overflow-wrap: break-word; height: max-content; ` diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx index f7a934b3949..db32b5b441a 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCarousel.tsx @@ -8,7 +8,7 @@ import { useSwipe, } from '@opentrons/components' import { useAllRunsQuery } from '@opentrons/react-api-client' -import { RecentProtocolRunCard } from './RecentProtocolRunCard' +import { RecentRunProtocolCard } from './RecentRunProtocolCard' import type { ProtocolResource } from '@opentrons/shared-data' @@ -77,7 +77,7 @@ export function RecentRunProtocolCarousel({ protocol.metadata.protocolName ?? protocol.files[0].name return ( - -const render = (props: React.ComponentProps) => { - return renderWithProviders(, { +const render = (props: React.ComponentProps) => { + return renderWithProviders(, { i18nInstance: i18n, }) } -describe('RecentProtocolRunCard', () => { - let props: React.ComponentProps +describe('RecentRunProtocolCard', () => { + let props: React.ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx index c5a39e443f0..d5e1c1f92ac 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx @@ -3,12 +3,12 @@ import * as React from 'react' import { renderWithProviders } from '@opentrons/components' import { useAllRunsQuery } from '@opentrons/react-api-client' -import { RecentProtocolRunCard, RecentRunProtocolCarousel } from '..' +import { RecentRunProtocolCard, RecentRunProtocolCarousel } from '..' import type { ProtocolResource } from '@opentrons/shared-data' jest.mock('@opentrons/react-api-client') -jest.mock('../RecentProtocolRunCard') +jest.mock('../RecentRunProtocolCard') const mockSortedProtocol = [ { @@ -47,8 +47,8 @@ const mockRun = { status: 'stopped', } -const mockRecentProtocolRunCard = RecentProtocolRunCard as jest.MockedFunction< - typeof RecentProtocolRunCard +const mockRecentRunProtocolCard = RecentRunProtocolCard as jest.MockedFunction< + typeof RecentRunProtocolCard > const mockUseAllRunsQuery = useAllRunsQuery as jest.MockedFunction< typeof useAllRunsQuery @@ -67,15 +67,15 @@ describe('RecentRunProtocolCarousel', () => { props = { sortedProtocols: mockSortedProtocol, } - mockRecentProtocolRunCard.mockReturnValue( -
mock RecentProtocolRunCard
+ mockRecentRunProtocolCard.mockReturnValue( +
mock RecentRunProtocolCard
) mockUseAllRunsQuery.mockReturnValue({ data: { data: [mockRun] } } as any) }) it('should render RecentRunProtocolCard', () => { const [{ getByText }] = render(props) - getByText('mock RecentProtocolRunCard') + getByText('mock RecentRunProtocolCard') }) // Note(kj:04/14/2023) still looking for a way to test swipe gesture in a unit test diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts b/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts index 7c88091b5e7..5d78373bedd 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/index.ts @@ -1,3 +1,3 @@ export * from './EmptyRecentRun' -export * from './RecentProtocolRunCard' +export * from './RecentRunProtocolCard' export * from './RecentRunProtocolCarousel'