Skip to content

Commit 4ea6c79

Browse files
feat: adds label support for Select
1 parent 8e99ad3 commit 4ea6c79

File tree

3 files changed

+68
-51
lines changed

3 files changed

+68
-51
lines changed

src/components/Select/Select.stories.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
SelectSeparator,
1818
SelectTrigger,
1919
SelectValue,
20-
SelectViewport
20+
SelectViewport,
2121
} from '.'
2222
import { Button } from '../Button'
2323
import { Box } from '../Box'
@@ -26,12 +26,12 @@ import {
2626
ConfirmDialog,
2727
ConfirmDialogContent,
2828
ConfirmDialogTrigger,
29-
ConfirmDialogActions
29+
ConfirmDialogActions,
3030
} from '../ConfirmDialog'
3131
import {
3232
mdiArrowUpDropCircleOutline,
3333
mdiArrowDownDropCircleOutline,
34-
mdiThumbUp
34+
mdiThumbUp,
3535
} from '@mdi/js'
3636
import { Svg } from '../Svg'
3737

@@ -52,12 +52,12 @@ export default {
5252
SelectValue,
5353
SelectViewport,
5454
SelectRoot,
55-
SelectRootItem
56-
}
55+
SelectRootItem,
56+
},
5757
} as Meta
5858

