diff --git a/src/components/Menu/Menu.stories.tsx b/src/components/Menu/Menu.stories.tsx index 1e09b379..dc7ad3d1 100644 --- a/src/components/Menu/Menu.stories.tsx +++ b/src/components/Menu/Menu.stories.tsx @@ -208,3 +208,36 @@ export const Nested = () => { ) } + +/* A `MenuCheckboxItem` are items with an indicated boolean state */ +export const MultipleMenus = () => { + const [checked, setChecked] = useState(true) + const [color, setColor] = React.useState('blue') + return ( + <> + + + + + + + Cut + + Paste + + + + + + + + + Red + Green + Blue + + + + + ) +} diff --git a/src/components/Select/Select.stories.tsx b/src/components/Select/Select.stories.tsx index d1cf1d7d..d0287db0 100644 --- a/src/components/Select/Select.stories.tsx +++ b/src/components/Select/Select.stories.tsx @@ -37,6 +37,10 @@ export const Placeholder = Template.bind({}) Placeholder.args = { placeholder: '--Select an option--', } +export const Header = Template.bind({}) +Header.args = { + header: '--Select an option--', +} export const Disabled = Template.bind({}) Disabled.args = { disabled: true, @@ -72,7 +76,7 @@ export const ConfirmDialogSelect = () => ( - One Two Three @@ -81,6 +85,15 @@ export const ConfirmDialogSelect = () => ( Six Seven + diff --git a/src/components/Select/Select.tsx b/src/components/Select/Select.tsx index ef6b8bed..d35c9bc8 100644 --- a/src/components/Select/Select.tsx +++ b/src/components/Select/Select.tsx @@ -1,11 +1,19 @@ import { useLabelContext } from '@radix-ui/react-label' import { useControllableState } from '@radix-ui/react-use-controllable-state' -import React, { ComponentProps, ElementRef, forwardRef } from 'react' +import React, { + ComponentProps, + ElementRef, + forwardRef, + useMemo, + Children, + isValidElement, +} from 'react' import type { CSSProps, VariantProps } from '../../stitches.config' import { styled } from '../../stitches.config' import { ChevronDown } from '../Icons' import { inputStyles } from '../Input/Input' import { Label } from '../Label' +import { useId } from '@radix-ui/react-id' import { Menu, MenuContent, @@ -20,6 +28,7 @@ const DEFAULT_TAG = 'input' const StyledSelect = styled(DEFAULT_TAG, inputStyles, { cursor: 'pointer', textAlign: 'left', + pointerEvents: 'none', }) export const SelectItem = MenuRadioItem @@ -54,6 +63,8 @@ type SelectProps = CSSProps & defaultValue?: string /** Supply a starting placeholder value for uncontrolled instance */ placeholder?: string + /** Supply a header for the select menu */ + header?: string /** Called on Select change with new value */ onValueChange?: (value: string) => void } & ComponentProps @@ -71,12 +82,13 @@ export const Select = forwardRef, SelectProps>( ( { label, - id, + id: idProp, value, onValueChange, defaultValue, children, placeholder, + header, disabled, ...props }, @@ -87,8 +99,25 @@ export const Select = forwardRef, SelectProps>( defaultProp: defaultValue || placeholder, onChange: onValueChange, }) + + const id = useId(idProp) const labelId = useLabelContext() + const valueToText = useMemo>(() => { + const mapped: Record = {} + Children.forEach(Children.toArray(children), (child) => { + if ( + isValidElement(child) && + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + typeof child?.props?.children === 'string' + ) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access + mapped[child.props.value] = child.props.children + } + }) + return mapped + }, [children]) + return ( <> {label && ( @@ -105,7 +134,8 @@ export const Select = forwardRef, SelectProps>( disabled={disabled} {...props} ref={ref} - value={internalValue} + placeholder={placeholder} + value={internalValue && valueToText[internalValue]} // Just there to suppress warning onChange={() => null} /> @@ -113,7 +143,7 @@ export const Select = forwardRef, SelectProps>( - {placeholder && {placeholder}} + {header && {header}} {children}