Skip to content

Commit 95546de

Browse files
authored
Merge pull request #866 from objectstack-ai/copilot/evaluate-view-config-panel
2 parents 3825f84 + bd418a6 commit 95546de

File tree

18 files changed

+670
-201
lines changed

18 files changed

+670
-201
lines changed

ROADMAP.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,29 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
511511
- [x] 136 ViewConfigPanel interaction tests pass (removed tests for deleted fields)
512512
- [x] 10 config-sync integration tests pass
513513

514+
**Phase 7 — Section Restructure & Field Selector Upgrade (Airtable UX Parity):**
515+
- [x] Split monolithic `pageConfig` section into 5 clear sub-sections: General, Toolbar, Navigation, Records, Export & Print
516+
- [x] General section: label, description, viewType (always expanded, non-collapsible)
517+
- [x] Toolbar section: showSearch, showSort, showFilters, showHideFields, showGroup, showColor, showDensity (collapsible)
518+
- [x] Navigation section: navigation mode, width, openNewTab (collapsible)
519+
- [x] Records section: selection mode, addRecord sub-editor (collapsible)
520+
- [x] Export & Print section: allowExport + sub-config, showRecordCount, allowPrinting (collapsible, defaultCollapsed)
521+
- [x] Field selector upgrade: eye/eye-off toggle for visibility (replacing checkboxes), search input for field filtering, Show All/Hide All batch operations, grip handles for visual reorder hints
522+
- [x] i18n keys added for all 11 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko, + ar)
523+
- [x] 110 schema tests pass (+7 new section tests)
524+
- [x] 136 ViewConfigPanel interaction tests pass (updated for eye toggles, section expansion)
525+
- [x] 31 ObjectView integration tests pass (updated for section expansion)
526+
- [x] 10 config-sync integration tests pass
527+
528+
**Phase 8 — Tab Gear Icon, Panel Animation & UX Polish:**
529+
- [x] Add `onConfigView` prop to ViewTabBar with Settings2 gear icon on active tab
530+
- [x] Wire gear icon in ObjectView: click opens ViewConfigPanel for that view's settings
531+
- [x] Panel slide-in/slide-out animation: CSS transition on max-width + opacity (300ms ease-in-out)
532+
- [x] Auto-sync panel content on view tab switch (ViewConfigPanel resets draft when activeView.id changes)
533+
- [x] 5 new ViewTabBar gear icon tests (show on active, hide on inactive, callback, event isolation)
534+
- [x] 3 new ViewConfigPanel tests (search input, Show All, Hide All)
535+
- [x] 49 ViewTabBar tests pass, 139 ViewConfigPanel tests pass, 31 ObjectView tests pass
536+
514537
**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
515538

