Skip to content

Commit 91e1671

Browse files
TheRealJonclaude
andcommitted
CONSOLE-4862: Create extension point for plugins to add items to catalog toolbars
This change introduces a new dynamic plugin extension point that allows plugins to contribute custom UI components to catalog toolbars. Plugins can target specific catalogs and catalog item types, or provide toolbar items that appear across all catalogs. Extension definition: - New console.catalog/toolbar-item extension type - Properties support optional catalogId and type filters - Components render as FlexItems in the catalog toolbar Implementation: - Extension type and type guard in catalog.ts - Hook to resolve and filter toolbar extensions by catalogId and type - CatalogToolbar component renders toolbar extension components - Comprehensive test coverage for extension filtering logic This enables plugins like operator-lifecycle-manager-v1 to add custom controls (toggles, filters, etc.) directly into catalog interfaces without modifying core catalog code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 9efb576 commit 91e1671

File tree

11 files changed

+312
-75
lines changed

11 files changed

+312
-75
lines changed

frontend/packages/console-dynamic-plugin-sdk/docs/console-extensions.md

Lines changed: 87 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,75 +11,76 @@
1111
9. [console.catalog/item-provider](#consolecatalogitem-provider)
1212
10. [console.catalog/item-type](#consolecatalogitem-type)
1313
11. [console.catalog/item-type-metadata](#consolecatalogitem-type-metadata)
14-
12. [console.cluster-overview/inventory-item](#consolecluster-overviewinventory-item)
15-
13. [console.cluster-overview/multiline-utilization-item](#consolecluster-overviewmultiline-utilization-item)
16-
14. [console.cluster-overview/utilization-item](#consolecluster-overviewutilization-item)
17-
15. [console.context-provider](#consolecontext-provider)
18-
16. [console.create-project-modal](#consolecreate-project-modal)
19-
17. [console.dashboards/card](#consoledashboardscard)
20-
18. [console.dashboards/custom/overview/detail/item](#consoledashboardscustomoverviewdetailitem)
21-
19. [console.dashboards/overview/activity/resource](#consoledashboardsoverviewactivityresource)
22-
20. [console.dashboards/overview/health/operator](#consoledashboardsoverviewhealthoperator)
23-
21. [console.dashboards/overview/health/prometheus](#consoledashboardsoverviewhealthprometheus)
24-
22. [console.dashboards/overview/health/resource](#consoledashboardsoverviewhealthresource)
25-
23. [console.dashboards/overview/health/url](#consoledashboardsoverviewhealthurl)
26-
24. [console.dashboards/overview/inventory/item](#consoledashboardsoverviewinventoryitem)
27-
25. [console.dashboards/overview/inventory/item/group](#consoledashboardsoverviewinventoryitemgroup)
28-
26. [console.dashboards/overview/inventory/item/replacement](#consoledashboardsoverviewinventoryitemreplacement)
29-
27. [console.dashboards/overview/prometheus/activity/resource](#consoledashboardsoverviewprometheusactivityresource)
30-
28. [console.dashboards/project/overview/item](#consoledashboardsprojectoverviewitem)
31-
29. [console.dashboards/tab](#consoledashboardstab)
32-
30. [console.file-upload](#consolefile-upload)
33-
31. [console.flag](#consoleflag)
34-
32. [console.flag/hookProvider](#consoleflaghookProvider)
35-
33. [console.flag/model](#consoleflagmodel)
36-
34. [console.global-config](#consoleglobal-config)
37-
35. [console.model-metadata](#consolemodel-metadata)
38-
36. [console.navigation/href](#consolenavigationhref)
39-
37. [console.navigation/resource-cluster](#consolenavigationresource-cluster)
40-
38. [console.navigation/resource-ns](#consolenavigationresource-ns)
41-
39. [console.navigation/section](#consolenavigationsection)
42-
40. [console.navigation/separator](#consolenavigationseparator)
43-
41. [console.page/resource/details](#consolepageresourcedetails)
44-
42. [console.page/resource/list](#consolepageresourcelist)
45-
43. [console.page/route](#consolepageroute)
46-
44. [console.page/route/standalone](#consolepageroutestandalone)
47-
45. [console.perspective](#consoleperspective)
48-
46. [console.project-overview/inventory-item](#consoleproject-overviewinventory-item)
49-
47. [console.project-overview/utilization-item](#consoleproject-overviewutilization-item)
50-
48. [console.pvc/alert](#consolepvcalert)
51-
49. [console.pvc/create-prop](#consolepvccreate-prop)
52-
50. [console.pvc/delete](#consolepvcdelete)
53-
51. [console.pvc/status](#consolepvcstatus)
54-
52. [console.redux-reducer](#consoleredux-reducer)
55-
53. [console.resource/create](#consoleresourcecreate)
56-
54. [console.resource/details-item](#consoleresourcedetails-item)
57-
55. [console.storage-class/provisioner](#consolestorage-classprovisioner)
58-
56. [console.storage-provider](#consolestorage-provider)
59-
57. [console.tab](#consoletab)
60-
58. [console.tab/horizontalNav](#consoletabhorizontalNav)
61-
59. [console.telemetry/listener](#consoletelemetrylistener)
62-
60. [console.topology/adapter/build](#consoletopologyadapterbuild)
63-
61. [console.topology/adapter/network](#consoletopologyadapternetwork)
64-
62. [console.topology/adapter/pod](#consoletopologyadapterpod)
65-
63. [console.topology/component/factory](#consoletopologycomponentfactory)
66-
64. [console.topology/create/connector](#consoletopologycreateconnector)
67-
65. [console.topology/data/factory](#consoletopologydatafactory)
68-
66. [console.topology/decorator/provider](#consoletopologydecoratorprovider)
69-
67. [console.topology/details/resource-alert](#consoletopologydetailsresource-alert)
70-
68. [console.topology/details/resource-link](#consoletopologydetailsresource-link)
71-
69. [console.topology/details/tab](#consoletopologydetailstab)
72-
70. [console.topology/details/tab-section](#consoletopologydetailstab-section)
73-
71. [console.topology/display/filters](#consoletopologydisplayfilters)
74-
72. [console.topology/relationship/provider](#consoletopologyrelationshipprovider)
75-
73. [console.user-preference/group](#consoleuser-preferencegroup)
76-
74. [console.user-preference/item](#consoleuser-preferenceitem)
77-
75. [console.yaml-template](#consoleyaml-template)
78-
76. [dev-console.add/action](#dev-consoleaddaction)
79-
77. [dev-console.add/action-group](#dev-consoleaddaction-group)
80-
78. [dev-console.import/environment](#dev-consoleimportenvironment)
81-
79. [DEPRECATED] [console.dashboards/overview/detail/item](#consoledashboardsoverviewdetailitem)
82-
80. [DEPRECATED] [console.page/resource/tab](#consolepageresourcetab)
14+
12. [console.catalog/toolbar-item](#consolecatalogtoolbar-item)
15+
13. [console.cluster-overview/inventory-item](#consolecluster-overviewinventory-item)
16+
14. [console.cluster-overview/multiline-utilization-item](#consolecluster-overviewmultiline-utilization-item)
17+
15. [console.cluster-overview/utilization-item](#consolecluster-overviewutilization-item)
18+
16. [console.context-provider](#consolecontext-provider)
19+
17. [console.create-project-modal](#consolecreate-project-modal)
20+
18. [console.dashboards/card](#consoledashboardscard)
21+
19. [console.dashboards/custom/overview/detail/item](#consoledashboardscustomoverviewdetailitem)
22+
20. [console.dashboards/overview/activity/resource](#consoledashboardsoverviewactivityresource)
23+
21. [console.dashboards/overview/health/operator](#consoledashboardsoverviewhealthoperator)
24+
22. [console.dashboards/overview/health/prometheus](#consoledashboardsoverviewhealthprometheus)
25+
23. [console.dashboards/overview/health/resource](#consoledashboardsoverviewhealthresource)
26+
24. [console.dashboards/overview/health/url](#consoledashboardsoverviewhealthurl)
27+
25. [console.dashboards/overview/inventory/item](#consoledashboardsoverviewinventoryitem)
28+
26. [console.dashboards/overview/inventory/item/group](#consoledashboardsoverviewinventoryitemgroup)
29+
27. [console.dashboards/overview/inventory/item/replacement](#consoledashboardsoverviewinventoryitemreplacement)
30+
28. [console.dashboards/overview/prometheus/activity/resource](#consoledashboardsoverviewprometheusactivityresource)
31+
29. [console.dashboards/project/overview/item](#consoledashboardsprojectoverviewitem)
32+
30. [console.dashboards/tab](#consoledashboardstab)
33+
31. [console.file-upload](#consolefile-upload)
34+
32. [console.flag](#consoleflag)
35+
33. [console.flag/hookProvider](#consoleflaghookProvider)
36+
34. [console.flag/model](#consoleflagmodel)
37+
35. [console.global-config](#consoleglobal-config)
38+
36. [console.model-metadata](#consolemodel-metadata)
39+
37. [console.navigation/href](#consolenavigationhref)
40+
38. [console.navigation/resource-cluster](#consolenavigationresource-cluster)
41+
39. [console.navigation/resource-ns](#consolenavigationresource-ns)
42+
40. [console.navigation/section](#consolenavigationsection)
43+
41. [console.navigation/separator](#consolenavigationseparator)
44+
42. [console.page/resource/details](#consolepageresourcedetails)
45+
43. [console.page/resource/list](#consolepageresourcelist)
46+
44. [console.page/route](#consolepageroute)
47+
45. [console.page/route/standalone](#consolepageroutestandalone)
48+
46. [console.perspective](#consoleperspective)
49+
47. [console.project-overview/inventory-item](#consoleproject-overviewinventory-item)
50+
48. [console.project-overview/utilization-item](#consoleproject-overviewutilization-item)
51+
49. [console.pvc/alert](#consolepvcalert)
52+
50. [console.pvc/create-prop](#consolepvccreate-prop)
53+
51. [console.pvc/delete](#consolepvcdelete)
54+
52. [console.pvc/status](#consolepvcstatus)
55+
53. [console.redux-reducer](#consoleredux-reducer)
56+
54. [console.resource/create](#consoleresourcecreate)
57+
55. [console.resource/details-item](#consoleresourcedetails-item)
58+
56. [console.storage-class/provisioner](#consolestorage-classprovisioner)
59+
57. [console.storage-provider](#consolestorage-provider)
60+
58. [console.tab](#consoletab)
61+
59. [console.tab/horizontalNav](#consoletabhorizontalNav)
62+
60. [console.telemetry/listener](#consoletelemetrylistener)
63+
61. [console.topology/adapter/build](#consoletopologyadapterbuild)
64+
62. [console.topology/adapter/network](#consoletopologyadapternetwork)
65+
63. [console.topology/adapter/pod](#consoletopologyadapterpod)
66+
64. [console.topology/component/factory](#consoletopologycomponentfactory)
67+
65. [console.topology/create/connector](#consoletopologycreateconnector)
68+
66. [console.topology/data/factory](#consoletopologydatafactory)
69+
67. [console.topology/decorator/provider](#consoletopologydecoratorprovider)
70+
68. [console.topology/details/resource-alert](#consoletopologydetailsresource-alert)
71+
69. [console.topology/details/resource-link](#consoletopologydetailsresource-link)
72+
70. [console.topology/details/tab](#consoletopologydetailstab)
73+
71. [console.topology/details/tab-section](#consoletopologydetailstab-section)
74+
72. [console.topology/display/filters](#consoletopologydisplayfilters)
75+
73. [console.topology/relationship/provider](#consoletopologyrelationshipprovider)
76+
74. [console.user-preference/group](#consoleuser-preferencegroup)
77+
75. [console.user-preference/item](#consoleuser-preferenceitem)
78+
76. [console.yaml-template](#consoleyaml-template)
79+
77. [dev-console.add/action](#dev-consoleaddaction)
80+
78. [dev-console.add/action-group](#dev-consoleaddaction-group)
81+
79. [dev-console.import/environment](#dev-consoleimportenvironment)
82+
80. [DEPRECATED] [console.dashboards/overview/detail/item](#consoledashboardsoverviewdetailitem)
83+
81. [DEPRECATED] [console.page/resource/tab](#consolepageresourcetab)
8384

8485
---
8586

@@ -264,6 +265,22 @@ This extension allows plugins to contribute extra metadata like custom filters o
264265

265266
---
266267

268+
## `console.catalog/toolbar-item`
269+
270+
### Summary
271+
272+
This extension allows plugins to contribute toolbar items to the Catalog view for a specific catalog type.
273+
274+
### Properties
275+
276+
| Name | Value Type | Optional | Description |
277+
| ---- | ---------- | -------- | ----------- |
278+
| `component` | `CodeRef<React.ComponentType<{}>>` | no | The component to render in the catalog toolbar. |
279+
| `catalogId` | `string` | yes | The catalog ID the toolbar item is for. If not specified, the toolbar item will be available for all catalogs. |
280+
| `type` | `string` | yes | The catalog item type for this toolbar item. If not specified, the toolbar item will be available for all types. |
281+
282+
---
283+
267284
## `console.cluster-overview/inventory-item`
268285

269286
### Summary
@@ -826,8 +843,8 @@ Adds new standalone page (rendered outside the common page layout) to Console ro
826843

827844
| Name | Value Type | Optional | Description |
828845
| ---- | ---------- | -------- | ----------- |
829-
| `path` | `string \| string[]` | no | Valid URL path or array of paths that `path-to-regexp@^1.7.0` understands. |
830846
| `component` | `CodeRef<React.ComponentType<{}>>` | no | The component to be rendered when the route matches. |
847+
| `path` | `string \| string[]` | no | Valid URL path or array of paths that `path-to-regexp@^1.7.0` understands. |
831848
| `exact` | `boolean` | yes | When true, will only match if the path matches the `location.pathname` exactly. |
832849

833850
---

frontend/packages/console-dynamic-plugin-sdk/src/extensions/catalog.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,27 @@ export type CatalogCategoriesProvider = ExtensionDeclaration<
9999
}
100100
>;
101101

102+
/** This extension allows plugins to contribute toolbar items to the Catalog view for a specific catalog type. */
103+
export type CatalogToolbarItem = ExtensionDeclaration<
104+
'console.catalog/toolbar-item',
105+
{
106+
/** The catalog ID the toolbar item is for. If not specified, the toolbar item will be available for all catalogs. */
107+
catalogId?: string;
108+
/** The catalog item type for this toolbar item. If not specified, the toolbar item will be available for all types. */
109+
type?: string;
110+
/** The component to render in the catalog toolbar. */
111+
component: CodeRef<React.ComponentType>;
112+
}
113+
>;
114+
102115
export type SupportedCatalogExtensions =
103116
| CatalogItemType
104117
| CatalogItemTypeMetadata
105118
| CatalogItemProvider
106119
| CatalogItemFilter
107120
| CatalogItemMetadataProvider
108-
| CatalogCategoriesProvider;
121+
| CatalogCategoriesProvider
122+
| CatalogToolbarItem;
109123

110124
// Type guards
111125

@@ -133,6 +147,10 @@ export const isCatalogCategoriesProvider = (e: Extension): e is CatalogCategorie
133147
return e.type === 'console.catalog/categories-provider';
134148
};
135149

150+
export const isCatalogToolbarItem = (e: Extension): e is CatalogToolbarItem => {
151+
return e.type === 'console.catalog/toolbar-item';
152+
};
153+
136154
// Support types
137155

138156
export type CatalogExtensionHookOptions = {

frontend/packages/console-shared/src/components/catalog/CatalogController.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const CatalogController: React.FC<CatalogControllerProps> = ({
4343
loaded,
4444
loadError,
4545
catalogExtensions,
46+
toolbarExtensions,
4647
enableDetailsPanel,
4748
title: defaultTitle,
4849
description: defaultDescription,
@@ -202,6 +203,7 @@ const CatalogController: React.FC<CatalogControllerProps> = ({
202203
filterGroups={filterGroups}
203204
filterGroupMap={filterGroupMap}
204205
groupings={groupings}
206+
toolbarExtensions={toolbarExtensions}
205207
renderTile={renderTile}
206208
hideSidebar={hideSidebar}
207209
sortFilterGroups={sortFilterGroups}

frontend/packages/console-shared/src/components/catalog/__tests__/CatalogController.spec.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('CatalogController', () => {
4444
uid: '@console/helm-plugin[9]',
4545
},
4646
],
47+
toolbarExtensions: [],
4748
items: [],
4849
itemsMap: { HelmChart: [] },
4950
loaded: true,
@@ -64,7 +65,20 @@ describe('CatalogController', () => {
6465
type: 'HelmChart',
6566
title: 'Default title',
6667
description: 'Default description',
67-
catalogExtensions: [],
68+
catalogExtensions: [
69+
{
70+
pluginID: '@console/helm-plugin',
71+
pluginName: '@console/helm-plugin',
72+
properties: {
73+
catalogDescription: null,
74+
title: null,
75+
type: 'HelmChart',
76+
},
77+
type: 'console.catalog/item-type',
78+
uid: '@console/helm-plugin[9]',
79+
},
80+
],
81+
toolbarExtensions: [],
6882
items: [],
6983
itemsMap: { HelmChart: [] },
7084
loaded: true,

frontend/packages/console-shared/src/components/catalog/catalog-view/CatalogToolbar.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { forwardRef } from 'react';
22
import { Flex, FlexItem, SearchInput } from '@patternfly/react-core';
33
import * as _ from 'lodash';
44
import { useTranslation } from 'react-i18next';
5+
import { CatalogToolbarItem } from '@console/dynamic-plugin-sdk/src/extensions';
6+
import { ResolvedExtension } from '@console/dynamic-plugin-sdk/src/types';
57
import { ConsoleSelect } from '@console/internal/components/utils/console-select';
68
import { useDebounceCallback } from '@console/shared';
79
import { NO_GROUPING } from '../utils/category-utils';
@@ -18,6 +20,7 @@ type CatalogToolbarProps = {
1820
sortOrder: CatalogSortOrder;
1921
groupings: CatalogStringMap;
2022
activeGrouping: string;
23+
toolbarExtensions?: ResolvedExtension<CatalogToolbarItem>[];
2124
onGroupingChange: (grouping: string) => void;
2225
onSearchKeywordChange: (searchKeyword: string) => void;
2326
onSortOrderChange: (sortOrder: CatalogSortOrder) => void;
@@ -32,6 +35,7 @@ const CatalogToolbar = forwardRef<HTMLInputElement, CatalogToolbarProps>(
3235
sortOrder,
3336
groupings,
3437
activeGrouping,
38+
toolbarExtensions,
3539
onGroupingChange,
3640
onSearchKeywordChange,
3741
onSortOrderChange,
@@ -94,6 +98,14 @@ const CatalogToolbar = forwardRef<HTMLInputElement, CatalogToolbarProps>(
9498
/>
9599
</FlexItem>
96100
)}
101+
{toolbarExtensions?.map((extension) => {
102+
const Component = extension.properties.component;
103+
return (
104+
<FlexItem key={extension.uid}>
105+
<Component />
106+
</FlexItem>
107+
);
108+
})}
97109
</Flex>
98110
<CatalogPageNumItems>
99111
{t('console-shared~{{totalItems}} items', { totalItems })}

frontend/packages/console-shared/src/components/catalog/catalog-view/CatalogView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as React from 'react';
22
import * as _ from 'lodash';
33
import { useTranslation } from 'react-i18next';
4-
import { CatalogCategory } from '@console/dynamic-plugin-sdk/src';
4+
import { CatalogCategory, CatalogToolbarItem } from '@console/dynamic-plugin-sdk/src';
55
import { CatalogItem } from '@console/dynamic-plugin-sdk/src/extensions';
6+
import { ResolvedExtension } from '@console/dynamic-plugin-sdk/src/types';
67
import { isModalOpen } from '@console/internal/components/modals';
78
import { useQueryParams } from '../../../hooks/useQueryParams';
89
import PaneBody from '../../layout/PaneBody';
@@ -57,6 +58,7 @@ type CatalogViewProps = {
5758
filterGroups: string[];
5859
filterGroupMap: CatalogFilterGroupMap;
5960
groupings: CatalogStringMap;
61+
toolbarExtensions?: ResolvedExtension<CatalogToolbarItem>[];
6062
renderTile: (item: CatalogItem) => React.ReactNode;
6163
hideSidebar?: boolean;
6264
sortFilterGroups: boolean;
@@ -71,6 +73,7 @@ const CatalogView: React.FC<CatalogViewProps> = ({
7173
filterGroups,
7274
filterGroupMap,
7375
groupings,
76+
toolbarExtensions,
7477
renderTile,
7578
hideSidebar,
7679
sortFilterGroups,
@@ -341,6 +344,7 @@ const CatalogView: React.FC<CatalogViewProps> = ({
341344
sortOrder={sortOrder}
342345
groupings={groupings}
343346
activeGrouping={activeGrouping}
347+
toolbarExtensions={toolbarExtensions}
344348
onGroupingChange={handleGroupingChange}
345349
onSortOrderChange={handleSortOrderChange}
346350
onSearchKeywordChange={handleSearchKeywordChange}

0 commit comments

Comments
 (0)