Skip to content

Commit

Permalink
Merge branch 'edge' into EXEC-655-store-commands-error-list-in-db
Browse files Browse the repository at this point in the history
  • Loading branch information
TamarZanzouri authored Nov 6, 2024
2 parents a88e95d + ac00351 commit 6fea08a
Show file tree
Hide file tree
Showing 27 changed files with 748 additions and 4 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
"flex_gripper": "Flex Gripper",
"flex_gripper_no_label": "No, do not use the Flex Gripper",
"modules_title": "Modules",
"no_modules_added_yet": "No modules added yet",
"modules_remove_label": "remove",
"modules_adapter_label": "Adapter",
"heater_shaker_module_v1": "Heater-Shaker Module GEN1",
"temperature_module_v2": "Temperature Module GEN2",
"thermocycler_module_v2": "Thermocycler Module GEN2",
"magnetic_module_v1": "Magnetic Block GEN1",
"labware_liquids_title": "Labware & Liquids",
"steps_title": "Steps"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../i18n'
import { ControlledEmptySelectorButtonGroup } from '../index'
import { describe, it, expect } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { FormProvider, useForm } from 'react-hook-form'
import { MODULES_FIELD_NAME } from '../../../organisms/ModulesSection'
import type { DisplayModules } from '../../../organisms/ModulesSection'

const modulesMock: DisplayModules[] = [
{
type: 'heaterShakerModuleType',
model: 'heaterShakerModuleV1',
name: 'Heater-Shaker Module GEN1',
},
{
type: 'temperatureModuleType',
model: 'temperatureModuleV2',
name: 'Temperature Module GEN2',
},
]

const TestFormProviderComponent = () => {
const methods = useForm({})

const selectedValue = methods.watch(MODULES_FIELD_NAME) ?? []

return (
<FormProvider {...methods}>
<ControlledEmptySelectorButtonGroup modules={modulesMock} />

{'selected values: ' + selectedValue.map((m: DisplayModules) => m.name)}
</FormProvider>
)
}

const render = (): ReturnType<typeof renderWithProviders> => {
return renderWithProviders(<TestFormProviderComponent />, {
i18nInstance: i18n,
})
}

