Skip to content

Commit 6208bf5

Browse files
authored
feat(components, protocol-designer): introduce new tip state SVGs and legend (#19753)
This PR introduces new SVG components for 5 tip states: new, used, selected, no, and inaccessible. It also creates the new PD component TipLegend to be used in the tip selection wizard flow. Closes AUTH-2379
1 parent 1ef1c36 commit 6208bf5

File tree

15 files changed

+333
-5
lines changed

15 files changed

+333
-5
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { COLORS } from '../../../../helix-design-system'
2+
import { DEFAULT_TIP_SIZE } from './constants'
3+
4+
const STROKE_WIDTH = '2'
5+
6+
export function InaccessibleTip(props: { size?: string }): JSX.Element {
7+
const { size } = props
8+
const width = size ?? DEFAULT_TIP_SIZE
9+
const height = size ?? DEFAULT_TIP_SIZE
10+
return (
11+
<svg
12+
width={width}
13+
height={height}
14+
viewBox="0 0 20 20"
15+
fill="none"
16+
xmlns="http://www.w3.org/2000/svg"
17+
>
18+
<mask
19+
id="mask0_2189_16275"
20+
style={{ maskType: 'alpha' }}
21+
maskUnits="userSpaceOnUse"
22+
x="0"
23+
y="0"
24+
width="20"
25+
height="20"
26+
>
27+
<path
28+
d="M10 0.5C15.2467 0.5 19.5 4.7533 19.5 10C19.5 15.2467 15.2467 19.5 10 19.5C4.7533 19.5 0.5 15.2467 0.5 10C0.5 4.7533 4.7533 0.5 10 0.5Z"
29+
fill={COLORS.grey35}
30+
stroke="black"
31+
/>
32+
</mask>
33+
<g mask="url(#mask0_2189_16275)">
34+
<path
35+
d="M10 1C14.9706 1 19 5.02944 19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1Z"
36+
fill={COLORS.blue35}
37+
stroke={COLORS.grey50}
38+
strokeWidth={STROKE_WIDTH}
39+
/>
40+
<path
41+
d="M10 5C12.7614 5 15 7.23858 15 10C15 12.7614 12.7614 15 10 15C7.23858 15 5 12.7614 5 10C5 7.23858 7.23858 5 10 5Z"
42+
fill={COLORS.blue35}
43+
stroke={COLORS.grey50}
44+
strokeWidth={STROKE_WIDTH}
45+
/>
46+
<line
47+
x1="24.7071"
48+
y1="-4.29289"
49+
x2="-3.29289"
50+
y2="23.7071"
51+
stroke={COLORS.grey50}
52+
strokeWidth={STROKE_WIDTH}
53+
/>
54+
</g>
55+
</svg>
56+
)
57+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { COLORS } from '../../../../helix-design-system'
2+
import { DEFAULT_TIP_SIZE } from './constants'
3+
4+
export function NewTip(props: { size?: string }): JSX.Element {
5+
const { size } = props
6+
const width = size ?? DEFAULT_TIP_SIZE
7+
const height = size ?? DEFAULT_TIP_SIZE
8+
return (
9+
<svg
10+
width={width}
11+
height={height}
12+
viewBox="0 0 20 20"
13+
fill="none"
14+
xmlns="http://www.w3.org/2000/svg"
15+
>
16+
<circle cx="10" cy="10" r="10" fill={COLORS.blue35} />
17+
<circle cx="10" cy="10" r="5" stroke={COLORS.grey50} strokeWidth="2" />
18+
</svg>
19+
)
20+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { COLORS } from '../../../../helix-design-system'
2+
import { DEFAULT_TIP_SIZE } from './constants'
3+
4+
export function NoTip(props: { size?: string }): JSX.Element {
5+
const { size } = props
6+
const width = size ?? DEFAULT_TIP_SIZE
7+
const height = size ?? DEFAULT_TIP_SIZE
8+
return (
9+
<svg
10+
width={width}
11+
height={height}
12+
fill="none"
13+
xmlns="http://www.w3.org/2000/svg"
14+
viewBox="0 0 20 20"
15+
>
16+
<path
17+
d="M10 1C14.9706 1 19 5.02944 19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1Z"
18+
fill={COLORS.grey35}
19+
stroke={COLORS.grey50}
20+
strokeWidth="2"
21+
/>
22+
</svg>
23+
)
24+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { COLORS } from '../../../../helix-design-system'
2+
import { DEFAULT_TIP_SIZE } from './constants'
3+
4+
export function SelectedTip(props: { size?: string }): JSX.Element {
5+
const { size } = props
6+
const width = size ?? DEFAULT_TIP_SIZE
7+
const height = size ?? DEFAULT_TIP_SIZE
8+
return (
9+
<svg
10+
width={width}
11+
height={height}
12+
viewBox="0 0 20 20"
13+
fill="none"
14+
xmlns="http://www.w3.org/2000/svg"
15+
>
16+
<circle cx="10" cy="10" r="10" fill={COLORS.blue50} />
17+
</svg>
18+
)
19+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { InaccessibleTip } from './InaccessibleTip'
2+
import { NewTip } from './NewTip'
3+
import { NoTip } from './NoTip'
4+
import { SelectedTip } from './SelectedTip'
5+
import { UsedTip } from './UsedTip'
6+
7+
import type { TipType } from '../types'
8+
9+
export function TipStatus(props: {
10+
type: TipType
11+
size?: string
12+
}): JSX.Element {
13+
const { type, size } = props
14+
switch (type) {
15+
case 'new':
16+
return <NewTip size={size} />
17+
case 'used':
18+
return <UsedTip size={size} />
19+
case 'selected':
20+
return <SelectedTip size={size} />
21+
case 'no':
22+
return <NoTip size={size} />
23+
case 'inaccessible':
24+
return <InaccessibleTip size={size} />
25+
}
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { COLORS } from '../../../../helix-design-system'
2+
import { DEFAULT_TIP_SIZE } from './constants'
3+
4+
export function UsedTip(props: { size?: string }): JSX.Element {
5+
const { size } = props
6+
const width = size ?? DEFAULT_TIP_SIZE
7+
const height = size ?? DEFAULT_TIP_SIZE
8+
return (
9+
<svg
10+
width={width}
11+
height={height}
12+
viewBox="0 0 20 20"
13+
fill="none"
14+
xmlns="http://www.w3.org/2000/svg"
15+
>
16+
<circle cx="10" cy="10" r="10" fill={COLORS.blue35} />
17+
<circle
18+
cx="10"
19+
cy="10"
20+
r="4.5"
21+
fill={COLORS.grey50}
22+
stroke={COLORS.grey50}
23+
strokeWidth="3"
24+
/>
25+
</svg>
26+
)
27+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { render, screen } from '@testing-library/react'
2+
import { beforeEach, describe, it, vi } from 'vitest'
3+
4+
import { InaccessibleTip } from '../InaccessibleTip'
5+
import { NewTip } from '../NewTip'
6+
import { NoTip } from '../NoTip'
7+
import { SelectedTip } from '../SelectedTip'
8+
import { TipStatus } from '../TipStatus'
9+
import { UsedTip } from '../UsedTip'
10+
11+
vi.mock('../NewTip')
12+
vi.mock('../UsedTip')
13+
vi.mock('../SelectedTip')
14+
vi.mock('../NoTip')
15+
vi.mock('../InaccessibleTip')
16+
17+
describe('TipStatus', () => {
18+
beforeEach(() => {
19+
vi.mocked(NewTip).mockReturnValue(<div>New tip</div>)
20+
vi.mocked(UsedTip).mockReturnValue(<div>Used tip</div>)
21+
vi.mocked(SelectedTip).mockReturnValue(<div>Selected tip</div>)
22+
vi.mocked(NoTip).mockReturnValue(<div>No tip</div>)
23+
vi.mocked(InaccessibleTip).mockReturnValue(<div>Inaccessible tip</div>)
24+
})
25+
26+
it('should render new tip', () => {
27+
render(<TipStatus type="new" />)
28+
screen.getByText('New tip')
29+
})
30+
31+
it('should render used tip', () => {
32+
render(<TipStatus type="used" />)
33+
screen.getByText('Used tip')
34+
})
35+
36+
it('should render selected tip', () => {
37+
render(<TipStatus type="selected" />)
38+
screen.getByText('Selected tip')
39+
})
40+
41+
it('should render no tip', () => {
42+
render(<TipStatus type="no" />)
43+
screen.getByText('No tip')
44+
})
45+
46+
it('should render inaccessible tip', () => {
47+
render(<TipStatus type="inaccessible" />)
48+
screen.getByText('Inaccessible tip')
49+
})
50+
})
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const DEFAULT_TIP_SIZE = '20'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './TipStatus'

components/src/hardware-sim/Labware/labwareInternals/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './FilledWells'
66
export * from './LabwareOutline'
77
export * from './Well'
88
export * from './LabwareWellLabels'
9+
export * from './Tips'

0 commit comments

Comments
 (0)