Skip to content

ObjectGrid cell type rendering: select/status Badge colors, date Overdue format, boolean semantic indicators#823

Merged
hotlong merged 5 commits intomainfrom
copilot/optimize-objectgrid-cell-rendering
Feb 24, 2026
Merged

ObjectGrid cell type rendering: select/status Badge colors, date Overdue format, boolean semantic indicators#823
hotlong merged 5 commits intomainfrom
copilot/optimize-objectgrid-cell-rendering

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 24, 2026

Module 4 of #820 — cell renderers for inferred column types were rendering without visual distinction: select/status fields showed plain text (no color), dates showed raw formatted strings (no relative time), booleans rendered unchecked checkboxes for null.

Select/Status Badge rendering

  • getCellRenderer() maps both select and status types to SelectCellRenderer with colored Badge output
  • Auto-generate options from unique data values when inferColumnType() infers 'select' — passes them through fieldMeta so SelectCellRenderer can render colored badges
  • Priority semantic color auto-detection via PRIORITY_COLOR_MAP: Critical→red, High→orange, Medium→yellow, Low→gray
  • Default badge style changed from hardcoded blue to muted (bg-muted text-muted-foreground border-border) when no color is configured
// Before: all inferred select values rendered as blue badges (no options passed)
<CellRenderer value={value} field={{ name: col.field, type: 'select' }} />

// After: options auto-populated from data, priority colors auto-detected
<CellRenderer value={value} field={fieldMeta} />
// fieldMeta.options = [{ value: 'High', label: 'High' }, ...]

Date formatting

  • DateCellRenderer defaults to 'relative' style — "Today", "Yesterday", "Overdue 3d", "In 4 days"
  • Overdue dates (2–7 days past) render as "Overdue Xd" with text-red-600 styling
  • Dates beyond 7 days fall back to human-readable format ("Feb 26, 2026")
  • Full ISO string available via title tooltip
  • formatRelativeDate() threshold tightened from 30→7 days

Boolean visualization

  • BooleanCellRenderer renders for null/undefined instead of an unchecked checkbox
  • Semantic completion fields (is_completed, done) render as green circle indicators with icon (true) or empty circle (false), with role="img" and aria-label for accessibility
  • Non-completion boolean fields continue to render standard <Checkbox disabled>

Housekeeping

  • Moved PRIORITY_COLOR_MAP, BADGE_COLOR_MAP, getBadgeColorClasses() to module-level constants
  • 45 new tests covering select/status color mapping, date relative/overdue, boolean null, completion indicators, formatRelativeDate edge cases
  • Updated ROADMAP.md cell renderer section
Original prompt

This section details on the original issue you should resolve

<issue_title>ObjectGrid 单元格类型渲染优化 — select Badge / 日期格式化 / Boolean 视觉化</issue_title>
<issue_description>## 背景

本 issue 为 #820 的遗留工作。#820 的 6 大模块中,第 4️⃣ 模块(ObjectGrid 单元格渲染器)是目前唯一完全未完成的部分,其余模块已在 PR #821#805#817#799 等中基本落地。

现状问题

  • inferColumnType() 自动推断类型后的渲染缺少视觉标识——select/status 类字段显示纯文本
  • 日期字段直接输出 ISO 字符串(如 2026-02-26T03:09:07.458Z),用户无法快速识别时间含义
  • Boolean 字段无视觉渲染,显示 true/false 原始值

涉及文件

  • packages/plugin-grid/src/ObjectGrid.tsxinferColumnType() 和列生成逻辑
  • packages/fields/src/formatters.tsformatDate() 等格式化函数
  • packages/fields/src/widgets/ — 字段级渲染组件
  • packages/fields/src/cell-renderers.tsgetCellRenderer() 函数

具体改进

