Skip to content

feat: support declarative children API with DepthSelect.Item compound component #2

@gfazioli

Description

@gfazioli

Description

Currently DepthSelect only accepts items via the data prop:

<DepthSelect data={[
  { value: 'v1', view: <Card>...</Card> },
  { value: 'v2', view: <Card>...</Card> },
]} />

Add support for a declarative children API using a DepthSelect.Item compound component:

<DepthSelect>
  <DepthSelect.Item value="v1"><Card>...</Card></DepthSelect.Item>
  <DepthSelect.Item value="v2"><Card>...</Card></DepthSelect.Item>
</DepthSelect>

Both APIs coexist. The data prop takes precedence over children — if data is provided and non-empty, DepthSelect.Item children are ignored. This matches Mantine's pattern (e.g., Select with data prop vs Select.Option children).

Implementation Plan

New file: DepthSelectItem.tsx

A "marker" component that doesn't render anything directly — the parent reads its props to build the items array:

export interface DepthSelectItemProps {
  value: string | number;
  children: React.ReactNode;
}

export function DepthSelectItem(_props: DepthSelectItemProps): null {
  return null;
}

DepthSelectItem.displayName = 'DepthSelectItem';

Modify DepthSelect.tsx

Add a utility to collect items from children:

function collectItemsFromChildren(children: React.ReactNode): DepthSelectItem[] {
  const items: DepthSelectItem[] = [];
  React.Children.forEach(children, (child) => {
    if (React.isValidElement(child) && child.type === DepthSelectItem) {
      items.push({ value: child.props.value, view: child.props.children });
    }
    // DepthSelect.Controls and other children are ignored here
  });
  return items;
}

Then in the component body:

const dataItems = useMemo(() => data ?? [], [data]);
const childItems = useMemo(() => collectItemsFromChildren(children), [children]);
const items = dataItems.length > 0 ? dataItems : childItems;

Update Factory type

staticComponents: {
  Item: typeof DepthSelectItem;     // NEW
  Controls: typeof DepthSelectControls;
};

Attach static component

DepthSelect.Item = DepthSelectItem;

Update exports (index.ts)

export type { DepthSelectItemProps } from './DepthSelectItem';

Precedence rules

data prop DepthSelect.Item children Result
Provided (non-empty) Any Uses data, children ignored
undefined or [] Present Uses children items
undefined or [] None Empty state

This matches Mantine's Select behavior where data prop overrides Select.Option children.

Naming convention

DepthSelect.Item follows the Mantine compound component naming: Tabs.Tab, Select.Option, Accordion.Item, Combobox.Option.

Scope

  • New file: package/src/DepthSelectItem.tsx
  • Modified: package/src/DepthSelect.tsx (Factory type, collectItemsFromChildren, static attachment)
  • Modified: package/src/index.ts (export DepthSelectItemProps)
  • New tests for children API
  • New Storybook story + docs demo
  • Backward compatible — no breaking changes

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions