Skip to content
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

refactor(protocol-designer): remove usage of class components and connect fns #14331

Merged
merged 20 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 30 additions & 54 deletions protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,48 @@
import * as React from 'react'
import { connect } from 'react-redux'
import { LabwareRender, WellGroup } from '@opentrons/components'
import { useSelector } from 'react-redux'
import { LabwareRender } from '@opentrons/components'

import { selectors } from '../../labware-ingred/selectors'
import * as wellContentsSelectors from '../../top-selectors/well-contents'
import * as highlightSelectors from '../../top-selectors/substep-highlight'
import * as tipContentsSelectors from '../../top-selectors/tip-contents'
import { LabwareOnDeck as LabwareOnDeckType } from '../../step-forms'
import { ContentsByWell } from '../../labware-ingred/types'
import { BaseState } from '../../types'
import { wellFillFromWellContents } from '../labware/utils'

interface OP {
interface LabwareOnDeckProps {
className?: string
labwareOnDeck: LabwareOnDeckType
x: number
y: number
}

interface SP {
wellContents: ContentsByWell
liquidDisplayColors: string[]
missingTips?: WellGroup | null
highlightedWells?: WellGroup | null
}

type Props = OP & SP

const LabwareOnDeckComponent = (props: Props): JSX.Element => (
<g
transform={`translate(${props.x}, ${props.y})`}
className={props.className}
>
<LabwareRender
definition={props.labwareOnDeck.def}
wellFill={wellFillFromWellContents(
props.wellContents,
props.liquidDisplayColors
)}
highlightedWells={props.highlightedWells}
missingTips={props.missingTips}
/>
</g>
)

const mapStateToProps = (state: BaseState, ownProps: OP): SP => {
const { labwareOnDeck } = ownProps

const missingTipsByLabwareId = tipContentsSelectors.getMissingTipsByLabwareId(
state
export function LabwareOnDeck(props: LabwareOnDeckProps): JSX.Element {
const { labwareOnDeck, x, y, className } = props
const missingTipsByLabwareId = useSelector(
tipContentsSelectors.getMissingTipsByLabwareId
)

const allWellContentsForActiveItem = wellContentsSelectors.getAllWellContentsForActiveItem(
state
const allWellContentsForActiveItem = useSelector(
wellContentsSelectors.getAllWellContentsForActiveItem
)
const allHighlightedWells = useSelector(
highlightSelectors.wellHighlightsByLabwareId
)
const liquidDisplayColors = useSelector(selectors.getLiquidDisplayColors)
const wellContents = allWellContentsForActiveItem
? allWellContentsForActiveItem[labwareOnDeck.id]
: null
const highlightedWells = allHighlightedWells[labwareOnDeck.id]
const missingTips = missingTipsByLabwareId
? missingTipsByLabwareId[labwareOnDeck.id]
: null
return (
<g transform={`translate(${x}, ${y})`} className={className}>
<LabwareRender
definition={labwareOnDeck.def}
wellFill={wellFillFromWellContents(wellContents, liquidDisplayColors)}
highlightedWells={highlightedWells}
missingTips={missingTips}
/>
</g>
)

return {
wellContents: allWellContentsForActiveItem
? allWellContentsForActiveItem[labwareOnDeck.id]
: null,
highlightedWells: highlightSelectors.wellHighlightsByLabwareId(state)[
labwareOnDeck.id
],
missingTips: missingTipsByLabwareId
? missingTipsByLabwareId[labwareOnDeck.id]
: null,
liquidDisplayColors: selectors.getLiquidDisplayColors(state),
}
}

export const LabwareOnDeck = connect(mapStateToProps)(LabwareOnDeckComponent)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { connect } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
import { useTranslation } from 'react-i18next'
import { css } from 'styled-components'
import {
Expand All @@ -23,7 +23,7 @@ import { NameThisLabware } from './NameThisLabware'
import styles from './LabwareOverlays.css'

import type { LabwareEntity } from '@opentrons/step-generation'
import type { BaseState, ThunkDispatch } from '../../../types'
import type { ThunkDispatch } from '../../../types'

const NAME_LABWARE_OVERLAY_STYLE = css`
z-index: 1;
Expand Down Expand Up @@ -58,30 +58,26 @@ const REGULAR_OVERLAY_STYLE = css`
}
`

interface OP {
interface EditLabwareOffDeckProps {
labwareEntity: LabwareEntity
}
interface SP {
isYetUnnamed: boolean
}
interface DP {
editLiquids: () => void
duplicateLabware: () => void
deleteLabware: () => void
}

type Props = OP & SP & DP

const EditLabwareOffDeckComponent = (props: Props): JSX.Element => {
const {
labwareEntity,
isYetUnnamed,
editLiquids,
deleteLabware,
duplicateLabware,
} = props
export function EditLabwareOffDeck(
props: EditLabwareOffDeckProps
): JSX.Element {
const { labwareEntity } = props
const { t } = useTranslation('deck')
const dispatch = useDispatch<ThunkDispatch<any>>()
const allSavedLabware = useSelector(labwareIngredSelectors.getSavedLabware)
const hasName = allSavedLabware[labwareEntity.id]
const { isTiprack } = labwareEntity.def.parameters

const isYetUnnamed = isTiprack && !hasName

const editLiquids = (): void => {
dispatch(openIngredientSelector(labwareEntity.id))
}

if (isYetUnnamed && !isTiprack) {
return (
<div css={NAME_LABWARE_OVERLAY_STYLE}>
Expand All @@ -102,44 +98,27 @@ const EditLabwareOffDeckComponent = (props: Props): JSX.Element => {
) : (
<div className={styles.button_spacer} />
)}
<a className={styles.overlay_button} onClick={duplicateLabware}>
<a
className={styles.overlay_button}
onClick={() => dispatch(duplicateLabware(labwareEntity.id))}
>
<Icon className={styles.overlay_icon} name="content-copy" />
{t('overlay.edit.duplicate')}
</a>
<a className={styles.overlay_button} onClick={deleteLabware}>
<a
className={styles.overlay_button}
onClick={() => {
window.confirm(
t('warning.cancelForSure', {
adapterName: getLabwareDisplayName(labwareEntity.def),
})
) && dispatch(deleteContainer({ labwareId: labwareEntity.id }))
}}
>
<Icon className={styles.overlay_icon} name="close" />
{t('overlay.edit.delete')}
</a>
</div>
)
}
}

const mapStateToProps = (state: BaseState, ownProps: OP): SP => {
const { id } = ownProps.labwareEntity
const hasName = labwareIngredSelectors.getSavedLabware(state)[id]
return {
isYetUnnamed: !ownProps.labwareEntity.def.parameters.isTiprack && !hasName,
}
}

const mapDispatchToProps = (
dispatch: ThunkDispatch<any>,
ownProps: OP
): DP => ({
editLiquids: () =>
dispatch(openIngredientSelector(ownProps.labwareEntity.id)),
duplicateLabware: () => dispatch(duplicateLabware(ownProps.labwareEntity.id)),
deleteLabware: () => {
window.confirm(
`Are you sure you want to permanently delete this ${getLabwareDisplayName(
ownProps.labwareEntity.def
)}?`
) && dispatch(deleteContainer({ labwareId: ownProps.labwareEntity.id }))
},
})

export const EditLabwareOffDeck = connect(
mapStateToProps,
mapDispatchToProps
)(EditLabwareOffDeckComponent)
Original file line number Diff line number Diff line change
@@ -1,33 +1,19 @@
import * as React from 'react'
import { connect } from 'react-redux'
import { useSelector } from 'react-redux'
import { LabwareNameOverlay, truncateString } from '@opentrons/components'
import { getLabwareDisplayName } from '@opentrons/shared-data'
import { BaseState } from '../../../types'
import { selectors as uiLabwareSelectors } from '../../../ui/labware'
import { LabwareOnDeck } from '../../../step-forms'
interface OP {
interface LabwareNameProps {
labwareOnDeck: LabwareOnDeck
}

interface SP {
nickname?: string | null
}

type Props = OP & SP

const NameOverlay = (props: Props): JSX.Element => {
const { labwareOnDeck, nickname } = props
export function LabwareName(props: LabwareNameProps): JSX.Element {
const { labwareOnDeck } = props
const nicknames = useSelector(uiLabwareSelectors.getLabwareNicknamesById)
const nickname = nicknames[labwareOnDeck.id]
const truncatedNickName =
nickname != null ? truncateString(nickname, 75, 25) : null
const title = truncatedNickName ?? getLabwareDisplayName(labwareOnDeck.def)
return <LabwareNameOverlay title={title} />
}

const mapStateToProps = (state: BaseState, ownProps: OP): SP => {
const { id } = ownProps.labwareOnDeck
return {
nickname: uiLabwareSelectors.getLabwareNicknamesById(state)[id],
}
}

export const LabwareName = connect(mapStateToProps)(NameOverlay)
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { useDispatch } from 'react-redux'
import cx from 'classnames'
import { Icon, useOnClickOutside } from '@opentrons/components'
import { renameLabware } from '../../../labware-ingred/actions'
import styles from './LabwareOverlays.css'

import type { LabwareEntity } from '@opentrons/step-generation'
import type { ThunkDispatch } from '../../../types'
import type { LabwareOnDeck } from '../../../step-forms'

interface OP {
interface NameThisLabwareProps {
labwareOnDeck: LabwareOnDeck | LabwareEntity
editLiquids: () => unknown
}

interface DP {
setLabwareName: (name: string | null | undefined) => unknown
editLiquids: () => void
}

type Props = OP & DP

const NameThisLabwareComponent = (props: Props): JSX.Element => {
const [inputValue, setInputValue] = React.useState('')
export function NameThisLabware(props: NameThisLabwareProps): JSX.Element {
const { labwareOnDeck } = props
const dispatch: ThunkDispatch<any> = useDispatch()
const [inputValue, setInputValue] = React.useState<string>('')
const { t } = useTranslation('deck')

const setLabwareName = (name: string | null | undefined): void => {
dispatch(renameLabware({ labwareId: labwareOnDeck.id, name }))
}

const saveNickname = (): void => {
props.setLabwareName(inputValue || null)
setLabwareName(inputValue ?? null)
}
const wrapperRef: React.RefObject<HTMLDivElement> = useOnClickOutside({
onClickOutside: saveNickname,
Expand Down Expand Up @@ -67,16 +68,3 @@ const NameThisLabwareComponent = (props: Props): JSX.Element => {
</div>
)
}

const mapDispatchToProps = (dispatch: ThunkDispatch<any>, ownProps: OP): DP => {
const { id } = ownProps.labwareOnDeck
return {
setLabwareName: (name: string | null | undefined) =>
dispatch(renameLabware({ labwareId: id, name })),
}
}

export const NameThisLabware = connect(
null,
mapDispatchToProps
)(NameThisLabwareComponent)
Loading
Loading