Skip to content

feat(OneDataCollection): add programmatic nested-table expansion control#4611

Open
AndresRojoFactorial wants to merge 11 commits into
mainfrom
feat/Nested-DataCollection-ProgramaticFolding
Open

feat(OneDataCollection): add programmatic nested-table expansion control#4611
AndresRojoFactorial wants to merge 11 commits into
mainfrom
feat/Nested-DataCollection-ProgramaticFolding

Conversation

@AndresRojoFactorial

@AndresRojoFactorial AndresRojoFactorial commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Description

Nested tables in the Data Collection pattern could only be expanded by clicking each chevron, with no way to control expansion from code. This PR adds a programmatic expansion system to the table visualization: a declarative auto-expansion policy, an imperative controller hook (useNestedTable), control over how children pages load (keep the "show more" row vs eager-load every page), targeted deep-node reveal, expand animations, and an onExpandedChange callback.

Storybook

📖 Docs — Programmatic Expansion Control (Table docs page, new section — link resolves once this branch's Storybook is deployed; locally: http://localhost:6006/?path=/docs/patterns-data-collection-visualizations-table--documentation#programmatic-expansion-control)

Interactive stories:

Implementation details

  • feat: add nested option to TableVisualizationOptionsdefaultExpanded (boolean | depth number | ({ item, depth }) => boolean), defaultExpandedChildren ("paginated" | "all"), control, expandAnimation, onExpandedChange

  • feat: add useNestedTable() controller hook — expand/collapse/toggle by item id or predicate, expandAll({ depth, where, children }), collapseAll, expandTo(idPath), isExpanded, getExpandedItems; operations invoked before the table mounts are queued and applied on mount

    How expansion is resolved

    Each rendered expandable row resolves its state as explicit override → active policy → collapsed. Policies (defaultExpanded, expandAll) are re-evaluated for rows revealed later by lazy loading, so auto-expansion cascades naturally through a tree that loads on demand, while a manual collapse always wins over an active policy. expandTo keeps not-yet-rendered ids pending and consumes them as each level registers, so a single call reveals a node buried under collapsed, unloaded ancestors.

  • feat: children fetching is now driven by the resolved open state (effect-based) instead of the click handler, so policy/controller-opened rows lazy-load their children too; eager mode (children: "all") chains pages until the adapter reports exhaustion (hasMore: false, reaching total, or a fetch error) — no artificial page limit — and a resolved page that adds no records is treated as end-of-pagination, so a misbehaving adapter cannot trap it in a request loop

  • feat: add expand animations (fade, stagger, slide, pop) — GPU-composited (opacity/transform only), staggered by sibling index and depth so cached subtrees still cascade level by level, disabled under prefers-reduced-motion

  • fix: filters/sortings changes now reset expansion in the provider only (overrides cleared, policy preserved) — the previous per-row collapse callback left a residual explicit-collapse override on an arbitrary row and emitted spurious onExpandedChange events

  • docs: new "Programmatic Expansion Control" section in the Table MDX + 4 interactive stories

  • feat: nested rows aligned with search/filters — fetchChildren receives the debounced search term (passed as undefined when empty, like the main collection fetch), the children cache resets on search changes (⚠️ intentional behavior change: expanded rows collapse and cached children are refetched when the search term changes, matching what filters already did), and expansion criteria receive hasActiveFilters in their context (defaultExpanded: (ctx) => ctx.hasActiveFilters auto-expands while searching and collapses back when cleared) — new story TableNestedSearchAlignedExpansion

  • feat: controlled declarative expansion via nested.expanded — mirrors the Graph's revealNodeId/focusOnEntry pattern and is driven by useControllableState (the repo's controlled-prop convention, same as F0Slider/F0Accordion): while defined it is the single source of truth — defaultExpanded is ignored and expandAll/collapseAll are complete no-ops, while targeted expand/collapse/toggle/expandTo layer per-row overrides that are cleared whenever the criteria changes by reference; precedence expanded (controlled) > control (targeted overrides) > defaultExpanded (mount-only); a dev-only warning fires when a non-memoized predicate changes identity repeatedly — new story TableNestedControlledExpansion

  • feat: nested rows are keyed by data identity, not position — source.idProvider(item) when the source defines one (the same convention Kanban and item navigation already use), falling back to item.id, and to the position only for id-less items, always namespaced by the parent row's key: expansion survives sibling reorders from a refetch, equal positions in different branches/groups cannot collide, and controller targets / expandTo paths match the same identity with String() normalization (expand("42") finds id: 42); identities must be unique among siblings (documented)

  • fix: a filters/sortings/search reset now cancels any children fetch still in flight (and re-arms the auto-load guard), so a slow response can no longer repopulate the cache with children of the previous filters

  • fix: an undefined → "" search transition (e.g. setSearch("") on mount) is normalized and no longer collapses expanded rows or clears the children cache; hasActiveFilters uses the debounced normalized search exclusively (in sync with cache invalidation) and delegates the filters side to getActiveFilterKeys — the collection's canonical per-filter-type isEmpty semantics — so a filter cleared to its empty value no longer counts as active

  • fix: re-expanding a row whose fetchChildren returned an empty list refetches again (restores the pre-refactor behavior; guarded to the collapsed→expanded transition so an empty result cannot cause a fetch loop)

  • perf: the motion-wrapped Row is a lazy module-scope singleton (motion does not cache custom components), the expand-animation variants are precomputed, and the nested context value is memoized so unrelated parent re-renders no longer re-render every row; the row registry re-registers on item identity change so controller predicates, getExpandedItems and onExpandedChange always see post-refetch field values

  • test: 40 new unit tests (NestedExpansionControl.test.tsx) covering policies, controller operations, predicate targets, expandTo path resolution, pre-mount queueing and controller unbind/rebind, filters reset, eager-loading guards (lying adapters included), animations, search normalization, empty-children refetch, the controlled expanded mode (incl. no-op expandAll/collapseAll), identity keying (id-less branches, sibling reorders, numeric ids, idProvider targeting) and stale in-flight fetch cancellation

