diff --git a/docusaurus/docs/extensions/api/tabs.md b/docusaurus/docs/extensions/api/tabs.md index eeada460b0e..d76089633a0 100644 --- a/docusaurus/docs/extensions/api/tabs.md +++ b/docusaurus/docs/extensions/api/tabs.md @@ -24,6 +24,7 @@ _Arguments_ | Key | Type | Description | |---|---|---| |`TabLocation.RESOURCE_DETAIL`| String | Location for a Tab on a Resource Detail page | +|`TabLocation.RESOURCE_SHOW_CONFIGURATION`| String | Location for a Tab on a Resource Show Configuration *(From Rancher version v2.14.0)* |
@@ -38,13 +39,13 @@ _Arguments_ ![Tabs](../screenshots/add-tab.png) -`options` config object. Admissable parameters for the `options` with `'TabLocation.RESOURCE_DETAIL'` are: +`options` config object. Admissible parameters for the `options` with `'TabLocation.RESOURCE_DETAIL'` are: | Key | Type | Description | |---|---|---| |`name`| String | Query param name used in url when tab is active/clicked | |`label`| String | Text for the tab label | -|`labelKey`| String | Same as "label" but allows for translation. Will superseed "label" | +|`labelKey`| String | Same as "label" but allows for translation. Will supersede "label" | |`weight`| Int | Defines the order on which the tab is displayed in relation to other tabs in the component | |`showHeader`| Boolean | Whether the tab header is displayed or not | |`tooltip`| String | Tooltip message (on tab header) | @@ -83,7 +84,7 @@ plugin.addTab( |---|---|---| |`name`| String | Query param name used in url when tab is active/clicked | |`label`| String | Text for the tab label | -|`labelKey`| String | Same as "label" but allows for translation. Will superseed "label" | +|`labelKey`| String | Same as "label" but allows for translation. Will supersede "label" | |`weight`| Int | Defines the order on which the tab is displayed in relation to other tabs in the component | |`showHeader`| Boolean | Whether the tab header is displayed or not | |`tooltip`| String | Tooltip message (on tab header) | @@ -125,4 +126,40 @@ props: { }, .... +``` + +### TabLocation.RESOURCE_SHOW_CONFIGURATION options + +> Available from Rancher `2.14` and onwards + +![Tabs](../screenshots/add-tab-show-configuration.png) + +`options` config object. Admissible parameters for the `options` with `'TabLocation.RESOURCE_SHOW_CONFIGURATION'` are: + +| Key | Type | Description | +|---|---|---| +|`name`| String | Query param name used in url when tab is active/clicked | +|`label`| String | Text for the tab label | +|`labelKey`| String | Same as "label" but allows for translation. Will supersede "label" | +|`weight`| Int | Defines the order on which the tab is displayed in relation to other tabs in the component | +|`showHeader`| Boolean | Whether the tab header is displayed or not | +|`tooltip`| String | Tooltip message (on tab header) | +|`component`| Function | Component to be rendered as content on the tab | + +Usage example: + +```ts +plugin.addTab( + TabLocation.RESOURCE_SHOW_CONFIGURATION, + { resource: ['pod'] }, + { + name: 'some-name', + labelKey: 'plugin-examples.tab-label', + label: 'some-label', + weight: -5, + showHeader: true, + tooltip: 'this is a tooltip message', + component: () => import('./MyTabComponent.vue') + } +); ``` \ No newline at end of file diff --git a/docusaurus/docs/extensions/screenshots/add-tab-show-configuration.png b/docusaurus/docs/extensions/screenshots/add-tab-show-configuration.png new file mode 100644 index 00000000000..5cdf503b0c7 Binary files /dev/null and b/docusaurus/docs/extensions/screenshots/add-tab-show-configuration.png differ diff --git a/shell/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts b/shell/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts index 2953fcf0dca..4ba3d96a5bb 100644 --- a/shell/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +++ b/shell/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts @@ -1,4 +1,5 @@ -import { useDefaultConfigTabProps, useDefaultYamlTabProps } from '@shell/components/Drawer/ResourceDetailDrawer/composables'; +import { provide, inject } from 'vue'; +import { useDefaultConfigTabProps, useDefaultYamlTabProps, useResourceDetailDrawerProvider, useIsInResourceDetailDrawer } from '@shell/components/Drawer/ResourceDetailDrawer/composables'; import * as helpers from '@shell/components/Drawer/ResourceDetailDrawer/helpers'; import * as vuex from 'vuex'; @@ -6,6 +7,11 @@ jest.mock('@shell/components/Drawer/ResourceDetailDrawer/helpers'); jest.mock('vuex'); jest.mock('@shell/composables/drawer'); jest.mock('@shell/components/Drawer/ResourceDetailDrawer/index.vue', () => ({ name: 'ResourceDetailDrawer' } as any)); +jest.mock('vue', () => ({ + ...jest.requireActual('vue'), + provide: jest.fn(), + inject: jest.fn() +})); describe('composables: ResourceDetailDrawer', () => { const resource = { type: 'RESOURCE' }; @@ -78,4 +84,47 @@ describe('composables: ResourceDetailDrawer', () => { expect(props?.resource).toStrictEqual(resource); }); }); + + describe('useResourceDetailDrawerProvider', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call provide with the correct key and value', () => { + useResourceDetailDrawerProvider(); + + expect(provide).toHaveBeenCalledWith('isInResourceDetailDrawerKey', true); + }); + }); + + describe('useIsInResourceDetailDrawer', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call inject with the correct key and default value', () => { + (inject as jest.Mock).mockReturnValue(false); + + const result = useIsInResourceDetailDrawer(); + + expect(inject).toHaveBeenCalledWith('isInResourceDetailDrawerKey', false); + expect(result).toBe(false); + }); + + it('should return true when inside a ResourceDetailDrawer', () => { + (inject as jest.Mock).mockReturnValue(true); + + const result = useIsInResourceDetailDrawer(); + + expect(result).toBe(true); + }); + + it('should return false when not inside a ResourceDetailDrawer', () => { + (inject as jest.Mock).mockReturnValue(false); + + const result = useIsInResourceDetailDrawer(); + + expect(result).toBe(false); + }); + }); }); diff --git a/shell/components/Drawer/ResourceDetailDrawer/composables.ts b/shell/components/Drawer/ResourceDetailDrawer/composables.ts index b4624c38171..7c80443f723 100644 --- a/shell/components/Drawer/ResourceDetailDrawer/composables.ts +++ b/shell/components/Drawer/ResourceDetailDrawer/composables.ts @@ -1,6 +1,7 @@ import { useStore } from 'vuex'; import { getYaml } from '@shell/components/Drawer/ResourceDetailDrawer/helpers'; import { ConfigProps, YamlProps } from '@shell/components/Drawer/ResourceDetailDrawer/types'; +import { inject, provide } from 'vue'; export async function useDefaultYamlTabProps(resource: any): Promise { const yaml = await getYaml(resource); @@ -27,3 +28,21 @@ export function useDefaultConfigTabProps(resource: any): ConfigProps | undefined resourceType: resource.type }; } + +const IS_IN_RESOURCE_DETAIL_DRAWER_KEY = 'isInResourceDetailDrawerKey'; + +/** + * Used to add a provide method which will indicate to all ancestors that they're inside the ResourceDetailDrawer. This is useful because we show + * config page components both independently and within the ResourceDetailDrawer and we sometimes want to distinguish between the two use cases. +*/ +export function useResourceDetailDrawerProvider() { + provide(IS_IN_RESOURCE_DETAIL_DRAWER_KEY, true); +} + +/** + * A composable used to determine if the current component was instantiated as an ancestor of a ResourceDetailDrawer. + * @returns true if the component is an ancestor of ResourceDetailDrawer, otherwise false + */ +export function useIsInResourceDetailDrawer() { + return inject(IS_IN_RESOURCE_DETAIL_DRAWER_KEY, false); +} diff --git a/shell/components/Drawer/ResourceDetailDrawer/index.vue b/shell/components/Drawer/ResourceDetailDrawer/index.vue index 95a53c77081..0e30fefb56c 100644 --- a/shell/components/Drawer/ResourceDetailDrawer/index.vue +++ b/shell/components/Drawer/ResourceDetailDrawer/index.vue @@ -4,7 +4,7 @@ import { useI18n } from '@shell/composables/useI18n'; import { useStore } from 'vuex'; import Tabbed from '@shell/components/Tabbed/index.vue'; import YamlTab, { Props as YamlProps } from '@shell/components/Drawer/ResourceDetailDrawer/YamlTab.vue'; -import { useDefaultConfigTabProps, useDefaultYamlTabProps } from '@shell/components/Drawer/ResourceDetailDrawer/composables'; +import { useDefaultConfigTabProps, useDefaultYamlTabProps, useResourceDetailDrawerProvider } from '@shell/components/Drawer/ResourceDetailDrawer/composables'; import ConfigTab from '@shell/components/Drawer/ResourceDetailDrawer/ConfigTab.vue'; import { computed, ref } from 'vue'; import RcButton from '@components/RcButton/RcButton.vue'; @@ -54,6 +54,8 @@ const canEdit = computed(() => { return isConfig.value ? props.resource.canEdit : props.resource.canEditYaml; }); +useResourceDetailDrawerProvider(); +