Skip to content

Commit 4dfdc3f

Browse files
fix(dropdown): pass children through when render prop is provided (#384)
1 parent a8adf02 commit 4dfdc3f

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/kumo": patch
3+
---
4+
5+
fix(dropdown): pass children through when render prop is provided on DropdownMenu.Trigger

packages/kumo-docs-astro/src/components/demos/DropdownDemo.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,33 @@ export function DropdownNestedDemo() {
143143
);
144144
}
145145

146+
/**
147+
* Demonstrates using the render prop with children to compose a custom trigger
148+
* that contains other elements. The render prop provides the trigger element,
149+
* while children are rendered inside it.
150+
*/
151+
export function DropdownAvatarTriggerDemo() {
152+
return (
153+
<DropdownMenu>
154+
<DropdownMenu.Trigger
155+
render={<button type="button" className="rounded-full" />}
156+
>
157+
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-kumo-brand text-sm font-medium text-white">
158+
MR
159+
</span>
160+
</DropdownMenu.Trigger>
161+
<DropdownMenu.Content>
162+
<DropdownMenu.Item icon={UserIcon}>Profile</DropdownMenu.Item>
163+
<DropdownMenu.Item icon={GearIcon}>Settings</DropdownMenu.Item>
164+
<DropdownMenu.Separator />
165+
<DropdownMenu.Item icon={SignOutIcon} variant="danger">
166+
Log out
167+
</DropdownMenu.Item>
168+
</DropdownMenu.Content>
169+
</DropdownMenu>
170+
);
171+
}
172+
146173
/**
147174
* Demonstrates the new LinkItem component for navigation links.
148175
* Use LinkItem instead of Item with href for cleaner, more semantic links.

packages/kumo-docs-astro/src/pages/components/dropdown.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
DropdownBasicDemo,
1515
DropdownCheckboxDemo,
1616
DropdownNestedDemo,
17+
DropdownAvatarTriggerDemo,
1718
DropdownLinkItemDemo,
1819
} from "~/components/demos/DropdownDemo";
1920

@@ -96,6 +97,16 @@ export default function Example() {
9697
<DropdownNestedDemo client:load />
9798
</ComponentExample>
9899

100+
<Heading level={3}>Custom Trigger with Avatar</Heading>
101+
<p>
102+
Use the `render` prop to customize the trigger element while passing children
103+
to render inside it. This is useful when you need a non-button trigger (like
104+
an avatar) wrapped in an accessible button element.
105+
</p>
106+
<ComponentExample demo="DropdownAvatarTriggerDemo">
107+
<DropdownAvatarTriggerDemo client:load />
108+
</ComponentExample>
109+
99110
<Heading level={3}>Navigation Links</Heading>
100111
<p>
101112
Use `DropdownMenu.LinkItem` for menu items that navigate to a URL.

packages/kumo/src/components/dropdown/dropdown.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -425,25 +425,34 @@ DropdownMenuRadioItemIndicator.displayName = "DropdownMenuRadioItemIndicator";
425425
/**
426426
* Custom Trigger that converts a single child element to the `render` prop
427427
* to avoid nested button issues with base-ui's Menu.Trigger.
428+
*
429+
* When an explicit `render` prop is provided, children are passed through
430+
* to the rendered element.
428431
*/
429432
const DropdownMenuTrigger = React.forwardRef<
430433
HTMLButtonElement,
431434
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>
432435
>(({ children, render, ...props }, ref) => {
433-
// If render prop is provided, use it directly
434-
// Otherwise, convert single child element to render prop
436+
// If render prop is explicitly provided, use it and pass children through
437+
if (render) {
438+
return (
439+
<DropdownMenuPrimitive.Trigger ref={ref} {...props} render={render}>
440+
{children}
441+
</DropdownMenuPrimitive.Trigger>
442+
);
443+
}
444+
445+
// Otherwise, auto-promote single child element to render prop
435446
const childElement = React.isValidElement(children) ? children : null;
436-
const effectiveRender = render ?? childElement;
437447

438448
return (
439449
<DropdownMenuPrimitive.Trigger
440450
ref={ref}
441451
{...props}
442-
{...(effectiveRender && {
443-
render: effectiveRender as React.ReactElement<Record<string, unknown>>,
452+
{...(childElement && {
453+
render: childElement as React.ReactElement<Record<string, unknown>>,
444454
})}
445455
>
446-
{/* Only pass children if not using as render prop */}
447456
{childElement ? undefined : children}
448457
</DropdownMenuPrimitive.Trigger>
449458
);

0 commit comments

Comments
 (0)