From 632f842b5130c81332766e0d84ed161bde74d3e2 Mon Sep 17 00:00:00 2001 From: Kamil Emeleev Date: Fri, 3 Jul 2026 18:46:56 +0300 Subject: [PATCH 1/4] feat(Tree): add `Tree.ItemContentText`, `Tree.ItemContentAddon`, and item alignment --- .../components/src/components/Tree/Tree.css | 7 + .../components/src/components/Tree/Tree.mdx | 22 +- .../src/components/Tree/Tree.stories.tsx | 237 +++++++++--------- .../components/src/components/Tree/Tree.tsx | 5 + .../Tree/components/TreeItem/TreeItem.tsx | 2 + .../Tree/components/TreeItem/types.ts | 11 +- .../TreeItemContent/TreeItemContent.tsx | 6 +- 7 files changed, 160 insertions(+), 130 deletions(-) diff --git a/packages/components/src/components/Tree/Tree.css b/packages/components/src/components/Tree/Tree.css index cfcd23c87..b324d56a7 100644 --- a/packages/components/src/components/Tree/Tree.css +++ b/packages/components/src/components/Tree/Tree.css @@ -49,6 +49,13 @@ } } +.kbq-TreeItem:has([data-slot='list-item-addon']) { + [data-slot='chevron'], + [data-slot='checkbox'] { + margin-inline-end: var(--kbq-size-s); + } +} + .kbq-TreeLoader { display: flex; align-items: center; diff --git a/packages/components/src/components/Tree/Tree.mdx b/packages/components/src/components/Tree/Tree.mdx index 36f8b357d..26efd5162 100644 --- a/packages/components/src/components/Tree/Tree.mdx +++ b/packages/components/src/components/Tree/Tree.mdx @@ -37,6 +37,8 @@ Use these components to build hierarchical navigation, selection, and lazy loadi - `Tree` — Root container for hierarchical items. - `Tree.Item` — Defines a node in the tree (leaf or branch). - `Tree.ItemContent` — Customizes row content (text, icons, slots). +- `Tree.ItemContentText` — Displays text and an optional caption in an item. +- `Tree.ItemContentAddon` — Displays an icon, badge, or other secondary content in an item. - `Tree.LoadMoreItem` — Triggers and displays async loading state for additional items. ## Content @@ -53,12 +55,20 @@ Use `selectionBehavior="toggle"` to render checkbox selection controls. -## Slots +## Item content -Use `Tree.ItemContent` to customize item layout. -For example, you can add icons, typography, badges, and slot-specific props for chevron and checkbox controls. +Tree items can be composed with helper components: - +- `Tree.ItemContentText` for text and captions. +- `Tree.ItemContentAddon` for icons, badges, or other secondary content. + + + +## Item actions + +Add an extra action to an item, revealed on hover or focus. + + ## Empty state @@ -92,7 +102,3 @@ Tree supports progressive loading with `Tree.LoadMoreItem`. Combine it with `useAsyncList` to fetch nested data on demand. - -## Other examples - - diff --git a/packages/components/src/components/Tree/Tree.stories.tsx b/packages/components/src/components/Tree/Tree.stories.tsx index 03aa1109e..f8cd01597 100644 --- a/packages/components/src/components/Tree/Tree.stories.tsx +++ b/packages/components/src/components/Tree/Tree.stories.tsx @@ -1,11 +1,16 @@ import { useState } from 'react'; -import { IconEllipsisVertical16, IconFolder16 } from '@koobiq/react-icons'; +import { + IconCircle16, + IconEllipsisVertical16, + IconFolder16, +} from '@koobiq/react-icons'; import { Collection } from '@koobiq/react-primitives'; import type { Meta, StoryObj } from '@storybook/react'; -import { FlexBox, useAsyncList } from '../../index'; +import { useAsyncList } from '../../index'; import type { Selection } from '../../index'; +import { Badge } from '../Badge'; import { IconButton } from '../IconButton'; import { spacing } from '../layout'; import { Menu } from '../Menu'; @@ -22,10 +27,12 @@ const meta = { subcomponents: { 'Tree.Item': Tree.Item, 'Tree.ItemContent': Tree.ItemContent, + 'Tree.ItemContentText': Tree.ItemContentText, + 'Tree.ItemContentAddon': Tree.ItemContentAddon, 'Tree.LoadMoreItem': Tree.LoadMoreItem, }, argTypes: {}, - tags: ['status:new', 'date:2026-03-02'], + tags: ['status:new', 'date:2026-07-03'], } satisfies Meta; export default meta; @@ -222,7 +229,95 @@ export const Content: Story = { }, }; -export const Slots: Story = { +export const ItemContent: Story = { + name: 'Item content', + parameters: { + layout: 'padded', + }, + render: function Render() { + const longText = + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. A at cupiditate dolor itaque molestias quasi quisquam quo. Deleniti ducimus fugit nulla repudiandae tenetur. Aliquid autem corporis culpa debitis exercitationem inventore labore nihil officia recusandae veniam. Beatae, doloribus, suscipit. Aut beatae consectetur consequuntur cum hic, obcaecati quia sunt temporibus unde vel!'; + + return ( + + + + + + + app + + + + + index.html + + + + + + + + + + + .gitignore + + + Badge + + + + + + + + + + README.md + + + + + + + + + + + {longText} + + + + + + + + + + {longText} + + + Badge + + + + + ); + }, +}; + +export const ItemActions: Story = { + name: 'Item actions', parameters: { layout: 'padded', }, @@ -240,22 +335,28 @@ export const Slots: Story = { {({ isHovered, isFocusVisibleWithin }) => ( <> - {type === 'directory' && } - {title} + {type === 'directory' && ( + + + + )} + {title} {(isHovered || isFocusVisibleWithin || isMenuOpen) && ( ( - - - + + + + + )} > Edit @@ -547,107 +648,3 @@ export const AsyncLoading: Story = { ); }, }; - -export const Examples: Story = { - parameters: { - layout: 'padded', - }, - render: (args) => ( - - - app - - Http - - index.html - - - - Providers - - EventServiceProvider.js - - - - - config - - app.js - - - database.js - - - - - - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus - asperiores delectus doloremque fugiat illo laudantium nesciunt - omnis. Aliquam, earum, velit? - - - - - - Lorem ipsum dolor sit amet, consectetur adipisicing elit. - Accusamus asperiores delectus doloremque fugiat illo laudantium - nesciunt omnis. Aliquam, earum, velit? - - - - - - .env - - - .gitignore - - - README.md - - - - - - Lorem ipsum dolor sit amet, consectetur adipisicing elit. - Accusamus asperiores delectus doloremque fugiat illo laudantium - nesciunt omnis. Aliquam, earum, velit? - - - Lorem ipsum dolor sit amet, consectetur adipisicing elit. - Accusamus asperiores delectus doloremque fugiat illo laudantium - nesciunt omnis. Aliquam, earum, velit? - - - - - - ), -}; diff --git a/packages/components/src/components/Tree/Tree.tsx b/packages/components/src/components/Tree/Tree.tsx index 4bb946ec0..b55913ca3 100644 --- a/packages/components/src/components/Tree/Tree.tsx +++ b/packages/components/src/components/Tree/Tree.tsx @@ -6,6 +6,7 @@ import { Tree as AriaTree, composeRenderProps } from '@koobiq/react-primitives'; import './Tree.css'; import { utilClasses } from '../../styles/utility'; +import { ListItemAddon, ListItemText } from '../List/components'; import { TreeItem, TreeItemContent, TreeLoadMoreItem } from './components'; @@ -41,6 +42,8 @@ TreeComponent.displayName = 'Tree'; type CompoundedComponent = typeof TreeComponent & { Item: typeof TreeItem; ItemContent: typeof TreeItemContent; + ItemContentText: typeof ListItemText; + ItemContentAddon: typeof ListItemAddon; LoadMoreItem: typeof TreeLoadMoreItem; }; @@ -52,4 +55,6 @@ export const Tree = TreeComponent as CompoundedComponent; TreeComponent.Item = TreeItem; TreeComponent.ItemContent = TreeItemContent; +TreeComponent.ItemContentText = ListItemText; +TreeComponent.ItemContentAddon = ListItemAddon; TreeComponent.LoadMoreItem = TreeLoadMoreItem; diff --git a/packages/components/src/components/Tree/components/TreeItem/TreeItem.tsx b/packages/components/src/components/Tree/components/TreeItem/TreeItem.tsx index a3aab7bea..ebfc97895 100644 --- a/packages/components/src/components/Tree/components/TreeItem/TreeItem.tsx +++ b/packages/components/src/components/Tree/components/TreeItem/TreeItem.tsx @@ -17,12 +17,14 @@ export function TreeItem({ children, className, textValue, + align = 'center', ...props }: TreeItemProps) { return ( clsx('kbq-TreeItem', listItem, textVariant['text-normal'], className) )} diff --git a/packages/components/src/components/Tree/components/TreeItem/types.ts b/packages/components/src/components/Tree/components/TreeItem/types.ts index 132ebcc8e..45f378612 100644 --- a/packages/components/src/components/Tree/components/TreeItem/types.ts +++ b/packages/components/src/components/Tree/components/TreeItem/types.ts @@ -1,4 +1,13 @@ import type { DataAttributeProps } from '@koobiq/react-core'; import type { TreeItemProps as AriaTreeItemProps } from '@koobiq/react-primitives'; -export type TreeItemProps = Partial & DataAttributeProps; +import type { ItemPropAlign } from '../../../Collections'; + +export type TreeItemProps = Partial & + DataAttributeProps & { + /** + * Vertical alignment of the item content. + * @default 'center' + */ + align?: ItemPropAlign; + }; diff --git a/packages/components/src/components/Tree/components/TreeItemContent/TreeItemContent.tsx b/packages/components/src/components/Tree/components/TreeItemContent/TreeItemContent.tsx index 88c5ccd73..2e4c45286 100644 --- a/packages/components/src/components/Tree/components/TreeItemContent/TreeItemContent.tsx +++ b/packages/components/src/components/Tree/components/TreeItemContent/TreeItemContent.tsx @@ -39,7 +39,11 @@ export function TreeItemContent(props: TreeItemContentProps) { /> {selectionBehavior === 'toggle' && selectionMode === 'multiple' && ( - + )} {typeof children === 'function' ? children(renderProps) : children} From 21d67cec828c33c82e47dd58145c11cbeaa0ae4a Mon Sep 17 00:00:00 2001 From: Kamil Emeleev Date: Fri, 3 Jul 2026 18:53:25 +0300 Subject: [PATCH 2/4] docs(Tree): improve 'Item content' story --- packages/components/src/components/Tree/Tree.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/Tree/Tree.mdx b/packages/components/src/components/Tree/Tree.mdx index 26efd5162..46881cfc0 100644 --- a/packages/components/src/components/Tree/Tree.mdx +++ b/packages/components/src/components/Tree/Tree.mdx @@ -57,11 +57,14 @@ Use `selectionBehavior="toggle"` to render checkbox selection controls. ## Item content -Tree items can be composed with helper components: +The `Tree.ItemContent` can be composed with helper components: - `Tree.ItemContentText` for text and captions. - `Tree.ItemContentAddon` for icons, badges, or other secondary content. +The `Tree.Item` also have layout props, such as `align`, which sets vertical alignment. +Use `align="start"` for captions or multi-line content. + ## Item actions From 5a70efcd39f3814da4f92cc0fa06f907cd7ff894 Mon Sep 17 00:00:00 2001 From: Kamil Emeleev Date: Fri, 3 Jul 2026 18:53:57 +0300 Subject: [PATCH 3/4] chore(Tree): aprove api --- tools/public_api_guard/components/Tree.api.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/public_api_guard/components/Tree.api.md b/tools/public_api_guard/components/Tree.api.md index 25b078905..dfbb149b6 100644 --- a/tools/public_api_guard/components/Tree.api.md +++ b/tools/public_api_guard/components/Tree.api.md @@ -10,10 +10,13 @@ import type { ComponentPropsWithRef } from 'react'; import type { CSSProperties } from 'react'; import type { DataAttributeProps } from '@koobiq/react-core'; import type { ElementType } from 'react'; +import type { ExtendableComponentPropsWithRef } from '@koobiq/react-core'; import type { ExtendableProps } from '@koobiq/react-core'; +import { ForwardRefExoticComponent } from 'react'; import { JSX } from 'react/jsx-runtime'; import { PolyForwardComponent } from '@koobiq/react-core'; import type { ReactNode } from 'react'; +import { RefAttributes } from 'react'; import type { TreeItemContentProps as TreeItemContentProps_2 } from '@koobiq/react-primitives'; import type { TreeItemProps as TreeItemProps_2 } from '@koobiq/react-primitives'; import { TreeLoadMoreItemProps as TreeLoadMoreItemProps_2 } from '@koobiq/react-primitives'; @@ -35,6 +38,14 @@ export namespace TreeComponent { Item: typeof TreeItem; var // (undocumented) ItemContent: typeof TreeItemContent; + var // Warning: (ae-forgotten-export) The symbol "ListItemTextProps" needs to be exported by the entry point index.d.ts + // + // (undocumented) + ItemContentText: ForwardRefExoticComponent & RefAttributes>; + var // Warning: (ae-forgotten-export) The symbol "ListItemAddonProps" needs to be exported by the entry point index.d.ts + // + // (undocumented) + ItemContentAddon: ForwardRefExoticComponent & RefAttributes>; var // (undocumented) LoadMoreItem: typeof TreeLoadMoreItem; } @@ -57,7 +68,9 @@ export type TreeItemContentPropSlotProps = { }; // @public (undocumented) -export type TreeItemProps = Partial & DataAttributeProps; +export type TreeItemProps = Partial & DataAttributeProps & { + align?: ItemPropAlign; +}; // @public (undocumented) export function TreeLoadMoreItem(props: TreeLoadMoreItemProps): JSX.Element; @@ -74,6 +87,7 @@ export type TreeProps = TreeProps_2 & { // Warnings were encountered during analysis: // +// packages/components/dist/components/Tree/components/TreeItem/types.d.ts:9:5 - (ae-forgotten-export) The symbol "ItemPropAlign" needs to be exported by the entry point index.d.ts // packages/components/dist/components/Tree/components/TreeItemContent/types.d.ts:6:5 - (ae-forgotten-export) The symbol "IconButtonProps" needs to be exported by the entry point index.d.ts // packages/components/dist/components/Tree/components/TreeItemContent/types.d.ts:7:5 - (ae-forgotten-export) The symbol "CheckboxProps" needs to be exported by the entry point index.d.ts From e8bc1e410e5044bd8e4c272ccda4fb49406d4501 Mon Sep 17 00:00:00 2001 From: Kamil Emeleev Date: Fri, 3 Jul 2026 19:09:56 +0300 Subject: [PATCH 4/4] docs(Tree): add 'status:updated' tag --- packages/components/src/components/Tree/Tree.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/Tree/Tree.stories.tsx b/packages/components/src/components/Tree/Tree.stories.tsx index f8cd01597..e08bc83df 100644 --- a/packages/components/src/components/Tree/Tree.stories.tsx +++ b/packages/components/src/components/Tree/Tree.stories.tsx @@ -32,7 +32,7 @@ const meta = { 'Tree.LoadMoreItem': Tree.LoadMoreItem, }, argTypes: {}, - tags: ['status:new', 'date:2026-07-03'], + tags: ['status:updated', 'date:2026-07-03'], } satisfies Meta; export default meta;