From 0ae6ec38851d821c29a74d6cf4925659f1f9146d Mon Sep 17 00:00:00 2001 From: Jethary Alcid <66035149+jerader@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:03:30 -0400 Subject: [PATCH] refactor(protocol-designer): nav routing and hover touchups (#16237) closes AUTH-790, AUTH-787, AUTH-785, AUTH-784, AUTH-756 --- .../ListItemChildren/ListItemCustomize.tsx | 22 +- protocol-designer/src/NavigationBar.tsx | 46 +-- .../src/__tests__/NavigationBar.test.tsx | 23 +- .../src/assets/localization/en/shared.json | 2 +- protocol-designer/src/atoms/constants.ts | 9 + protocol-designer/src/atoms/index.ts | 2 +- .../src/organisms/PipetteInfoItem/index.tsx | 3 + .../src/organisms/SlotInformation/index.tsx | 9 +- .../SelectPipettes.tsx | 5 +- .../CreateNewProtocolWizard/WizardBody.tsx | 8 +- .../__tests__/CreateNewProtocol.test.tsx | 3 - .../pages/CreateNewProtocolWizard/index.tsx | 262 +++++++++--------- .../pages/Landing/__tests__/Landing.test.tsx | 11 +- protocol-designer/src/pages/Landing/index.tsx | 13 +- .../pages/ProtocolOverview/DeckThumbnail.tsx | 4 - .../src/pages/ProtocolOverview/index.tsx | 17 +- 16 files changed, 238 insertions(+), 201 deletions(-) create mode 100644 protocol-designer/src/atoms/constants.ts delete mode 100644 protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/CreateNewProtocol.test.tsx diff --git a/components/src/atoms/ListItem/ListItemChildren/ListItemCustomize.tsx b/components/src/atoms/ListItem/ListItemChildren/ListItemCustomize.tsx index 9a2482d3f00..b329b2fdc78 100644 --- a/components/src/atoms/ListItem/ListItemChildren/ListItemCustomize.tsx +++ b/components/src/atoms/ListItem/ListItemChildren/ListItemCustomize.tsx @@ -1,4 +1,5 @@ import * as React from 'react' +import { css } from 'styled-components' import { ALIGN_CENTER, JUSTIFY_CENTER } from '../../../styles' import { COLORS } from '../../../helix-design-system' import { Flex, Link } from '../../../primitives' @@ -53,13 +54,20 @@ export function ListItemCustomize(props: ListItemCustomizeProps): JSX.Element { {tag != null ? : null} {onClick != null && linkText != null ? ( - - - - {linkText} - - - + + {linkText} + ) : null} ) diff --git a/protocol-designer/src/NavigationBar.tsx b/protocol-designer/src/NavigationBar.tsx index 9ce09424a54..cecddf6d0c1 100644 --- a/protocol-designer/src/NavigationBar.tsx +++ b/protocol-designer/src/NavigationBar.tsx @@ -1,11 +1,12 @@ import * as React from 'react' -import { NavLink, useLocation } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { ALIGN_CENTER, + Btn, COLORS, DIRECTION_COLUMN, Flex, @@ -13,17 +14,33 @@ import { SPACING, StyledText, } from '@opentrons/components' +import { toggleNewProtocolModal } from './navigation/actions' import { actions as loadFileActions } from './load-file' +import { BUTTON_LINK_STYLE } from './atoms' +import { getHasUnsavedChanges } from './load-file/selectors' import type { ThunkDispatch } from './types' export function NavigationBar(): JSX.Element | null { - const { t } = useTranslation('shared') + const { t } = useTranslation(['shared', 'alert']) const location = useLocation() + const navigate = useNavigate() const dispatch: ThunkDispatch = useDispatch() const loadFile = ( fileChangeEvent: React.ChangeEvent ): void => { dispatch(loadFileActions.loadProtocolFile(fileChangeEvent)) + dispatch(toggleNewProtocolModal(false)) + } + const hasUnsavedChanges = useSelector(getHasUnsavedChanges) + + const handleCreateNew = (): void => { + if ( + !hasUnsavedChanges || + window.confirm(t('alert:confirm_create_new') as string) + ) { + dispatch(toggleNewProtocolModal(true)) + navigate('/createNew') + } } return location.pathname === '/designer' || @@ -46,16 +63,18 @@ export function NavigationBar(): JSX.Element | null { {location.pathname === '/createNew' ? null : ( - + - {t('create_new_protocol')} + {t('create_new')} - + )} - - {t('import')} - + + + {t('import')} + + @@ -64,15 +83,6 @@ export function NavigationBar(): JSX.Element | null { ) } -const NavbarLink = styled(NavLink)` - color: ${COLORS.black90}; - text-decoration: none; - align-self: ${ALIGN_CENTER}; - &:hover { - color: ${COLORS.black70}; - } -` - const StyledLabel = styled.label` height: 20px; cursor: pointer; diff --git a/protocol-designer/src/__tests__/NavigationBar.test.tsx b/protocol-designer/src/__tests__/NavigationBar.test.tsx index 86bbe715623..1bd852474e8 100644 --- a/protocol-designer/src/__tests__/NavigationBar.test.tsx +++ b/protocol-designer/src/__tests__/NavigationBar.test.tsx @@ -1,14 +1,17 @@ import * as React from 'react' -import { describe, it, vi } from 'vitest' -import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import { fireEvent, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { i18n } from '../assets/localization' import { renderWithProviders } from '../__testing-utils__' import { NavigationBar } from '../NavigationBar' +import { getHasUnsavedChanges } from '../load-file/selectors' +import { toggleNewProtocolModal } from '../navigation/actions' +vi.mock('../navigation/actions') vi.mock('../file-data/selectors') - +vi.mock('../load-file/selectors') const render = () => { return renderWithProviders( @@ -19,19 +22,23 @@ const render = () => { } describe('NavigationBar', () => { + beforeEach(() => { + vi.mocked(getHasUnsavedChanges).mockReturnValue(false) + }) it('should render text and link button', () => { render() screen.getByText('Opentrons') screen.getByText('Protocol Designer') screen.getByText('Version # fake_PD_version') - screen.getByText('Create new protocol') + screen.getByText('Create new') screen.getByText('Import') }) - it.todo( - 'when clicking Create new protocol, mock function should be called', - () => {} - ) + it('when clicking Create new, should call the toggle action', () => { + render() + fireEvent.click(screen.getByText('Create new')) + expect(vi.mocked(toggleNewProtocolModal)).toHaveBeenCalled() + }) it.todo('when clicking Import, mock function should be called', () => {}) }) diff --git a/protocol-designer/src/assets/localization/en/shared.json b/protocol-designer/src/assets/localization/en/shared.json index c96a789d594..77be26bec0f 100644 --- a/protocol-designer/src/assets/localization/en/shared.json +++ b/protocol-designer/src/assets/localization/en/shared.json @@ -8,7 +8,7 @@ "confirm_reorder": "Are you sure you want to reorder these steps, it may cause errors?", "confirm": "Confirm", "create_a_protocol": "Create a protocol", - "create_new_protocol": "Create new protocol", + "create_new": "Create new", "done": "Done", "edit_existing": "Edit existing protocol", "edit": "edit", diff --git a/protocol-designer/src/atoms/constants.ts b/protocol-designer/src/atoms/constants.ts new file mode 100644 index 00000000000..e5c73333cd8 --- /dev/null +++ b/protocol-designer/src/atoms/constants.ts @@ -0,0 +1,9 @@ +import { css } from 'styled-components' +import { COLORS } from '@opentrons/components' + +export const BUTTON_LINK_STYLE = css` + color: ${COLORS.grey60}; + &:hover { + color: ${COLORS.grey40}; + } +` diff --git a/protocol-designer/src/atoms/index.ts b/protocol-designer/src/atoms/index.ts index f8f44e7744b..f87cf0102a1 100644 --- a/protocol-designer/src/atoms/index.ts +++ b/protocol-designer/src/atoms/index.ts @@ -1 +1 @@ -console.log('atoms for new components') +export * from './constants' diff --git a/protocol-designer/src/organisms/PipetteInfoItem/index.tsx b/protocol-designer/src/organisms/PipetteInfoItem/index.tsx index c376f0f7777..4628fc7481b 100644 --- a/protocol-designer/src/organisms/PipetteInfoItem/index.tsx +++ b/protocol-designer/src/organisms/PipetteInfoItem/index.tsx @@ -13,6 +13,7 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { getPipetteSpecsV2 } from '@opentrons/shared-data' +import { BUTTON_LINK_STYLE } from '../../atoms' import { getLabwareDefsByURI } from '../../labware-defs/selectors' import type { PipetteMount, PipetteName } from '@opentrons/shared-data' import type { FormPipettesByMount, PipetteOnDeck } from '../../step-forms' @@ -79,6 +80,7 @@ export function PipetteInfoItem(props: PipetteInfoItemProps): JSX.Element { {t('edit')} @@ -92,6 +94,7 @@ export function PipetteInfoItem(props: PipetteInfoItemProps): JSX.Element { cleanForm() }} textDecoration={TYPOGRAPHY.textDecorationUnderline} + css={BUTTON_LINK_STYLE} > {t('remove')} diff --git a/protocol-designer/src/organisms/SlotInformation/index.tsx b/protocol-designer/src/organisms/SlotInformation/index.tsx index b2e785b0af1..c01c1266cc5 100644 --- a/protocol-designer/src/organisms/SlotInformation/index.tsx +++ b/protocol-designer/src/organisms/SlotInformation/index.tsx @@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next' import { useLocation } from 'react-router-dom' import { ALIGN_CENTER, - Box, DeckInfoLabel, DIRECTION_COLUMN, Flex, @@ -83,7 +82,11 @@ interface StackInfoListProps { function StackInfoList({ title, items }: StackInfoListProps): JSX.Element { const pathLocation = useLocation() return ( - + {items.length > 0 ? ( items.map((item, index) => ( )} - + ) } diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx index fa3a7f1dc10..5ed48732bc0 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx @@ -30,8 +30,8 @@ import { getLabwareDefsByURI } from '../../labware-defs/selectors' import { setFeatureFlags } from '../../feature-flags/actions' import { createCustomTiprackDef } from '../../labware-defs/actions' import { useKitchen } from '../../organisms/Kitchen/hooks' -import { IncompatibleTipsModal } from '../../organisms' -import { PipetteInfoItem } from '../../organisms/' +import { IncompatibleTipsModal, PipetteInfoItem } from '../../organisms' +import { BUTTON_LINK_STYLE } from '../../atoms' import { WizardBody } from './WizardBody' import { PIPETTE_GENS, PIPETTE_TYPES, PIPETTE_VOLUMES } from './constants' import { getTiprackOptions } from './utils' @@ -373,6 +373,7 @@ export function SelectPipettes(props: WizardTileProps): JSX.Element | null { {has96Channel ? null : ( { const leftPipetteName = pipettesByMount.left.pipetteName const rightPipetteName = pipettesByMount.right.pipetteName diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx index acfdbfcd992..498a9392685 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx @@ -15,6 +15,7 @@ import { JUSTIFY_SPACE_BETWEEN, } from '@opentrons/components' import temporaryImg from '../../assets/images/placeholder_image_delete.png' +import { BUTTON_LINK_STYLE } from '../../atoms' interface WizardBodyProps { stepNumber: number @@ -83,11 +84,8 @@ export function WizardBody(props: WizardBodyProps): JSX.Element { justifyContent={JUSTIFY_SPACE_BETWEEN} > {goBack != null ? ( - - + + {t('go_back')} diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/CreateNewProtocol.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/CreateNewProtocol.test.tsx deleted file mode 100644 index 8ca87d9d825..00000000000 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/CreateNewProtocol.test.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { it } from 'vitest' - -it.todo('write test for CreateNewProtocol') diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx index 8b2e28bdada..753965c6300 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx @@ -6,7 +6,6 @@ import uniq from 'lodash/uniq' import mapValues from 'lodash/mapValues' import { yupResolver } from '@hookform/resolvers/yup' import { useDispatch, useSelector } from 'react-redux' -import { useTranslation } from 'react-i18next' import { useForm } from 'react-hook-form' import { useNavigate } from 'react-router-dom' import { @@ -22,10 +21,7 @@ import { getAreSlotsAdjacent, } from '@opentrons/shared-data' import { Box, COLORS } from '@opentrons/components' -import { - actions as fileActions, - selectors as loadFileSelectors, -} from '../../load-file' +import { actions as fileActions } from '../../load-file' import { uuid } from '../../utils' import * as labwareDefSelectors from '../../labware-defs/selectors' import * as labwareDefActions from '../../labware-defs/actions' @@ -38,6 +34,7 @@ import { createDeckFixture, toggleIsGripperRequired, } from '../../step-forms/actions/additionalItems' +import { getNewProtocolModal } from '../../navigation/selectors' import { SelectRobot } from './SelectRobot' import { SelectPipettes } from './SelectPipettes' import { SelectGripper } from './SelectGripper' @@ -153,12 +150,11 @@ const validationSchema: any = Yup.object().shape({ }) export function CreateNewProtocolWizard(): JSX.Element | null { - const { t } = useTranslation(['modal', 'alert']) - const hasUnsavedChanges = useSelector(loadFileSelectors.getHasUnsavedChanges) + const navigate = useNavigate() + const showWizard = useSelector(getNewProtocolModal) const customLabware = useSelector( labwareDefSelectors.getCustomLabwareDefsByURI ) - const navigate = useNavigate() const [currentStepIndex, setCurrentStepIndex] = React.useState(0) const [wizardSteps, setWizardSteps] = React.useState( WIZARD_STEPS @@ -166,6 +162,12 @@ export function CreateNewProtocolWizard(): JSX.Element | null { const dispatch = useDispatch>() + React.useEffect(() => { + if (!showWizard) { + navigate('/overview') + } + }, [showWizard]) + const createProtocolFile = (values: WizardFormState): void => { navigate('/overview') @@ -217,144 +219,140 @@ export function CreateNewProtocolWizard(): JSX.Element | null { } const newProtocolFields = values.fields - if ( - !hasUnsavedChanges || - window.confirm(t('alert:confirm_create_new') as string) - ) { - dispatch(fileActions.createNewProtocol(newProtocolFields)) - const pipettesById: Record = pipettes.reduce( - (acc, pipette) => ({ ...acc, [uuid()]: pipette }), - {} + dispatch(fileActions.createNewProtocol(newProtocolFields)) + const pipettesById: Record = pipettes.reduce( + (acc, pipette) => ({ ...acc, [uuid()]: pipette }), + {} + ) + // create custom labware + mapValues(customLabware, labwareDef => + dispatch( + labwareDefActions.createCustomLabwareDefAction({ + def: labwareDef, + }) ) - // create custom labware - mapValues(customLabware, labwareDef => - dispatch( - labwareDefActions.createCustomLabwareDefAction({ - def: labwareDef, + ) + // create new pipette entities + dispatch( + stepFormActions.createPipettes( + mapValues( + pipettesById, + (p: PipetteOnDeck, id: string): NormalizedPipette => ({ + // @ts-expect-error(sa, 2021-6-22): id will always get overwritten + id, + ...omit(p, 'mount'), }) ) ) - // create new pipette entities - dispatch( - stepFormActions.createPipettes( - mapValues( + ) + // update pipette locations in initial deck setup step + dispatch( + steplistActions.changeSavedStepForm({ + stepId: INITIAL_DECK_SETUP_STEP_ID, + update: { + pipetteLocationUpdate: mapValues( pipettesById, - (p: PipetteOnDeck, id: string): NormalizedPipette => ({ - // @ts-expect-error(sa, 2021-6-22): id will always get overwritten - id, - ...omit(p, 'mount'), - }) - ) - ) - ) - // update pipette locations in initial deck setup step + (p: typeof pipettesById[keyof typeof pipettesById]) => p.mount + ), + }, + }) + ) + + // add trash + if (values.additionalEquipment.includes('trashBin')) { + // defaulting trash to appropriate locations dispatch( - steplistActions.changeSavedStepForm({ - stepId: INITIAL_DECK_SETUP_STEP_ID, - update: { - pipetteLocationUpdate: mapValues( - pipettesById, - (p: typeof pipettesById[keyof typeof pipettesById]) => p.mount - ), - }, - }) + createDeckFixture( + 'trashBin', + values.fields.robotType === FLEX_ROBOT_TYPE + ? getTrashSlot(values) + : 'cutout12' + ) ) + } - // add trash - if (values.additionalEquipment.includes('trashBin')) { - // defaulting trash to appropriate locations - dispatch( - createDeckFixture( - 'trashBin', - values.fields.robotType === FLEX_ROBOT_TYPE - ? getTrashSlot(values) - : 'cutout12' - ) + // add waste chute + if (values.additionalEquipment.includes('wasteChute')) { + dispatch(createDeckFixture('wasteChute', WASTE_CHUTE_CUTOUT)) + } + // add staging areas + const stagingAreas = values.additionalEquipment.filter( + equipment => equipment === 'stagingArea' + ) + if (stagingAreas.length > 0) { + stagingAreas.forEach((_, index) => { + return dispatch( + createDeckFixture('stagingArea', STAGING_AREA_CUTOUTS[index]) ) - } + }) + } - // add waste chute - if (values.additionalEquipment.includes('wasteChute')) { - dispatch(createDeckFixture('wasteChute', WASTE_CHUTE_CUTOUT)) + // create modules + // sort so modules with slot are created first + // then modules without a slot are generated in remaining available slots + modules.sort((a, b) => { + if (a.slot == null && b.slot != null) { + return 1 } - // add staging areas - const stagingAreas = values.additionalEquipment.filter( - equipment => equipment === 'stagingArea' - ) - if (stagingAreas.length > 0) { - stagingAreas.forEach((_, index) => { - return dispatch( - createDeckFixture('stagingArea', STAGING_AREA_CUTOUTS[index]) - ) - }) + if (b.slot == null && a.slot != null) { + return -1 } + return 0 + }) - // create modules - // sort so modules with slot are created first - // then modules without a slot are generated in remaining available slots - modules.sort((a, b) => { - if (a.slot == null && b.slot != null) { - return 1 - } - if (b.slot == null && a.slot != null) { - return -1 - } - return 0 - }) - - modules.forEach(moduleArgs => { - return moduleArgs.slot != null - ? dispatch(stepFormActions.createModule(moduleArgs)) - : dispatch( - createModuleWithNoSlot({ - model: moduleArgs.model, - type: moduleArgs.type, - isMagneticBlock: moduleArgs.type === MAGNETIC_BLOCK_TYPE, - }) - ) - }) + modules.forEach(moduleArgs => { + return moduleArgs.slot != null + ? dispatch(stepFormActions.createModule(moduleArgs)) + : dispatch( + createModuleWithNoSlot({ + model: moduleArgs.model, + type: moduleArgs.type, + isMagneticBlock: moduleArgs.type === MAGNETIC_BLOCK_TYPE, + }) + ) + }) - // add gripper - if (values.additionalEquipment.includes('gripper')) { - dispatch(toggleIsGripperRequired()) - } + // add gripper + if (values.additionalEquipment.includes('gripper')) { + dispatch(toggleIsGripperRequired()) + } - // auto-generate assigned tipracks for pipettes - const newTiprackModels: string[] = uniq( - pipettes.flatMap(pipette => pipette.tiprackDefURI) - ) - const hasMagneticBlock = modules.some( - module => module.type === MAGNETIC_BLOCK_TYPE - ) - const FLEX_MIDDLE_SLOTS = hasMagneticBlock ? [] : ['C2', 'B2', 'A2'] - const hasOt2TC = modules.find( - module => module.type === THERMOCYCLER_MODULE_TYPE - ) - const heaterShakerSlot = modules.find( - module => module.type === HEATERSHAKER_MODULE_TYPE - )?.slot - const OT2_MIDDLE_SLOTS = hasOt2TC ? ['2', '5'] : ['2', '5', '8', '11'] - const modifiedOt2Slots = OT2_MIDDLE_SLOTS.filter(slot => - heaterShakerSlot != null - ? !getAreSlotsAdjacent(heaterShakerSlot, slot) - : slot + // auto-generate assigned tipracks for pipettes + const newTiprackModels: string[] = uniq( + pipettes.flatMap(pipette => pipette.tiprackDefURI) + ) + const hasMagneticBlock = modules.some( + module => module.type === MAGNETIC_BLOCK_TYPE + ) + const FLEX_MIDDLE_SLOTS = hasMagneticBlock ? [] : ['C2', 'B2', 'A2'] + const hasOt2TC = modules.find( + module => module.type === THERMOCYCLER_MODULE_TYPE + ) + const heaterShakerSlot = modules.find( + module => module.type === HEATERSHAKER_MODULE_TYPE + )?.slot + const OT2_MIDDLE_SLOTS = hasOt2TC ? ['2', '5'] : ['2', '5', '8', '11'] + const modifiedOt2Slots = OT2_MIDDLE_SLOTS.filter(slot => + heaterShakerSlot != null + ? !getAreSlotsAdjacent(heaterShakerSlot, slot) + : slot + ) + newTiprackModels.forEach((tiprackDefURI, index) => { + dispatch( + labwareIngredActions.createContainer({ + slot: + values.fields.robotType === FLEX_ROBOT_TYPE + ? FLEX_MIDDLE_SLOTS[index] + : modifiedOt2Slots[index], + labwareDefURI: tiprackDefURI, + adapterUnderLabwareDefURI: + values.pipettesByMount.left.pipetteName === 'p1000_96' + ? adapter96ChannelDefUri + : undefined, + }) ) - newTiprackModels.forEach((tiprackDefURI, index) => { - dispatch( - labwareIngredActions.createContainer({ - slot: - values.fields.robotType === FLEX_ROBOT_TYPE - ? FLEX_MIDDLE_SLOTS[index] - : modifiedOt2Slots[index], - labwareDefURI: tiprackDefURI, - adapterUnderLabwareDefURI: - values.pipettesByMount.left.pipetteName === 'p1000_96' - ? adapter96ChannelDefUri - : undefined, - }) - ) - }) - } + }) + dispatch(labwareIngredActions.generateNewProtocol({ isNewProtocol: true })) } @@ -370,7 +368,7 @@ export function CreateNewProtocolWizard(): JSX.Element | null { } } - return ( + return showWizard ? ( - ) + ) : null } interface CreateFileFormProps { diff --git a/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx b/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx index 4bf071027fd..b340fc6d471 100644 --- a/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx +++ b/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx @@ -1,16 +1,18 @@ import * as React from 'react' -import { describe, it, vi, beforeEach } from 'vitest' +import { describe, it, vi, beforeEach, expect } from 'vitest' import { MemoryRouter } from 'react-router-dom' -import { screen } from '@testing-library/react' +import { screen, fireEvent } from '@testing-library/react' import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { loadProtocolFile } from '../../../load-file/actions' import { getFileMetadata } from '../../../file-data/selectors' +import { toggleNewProtocolModal } from '../../../navigation/actions' import { Landing } from '../index' vi.mock('../../../load-file/actions') vi.mock('../../../file-data/selectors') +vi.mock('../../../navigation/actions') const render = () => { return renderWithProviders( @@ -25,7 +27,7 @@ const render = () => { describe('Landing', () => { beforeEach(() => { - vi.mocked(getFileMetadata).mockReturnValue({ created: 123 }) + vi.mocked(getFileMetadata).mockReturnValue({}) vi.mocked(loadProtocolFile).mockReturnValue(vi.fn()) }) it('renders the landing page image and text', () => { @@ -35,7 +37,8 @@ describe('Landing', () => { screen.getByText( 'The easiest way to automate liquid handling on your Opentrons robot. No code required.' ) - screen.getByRole('button', { name: 'Create a protocol' }) + fireEvent.click(screen.getByRole('button', { name: 'Create a protocol' })) + expect(vi.mocked(toggleNewProtocolModal)).toHaveBeenCalled() screen.getByText('Edit existing protocol') screen.getByRole('img', { name: 'welcome image' }) }) diff --git a/protocol-designer/src/pages/Landing/index.tsx b/protocol-designer/src/pages/Landing/index.tsx index 61c590462ed..47674bddda3 100644 --- a/protocol-designer/src/pages/Landing/index.tsx +++ b/protocol-designer/src/pages/Landing/index.tsx @@ -13,8 +13,10 @@ import { StyledText, TYPOGRAPHY, } from '@opentrons/components' +import { BUTTON_LINK_STYLE } from '../../atoms' import { actions as loadFileActions } from '../../load-file' import { getFileMetadata } from '../../file-data/selectors' +import { toggleNewProtocolModal } from '../../navigation/actions' import welcomeImage from '../../assets/images/welcome_page.png' import type { ThunkDispatch } from '../../types' @@ -64,6 +66,9 @@ export function Landing(): JSX.Element { {t('no-code-required')} { + dispatch(toggleNewProtocolModal(true)) + }} marginY={SPACING.spacing32} buttonText={ @@ -75,9 +80,11 @@ export function Landing(): JSX.Element { /> - - {t('edit_existing')} - + + + {t('edit_existing')} + + diff --git a/protocol-designer/src/pages/ProtocolOverview/DeckThumbnail.tsx b/protocol-designer/src/pages/ProtocolOverview/DeckThumbnail.tsx index 3036d1fe5de..b37fa5897fe 100644 --- a/protocol-designer/src/pages/ProtocolOverview/DeckThumbnail.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/DeckThumbnail.tsx @@ -44,7 +44,6 @@ const OT2_STANDARD_DECK_VIEW_LAYER_BLOCK_LIST: string[] = [ ] const lightFill = COLORS.grey35 -const darkFill = COLORS.grey60 interface DeckThumbnailProps { hoverSlot: DeckSlotId | null @@ -135,7 +134,6 @@ export function DeckThumbnail(props: DeckThumbnailProps): JSX.Element { deckDefinition={deckDef} showExpansion={cutoutId === 'cutoutA1'} fixtureBaseColor={lightFill} - slotClipColor={darkFill} /> ) : null })} @@ -144,7 +142,6 @@ export function DeckThumbnail(props: DeckThumbnailProps): JSX.Element { key={fixture.id} cutoutId={fixture.location as StagingAreaLocation} deckDefinition={deckDef} - slotClipColor={darkFill} fixtureBaseColor={lightFill} /> ))} @@ -181,7 +178,6 @@ export function DeckThumbnail(props: DeckThumbnailProps): JSX.Element { key={fixture.id} cutoutId={fixture.location as typeof WASTE_CHUTE_CUTOUT} deckDefinition={deckDef} - slotClipColor={darkFill} fixtureBaseColor={lightFill} /> ))} diff --git a/protocol-designer/src/pages/ProtocolOverview/index.tsx b/protocol-designer/src/pages/ProtocolOverview/index.tsx index 3f0b17be10c..2be14aa9b56 100644 --- a/protocol-designer/src/pages/ProtocolOverview/index.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/index.tsx @@ -8,7 +8,6 @@ import { css } from 'styled-components' import { ALIGN_CENTER, Btn, - COLORS, DIRECTION_COLUMN, Flex, InfoScreen, @@ -45,6 +44,7 @@ import { resetScrollElements } from '../../ui/steps/utils' import { useBlockingHint } from '../../components/Hints/useBlockingHint' import { v8WarningContent } from '../../components/FileSidebar/FileSidebar' import { MaterialsListModal } from '../../organisms/MaterialsListModal' +import { BUTTON_LINK_STYLE } from '../../atoms' import { EditProtocolMetadataModal, EditInstrumentsModal, @@ -322,6 +322,7 @@ export function ProtocolOverview(): JSX.Element { onClick={() => { setShowEditMetadataModal(true) }} + css={BUTTON_LINK_STYLE} data-testid="ProtocolOverview_MetadataEditButton" > @@ -358,6 +359,7 @@ export function ProtocolOverview(): JSX.Element { onClick={() => { setShowEditInstrumentsModal(true) }} + css={BUTTON_LINK_STYLE} > {t('edit')} @@ -438,16 +440,13 @@ export function ProtocolOverview(): JSX.Element { } - content={liquid.description ?? t('n/a')} + content={liquid.description ?? t('na')} /> ) ) ) : ( - + )} @@ -459,10 +458,7 @@ export function ProtocolOverview(): JSX.Element { {Object.keys(savedStepForms).length <= 1 ? ( - + ) : ( { setShowMaterialsListModal(true) }} + css={BUTTON_LINK_STYLE} > {t('materials_list')}