Skip to content

Edit page: bounded + live-searchable relationship dropdowns#638

Merged
borisno2 merged 2 commits into
mainfrom
claude/issue-631-n1eg13
Jul 10, 2026
Merged

Edit page: bounded + live-searchable relationship dropdowns#638
borisno2 merged 2 commits into
mainfrom
claude/issue-631-n1eg13

Conversation

@borisno2

Copy link
Copy Markdown
Member

Summary

  • Replace the unbounded findMany({}) in prepareItemForm with getRelationshipOptions(context, config, relatedListName, { selectedIds }) — the edit page's relationship dropdowns now fetch a bounded, take-limited window per relationship field instead of scanning the whole related table, and always union the item's currently-selected id(s) so their label renders even outside that window.
  • ComboboxField (single) and RelationshipManager (many) gain debounced live search via a new shared hook (useRelationshipSearch) that calls the relationshipOptions context.serverAction op, seeded with the server-rendered initial window. Typing narrows results server-side against the label field.
  • Threaded listKey/serverAction through ItemFormClientFieldRendererRelationshipFieldComboboxField/RelationshipManager (all optional — components without them fall back to client-side filtering over the initial window, unchanged from previous behavior).
  • Only { id, label }[] plus the list/field metadata needed to issue a search crosses the client boundary.

Test plan

  • Unit tests for prepareItemForm asserting every relationship fetch is take-bounded and unions the current selection (single and many), including the create-mode no-selection case (packages/ui/tests/lib/prepareItemForm.test.ts)
  • Component tests for ComboboxField covering initial-value label rendering, debounced server search call shape, and persisting a selection found via search (packages/ui/tests/components/ComboboxField.test.tsx)
  • Component tests for RelationshipManager covering connected-item labels, excluding already-connected items from search candidates, the debounced search call shape, and that a mid-session connection found via search keeps its label (packages/ui/tests/components/RelationshipManager.test.tsx)
  • Full packages/ui and packages/core test suites pass (209 and 766 tests respectively)
  • pnpm lint, pnpm manypkg fix, pnpm format all pass clean
  • Changeset added (@opensaas/stack-ui: minor)

Closes #631


Generated by Claude Code

…t page

Replace the unbounded findMany({}) in item-form preparation with
getRelationshipOptions (bounded, take-limited, selected-id seeded).
ComboboxField and RelationshipManager gain debounced live search via the
relationshipOptions serverAction op, falling back to client-side filtering
when no server action is wired.

Closes #631

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVrp6NFf7v2WrxCT7d8i3Z
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 888d169

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@opensaas/stack-ui Minor
@opensaas/stack-tiptap Minor
@opensaas/stack-auth Minor
@opensaas/stack-cli Minor
@opensaas/stack-core Minor
@opensaas/stack-rag Minor
@opensaas/stack-storage-s3 Minor
@opensaas/stack-storage-vercel Minor
@opensaas/stack-storage Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-docs Ready Ready Preview, Comment Jul 10, 2026 8:41am

…pSearch

A failed serverAction call left isSearching stuck true forever and
produced an unhandled promise rejection. Catch the error, log it, fall
back to an empty result set, and always clear isSearching.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVrp6NFf7v2WrxCT7d8i3Z

Copy link
Copy Markdown
Member Author

Self-review