516539
### P1.9 Console — Content Area Layout & Responsiveness

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,9 @@ describe('ObjectView Component', () => {
605605
fireEvent.click(screen.getByTitle('console.objectView.designTools'));
606606
fireEvent.click(screen.getByText('console.objectView.editView'));
607607

608+
// Expand the Export & Print section (defaultCollapsed)
609+
fireEvent.click(screen.getByTestId('section-header-exportPrint'));
610+
608611
// Toggle showRecordCount on
609612
const recordCountSwitch = screen.getByTestId('toggle-showRecordCount');
610613
fireEvent.click(recordCountSwitch);
@@ -628,6 +631,9 @@ describe('ObjectView Component', () => {
628631
fireEvent.click(screen.getByTitle('console.objectView.designTools'));
629632
fireEvent.click(screen.getByText('console.objectView.editView'));
630633

634+
// Expand the Export & Print section (defaultCollapsed)
635+
fireEvent.click(screen.getByTestId('section-header-exportPrint'));
636+
631637
// Toggle allowPrinting on
632638
const printSwitch = screen.getByTestId('toggle-allowPrinting');
633639
fireEvent.click(printSwitch);

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

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -336,13 +336,9 @@ describe('ViewConfigPanel', () => {
336336

337337
// 3 fields → 3 checkboxes
338338
expect(screen.getByTestId('column-selector')).toBeInTheDocument();
339-
expect(screen.getByTestId('col-checkbox-name')).toBeInTheDocument();
340-
expect(screen.getByTestId('col-checkbox-stage')).toBeInTheDocument();
341-
expect(screen.getByTestId('col-checkbox-amount')).toBeInTheDocument();
342-
// Columns in activeView should be checked
343-
expect(screen.getByTestId('col-checkbox-name')).toBeChecked();
344-
expect(screen.getByTestId('col-checkbox-stage')).toBeChecked();
345-
expect(screen.getByTestId('col-checkbox-amount')).toBeChecked();
339+
expect(screen.getByTestId('col-eye-name')).toBeInTheDocument();
340+
expect(screen.getByTestId('col-eye-stage')).toBeInTheDocument();
341+
expect(screen.getByTestId('col-eye-amount')).toBeInTheDocument();
346342
});
347343

348344
it('displays object source name', () => {
@@ -541,6 +537,7 @@ describe('ViewConfigPanel', () => {
541537
/>
542538
);
543539

540+
fireEvent.click(screen.getByTestId('section-exportPrint'));
544541
const exportSwitch = screen.getByTestId('toggle-allowExport');
545542
fireEvent.click(exportSwitch);
546543
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', true);
@@ -692,7 +689,7 @@ describe('ViewConfigPanel', () => {
692689
fireEvent.click(screen.getByText('console.objectView.fields'));
693690

694691
// Uncheck the 'stage' column
695-
fireEvent.click(screen.getByTestId('col-checkbox-stage'));
692+
fireEvent.click(screen.getByTestId('col-eye-stage'));
696693
expect(onViewUpdate).toHaveBeenCalledWith('columns', ['name', 'amount']);
697694
});
698695

@@ -783,8 +780,9 @@ describe('ViewConfigPanel', () => {
783780
expect(screen.getByTestId('toggle-showSearch')).toHaveAttribute('aria-checked', 'false');
784781
expect(screen.getByTestId('toggle-showFilters')).toHaveAttribute('aria-checked', 'true');
785782
expect(screen.getByTestId('toggle-showSort')).toHaveAttribute('aria-checked', 'false');
786-
expect(screen.getByTestId('toggle-allowExport')).toHaveAttribute('aria-checked', 'false');
787783
expect(screen.getByTestId('toggle-addRecord-enabled')).toHaveAttribute('aria-checked', 'true');
784+
fireEvent.click(screen.getByTestId('section-exportPrint'));
785+
expect(screen.getByTestId('toggle-allowExport')).toHaveAttribute('aria-checked', 'false');
788786
});
789787

790788
// ── Real-time draft propagation tests (issue fix) ──
@@ -1534,8 +1532,7 @@ describe('ViewConfigPanel', () => {
15341532
fireEvent.click(screen.getByText('console.objectView.fields'));
15351533

15361534
// 'amount' should have a checkbox but no move buttons
1537-
expect(screen.getByTestId('col-checkbox-amount')).toBeInTheDocument();
1538-
expect(screen.getByTestId('col-checkbox-amount')).not.toBeChecked();
1535+
expect(screen.getByTestId('col-eye-amount')).toBeInTheDocument();
15391536
expect(screen.queryByTestId('col-move-up-amount')).not.toBeInTheDocument();
15401537
expect(screen.queryByTestId('col-move-down-amount')).not.toBeInTheDocument();
15411538
});
@@ -1555,7 +1552,7 @@ describe('ViewConfigPanel', () => {
15551552
fireEvent.click(screen.getByText('console.objectView.fields'));
15561553

15571554
// Click checkbox for 'amount' to add it
1558-
fireEvent.click(screen.getByTestId('col-checkbox-amount'));
1555+
fireEvent.click(screen.getByTestId('col-eye-amount'));
15591556
expect(onViewUpdate).toHaveBeenCalledWith('columns', ['name', 'stage', 'amount']);
15601557
});
15611558

@@ -1584,6 +1581,59 @@ describe('ViewConfigPanel', () => {
15841581
expect(onViewUpdate).toHaveBeenCalledTimes(2);
15851582
});
15861583

1584+
it('renders column search input and show/hide all buttons', () => {
1585+
render(
1586+
<ViewConfigPanel
1587+
open={true}
1588+
onClose={vi.fn()}
1589+
activeView={mockActiveView}
1590+
objectDef={mockObjectDef}
1591+
/>
1592+
);
1593+
1594+
fireEvent.click(screen.getByText('console.objectView.fields'));
1595+
1596+
expect(screen.getByTestId('column-search-input')).toBeInTheDocument();
1597+
expect(screen.getByTestId('column-show-all')).toBeInTheDocument();
1598+
expect(screen.getByTestId('column-hide-all')).toBeInTheDocument();
1599+
});
1600+
1601+
it('clicking Show All selects all fields and calls onViewUpdate', () => {
1602+
const onViewUpdate = vi.fn();
1603+
render(
1604+
<ViewConfigPanel
1605+
open={true}
1606+
onClose={vi.fn()}
1607+
activeView={{ ...mockActiveView, columns: ['name'] }}
1608+
objectDef={mockObjectDef}
1609+
onViewUpdate={onViewUpdate}
1610+
/>
1611+
);
1612+
1613+
fireEvent.click(screen.getByText('console.objectView.fields'));
1614+
fireEvent.click(screen.getByTestId('column-show-all'));
1615+
1616+
expect(onViewUpdate).toHaveBeenCalledWith('columns', ['name', 'stage', 'amount']);
1617+
});
1618+
1619+
it('clicking Hide All removes all fields and calls onViewUpdate', () => {
1620+
const onViewUpdate = vi.fn();
1621+
render(
1622+
<ViewConfigPanel
1623+
open={true}
1624+
onClose={vi.fn()}
1625+
activeView={mockActiveView}
1626+
objectDef={mockObjectDef}
1627+
onViewUpdate={onViewUpdate}
1628+
/>
1629+
);
1630+
1631+
fireEvent.click(screen.getByText('console.objectView.fields'));
1632+
fireEvent.click(screen.getByTestId('column-hide-all'));
1633+
1634+
expect(onViewUpdate).toHaveBeenCalledWith('columns', []);
1635+
});
1636+
15871637
// ── Section Layout Tests: Page vs ListView Config ──
15881638

15891639
it('renders page-level config items in the Page section (showSearch, showFilters, showSort, navigation, addRecord, allowExport)', () => {
@@ -1606,6 +1656,7 @@ describe('ViewConfigPanel', () => {
16061656
expect(screen.getByTestId('toggle-showSort')).toBeInTheDocument();
16071657
expect(screen.getByTestId('select-navigation-mode')).toBeInTheDocument();
16081658
expect(screen.getByTestId('toggle-addRecord-enabled')).toBeInTheDocument();
1659+
fireEvent.click(screen.getByTestId('section-exportPrint'));
16091660
expect(screen.getByTestId('toggle-allowExport')).toBeInTheDocument();
16101661
});
16111662

@@ -1619,7 +1670,7 @@ describe('ViewConfigPanel', () => {
16191670
/>
16201671
);
16211672

1622-
expect(screen.getByText('console.objectView.pageConfigHint')).toBeInTheDocument();
1673+
expect(screen.getByText('console.objectView.generalHint')).toBeInTheDocument();
16231674
expect(screen.getByText('console.objectView.listConfigHint')).toBeInTheDocument();
16241675
});
16251676

@@ -1673,6 +1724,7 @@ describe('ViewConfigPanel', () => {
16731724
expect(onViewUpdate).toHaveBeenCalledWith('addRecordViaForm', true);
16741725

16751726
// Toggle allowExport on (starts unchecked by default)
1727+
fireEvent.click(screen.getByTestId('section-exportPrint'));
16761728
fireEvent.click(screen.getByTestId('toggle-allowExport'));
16771729
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', true);
16781730
});
@@ -1719,6 +1771,7 @@ describe('ViewConfigPanel', () => {
17191771
);
17201772

17211773
// allowExport toggle should be unchecked (false) when allowExport is undefined
1774+
fireEvent.click(screen.getByTestId('section-exportPrint'));
17221775
const exportSwitch = screen.getByTestId('toggle-allowExport');
17231776
expect(exportSwitch).toHaveAttribute('aria-checked', 'false');
17241777
});
@@ -1880,6 +1933,7 @@ describe('ViewConfigPanel', () => {
18801933
/>
18811934
);
18821935

1936+
fireEvent.click(screen.getByTestId('section-exportPrint'));
18831937
expect(screen.getByTestId('export-formats')).toBeInTheDocument();
18841938
expect(screen.getByTestId('input-export-maxRecords')).toBeInTheDocument();
18851939
expect(screen.getByTestId('toggle-export-includeHeaders')).toBeInTheDocument();
@@ -1896,6 +1950,7 @@ describe('ViewConfigPanel', () => {
18961950
/>
18971951
);
18981952

1953+
fireEvent.click(screen.getByTestId('section-exportPrint'));
18991954
expect(screen.getByTestId('toggle-showRecordCount')).toBeInTheDocument();
19001955
});
19011956

@@ -1909,6 +1964,7 @@ describe('ViewConfigPanel', () => {
19091964
/>
19101965
);
19111966

1967+
fireEvent.click(screen.getByTestId('section-exportPrint'));
19121968
expect(screen.getByTestId('toggle-allowPrinting')).toBeInTheDocument();
19131969
});
19141970

@@ -2383,6 +2439,7 @@ describe('ViewConfigPanel', () => {
23832439
/>
23842440
);
23852441

2442+
fireEvent.click(screen.getByTestId('section-exportPrint'));
23862443
fireEvent.click(screen.getByTestId('toggle-showRecordCount'));
23872444
expect(onViewUpdate).toHaveBeenCalledWith('showRecordCount', true);
23882445
});
@@ -2399,6 +2456,7 @@ describe('ViewConfigPanel', () => {
23992456
/>
24002457
);
24012458

2459+
fireEvent.click(screen.getByTestId('section-exportPrint'));
24022460
fireEvent.click(screen.getByTestId('toggle-allowPrinting'));
24032461
expect(onViewUpdate).toHaveBeenCalledWith('allowPrinting', true);
24042462
});

0 commit comments

Comments
 (0)