feat(renderers): add SearchSelectControl with dynamic search and pagination - #601
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new 'SearchSelectControl' component for JSON Forms, which supports server-side filtering, infinite scrolling, and selection clearing, along with a 'DataSourceContext' to provide search and resolution capabilities. The code review feedback suggests storing the full selected option object in state instead of just the label to fix a hydration bug when external data changes and to prevent losing selection during filtering. Additionally, the reviewer recommends resetting the control's state when the popover is closed to ensure a clean state for subsequent uses, and simplifying the control tester by removing a redundant 'and' combinator.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
05ae94d to
a9dcbed
Compare
4d1df5d to
8b8143e
Compare
Heads-up: align with conventions from #604 / #605 (now merged to
|
|
@saknarajapakshe There are conventions introduced in PR #604 and #605. We need to follow them. First rebase the branch. I made what changes you need to do here with the help from Claude. Can you take a look? |
8b8143e to
12d9633
Compare
Thank you for your feedback. I have rebased the branch with upstream/main and applied the conventions introduced in the last 2 PRs now. You can check it. |
12d9633 to
87f8af4
Compare
87f8af4 to
f02e0d8
Compare
…ynamic search options
f02e0d8 to
943cae4
Compare
Summary
Adds
SearchSelectControl— a JSONForms renderer forstringfields that need a server-driven, searchable dropdown. Configured entirely via anx-searchextension on the JSON Schema, keeping the schema self-describing.Replaces the earlier
DataSourceContextdesign (consumer-owned search function) with a minimalSearchContextthat only providesbaseUrlandgetHeaders. The renderer owns all HTTP logic — URL building, fetch, response parsing, and pagination — mirroring the existingFileControl+UploadContextpattern.Type of Change
Changes Made
SearchContext.tsx— new context (SearchProvider/useSearchContext) that accepts:baseUrl— API origin (e.g.https://api.example.com)getHeaders— async function returning auth headers (e.g.Bearertoken)SearchSelectControl.tsx— JSONForms renderer built on Radix UITextField+ScrollArea:none(full fetch),offset, andcursor, with a "Load more" buttonloadOnOpenflag to pre-fetch results on dropdown openAbortControllerto cancel in-flight requests on new search or dropdown closeresolvePathfor arbitrary API shapesdescriptionhintuseClearWhenHiddento reset value when the field is hidden by a visibility rulegetErrorMessagefor consistent required-field error messages'Search service not configured.'when noSearchProvideris mountedSearchSelectControlTester.ts— ranks at priority 3; activates onstringschemas carrying anx-searchobjectrenderers/index.ts— registers the renderer inradixRendererssrc/index.ts— exportsSearchProvider,useSearchContext, and associated types alongsideUploadContextDataSourceContext.tsx— deleted (replaced bySearchContext)How to use in production
1. Mount
SearchProviderin your appWrap your JSONForms tree with
SearchProvider— same pattern asUploadProvider:2. Add
x-searchto a schema field{ "type": "object", "properties": { "chaCompany": { "type": "string", "description": "Select a CHA company", "x-search": { "path": "/api/v1/companies", "valueKey": "id", "labelKey": "name", "pagination": "offset", "pageSize": 10, "loadOnOpen": true } } } }The renderer builds the request URL as
new URL(path, baseUrl)and attaches the headers fromgetHeaders(). No other wiring required.x-searchoptions referencepathvalueKey"id"labelKey"name"pagination"offset""none","offset", or"cursor"pageSize5"none")itemsPath""(none) /"items"(offset, cursor)nextCursorPath"nextCursor"searchParam"q"limitParam"limit"offsetParam"offset"cursorParam"cursor"loadOnOpenfalseExpected API response shapes
pagination: "none"— endpoint returns the full list; response IS the array or array atitemsPath:[{ "id": "cha-001", "name": "Ace Customs Brokers" }]Or with a wrapper path (
itemsPath: "data"):{ "data": [{ "id": "cha-001", "name": "Ace Customs Brokers" }] }pagination: "offset"/"cursor"— paginated endpoint, defaults toitemsPath: "items":{ "items": [{ "id": "cha-001", "name": "Ace Customs Brokers" }], "total": 50 }The
itemsPath,valueKey, andlabelKeyoptions accept dot-notation (e.g."data.results","company.id") for nested response shapes.Backend integration
Add the search endpoint to the relevant Go service and register it under
app.go. The renderer calls it with the query params defined inx-search(defaults:q,limit,offset). No additional backend scaffolding is required — the renderer adapts to whatever param names and response shape the endpoint exposes viax-searchconfig.How to test locally
Option A — dev playground (renderer package)
cd portals/packages/jsonforms-renderers pnpm devOpen
http://localhost:5173and select Search Select from the sidebar. Without aSearchProvidermounted the field shows'Search service not configured.'— wire up a provider pointing at a real or mock API to see results.Option B — trader-app with mock backend
Open
http://localhost:5173, log in, open a consignment — the CHA search box calls the mock backend.Checklist
Related Issues
Closes #599