Skip to content

Commit 8750229

Browse files
authored
Merge pull request #716 from objectstack-ai/copilot/refactor-viewconfigpanel-structure
2 parents 55ee339 + 6002a3a commit 8750229

19 files changed

Lines changed: 320 additions & 87 deletions

File tree

ROADMAP.md

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
145145
-`gridSchema` in plugin-view includes `striped`/`bordered` from active view config (Grid only)
146146
- ✅ Plugin `renderContent` passes `rowHeight`, `densityMode`, `groupBy` to `renderListView` schema
147147
-`useMemo` dependency arrays expanded to cover full view config
148-
- ⚠️ `showSort`/`showSearch`/`showFilters` only wired to Grid — not propagated to Kanban/Calendar/Timeline/Gallery/Map/Gantt
149-
- ⚠️ `striped`/`bordered` only applied via `gridSchema` — not passed to non-grid views through `renderListView`
150-
- ⚠️ `generateViewSchema` sets `showSearch: false` unconditionally for all non-grid types
151-
- ⚠️ Console `renderListView` callback does not pass `showSort`/`showSearch`/`showFilters`/`striped`/`bordered` to `fullSchema`
148+
-`generateViewSchema` propagates `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` from `activeView` for all view types (hardcoded `showSearch: false` removed)
149+
- ✅ Console `renderListView` passes `showSort`/`showSearch`/`showFilters`/`striped`/`bordered`/`color`/`filter`/`sort` to `fullSchema`
150+
-`NamedListView` type declares `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` as first-class properties
151+
-`ListViewSchema` TypeScript interface and Zod schema include `showSearch`/`showSort`/`showFilters`/`color`
152+
- ✅ ViewConfigPanel refactored into Page Config (toolbar/shell) and ListView Config (data/appearance) sections
152153
- ⚠️ No per-view-type integration tests verifying config properties reach non-grid renderers
153154
- [ ] Conditional formatting rules
154155

@@ -160,57 +161,58 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
160161

161162
| Property | Grid | Kanban | Calendar | Timeline | Gallery | Map | Gantt |
162163
|----------|:----:|:------:|:--------:|:--------:|:-------:|:---:|:-----:|
163-
| `showSearch` || | | | | | |
164-
| `showSort` || | | | | | |
165-
| `showFilters` || | | | | | |
164+
| `showSearch` || | | | | | |
165+
| `showSort` || | | | | | |
166+
| `showFilters` || | | | | | |
166167
| `rowHeight` ||||||||
167168
| `densityMode` ||||||||
168-
| `striped` || | | | | | |
169-
| `bordered` || | | | | | |
169+
| `striped` || | | | | | |
170+
| `bordered` || | | | | | |
170171
| `groupBy` | N/A || N/A | N/A | N/A | N/A | N/A |
171-
| `color` ||||||||
172+
| `color` ||||||||
173+
| `filter`/`sort` ||||||||
172174
| Type-specific options ||||||||
173175

174-
**Root Causes:**
175-
1. **`generateViewSchema` (plugin-view):** Hardcodes `showSearch: false` for non-grid views; does not propagate `showSort`/`showFilters`/`striped`/`bordered`/`color` from `activeView`
176-
2. **Console `renderListView`:** Omits `showSort`/`showSearch`/`showFilters`/`striped`/`bordered` from the `fullSchema` passed to `ListView`
177-
3. **`NamedListView` type:** Does not declare `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` as first-class properties
178-
4. **No per-view-type integration tests:** Tests verify config reaches the `ViewConfigPanel` switch, but not that non-grid renderers actually receive and apply the properties
176+
**Root Causes (resolved):**
177+
1. ~~**`generateViewSchema` (plugin-view):** Hardcodes `showSearch: false` for non-grid views~~ → Now propagates from `activeView`
178+
2. ~~**Console `renderListView`:** Omits toolbar/display flags from `fullSchema`~~ → Now passes all config properties
179+
3. ~~**`NamedListView` type:** Missing toolbar/display properties~~ → Added as first-class properties
180+
4. **No per-view-type integration tests:** Pending — tests verify config reaches `fullSchema`, but per-renderer integration tests still needed
179181

180182
**Phase 1 — Grid/Table View (baseline, already complete):**
181183
- [x] `gridSchema` includes `striped`/`bordered` from `activeView`
182184
- [x] `showSort`/`showSearch`/`showFilters` passed via `ObjectViewSchema`
183185
- [x] `useMemo` dependency arrays cover all grid config
184186

