Skip to content

Commit

Permalink
feat(protocol-designer, components): add SlotInformation component (#…
Browse files Browse the repository at this point in the history
…16043)

* feat(protocol-designer, components): add SlotInformation component
  • Loading branch information
koji authored Aug 19, 2024
1 parent e180383 commit 177001f
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 10 deletions.
1 change: 1 addition & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
stories: [
'../components/**/*.stories.@(js|jsx|ts|tsx)',
'../app/**/*.stories.@(js|jsx|ts|tsx)',
'../protocol-designer/**/*.stories.@(js|jsx|ts|tsx)',
'../opentrons-ai-client/**/*.stories.@(js|jsx|ts|tsx)',
],

Expand Down
21 changes: 14 additions & 7 deletions .storybook/preview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ global.APP_SHELL_REMOTE = {
ipcRenderer: {
on: (topic, cb) => {},
invoke: (callname, args) => {},
send: (message, payload) => {}
send: (message, payload) => {},
},
}
global._PKG_VERSION_ = '0.0.0-storybook'
Expand All @@ -29,8 +29,8 @@ export const customViewports = {
type: 'desktop',
styles: {
width: '600px',
height: '450px'
}
height: '450px',
},
},
desktopSmall: {
// A size typically used in figma app backgrounds, useful for viewing
Expand All @@ -39,9 +39,9 @@ export const customViewports = {
type: 'desktop',
styles: {
width: '1024px',
height: '700px'
}
}
height: '700px',
},
},
}