Overview: Replaces the per-relationship-field unbounded findMany({}) in prepareItemForm with the bounded getRelationshipOptions primitive (from #630/#636), and adds debounced live search to ComboboxField/RelationshipManager via a new shared useRelationshipSearch hook that calls the relationshipOptions context.serverAction op.

Code quality / conventions

  • Follows the existing prop-threading pattern (ItemFormClientFieldRendererRelationshipField → field components) rather than introducing a new context/provider — consistent with how serverAction is already plumbed for create/update/delete.
  • New listKey/serverAction props are optional everywhere, so components used without them (e.g. hand-rolled usages outside AdminUI) keep the old client-side-filter-over-items behavior unchanged.
  • useRelationshipSearch derives searchResults directly from render inputs instead of syncing them via effects, to stay clear of the newer react-hooks/set-state-in-effect lint rule (verified — pnpm lint is clean with 0 errors).

Issue found during self-review (fixed, pushed in 888d169)

  • The debounced server call in useRelationshipSearch had no .catch(). A rejected serverAction (network blip, thrown server action) left isSearching stuck true forever — the combobox/connect-dropdown would show "Searching…" indefinitely — and produced an unhandled promise rejection. Fixed by catching, logging, falling back to an empty result set, and always clearing isSearching in a .finally(). Added a regression test (ComboboxField.test.tsx: "recovers from a rejected search instead of getting stuck on 'Searching...'").

Test coverage

  • prepareItemForm.test.ts: every relationship fetch is take-bounded (never unbounded {}), currently-selected id(s) are unioned in for both single and many relationships even when outside the window, and create-mode (no value) issues no second id-scoped query.
  • ComboboxField.test.tsx / RelationshipManager.test.tsx: initial-value label rendering without a search call, debounced search call shape (listKey/field/search/selectedIds), persisting a selection found via search, excluding already-connected items from candidates, and the new error-recovery case.
  • Full suites: 210 passed (ui), 766 passed (core).

Risks / follow-ups (non-blocking)

  • The bounded window size isn't configurable per-field in this slice (relies on getRelationshipOptions's default take: 50) — fine for this issue's scope; a future ui.take option could be added if a project needs a different window.
  • RelationshipManager's "Connect Existing" trigger no longer disables when the initial window's available-items count is zero, since that's no longer a reliable signal once options are bounded/searchable — intentional, minor UX behavior change.

Lint, format (pnpm manypkg fix, pnpm format), and both packages/ui/packages/core test suites are green on the latest commit.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for Core Package Coverage (./packages/core)

Status Category Percentage Covered / Total
🔵 Lines 92.22% (🎯 65%) 901 / 977
🔵 Statements 91.56% (🎯 65%) 945 / 1032
🔵 Functions 98.05% (🎯 62%) 151 / 154
🔵 Branches 80.92% (🎯 50%) 615 / 760
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for UI Package Coverage (./packages/ui)

Status Category Percentage Covered / Total
🔵 Lines 76.03% 92 / 121
🔵 Statements 75.39% 95 / 126
🔵 Functions 75.6% 31 / 41
🔵 Branches 65.78% 75 / 114
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for CLI Package Coverage (./packages/cli)

Status Category Percentage Covered / Total
🔵 Lines 79.21% 1490 / 1881
🔵 Statements 78.92% 1550 / 1964
🔵 Functions 84.45% 201 / 238
🔵 Branches 67.25% 653 / 971
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for Auth Package Coverage (./packages/auth)

Status Category Percentage Covered / Total
🔵 Lines 74.64% 159 / 213
🔵 Statements 69.74% 166 / 238
🔵 Functions 83.11% 64 / 77
🔵 Branches 70.67% 94 / 133
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for Storage Package Coverage (./packages/storage)

Status Category Percentage Covered / Total
🔵 Lines 74.8% 190 / 254
🔵 Statements 76.44% 211 / 276
🔵 Functions 85.89% 67 / 78
🔵 Branches 70.73% 174 / 246
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for RAG Package Coverage (./packages/rag)

Status Category Percentage Covered / Total
🔵 Lines 47.97% 355 / 740
🔵 Statements 48.14% 377 / 783
🔵 Functions 54.26% 70 / 129
🔵 Branches 42.55% 180 / 423
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for Storage S3 Package Coverage (./packages/storage-s3)

Status Category Percentage Covered / Total
🔵 Lines 100% 40 / 40
🔵 Statements 100% 40 / 40
🔵 Functions 100% 9 / 9
🔵 Branches 100% 19 / 19
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for Storage Vercel Package Coverage (./packages/storage-vercel)

Status Category Percentage Covered / Total
🔵 Lines 100% 38 / 38
🔵 Statements 100% 38 / 38
🔵 Functions 100% 8 / 8
🔵 Branches 100% 22 / 22
File CoverageNo changed files found.
Generated in workflow #1235 for commit 888d169 by the Vitest Coverage Report Action

@borisno2 borisno2 merged commit 95560e4 into main Jul 10, 2026
6 checks passed
@borisno2 borisno2 deleted the claude/issue-631-n1eg13 branch July 10, 2026 08:57
@github-actions github-actions Bot mentioned this pull request Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Edit page: bounded + live-searchable relationship dropdowns

2 participants