185187
**Phase 2 — Kanban Live Preview:**
186-
- [ ] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` kanban branch
187-
- [ ] Pass `color`/`striped`/`bordered` in `renderContent``renderListView` for kanban
188-
- [ ] Ensure `groupBy` config changes reflect immediately (currently ✅ via `renderListView`)
188+
- [x] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` kanban branch
189+
- [x] Pass `color`/`striped`/`bordered` in `renderContent``renderListView` for kanban
190+
- [x] Ensure `groupBy` config changes reflect immediately (currently ✅ via `renderListView`)
189191
- [ ] Add integration test: ViewConfigPanel kanban config change → Kanban renderer receives updated props
190192

191193
**Phase 3 — Calendar Live Preview:**
192-
- [ ] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` calendar branch
193-
- [ ] Pass `filter`/`sort`/appearance properties to calendar renderer in real-time
194-
- [ ] Verify `startDateField`/`endDateField` config changes trigger re-render via `useMemo` deps
194+
- [x] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` calendar branch
195+
- [x] Pass `filter`/`sort`/appearance properties to calendar renderer in real-time
196+
- [x] Verify `startDateField`/`endDateField` config changes trigger re-render via `useMemo` deps
195197
- [ ] Add integration test: ViewConfigPanel calendar config change → Calendar renderer receives updated props
196198

197199
**Phase 4 — Timeline/Gantt Live Preview:**
198-
- [ ] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` timeline/gantt branches
199-
- [ ] Pass appearance properties (`color`, `striped`, `bordered`) through `renderListView` schema
200-
- [ ] Ensure `dateField`/`startDateField`/`endDateField` config changes trigger re-render
200+
- [x] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` timeline/gantt branches
201+
- [x] Pass appearance properties (`color`, `striped`, `bordered`) through `renderListView` schema
202+
- [x] Ensure `dateField`/`startDateField`/`endDateField` config changes trigger re-render
201203
- [ ] Add integration tests for timeline and gantt config sync
202204

203205
**Phase 5 — Gallery & Map Live Preview:**
204-
- [ ] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` gallery/map branches
205-
- [ ] Pass appearance properties through `renderListView` schema for gallery/map
206-
- [ ] Ensure gallery `imageField`/`titleField` and map `locationField`/`zoom`/`center` config changes trigger re-render
206+
- [x] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` gallery/map branches
207+
- [x] Pass appearance properties through `renderListView` schema for gallery/map
208+
- [x] Ensure gallery `imageField`/`titleField` and map `locationField`/`zoom`/`center` config changes trigger re-render
207209
- [ ] Add integration tests for gallery and map config sync
208210

209211
**Phase 6 — Data Flow & Dependency Refactor:**
210-
- [ ] Add `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` to `NamedListView` type in `@object-ui/types`
211-
- [ ] Update Console `renderListView` to pass all config properties in `fullSchema`
212+
- [x] Add `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` to `NamedListView` type in `@object-ui/types`
213+
- [x] Update Console `renderListView` to pass all config properties in `fullSchema`
212214
- [ ] Audit all `useMemo`/`useEffect` dependency arrays in `plugin-view/ObjectView.tsx` for missing `activeView` sub-properties
213-
- [ ] Remove hardcoded `showSearch: false` from `generateViewSchema` — use `activeView.showSearch ?? schema.showSearch` instead
215+
- [x] Remove hardcoded `showSearch: false` from `generateViewSchema` — use `activeView.showSearch ?? schema.showSearch` instead
214216

215217
**Phase 7 — End-to-End Integration Tests:**
216218
- [ ] Per-view-type test: Grid config sync (showSort, showSearch, showFilters, striped, bordered)

