Skip to content

feat(fields): RecordPicker lookup_filters, cell formatter, FilterUI integration, ObjectGrid reuse#1088

Merged
hotlong merged 6 commits intomainfrom
copilot/objectui-1072-merge-high-priority-features
Mar 18, 2026
Merged

feat(fields): RecordPicker lookup_filters, cell formatter, FilterUI integration, ObjectGrid reuse#1088
hotlong merged 6 commits intomainfrom
copilot/objectui-1072-merge-high-priority-features

Conversation

Copy link
Contributor

Copilot AI commented Mar 18, 2026

Implements four high-priority RecordPicker features to complete the enterprise Record Picker upgrade: schema-driven filter injection, type-aware cell rendering, FilterUI integration via slot architecture, and ObjectGrid reuse via render prop injection.

lookup_filters consumption

  • RecordPickerDialog accepts lookupFilters: LookupFilterDef[], converts operators (eq→direct, ne$ne, in$in, etc.) to $filter Record, injected into every DataSource.find() call
  • Filters persist through search, sort, and pagination
  • LookupField reads lookup_filters from field metadata and passes through

Cell Type Formatter

  • RecordPickerDialog accepts optional cellRenderer: CellRendererResolver prop
  • Columns with a type property (e.g. currency, date, select, boolean) are rendered via the resolver; falls back to plain text
  • Circular dependency (index.tsxLookupFieldRecordPickerDialogindex.tsx) broken via _cell-renderer-bridge.ts singleton pattern

FilterUI filter bar integration

  • Built-in inline filter bar between search input and table, toggled via button with active-count badge
  • Supports text/number/select/date/boolean inputs; values merged with lookupFilters
  • LookupField auto-derives filterColumns from lookup_columns entries that have type via mapFieldTypeToFilterType()
  • renderFilterBar slot prop allows injecting the actual FilterUI component from @object-ui/plugin-view (avoids circular dependency since plugin-view depends on fields)
  • Exports RecordPickerFilterBarProps interface with filterColumns, values, onChange, onClear, activeCount
  • Auto-generates filterColumns from lookupFilters when no explicit filterColumns prop is provided, inferring input types from value types and operators

ObjectGrid table reuse

  • Built-in table with drag-to-resize column handles (cursor-col-resize, min-width 60px), table-layout: fixed when resized
  • renderGrid slot prop allows injecting ObjectGrid from @object-ui/plugin-grid (avoids circular dependency since plugin-grid depends on fields)
  • Exports RecordPickerGridSlotProps interface with columns, records, loading, totalCount, currentPage, pageSize, sortField, sortDirection, onSort, onPageChange, onRowClick, isSelected, multiple, idField, cellRenderer
  • Built-in table serves as default fallback when no renderGrid is provided
<RecordPickerDialog
  open={open}
  onOpenChange={setOpen}
  dataSource={ds}
  objectName="customers"
  columns={[
    { field: 'name', label: 'Name' },
    { field: 'amount', label: 'Amount', type: 'currency' },
    { field: 'status', label: 'Status', type: 'select' },
  ]}
  lookupFilters={[{ field: 'active', operator: 'eq', value: true }]}
  cellRenderer={getCellRenderer}
  filterColumns={[
    { field: 'status', label: 'Status', type: 'select', options: [...] },
  ]}
  // Inject FilterUI from plugin-view
  renderFilterBar={(props) => (
    <FilterUI
      schema={{ type: 'filter-ui', filters: props.filterColumns, layout: 'inline' }}
      onChange={(values) => Object.entries(values).forEach(([k, v]) => props.onChange(k, v))}
    />
  )}
  // Inject ObjectGrid from plugin-grid
  renderGrid={(props) => (
    <ObjectGrid
      schema={{ type: 'object-grid', objectName, columns: props.columns }}
      dataSource={dataSource}
    />
  )}
  onSelect={handleSelect}
/>

Tests

37 record-picker tests (17 new) covering filter injection (3), cell formatting (3), filter bar rendering (3), column resize handles (2), renderFilterBar slot (2), renderGrid slot (2), and auto-generated filter bar from lookupFilters (2). All 412 existing tests continue to pass.