Testing

  • pnpm vitest on src/patterns/OneDataCollection/visualizations/collection/Table: 130 tests passing (9 files), 630 across the whole OneDataCollection suite — no regressions in NestedSelection / Table / EditableTable
  • pnpm tsc, pnpm lint, pnpm format:check all green
  • Quality gate (code review / a11y / storybook / test-coverage subagent reviews) run; the one pre-existing a11y gap (expand chevron is a div[onClick] without role/aria-expanded in OneTable/NestedCell) predates this PR and is left for a follow-up
Screen.Recording.2026-07-10.at.11.56.23.mov

🤖 Generated with Claude Code

@AndresRojoFactorial
AndresRojoFactorial requested a review from a team as a code owner July 3, 2026 12:09
@github-actions github-actions Bot added feat react Changes affect packages/react labels Jul 3, 2026
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

✅ No New Circular Dependencies

No new circular dependencies detected. Current count: 0

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

📦 Alpha Package Version Published

Use pnpm i github:factorialco/f0#npm/alpha-pr-4611 to install the package

Use pnpm i github:factorialco/f0#0cba9c2f6b94d11262e268c7e5b30e5c730a77c0 to install this specific commit

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🔍 Visual review for your branch is published 🔍

Here are the links to:

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for packages/react

Status Category Percentage Covered / Total
🔵 Lines 61.01% 21786 / 35707
🔵 Statements 60.07% 22903 / 38121
🔵 Functions 53.64% 5161 / 9620
🔵 Branches 54.09% 15907 / 29407
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/hooks/datasource/types/datasource.typings.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/exports.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/Table.tsx 95.13% 86.72% 92% 95.65% 77-80, 571, 695, 728, 746-753, 804-811
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/types.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/components/NestedRow.tsx 98.26% 97.01% 95% 98.18% 519, 536-543
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/components/Row.tsx 100% 94.59% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/hooks/useLoadChildren.ts 91.95% 79.06% 91.66% 93.97% 50, 193, 213-214, 241-243
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/nested/animations.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/nested/index.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/nested/internal-types.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/nested/types.ts 100% 100% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/nested/useNestedTable.ts 100% 62.5% 100% 100%
packages/react/src/patterns/OneDataCollection/visualizations/collection/Table/providers/NestedProvider.tsx 95.53% 87.5% 100% 99.36% 222, 234, 239, 271, 328, 396, 409, 523-525
Generated in workflow #15870 for commit 426ab66 by the Vitest Coverage Report Action

