Skip to content

Commit 15409ec

Browse files
committed
Merge back 'chore_release-pd-8.5.0' into 'edge', part 1.
2 parents 75d0979 + e445014 commit 15409ec

File tree

101 files changed

+2197
-1748
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+2197
-1748
lines changed

app/src/organisms/ODD/QuickTransferFlow/__tests__/utils/quickTransferStepCommands.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ const mockRobotState: TimelineFrame = {
9090
},
9191
},
9292
pipettes: {
93-
mockPipette: false,
93+
mockPipette: {
94+
hasTip: false,
95+
tiprackURI: null,
96+
},
9497
},
9598
},
9699
liquidState: {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import styled from 'styled-components'
2+
3+
import { COLORS } from '../../helix-design-system'
4+
import { Icon } from '../../icons'
5+
import { Flex } from '../../primitives'
6+
import { ALIGN_CENTER } from '../../styles'
7+
import { CURSOR_NOT_ALLOWED, CURSOR_POINTER } from '../../styles/cursor'
8+
import { SPACING, TYPOGRAPHY } from '../../ui-style-constants'
9+
import { StyledText } from '../StyledText/StyledText'
10+
11+
import type { MouseEvent } from 'react'
12+
import type { IconName } from '../../icons'
13+
14+
interface BasicButtonProps {
15+
children: string // Content of basic button
16+
onClick: (event: MouseEvent<HTMLButtonElement>) => void // Function to handle button click events
17+
isDisabled?: boolean // Optional prop to control button aria-disabled
18+
underLine?: boolean // Optional prop to control underline styling
19+
tabIndex?: number // Optional prop for tab index
20+
iconName?: IconName // Optional prop for icon
21+
}
22+
23+
export function BasicButton({
24+
children,
25+
onClick,
26+
isDisabled = false,
27+
underLine = false,
28+
tabIndex = 0,
29+
iconName,
30+
...props
31+
}: BasicButtonProps): JSX.Element {
32+
const handleButtonClick = (event: MouseEvent<HTMLButtonElement>): void => {
33+
if (isDisabled) {
34+
event.preventDefault()
35+
return
36+
}
37+
if (onClick != null) {
38+
onClick(event)
39+
}
40+
}
41+
42+
return (
43+
<StyledButton
44+
data-testid={`basic_button_${children}`}
45+
onClick={handleButtonClick}
46+
aria-disabled={isDisabled}
47+
underLine={underLine}
48+
tabIndex={tabIndex}
49+
{...props}
50+
>
51+
{iconName != null ? (
52+
<Flex alignItems={ALIGN_CENTER} gap={SPACING.spacing8}>
53+
<Icon
54+
name={iconName}
55+
size="1.25rem"
56+
data-testid={`basic_button_${iconName}`}
57+
/>
58+
<StyledText desktopStyle="bodyDefaultRegular">{children}</StyledText>
59+
</Flex>
60+
) : (
61+
<StyledText desktopStyle="bodyDefaultRegular">{children}</StyledText>
62+
)}
63+
</StyledButton>
64+
)
65+
}
66+
67+
const StyledButton = styled.button<{
68+
underLine?: boolean
69+
'aria-disabled'?: boolean
70+
}>`
71+
background: none;
72+
border: none;
73+
padding: ${SPACING.spacing4};
74+
75+
color: ${props => (props['aria-disabled'] ? COLORS.grey40 : COLORS.black90)};
76+
cursor: ${props =>
77+
props['aria-disabled'] ? CURSOR_NOT_ALLOWED : CURSOR_POINTER};
78+
79+
text-decoration: ${props =>
80+
props.underLine ? TYPOGRAPHY.textDecorationUnderline : 'none'};
81+
82+
${props =>
83+
!props['aria-disabled']
84+
? `
85+
&:hover {
86+
color: ${COLORS.blue50};
87+
}
88+
89+
&:focus-visible {
90+
color: ${COLORS.blue50};
91+
outline: 2px solid ${COLORS.blue50};
92+
outline-offset: ${SPACING.spacing4};
93+
}
94+
`
95+
: undefined}
96+
97+
&[aria-disabled="true"] {
98+
outline: none;
99+
}
100+
`
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { fireEvent, screen } from '@testing-library/react'
2+
import { beforeEach, describe, expect, it, vi } from 'vitest'
3+
4+
import { COLORS } from '../../../helix-design-system'
5+
import { CURSOR_NOT_ALLOWED, CURSOR_POINTER } from '../../../styles/cursor'
6+
import { renderWithProviders } from '../../../testing/utils'
7+
import { TYPOGRAPHY } from '../../../ui-style-constants'
8+
import { BasicButton } from '../BasicButton'
9+
10+
import type { ComponentProps } from 'react'
11+
12+
const render = (props: ComponentProps<typeof BasicButton>) => {
13+
return renderWithProviders(<BasicButton {...props} />)
14+
}
15+
16+
describe('BasicButton', () => {
17+
let props: ComponentProps<typeof BasicButton>
18+
19+
beforeEach(() => {
20+
props = {
21+
children: 'basic button',
22+
onClick: vi.fn(),
23+
isDisabled: false,
24+
underLine: false,
25+
}
26+
})
27+
28+
it('renders basic button with text', async () => {
29+
render(props)
30+
const button = screen.getByRole('button', { name: 'basic button' })
31+
expect(button).toHaveAttribute('aria-disabled', 'false')
32+
expect(button).toHaveStyle(`cursor: ${CURSOR_POINTER}`)
33+
expect(button).toHaveStyle(`color: ${COLORS.black90}`)
34+
expect(button).toHaveStyle(`text-decoration: none`)
35+
})
36+
37+
it('renders basic button with icon', async () => {
38+
props.iconName = 'alert'
39+
render(props)
40+
screen.getByTestId('basic_button_alert')
41+
screen.getByRole('button', { name: 'basic button' })
42+
})
43+
44+
// TODO: need to update '@testing-library/user-event' v14+
45+
// it('has hover styles when not disabled', async () => {
46+
// render(props)
47+
// const button = screen.getByRole('button', { name: 'basic button' })
48+
// await userEvent.hover(button)
49+
// expect(button).toHaveStyle(`color: ${COLORS.blue50}`)
50+
// })
51+
52+
it('calls onClick when clicked', () => {
53+
render(props)
54+
const button = screen.getByRole('button', { name: 'basic button' })
55+
fireEvent.click(button)
56+
expect(props.onClick).toHaveBeenCalled()
57+
})
58+
59+
it('renders basic button with text and aria-disabled', () => {
60+
props.isDisabled = true
61+
render(props)
62+
const button = screen.getByRole('button', { name: 'basic button' })
63+
expect(button).toHaveAttribute('aria-disabled', 'true')
64+
expect(button).toHaveStyle(`cursor: ${CURSOR_NOT_ALLOWED}`)
65+
expect(button).toHaveStyle(`color: ${COLORS.grey40}`)
66+
})
67+
68+
it('renders basic button with text and underline', () => {
69+
props.underLine = true
70+
render(props)
71+
const button = screen.getByRole('button', { name: 'basic button' })
72+
expect(button).toHaveAttribute('aria-disabled', 'false')
73+
expect(button).toHaveStyle(`cursor: ${CURSOR_POINTER}`)
74+
expect(button).toHaveStyle(`color: ${COLORS.black90}`)
75+
expect(button).toHaveStyle(
76+
`text-decoration: ${TYPOGRAPHY.textDecorationUnderline}`
77+
)
78+
})
79+
80+
it('has hover styles when not disabled', () => {
81+
render(props)
82+
const button = screen.getByRole('button', { name: 'basic button' })
83+
expect(button).toHaveStyle(`color: ${COLORS.black90}`)
84+
expect(button).toHaveAttribute('aria-disabled', 'false')
85+
expect(button).toHaveStyle(`cursor: ${CURSOR_POINTER}`)
86+
})
87+
})

components/src/atoms/buttons/buttons.stories.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { ICON_DATA_BY_NAME } from '../../icons/icon-data'
12
import { Flex, STYLE_PROPS } from '../../primitives'
23
import { DIRECTION_ROW } from '../../styles'
34
import { SPACING } from '../../ui-style-constants'
45
import { AlertPrimaryButton as AlertPrimaryButtonComponent } from './AlertPrimaryButton'
56
import { AltPrimaryButton as AltPrimaryButtonComponent } from './AltPrimaryButton'
7+
import { BasicButton as BasicButtonComponent } from './BasicButton'
68
import { PrimaryButton as PrimaryButtonComponent } from './PrimaryButton'
79
import { SecondaryButton as SecondaryButtonComponent } from './SecondaryButton'
810

@@ -82,3 +84,31 @@ export const AltPrimaryButton: StoryObj<typeof AltPrimaryButtonComponent> = {
8284
</Flex>
8385
),
8486
}
87+
88+
export const BasicButton: StoryObj<typeof BasicButtonComponent> = {
89+
args: {
90+
children: 'basic button',
91+
},
92+
argTypes: {
93+
underLine: {
94+
control: {
95+
type: 'boolean',
96+
},
97+
description:
98+
'Toggles the underline style for the button text (BasicButton only).',
99+
},
100+
iconName: {
101+
options: Object.keys(ICON_DATA_BY_NAME),
102+
control: {
103+
type: 'select',
104+
},
105+
description:
106+
'Optional icon to display alongside the button text (BasicButton only).',
107+
},
108+
},
109+
render: args => (
110+
<Flex flexDirection={DIRECTION_ROW} gridGap={SPACING.spacing16}>
111+
<BasicButtonComponent {...args} />
112+
</Flex>
113+
),
114+
}

