diff --git a/packages/core/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap b/packages/core/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap
index 0936bd6264..3ad0ba73e9 100644
--- a/packages/core/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap
+++ b/packages/core/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap
@@ -4507,3 +4507,497 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
`;
+
+exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus with explicit 'orderNumber' given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered renders 1`] = `
+
+
+
+
+
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some child page
+
+
+
+
+
+
+
+`;
diff --git a/packages/core/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx b/packages/core/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx
index 27a211a0cb..9bde5663c2 100644
--- a/packages/core/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx
+++ b/packages/core/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx
@@ -21,6 +21,7 @@ import { runInAction, computed, observable } from "mobx";
import storageSaveDelayInjectable from "../../renderer/utils/create-storage/storage-save-delay.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import { flushPromises } from "../../common/test-utils/flush-promises";
+import type { ClusterPageMenuRegistration } from "../../extensions/common-api/types";
describe("cluster - sidebar and tab navigation for extensions", () => {
let applicationBuilder: ApplicationBuilder;
@@ -139,15 +140,12 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
const route = windowDi
.inject(routesInjectable)
.get()
- .find(
- matches({
- path: "/extension/some-extension-name/some-child-page-id",
- }),
- );
+ .find(matches({
+ path: "/extension/some-extension-name/some-child-page-id",
+ }));
assert(route);
navigateToRoute(route);
-
});
it("renders", () => {
@@ -453,4 +451,131 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
});
});
});
+
+ describe("given extension with cluster pages and cluster page menus with explicit 'orderNumber'", () => {
+ let someObservable: IObservableValue;
+
+ beforeEach(() => {
+ someObservable = observable.box(false);
+
+ const testExtension = {
+ id: "some-extension-id",
+ name: "some-extension-name",
+
+ rendererOptions: {
+ clusterPages: [
+ {
+ components: {
+ Page: () => {
+ throw new Error("should never come here");
+ },
+ },
+ },
+ {
+ id: "some-child-page-id",
+
+ components: {
+ Page: () => Some child page
,
+ },
+ },
+ {
+ id: "some-other-child-page-id",
+
+ components: {
+ Page: () => (
+ Some other child page
+ ),
+ },
+ },
+ ],
+
+ clusterPageMenus: [
+ {
+ id: "some-parent-id",
+ title: "Parent",
+ components: {
+ Icon: () => Some icon
,
+ },
+ orderNumber: -Infinity,
+ },
+ {
+ id: "some-child-id",
+ target: { pageId: "some-child-page-id" },
+ parentId: "some-parent-id",
+ title: "Child 1",
+ components: {
+ Icon: null as never,
+ },
+ },
+ {
+ id: "some-other-child-id",
+ target: { pageId: "some-other-child-page-id" },
+ parentId: "some-parent-id",
+ title: "Child 2",
+ components: {
+ Icon: null as never,
+ },
+ },
+ {
+ id: "some-menu-with-controlled-visibility",
+ title: "Some menu with controlled visibility",
+ visible: computed(() => someObservable.get()),
+ components: {
+ Icon: () => Some icon
,
+ },
+ },
+ ] as ClusterPageMenuRegistration[],
+ },
+ };
+
+ applicationBuilder.extensions.enable(testExtension);
+ });
+
+ describe("given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered", () => {
+ beforeEach(async () => {
+ rendered = await applicationBuilder.render();
+
+ const windowDi = applicationBuilder.applicationWindow.only.di;
+
+ const navigateToRoute = windowDi.inject(navigateToRouteInjectionToken);
+
+ const route = windowDi
+ .inject(routesInjectable)
+ .get()
+ .find(matches({
+ path: "/extension/some-extension-name/some-child-page-id",
+ }));
+
+ assert(route);
+ navigateToRoute(route);
+ });
+
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
+
+ it("renderes parent as first item in sidebar", () => {
+ const parent = rendered.getByTestId("sidebar-item-some-extension-name-some-parent-id");
+
+ assert(parent);
+ expect(parent.previousSibling).toBeNull();
+ });
+
+ it("parent is highlighted", () => {
+ const parent = rendered.getByTestId("sidebar-item-some-extension-name-some-parent-id");
+
+ expect(parent?.dataset.isActiveTest).toBe("true");
+ });
+
+ it("parent sidebar item is not expanded", () => {
+ const child = rendered.queryByTestId("sidebar-item-some-extension-name-some-child-id");
+
+ expect(child).toBeNull();
+ });
+
+ it("child page is shown", () => {
+ expect(rendered.getByTestId("some-child-page")).not.toBeNull();
+ });
+ });
+ });
});
diff --git a/packages/core/src/renderer/components/layout/cluster-page-menu.ts b/packages/core/src/renderer/components/layout/cluster-page-menu.ts
index 7f81ad82b9..1e38289ad0 100644
--- a/packages/core/src/renderer/components/layout/cluster-page-menu.ts
+++ b/packages/core/src/renderer/components/layout/cluster-page-menu.ts
@@ -15,6 +15,7 @@ export interface ClusterPageMenuRegistration {
title: React.ReactNode;
components: ClusterPageMenuComponents;
visible?: IComputedValue;
+ orderNumber?: number;
}
export interface ClusterPageMenuComponents {
diff --git a/packages/core/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx b/packages/core/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx
index 9792855b07..ea49cdbb5f 100644
--- a/packages/core/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx
+++ b/packages/core/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx
@@ -57,7 +57,7 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({
const res: SidebarItemRegistration = {
id: `${extension.sanitizedExtensionId}-${registration.id}`,
- orderNumber: 9999,
+ orderNumber: registration.orderNumber ?? 9999,
parentId: registration.parentId
? `${extension.sanitizedExtensionId}-${registration.parentId}`