Skip to content

Commit 1a57fd7

Browse files
Merge pull request #237 from commitd/stuarthendren/select
feat(select): fixes select behaviour when multiple instances
2 parents dc2122c + 0e4290f commit 1a57fd7

File tree

3 files changed

+81
-5
lines changed

3 files changed

+81
-5
lines changed

src/components/Menu/Menu.stories.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,36 @@ export const Nested = () => {
208208
</Menu>
209209
)
210210
}
211+
212+
/* A `MenuCheckboxItem` are items with an indicated boolean state */
213+
export const MultipleMenus = () => {
214+
const [checked, setChecked] = useState(true)
215+
const [color, setColor] = React.useState('blue')
216+
return (
217+
<>
218+
<Menu>
219+
<MenuTrigger>
220+
<Button>Trigger</Button>
221+
</MenuTrigger>
222+
<MenuContent>
223+
<MenuCheckboxItem checked={checked} onCheckedChange={setChecked}>
224+
Cut
225+
</MenuCheckboxItem>
226+
<MenuCheckboxItem checked={false}>Paste</MenuCheckboxItem>
227+
</MenuContent>
228+
</Menu>
229+
<Menu>
230+
<MenuTrigger>
231+
<Button>Trigger</Button>
232+
</MenuTrigger>
233+
<MenuContent>
234+
<MenuRadioGroup value={color} onValueChange={setColor}>
235+
<MenuRadioItem value="red">Red</MenuRadioItem>
236+
<MenuRadioItem value="green">Green</MenuRadioItem>
237+
<MenuRadioItem value="blue">Blue</MenuRadioItem>
238+
</MenuRadioGroup>
239+
</MenuContent>
240+
</Menu>
241+
</>
242+
)
243+
}

src/components/Select/Select.stories.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export const Placeholder = Template.bind({})
3737
Placeholder.args = {
3838
placeholder: '--Select an option--',
3939
}
40+
export const Header = Template.bind({})
41+
Header.args = {
42+
header: '--Select an option--',
43+
}
4044
export const Disabled = Template.bind({})
4145
Disabled.args = {
4246
disabled: true,
@@ -72,7 +76,7 @@ export const ConfirmDialogSelect = () => (
7276
<Box variant="fullscreen">
7377
<ConfirmDialog>
7478
<ConfirmDialogContent description="This is a test" title="Test Select">
75-
<Select label="Label" placeholder="Placeholder">
79+
<Select label="Label" placeholder="Placeholder" header="Pick a number">
7680
<SelectItem value="one">One</SelectItem>
7781
<SelectItem value="two">Two</SelectItem>
7882
<SelectItem value="three">Three</SelectItem>
@@ -81,6 +85,15 @@ export const ConfirmDialogSelect = () => (
8185
<SelectItem value="six">Six</SelectItem>
8286
<SelectItem value="seven">Seven</SelectItem>
8387
</Select>
88+
<Select label="Label" placeholder="Placeholder">
89+
<SelectItem value="one">A</SelectItem>
90+
<SelectItem value="two">B</SelectItem>
91+
<SelectItem value="three">C</SelectItem>
92+
<SelectItem value="four">D</SelectItem>
93+
<SelectItem value="five">E</SelectItem>
94+
<SelectItem value="six">F</SelectItem>
95+
<SelectItem value="seven">G</SelectItem>
96+
</Select>
8497
<ConfirmDialogActions confirm="Confirm" onConfirm={action('Confirm')} />
8598
</ConfirmDialogContent>
8699
<ConfirmDialogTrigger>

src/components/Select/Select.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { useLabelContext } from '@radix-ui/react-label'
22
import { useControllableState } from '@radix-ui/react-use-controllable-state'
3-
import React, { ComponentProps, ElementRef, forwardRef } from 'react'
3+
import React, {
4+
ComponentProps,
5+
ElementRef,
6+
forwardRef,
7+
useMemo,
8+
Children,
9+
isValidElement,
10+
} from 'react'
411
import type { CSSProps, VariantProps } from '../../stitches.config'
512
import { styled } from '../../stitches.config'
613
import { ChevronDown } from '../Icons'
714
import { inputStyles } from '../Input/Input'
815
import { Label } from '../Label'
16+
import { useId } from '@radix-ui/react-id'
917
import {
1018
Menu,
1119
MenuContent,
@@ -20,6 +28,7 @@ const DEFAULT_TAG = 'input'
2028
const StyledSelect = styled(DEFAULT_TAG, inputStyles, {
2129
cursor: 'pointer',
2230
textAlign: 'left',
31+
pointerEvents: 'none',
2332
})
2433
export const SelectItem = MenuRadioItem
2534

@@ -54,6 +63,8 @@ type SelectProps = CSSProps &
5463
defaultValue?: string
5564
/** Supply a starting placeholder value for uncontrolled instance */
5665
placeholder?: string
66+
/** Supply a header for the select menu */
67+
header?: string
5768
/** Called on Select change with new value */
5869
onValueChange?: (value: string) => void
5970
} & ComponentProps<typeof DEFAULT_TAG>
@@ -71,12 +82,13 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
7182
(
7283
{
7384
label,
74-
id,
85+
id: idProp,
7586
value,
7687
onValueChange,
7788
defaultValue,
7889
children,
7990
placeholder,
91+
header,
8092
disabled,
8193
...props
8294
},
@@ -87,8 +99,25 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
8799
defaultProp: defaultValue || placeholder,
88100
onChange: onValueChange,
89101
})
102+
103+
const id = useId(idProp)
90104
const labelId = useLabelContext()
91105

106+
const valueToText = useMemo<Record<string, string>>(() => {
107+
const mapped: Record<string, string> = {}
108+
Children.forEach(Children.toArray(children), (child) => {
109+
if (
110+
isValidElement(child) &&
111+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
112+
typeof child?.props?.children === 'string'
113+
) {
114+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
115+
mapped[child.props.value] = child.props.children
116+
}
117+
})
118+
return mapped
119+
}, [children])
120+
92121
return (
93122
<>
94123
{label && (
@@ -105,15 +134,16 @@ export const Select = forwardRef<ElementRef<typeof StyledSelect>, SelectProps>(
105134
disabled={disabled}
106135
{...props}
107136
ref={ref}
108-
value={internalValue}
137+
placeholder={placeholder}
138+
value={internalValue && valueToText[internalValue]}
109139
// Just there to suppress warning
110140
onChange={() => null}
111141
/>
112142
<ChevronDown />
113143
</Root>
114144
</MenuTrigger>
115145
<MenuContent align="start" sideOffset={4} alignOffset={4}>
116-
{placeholder && <MenuItem disabled>{placeholder}</MenuItem>}
146+
{header && <MenuItem disabled>{header}</MenuItem>}
117147
<MenuRadioGroup value={internalValue} onValueChange={setValue}>
118148
{children}
119149
</MenuRadioGroup>

0 commit comments

Comments
 (0)