Skip to content

Align ViewConfigPanel with full ListViewSchema spec#725

Merged
hotlong merged 5 commits intomainfrom
copilot/complete-view-config-panel
Feb 22, 2026
Merged

Align ViewConfigPanel with full ListViewSchema spec#725
hotlong merged 5 commits intomainfrom
copilot/complete-view-config-panel

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 22, 2026

ViewConfigPanel covered ~58% of ListViewSchema properties. This PR adds UI entries for all remaining spec attributes across P0/P1/P2 priority tiers, with full type safety, i18n, propagation, and test coverage.

Type System

  • NamedListView: +24 properties (navigation, selection, pagination, searchableFields, filterableFields, resizable, densityMode, hiddenFields, exportOptions, rowActions, bulkActions, sharing, addRecord, conditionalFormatting, quickFilters, showRecordCount, allowPrinting, virtualScroll, emptyState, aria)
  • Zod schema: Synced ListViewSchema with typed validators for all new fields

ViewConfigPanel Controls

  • P0: navigation.mode Select replacing clickIntoRecordDetails toggle (page/drawer/modal/split/popover/new_window/none) with conditional width and openNewTab; selection.type Select; pagination.pageSize + pageSizeOptions inputs
  • P1: Export sub-config (formats/maxRecords/includeHeaders/fileNamePrefix); searchable/filterable/hidden field multi-selects; resizable toggle; densityMode select; row/bulk action inputs; sharing section; addRecord sub-editor (enabled/position/mode/formView); conditional formatting rule editor; quick filters editor
  • P2: showRecordCount, allowPrinting, virtualScroll toggles; empty state inputs; ARIA accessibility section

Semantic Fixes

  • editRecordsInlineinlineEdit (field name alignment with spec)
  • rowHeight values: short/medium/tall/extraTallcompact/medium/tall
  • clickIntoRecordDetails backward-compat sync maintained via navigation.mode

Propagation

All 18 new properties flow through the 3-layer pipeline:

Console ObjectView (fullSchema) → PluginObjectView (renderListView) → ListView

i18n

+46 keys in en.ts and zh.ts covering all new controls.

Tests

Updated 6 existing tests for renamed controls, added 33 new tests (111 total). 942 tests pass across affected suites.

Original prompt

This section details on the original issue you should resolve

<issue_title>ViewConfigPanel 对齐 @objectstack/spec ListViewSchema 完整修改清单</issue_title>
<issue_description>

背景

ViewConfigPanel.tsx 是 Console 右侧面板,用于配置列表视图。当前面板覆盖了约 58% 的 ListViewSchema spec 属性。本 Issue 的目标是@objectstack/spec UI 协议为准,补齐所有缺失的面板配置项,确保每个 spec 属性在面板中都有对应的 UI 入口。

权威来源packages/types/src/objectql.ts L1101-L1511 ListViewSchema interface

当前面板结构apps/console/src/components/ViewConfigPanel.tsx):

  • Page Config 区:label, description, viewType, 11 个 toolbar 开关
  • Data 区:sort, filter, columns, groupBy, prefixField, viewType 子选项
  • Appearance 区:color, fieldTextColor, rowHeight, wrapHeaders, showDescription, collapseAllByDefault, striped, bordered
  • User Actions 区:editRecordsInline, addDeleteRecordsInline

修改清单

P0 — ���优先级(用户可感知,必须在本轮完成)

1. 新增 Navigation 配置(替代 clickIntoRecordDetails 简单 toggle)

Spec 属性navigation: ViewNavigationConfig

interface ViewNavigationConfig {
  mode: 'page' | 'drawer' | 'modal' | 'split' | 'popover' | 'new_window' | 'none';
  view?: string;
  preventNavigation?: boolean;
  openNewTab?: boolean;
  width?: string | number;
}

当前面板:只有 clickIntoRecordDetails boolean toggle(L546-L552)

修改内容

  • ViewConfigPanel.tsx Page Config 区:将 clickIntoRecordDetails toggle 替换为 navigation.mode Select 下拉
    • 选项:page(默认)、drawermodalsplitpopovernew_windownone
    • data-testid: select-navigation-mode
  • ViewConfigPanel.tsx:当 modedrawer/modal/split 时,展示 navigation.width Input
    • data-testid: input-navigation-width
  • ViewConfigPanel.tsx:当 modepage/new_window 时,展示 navigation.openNewTab Switch
    • data-testid: toggle-navigation-openNewTab
  • Console ObjectView.tsx renderListView fullSchema:透传 navigation
  • PluginObjectView.tsx renderContent schema:透传 navigation
  • ListView.tsx:消费 schema.navigation 控制行点击行为
  • i18n keysconsole.objectView.navigationMode, console.objectView.navigationWidth, console.objectView.openNewTab
  • 测试:navigation mode 切换 → 面板 UI 变化 + 值透传

