Skip to content

Commit f72fc27

Browse files
authored
Merge pull request #723 from objectstack-ai/copilot/fix-panel-data-flow-issue
2 parents d275424 + 2ba2ef6 commit f72fc27

File tree

19 files changed

+523
-6
lines changed

19 files changed

+523
-6
lines changed

ROADMAP.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,11 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
203203
| `showSearch` ||||||||
204204
| `showSort` ||||||||
205205
| `showFilters` ||||||||
206+
| `showHideFields` ||||||||
207+
| `showGroup` ||||||||
208+
| `showColor` ||||||||
209+
| `showDensity` ||||||||
210+
| `allowExport` ||||||||
206211
| `rowHeight` ||||||||
207212
| `densityMode` ||||||||
208213
| `striped` ||||||||
@@ -218,14 +223,26 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
218223
3. ~~**`NamedListView` type:** Missing toolbar/display properties~~ → Added as first-class properties
219224
4. ~~**Plugin `renderListView` schema missing toolbar flags:** `renderContent``renderListView` schema did not include `showSearch`/`showFilters`/`showSort`~~ → Now propagated (PR #771)
220225
5. ~~**ListView toolbar unconditionally rendered:** Search/Filter/Sort buttons always visible regardless of schema flags~~ → Now conditionally rendered based on `schema.showSearch`/`showFilters`/`showSort` (PR #771)
221-
6. **No per-view-type integration tests:** Pending — tests verify config reaches `fullSchema`, but per-renderer integration tests still needed
226+
6. ~~**Hide Fields/Group/Color/Density buttons always visible:** No schema property to control visibility~~ → Added `showHideFields`/`showGroup`/`showColor`/`showDensity` with conditional rendering (Issue #719)
227+
7. ~~**Export toggle broken:** ViewConfigPanel writes `allowExport: boolean` but ListView checks `exportOptions` object~~ → Export now checks both `exportOptions && allowExport !== false`; Console clears `exportOptions` when `allowExport === false` (Issue #719)
228+
8. ~~**`hasExport` logic bug:** `draft.allowExport !== false` was always true when undefined~~ → Fixed to `draft.allowExport === true || draft.exportOptions != null` (Issue #719)
229+
9. **No per-view-type integration tests:** Pending — tests verify config reaches `fullSchema`, but per-renderer integration tests still needed
222230

223231
**Phase 1 — Grid/Table View (baseline, already complete):**
224232
- [x] `gridSchema` includes `striped`/`bordered` from `activeView`
225233
- [x] `showSort`/`showSearch`/`showFilters` passed via `ObjectViewSchema`
226234
- [x] `useMemo` dependency arrays cover all grid config
227235
- [x] ListView toolbar buttons conditionally rendered based on `schema.showSearch`/`showFilters`/`showSort` (PR #771)
228236
- [x] `renderListView` schema includes toolbar toggle flags (`showSearch`/`showFilters`/`showSort`) and display props (`striped`/`bordered`/`color`) (PR #771)
237+
- [x] Hide Fields/Group/Color/Density toolbar buttons conditionally rendered via `showHideFields`/`showGroup`/`showColor`/`showDensity` (Issue #719)
238+
- [x] Export button checks both `exportOptions` and `allowExport` (Issue #719)
239+
- [x] `hasExport` logic fixed — no longer always true when `allowExport` is undefined (Issue #719)
240+
- [x] ViewConfigPanel includes toggles for `showHideFields`/`showGroup`/`showColor`/`showDensity` (Issue #719)
241+
- [x] `showHideFields`/`showGroup`/`showColor`/`showDensity`/`allowExport` propagated through Console `fullSchema` and PluginObjectView `renderListView` (Issue #719)
242+
- [x] Full end-to-end data flow: all ViewConfigPanel props (`inlineEdit`/`wrapHeaders`/`clickIntoRecordDetails`/`addRecordViaForm`/`addDeleteRecordsInline`/`collapseAllByDefault`/`fieldTextColor`/`prefixField`/`showDescription`) propagated through Console `fullSchema` → PluginObjectView `renderListView` → ListView (Issue #719)
243+
- [x] ListView forwards `striped`/`bordered`/`wrapHeaders` to child `viewComponentSchema` (grid gets `wrapHeaders`, all views get `striped`/`bordered`) (Issue #719)
244+
- [x] ViewConfigPanel includes `striped`/`bordered` toggles in Appearance section (Issue #719)
245+
- [x] Type definitions complete: `NamedListView` + `ListViewSchema` + Zod schema include all 22 view-config properties (Issue #719)
229246

230247
**Phase 2 — Kanban Live Preview:**
231248
- [x] Propagate `showSort`/`showSearch`/`showFilters` through `generateViewSchema` kanban branch
@@ -253,6 +270,8 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
253270

254271
**Phase 6 — Data Flow & Dependency Refactor:**
255272
- [x] Add `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` to `NamedListView` type in `@object-ui/types`
273+
- [x] Add `showHideFields`/`showGroup`/`showColor`/`showDensity`/`allowExport` to `NamedListView` and `ListViewSchema` types and Zod schema (Issue #719)
274+
- [x] Add `inlineEdit`/`wrapHeaders`/`clickIntoRecordDetails`/`addRecordViaForm`/`addDeleteRecordsInline`/`collapseAllByDefault`/`fieldTextColor`/`prefixField`/`showDescription` to `NamedListView` and `ListViewSchema` types and Zod schema (Issue #719)
256275
- [x] Update Console `renderListView` to pass all config properties in `fullSchema`
257276
- [ ] Audit all `useMemo`/`useEffect` dependency arrays in `plugin-view/ObjectView.tsx` for missing `activeView` sub-properties
258277
- [x] Remove hardcoded `showSearch: false` from `generateViewSchema` — use `activeView.showSearch ?? schema.showSearch` instead

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

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ describe('ViewConfigPanel', () => {
439439

440440
const exportSwitch = screen.getByTestId('toggle-allowExport');
441441
fireEvent.click(exportSwitch);
442-
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', false);
442+
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', true);
443443
});
444444

445445
it('toggles addRecordViaForm via Switch', () => {
@@ -1566,8 +1566,81 @@ describe('ViewConfigPanel', () => {
15661566
fireEvent.click(screen.getByTestId('toggle-addRecordViaForm'));
15671567
expect(onViewUpdate).toHaveBeenCalledWith('addRecordViaForm', true);
15681568

1569-
// Toggle allowExport off
1569+
// Toggle allowExport on (starts unchecked by default)
15701570
fireEvent.click(screen.getByTestId('toggle-allowExport'));
1571-
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', false);
1571+
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', true);
1572+
});
1573+
1574+
it('new toolbar toggles (showHideFields, showGroup, showColor, showDensity) call onViewUpdate correctly', () => {
1575+
const onViewUpdate = vi.fn();
1576+
render(
1577+
<ViewConfigPanel
1578+
open={true}
1579+
onClose={vi.fn()}
1580+
activeView={mockActiveView}
1581+
objectDef={mockObjectDef}
1582+
onViewUpdate={onViewUpdate}
1583+
/>
1584+
);
1585+
1586+
// Toggle showHideFields off
1587+
fireEvent.click(screen.getByTestId('toggle-showHideFields'));
1588+
expect(onViewUpdate).toHaveBeenCalledWith('showHideFields', false);
1589+
1590+
// Toggle showGroup off
1591+
fireEvent.click(screen.getByTestId('toggle-showGroup'));
1592+
expect(onViewUpdate).toHaveBeenCalledWith('showGroup', false);
1593+
1594+
// Toggle showColor off
1595+
fireEvent.click(screen.getByTestId('toggle-showColor'));
1596+
expect(onViewUpdate).toHaveBeenCalledWith('showColor', false);
1597+
1598+
// Toggle showDensity off
1599+
fireEvent.click(screen.getByTestId('toggle-showDensity'));
1600+
expect(onViewUpdate).toHaveBeenCalledWith('showDensity', false);
1601+
});
1602+
1603+
it('hasExport should be false when allowExport is undefined and no exportOptions', () => {
1604+
const onViewUpdate = vi.fn();
1605+
render(
1606+
<ViewConfigPanel
1607+
open={true}
1608+
onClose={vi.fn()}
1609+
activeView={{ ...mockActiveView, allowExport: undefined, exportOptions: undefined }}
1610+
objectDef={mockObjectDef}
1611+
onViewUpdate={onViewUpdate}
1612+
/>
1613+
);
1614+
1615+
// allowExport toggle should be unchecked (false) when allowExport is undefined
1616+
const exportSwitch = screen.getByTestId('toggle-allowExport');
1617+
expect(exportSwitch).toHaveAttribute('aria-checked', 'false');
1618+
});
1619+
1620+
it('striped and bordered toggles call onViewUpdate correctly', () => {
1621+
const onViewUpdate = vi.fn();
1622+
render(
1623+
<ViewConfigPanel
1624+
open={true}
1625+
onClose={vi.fn()}
1626+
activeView={mockActiveView}
1627+
objectDef={mockObjectDef}
1628+
onViewUpdate={onViewUpdate}
1629+
/>
1630+
);
1631+
1632+
// Expand appearance section if collapsed
1633+
const appearanceSection = screen.getByTestId('section-appearance');
1634+
if (appearanceSection.getAttribute('aria-expanded') === 'false') {
1635+
fireEvent.click(appearanceSection);
1636+
}
1637+
1638+
// Toggle striped on
1639+
fireEvent.click(screen.getByTestId('toggle-striped'));
1640+
expect(onViewUpdate).toHaveBeenCalledWith('striped', true);
1641+
1642+
// Toggle bordered on
1643+
fireEvent.click(screen.getByTestId('toggle-bordered'));
1644+
expect(onViewUpdate).toHaveBeenCalledWith('bordered', true);
15721645
});
15731646
});

apps/console/src/components/ObjectView.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,24 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
307307
showSearch: viewDef.showSearch ?? listSchema.showSearch,
308308
showSort: viewDef.showSort ?? listSchema.showSort,
309309
showFilters: viewDef.showFilters ?? listSchema.showFilters,
310+
showHideFields: viewDef.showHideFields ?? listSchema.showHideFields,
311+
showGroup: viewDef.showGroup ?? listSchema.showGroup,
312+
showColor: viewDef.showColor ?? listSchema.showColor,
313+
showDensity: viewDef.showDensity ?? listSchema.showDensity,
314+
allowExport: viewDef.allowExport ?? listSchema.allowExport,
315+
exportOptions: viewDef.allowExport === false ? undefined : (viewDef.exportOptions ?? listSchema.exportOptions),
310316
striped: viewDef.striped ?? listSchema.striped,
311317
bordered: viewDef.bordered ?? listSchema.bordered,
312318
color: viewDef.color ?? listSchema.color,
319+
// Propagate view-config properties (Bug 4 / items 14-22)
320+
wrapHeaders: viewDef.wrapHeaders ?? listSchema.wrapHeaders,
321+
clickIntoRecordDetails: viewDef.clickIntoRecordDetails ?? listSchema.clickIntoRecordDetails,
322+
addRecordViaForm: viewDef.addRecordViaForm ?? listSchema.addRecordViaForm,
323+
addDeleteRecordsInline: viewDef.addDeleteRecordsInline ?? listSchema.addDeleteRecordsInline,
324+
collapseAllByDefault: viewDef.collapseAllByDefault ?? listSchema.collapseAllByDefault,
325+
fieldTextColor: viewDef.fieldTextColor ?? listSchema.fieldTextColor,
326+
prefixField: viewDef.prefixField ?? listSchema.prefixField,
327+
showDescription: viewDef.showDescription ?? listSchema.showDescription,
313328
// Propagate filter/sort as default filters/sort for data flow
314329
...(viewDef.filter?.length ? { filters: viewDef.filter } : {}),
315330
...(viewDef.sort?.length ? { sort: viewDef.sort } : {}),

apps/console/src/components/ViewConfigPanel.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,11 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
320320
const hasSearch = draft.showSearch !== false;
321321
const hasFilter = draft.showFilters !== false;
322322
const hasSort = draft.showSort !== false;
323-
const hasExport = draft.exportOptions !== undefined || draft.allowExport !== false;
323+
const hasHideFields = draft.showHideFields !== false;
324+
const hasGroup = draft.showGroup !== false;
325+
const hasColor = draft.showColor !== false;
326+
const hasDensity = draft.showDensity !== false;
327+
const hasExport = draft.exportOptions != null || draft.allowExport === true;
324328
const hasAddForm = draft.addRecordViaForm === true;
325329
const hasShowDescription = draft.showDescription !== false;
326330

@@ -507,6 +511,38 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
507511
className="scale-75"
508512
/>
509513
</ConfigRow>
514+
<ConfigRow label={t('console.objectView.enableHideFields')}>
515+
<Switch
516+
data-testid="toggle-showHideFields"
517+
checked={hasHideFields}
518+
onCheckedChange={(checked: boolean) => updateDraft('showHideFields', checked)}
519+
className="scale-75"
520+
/>
521+
</ConfigRow>
522+
<ConfigRow label={t('console.objectView.enableGroup')}>
523+
<Switch
524+
data-testid="toggle-showGroup"
525+
checked={hasGroup}
526+
onCheckedChange={(checked: boolean) => updateDraft('showGroup', checked)}
527+
className="scale-75"
528+
/>
529+
</ConfigRow>
530+
<ConfigRow label={t('console.objectView.enableColor')}>
531+
<Switch
532+
data-testid="toggle-showColor"
533+
checked={hasColor}
534+
onCheckedChange={(checked: boolean) => updateDraft('showColor', checked)}
535+
className="scale-75"
536+
/>
537+
</ConfigRow>
538+
<ConfigRow label={t('console.objectView.enableDensity')}>
539+
<Switch
540+
data-testid="toggle-showDensity"
541+
checked={hasDensity}
542+
onCheckedChange={(checked: boolean) => updateDraft('showDensity', checked)}
543+
className="scale-75"
544+
/>
545+
</ConfigRow>
510546
<ConfigRow label={t('console.objectView.clickIntoRecordDetails')}>
511547
<Switch
512548
data-testid="toggle-clickIntoRecordDetails"
@@ -921,6 +957,22 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
921957
className="scale-75"
922958
/>
923959
</ConfigRow>
960+
<ConfigRow label={t('console.objectView.striped')}>
961+
<Switch
962+
data-testid="toggle-striped"
963+
checked={draft.striped === true}
964+
onCheckedChange={(checked: boolean) => updateDraft('striped', checked)}
965+
className="scale-75"
966+
/>
967+
</ConfigRow>
968+
<ConfigRow label={t('console.objectView.bordered')}>
969+
<Switch
970+
data-testid="toggle-bordered"
971+
checked={draft.bordered === true}
972+
onCheckedChange={(checked: boolean) => updateDraft('bordered', checked)}
973+
className="scale-75"
974+
/>
975+
</ConfigRow>
924976
</div>
925977
)}
926978

packages/i18n/src/locales/ar.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ const ar = {
202202
enableSearch: 'تفعيل البحث',
203203
enableFilter: 'تفعيل التصفية',
204204
enableSort: 'تفعيل الترتيب',
205+
enableHideFields: 'تفعيل إخفاء الحقول',
206+
enableGroup: 'تفعيل التجميع',
207+
enableColor: 'تفعيل اللون',
208+
enableDensity: 'تفعيل الكثافة',
205209
userActions: 'إجراءات المستخدم',
206210
addRecordViaForm: 'إضافة سجلات عبر النموذج',
207211
advanced: 'متقدم',
@@ -238,6 +242,8 @@ const ar = {
238242
wrapHeaders: 'التفاف العناوين',
239243
showFieldDescriptions: 'إظهار أوصاف الحقول',
240244
collapseAllByDefault: 'طي الكل افتراضياً',
245+
striped: 'صفوف مخططة',
246+
bordered: 'خلايا محاطة بحدود',
241247
editRecordsInline: 'تحرير السجلات مباشرة',
242248
addDeleteRecordsInline: 'إضافة/حذف السجلات مباشرة',
243249
clickIntoRecordDetails: 'انقر لعرض تفاصيل السجل',

packages/i18n/src/locales/de.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ const de = {
206206
enableSearch: 'Suche aktivieren',
207207
enableFilter: 'Filter aktivieren',
208208
enableSort: 'Sortierung aktivieren',
209+
enableHideFields: 'Felder ausblenden aktivieren',
210+
enableGroup: 'Gruppierung aktivieren',
211+
enableColor: 'Farbe aktivieren',
212+
enableDensity: 'Dichte aktivieren',
209213
userActions: 'Benutzeraktionen',
210214
addRecordViaForm: 'Datensätze über Formular hinzufügen',
211215
advanced: 'Erweitert',
@@ -242,6 +246,8 @@ const de = {
242246
wrapHeaders: 'Kopfzeilen umbrechen',
243247
showFieldDescriptions: 'Feldbeschreibungen anzeigen',
244248
collapseAllByDefault: 'Standardmäßig alle einklappen',
249+
striped: 'Gestreifte Zeilen',
250+
bordered: 'Umrandete Zellen',
245251
editRecordsInline: 'Datensätze inline bearbeiten',
246252
addDeleteRecordsInline: 'Datensätze inline hinzufügen/löschen',
247253
clickIntoRecordDetails: 'Klicken für Datensatzdetails',

packages/i18n/src/locales/en.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ const en = {
206206
enableSearch: 'Enable search',
207207
enableFilter: 'Enable filter',
208208
enableSort: 'Enable sort',
209+
enableHideFields: 'Enable hide fields',
210+
enableGroup: 'Enable group',
211+
enableColor: 'Enable color',
212+
enableDensity: 'Enable density',
209213
userActions: 'User actions',
210214
addRecordViaForm: 'Add records through a form',
211215
advanced: 'Advanced',
@@ -242,6 +246,8 @@ const en = {
242246
wrapHeaders: 'Wrap headers',
243247
showFieldDescriptions: 'Show field descriptions',
244248
collapseAllByDefault: 'Collapse all by default',
249+
striped: 'Striped rows',
250+
bordered: 'Bordered cells',
245251
editRecordsInline: 'Edit records inline',
246252
addDeleteRecordsInline: 'Add/delete records inline',
247253
clickIntoRecordDetails: 'Click into record details',

packages/i18n/src/locales/es.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ const es = {
201201
enableSearch: 'Habilitar búsqueda',
202202
enableFilter: 'Habilitar filtro',
203203
enableSort: 'Habilitar ordenamiento',
204+
enableHideFields: 'Habilitar ocultar campos',
205+
enableGroup: 'Habilitar agrupación',
206+
enableColor: 'Habilitar color',
207+
enableDensity: 'Habilitar densidad',
204208
userActions: 'Acciones de usuario',
205209
addRecordViaForm: 'Agregar registros mediante formulario',
206210
advanced: 'Avanzado',
@@ -237,6 +241,8 @@ const es = {
237241
wrapHeaders: 'Ajustar encabezados',
238242
showFieldDescriptions: 'Mostrar descripciones de campos',
239243
collapseAllByDefault: 'Contraer todo por defecto',
244+
striped: 'Filas alternadas',
245+
bordered: 'Celdas con borde',
240246
editRecordsInline: 'Editar registros en línea',
241247
addDeleteRecordsInline: 'Agregar/eliminar registros en línea',
242248
clickIntoRecordDetails: 'Clic para detalles del registro',

packages/i18n/src/locales/fr.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ const fr = {
206206
enableSearch: 'Activer la recherche',
207207
enableFilter: 'Activer le filtre',
208208
enableSort: 'Activer le tri',
209+
enableHideFields: 'Activer masquer les champs',
210+
enableGroup: 'Activer le regroupement',
211+
enableColor: 'Activer la couleur',
212+
enableDensity: 'Activer la densité',
209213
userActions: 'Actions utilisateur',
210214
addRecordViaForm: 'Ajouter des enregistrements via un formulaire',
211215
advanced: 'Avancé',
@@ -242,6 +246,8 @@ const fr = {
242246
wrapHeaders: 'Retour à la ligne des en-têtes',
243247
showFieldDescriptions: 'Afficher les descriptions des champs',
244248
collapseAllByDefault: 'Tout réduire par défaut',
249+
striped: 'Lignes rayées',
250+
bordered: 'Cellules bordées',
245251
editRecordsInline: 'Modifier les enregistrements en ligne',
246252
addDeleteRecordsInline: 'Ajouter/supprimer des enregistrements en ligne',
247253
clickIntoRecordDetails: "Cliquer pour les détails de l'enregistrement",

0 commit comments

Comments
 (0)