Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,9 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind

**Phase 2 — Schema Factory (All Sections):**
- [x] Page Config section: label, description, viewType, toolbar toggles (7 switches), navigation mode/width/openNewTab, selection, addRecord sub-editor, export + sub-config, showRecordCount, allowPrinting
- [x] Data section: source, sortBy (expandable), groupBy, prefixField, columns selector (expandable w/ reorder), filterBy (expandable), pagination, searchable/filterable/hidden fields (expandable), quickFilters (expandable), virtualScroll, type-specific options (kanban/calendar/map/gallery/timeline/gantt)
- [x] Appearance section: color, fieldTextColor, rowHeight (icon group), wrapHeaders, showDescription, collapseAllByDefault, striped, bordered, resizable, densityMode, conditionalFormatting (expandable), emptyState (title/message/icon)
- [x] User Actions section: inlineEdit, addDeleteRecordsInline, rowActions (expandable), bulkActions (expandable)
- [x] Data section: source, sortBy (expandable), groupBy (grid/gallery), columns selector (expandable w/ reorder), filterBy (expandable), pagination, searchable/filterable/hidden fields (expandable), quickFilters (expandable), virtualScroll (grid only), type-specific options (kanban/calendar/map/gallery/timeline/gantt)
- [x] Appearance section: color (grid/calendar/timeline/gantt), rowHeight (icon group, grid only), wrapHeaders (grid only), showDescription, striped/bordered (grid only), resizable (grid only), conditionalFormatting (expandable, grid/kanban), emptyState (title/message/icon)
- [x] User Actions section: inlineEdit (grid only), addDeleteRecordsInline (grid only), rowActions/bulkActions (expandable, grid/kanban)
- [x] Sharing section: sharingEnabled, sharingVisibility (visibleWhen: sharing.enabled)
- [x] Accessibility section: ariaLabel, ariaDescribedBy, ariaLive
- [x] `ExpandableWidget` component for hook-safe expandable sub-sections within custom render functions
Expand Down Expand Up @@ -433,6 +433,23 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
- [x] Add `helpText` to navigation-dependent fields (width/openNewTab) with i18n hints (all 11 locales)
- [x] 24 new tests: expandedSections override (3), disabledWhen evaluation (2), grid-only disabledWhen predicates (16), helpText validation (2), description spec alignment (1)

**Phase 6 — Config Panel Cleanup (Invalid Items Fix):**
- [x] Remove `densityMode` field from appearance section (redundant with `rowHeight` which provides finer 5-value granularity)
- [x] Remove `prefixField` from data section (not consumed by any runtime renderer)
- [x] Remove `collapseAllByDefault` from appearance section (not consumed by any runtime renderer)
- [x] Remove `fieldTextColor` from appearance section (not consumed by any runtime renderer)
- [x] Remove `clickIntoRecordDetails` from userActions section (controlled implicitly via navigation mode, not directly consumed)
- [x] Add view-type-aware `visibleWhen` to toolbar toggles: `showGroup` (grid/kanban/gallery), `showColor` (grid/calendar/timeline/gantt), `showDensity` (grid only), `showRecordCount` (grid only), `allowPrinting` (grid only)
- [x] Add view-type-aware `visibleWhen` to data fields: `_groupBy` (grid/gallery — kanban uses dedicated type-specific option), `virtualScroll` (grid only)
- [x] Add view-type-aware `visibleWhen` to appearance fields: `striped`/`bordered`/`wrapHeaders`/`resizable`/`rowHeight` (grid only, changed from disabledWhen to visibleWhen), `color` (grid/calendar/timeline/gantt), `conditionalFormatting` (grid/kanban)
- [x] Add view-type-aware `visibleWhen` to userActions fields: `inlineEdit`/`addDeleteRecordsInline` (grid only), `rowActions`/`bulkActions` (grid/kanban)
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 ROADMAP line 445 incorrectly states that rowActions/bulkActions support "grid/kanban" view types. Based on the runtime code investigation (packages/plugin-list/src/ListView.tsx lines 768-769), rowActions and bulkActions are only passed to the grid view schema, not to kanban or any other view type. The kanban plugin does not consume these properties.

This should be updated to:

- [x] Add view-type-aware `visibleWhen` to userActions fields: `inlineEdit`/`addDeleteRecordsInline` (grid only), `rowActions`/`bulkActions` (grid only)

This aligns with the critical bug identified in the view-config-schema.tsx predicates where supportsRowActions incorrectly includes 'kanban'.

Suggested change
- [x] Add view-type-aware `visibleWhen` to userActions fields: `inlineEdit`/`addDeleteRecordsInline` (grid only), `rowActions`/`bulkActions` (grid/kanban)
- [x] Add view-type-aware `visibleWhen` to userActions fields: `inlineEdit`/`addDeleteRecordsInline` (grid only), `rowActions`/`bulkActions` (grid only)