Original prompt

This section details on the original issue you should resolve

<issue_title>【RecordPicker 优先开发合并】高优功能实现:lookup_filters、cell 格式化、FilterUI 集成、ObjectGrid 复用</issue_title>
<issue_description>为全面完成 Issue #1072『LookupField/Reference 字段下拉弹窗的企业级 Record Picker 方案升级』,需优先合并以下高优功能,提升实际可用性和架构完整度:

1. lookup_filters 实际消费

  • 让 RecordPickerDialog/LookupField 能读取 schema 上配置的 lookup_filters,并动态注入到弹窗查询参数中,实现 UI 与业务筛选协议解耦。
  • 支持组合筛选,兼容 FilterUI。

2. Cell Type Formatter(字段格式化渲染)

  • 在 RecordPickerDialog 内支持 currency/date/select/boolean 字段类型格式化。
  • 按 schema 的 LookupColumnDef.type,调用对应 renderer,包括 badge、���币、枚举等。

3. FilterUI filter bar 集成

  • 弹窗内集成 FilterUI 作为 filter bar,支持多字段组合筛选。
  • filter bar 可根据 lookup_filters 或 objectSchema 智能生成。

4. ObjectGrid 表格能力复用

  • 使 RecordPickerDialog 内嵌 ObjectGrid 实现与主表格一致的列宽调整、排序、分页、空态适配。
  • 避免弹窗自实现表格,提升一致性。

---</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

@vercel
Copy link

vercel bot commented Mar 18, 2026

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

Project Deployment Actions Updated (UTC)
objectui Canceled Canceled Mar 18, 2026 11:12am
objectui-demo Ready Ready Preview, Comment Mar 18, 2026 11:12am
objectui-storybook Ready Ready Preview, Comment Mar 18, 2026 11:12am

Request Review

…filter bar, column resize

