diff --git a/packages/components/dropdown/src/Dropdown.doc.mdx b/packages/components/dropdown/src/Dropdown.doc.mdx
index 684a39839..859b58d3d 100644
--- a/packages/components/dropdown/src/Dropdown.doc.mdx
+++ b/packages/components/dropdown/src/Dropdown.doc.mdx
@@ -41,7 +41,7 @@ import { Dropdown } from '@spark-ui/dropdown'
},
'Dropdown.LeadingIcon': {
of: Dropdown.LeadingIcon,
- description: "Prepend a decorative icon inside the input (to the left).",
+ description: 'Prepend a decorative icon inside the input (to the left).',
},
'Dropdown.Popover': {
of: Dropdown.Popover,
@@ -62,6 +62,11 @@ import { Dropdown } from '@spark-ui/dropdown'
description:
'The textual part of the item. It should only contain the text you want to see in the trigger when that item is selected. It should not be styled to ensure correct positioning.',
},
+ 'Dropdown.ItemIndicator': {
+ of: Dropdown.ItemIndicator,
+ description:
+ 'Renders when the item is selected. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.',
+ },
'Dropdown.Divider': {
of: Dropdown.Divider,
description: 'Used to visually separate items in the select.',
@@ -101,7 +106,7 @@ TODO
### Item indicator
-TODO
+
### Trigger leading icon
diff --git a/packages/components/dropdown/src/Dropdown.stories.tsx b/packages/components/dropdown/src/Dropdown.stories.tsx
index 5f3c0263c..b9c5eaef2 100644
--- a/packages/components/dropdown/src/Dropdown.stories.tsx
+++ b/packages/components/dropdown/src/Dropdown.stories.tsx
@@ -198,6 +198,51 @@ export const Grouped: StoryFn = _args => {
)
}
+export const ItemIndicator: StoryFn = _args => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ To Kill a Mockingbird
+ New
+
+
+
+ War and Peace
+ New
+
+
+
+ The Idiot
+ New
+
+
+ A Picture of Dorian Gray
+ New
+
+
+
+ 1984
+ New
+
+
+ Pride and Prejudice
+ New
+
+
+
+
+
+ )
+}
+
export const FormFieldLabel: StoryFn = _args => {
return (
diff --git a/packages/components/dropdown/src/Dropdown.test.tsx b/packages/components/dropdown/src/Dropdown.test.tsx
index f4ca1c806..ecdff62a0 100644
--- a/packages/components/dropdown/src/Dropdown.test.tsx
+++ b/packages/components/dropdown/src/Dropdown.test.tsx
@@ -302,6 +302,36 @@ describe('Dropdown', () => {
expect(getItem('1984')).toHaveAttribute('aria-selected', 'true')
})
+ it('should render default selected option with proper indicator when including it', () => {
+ // Given a dropdown with a default selected value
+ render(
+
+
+
+
+
+
+
+
+ War and Peace
+
+
+
+ 1984
+
+
+
+ Pride and Prejudice
+
+
+
+
+ )
+
+ // Then the corresponding item is selected
+ expect(screen.getByLabelText('selected')).toBeVisible()
+ })
+
it('should control value', async () => {
const user = userEvent.setup()
diff --git a/packages/components/dropdown/src/DropdownItem.tsx b/packages/components/dropdown/src/DropdownItem.tsx
index e0e2f78a6..d576c824a 100644
--- a/packages/components/dropdown/src/DropdownItem.tsx
+++ b/packages/components/dropdown/src/DropdownItem.tsx
@@ -3,8 +3,6 @@ import { ReactNode } from 'react'
import { useDropdownContext } from './DropdownContext'
import { DropdownItemProvider, useDropdownItemContext } from './DropdownItemContext'
-import { DropdownItem } from './types'
-import { getIndexByKey, getItemText } from './utils'
export interface ItemProps {
disabled?: boolean
@@ -14,25 +12,19 @@ export interface ItemProps {
}
export const Item = ({ children, ...props }: ItemProps) => {
+ const { value, disabled } = props
+
return (
-
+
{children}
)
}
const ItemContent = ({ className, disabled = false, value, children }: ItemProps) => {
- const { multiple, computedItems, selectedItem, selectedItems, getItemProps, highlightedItem } =
- useDropdownContext()
-
- const { textId } = useDropdownItemContext()
-
- const index = getIndexByKey(computedItems, value)
- const itemData: DropdownItem = { disabled, value, text: getItemText(children) }
+ const { getItemProps, highlightedItem } = useDropdownContext()
- const isSelected = multiple
- ? selectedItems.some(selectedItem => selectedItem.value === value)
- : selectedItem?.value === value
+ const { textId, index, itemData, isSelected } = useDropdownItemContext()
return (
>
+ isSelected: boolean
+ itemData: DropdownItem
+ index: number
+ disabled: boolean
}
const DropdownItemContext = createContext(null)
-export const DropdownItemProvider = ({ children }: PropsWithChildren) => {
+export const DropdownItemProvider = ({
+ value,
+ disabled = false,
+ children,
+}: PropsWithChildren<{ value: string; disabled?: boolean }>) => {
+ const { multiple, computedItems, selectedItem, selectedItems } = useDropdownContext()
+
const [textId, setTextId] = useState(undefined)
+ const index = getIndexByKey(computedItems, value)
+ const itemData: DropdownItem = { disabled, value, text: getItemText(children) }
+
+ const isSelected = multiple
+ ? selectedItems.some(selectedItem => selectedItem.value === value)
+ : selectedItem?.value === value
+
return (
-
+
{children}
)
diff --git a/packages/components/dropdown/src/DropdownItemIndicator.tsx b/packages/components/dropdown/src/DropdownItemIndicator.tsx
new file mode 100644
index 000000000..44d0cb507
--- /dev/null
+++ b/packages/components/dropdown/src/DropdownItemIndicator.tsx
@@ -0,0 +1,28 @@
+import { Check } from '@spark-ui/icons/dist/icons/Check'
+import { cx } from 'class-variance-authority'
+import { ReactNode } from 'react'
+
+import { useDropdownContext } from './DropdownContext'
+import { useDropdownItemContext } from './DropdownItemContext'
+
+export interface ItemIndicatorProps {
+ children?: ReactNode
+ className?: string
+ label?: string
+}
+
+export const ItemIndicator = ({ className, children, label }: ItemIndicatorProps) => {
+ const { disabled, isSelected } = useDropdownItemContext()
+ const { multiple } = useDropdownContext()
+ const childElement =
+ children || (multiple ? : ✓)
+
+ return (
+
+ {isSelected && childElement}
+
+ )
+}
+
+ItemIndicator.id = 'ItemIndicator'
+ItemIndicator.displayName = 'Dropdown.ItemIndicator'
diff --git a/packages/components/dropdown/src/index.ts b/packages/components/dropdown/src/index.ts
index 4f071f298..b569a6d28 100644
--- a/packages/components/dropdown/src/index.ts
+++ b/packages/components/dropdown/src/index.ts
@@ -5,6 +5,7 @@ import { DropdownProvider, useDropdownContext } from './DropdownContext'
import { Divider } from './DropdownDivider'
import { Group } from './DropdownGroup'
import { Item } from './DropdownItem'
+import { ItemIndicator } from './DropdownItemIndicator'
import { Items } from './DropdownItems'
import { ItemText } from './DropdownItemText'
import { Label } from './DropdownLabel'
@@ -20,6 +21,7 @@ export const Dropdown: FC & {
Item: typeof Item
Items: typeof Items
ItemText: typeof ItemText
+ ItemIndicator: typeof ItemIndicator
Label: typeof Label
Popover: typeof Popover
Divider: typeof Divider
@@ -31,6 +33,7 @@ export const Dropdown: FC & {
Item,
Items,
ItemText,
+ ItemIndicator,
Label,
Popover,
Divider,
@@ -41,9 +44,10 @@ export const Dropdown: FC & {
Dropdown.displayName = 'Dropdown'
Group.displayName = 'Dropdown.Group'
-Item.displayName = 'Dropdown.Item'
Items.displayName = 'Dropdown.Items'
+Item.displayName = 'Dropdown.Item'
ItemText.displayName = 'Dropdown.ItemText'
+ItemIndicator.displayName = 'Dropdown.ItemIndicator'
Label.displayName = 'Dropdown.Label'
Popover.displayName = 'Dropdown.Popover'
Divider.displayName = 'Dropdown.Divider'