Copilot uses AI. Check for mistakes.
- [x] Correct `searchableFields`/`filterableFields`/`quickFilters`/`showDescription` to universal (all view types) — data fetch/toolbar features not view-specific
- [x] Extend `buildSwitchField` and `buildFieldMultiSelect` helpers to accept `visibleWhen` parameter
- [x] Define semantic predicates: `supportsGrouping`, `supportsColorField`, `supportsConditionalFormatting`, `supportsRowActions`, `supportsGenericGroupBy`
- [x] 103 schema tests pass (updated field key lists, visibleWhen predicates for all view types, removed field verification)
- [x] 136 ViewConfigPanel interaction tests pass (removed tests for deleted fields)
- [x] 10 config-sync integration tests pass

**Code Reduction:** ~1655 lines imperative → ~170 lines declarative wrapper + ~1100 lines schema factory + ~180 lines shared utils = **>50% net reduction in component code** with significantly improved maintainability

### P1.9 Console — Content Area Layout & Responsiveness
Expand Down
94 changes: 6 additions & 88 deletions apps/console/src/__tests__/ViewConfigPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1250,21 +1250,19 @@ describe('ViewConfigPanel', () => {

// ── Appearance fields tests ──

it('renders new appearance fields: color, fieldTextColor, rowHeight, wrapHeaders, collapseAllByDefault', () => {
it('renders new appearance fields: color, rowHeight, wrapHeaders', () => {
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
activeView={{ ...mockActiveView, groupBy: 'stage' }}
objectDef={mockObjectDef}
/>
);

expect(screen.getByTestId('appearance-color')).toBeInTheDocument();
expect(screen.getByTestId('appearance-fieldTextColor')).toBeInTheDocument();
expect(screen.getByTestId('appearance-rowHeight')).toBeInTheDocument();
expect(screen.getByTestId('toggle-wrapHeaders')).toBeInTheDocument();
expect(screen.getByTestId('toggle-collapseAllByDefault')).toBeInTheDocument();
});

it('changes row height via icon buttons', () => {
Expand Down Expand Up @@ -1346,16 +1344,15 @@ describe('ViewConfigPanel', () => {
);

expect(screen.getByTestId('data-groupBy')).toBeInTheDocument();
expect(screen.getByTestId('data-prefixField')).toBeInTheDocument();
});

it('changes groupBy and propagates to kanban type option', () => {
it('changes groupBy for grid view', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={{ ...mockActiveView, type: 'kanban' }}
activeView={{ ...mockActiveView, type: 'grid' }}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
Expand All @@ -1365,7 +1362,6 @@ describe('ViewConfigPanel', () => {
fireEvent.change(groupBySelect, { target: { value: 'stage' } });

expect(onViewUpdate).toHaveBeenCalledWith('groupBy', 'stage');
expect(onViewUpdate).toHaveBeenCalledWith('kanban', expect.objectContaining({ groupByField: 'stage' }));
});

// ── Calendar endDateField test ──
Expand Down Expand Up @@ -2014,34 +2010,7 @@ describe('ViewConfigPanel', () => {
expect(onViewUpdate).toHaveBeenCalledWith('resizable', true);
});

it('renders density mode select in appearance section', () => {
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
objectDef={mockObjectDef}
/>
);

expect(screen.getByTestId('select-densityMode')).toBeInTheDocument();
});

it('changes densityMode and calls onViewUpdate', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
);

fireEvent.change(screen.getByTestId('select-densityMode'), { target: { value: 'compact' } });
expect(onViewUpdate).toHaveBeenCalledWith('densityMode', 'compact');
});
// densityMode tests removed — densityMode field was removed (redundant with rowHeight)

it('renders conditional formatting editor when expanded', () => {
render(
Expand Down Expand Up @@ -2436,22 +2405,6 @@ describe('ViewConfigPanel', () => {

// ── Spec alignment: toggle interaction tests for all switch fields ──

it('toggles collapseAllByDefault and calls onViewUpdate', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
);

fireEvent.click(screen.getByTestId('toggle-collapseAllByDefault'));
expect(onViewUpdate).toHaveBeenCalledWith('collapseAllByDefault', true);
});

it('toggles showDescription and calls onViewUpdate', () => {
const onViewUpdate = vi.fn();
render(
Expand All @@ -2468,22 +2421,6 @@ describe('ViewConfigPanel', () => {
expect(onViewUpdate).toHaveBeenCalledWith('showDescription', false);
});

it('toggles clickIntoRecordDetails and calls onViewUpdate', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
);

fireEvent.click(screen.getByTestId('toggle-clickIntoRecordDetails'));
expect(onViewUpdate).toHaveBeenCalledWith('clickIntoRecordDetails', false);
});

it('toggles addDeleteRecordsInline and calls onViewUpdate', () => {
const onViewUpdate = vi.fn();
render(
Expand Down Expand Up @@ -2693,26 +2630,7 @@ describe('ViewConfigPanel', () => {
}));
});

// ── Boundary: densityMode enum selection ──

it('changes densityMode to all enum values', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
);

const select = screen.getByTestId('select-densityMode');
['compact', 'comfortable', 'spacious'].forEach((mode) => {
fireEvent.change(select, { target: { value: mode } });
expect(onViewUpdate).toHaveBeenCalledWith('densityMode', mode);
});
});
// densityMode enum test removed — densityMode field was removed (redundant with rowHeight)

// ── Conditional rendering: addRecord sub-editor hidden when not enabled ──

Expand Down
Loading