@AndresRojoFactorial
AndresRojoFactorial force-pushed the feat/Nested-DataCollection-ProgramaticFolding branch 3 times, most recently from 7d58402 to 2a95bff Compare July 10, 2026 09:13
@factorialco factorialco deleted a comment from github-actions Bot Jul 17, 2026
@AndresRojoFactorial
AndresRojoFactorial force-pushed the feat/Nested-DataCollection-ProgramaticFolding branch from abfb9d1 to 31ef70e Compare July 17, 2026 11:26
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Breaking public API changes (27)

These public exports were renamed/removed, or had a property/parameter removed, retyped, or newly required compared to main — that breaks consumers. Adding new exports or new optional props is always safe and is not flagged. If a breaking change is intentional, note it in the PR description and use a feat!:/BREAKING CHANGE commit so the release is a major bump.

Comparing f0, experimental and ai against main. Adding components, types, or optional props is safe. This check is non-blocking.

f0

  • ✏️ F0ArrayConfigbreaking change

    • source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0ArrayConfig<T, R>
    // after
    F0ArrayConfig<T, R>
  • ✏️ F0Fieldbreaking change

    • source.fetchChildren.__0 changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0Field
    // after
    F0Field
  • ✏️ F0FieldConfigbreaking change

    • source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0FieldConfig<T, R>
    // after
    F0FieldConfig<T, R>
  • ✏️ f0FormFieldbreaking change

    • config.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    typeof f0FormField
    // after
    typeof f0FormField
  • ✏️ F0NumberFieldConfigbreaking change

    • source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0NumberFieldConfig<R>
    // after
    F0NumberFieldConfig<R>
  • ✏️ F0SelectConfigbreaking change

    • source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0SelectConfig<T, R>
    // after
    F0SelectConfig<T, R>
  • ✏️ F0SelectFieldbreaking change

    • source.fetchChildren.__0 changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0SelectField
    // after
    F0SelectField
  • ✏️ F0SelectPropsbreaking change

    • source.fetchChildren.__0 changed: { item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0SelectProps<T, R>
    // after
    F0SelectProps<T, R>
  • ✏️ F0StringConfigbreaking change

    • source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: R; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0StringConfig<TValue, TConfig, R>
    // after
    F0StringConfig<TValue, TConfig, R>
  • ✏️ F0ZodTypebreaking change

    • _f0Config.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: Record<string, unknown>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: Record<string, unknown>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    F0ZodType<T>
    // after
    F0ZodType<T>
  • ✏️ getF0Configbreaking change

    • return.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: Record<string, unknown>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: Record<string, unknown>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    (schema: ZodTypeAny) => F0FieldConfig<string | number, Record<string, unknown>>
    // after
    (schema: ZodTypeAny) => F0FieldConfig<string | number, Record<string, unknown>>
  • ✏️ inferFieldTypebreaking change

    • config.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: Record<string, unknown>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: Record<string, unknown>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    (schema: ZodTypeAny, config: F0FieldConfig<string | number, Record<string, unknown>>) => F0FieldType
    // after
    (schema: ZodTypeAny, config: F0FieldConfig<string | number, Record<string, unknown>>) => F0FieldType
  • ✏️ InFilterDefinitionbreaking change

    • options.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: any; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: any; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    InFilterDefinition<T, R>
    // after
    InFilterDefinition<T, R>
  • ✏️ useSelectablebreaking change

    • __0.source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; }{ item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; search?: string; }
    before → after
    // before
    <R extends RecordType, Filters extends FiltersDefinition, Sortings extends SortingsDefinition, Grouping extends GroupingDefinition<R>>({ data, paginationInfo, source, selectionMode, selectedState, onSelectItems, disableSelectAll, isSearchActive, allPagesSelection, resetOnPageChange, preserveSelectionOnDatasetChange, getRenderedSelectableEntries, renderedSelectableCount, }: UseSelectableProps<R, Fi 
    // after
    <R extends RecordType, Filters extends FiltersDefinition, Sortings extends SortingsDefinition, Grouping extends GroupingDefinition<R>>({ data, paginationInfo, source, selectionMode, selectedState, onSelectItems, disableSelectAll, isSearchActive, allPagesSelection, resetOnPageChange, preserveSelectionOnDatasetChange, getRenderedSelectableEntries, renderedSelectableCount, }: UseSelectableProps<R, Fi 

experimental

  • ✏️ BreadcrumbCollectionSelectItemTypebreaking change

    • source.fetchChildren.args changed: [{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }][{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }]
    before → after
    // before
    BreadcrumbCollectionSelectItemType
    // after
    BreadcrumbCollectionSelectItemType
  • ✏️ BreadcrumbItemTypebreaking change

    • source.fetchChildren.__0 changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • source.fetchChildren.args changed: [{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }][{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }]
    before → after
    // before
    BreadcrumbItemType
    // after
    BreadcrumbItemType
  • ✏️ BreadcrumbSelectbreaking change

    • __0.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    <T extends string, R = unknown>({ ...props }: ({ withApplySelection?: boolean; onChangeSelectedOption?: (option: F0SelectItemObject<T, ResolvedRecordType<R>>, checked: boolean) => void; children?: ReactNode; open?: boolean; showSearchBox?: boolean; searchBoxPlaceholder?: string; onSearchChange?: (value: string) => void; searchValue?: string; onOpenChange?: (open: boolean) => void; onFiltersChange? 
    // after
    <T extends string, R = unknown>({ ...props }: ({ withApplySelection?: boolean; onChangeSelectedOption?: (option: F0SelectItemObject<T, ResolvedRecordType<R>>, checked: boolean) => void; children?: ReactNode; open?: boolean; showSearchBox?: boolean; searchBoxPlaceholder?: string; onSearchChange?: (value: string) => void; searchValue?: string; onOpenChange?: (open: boolean) => void; onFiltersChange? 
  • ✏️ BreadcrumbSelectItemTypebreaking change

    • source.fetchChildren.__0 changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    BreadcrumbSelectItemType
    // after
    BreadcrumbSelectItemType
  • ✏️ BreadcrumbSelectPropsbreaking change

    • source.fetchChildren.__0 changed: { item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    ({ withApplySelection?: boolean; onChangeSelectedOption?: (option: F0SelectItemObject<T, ResolvedRecordType<R>>, checked: boolean) => void; children?: ReactNode; open?: boolean; showSearchBox?: boolean; searchBoxPlaceholder?: string; onSearchChange?: (value: string) => void; searchValue?: string; onOpenChange?: (open: boolean) => void; onFiltersChange?: (filters: FiltersState<FiltersDefinition<str 
    // after
    ({ withApplySelection?: boolean; onChangeSelectedOption?: (option: F0SelectItemObject<T, ResolvedRecordType<R>>, checked: boolean) => void; children?: ReactNode; open?: boolean; showSearchBox?: boolean; searchBoxPlaceholder?: string; onSearchChange?: (value: string) => void; searchValue?: string; onOpenChange?: (open: boolean) => void; onFiltersChange?: (filters: FiltersState<FiltersDefinition<str 
  • ✏️ BreadcrumbStatebreaking change

    • headItem.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    • headItem.source.fetchChildren changed: (args_0: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any(args_0: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    BreadcrumbState
    // after
    BreadcrumbState
  • ✏️ CollectionSelectSourceDefinitionbreaking change

    • fetchChildren.args.pop.return changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.push.items changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.concat.items changed: Array<ConcatArray<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>>Array<ConcatArray<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>>
    • fetchChildren.args.concat.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.reverse.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.shift.return changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.slice.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.sort.compareFn changed: (a: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, b: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => number(a: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, b: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => number
    • fetchChildren.args.sort.return changed: [{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }][{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }]
    • fetchChildren.args.splice.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.unshift.items changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.indexOf.searchElement changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.lastIndexOf.searchElement changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.every.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => value is S(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => value is S
    • fetchChildren.args.some.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => unknown(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => unknown
    • fetchChildren.args.forEach.callbackfn changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => void(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => void
    • fetchChildren.args.map.callbackfn changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => U(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => U
    • fetchChildren.args.filter.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => value is S(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => value is S
    • fetchChildren.args.reduce.callbackfn changed: (previousValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, currentValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, currentIndex: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }(previousValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, currentValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, currentIndex: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.reduce.return changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.reduceRight.callbackfn changed: (previousValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, currentValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, currentIndex: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }(previousValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, currentValue: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, currentIndex: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.reduceRight.return changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.find.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, obj: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => value is S(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, obj: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => value is S
    • fetchChildren.args.findIndex.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, obj: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => unknown(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, obj: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => unknown
    • fetchChildren.args.fill.value changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.fill.return changed: [{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }][{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }]
    • fetchChildren.args.copyWithin.return changed: [{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }][{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }]
    • fetchChildren.args.entries.return changed: ArrayIterator<[number, { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }]>ArrayIterator<[number, { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }]>
    • fetchChildren.args.values.return changed: ArrayIterator<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>ArrayIterator<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.includes.searchElement changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.flatMap.callback changed: (this: This, value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => U | ReadonlyArray<U>(this: This, value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => U | ReadonlyArray<U>
    • fetchChildren.args.at.return changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.findLast.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => value is S(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => value is S
    • fetchChildren.args.findLastIndex.predicate changed: (value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>) => unknown(value: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, index: number, array: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>) => unknown
    • fetchChildren.args.toReversed.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.toSorted.compareFn changed: (a: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }, b: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => number(a: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }, b: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => number
    • fetchChildren.args.toSorted.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.toSpliced.items changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.toSpliced.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.with.value changed: { item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    • fetchChildren.args.with.return changed: Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>Array<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args.__@iterator.return changed: ArrayIterator<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }>ArrayIterator<{ item: RecordType; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }>
    • fetchChildren.args index signature changed
    before → after
    // before
    CollectionSelectSourceDefinition
    // after
    CollectionSelectSourceDefinition
  • ✏️ F0SelectPropsbreaking change

    • source.fetchChildren.__0 changed: { item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0SelectProps<T, R>
    // after
    F0SelectProps<T, R>
  • ✏️ InFilterDefinitionbreaking change

    • options.source.fetchChildren changed: ({ item, filters, pagination, sortings, }: { item: any; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }) => any({ item, filters, pagination, sortings, search, }: { item: any; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }) => any
    before → after
    // before
    InFilterDefinition<T, R>
    // after
    InFilterDefinition<T, R>
  • ✏️ OneDataCollectionbreaking change

    • props.source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; }{ item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; search?: string; }
    before → after
    // before
    OneDataCollectionGeneric
    // after
    OneDataCollectionGeneric
  • ✏️ SelectPropsbreaking change

    • source.fetchChildren.__0 changed: { item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; }{ item: ResolvedRecordType<R>; filters?: FiltersState<FiltersDefinition<string>>; pagination?: ChildrenPaginationInfo; sortings?: { field: string; order: SortOrder; }; search?: string; }
    before → after
    // before
    F0SelectProps<T, R>
    // after
    F0SelectProps<T, R>
  • ✏️ useDataCollectionItemNavigationbreaking change

    • props.source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; }{ item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; search?: string; }
    • return.dataSource.fetchChildren.__0 changed: { item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; }{ item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; search?: string; }
    before → after
    // before
    <R extends RecordType = RecordType, Filters extends FiltersDefinition = FiltersDefinition<string>, Sortings extends SortingsDefinition = SortingsDefinition, Summaries extends SummariesDefinition = SummariesDefinition, ItemActions extends ItemActionsDefinition<R> = ItemActionsDefinition<R>, NavigationFilters extends NavigationFiltersDefinition = NavigationFiltersDefinition<string>, Grouping extends 
    // after
    <R extends RecordType = RecordType, Filters extends FiltersDefinition = FiltersDefinition<string>, Sortings extends SortingsDefinition = SortingsDefinition, Summaries extends SummariesDefinition = SummariesDefinition, ItemActions extends ItemActionsDefinition<R> = ItemActionsDefinition<R>, NavigationFilters extends NavigationFiltersDefinition = NavigationFiltersDefinition<string>, Grouping extends 
  • ✏️ useExportActionbreaking change

    • __0.source.fetchChildren.__0 changed: { item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; }{ item: R; filters?: FiltersState<Filters>; pagination?: ChildrenPaginationInfo; sortings?: { field: keyof Sortings; order: SortOrder; }; search?: string; }
    before → after
    // before
    <R extends RecordType, Filters extends FiltersDefinition, Sortings extends SortingsDefinition, Summaries extends SummariesDefinition, ItemActions extends ItemActionsDefinition<R>, NavigationFilters extends NavigationFiltersDefinition, Grouping extends GroupingDefinition<R>>({ source, currentVisualization, filename, enabled, }: UseExportActionProps<R, Filters, Sortings, Summaries, ItemActions, Navi 
    // after
    <R extends RecordType, Filters extends FiltersDefinition, Sortings extends SortingsDefinition, Summaries extends SummariesDefinition, ItemActions extends ItemActionsDefinition<R>, NavigationFilters extends NavigationFiltersDefinition, Grouping extends GroupingDefinition<R>>({ source, currentVisualization, filename, enabled, }: UseExportActionProps<R, Filters, Sortings, Summaries, ItemActions, Navi 
➕ Additive changes (safe) — 12
  • experimental: NestedChildrenDisplayMode, NestedExpandAllOptions, NestedExpandAnimation, NestedExpandOptions, NestedExpansionContext, NestedExpansionCriteria, NestedRowTarget, NestedTableController, NestedTableOptions, nestedChildrenDisplayModes, nestedExpandAnimations, useNestedTable

⚠️ Could not analyze component-status (no-base) — a build may have failed; results may be incomplete.

AndresRojoFactorial and others added 10 commits July 21, 2026 10:19
Adds a `nested` option to the table visualization with a declarative
auto-expansion policy (defaultExpanded as boolean/depth/predicate), an
imperative controller via useNestedTable() (expand/collapse/toggle by id
or predicate, expandAll, collapseAll, expandTo id-path, isExpanded,
getExpandedItems), eager children loading ("all" vs "paginated" show
more) with runaway-adapter guards, expand animations (fade/stagger/
slide/pop, reduced-motion aware) and an onExpandedChange callback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fetchChildren now receives the debounced search term (like the main
fetch), the children cache resets when the search changes, and
expansion criteria receive hasActiveFilters in their context so
policies can auto-expand matching branches while the user searches or
filters and collapse back when cleared.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dren refetch

- Normalize empty search ("" ≡ undefined) so an undefined→"" transition
  (e.g. setSearch("") on mount) no longer collapses expanded rows or
  clears the children cache; fetchChildren now receives undefined instead
  of "" like the main collection fetch
- hasActiveFilters uses the debounced normalized search exclusively so it
  stays in sync with the children cache invalidation
- Restore refetch when re-expanding a row whose cached children are empty
  (collapsed→expanded transition only, so an empty result can't fetch-loop)
- Create the motion-wrapped Row lazily (skip it for expandAnimation: none)
  and register nested rows keyed by rowId instead of item identity

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xpanded

Declarative, controlled expansion criteria for the nested table (mirrors
the Graph's revealNodeId/focusOnEntry pattern): while defined, the
provider re-applies the criteria whenever its reference changes, taking
over from user clicks or imperative controller calls. Precedence:
expanded (controlled) > control (imperative) > defaultExpanded (mount-only).

Includes story (TableNestedControlledExpansion), MDX section and tests
for the controlled mode plus the search-normalization and empty-children
refetch fixes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…en loading

Remove the hardcoded MAX_EAGER_CHILDREN_PAGES backstop: eager loading
(children: "all") now stops solely on the adapter-reported signals —
hasMore: false, reaching total, or a fetch error. The consumer's adapter
already fully controls termination, so a baked-in page limit was redundant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions

Review findings from an 8-angle adversarially-verified pass over the branch:

- hasActiveFilters now delegates to getActiveFilterKeys (per-filter-type
  isEmpty semantics), so a filter cleared to its empty value (e.g. a search
  filter left as "") no longer counts as active and no longer keeps
  hasActiveFilters-driven expansion policies engaged.
- The nested-row registry re-registers when the item identity changes, so
  controller predicates, getExpandedItems and onExpandedChange see current
  field values after a refetch instead of the first-render snapshot.
- A row that remounts already-open with cached empty children now retries
  the fetch once, matching the behavior of toggling the row itself.
- The NestedDataContext value is memoized; all its callbacks were already
  stable, so unrelated parent re-renders no longer re-render every row.
- The motion-wrapped Row is a lazy module-scope singleton (motion.create
  does not cache custom components) and the expand-animation variants are
  precomputed instead of rebuilt per row render.
- table.mdx no longer claims a page cap on eager children loading; the
  adapter's hasMore/total/error signals drive termination.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ControllableState

Align the controlled expansion criteria with the F0 convention (AGENTS.md:
"use the useControllable hook — do not manually sync state via useEffect").
The auto-expansion policy is now held by useControllableState: while
nested.expanded is defined, the derived controlled policy is the single
source of truth (standard controlled-prop semantics — defaultExpanded is
ignored and the policy side of expandAll/collapseAll is a no-op), and
targeted controller calls keep layering per-row overrides on top. The only
remaining effect handles the reset side effect: overrides are cleared when
the controlled criteria changes by reference.

NestedExpansionState no longer bundles the policy — overrides/eager stay in
provider state, and all resolution paths receive the policy explicitly.
JSDoc and MDX updated to describe the strict controlled precedence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… manual toggles

A row eagerly expanded by a policy (defaultExpandedChildren: "all" or
expandAll({children: "all"})) used to permanently fall back to "show more"
pagination after the user collapsed and re-expanded it: the collapse stored
an eager=false marker that was never cleared, and the explicit override
suppressed the policy fallback entirely.

The eager fallback now evaluates the policy criteria independently of
explicit overrides — a user click expresses expand/collapse, never a
pagination preference — and expanding (via chevron or a targeted controller
call without options) drops the stale collapse marker, while an explicit
eager opt-in (children: "all") still persists. Regression test covers the
policy-eager → manual collapse → re-expand sequence with deferred fetches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…indings

Addresses findings 1, 2, 4, 5, 6, 7 and 9 of the PR review artifact:

- expandAll/collapseAll are now complete no-ops while nested.expanded is
  controlled: they no longer clear manual overrides, so a row the user
  collapsed cannot snap back open (finding 1).
- rowId is namespaced with the parent row's id (or the group for roots),
  so equal depth/index positions in different branches or groups can no
  longer collide and share expansion state (finding 2).
- A filters/search reset now unsubscribes any children fetch still in
  flight (and re-arms the auto-load guard when a first fetch is
  cancelled), so a slow response can no longer repopulate the cache with
  children of the previous search (finding 4).
- A resolved page that adds no records is treated as the end of
  pagination even if the adapter reports hasMore, breaking the infinite
  request loop a misbehaving adapter could cause under eager loading
  (finding 5).
- Dev-only warning when a controlled expanded predicate changes identity
  repeatedly (the inline-function footgun), plus a memoized example in
  the MDX (finding 6).
- Controller targets and expandTo paths normalize ids with String(), so
  string targets (e.g. URL params) match numeric item ids (finding 7).
- MDX now shows the import path for useNestedTable (finding 9).

7 new regression tests (128 in the file, 628 in OneDataCollection).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rce.idProvider

Connect the nested table to the collection's existing identity convention
instead of the inherited positional key (depth-id-index, which predates
this branch): a row's identity is now source.idProvider(item) when the
source defines one — the same mechanism Kanban and item navigation already
use — falling back to item.id, and to the position only for id-less items.
The parent row's id still namespaces the key, so branches cannot collide.

Consequences:
- Expansion survives sibling reorders from a refetch (artifact finding 3):
  the positional index is gone from the key whenever the item has identity.
- Controller targets and expandTo paths match against the same resolved
  identity, so a consumer can designate any univocal column via idProvider
  without new API surface.
- Documented requirement: identities must be unique among sibling rows.

Two new regression tests: sibling reorder keeps expansion, and targeting
by an idProvider-designated column on id-less items (130 in the file, 630
in OneDataCollection).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresRojoFactorial
AndresRojoFactorial force-pushed the feat/Nested-DataCollection-ProgramaticFolding branch from de8595d to d527b2d Compare July 21, 2026 08:19
…led fetches after a reset

Addresses findings 1 and 2 of the second review pass:

- applyPendingExpansion (the expandTo path) now clears a stale eager:false
  collapse marker when expanding without explicit options, in parity with
  expand/toggle: jumping to a branch declared with children "all" loads
  every page again instead of falling back to "show more".
- The auto-load guard also re-arms when a settled fetch error is cleared
  by a filters/search reset (hasError true->false with nothing fetched and
  nothing in flight), so a row kept open by a policy retries with the new
  context instead of staying empty forever. A fresh fetch also clears the
  error but with isLoading active, so no false positives.

Two regression tests with deferred/rejecting fetches (132 in the Table
suite).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking changes feat react Changes affect packages/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant