Skip to content

Commit

Permalink
refactor(labware-creator): break apart footprint section (#7737)
Browse files Browse the repository at this point in the history
* test(labware-creator): add missing tests to sections

* refactor(labware-creator): break out footprint section into its own component

#7707
  • Loading branch information
shlokamin authored Apr 30, 2021
1 parent d3552eb commit e8f2ae9
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as React from 'react'
import { AlertItem } from '@opentrons/components'
import { getIsHidden } from '../../formSelectors'
import { IRREGULAR_LABWARE_ERROR } from '../../fields'
import {
getFormAlerts,
Props as FormAlertProps,
IrregularLabwareAlert,
} from '../utils/getFormAlerts'
import { when, resetAllWhenMocks } from 'jest-when'

jest.mock('../../formSelectors')

const getIsHiddenMock = getIsHidden as jest.MockedFunction<typeof getIsHidden>

describe('getFormAlerts', () => {
afterEach(() => {
resetAllWhenMocks()
})
it('should return null when all fields are hidden', () => {
when(getIsHiddenMock)
.expectCalledWith('labwareType', {} as any)
.mockReturnValue(true)
const props: FormAlertProps = {
values: {} as any,
fieldList: ['labwareType'],
touched: {},
errors: {},
}
expect(getFormAlerts(props)).toBe(null)
})
it('should return errors for the dirty fields with errors when fields are not hidden', () => {
when(getIsHiddenMock)
.calledWith('labwareType', {} as any)
.mockReturnValue(false)

when(getIsHiddenMock)
.calledWith('tubeRackInsertLoadName', {} as any)
.mockReturnValue(false)

const props: FormAlertProps = {
values: {} as any,
fieldList: ['labwareType', 'tubeRackInsertLoadName'],
touched: { labwareType: true, tubeRackInsertLoadName: true },
errors: {
labwareType: 'some error',
tubeRackInsertLoadName: IRREGULAR_LABWARE_ERROR,
},
}
const expectedErrors = [
<AlertItem key={'some error'} type="warning" title={'some error'} />,
// eslint-disable-next-line react/jsx-key
<IrregularLabwareAlert />,
]
expect(getFormAlerts(props)).toEqual(expectedErrors)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react'
import { FormikConfig } from 'formik'
import { when } from 'jest-when'
import { render, screen } from '@testing-library/react'
import { getDefaultFormState, LabwareFields } from '../../../fields'
import { Footprint } from '../../sections/Footprint'
import { getFormAlerts } from '../../utils/getFormAlerts'
import { TextField } from '../../TextField'
import { wrapInFormik } from '../../utils/wrapInFormik'
import { getXYDimensionAlerts } from '../../utils/getXYDimensionAlerts'

jest.mock('../../TextField')
jest.mock('../../utils/getFormAlerts')
jest.mock('../../utils/getXYDimensionAlerts')

const getFormAlertsMock = getFormAlerts as jest.MockedFunction<
typeof getFormAlerts
>
const textFieldMock = TextField as jest.MockedFunction<typeof TextField>

const getXYDimensionAlertsMock = getXYDimensionAlerts as jest.MockedFunction<
typeof getXYDimensionAlerts
>

const formikConfig: FormikConfig<LabwareFields> = {
initialValues: getDefaultFormState(),
onSubmit: jest.fn(),
}

describe('Footprint', () => {
beforeEach(() => {
textFieldMock.mockImplementation(args => {
if (args.name === 'footprintXDimension') {
return <div>footprintXDimension text field</div>
}
if (args.name === 'footprintYDimension') {
return <div>footprintYDimension text field</div>
}
throw new Error(
`Text field should have been called with footprintXDimension or footprintYDimension, instead got ${args.name} `
)
})

when(getFormAlertsMock)
.expectCalledWith({
values: getDefaultFormState(),
touched: {},
errors: {},
fieldList: ['footprintXDimension', 'footprintYDimension'],
})
.mockReturnValue([<div key="mock key">mock alerts</div>])

when(getXYDimensionAlertsMock)
.expectCalledWith(getDefaultFormState(), {})
.mockReturnValue(<div>mock getXYDimensionAlertsMock alerts</div>)
})

afterEach(() => {
jest.restoreAllMocks()
})
it('should render with the correct information', () => {
render(wrapInFormik(<Footprint />, formikConfig))
expect(screen.getByText('Footprint'))
expect(
screen.getByText(
'The footprint measurement helps determine if the labware fits firmly into the slots on the OT-2 deck.'
)
)
expect(screen.getByText('mock alerts'))
expect(screen.getByText('footprintXDimension text field'))
expect(screen.getByText('footprintYDimension text field'))
expect(screen.getByText('mock getXYDimensionAlertsMock alerts'))
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react'
import { FormikConfig } from 'formik'
import { when } from 'jest-when'
import { render, screen } from '@testing-library/react'
import {
getDefaultFormState,
LabwareFields,
yesNoOptions,
} from '../../../fields'
import { Regularity } from '../../sections/Regularity'
import { getFormAlerts } from '../../utils/getFormAlerts'
import { RadioField } from '../../RadioField'
import { wrapInFormik } from '../../utils/wrapInFormik'

jest.mock('../../RadioField')
jest.mock('../../utils/getFormAlerts')

const getFormAlertsMock = getFormAlerts as jest.MockedFunction<
typeof getFormAlerts
>
const radioFieldMock = RadioField as jest.MockedFunction<typeof RadioField>

const formikConfig: FormikConfig<LabwareFields> = {
initialValues: getDefaultFormState(),
onSubmit: jest.fn(),
}

describe('Regularity', () => {
beforeEach(() => {
radioFieldMock.mockImplementation(args => {
expect(args).toEqual({ name: 'homogeneousWells', options: yesNoOptions })
return <div>homogeneousWells radio group</div>
})

when(getFormAlertsMock)
.expectCalledWith({
values: getDefaultFormState(),
touched: {},
errors: {},
fieldList: ['homogeneousWells'],
})
.mockReturnValue([<div key="mock key">mock alerts</div>])
})

afterEach(() => {
jest.restoreAllMocks()
})
it('should render with the correct information', () => {
render(wrapInFormik(<Regularity />, formikConfig))
expect(screen.getByText('Regularity'))
expect(screen.getByText('mock alerts'))
expect(screen.getByText('homogeneousWells radio group'))
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as React from 'react'
import { useFormikContext } from 'formik'
import { makeMaskToDecimal } from '../../fieldMasks'
import { LabwareFields } from '../../fields'
import { getFormAlerts } from '../utils/getFormAlerts'
import { getXYDimensionAlerts } from '../utils/getXYDimensionAlerts'
import { TextField } from '../TextField'
import { SectionBody } from './SectionBody'

import styles from '../../styles.css'

const maskTo2Decimal = makeMaskToDecimal(2)

const getContent = (): JSX.Element => (
<div className={styles.flex_row}>
<div className={styles.instructions_column}>
<p>
Ensure measurement is taken from the <strong>very bottom</strong> of
plate.
</p>
<p>
The footprint measurement helps determine if the labware fits firmly
into the slots on the OT-2 deck.
</p>
</div>
<div className={styles.diagram_column}>
<img src={require('../../images/footprint.svg')} />
</div>
<div className={styles.form_fields_column}>
<TextField
name="footprintXDimension"
inputMasks={[maskTo2Decimal]}
units="mm"
/>
<TextField
name="footprintYDimension"
inputMasks={[maskTo2Decimal]}
units="mm"
/>
</div>
</div>
)

export const Footprint = (): JSX.Element => {
const fieldList: Array<keyof LabwareFields> = [
'footprintXDimension',
'footprintYDimension',
]
const { values, errors, touched } = useFormikContext<LabwareFields>()

return (
<div className={styles.new_definition_section}>
<SectionBody label="Footprint">
<>
{getFormAlerts({ values, touched, errors, fieldList })}
{getXYDimensionAlerts(values, touched)}
{getContent()}
</>
</SectionBody>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const Regularity = (): JSX.Element => {
const fieldList: Array<keyof LabwareFields> = ['homogeneousWells']
const { values, errors, touched } = useFormikContext<LabwareFields>()

const ret = (
return (
<SectionBody label="Regularity">
<>
{getFormAlerts({ values, touched, errors, fieldList })}
Expand All @@ -24,5 +24,4 @@ export const Regularity = (): JSX.Element => {
</>
</SectionBody>
)
return ret
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const UploadExisting = (props: Props): JSX.Element => {
return (
<div className={styles.upload_existing_section}>
<h2 className={styles.setup_heading}>
Edit a file you’ve built with our labware creator omg!
Edit a file you’ve built with our labware creator
</h2>
{lastUploaded === null ? (
<ImportLabware onUpload={onUpload} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,38 @@ import compact from 'lodash/compact'
import uniq from 'lodash/uniq'
import { AlertItem } from '@opentrons/components'
import { getIsHidden } from '../../formSelectors'
import { IRREGULAR_LABWARE_ERROR, LINK_CUSTOM_LABWARE_FORM } from '../../fields'
import {
LabwareFields,
IRREGULAR_LABWARE_ERROR,
LINK_CUSTOM_LABWARE_FORM,
} from '../../fields'
import { LinkOut } from '../LinkOut'

import type { FormikTouched, FormikErrors } from 'formik'
import type { LabwareFields } from '../../fields'

interface Props {
export interface Props {
values: LabwareFields
fieldList: Array<keyof LabwareFields>
touched: FormikTouched<LabwareFields>
errors: FormikErrors<LabwareFields>
}

export const IrregularLabwareAlert = (): JSX.Element => (
<AlertItem
key={IRREGULAR_LABWARE_ERROR}
type="error"
title={
<>
Your labware is not compatible with the Labware Creator. Please fill out{' '}
<LinkOut href={LINK_CUSTOM_LABWARE_FORM}>this form</LinkOut> to request
a custom labware definition.
</>
}
/>
)

export const getFormAlerts = (props: Props): JSX.Element[] | null => {
const { values, fieldList, touched, errors } = props
if (fieldList != null && fieldList.length > 0) {
if (fieldList.length > 0) {
const numFieldsHidden = fieldList
.map(field => getIsHidden(field, values))
.filter(Boolean).length
Expand All @@ -37,20 +53,7 @@ export const getFormAlerts = (props: Props): JSX.Element[] | null => {

return allErrors.map(error => {
if (error === IRREGULAR_LABWARE_ERROR) {
return (
<AlertItem
key={error}
type="error"
title={
<>
Your labware is not compatible with the Labware Creator. Please
fill out{' '}
<LinkOut href={LINK_CUSTOM_LABWARE_FORM}>this form</LinkOut> to
request a custom labware definition.
</>
}
/>
)
return <IrregularLabwareAlert />
}
return <AlertItem key={error} type="warning" title={error} />
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react'
import { FormikTouched } from 'formik'
import { LabwareFields, MAX_SUGGESTED_Z } from '../../fields'
import { AlertItem } from '@opentrons/components'

export const getHeightAlerts = (
values: LabwareFields,
touched: FormikTouched<LabwareFields>
): JSX.Element | null => {
const { labwareZDimension } = values
const zAsNum = Number(labwareZDimension) // NOTE: if empty string or null, may be cast to 0, but that's fine for `>`
if (touched.labwareZDimension && zAsNum > MAX_SUGGESTED_Z) {
return (
<AlertItem
type="info"
title="This labware may be too tall to work with some pipette + tip combinations. Please test on robot."
/>
)
}
return null
}
Loading

0 comments on commit e8f2ae9

Please sign in to comment.