feat(twenty-server): allow shouldHideEmptyGroups in app view manifest (#21370)

## Context

The view **Hide empty groups** setting (`shouldHideEmptyGroups`) can be
toggled in the UI, is persisted on the `View` entity, exposed in the
`CreateView`/`UpdateView` GraphQL inputs, and tracked by the flat-view
sync machinery — but it could **not** be set from an app's view
manifest.

Root cause: the field postdates the manifest plumbing (added in #16385,
Dec 2025). Two spots were never updated to thread it through:
- `ViewManifest` didn't declare the field.
- `fromViewManifestToUniversalFlatView` hardcoded
`shouldHideEmptyGroups: false`.

Ref: twentyhq/core-team-issues#414

## Changes

- Add optional `shouldHideEmptyGroups?: boolean` to `ViewManifest`.
- Read it in the converter (`?? false`), mirroring the existing
`isCompact` handling.
- Cover it in the converter unit test (default + explicit value).

No migration or schema change — the column already exists, and
downstream sync (`FLAT_VIEW_EDITABLE_PROPERTIES` + the universal-flat
compare type) already handles it.

## Test

- `npx jest from-view-manifest-to-universal-flat-view` → 5 passed
- `tsgo -p tsconfig.json` (twenty-server) → no new errors
- oxlint + oxfmt clean
This commit is contained in:
Félix Malfait
2026-06-09 16:33:58 +02:00
committed by GitHub
parent 834541da6d
commit cf70565976
3 changed files with 5 additions and 1 deletions
@@ -35,6 +35,7 @@ describe('fromViewManifestToUniversalFlatView', () => {
expect(result.icon).toBe('IconList');
expect(result.position).toBe(0);
expect(result.isCompact).toBe(false);
expect(result.shouldHideEmptyGroups).toBe(false);
expect(result.isCustom).toBe(true);
expect(result.visibility).toBe(ViewVisibility.WORKSPACE);
expect(result.openRecordIn).toBe(ViewOpenRecordIn.SIDE_PANEL);
@@ -53,6 +54,7 @@ describe('fromViewManifestToUniversalFlatView', () => {
icon: 'IconLayoutKanban',
position: 3,
isCompact: true,
shouldHideEmptyGroups: true,
visibility: ViewVisibility.UNLISTED,
openRecordIn: ViewOpenRecordIn.RECORD_PAGE,
},
@@ -64,6 +66,7 @@ describe('fromViewManifestToUniversalFlatView', () => {
expect(result.icon).toBe('IconLayoutKanban');
expect(result.position).toBe(3);
expect(result.isCompact).toBe(true);
expect(result.shouldHideEmptyGroups).toBe(true);
expect(result.visibility).toBe(ViewVisibility.UNLISTED);
expect(result.openRecordIn).toBe(ViewOpenRecordIn.RECORD_PAGE);
});
@@ -38,7 +38,7 @@ export const fromViewManifestToUniversalFlatView = ({
viewManifest.calendarFieldMetadataUniversalIdentifier ?? null,
mainGroupByFieldMetadataUniversalIdentifier:
viewManifest.mainGroupByFieldMetadataUniversalIdentifier ?? null,
shouldHideEmptyGroups: false,
shouldHideEmptyGroups: viewManifest.shouldHideEmptyGroups ?? false,
anyFieldFilterValue: null,
createdByUserWorkspaceId: null,
viewFieldUniversalIdentifiers: [],
@@ -74,6 +74,7 @@ export type ViewManifest = SyncableEntityOptions & {
visibility?: ViewVisibility;
openRecordIn?: ViewOpenRecordIn;
mainGroupByFieldMetadataUniversalIdentifier?: string;
shouldHideEmptyGroups?: boolean;
kanbanAggregateOperation?: AggregateOperations;
kanbanAggregateOperationFieldMetadataUniversalIdentifier?: string;
calendarLayout?: ViewCalendarLayout;