Skip to content

Commit

Permalink
refactor(labware-creator): Break out spacing and offset sections (#7798)
Browse files Browse the repository at this point in the history
closes #7707
  • Loading branch information
Kadee80 authored May 19, 2021
1 parent 1594984 commit d1fbefc
Show file tree
Hide file tree
Showing 6 changed files with 439 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Matcher } from '@testing-library/react'

// Match things like <p>Some <strong>nested</strong> text</p>
// Use with either string match: getByText(nestedTextMatcher("Some nested text"))
// or regexp: getByText(nestedTextMatcher(/Some nested text/))
export const nestedTextMatcher = (textMatch: string | RegExp): Matcher => (
content,
node
) => {
const hasText = (n: typeof node): boolean => {
if (n == null || n.textContent === null) return false
return typeof textMatch === 'string'
? n?.textContent === textMatch
: textMatch.test(n.textContent)
}
const nodeHasText = hasText(node)
const childrenDontHaveText =
node != null && Array.from(node.children).every(child => !hasText(child))

return nodeHasText && childrenDontHaveText
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react'
import { FormikConfig } from 'formik'
import isEqual from 'lodash/isEqual'
import { when } from 'jest-when'
import { render, screen } from '@testing-library/react'
import { nestedTextMatcher } from '../../__testUtils__/nestedTextMatcher'
import { getDefaultFormState, LabwareFields } from '../../../fields'
import { isEveryFieldHidden } from '../../../utils'
import { GridOffset } from '../../sections/GridOffset'
import { FormAlerts } from '../../FormAlerts'
import { TextField } from '../../TextField'
import { wrapInFormik } from '../../utils/wrapInFormik'
jest.mock('../../../utils')
jest.mock('../../TextField')
jest.mock('../../FormAlerts')

const FormAlertsMock = FormAlerts as jest.MockedFunction<typeof FormAlerts>

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

const isEveryFieldHiddenMock = isEveryFieldHidden as jest.MockedFunction<
typeof isEveryFieldHidden
>

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

describe('GridOffset', () => {
beforeEach(() => {
textFieldMock.mockImplementation(args => {
if (args.name === 'gridOffsetX') {
return <div>gridOffsetX text field</div>
}
if (args.name === 'gridOffsetY') {
return <div>gridOffsetY text field</div>
} else {
return <div></div>
}
})

FormAlertsMock.mockImplementation(args => {
if (
isEqual(args, {
touched: {},
errors: {},
fieldList: ['gridOffsetX', 'gridOffsetY'],
})
) {
return <div>mock alerts</div>
} else {
return <div></div>
}
})
})

afterEach(() => {
jest.restoreAllMocks()
})

it('should render when fields are visible', () => {
render(wrapInFormik(<GridOffset />, formikConfig))
expect(screen.getByText('Grid Offset'))
expect(screen.getByText('mock alerts'))
expect(
screen.getByText(
"Corner offset informs the robot how far the grid of wells is from the slot's top left corner."
)
)
expect(screen.getByText('gridOffsetX text field'))
expect(screen.getByText('gridOffsetY text field'))
})

it('should update instructions when reservoir is selected', () => {
const { getByText } = render(
wrapInFormik(<GridOffset />, {
...formikConfig,
initialValues: {
...formikConfig.initialValues,
labwareType: 'reservoir',
},
})
)

getByText(
nestedTextMatcher(
"Find the measurement from the center of the top left-most well to the edge of the labware's footprint."
)
)
})

it('should NOT render when the labware type is aluminumBlock', () => {
const { container } = render(
wrapInFormik(<GridOffset />, {
...formikConfig,
initialValues: {
...formikConfig.initialValues,
labwareType: 'aluminumBlock',
},
})
)
expect(container.firstChild).toBe(null)
})

it('should NOT render when the labware type is tubeRack', () => {
const { container } = render(
wrapInFormik(<GridOffset />, {
...formikConfig,
initialValues: {
...formikConfig.initialValues,
labwareType: 'tubeRack',
},
})
)
expect(container.firstChild).toBe(null)
})

it('should not render when all fields are hidden', () => {
when(isEveryFieldHiddenMock)
.calledWith(['gridOffsetX', 'gridOffsetY'], formikConfig.initialValues)
.mockReturnValue(true)

const { container } = render(wrapInFormik(<GridOffset />, formikConfig))
expect(container.firstChild).toBe(null)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react'
import { FormikConfig } from 'formik'
import isEqual from 'lodash/isEqual'
import { when } from 'jest-when'
import { render, screen } from '@testing-library/react'
import { getDefaultFormState, LabwareFields } from '../../../fields'
import { isEveryFieldHidden } from '../../../utils'
import { WellSpacing } from '../../sections/WellSpacing'
import { FormAlerts } from '../../FormAlerts'
import { TextField } from '../../TextField'

import { wrapInFormik } from '../../utils/wrapInFormik'

jest.mock('../../../utils')
jest.mock('../../TextField')
jest.mock('../../FormAlerts')

const FormAlertsMock = FormAlerts as jest.MockedFunction<typeof FormAlerts>

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

const isEveryFieldHiddenMock = isEveryFieldHidden as jest.MockedFunction<
typeof isEveryFieldHidden
>

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

describe('WellSpacing', () => {
beforeEach(() => {
textFieldMock.mockImplementation(args => {
if (args.name === 'gridSpacingX') {
return <div>gridSpacingX text field</div>
}
if (args.name === 'gridSpacingY') {
return <div>gridSpacingY text field</div>
} else {
return <div></div>
}
})

FormAlertsMock.mockImplementation(args => {
if (
isEqual(args, {
touched: {},
errors: {},
fieldList: ['gridSpacingX', 'gridSpacingY'],
})
) {
return <div>mock alerts</div>
} else {
return <div></div>
}
})
})

afterEach(() => {
jest.restoreAllMocks()
})

it('should render when fields are visible', () => {
render(wrapInFormik(<WellSpacing />, formikConfig))
expect(screen.getByText('Well Spacing'))
expect(screen.getByText('mock alerts'))
expect(
screen.getByText(
'Well spacing measurements inform the robot how far away rows and columns are from each other.'
)
)
expect(screen.getByText('gridSpacingX text field'))
expect(screen.getByText('gridSpacingX text field'))
})

it('should NOT render when the labware type is aluminumBlock', () => {
const { container } = render(
wrapInFormik(<WellSpacing />, {
...formikConfig,
initialValues: {
...formikConfig.initialValues,
labwareType: 'aluminumBlock',
},
})
)
expect(container.firstChild).toBe(null)
})
it('should NOT render when the labware type is tubeRack', () => {
const { container } = render(
wrapInFormik(<WellSpacing />, {
...formikConfig,
initialValues: {
...formikConfig.initialValues,
labwareType: 'tubeRack',
},
})
)
expect(container.firstChild).toBe(null)
})

it('should not render when all fields are hidden', () => {
when(isEveryFieldHiddenMock)
.calledWith(['gridSpacingX', 'gridSpacingY'], formikConfig.initialValues)
.mockReturnValue(true)

const { container } = render(wrapInFormik(<WellSpacing />, formikConfig))
expect(container.firstChild).toBe(null)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as React from 'react'
import { useFormikContext } from 'formik'
import { makeMaskToDecimal } from '../../fieldMasks'
import { isEveryFieldHidden } from '../../utils'
import { LabwareFields } from '../../fields'
import { FormAlerts } from '../FormAlerts'
import { TextField } from '../TextField'
import { XYOffsetImg } from '../diagrams'
import { SectionBody } from './SectionBody'

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

const maskTo2Decimal = makeMaskToDecimal(2)

interface Props {
values: LabwareFields
}

// TODO (ka 2021-5-11): Broke this out here since we will need to have more conditions for tips
const Instructions = (props: Props): JSX.Element => {
const { values } = props
return (
<>
<p>
Find the measurement from the center of{' '}
<strong>
{values.labwareType === 'reservoir'
? 'the top left-most well'
: 'well A1'}
</strong>{' '}
to the edge of the labware{"'"}s footprint.
</p>
<p>
Corner offset informs the robot how far the grid of wells is from the
slot{"'"}s top left corner.
</p>
<div className={styles.help_text}>
<img src={require('../../images/offset_helpText.svg')} />
</div>
</>
)
}

const Content = (props: Props): JSX.Element => {
const { values } = props
return (
<div className={styles.flex_row}>
<div className={styles.instructions_column}>
<Instructions values={values} />
</div>
<div className={styles.diagram_column}>
<XYOffsetImg
labwareType={values.labwareType}
wellShape={values.wellShape}
/>
</div>
<div className={styles.form_fields_column}>
<TextField
name="gridOffsetX"
inputMasks={[maskTo2Decimal]}
units="mm"
/>
<TextField
name="gridOffsetY"
inputMasks={[maskTo2Decimal]}
units="mm"
/>
</div>
</div>
)
}

export const GridOffset = (): JSX.Element | null => {
const fieldList: Array<keyof LabwareFields> = ['gridOffsetX', 'gridOffsetY']
const { values, errors, touched } = useFormikContext<LabwareFields>()
if (
isEveryFieldHidden(fieldList, values) ||
(values.labwareType != null &&
['aluminumBlock', 'tubeRack'].includes(values.labwareType))
) {
return null
}
return (
<div className={styles.new_definition_section}>
<SectionBody label="Grid Offset">
<>
<FormAlerts touched={touched} errors={errors} fieldList={fieldList} />
<Content values={values} />
</>
</SectionBody>
</div>
)
}
Loading

0 comments on commit d1fbefc

Please sign in to comment.