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_

-`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
+
+
+
+`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();
+
{
return {
@@ -133,6 +135,12 @@ export default {
}
},
+ setup() {
+ const isInResourceDetailDrawer = useIsInResourceDetailDrawer();
+
+ return { isInResourceDetailDrawer };
+ },
+
watch: {
sortedTabs(tabs) {
const {
diff --git a/shell/core/types.ts b/shell/core/types.ts
index ead9531fe9f..2cdeac4e515 100644
--- a/shell/core/types.ts
+++ b/shell/core/types.ts
@@ -79,6 +79,7 @@ export enum PanelLocation {
/** Enum regarding tab locations that are extensible in the UI */
export enum TabLocation {
RESOURCE_DETAIL = 'tab', // eslint-disable-line no-unused-vars
+ RESOURCE_SHOW_CONFIGURATION = 'resource-show-configuration', // eslint-disable-line no-unused-vars
CLUSTER_CREATE_RKE2 = 'cluster-create-rke2', // eslint-disable-line no-unused-vars
}