feat(sdk): add definePageLayoutTab for extending existing page layouts (#20004)
## Summary
Introduces `definePageLayoutTab` so apps can attach a single tab (with
optional widgets) to an **existing** `pageLayout` referenced by
`pageLayoutUniversalIdentifier`. The parent layout can be standard, from
the same app, or from another app — mirroring how `defineField`
references an object via `objectUniversalIdentifier`.
This complements `definePageLayout`: use `definePageLayout` when you own
the entire layout, use `definePageLayoutTab` when you only want to add
to one.
```ts
import { definePageLayoutTab, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
export default definePageLayoutTab({
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
pageLayoutUniversalIdentifier: 'STANDARD-OR-OTHER-APP-PAGE-LAYOUT-UUID',
title: 'Hello World',
position: 1000,
icon: 'IconWorld',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
widgets: [/* ... */],
});
```
## Changes
- **twenty-shared**: new top-level `pageLayoutTabs:
PageLayoutTabManifest[]` on `Manifest`, optional
`pageLayoutUniversalIdentifier` on `PageLayoutTabManifest`, new
`SyncableEntity.PageLayoutTab`.
- **twenty-sdk**:
- new `definePageLayoutTab` + `PageLayoutTabConfig` exports;
- manifest extraction wiring (`TargetFunction.DefinePageLayoutTab`,
`ManifestEntityKey.PageLayoutTabs`);
- dev-mode label/state for the new entity;
- CLI scaffold (`getPageLayoutTabBaseFile`) + unit tests for `npx
twenty-cli add`.
- **twenty-server**: convert top-level `pageLayoutTabs` (and their
widgets) into universal flat entities in
`computeApplicationManifestAllUniversalFlatEntityMaps`. Cross-app FK
validation on `pageLayoutUniversalIdentifier` is already handled by the
existing `FlatPageLayoutTab` validator.
- **docs**: new `definePageLayoutTab` accordion in `apps/layout.mdx`
with usage example and guidance vs `definePageLayout`.
- **CI / rich-app fixture**: `extra-tab.page-layout-tab.ts` exercises
the new flow with a front-component widget; `expected-manifest.ts` and
`manifest.tests.ts` updated.
This commit is contained in:
+1
@@ -315,6 +315,7 @@ export const EXPECTED_MANIFEST: Manifest = {
|
||||
views: [],
|
||||
navigationMenuItems: [],
|
||||
pageLayouts: [],
|
||||
pageLayoutTabs: [],
|
||||
roles: [
|
||||
{
|
||||
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000040',
|
||||
|
||||
+23
@@ -4,6 +4,7 @@ import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
NavigationMenuItemType,
|
||||
PageLayoutTabLayoutMode,
|
||||
RelationOnDeleteAction,
|
||||
RelationType,
|
||||
ViewCalendarLayout,
|
||||
@@ -12,6 +13,28 @@ import {
|
||||
|
||||
export const EXPECTED_MANIFEST: Manifest = {
|
||||
pageLayouts: [],
|
||||
pageLayoutTabs: [
|
||||
{
|
||||
universalIdentifier: 'b0b1b2b3-b4b5-4000-8000-000000000010',
|
||||
pageLayoutUniversalIdentifier: 'b0b1b2b3-b4b5-4000-8000-000000000020',
|
||||
title: 'Extra Tab',
|
||||
position: 1000,
|
||||
icon: 'IconLayout',
|
||||
layoutMode: PageLayoutTabLayoutMode.CANVAS,
|
||||
widgets: [
|
||||
{
|
||||
universalIdentifier: 'b0b1b2b3-b4b5-4000-8000-000000000011',
|
||||
title: 'Extra Widget',
|
||||
type: 'FRONT_COMPONENT',
|
||||
configuration: {
|
||||
configurationType: 'FRONT_COMPONENT',
|
||||
frontComponentUniversalIdentifier:
|
||||
'370ae182-743f-4ecb-b625-7ac48e21f0e5',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
publicAssets: [
|
||||
{
|
||||
checksum: '99496069dcc2a1488e1cae9f826d2707',
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ export const defineManifestTests = (appPath: string): void => {
|
||||
expect(manifest.fields).toHaveLength(23);
|
||||
expect(manifest.views).toHaveLength(5);
|
||||
expect(manifest.navigationMenuItems).toHaveLength(3);
|
||||
expect(manifest.pageLayoutTabs).toHaveLength(1);
|
||||
|
||||
expect(normalizeManifestForComparison(manifest)).toEqual(
|
||||
normalizeManifestForComparison(EXPECTED_MANIFEST),
|
||||
|
||||
@@ -31,6 +31,7 @@ export const normalizeManifestForComparison = <T extends Manifest>(
|
||||
views: sortById(manifest.views),
|
||||
navigationMenuItems: sortById(manifest.navigationMenuItems),
|
||||
pageLayouts: sortById(manifest.pageLayouts),
|
||||
pageLayoutTabs: sortById(manifest.pageLayoutTabs ?? []),
|
||||
logicFunctions: sortById(
|
||||
manifest.logicFunctions?.map((fn) => ({
|
||||
...fn,
|
||||
|
||||
Reference in New Issue
Block a user