1. select/status 类型 Badge 渲染

  • field.typeselectstatus 时,getCellRenderer() 返回 <Badge> 组件
  • 支持从 field.options 映射颜色(如 { value: 'In Progress', color: 'blue' }<Badge variant="blue">
  • 未配置颜色时使用默认 muted 样式的 Badge
  • Priority 字段支持语义配色(Critical → red, High → orange, Medium → yellow, Low → gray)

2. 日期类型格式化

  • formatDate()packages/fields/src/formatters.ts 中增加相对时间模式:
    • 今天 → "Today"
    • 昨天 → "Yesterday"
    • 7 天内 → "3 days ago"
    • 逾期 → 红色文字 "Overdue 2d"
    • 超过 7 天 → "Feb 26, 2026" 人类可读格式
  • ObjectGrid inferColumnType() 推断为 date 类型时默认启用人类可读格式
  • 保留 ISO 格式作为 hover tooltip 显示完整时间

3. Boolean 类型视觉渲染

  • Boolean 字段渲染为 <Checkbox disabled checked={value} />✓/— 图标
  • 支持 is_completed 等布尔字段的语义化渲染(绿色圆形指示器)
  • null/undefined 值显示为空白或

4. 表头样式统一(已在 #821 中部分完成,需验证)

  • 确认 ObjectGrid 表头行使用 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70 bg-muted/5
  • 如未完成,补齐表头样式

测试要求

  • getCellRenderer() 的 select/date/boolean 类型新增单元测试
  • formatDate() 相对时间模式新增测试用例(含 edge case:null、未来日期、逾期日期)
  • pnpm test 全量通过

验收标准

  • ObjectGrid 中 select/status 字段显示为带颜色的 Badge
  • 日期字段显示为人类可读格式,逾期用红色标记
  • Boolean 字段显示为 Checkbox 或图标
  • 全部测试通过
  • ROADMAP.md 更新 ObjectUI 平台级界面风格与交互一致性优化 #820 第 4️⃣ 模块为已完成

关联

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


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 24, 2026

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

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 24, 2026 6:56am
objectui-demo Ready Ready Preview, Comment Feb 24, 2026 6:56am
objectui-storybook Ready Ready Preview, Comment Feb 24, 2026 6:56am

Request Review

…tive format, boolean null handling

- SelectCellRenderer: priority semantic color auto-detection (Critical→red, High→orange, Medium→yellow, Low→gray), muted default for unconfigured colors
- DateCellRenderer: relative format by default, overdue red text styling, ISO tooltip
- BooleanCellRenderer: null/undefined renders as — instead of unchecked checkbox
- ObjectGrid: auto-generates field options from data for inferred select columns
- formatRelativeDate: threshold tightened from 30 to 7 days
- 40 new unit tests for cell renderers and formatRelativeDate edge cases
- ROADMAP.md updated with Module 4 completion details

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

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Optimize ObjectGrid cell rendering for select, date, and boolean types ObjectGrid cell type rendering: select Badge colors, date relative format, boolean null handling Feb 24, 2026
Copilot AI requested a review from hotlong February 24, 2026 06:38
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Feb 24, 2026

@copilot 仔细检查确认已经完成任务要求的所有开发内容

Copilot AI and others added 2 commits February 24, 2026 06:45
…erdue Xd format, is_completed green indicator

- getCellRenderer(): add 'status' type → SelectCellRenderer mapping
- formatRelativeDate(): overdue dates now show "Overdue Xd" format (e.g. "Overdue 3d")
- BooleanCellRenderer: is_completed/done fields render as green circle indicator
- Update airtable-style test for new completion indicator
- Add 5 new tests for status type, completion indicator variants

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Use exact field name matching to prevent false positives
- Add role="img" and aria-label to completion indicators

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

Copilot AI commented Feb 24, 2026

@copilot 仔细检查确认已经完成任务要求的所有开发内容

已逐项检查并补全了遗漏的需求,最终提交 8535939

1. select/status Badge 渲染

  • getCellRenderer() 同时映射 selectstatusSelectCellRenderer(之前缺少 status 映射)
  • field.options 颜色映射 + 未配置颜色时 muted 默认样式
  • Priority 语义配色:Critical→red, High→orange, Medium→yellow, Low→gray
  • ObjectGrid 自动从数据生成 options

2. 日期格式化

  • 今天 → "Today", 昨天 → "Yesterday", 逾期 → "Overdue 3d" (之前是 "3 days ago",已修正为 issue 要求的 "Overdue Xd" 格式)
  • 7天内未来 → "In 4 days", 超过7天 → "Feb 26, 2026"
  • 逾期红色文字 text-red-600
  • ISO tooltip 保留

3. Boolean 视觉渲染

  • Checkbox disabled 渲染 true/false
  • null/undefined →
  • is_completed/done 语义化绿色圆形指示器(之前缺失,已补全)
  • 添加 role="img" + aria-label 无障碍属性

4. 表头样式 ✅ 已确认 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70 bg-muted/30 在 data-table 和 VirtualGrid 中

5. 测试 ✅ 45 个新测试 + 全部 263 grid / 190 fields / 229 detail 测试通过

6. ROADMAP.md ✅ 已更新

Copilot AI changed the title ObjectGrid cell type rendering: select Badge colors, date relative format, boolean null handling ObjectGrid cell type rendering: select/status Badge colors, date Overdue format, boolean semantic indicators Feb 24, 2026
@hotlong hotlong marked this pull request as ready for review February 24, 2026 06:56
Copilot AI review requested due to automatic review settings February 24, 2026 06:56
@hotlong hotlong merged commit cbb6fb6 into main Feb 24, 2026
4 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 implements Module 4 of #820, adding visual rendering improvements for ObjectGrid cell types (select/status, date, boolean) that were previously rendering as plain text. The changes enable Airtable-style badge rendering for select fields, human-readable date formatting with overdue indicators, and semantic visualization for boolean completion fields.

Changes:

  • Select/status fields now render as colored badges with auto-generated options from unique data values and priority semantic color detection (Critical→red, High→orange, etc.)
  • Date fields default to relative format ("Today", "Overdue 3d") with red text for overdue dates and ISO tooltips
  • Boolean fields render as checkboxes for true/false, dash for null, and green circle indicators for completion fields (is_completed, done)

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/fields/src/index.tsx Added PRIORITY_COLOR_MAP and BADGE_COLOR_MAP constants; updated SelectCellRenderer with getBadgeColorClasses helper for semantic colors; modified DateCellRenderer to default to relative format with overdue styling; enhanced BooleanCellRenderer with null handling and completion field semantic indicators; added status type mapping to SelectCellRenderer
packages/plugin-grid/src/ObjectGrid.tsx Added fieldMeta building logic to auto-generate select field options from unique data values when type is inferred; passes options through to cell renderers
packages/fields/src/tests/cell-renderers.test.tsx New comprehensive test file with 45 tests covering getCellRenderer type resolution, SelectCellRenderer color mapping, DateCellRenderer relative formatting and overdue styling, BooleanCellRenderer null handling and completion indicators, formatRelativeDate edge cases
packages/fields/src/tests/boolean-checkbox.test.tsx Updated test expectation for null values to verify dash rendering instead of unchecked checkbox
packages/plugin-grid/src/tests/airtable-style.test.tsx Updated date field test to check for relative format rendering; updated boolean field test to verify completion indicators instead of checkboxes
ROADMAP.md Added three completed items documenting select/status Badge rendering, date formatting improvements, and boolean visual rendering under ObjectGrid Cell Renderers section

Comment on lines +347 to +357
// Color to Tailwind class mapping for custom Badge styling
const BADGE_COLOR_MAP: Record<string, string> = {
gray: 'bg-gray-100 text-gray-800 border-gray-300',
red: 'bg-red-100 text-red-800 border-red-300',
orange: 'bg-orange-100 text-orange-800 border-orange-300',
yellow: 'bg-yellow-100 text-yellow-800 border-yellow-300',
green: 'bg-green-100 text-green-800 border-green-300',
blue: 'bg-blue-100 text-blue-800 border-blue-300',
indigo: 'bg-indigo-100 text-indigo-800 border-indigo-300',
purple: 'bg-purple-100 text-purple-800 border-purple-300',
pink: 'bg-pink-100 text-pink-800 border-pink-300',
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The BADGE_COLOR_MAP uses hardcoded Tailwind color classes instead of CSS custom properties or theme tokens, which violates the ObjectUI theming convention established in guideline CodingGuidelineID 1000000 Rule #3. These hardcoded color classes (e.g., 'bg-gray-100', 'bg-red-100') won't respond to theme changes or dark mode.

Consider using theme tokens (bg-muted, text-muted-foreground) or creating semantic CSS custom properties for badge colors to ensure theming consistency across the application.

Suggested change
// Color to Tailwind class mapping for custom Badge styling
const BADGE_COLOR_MAP: Record<string, string> = {
gray: 'bg-gray-100 text-gray-800 border-gray-300',
red: 'bg-red-100 text-red-800 border-red-300',
orange: 'bg-orange-100 text-orange-800 border-orange-300',
yellow: 'bg-yellow-100 text-yellow-800 border-yellow-300',
green: 'bg-green-100 text-green-800 border-green-300',
blue: 'bg-blue-100 text-blue-800 border-blue-300',
indigo: 'bg-indigo-100 text-indigo-800 border-indigo-300',
purple: 'bg-purple-100 text-purple-800 border-purple-300',
pink: 'bg-pink-100 text-pink-800 border-pink-300',
// Color to Tailwind class mapping for custom Badge styling (using semantic theme tokens)
const BADGE_COLOR_MAP: Record<string, string> = {
// Neutral / default
gray: 'bg-muted text-muted-foreground border-border',
// Destructive / warning spectrum
red: 'bg-destructive/10 text-destructive border-destructive/30',
orange: 'bg-destructive/10 text-destructive border-destructive/30',
yellow: 'bg-destructive/10 text-destructive border-destructive/30',
// Secondary / informational spectrum
green: 'bg-secondary text-secondary-foreground border-border',
blue: 'bg-secondary text-secondary-foreground border-border',
indigo: 'bg-secondary text-secondary-foreground border-border',
purple: 'bg-secondary text-secondary-foreground border-border',
pink: 'bg-secondary text-secondary-foreground border-border',

Copilot uses AI. Check for mistakes.
return (
<div className="flex items-center justify-center">
{value ? (
<div className="size-5 rounded-full bg-green-500 flex items-center justify-center" role="img" aria-label="Completed" data-testid="completion-indicator">
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The completion indicator for boolean fields uses a hardcoded bg-green-500 color instead of a theme token. This violates the theming guidelines (CodingGuidelineID 1000000 Rule #3) which require all colors to use theme tokens to support dark mode and custom theming.

Consider using a semantic color token like bg-success or creating a CSS custom property for completion status colors to ensure theming consistency.

Suggested change
<div className="size-5 rounded-full bg-green-500 flex items-center justify-center" role="img" aria-label="Completed" data-testid="completion-indicator">
<div className="size-5 rounded-full bg-success flex items-center justify-center" role="img" aria-label="Completed" data-testid="completion-indicator">

Copilot uses AI. Check for mistakes.
Comment on lines +470 to +473
if (inferredType === 'select' && !(col as any).options) {
// Auto-generate options from unique data values for inferred select fields
const uniqueValues = Array.from(new Set(data.map(row => row[col.field]).filter(Boolean)));
fieldMeta.options = uniqueValues.map(v => ({ value: v, label: String(v) }));
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The auto-generation of select field options recalculates unique values on every render of generateColumns. For large datasets, this could cause performance issues as it iterates through all data rows for each select column.

Consider memoizing the auto-generated options or moving this calculation outside the column mapping loop to avoid redundant computation. Alternatively, you could compute all unique values once per column at the start of the generateColumns callback.

Copilot uses AI. Check for mistakes.
Comment on lines +291 to +296
// Determine if date is overdue (in the past)
const date = typeof value === 'string' ? new Date(value) : value;
const isValidDate = date instanceof Date && !isNaN(date.getTime());
const startOfToday = new Date();
startOfToday.setHours(0, 0, 0, 0);
const isOverdue = isValidDate && date < startOfToday;
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The DateCellRenderer applies red text styling to "Yesterday" dates, but the "Overdue Xd" format only starts at 2+ days in the past. This creates an inconsistency where "Yesterday" appears in red but doesn't have an "Overdue" label, while dates 2+ days past show both red styling and "Overdue Xd" label.

Consider updating line 296 to const isOverdue = isValidDate && diffDays < -1; to only apply red styling when the date matches the "Overdue Xd" terminology (2-7 days past). You'll need to calculate diffDays in DateCellRenderer similar to formatRelativeDate, or add it as a helper function.

Suggested change
// Determine if date is overdue (in the past)
const date = typeof value === 'string' ? new Date(value) : value;
const isValidDate = date instanceof Date && !isNaN(date.getTime());
const startOfToday = new Date();
startOfToday.setHours(0, 0, 0, 0);
const isOverdue = isValidDate && date < startOfToday;
// Determine if date is overdue (in the past, excluding "Yesterday" to match "Overdue Xd" semantics)
const date = typeof value === 'string' ? new Date(value) : value;
const isValidDate = date instanceof Date && !isNaN(date.getTime());
const startOfToday = new Date();
startOfToday.setHours(0, 0, 0, 0);
let diffDays = 0;
if (isValidDate) {
// Normalize target date to midnight for day-based comparison
const dateAtMidnight = new Date(date.getTime());
dateAtMidnight.setHours(0, 0, 0, 0);
diffDays = Math.round(
(dateAtMidnight.getTime() - startOfToday.getTime()) / (1000 * 60 * 60 * 24),
);
}
const isOverdue = isValidDate && diffDays < -1;

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.

ObjectGrid 单元格类型渲染优化 — select Badge / 日期格式化 / Boolean 视觉化

3 participants