2. 新增 Selection 配置

Spec 属性selection: { type: 'none' | 'single' | 'multiple' }

当前面板:完全缺失

修改内容

  • ViewConfigPanel.tsx Page Config 区:新增 selection.type Select 下拉
    • 选项:nonesinglemultiple
    • data-testid: select-selection-type
  • Console ObjectView.tsx fullSchema:透传 selection
  • PluginObjectView.tsx schema:透传 selection
  • i18n keysconsole.objectView.selectionMode, console.objectView.selectionNone, console.objectView.selectionSingle, console.objectView.selectionMultiple
  • 测试:selection type 切换 → 值透传

3. 新增 Pagination 配置

Spec 属性pagination: { pageSize: number; pageSizeOptions?: number[] }

当前面板:完全缺失

修改内容

  • ViewConfigPanel.tsx Data 区:新增 pagination.pageSize Number Input
    • data-testid: input-pagination-pageSize
    • 默认值提示:25
  • ViewConfigPanel.tsx:新增 pagination.pageSizeOptions multi-select 或逗号分隔 Input
    • data-testid: input-pagination-pageSizeOptions
    • 默认提示:10, 25, 50, 100
  • Console ObjectView.tsx fullSchema:透传 pagination
  • PluginObjectView.tsx schema:透传 pagination
  • i18n keysconsole.objectView.pageSize, console.objectView.pageSizeOptions
  • 测试:pageSize 输入 → 值透传

P1 — 中优先级(高级配置,提升完整度)

4. Export Options 展开配置

Spec 属性exportOptions: { formats?, maxRecords?, includeHeaders?, fileNamePrefix? }

当前面板:只有 allowExport boolean toggle(L562-L568),缺 exportOptions 细节

修改内容

  • ViewConfigPanel.tsx Page Config 区:当 allowExport = true 时展开子配置区
    • exportOptions.formats multi-select checkbox: csv, xlsx, json, pdf
      • data-testid: export-formats
    • exportOptions.maxRecords Number Input
      • data-testid: input-export-maxRecords
    • exportOptions.includeHeaders Switch
      • data-testid: toggle-export-includeHeaders
    • exportOptions.fileNamePrefix Input
      • data-testid: input-export-fileNamePrefix
  • i18n keysconsole.objectView.exportFormats, console.objectView.exportMaxRecords, console.objectView.exportIncludeHeaders, console.objectView.exportFileNamePrefix
  • 测试:allowExport ON → 展开子配置;formats 选择 → exportOptions 正确生成

5. Searchable Fields 配置

Spec 属性searchableFields: string[]

当前面板:完全缺失

修改内容

  • ViewConfigPanel.tsx Data 区:新增 searchableFields multi-select checkbox 列表(从 objectDef.fields 派生)
    • data-testid: searchable-fields-selector
    • 点击展开,类似现有 columns selector 的 UX
  • Console ObjectView.tsx fullSchema:透传 searchableFields
  • i18n keysconsole.objectView.searchableFields
  • 测试:选择字段 → searchableFields 数组更新

6. Filterable Fields 配置

Spec 属性filterableFields: string[]

当前面板:完全缺失

修改内容

  • ViewConfigPanel.tsx Data 区:新增 filterableFields multi-sel...

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 22, 2026

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

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 22, 2026 2:55pm
objectui-demo Ready Ready Preview, Comment Feb 22, 2026 2:55pm
objectui-storybook Ready Ready Preview, Comment Feb 22, 2026 2:55pm

Request Review

…stViewSchema

Add 24 new optional properties to the NamedListView interface including:
- navigation, selection, pagination configuration
- searchableFields, filterableFields, hiddenFields
- resizable, densityMode, rowHeight display options
- exportOptions, rowActions, bulkActions
- sharing, addRecord, conditionalFormatting
- quickFilters, showRecordCount, allowPrinting
- virtualScroll, emptyState, aria accessibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n, and propagation layers

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…D, enhance test

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Update ViewConfigPanel to align with ListViewSchema Align ViewConfigPanel with full ListViewSchema spec Feb 22, 2026
Copilot AI requested a review from hotlong February 22, 2026 14:53
@hotlong hotlong marked this pull request as ready for review February 22, 2026 15:26
Copilot AI review requested due to automatic review settings February 22, 2026 15:26
@hotlong hotlong merged commit 8728270 into main Feb 22, 2026
6 checks passed
Copy link
Copy Markdown
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 expands ObjectUI’s list view configuration support to fully cover the ListViewSchema spec by extending types/validation, adding missing ViewConfigPanel controls, and propagating the new configuration through the Console → plugin-view → ListView pipeline.