components/src/atoms/buttons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './AlertPrimaryButton'
2+
export * from './BasicButton'
23
export * from './EmptySelectorButton'
34
export * from './LargeButton'
45
export * from './PrimaryButton'

components/src/organisms/ProtocolTimelineScrubber/PipetteVisuals.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function PipetteMountViz(
6060
) : null}
6161
{pipetteEntity != null && pipetteId != null ? (
6262
<PipetteSideView
63-
allNozzlesHaveTips={timelineFrame.tipState.pipettes[pipetteId]}
63+
allNozzlesHaveTips={timelineFrame.tipState.pipettes[pipetteId].hasTip}
6464
allNozzleTipContents={Object.values(
6565
timelineFrame.liquidState.pipettes[pipetteId]
6666
)}

protocol-designer/cypress/e2e/modules.cy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ describe('The Redesigned Create Protocol Landing Page', () => {
4545
steps.add(SetupSteps.Confirm())
4646
steps.add(SetupSteps.EditProtocolA())
4747
steps.add(SetupSteps.ChoseDeckSlot('C2'))
48-
steps.add(SetupSteps.EditHardwareLabwareOnDeck())
4948
steps.add(SetupSteps.OpenSelectLabwareModal())
5049
steps.add(SetupSteps.ClickWellPlatesSection())
5150
steps.add(SetupSteps.SelectLabwareByDisplayName('Bio-Rad 96 Well Plate'))
@@ -63,7 +62,6 @@ describe('The Redesigned Create Protocol Landing Page', () => {
6362
steps.add(SetupSteps.SetVolumeAndSaveForWells('150'))
6463
steps.add(SetupSteps.SelectDone())
6564
steps.add(SetupSteps.ChoseDeckSlot('C1'))
66-
steps.add(SetupSteps.EditHardwareLabwareOnDeck())
6765
steps.add(SetupSteps.OpenSelectLabwareModal())
6866
steps.add(SetupSteps.AddAdapters())
6967
steps.add(SetupSteps.DeepWellTempModAdapter())

protocol-designer/cypress/e2e/plateReaderTest.cy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ describe('Plate Reader Happy Path Single-Wavelength', () => {
4444
steps.add(SetupSteps.Confirm())
4545
steps.add(SetupSteps.EditProtocolA())
4646
steps.add(SetupSteps.ChoseDeckSlotWithLabware('C3'))
47-
steps.add(SetupSteps.AddHardwareLabware())
4847
steps.add(SetupSteps.OpenSelectLabwareModal())
4948
steps.add(SetupSteps.ClickWellPlatesSection())
5049
steps.add(SetupSteps.SelectLabwareByDisplayName('Bio-Rad 96 Well Plate'))
@@ -64,7 +63,6 @@ describe('Plate Reader Happy Path Single-Wavelength', () => {
6463
// Add another labware
6564
steps.add(SetupSteps.ChoseDeckSlotWithLabware('D2'))
6665

67-
steps.add(SetupSteps.AddHardwareLabware())
6866
steps.add(SetupSteps.OpenSelectLabwareModal())
6967
steps.add(SetupSteps.ClickWellPlatesSection())
7068
steps.add(SetupSteps.SelectLabwareByDisplayName('Armadillo 96 Well Plate'))

protocol-designer/cypress/e2e/transferSettings.cy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ describe('Transfer stepform testing Single Channel - Happy Path', () => {
4343
steps.add(SetupSteps.Confirm())
4444
steps.add(SetupSteps.EditProtocolA())
4545
steps.add(SetupSteps.ChoseDeckSlot('C2'))
46-
steps.add(SetupSteps.AddHardwareLabware())
4746
steps.add(SetupSteps.OpenSelectLabwareModal())
4847
steps.add(SetupSteps.ClickWellPlatesSection())
4948
steps.add(SetupSteps.SelectLabwareByDisplayName('Bio-Rad 96 Well Plate'))
@@ -61,7 +60,6 @@ describe('Transfer stepform testing Single Channel - Happy Path', () => {
6160
steps.add(SetupSteps.SetVolumeAndSaveForWells('150'))
6261
steps.add(SetupSteps.SelectDone())
6362
steps.add(SetupSteps.ChoseDeckSlot('C3'))
64-
steps.add(SetupSteps.AddHardwareLabware())
6563
steps.add(SetupSteps.OpenSelectLabwareModal())
6664
steps.add(SetupSteps.ClickWellPlatesSection())
6765
steps.add(SetupSteps.SelectLabwareByDisplayName('Bio-Rad 96 Well Plate'))

protocol-designer/cypress/support/SetupSteps.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export enum SetupLocators {
9292
}
9393

9494
export const RegexSetupContent = {
95-
slotText: /Edit (slot|labware)/i,
95+
slotText: /(Add|Edit) labware/i,
9696
}
9797

9898
/**
@@ -405,15 +405,6 @@ export const SetupSteps = {
405405
},
406406
}),
407407

408-
/**
409-
* Edits existing labware/hardware on a deck slot.
410-
*/
411-
EditHardwareLabwareOnDeck: (): StepThunk => ({
412-
call: () => {
413-
cy.get('button[data-testid="SlotOverflowMenu_openTools"]').click()
414-
},
415-
}),
416-
417408
/**
418409
* Clicks the "Labware" header.
419410
*/
@@ -1182,7 +1173,7 @@ export const CompositeSetupSteps = {
11821173
`Running AddLabwareToDeckSlot with slot ${deckSlot} and labware ${labwareName}`
11831174
)
11841175
SetupSteps.ChoseDeckSlotWithLabware(slotToUse).call()
1185-
SetupSteps.AddHardwareLabware().call()
1176+
// SetupSteps.AddHardwareLabware().call()
11861177
SetupSteps.OpenSelectLabwareModal().call()
11871178
SetupSteps.ClickWellPlatesSection().call()
11881179
SetupSteps.SelectLabwareByDisplayName(labwareToUse).call()

0 commit comments

Comments
 (0)