describe('ControlledEmptySelectorButtonGroup', () => {
it('should render ControlledEmptySelectorButtonGroup component', () => {
render()

screen.getByText('Heater-Shaker Module GEN1')
screen.getByText('Temperature Module GEN2')
})

it('should add the value when the button is clicked', async () => {
render()

const button1 = screen.getByText('Heater-Shaker Module GEN1')

expect(
screen.queryByText(
'selected values: Heater-Shaker Module GEN1,Temperature Module GEN2'
)
).not.toBeInTheDocument()

fireEvent.click(button1)

const button2 = screen.getByText('Temperature Module GEN2')

fireEvent.click(button2)

expect(
await screen.findByText(
'selected values: Heater-Shaker Module GEN1,Temperature Module GEN2'
)
).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Flex, WRAP, SPACING, EmptySelectorButton } from '@opentrons/components'
import { Controller, useFormContext } from 'react-hook-form'
import type { DisplayModules } from '../../organisms/ModulesSection'
import { MODULES_FIELD_NAME } from '../../organisms/ModulesSection'

export function ControlledEmptySelectorButtonGroup({
modules,
}: {
modules: DisplayModules[]
}): JSX.Element | null {
const { watch } = useFormContext()
const modulesWatch: DisplayModules[] = watch(MODULES_FIELD_NAME) ?? []

return (
<Controller
defaultValue={[]}
name={MODULES_FIELD_NAME}
render={({ field }) => {
return (
<Flex flexWrap={WRAP} gap={SPACING.spacing8}>
{modules.map(module => (
<EmptySelectorButton
key={module.type}
iconName="plus"
onClick={() => {
if (modulesWatch.some(m => m.type === module.type)) {
return
}
field.onChange([...modulesWatch, module])
}}
text={module.name}
textAlignment="left"
/>
))}
</Flex>
)
}}
/>
)
}
80 changes: 80 additions & 0 deletions opentrons-ai-client/src/molecules/ModelDiagram/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { css } from 'styled-components'
import {
MAGNETIC_MODULE_TYPE,
TEMPERATURE_MODULE_TYPE,
THERMOCYCLER_MODULE_TYPE,
MAGNETIC_MODULE_V1,
MAGNETIC_MODULE_V2,
TEMPERATURE_MODULE_V1,
TEMPERATURE_MODULE_V2,
THERMOCYCLER_MODULE_V1,
HEATERSHAKER_MODULE_TYPE,
HEATERSHAKER_MODULE_V1,
THERMOCYCLER_MODULE_V2,
MAGNETIC_BLOCK_TYPE,
MAGNETIC_BLOCK_V1,
ABSORBANCE_READER_TYPE,
ABSORBANCE_READER_V1,
} from '@opentrons/shared-data'

import magdeck_gen1 from '../../assets/images/modules/magdeck_gen1.png'
import magdeck_gen2 from '../../assets/images/modules/magdeck_gen2.png'
import tempdeck_gen1 from '../../assets/images/modules/tempdeck_gen1.png'
import temp_deck_gen_2_transparent from '../../assets/images/modules/temp_deck_gen_2_transparent.png'
import thermocycler from '../../assets/images/modules/thermocycler.png'
import thermocycler_gen2 from '../../assets/images/modules/thermocycler_gen2.png'
import heater_shaker_module_transparent from '../../assets/images/modules/heater_shaker_module_transparent.png'
import mag_block from '../../assets/images/modules/MagneticBlock_GEN1_HERO.png'
import type { ModuleType, ModuleModel } from '@opentrons/shared-data'

interface Props {
type: ModuleType
model: ModuleModel
}

type ModuleImg = {
[type in ModuleType]: {
[model in ModuleModel]?: string
}
}

const MODULE_IMG_BY_TYPE: ModuleImg = {
[MAGNETIC_MODULE_TYPE]: {
[MAGNETIC_MODULE_V1]: magdeck_gen1,
[MAGNETIC_MODULE_V2]: magdeck_gen2,
},
[TEMPERATURE_MODULE_TYPE]: {
[TEMPERATURE_MODULE_V1]: tempdeck_gen1,
[TEMPERATURE_MODULE_V2]: temp_deck_gen_2_transparent,
},
[THERMOCYCLER_MODULE_TYPE]: {
[THERMOCYCLER_MODULE_V1]: thermocycler,
[THERMOCYCLER_MODULE_V2]: thermocycler_gen2,
},
[HEATERSHAKER_MODULE_TYPE]: {
[HEATERSHAKER_MODULE_V1]: heater_shaker_module_transparent,
},
[MAGNETIC_BLOCK_TYPE]: {
[MAGNETIC_BLOCK_V1]: mag_block,
},
[ABSORBANCE_READER_TYPE]: {
// TODO (AA): update absorbance reader image
[ABSORBANCE_READER_V1]: heater_shaker_module_transparent,
},
}

const IMAGE_MAX_WIDTH = '96px'
export function ModuleDiagram(props: Props): JSX.Element {
const model = MODULE_IMG_BY_TYPE[props.type][props.model]
return (
<img
css={css`
max-width: ${IMAGE_MAX_WIDTH};
width: 100%;
height: auto;
`}
src={model}
alt={props.type}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../i18n'
import { ModuleListItemGroup } from '../index'
import { describe, it, expect } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { FormProvider, useForm } from 'react-hook-form'
import type { DisplayModules } from '../../../organisms/ModulesSection'

const modulesMock: DisplayModules[] = [
{
type: 'heaterShakerModuleType',
model: 'heaterShakerModuleV1',
name: 'Heater-Shaker Module GEN1',
},
{
type: 'temperatureModuleType',
model: 'temperatureModuleV2',
name: 'Temperature Module GEN2',
},
]

const TestFormProviderComponent = () => {
const methods = useForm({
defaultValues: {
modules: modulesMock,
},
})

return (
<FormProvider {...methods}>
<ModuleListItemGroup />
</FormProvider>
)
}

const render = (): ReturnType<typeof renderWithProviders> => {
return renderWithProviders(<TestFormProviderComponent />, {
i18nInstance: i18n,
})
}

describe('ModuleListItemGroup', () => {
it('should render ModuleListItemGroup component', () => {
render()

expect(screen.getAllByText('Adapter').length).toBe(2)
expect(screen.getAllByText('remove').length).toBe(2)

screen.getByAltText('heaterShakerModuleType')
screen.getByText('Heater-Shaker Module GEN1')

screen.getByAltText('temperatureModuleType')
screen.getByText('Temperature Module GEN2')
})

it('should remove the list item if remove is clicked', async () => {
render()

const removeListItemButton = screen.getAllByText('remove')[0]

fireEvent.click(removeListItemButton)

expect(
screen.queryByText('Heater-Shaker Module GEN1')
).not.toBeInTheDocument()
})

it('should render the dropdown if adapters are available', () => {
render()

expect(screen.getAllByText('Choose an adapter').length).toBe(2)
})

it('should be able to select an adapter', () => {
render()

const dropdownButton = screen.getAllByText('Choose an adapter')[1]

fireEvent.click(dropdownButton)

const adapterOption = screen.getByText(
'Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap'
)

fireEvent.click(adapterOption)

expect(
screen.getByText(
'Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap'
)
).toBeInTheDocument()
})
})
Loading

0 comments on commit 6fea08a

Please sign in to comment.