Changes:

  • Extended NamedListView and ListViewSchema Zod validation with the remaining spec attributes (navigation/selection/pagination/exportOptions/etc.).
  • Added UI controls in ViewConfigPanel for the missing spec properties (including semantic alignment like inlineEdit and spec-aligned rowHeight values).
  • Propagated new spec fields through Console ObjectView and plugin-view ObjectView for live preview / rendering.

Reviewed changes

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

Show a summary per file
File Description
packages/types/src/zod/objectql.zod.ts Adds Zod validation for the newly supported ListViewSchema properties.
packages/types/src/objectql.ts Extends NamedListView with new spec-aligned configuration fields.
packages/plugin-view/src/ObjectView.tsx Forwards newly added view config properties into the schema passed to renderListView.
packages/i18n/src/locales/en.ts Adds i18n keys for the new ViewConfigPanel controls.
packages/i18n/src/locales/zh.ts Adds i18n keys for the new ViewConfigPanel controls (Chinese locale).
apps/console/src/components/ViewConfigPanel.tsx Implements new config controls (navigation, selection, pagination, export sub-config, etc.) and semantic fixes.
apps/console/src/components/ObjectView.tsx Propagates additional spec fields (and aligns inline edit semantics) into live preview schema.
apps/console/src/tests/ViewConfigPanel.test.tsx Updates/extends tests to cover the new controls and renamed semantics.
ROADMAP.md Marks the expanded ViewConfigPanel/spec-alignment work as complete in the roadmap.

@@ -325,8 +334,9 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
const hasColor = draft.showColor !== false;
const hasDensity = draft.showDensity !== false;
const hasExport = draft.exportOptions != null || draft.allowExport === true;
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

hasExport is derived from draft.exportOptions != null || draft.allowExport === true, which makes it impossible to disable export once exportOptions has been set (toggling allowExport off won’t change hasExport). Consider aligning this with the other toolbar flags (treat export as enabled unless allowExport === false) and/or clear exportOptions when export is disabled so the switch and sub-config behave consistently.

Suggested change
const hasExport = draft.exportOptions != null || draft.allowExport === true;
const hasExport = draft.allowExport !== false;

Copilot uses AI. Check for mistakes.
const hasAddForm = draft.addRecordViaForm === true;
const hasAddForm = draft.addRecordViaForm === true || draft.addRecord?.enabled === true;
const hasShowDescription = draft.showDescription !== false;
const navigationMode = draft.navigation?.mode || 'page';
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

navigationMode defaults to 'page' whenever draft.navigation?.mode is missing, but legacy views can still be configured via clickIntoRecordDetails: false (with no navigation object). In that case, the UI will incorrectly show "page" and may re-enable navigation on save. Derive the initial mode from clickIntoRecordDetails when navigation.mode is undefined (e.g., map false → 'none').

Suggested change
const navigationMode = draft.navigation?.mode || 'page';
const navigationMode =
draft.navigation?.mode ?? (draft.clickIntoRecordDetails === false ? 'none' : 'page');

Copilot uses AI. Check for mistakes.
Comment on lines +917 to +918
const val = Number(e.target.value) || undefined;
updateDraft('pagination', { ...(draft.pagination || {}), pageSize: val });
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

The pagination pageSize handler sets pageSize to undefined when the input is cleared (or 0), but the schema/type requires a number when pagination is present. This can leave draft.pagination as { pageSize: undefined }. Consider either removing pagination entirely when the input is empty, or keeping the previous value until a valid number is entered.

Suggested change
const val = Number(e.target.value) || undefined;
updateDraft('pagination', { ...(draft.pagination || {}), pageSize: val });
const raw = e.target.value;
const val = raw === '' ? undefined : Number(raw) || undefined;
if (val === undefined) {
// If no valid page size, remove pageSize (and possibly pagination)
if (!draft.pagination || Object.keys(draft.pagination).length <= 1) {
// No other pagination settings; clear pagination entirely
updateDraft('pagination', undefined as any);
} else {
// Preserve other pagination settings but drop pageSize
const { pageSize: _omit, ...rest } = draft.pagination;
updateDraft('pagination', rest as any);
}
} else {
updateDraft('pagination', { ...(draft.pagination || {}), pageSize: val });
}

Copilot uses AI. Check for mistakes.
Comment on lines +712 to +714
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
updateDraft('exportOptions', { ...(draft.exportOptions || {}), maxRecords: Number(e.target.value) || undefined })
}
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

maxRecords: Number(e.target.value) || undefined treats a valid value of 0 as undefined, but the placeholder/docs imply 0 is meaningful (often "unlimited"). Parse explicitly so '' → undefined while '0' → 0, otherwise users cannot set 0 intentionally.