apps/console/src/__tests__/ViewConfigPanel.test.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,4 +1479,95 @@ describe('ViewConfigPanel', () => {
14791479
// The second move should operate on the updated state
14801480
expect(onViewUpdate).toHaveBeenCalledTimes(2);
14811481
});
1482+
1483+
// ── Section Layout Tests: Page vs ListView Config ──
1484+
1485+
it('renders page-level config items in the Page section (showSearch, showFilters, showSort, clickIntoRecordDetails, addRecordViaForm, allowExport)', () => {
1486+
render(
1487+
<ViewConfigPanel
1488+
open={true}
1489+
onClose={vi.fn()}
1490+
activeView={mockActiveView}
1491+
objectDef={mockObjectDef}
1492+
/>
1493+
);
1494+
1495+
// Page section should contain toolbar toggles
1496+
const panel = screen.getByTestId('view-config-panel');
1497+
expect(panel).toBeInTheDocument();
1498+
1499+
// These toggles should be rendered in the page section (always visible, not behind a collapsible)
1500+
expect(screen.getByTestId('toggle-showSearch')).toBeInTheDocument();
1501+
expect(screen.getByTestId('toggle-showFilters')).toBeInTheDocument();
1502+
expect(screen.getByTestId('toggle-showSort')).toBeInTheDocument();
1503+
expect(screen.getByTestId('toggle-clickIntoRecordDetails')).toBeInTheDocument();
1504+
expect(screen.getByTestId('toggle-addRecordViaForm')).toBeInTheDocument();
1505+
expect(screen.getByTestId('toggle-allowExport')).toBeInTheDocument();
1506+
});
1507+
1508+
it('renders section description hints for Page and ListView config', () => {
1509+
render(
1510+
<ViewConfigPanel
1511+
open={true}
1512+
onClose={vi.fn()}
1513+
activeView={mockActiveView}
1514+
objectDef={mockObjectDef}
1515+
/>
1516+
);
1517+
1518+
expect(screen.getByText('console.objectView.pageConfigHint')).toBeInTheDocument();
1519+
expect(screen.getByText('console.objectView.listConfigHint')).toBeInTheDocument();
1520+
});
1521+
1522+
it('renders list-level inline action items in the User Actions section (editRecordsInline, addDeleteRecordsInline)', () => {
1523+
render(
1524+
<ViewConfigPanel
1525+
open={true}
1526+
onClose={vi.fn()}
1527+
activeView={mockActiveView}
1528+
objectDef={mockObjectDef}
1529+
/>
1530+
);
1531+
1532+
// List-level inline actions should be in the User Actions collapsible section
1533+
expect(screen.getByTestId('toggle-editRecordsInline')).toBeInTheDocument();
1534+
expect(screen.getByTestId('toggle-addDeleteRecordsInline')).toBeInTheDocument();
1535+
});
1536+
1537+
it('page-level toggles call onViewUpdate correctly for live preview', () => {
1538+
const onViewUpdate = vi.fn();
1539+
render(
1540+
<ViewConfigPanel
1541+
open={true}
1542+
onClose={vi.fn()}
1543+
activeView={mockActiveView}
1544+
objectDef={mockObjectDef}
1545+
onViewUpdate={onViewUpdate}
1546+
/>
1547+
);
1548+
1549+
// Toggle showSearch off
1550+
fireEvent.click(screen.getByTestId('toggle-showSearch'));
1551+
expect(onViewUpdate).toHaveBeenCalledWith('showSearch', false);
1552+
1553+
// Toggle showFilters off
1554+
fireEvent.click(screen.getByTestId('toggle-showFilters'));
1555+
expect(onViewUpdate).toHaveBeenCalledWith('showFilters', false);
1556+
1557+
// Toggle showSort off
1558+
fireEvent.click(screen.getByTestId('toggle-showSort'));
1559+
expect(onViewUpdate).toHaveBeenCalledWith('showSort', false);
1560+
1561+
// Toggle clickIntoRecordDetails off
1562+
fireEvent.click(screen.getByTestId('toggle-clickIntoRecordDetails'));
1563+
expect(onViewUpdate).toHaveBeenCalledWith('clickIntoRecordDetails', false);
1564+
1565+
// Toggle addRecordViaForm on
1566+
fireEvent.click(screen.getByTestId('toggle-addRecordViaForm'));
1567+
expect(onViewUpdate).toHaveBeenCalledWith('addRecordViaForm', true);
1568+
1569+
// Toggle allowExport off
1570+
fireEvent.click(screen.getByTestId('toggle-allowExport'));
1571+
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', false);
1572+
});
14821573
});

apps/console/src/components/ObjectView.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,16 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
303303
appearance: viewDef.showDescription != null
304304
? { showDescription: viewDef.showDescription }
305305
: listSchema.appearance,
306+
// Propagate toolbar/display flags for all view types
307+
showSearch: viewDef.showSearch ?? listSchema.showSearch,
308+
showSort: viewDef.showSort ?? listSchema.showSort,
309+
showFilters: viewDef.showFilters ?? listSchema.showFilters,
310+
striped: viewDef.striped ?? listSchema.striped,
311+
bordered: viewDef.bordered ?? listSchema.bordered,
312+
color: viewDef.color ?? listSchema.color,
313+
// Propagate filter/sort as default filters/sort for data flow
314+
...(viewDef.filter?.length ? { filters: viewDef.filter } : {}),
315+
...(viewDef.sort?.length ? { sort: viewDef.sort } : {}),
306316
options: {
307317
kanban: {
308318
groupBy: viewDef.kanban?.groupByField || viewDef.kanban?.groupField || 'status',

0 commit comments

Comments
 (0)