export const parameters = {
Expand All @@ -50,7 +50,14 @@ export const parameters = {
options: {
storySort: {
method: 'alphabetical',
order: ['Design Tokens', 'Library', 'App', 'ODD', 'AI'],
order: [
'Design Tokens',
'Library',
'App',
'ODD',
'Protocol-Designer',
'AI',
],
},
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { SPACING } from '../../../ui-style-constants'

interface ListItemDescriptorProps {
type: 'default' | 'mini'
description: JSX.Element
content: JSX.Element
description: JSX.Element | string
content: JSX.Element | string
}

export const ListItemDescriptor = (
Expand Down
1 change: 1 addition & 0 deletions components/src/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './CheckboxField'
export * from './Chip'
export * from './InputField'
export * from './ListItem'
export * from './ListItem/ListItemChildren//ListItemDescriptor'
export * from './MenuList'
export * from './MenuList/MenuItem'
export * from './MenuList/OverflowBtn'
Expand Down
5 changes: 5 additions & 0 deletions protocol-designer/src/localization/en/shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@
"go_back": "Go back",
"import_existing": "Import existing protocol",
"import": "Import",
"labware": "Labware",
"liquid": "Liquid",
"module": "Module",
"next": "next",
"ninety_six_channel": "96-Channel",
"no-code-solution": "A no-code solution to create protocols that x, y and z meaning for your lab and workflow.",
"no": "No",
"none": "None",
"one_channel": "1-Channel",
"opentrons_flex": "Opentrons Flex",
"opentrons": "Opentrons",
"ot2": "Opentrons OT-2",
"protocol_designer": "Protocol Designer",
"remove": "remove",
"slot_stack_information": "Slot Stack Information",
"step_count": "Step {{current}}",
"step": "Step {{current}} / {{max}}",
"version": "Version # {{version}}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react'
import { Flex } from '@opentrons/components'
import { I18nextProvider } from 'react-i18next'
import { i18n } from '../../localization'
import { SlotInformation as SlotInformationComponent } from '.'

import type { Meta, StoryObj } from '@storybook/react'

const mockLocations = [
'A1',
'A2',
'A3',
'B1',
'B2',
'B3',
'C1',
'C2',
'C3',
'D1',
'D2',
'D3',
]

const mockLiquids = ['Mastermix', 'Ethanol', 'Water']
const mockLabwares = ['96 Well Plate', 'Adapter']
const mockModules = ['Thermocycler Module Gen2', 'Heater-Shaker Module']

const meta: Meta<typeof SlotInformationComponent> = {
title: 'Protocol-Designer/Organisms/SlotInformation',
component: SlotInformationComponent,
argTypes: {
location: {
control: {
type: 'select',
},
options: mockLocations,
},
},
decorators: [
Story => (
<I18nextProvider i18n={i18n}>
<Flex width="21rem">
<Story />
</Flex>
</I18nextProvider>
),
],
}

export default meta

type Story = StoryObj<typeof SlotInformationComponent>

export const SlotInformation: Story = {
args: {
location: 'A1',
liquids: mockLiquids,
labwares: mockLabwares,
modules: mockModules,
},
}

export const SlotInformationEmpty: Story = {
args: {
location: 'A1',
liquids: [],
labwares: [],
modules: [],
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from 'react'
import { describe, it, beforeEach, expect } from 'vitest'
import { screen } from '@testing-library/react'

import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../localization'

import { SlotInformation } from '..'

const mockLiquids = ['Mastermix', 'Ethanol', 'Water']
const mockLabwares = ['96 Well Plate', 'Adapter']
const mockModules = ['Thermocycler Module Gen2', 'Heater-Shaker Module']

const render = (props: React.ComponentProps<typeof SlotInformation>) => {
return renderWithProviders(<SlotInformation {...props} />, {
i18nInstance: i18n,
})
}

describe('SlotInformation', () => {
let props: React.ComponentProps<typeof SlotInformation>

beforeEach(() => {
props = {
location: 'A1',
liquids: [],
labwares: [],
modules: [],
}
})

it('should render DeckInfoLabel and title', () => {
render(props)
screen.getByText('A1')
screen.getByText('Slot Stack Information')
})

it('should render liquid, labware, and module', () => {
render(props)
screen.getByText('Liquid')
screen.getByText('Labware')
screen.getByText('Module')
expect(screen.getAllByText('None').length).toBe(3)
})

it('should render info of liquid, labware, and module', () => {
props = {
...props,
liquids: mockLiquids,
labwares: mockLabwares,
modules: mockModules,
}
render(props)
expect(screen.getAllByText('Liquid').length).toBe(mockLiquids.length)
expect(screen.getAllByText('Labware').length).toBe(mockLabwares.length)
expect(screen.getAllByText('Module').length).toBe(mockModules.length)
screen.getByText('Mastermix')
screen.getByText('Ethanol')
screen.getByText('Water')
screen.getByText('96 Well Plate')
screen.getByText('Adapter')
screen.getByText('Thermocycler Module Gen2')
screen.getByText('Heater-Shaker Module')
})
})
88 changes: 88 additions & 0 deletions protocol-designer/src/organisms/SlotInformation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
ALIGN_CENTER,
DeckInfoLabel,
DIRECTION_COLUMN,
Flex,
ListItem,
ListItemDescriptor,
SPACING,
StyledText,
} from '@opentrons/components'

interface SlotInformationProps {
location: string
liquids?: string[]
labwares?: string[]
modules?: string[]
}

export const SlotInformation: React.FC<SlotInformationProps> = ({
location,
liquids = [],
labwares = [],
modules = [],
}) => {
const { t } = useTranslation('shared')
return (
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing12}
width="100%"
>
<Flex gridGap={SPACING.spacing8} alignItems={ALIGN_CENTER}>
<DeckInfoLabel deckLabel={location} />
<StyledText desktopStyle="headingSmallBold">
{t('slot_stack_information')}
</StyledText>
</Flex>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing4}>
<StackInfoList title={t('liquid')} items={liquids} />
<StackInfoList title={t('labware')} items={labwares} />
<StackInfoList title={t('module')} items={modules} />
</Flex>
</Flex>
)
}

interface StackInfoListProps {
title: string
items: string[]
}

function StackInfoList({ title, items }: StackInfoListProps): JSX.Element {
return (
<>
{items.length > 0 ? (
items.map((item, index) => (
<StackInfo
key={`${title}_${index}`}
title={title}
stackInformation={item}
/>
))
) : (
<StackInfo title={title} />
)}
</>
)
}

interface StackInfoProps {
title: string
stackInformation?: string
}

function StackInfo({ title, stackInformation }: StackInfoProps): JSX.Element {
const { t } = useTranslation('shared')
return (
<ListItem type="noActive">
<ListItemDescriptor
type="mini"
content={stackInformation ?? t('none')}
description={title}
/>
</ListItem>
)
}
2 changes: 1 addition & 1 deletion protocol-designer/src/organisms/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
console.log('organisms for new components')
export * from './SlotInformation'

0 comments on commit 177001f

Please sign in to comment.