Suggested change
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
updateDraft('exportOptions', { ...(draft.exportOptions || {}), maxRecords: Number(e.target.value) || undefined })
}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const raw = e.target.value;
const maxRecords =
raw === ''
? undefined
: Number.isNaN(Number(raw))
? undefined
: Number(raw);
updateDraft('exportOptions', {
...(draft.exportOptions || {}),
maxRecords,
});
}}

Copilot uses AI. Check for mistakes.
Comment on lines +569 to +575
<option value="page">Page</option>
<option value="drawer">Drawer</option>
<option value="modal">Modal</option>
<option value="split">Split</option>
<option value="popover">Popover</option>
<option value="new_window">New Window</option>
<option value="none">None</option>
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

Navigation mode option labels are hard-coded ("Page", "Drawer", etc.), so they won’t be translated even though i18n keys were added for this section. Consider sourcing these option labels from t(...) (and adding the corresponding keys) to keep the panel fully localized.

Suggested change
<option value="page">Page</option>
<option value="drawer">Drawer</option>
<option value="modal">Modal</option>
<option value="split">Split</option>
<option value="popover">Popover</option>
<option value="new_window">New Window</option>
<option value="none">None</option>
<option value="page">{t('console.objectView.navigationMode.page')}</option>
<option value="drawer">{t('console.objectView.navigationMode.drawer')}</option>
<option value="modal">{t('console.objectView.navigationMode.modal')}</option>
<option value="split">{t('console.objectView.navigationMode.split')}</option>
<option value="popover">{t('console.objectView.navigationMode.popover')}</option>
<option value="new_window">{t('console.objectView.navigationMode.newWindow')}</option>
<option value="none">{t('console.objectView.navigationMode.none')}</option>

Copilot uses AI. Check for mistakes.
Comment on lines +643 to +659
<option value="top">Top</option>
<option value="bottom">Bottom</option>
</select>
</ConfigRow>
<ConfigRow label={t('console.objectView.addRecordMode')}>
<select
data-testid="select-addRecord-mode"
className="text-xs h-7 rounded-md border border-input bg-background px-2 text-foreground max-w-[100px]"
value={draft.addRecord?.mode || 'form'}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
updateDraft('addRecord', { ...(draft.addRecord || {}), enabled: true, mode: e.target.value })
}
>
<option value="inline">Inline</option>
<option value="form">Form</option>
<option value="modal">Modal</option>
</select>
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

Add-record position/mode option labels ("Top/Bottom", "Inline/Form/Modal") are hard-coded, so they won’t be localized in non-English locales. Consider using i18n keys for these option labels (similar to selection/density).

Copilot uses AI. Check for mistakes.
Comment on lines +1572 to +1575
<option value="private">Private</option>
<option value="team">Team</option>
<option value="organization">Organization</option>
<option value="public">Public</option>
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

Sharing visibility option labels are hard-coded ("Private/Team/Organization/Public"), which bypasses i18n even though this PR adds new locale keys. Consider translating these option labels via t(...) to keep the sharing section localized.

Suggested change
<option value="private">Private</option>
<option value="team">Team</option>
<option value="organization">Organization</option>
<option value="public">Public</option>
<option value="private">{t('console.objectView.sharingVisibilityPrivate')}</option>
<option value="team">{t('console.objectView.sharingVisibilityTeam')}</option>
<option value="organization">{t('console.objectView.sharingVisibilityOrganization')}</option>
<option value="public">{t('console.objectView.sharingVisibilityPublic')}</option>

Copilot uses AI. Check for mistakes.
Comment on lines +839 to +846
// Propagate new spec properties (P0/P1/P2)
navigation: activeView?.navigation ?? (schema as any).navigation,
selection: activeView?.selection ?? (schema as any).selection,
pagination: activeView?.pagination ?? (schema as any).pagination,
searchableFields: activeView?.searchableFields ?? (schema as any).searchableFields,
filterableFields: activeView?.filterableFields ?? (schema as any).filterableFields,
resizable: activeView?.resizable ?? (schema as any).resizable,
hiddenFields: activeView?.hiddenFields ?? (schema as any).hiddenFields,
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

exportOptions is part of the expanded ListViewSchema/NamedListView config, but it’s not propagated into the schema passed to renderListView (only allowExport is). This can break live preview/renderer behavior for the export sub-config. Please add exportOptions: activeView?.exportOptions ?? (schema as any).exportOptions (and keep the existing allowExport gating if needed).

Copilot uses AI. Check for mistakes.
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.

ViewConfigPanel 对齐 @objectstack/spec ListViewSchema 完整修改清单

3 participants