diff --git a/packages/business-features/dock/src/__snapshots__/dock.test.tsx.snap b/packages/business-features/dock/src/__snapshots__/dock.test.tsx.snap index 856251be46..bbf89358a7 100644 --- a/packages/business-features/dock/src/__snapshots__/dock.test.tsx.snap +++ b/packages/business-features/dock/src/__snapshots__/dock.test.tsx.snap @@ -7,19 +7,68 @@ exports[`DockHost, given rendered given implementations of dock tabs emerge rend
-
+
- Some title + Some title 1 +
+
+
+
+ Some title 2
- Some content + Some content 1 +
+
+
+ + +`; + +exports[`DockHost, given rendered given implementations of dock tabs emerge when the second dock tab is clicked renders 1`] = ` + +
+
+
+
+
+ Some title 1 +
+
+
+
+ Some title 2 +
+
+
+
+
+ Some content 2
diff --git a/packages/business-features/dock/src/dock.test.tsx b/packages/business-features/dock/src/dock.test.tsx index 01e81f138d..d54c85663b 100644 --- a/packages/business-features/dock/src/dock.test.tsx +++ b/packages/business-features/dock/src/dock.test.tsx @@ -4,6 +4,7 @@ import { renderFor } from "@k8slens/test-utils"; import { DockHost } from "./dock/dock-host"; import React from "react"; import type { RenderResult } from "@testing-library/react"; +import { act } from "@testing-library/react"; import { dockTabInjectionToken } from "./dock-tab"; import { Discover, discoverFor } from "@k8slens/react-testing-library-discovery"; import { registerFeature } from "@k8slens/feature-core"; @@ -31,20 +32,38 @@ describe("DockHost, given rendered", () => { describe("given implementations of dock tabs emerge", () => { beforeEach(() => { - const dockTabInjectable = getInjectable({ - id: "some-dock-tab", + const dockTabInjectable1 = getInjectable({ + id: "some-dock-tab-1", instantiate: () => ({ - id: "some-dock-tab", - TitleComponent: () =>
Some title
, - ContentComponent: () =>
Some content
, + id: "some-dock-tab-1", + TitleComponent: () =>
Some title 1
, + + ContentComponent: () => ( +
Some content 1
+ ), + }), + + injectionToken: dockTabInjectionToken, + }); + + const dockTabInjectable2 = getInjectable({ + id: "some-dock-tab-2", + + instantiate: () => ({ + id: "some-dock-tab-2", + TitleComponent: () =>
Some title 2
, + + ContentComponent: () => ( +
Some content 2
+ ), }), injectionToken: dockTabInjectionToken, }); runInAction(() => { - di.register(dockTabInjectable); + di.register(dockTabInjectable1, dockTabInjectable2); }); }); @@ -52,12 +71,35 @@ describe("DockHost, given rendered", () => { expect(rendered.baseElement).toMatchSnapshot(); }); - it("renders the title of dock tab", () => { - discover.getSingleElement("some-dock-tab-title"); + it("renders the titles of all the dock tabs in order", () => { + expect(discover.queryAllElements("dock-tab-title").attributeValues).toEqual([ + "some-title-1", + "some-title-2", + ]); }); - it("renders the content of the dock tab", () => { - discover.getSingleElement("some-dock-tab-content"); + it("renders only the content of the first dock tab", () => { + expect(discover.queryAllElements("dock-tab-content").attributeValues).toEqual([ + "some-content-1", + ]); + }); + + describe("when the second dock tab is clicked", () => { + beforeEach(() => { + act(() => { + discover.getSingleElement("dock-tab", "some-dock-tab-2").click(); + }); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("renders only the content of the second dock tab", () => { + expect(discover.queryAllElements("dock-tab-content").attributeValues).toEqual([ + "some-content-2", + ]); + }); }); }); }); diff --git a/packages/business-features/dock/src/dock/activate-dock-tab.injectable.ts b/packages/business-features/dock/src/dock/activate-dock-tab.injectable.ts new file mode 100644 index 0000000000..80d68e103a --- /dev/null +++ b/packages/business-features/dock/src/dock/activate-dock-tab.injectable.ts @@ -0,0 +1,19 @@ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import activeDockTabIdInjectable from "./active-dock-tab-id.injectable"; + +const activateDockTabInjectable = getInjectable({ + id: "activate-dock-tab", + + instantiate: (di, tabId) => { + const activeDockTabId = di.inject(activeDockTabIdInjectable); + + return action(() => activeDockTabId.set(tabId)); + }, + + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, tabId: string) => tabId, + }), +}); + +export default activateDockTabInjectable; diff --git a/packages/business-features/dock/src/dock/active-dock-tab-id.injectable.ts b/packages/business-features/dock/src/dock/active-dock-tab-id.injectable.ts new file mode 100644 index 0000000000..e689fae9b1 --- /dev/null +++ b/packages/business-features/dock/src/dock/active-dock-tab-id.injectable.ts @@ -0,0 +1,9 @@ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; + +const activeDockTabIdInjectable = getInjectable({ + id: "active-dock-tab-id", + instantiate: () => observable.box(), +}); + +export default activeDockTabIdInjectable; diff --git a/packages/business-features/dock/src/dock/active-dock-tab.injectable.ts b/packages/business-features/dock/src/dock/active-dock-tab.injectable.ts index fd1fc5f517..02d634eabf 100644 --- a/packages/business-features/dock/src/dock/active-dock-tab.injectable.ts +++ b/packages/business-features/dock/src/dock/active-dock-tab.injectable.ts @@ -1,23 +1,33 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import dockTabsInjectable from "./dock-tabs.injectable"; +import activeDockTabIdInjectable from "./active-dock-tab-id.injectable"; +import { pipeline } from "@ogre-tools/fp"; +import { defaults, find, first } from "lodash/fp"; +import type { DockTab } from "../dock-tab"; +import dockTabTypesInjectable from "./dock-tabs-types.injectable"; + +const nullTab: DockTab = { + id: "no-active-dock-tab", + TitleComponent: () => null, + ContentComponent: () => null, +}; const activeDockTabInjectable = getInjectable({ id: "active-dock-tab", instantiate: (di) => { - const dockTabs = di.inject(dockTabsInjectable); + const dockTabTypes = di.inject(dockTabTypesInjectable); + const activeDockTabId = di.inject(activeDockTabIdInjectable); return computed(() => { - const [ - activeDockTab = { - id: "no-active-dock-tab", - TitleComponent: () => null, - ContentComponent: () => null, - }, - ] = dockTabs.get(); + const tabs = dockTabTypes.get(); - return activeDockTab; + return pipeline( + tabs, + find((tab) => tab.id === activeDockTabId.get()), + defaults(first(tabs)), + defaults(nullTab), + ); }); }, }); diff --git a/packages/business-features/dock/src/dock/dock-host.tsx b/packages/business-features/dock/src/dock/dock-host.tsx index f2250c2c9d..47709e5800 100644 --- a/packages/business-features/dock/src/dock/dock-host.tsx +++ b/packages/business-features/dock/src/dock/dock-host.tsx @@ -4,7 +4,7 @@ import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; import { Tabs } from "./tabs"; import { Div, Map } from "@k8slens/ui-components"; -import dockTabsInjectable from "./dock-tabs.injectable"; +import dockTabsInjectable, { ActivatableDockTab } from "./dock-tabs.injectable"; import type { DockTab } from "../dock-tab"; import activeDockTabInjectable from "./active-dock-tab.injectable"; @@ -15,8 +15,8 @@ const NonInjectedDockHost = observer(({ dockTabs, activeDockTab }: Dependencies)
- {({ TitleComponent }) => ( - + {({ id, TitleComponent, activate }) => ( + )} @@ -31,7 +31,7 @@ const NonInjectedDockHost = observer(({ dockTabs, activeDockTab }: Dependencies) }); interface Dependencies { - dockTabs: IComputedValue; + dockTabs: IComputedValue; activeDockTab: IComputedValue; } diff --git a/packages/business-features/dock/src/dock/dock-tabs-types.injectable.ts b/packages/business-features/dock/src/dock/dock-tabs-types.injectable.ts new file mode 100644 index 0000000000..b62b2d5fc9 --- /dev/null +++ b/packages/business-features/dock/src/dock/dock-tabs-types.injectable.ts @@ -0,0 +1,15 @@ +import { getInjectable } from "@ogre-tools/injectable"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; +import { dockTabInjectionToken } from "../dock-tab"; + +const dockTabTypesInjectable = getInjectable({ + id: "dock-tab-types", + + instantiate: (di) => { + const computedInjectMany = di.inject(computedInjectManyInjectable); + + return computedInjectMany(dockTabInjectionToken); + }, +}); + +export default dockTabTypesInjectable; diff --git a/packages/business-features/dock/src/dock/dock-tabs.injectable.ts b/packages/business-features/dock/src/dock/dock-tabs.injectable.ts index c7cc7eb939..1799e0a046 100644 --- a/packages/business-features/dock/src/dock/dock-tabs.injectable.ts +++ b/packages/business-features/dock/src/dock/dock-tabs.injectable.ts @@ -1,14 +1,27 @@ import { getInjectable } from "@ogre-tools/injectable"; -import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; -import { dockTabInjectionToken } from "../dock-tab"; +import { computed } from "mobx"; +import type { DockTab } from "../dock-tab"; +import activateDockTabInjectable from "./activate-dock-tab.injectable"; +import dockTabTypesInjectable from "./dock-tabs-types.injectable"; + +type Activatable = { activate: () => void }; + +export type ActivatableDockTab = DockTab & Activatable; const dockTabsInjectable = getInjectable({ id: "dock-tabs", instantiate: (di) => { - const computedInjectMany = di.inject(computedInjectManyInjectable); + const dockTabTypes = di.inject(dockTabTypesInjectable); + const activateFor = di.injectFactory(activateDockTabInjectable); - return computedInjectMany(dockTabInjectionToken); + return computed(() => + dockTabTypes.get().map((tab) => ({ + ...tab, + + activate: activateFor(tab.id), + })), + ); }, }); diff --git a/packages/business-features/dock/src/dock/tabs.tsx b/packages/business-features/dock/src/dock/tabs.tsx index 3c70383c6a..5e2feac5ee 100644 --- a/packages/business-features/dock/src/dock/tabs.tsx +++ b/packages/business-features/dock/src/dock/tabs.tsx @@ -1,5 +1,6 @@ import { Div } from "@k8slens/ui-components"; import React from "react"; +import type { DivProps } from "@k8slens/ui-components"; export interface TabsProps { children: React.ReactNode; @@ -9,12 +10,10 @@ export const Tabs = ({ children }: TabsProps) => (
{children}
); -export interface TabProps { +export type TabProps = { children: React.ReactNode; -} +} & DivProps; -const Tab = ({ children }: TabProps) => ( -
{children}
-); +const Tab = ({ ...props }: TabProps) =>
; Tabs.Tab = Tab;