59-
const SimpleSelect = () => (
60-
<Select defaultValue="1">
59+
const SimpleSelect: Story = (args) => (
60+
<Select defaultValue="1" {...args}>
6161
<SelectItem value="1">Item 1</SelectItem>
6262
<SelectItem value="2">Item 2</SelectItem>
6363
<SelectItem value="3">Item 3</SelectItem>
@@ -118,8 +118,10 @@ export const Scrollable = () => (
118118
</Select>
119119
)
120120

121+
export const WithLabel = () => <SimpleSelect label="Label" />
122+
121123
/** Using `<SelectRoot>` and `<SelectRootItem>` gives further access to the underlying Radix implementation, allowing for more customization */
122-
export const Full = () => (
124+
export const Customization = () => (
123125
<SelectRoot defaultValue="19">
124126
<SelectTrigger>
125127
<SelectValue />

src/components/Select/Select.tsx

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useLabelContext } from '@radix-ui/react-label'
12
import {
23
Root,
34
Trigger,
@@ -12,36 +13,34 @@ import {
1213
Group,
1314
Separator,
1415
ScrollDownButton,
15-
Label
1616
} from '@radix-ui/react-select'
1717
import React, { ComponentProps, ElementRef, forwardRef } from 'react'
1818
import { CSSProps, styled } from '../../stitches.config'
1919
import { ChevronDown, ChevronUp, Check } from '../Icons'
2020
import { inputStyles } from '../Input/Input'
21+
import { Label } from '../Label'
2122

2223
const StyledTrigger = styled(Trigger, inputStyles, {
23-
// all: 'unset',
2424
display: 'inline-flex',
2525
alignItems: 'center',
2626
justifyContent: 'space-between',
27-
// padding: '$2 $3 $2 $6',
2827
cursor: 'pointer',
29-
alignContent: 'center'
28+
alignContent: 'center',
3029
})
3130

3231
const StyledValue = styled(Value, {
33-
flexGrow: 1
32+
flexGrow: 1,
3433
})
3534

3635
const StyledContent = styled(Content, {
3736
overflow: 'hidden',
3837
backgroundColor: '$colors$brandBackground',
3938
borderRadius: '$default',
40-
boxShadow: '$2'
39+
boxShadow: '$2',
4140
})
4241

4342
const StyledViewport = styled(Viewport, {
44-
padding: 5
43+
padding: 5,
4544
})
4645

4746
const StyledItem = styled(Item, {
@@ -63,25 +62,25 @@ const StyledItem = styled(Item, {
6362

6463
'&:focus': {
6564
background: '$selection',
66-
cursor: 'pointer'
65+
cursor: 'pointer',
6766
},
6867
'&[data-disabled]': {
6968
color: '$grey9',
70-
pointerEvents: 'none'
71-
}
69+
pointerEvents: 'none',
70+
},
7271
})
7372

7473
const StyledLabel = styled(Label, {
7574
padding: '$0 $2',
7675
fontSize: '$0',
7776
lineHeight: '$2',
78-
color: '$grey10'
77+
color: '$grey10',
7978
})
8079

8180
const StyledSeparator = styled(Separator, {
8281
height: 1,
8382
backgroundColor: '$grey7',
84-
margin: '$1'
83+
margin: '$1',
8584
})
8685

8786
const StyledItemIndicator = styled(ItemIndicator, {
@@ -90,7 +89,7 @@ const StyledItemIndicator = styled(ItemIndicator, {
9089
width: '$4',
9190
display: 'inline-flex',
9291
alignItems: 'center',
93-
justifyContent: 'center'
92+
justifyContent: 'center',
9493
})
9594

9695
const scrollButtonStyles = {
@@ -99,50 +98,67 @@ const scrollButtonStyles = {
9998
justifyContent: 'center',
10099
height: '$5',
101100
color: '$grey7',
102-
cursor: 'default'
101+
cursor: 'default',
103102
}
104103

105104
const StyledScrollUpButton = styled(ScrollUpButton, scrollButtonStyles)
106105

107106
const StyledScrollDownButton = styled(ScrollDownButton, scrollButtonStyles)
108107

109-
type AbstractSelectProps = ComponentProps<typeof Root> & CSSProps
108+
type SelectProps = ComponentProps<typeof Root> &
109+
CSSProps & {
110+
/** Add a label to the select */
111+
label?: string
112+
/** Add id to the select */
113+
id?: string
114+
}
110115

111-
const AbstractedSelect = forwardRef<
112-
ElementRef<typeof Root>,
113-
AbstractSelectProps
114-
>(({ children, ...props }, forwardedRef) => {
115-
return (
116-
<Root {...props}>
117-
<StyledTrigger ref={forwardedRef}>
118-
<StyledValue />
119-
<ChevronDown />
120-
</StyledTrigger>
121-
<StyledContent>
122-
<StyledScrollUpButton>
123-
<ChevronUp />
124-
</StyledScrollUpButton>
125-
<StyledViewport>{children}</StyledViewport>
126-
<StyledScrollDownButton>
127-
<ChevronDown />
128-
</StyledScrollDownButton>
129-
</StyledContent>
130-
</Root>
131-
)
132-
})
116+
export const Select = forwardRef<ElementRef<typeof Root>, SelectProps>(
117+
({ label, id, children, ...props }, forwardedRef) => {
118+
const labelId = useLabelContext()
119+
return (
120+
<>
121+
{label && (
122+
<Label variant="above" htmlFor={id || labelId}>
123+
{label}
124+
</Label>
125+
)}
126+
<Root {...props}>
127+
<StyledTrigger
128+
aria-labelledby={labelId}
129+
id={id || label}
130+
ref={forwardedRef}
131+
>
132+
<StyledValue />
133+
<ChevronDown />
134+
</StyledTrigger>
135+
<StyledContent>
136+
<StyledScrollUpButton>
137+
<ChevronUp />
138+
</StyledScrollUpButton>
139+
<StyledViewport>{children}</StyledViewport>
140+
<StyledScrollDownButton>
141+
<ChevronDown />
142+
</StyledScrollDownButton>
143+
</StyledContent>
144+
</Root>
145+
</>
146+
)
147+
}
148+
)
133149

134-
type AbstractSelectItemProps = ComponentProps<typeof Item>
150+
type SelectItemProps = ComponentProps<typeof Item>
135151

136-
const AbstractedSelectItem = forwardRef<
152+
export const SelectItem = forwardRef<
137153
ElementRef<typeof StyledItem>,
138-
AbstractSelectItemProps
154+
SelectItemProps
139155
>(({ children, ...props }, forwardedRef) => {
140156
return (
141157
<StyledItem {...props} ref={forwardedRef}>
142-
<ItemText>{children}</ItemText>
143158
<StyledItemIndicator>
144159
<Check />
145160
</StyledItemIndicator>
161+
<ItemText>{children}</ItemText>
146162
</StyledItem>
147163
)
148164
})
@@ -155,14 +171,12 @@ const AbstractedSelectItem = forwardRef<
155171
*
156172
* Based on [Radix Select](https://www.radix-ui.com/docs/primitives/components/select).
157173
*/
158-
export const Select = AbstractedSelect
159174
export const SelectTrigger = StyledTrigger
160175
export const SelectValue = StyledValue
161176
export const SelectIcon = Icon
162177
export const SelectContent = StyledContent
163178
export const SelectViewport = StyledViewport
164179
export const SelectGroup = Group
165-
export const SelectItem = AbstractedSelectItem
166180
export const SelectItemText = ItemText
167181
export const SelectItemIndicator = StyledItemIndicator
168182
export const SelectLabel = StyledLabel

src/docs/examples/form.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export const Form = () => {
7777
<Select
7878
id="country"
7979
label="Country"
80+
defaultValue={listOfCountries[0].title}
8081
onValueChange={(value) => setCountry(value)}
8182
>
8283
{listOfCountries.map((c) => (

0 commit comments

Comments
 (0)