- Add lookupFilters prop to RecordPickerDialog for injecting base filters into $filter
- Add cellRenderer prop for type-aware cell formatting (currency, date, select, boolean)
- Add filterColumns prop for inline filter bar with text/number/select/date/boolean inputs
- Add column resize handles with drag-to-resize support
- Create _cell-renderer-bridge.ts to break circular dependency for getCellRenderer
- LookupField now passes lookup_filters, getCellRenderer, and derived filterColumns
- Add 11 comprehensive tests for all new features

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…eToFilterType helper

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] [objectstack-ai#1072] Implement high priority features for RecordPicker feat(fields): RecordPicker lookup_filters, cell formatter, filter bar, column resize Mar 18, 2026
Copilot AI requested a review from hotlong March 18, 2026 10:08
@hotlong
Copy link
Contributor

hotlong commented Mar 18, 2026

@copilot 仔细评估是否已经完成了任务中要求的所有开发

…ters from lookupFilters

- Add renderFilterBar slot prop for FilterUI injection from plugin-view
- Add renderGrid slot prop for ObjectGrid injection from plugin-grid
- Auto-generate filterColumns from lookupFilters when no explicit ones given
- Export RecordPickerFilterBarProps and RecordPickerGridSlotProps interfaces
- Add 6 new tests for slot props and auto-generation
- Total: 37 record-picker tests, 412 fields tests passing

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot 仔细评估是否已经完成了任务中要求的所有开发

评估后发现两个架构性缺口已补齐(commit d8e08bc, 85c77f9):

3. FilterUI 集成:之前只有内置 filter bar,未提供 FilterUI 注入能力。现在新增 renderFilterBar slot prop,上层可注入 FilterUI from @object-ui/plugin-view(避免循环依赖)。同时新增 effectiveFilterColumns:当未显式传 filterColumns 时,自动从 lookupFilters 推导出可筛选字段。

4. ObjectGrid 复用:之前只有列宽调整,未提供 ObjectGrid 注入能力。现在新增 renderGrid slot prop,上层可注入 ObjectGrid from @object-ui/plugin-grid。导出 RecordPickerGridSlotProps 接口,包含 columns/records/sorting/pagination/selection 所有数据和回调。

由于 @object-ui/fields(RecordPickerDialog 所在包)是被 plugin-viewplugin-grid 依赖的底层包,不能反向导入它们。通过 slot/render prop 注入模式解决,内置 table 和 filter bar 作为默认 fallback。

四项需求完成状态:

  1. ✅ lookup_filters 消费($filter 注入)
  2. ✅ Cell Type Formatter(cellRenderer + bridge)
  3. ✅ FilterUI 集成(renderFilterBar slot + 自动推导)
  4. ✅ ObjectGrid 复用(renderGrid slot + 完整接口)

新增 6 个测试覆盖 slot 注入和自动推导,共 37 个 record-picker 测试全部通过。

Copilot AI changed the title feat(fields): RecordPicker lookup_filters, cell formatter, filter bar, column resize feat(fields): RecordPicker lookup_filters, cell formatter, FilterUI integration, ObjectGrid reuse Mar 18, 2026
@hotlong hotlong marked this pull request as ready for review March 18, 2026 10:41
Copilot AI review requested due to automatic review settings March 18, 2026 10:41
@hotlong hotlong merged commit e35fa5a into main Mar 18, 2026
4 of 6 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades RecordPickerDialog and LookupField in @object-ui/fields to support schema-driven base filters (lookup_filters), type-aware cell formatting, pluggable FilterUI/ObjectGrid integration via render slots, and column resizing for the built-in table fallback.

Changes:

  • Add lookupFilters, cellRenderer, filterColumns, renderFilterBar, and renderGrid capabilities to RecordPickerDialog, including filter injection into DataSource.find().
  • Add a bridge module to break the index.tsxRecordPickerDialog circular dependency for getCellRenderer.
  • Extend LookupField to pass lookup_filters, derived filter columns, and the bridged cellRenderer into RecordPickerDialog, plus add comprehensive tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/fields/src/widgets/_cell-renderer-bridge.ts Adds singleton bridge to share getCellRenderer without circular imports.
packages/fields/src/widgets/RecordPickerDialog.tsx Implements lookup filter injection, filter bar + slots, cell formatting hook, and resize handles.
packages/fields/src/widgets/LookupField.tsx Passes lookup_filters through and derives filter columns/cell renderer for the picker dialog.
packages/fields/src/record-picker.test.tsx Adds test coverage for new RecordPicker features (filters, slots, formatting, resize).
packages/fields/src/index.tsx Registers the cell renderer resolver into the bridge at module init.

Comment on lines +709 to +712
<Select
value={val !== undefined && val !== null ? String(val) : ''}
onValueChange={v => handleFilterChange(col.field, v)}
>
Comment on lines +950 to +954
const styleWidth = w ? { width: `${w}px`, minWidth: `${w}px` } : col.width ? { width: col.width } : undefined;
return (
<TableHead
key={col.field}
style={styleWidth}
Comment on lines +71 to +72
select: 'select',
status: 'select',
Comment on lines +891 to +894
// Register getCellRenderer in the bridge so RecordPickerDialog can access it
// via LookupField without circular imports.
import { setCellRendererResolver } from './widgets/_cell-renderer-bridge';
setCellRendererResolver(getCellRenderer);
const userFilter = effectiveFilterColumns?.length
? filterValuesToRecord(filterValues, effectiveFilterColumns)
: {};
const combined = { ...baseFilter, ...userFilter };
Comment on lines +717 to +721
{col.options?.map(opt => (
<SelectItem key={String(opt.value)} value={String(opt.value)}>
{opt.label}
</SelectItem>
))}
Comment on lines +671 to +673
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
},
role="grid"
aria-label="Records"
>
<Table style={Object.keys(columnWidths).length > 0 ? { tableLayout: 'fixed' } : undefined}>
Comment on lines +380 to +386
options: (f.value as any[]).map(v => {
if (v != null && typeof v === 'object') {
const obj = v as Record<string, unknown>;
return { label: String(obj.name || obj.label || obj.title || v), value: v };
}
return { label: String(v), value: v };
}),
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.

【RecordPicker 优先开发合并】高优功能实现:lookup_filters、cell 格式化、FilterUI 集成、ObjectGrid 复用

3 participants