Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…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 仔细检查确认已经完成任务要求的所有开发内容 |
…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>
已逐项检查并补全了遗漏的需求,最终提交 1. select/status Badge 渲染 ✅
2. 日期格式化 ✅
3. Boolean 视觉渲染 ✅
4. 表头样式 ✅ 已确认 5. 测试 ✅ 45 个新测试 + 全部 263 grid / 190 fields / 229 detail 测试通过 6. ROADMAP.md ✅ 已更新 |
There was a problem hiding this comment.
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 |
| // 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', |
There was a problem hiding this comment.
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.
| // 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', |
| 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"> |
There was a problem hiding this comment.
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.
| <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"> |
| 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) })); |
There was a problem hiding this comment.
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.
| // 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; |
There was a problem hiding this comment.
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.
| // 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; |
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 bothselectandstatustypes toSelectCellRendererwith colored Badge outputoptionsfrom unique data values wheninferColumnType()infers'select'— passes them throughfieldMetasoSelectCellRenderercan render colored badgesPRIORITY_COLOR_MAP:Critical→red,High→orange,Medium→yellow,Low→graybg-muted text-muted-foreground border-border) when no color is configuredDate formatting
DateCellRendererdefaults to'relative'style — "Today", "Yesterday", "Overdue 3d", "In 4 days""Overdue Xd"withtext-red-600styling"Feb 26, 2026")titletooltipformatRelativeDate()threshold tightened from 30→7 daysBoolean visualization
BooleanCellRendererrenders—fornull/undefinedinstead of an unchecked checkboxis_completed,done) render as green circle indicators with✓icon (true) or empty circle (false), withrole="img"andaria-labelfor accessibility<Checkbox disabled>Housekeeping
PRIORITY_COLOR_MAP,BADGE_COLOR_MAP,getBadgeColorClasses()to module-level constantsformatRelativeDateedge casesOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.