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
21 changes: 20 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
| `showSearch` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `showSort` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `showFilters` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `showHideFields` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `showGroup` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `showColor` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `showDensity` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `allowExport` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `rowHeight` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `densityMode` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `striped` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Expand All @@ -218,14 +223,26 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
3. ~~**`NamedListView` type:** Missing toolbar/display properties~~ → Added as first-class properties
4. ~~**Plugin `renderListView` schema missing toolbar flags:** `renderContent` → `renderListView` schema did not include `showSearch`/`showFilters`/`showSort`~~ → Now propagated (PR #771)
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)
6. **No per-view-type integration tests:** Pending — tests verify config reaches `fullSchema`, but per-renderer integration tests still needed
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)
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)
8. ~~**`hasExport` logic bug:** `draft.allowExport !== false` was always true when undefined~~ → Fixed to `draft.allowExport === true || draft.exportOptions != null` (Issue #719)
9. **No per-view-type integration tests:** Pending — tests verify config reaches `fullSchema`, but per-renderer integration tests still needed

**Phase 1 — Grid/Table View (baseline, already complete):**
- [x] `gridSchema` includes `striped`/`bordered` from `activeView`
- [x] `showSort`/`showSearch`/`showFilters` passed via `ObjectViewSchema`
- [x] `useMemo` dependency arrays cover all grid config
- [x] ListView toolbar buttons conditionally rendered based on `schema.showSearch`/`showFilters`/`showSort` (PR #771)
- [x] `renderListView` schema includes toolbar toggle flags (`showSearch`/`showFilters`/`showSort`) and display props (`striped`/`bordered`/`color`) (PR #771)
- [x] Hide Fields/Group/Color/Density toolbar buttons conditionally rendered via `showHideFields`/`showGroup`/`showColor`/`showDensity` (Issue #719)
- [x] Export button checks both `exportOptions` and `allowExport` (Issue #719)
- [x] `hasExport` logic fixed — no longer always true when `allowExport` is undefined (Issue #719)
- [x] ViewConfigPanel includes toggles for `showHideFields`/`showGroup`/`showColor`/`showDensity` (Issue #719)
- [x] `showHideFields`/`showGroup`/`showColor`/`showDensity`/`allowExport` propagated through Console `fullSchema` and PluginObjectView `renderListView` (Issue #719)
- [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)
- [x] ListView forwards `striped`/`bordered`/`wrapHeaders` to child `viewComponentSchema` (grid gets `wrapHeaders`, all views get `striped`/`bordered`) (Issue #719)
- [x] ViewConfigPanel includes `striped`/`bordered` toggles in Appearance section (Issue #719)
- [x] Type definitions complete: `NamedListView` + `ListViewSchema` + Zod schema include all 22 view-config properties (Issue #719)

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

**Phase 6 — Data Flow & Dependency Refactor:**
- [x] Add `showSearch`/`showSort`/`showFilters`/`striped`/`bordered`/`color` to `NamedListView` type in `@object-ui/types`
- [x] Add `showHideFields`/`showGroup`/`showColor`/`showDensity`/`allowExport` to `NamedListView` and `ListViewSchema` types and Zod schema (Issue #719)
- [x] Add `inlineEdit`/`wrapHeaders`/`clickIntoRecordDetails`/`addRecordViaForm`/`addDeleteRecordsInline`/`collapseAllByDefault`/`fieldTextColor`/`prefixField`/`showDescription` to `NamedListView` and `ListViewSchema` types and Zod schema (Issue #719)
- [x] Update Console `renderListView` to pass all config properties in `fullSchema`
- [ ] Audit all `useMemo`/`useEffect` dependency arrays in `plugin-view/ObjectView.tsx` for missing `activeView` sub-properties
- [x] Remove hardcoded `showSearch: false` from `generateViewSchema` — use `activeView.showSearch ?? schema.showSearch` instead
Expand Down
79 changes: 76 additions & 3 deletions apps/console/src/__tests__/ViewConfigPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ describe('ViewConfigPanel', () => {

const exportSwitch = screen.getByTestId('toggle-allowExport');
fireEvent.click(exportSwitch);
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', false);
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', true);
});

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

// Toggle allowExport off
// Toggle allowExport on (starts unchecked by default)
fireEvent.click(screen.getByTestId('toggle-allowExport'));
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', false);
expect(onViewUpdate).toHaveBeenCalledWith('allowExport', true);
});

it('new toolbar toggles (showHideFields, showGroup, showColor, showDensity) call onViewUpdate correctly', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={mockActiveView}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
);

// Toggle showHideFields off
fireEvent.click(screen.getByTestId('toggle-showHideFields'));
expect(onViewUpdate).toHaveBeenCalledWith('showHideFields', false);

// Toggle showGroup off
fireEvent.click(screen.getByTestId('toggle-showGroup'));
expect(onViewUpdate).toHaveBeenCalledWith('showGroup', false);

// Toggle showColor off
fireEvent.click(screen.getByTestId('toggle-showColor'));
expect(onViewUpdate).toHaveBeenCalledWith('showColor', false);

// Toggle showDensity off
fireEvent.click(screen.getByTestId('toggle-showDensity'));
expect(onViewUpdate).toHaveBeenCalledWith('showDensity', false);
});

it('hasExport should be false when allowExport is undefined and no exportOptions', () => {
const onViewUpdate = vi.fn();
render(
<ViewConfigPanel
open={true}
onClose={vi.fn()}
activeView={{ ...mockActiveView, allowExport: undefined, exportOptions: undefined }}
objectDef={mockObjectDef}
onViewUpdate={onViewUpdate}
/>
);

// allowExport toggle should be unchecked (false) when allowExport is undefined
const exportSwitch = screen.getByTestId('toggle-allowExport');
expect(exportSwitch).toHaveAttribute('aria-checked', 'false');
});

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

// Expand appearance section if collapsed
const appearanceSection = screen.getByTestId('section-appearance');
if (appearanceSection.getAttribute('aria-expanded') === 'false') {
fireEvent.click(appearanceSection);
}

// Toggle striped on
fireEvent.click(screen.getByTestId('toggle-striped'));
expect(onViewUpdate).toHaveBeenCalledWith('striped', true);

// Toggle bordered on
fireEvent.click(screen.getByTestId('toggle-bordered'));
expect(onViewUpdate).toHaveBeenCalledWith('bordered', true);
});
});
15 changes: 15 additions & 0 deletions apps/console/src/components/ObjectView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,24 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
showSearch: viewDef.showSearch ?? listSchema.showSearch,
showSort: viewDef.showSort ?? listSchema.showSort,
showFilters: viewDef.showFilters ?? listSchema.showFilters,
showHideFields: viewDef.showHideFields ?? listSchema.showHideFields,
showGroup: viewDef.showGroup ?? listSchema.showGroup,
showColor: viewDef.showColor ?? listSchema.showColor,
showDensity: viewDef.showDensity ?? listSchema.showDensity,
allowExport: viewDef.allowExport ?? listSchema.allowExport,
exportOptions: viewDef.allowExport === false ? undefined : (viewDef.exportOptions ?? listSchema.exportOptions),
striped: viewDef.striped ?? listSchema.striped,
bordered: viewDef.bordered ?? listSchema.bordered,
color: viewDef.color ?? listSchema.color,
// Propagate view-config properties (Bug 4 / items 14-22)
wrapHeaders: viewDef.wrapHeaders ?? listSchema.wrapHeaders,
clickIntoRecordDetails: viewDef.clickIntoRecordDetails ?? listSchema.clickIntoRecordDetails,
addRecordViaForm: viewDef.addRecordViaForm ?? listSchema.addRecordViaForm,
addDeleteRecordsInline: viewDef.addDeleteRecordsInline ?? listSchema.addDeleteRecordsInline,
collapseAllByDefault: viewDef.collapseAllByDefault ?? listSchema.collapseAllByDefault,
fieldTextColor: viewDef.fieldTextColor ?? listSchema.fieldTextColor,
prefixField: viewDef.prefixField ?? listSchema.prefixField,
showDescription: viewDef.showDescription ?? listSchema.showDescription,
// Propagate filter/sort as default filters/sort for data flow
...(viewDef.filter?.length ? { filters: viewDef.filter } : {}),
...(viewDef.sort?.length ? { sort: viewDef.sort } : {}),
Expand Down
54 changes: 53 additions & 1 deletion apps/console/src/components/ViewConfigPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,11 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
const hasSearch = draft.showSearch !== false;
const hasFilter = draft.showFilters !== false;
const hasSort = draft.showSort !== false;
const hasExport = draft.exportOptions !== undefined || draft.allowExport !== false;
const hasHideFields = draft.showHideFields !== false;
const hasGroup = draft.showGroup !== false;
const hasColor = draft.showColor !== false;
const hasDensity = draft.showDensity !== false;
const hasExport = draft.exportOptions != null || draft.allowExport === true;
const hasAddForm = draft.addRecordViaForm === true;
const hasShowDescription = draft.showDescription !== false;

Expand Down Expand Up @@ -507,6 +511,38 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.enableHideFields')}>
<Switch
data-testid="toggle-showHideFields"
checked={hasHideFields}
onCheckedChange={(checked: boolean) => updateDraft('showHideFields', checked)}
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.enableGroup')}>
<Switch
data-testid="toggle-showGroup"
checked={hasGroup}
onCheckedChange={(checked: boolean) => updateDraft('showGroup', checked)}
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.enableColor')}>
<Switch
data-testid="toggle-showColor"
checked={hasColor}
onCheckedChange={(checked: boolean) => updateDraft('showColor', checked)}
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.enableDensity')}>
<Switch
data-testid="toggle-showDensity"
checked={hasDensity}
onCheckedChange={(checked: boolean) => updateDraft('showDensity', checked)}
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.clickIntoRecordDetails')}>
<Switch
data-testid="toggle-clickIntoRecordDetails"
Expand Down Expand Up @@ -921,6 +957,22 @@ export function ViewConfigPanel({ open, onClose, mode = 'edit', activeView, obje
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.striped')}>
<Switch
data-testid="toggle-striped"
checked={draft.striped === true}
onCheckedChange={(checked: boolean) => updateDraft('striped', checked)}
className="scale-75"
/>
</ConfigRow>
<ConfigRow label={t('console.objectView.bordered')}>
<Switch
data-testid="toggle-bordered"
checked={draft.bordered === true}
onCheckedChange={(checked: boolean) => updateDraft('bordered', checked)}
className="scale-75"
/>
</ConfigRow>
</div>
)}

Expand Down
6 changes: 6 additions & 0 deletions packages/i18n/src/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ const ar = {
enableSearch: 'تفعيل البحث',
enableFilter: 'تفعيل التصفية',
enableSort: 'تفعيل الترتيب',
enableHideFields: 'تفعيل إخفاء الحقول',
enableGroup: 'تفعيل التجميع',
enableColor: 'تفعيل اللون',
enableDensity: 'تفعيل الكثافة',
userActions: 'إجراءات المستخدم',
addRecordViaForm: 'إضافة سجلات عبر النموذج',
advanced: 'متقدم',
Expand Down Expand Up @@ -238,6 +242,8 @@ const ar = {
wrapHeaders: 'التفاف العناوين',
showFieldDescriptions: 'إظهار أوصاف الحقول',
collapseAllByDefault: 'طي الكل افتراضياً',
striped: 'صفوف مخططة',
bordered: 'خلايا محاطة بحدود',
editRecordsInline: 'تحرير السجلات مباشرة',
addDeleteRecordsInline: 'إضافة/حذف السجلات مباشرة',
clickIntoRecordDetails: 'انقر لعرض تفاصيل السجل',
Expand Down
6 changes: 6 additions & 0 deletions packages/i18n/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ const de = {
enableSearch: 'Suche aktivieren',
enableFilter: 'Filter aktivieren',
enableSort: 'Sortierung aktivieren',
enableHideFields: 'Felder ausblenden aktivieren',
enableGroup: 'Gruppierung aktivieren',
enableColor: 'Farbe aktivieren',
enableDensity: 'Dichte aktivieren',
userActions: 'Benutzeraktionen',
addRecordViaForm: 'Datensätze über Formular hinzufügen',
advanced: 'Erweitert',
Expand Down Expand Up @@ -242,6 +246,8 @@ const de = {
wrapHeaders: 'Kopfzeilen umbrechen',
showFieldDescriptions: 'Feldbeschreibungen anzeigen',
collapseAllByDefault: 'Standardmäßig alle einklappen',
striped: 'Gestreifte Zeilen',
bordered: 'Umrandete Zellen',
editRecordsInline: 'Datensätze inline bearbeiten',
addDeleteRecordsInline: 'Datensätze inline hinzufügen/löschen',
clickIntoRecordDetails: 'Klicken für Datensatzdetails',
Expand Down
6 changes: 6 additions & 0 deletions packages/i18n/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ const en = {
enableSearch: 'Enable search',
enableFilter: 'Enable filter',
enableSort: 'Enable sort',
enableHideFields: 'Enable hide fields',
enableGroup: 'Enable group',
enableColor: 'Enable color',
enableDensity: 'Enable density',
userActions: 'User actions',
addRecordViaForm: 'Add records through a form',
advanced: 'Advanced',
Expand Down Expand Up @@ -242,6 +246,8 @@ const en = {
wrapHeaders: 'Wrap headers',
showFieldDescriptions: 'Show field descriptions',
collapseAllByDefault: 'Collapse all by default',
striped: 'Striped rows',
bordered: 'Bordered cells',
editRecordsInline: 'Edit records inline',
addDeleteRecordsInline: 'Add/delete records inline',
clickIntoRecordDetails: 'Click into record details',
Expand Down
6 changes: 6 additions & 0 deletions packages/i18n/src/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ const es = {
enableSearch: 'Habilitar búsqueda',
enableFilter: 'Habilitar filtro',
enableSort: 'Habilitar ordenamiento',
enableHideFields: 'Habilitar ocultar campos',
enableGroup: 'Habilitar agrupación',
enableColor: 'Habilitar color',
enableDensity: 'Habilitar densidad',
userActions: 'Acciones de usuario',
addRecordViaForm: 'Agregar registros mediante formulario',
advanced: 'Avanzado',
Expand Down Expand Up @@ -237,6 +241,8 @@ const es = {
wrapHeaders: 'Ajustar encabezados',
showFieldDescriptions: 'Mostrar descripciones de campos',
collapseAllByDefault: 'Contraer todo por defecto',
striped: 'Filas alternadas',
bordered: 'Celdas con borde',
editRecordsInline: 'Editar registros en línea',
addDeleteRecordsInline: 'Agregar/eliminar registros en línea',
clickIntoRecordDetails: 'Clic para detalles del registro',
Expand Down
6 changes: 6 additions & 0 deletions packages/i18n/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ const fr = {
enableSearch: 'Activer la recherche',
enableFilter: 'Activer le filtre',
enableSort: 'Activer le tri',
enableHideFields: 'Activer masquer les champs',
enableGroup: 'Activer le regroupement',
enableColor: 'Activer la couleur',
enableDensity: 'Activer la densité',
userActions: 'Actions utilisateur',
addRecordViaForm: 'Ajouter des enregistrements via un formulaire',
advanced: 'Avancé',
Expand Down Expand Up @@ -242,6 +246,8 @@ const fr = {
wrapHeaders: 'Retour à la ligne des en-têtes',
showFieldDescriptions: 'Afficher les descriptions des champs',
collapseAllByDefault: 'Tout réduire par défaut',
striped: 'Lignes rayées',
bordered: 'Cellules bordées',
editRecordsInline: 'Modifier les enregistrements en ligne',
addDeleteRecordsInline: 'Ajouter/supprimer des enregistrements en ligne',
clickIntoRecordDetails: "Cliquer pour les détails de l'enregistrement",
Expand Down
Loading