-
Notifications
You must be signed in to change notification settings - Fork 187
feature(protocol-designer): Add stacker step form skeleton #20277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4c98bb7
cd92075
a1be733
17db914
ec84ede
0dae439
679f249
9a6b266
2229ff1
dc7fe5a
7a6f9fb
038b3c0
0f8ff18
92e86c8
fe2cee8
94621f8
3c6051d
df9a739
a297c52
38e6db2
72bc09a
705738c
f81982e
0b16f8f
b16ff04
77884e1
ec51827
ae46454
34171d7
2b433d9
a25e1aa
844e753
21ce32f
e755165
8abc0bb
913c97b
5edf09a
98f4557
e852f6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| div.container { | ||
| width: 318px; | ||
| padding: var(--spacing-16) var(--spacing-8); | ||
| border-radius: var(--border-radius-4); | ||
| background-color: var(--grey-20); | ||
| } | ||
|
|
||
| div.sub_title { | ||
| display: flex; | ||
| width: 100%; | ||
| flex-direction: column; | ||
| color: var(--grey-60); | ||
| gap: var(--spacing-8); | ||
| } | ||
|
|
||
| div.label { | ||
| display: flex; | ||
| width: 88px; | ||
| height: 24px; | ||
| align-items: center; | ||
| justify-content: center; | ||
| border-radius: var(--border-radius-4); | ||
| background-color: var(--transparent-black-80); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { LabwareDetailsWithCount } from './index' | ||
|
|
||
| import type { Meta, StoryObj } from '@storybook/react' | ||
|
|
||
| const meta: Meta<typeof LabwareDetailsWithCount> = { | ||
| title: 'Helix/Organisms/LabwareDetailsWithCount', | ||
| component: LabwareDetailsWithCount, | ||
| decorators: [ | ||
| Story => ( | ||
| <div> | ||
| <Story /> | ||
| </div> | ||
| ), | ||
| ], | ||
| } | ||
| export default meta | ||
|
|
||
| type Story = StoryObj<typeof LabwareDetailsWithCount> | ||
|
|
||
| export const LabwareDetailsWithCountStory: Story = { | ||
| args: { | ||
| title: 'Opentrons Flex 96 Tip Rack 1000 µL', | ||
| subTitle: 'With tip rack lid', | ||
| quantity: 1, | ||
| }, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { useTranslation } from 'react-i18next' | ||
| import { screen } from '@testing-library/react' | ||
| import { beforeEach, describe, expect, it, vi } from 'vitest' | ||
|
|
||
| import { LabwareDetailsWithCount } from '..' | ||
| import { renderWithProviders } from '../../../testing/utils' | ||
|
|
||
| import type { ComponentProps } from 'react' | ||
|
|
||
| vi.mock('react-i18next', () => ({ | ||
| useTranslation: vi.fn(), | ||
| initReactI18next: vi.fn(), | ||
| })) | ||
|
|
||
| vi.mock('i18next', () => { | ||
| return { | ||
| default: { | ||
| use: () => ({ init: vi.fn() }), | ||
| createInstance: () => ({ | ||
| use: () => ({ init: vi.fn() }), | ||
| init: vi.fn(), | ||
| t: (k: string) => k, | ||
| }), | ||
| init: vi.fn(), | ||
| t: (k: string) => k, | ||
| }, | ||
| } | ||
| }) | ||
| const render = (props: ComponentProps<typeof LabwareDetailsWithCount>) => { | ||
| return renderWithProviders(<LabwareDetailsWithCount {...props} />) | ||
| } | ||
| describe('LabwareDetailsWithCount', () => { | ||
| let props: ComponentProps<typeof LabwareDetailsWithCount> | ||
| const t = vi.fn(key => key) | ||
| beforeEach(() => { | ||
| props = { | ||
| title: 'Title', | ||
| subTitle: 'SubTitle', | ||
| quantity: 1, | ||
| } | ||
| vi.mocked(useTranslation).mockReturnValue({ t } as any) | ||
| }) | ||
|
|
||
| it('should render title, subTitle and label', () => { | ||
| render(props) | ||
| expect(screen.getByText('Title')).toBeInTheDocument() | ||
| expect(screen.getByText('SubTitle')).toBeInTheDocument() | ||
| expect(screen.getByText('quantity')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('should render title without subTitle and label', () => { | ||
| props.subTitle = undefined | ||
| props.quantity = undefined | ||
| render(props) | ||
| expect(screen.getByText('Title')).toBeInTheDocument() | ||
| expect(screen.queryByText('SubTitle')).not.toBeInTheDocument() | ||
| expect(screen.queryByText('quantity')).not.toBeInTheDocument() | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { useTranslation } from 'react-i18next' | ||
|
|
||
| import { StyledText, Tag } from '../../atoms' | ||
| import styles from './LabwareDetailsWithCount.module.css' | ||
|
|
||
| interface LabwareDetailsWithCountProps { | ||
| title: string | ||
| subTitle?: string | ||
| quantity?: number | ||
| } | ||
|
|
||
| export function LabwareDetailsWithCount({ | ||
| title, | ||
| subTitle, | ||
| quantity: label, | ||
| }: LabwareDetailsWithCountProps): JSX.Element { | ||
| const { t } = useTranslation('protocol_command_text') | ||
| return ( | ||
| <div className={styles.container}> | ||
| <StyledText desktopStyle="bodyDefaultRegular">{title}</StyledText> | ||
| <div className={styles.subTitle}> | ||
| <StyledText desktopStyle="bodyDefaultRegular">{subTitle}</StyledText> | ||
| </div> | ||
| {label != null ? ( | ||
| <div className={styles.label}> | ||
| <Tag type="default" text={t('quantity', { count: label })} /> | ||
| </div> | ||
| ) : null} | ||
| </div> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| export * from './CommandText' | ||
| export * from './DeckLabelSet' | ||
| export * from './FixtureOption' | ||
| export * from './LabwareDetailsWithCount' | ||
| export * from './LabwareInfoOverlay' | ||
| export * from './ProtocolDeck' | ||
| export * from './Toolbox' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -172,6 +172,7 @@ export type StepType = | |
| | 'pause' | ||
| | 'temperature' | ||
| | 'thermocycler' | ||
| | 'flexStacker' | ||
|
|
||
| export const stepIconsByType: Record<StepType, IconName> = { | ||
| absorbanceReader: 'ot-absorbance', | ||
|
|
@@ -186,6 +187,7 @@ export const stepIconsByType: Record<StepType, IconName> = { | |
| temperature: 'ot-temperature-v2', | ||
| thermocycler: 'ot-thermocycler', | ||
| heaterShaker: 'ot-heater-shaker', | ||
| flexStacker: 'ot-flex-stacker', | ||
| } | ||
| // ===== Unprocessed form types ===== | ||
| export interface AnnotationFields { | ||
|
|
@@ -498,6 +500,13 @@ export interface HydratedAbsorbanceReaderFormData extends AnnotationFields { | |
| wavelengths: string[] | ||
| } | ||
|
|
||
| // TODO(TZ, 2025-12-03): not fully flushed out, but this is the initial hydrated form data for the flex stacker form | ||
| export interface HydratedFlexStackerFormData extends AnnotationFields { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we're missing fields here. This should include the types of all the properties in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. left a comment about not being fully flushed |
||
| stepType: 'flexStacker' | ||
| id: string | ||
| moduleId: string | ||
| } | ||
|
|
||
| // fields used in TipPositionInput | ||
| export type TipZOffsetFields = | ||
| | 'aspirate_mmFromBottom' | ||
|
|
@@ -616,3 +625,4 @@ export type HydratedFormData = | |
| | HydratedPauseFormData | ||
| | HydratedTemperatureFormData | ||
| | HydratedThermocyclerFormData | ||
| | HydratedFlexStackerFormData | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,6 +62,7 @@ import { | |
| AbsorbanceReaderTools, | ||
| CameraTools, | ||
| CommentTools, | ||
| FlexStackerToolsContainer, | ||
| HeaterShakerTools, | ||
| MagnetTools, | ||
| MixTools, | ||
|
|
@@ -107,6 +108,7 @@ const STEP_FORM_MAP: StepFormMap = { | |
| comment: CommentTools, | ||
| camera: CameraTools, | ||
| absorbanceReader: AbsorbanceReaderTools, | ||
| flexStacker: FlexStackerToolsContainer, | ||
| } | ||
|
|
||
| // used to inform StepFormToolbox when to prompt user confirmation for overriding advanced settings | ||
|
|
@@ -535,7 +537,10 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element { | |
| desktopStyle="bodyLargeSemiBold" | ||
| css={LINE_CLAMP_TEXT_STYLE(2, true)} | ||
| > | ||
| {capitalizeFirstLetter(String(formData.stepName))} | ||
| {/* TODO: use module object from form.json instead */} | ||
| {formData.stepType === 'flexStacker' | ||
| ? t(`protocol_steps:${formData.stepType}`) | ||
| : capitalizeFirstLetter(String(formData.stepName))} | ||
|
Comment on lines
+540
to
+543
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry why do we need to special case the stacker? it could be right I am just not following
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we want to move away from |
||
| </StyledText> | ||
| </Flex> | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| .space_between { | ||
| display: flex; | ||
| justify-content: space-between; | ||
| } | ||
|
|
||
| div.container { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so I added the div bc the I wanted to make sure that this class is being appended to a div but its also a local css file so its not really a MUST |
||
| display: flex; | ||
| width: 100%; | ||
| flex-direction: column; | ||
| padding-top: var(--spacing-16); | ||
| gap: var(--spacing-8); | ||
| } | ||
|
|
||
| .padding_x { | ||
| padding: 0 var(--spacing-16); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.