Skip to content

Commit d0ad6d3

Browse files
authored
create docs for prod registration (#17264)
* create docs for prod registration + fix issue where same resource page inside the same product was being highlighted in both places + fix issue where group page did not have a defined end route name * address cody pr comments * remove limitation on not being allowed to have overview pages on existing products + adjust unit tests * adjust docs * update all references about plugin to extension + remove DSL text from note
1 parent 3f0fc8d commit d0ad6d3

8 files changed

Lines changed: 1482 additions & 48 deletions

File tree

docusaurus/docs/extensions/api/nav/product-registration.md

Lines changed: 399 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
# Product Registration Examples (Experimental)
2+
3+
> Note: These examples use the experimental product registration API, which may change in future releases. For the current stable approach, see [Extension as a top-level product](./top-level-product.md) and [Extension as a cluster-level product](./cluster-level-product.md).
4+
5+
This page provides a set of copy-paste-ready examples for common product registration use cases. Each example is self-contained and shows the complete `index.ts` for an Extension.
6+
7+
For a detailed explanation of each API method and its types, see the [Product Registration API reference](../api/nav/product-registration.md).
8+
9+
## Quick start: empty product
10+
11+
Register a product with a single call. Useful for bootstrapping a new Extension before adding real pages:
12+
13+
```ts
14+
// ./index.ts
15+
import { importTypes } from '@rancher/auto-import';
16+
import { IPlugin } from '@shell/core/types';
17+
18+
export default function(extension: IPlugin) {
19+
importTypes(extension);
20+
extension.metadata = require('./package.json');
21+
22+
extension.addProduct('my-first-product');
23+
}
24+
```
25+
26+
**Result:** A product named "my-first-product" appears in the top-level slide-in menu with a default empty page.
27+
28+
---
29+
30+
## Single page product
31+
32+
A product that shows one full-page component with no side-menu. Good for dashboards or settings screens:
33+
34+
```ts
35+
// ./index.ts
36+
import { importTypes } from '@rancher/auto-import';
37+
import { IPlugin } from '@shell/core/types';
38+
import { ProductSinglePage } from '@shell/core/plugin-types';
39+
40+
export default function(extension: IPlugin) {
41+
importTypes(extension);
42+
extension.metadata = require('./package.json');
43+
44+
const product: ProductSinglePage = {
45+
name: 'status-board',
46+
label: 'Status Board',
47+
icon: 'globe',
48+
component: () => import('./pages/StatusBoard.vue'),
49+
};
50+
51+
extension.addProduct(product);
52+
}
53+
```
54+
55+
**Result:** A product with a single full-page view and no side-menu.
56+
57+
---
58+
59+
## Multiple custom pages
60+
61+
A product with several custom pages listed as side-menu entries:
62+
63+
```ts
64+
// ./index.ts
65+
import { importTypes } from '@rancher/auto-import';
66+
import { IPlugin } from '@shell/core/types';
67+
import {
68+
ProductMetadata,
69+
ProductChildCustomPage
70+
} from '@shell/core/plugin-types';
71+
72+
export default function(extension: IPlugin) {
73+
importTypes(extension);
74+
extension.metadata = require('./package.json');
75+
76+
const overviewPage: ProductChildCustomPage = {
77+
name: 'overview',
78+
label: 'Overview',
79+
component: () => import('./pages/Overview.vue'),
80+
weight: 3,
81+
};
82+
83+
const usersPage: ProductChildCustomPage = {
84+
name: 'users',
85+
label: 'Users',
86+
component: () => import('./pages/Users.vue'),
87+
weight: 2,
88+
};
89+
90+
const logsPage: ProductChildCustomPage = {
91+
name: 'logs',
92+
label: 'Logs',
93+
component: () => import('./pages/Logs.vue'),
94+
weight: 1,
95+
};
96+
97+
const product: ProductMetadata = {
98+
name: 'my-app',
99+
label: 'My App',
100+
icon: 'gear',
101+
};
102+
103+
extension.addProduct(product, [overviewPage, usersPage, logsPage]);
104+
}
105+
```
106+
107+
**Result:** A product with three pages in the side-menu, ordered by weight (bigger number on top).
108+
109+
---
110+
111+
## Resource pages (Kubernetes resources)
112+
113+
A product that uses Rancher Dashboard's built-in list/detail/edit views for Kubernetes resource types:
114+
115+
```ts
116+
// ./index.ts
117+
import { importTypes } from '@rancher/auto-import';
118+
import { IPlugin } from '@shell/core/types';
119+
import {
120+
ProductMetadata,
121+
ProductChildResourcePage
122+
} from '@shell/core/plugin-types';
123+
124+
export default function(extension: IPlugin) {
125+
importTypes(extension);
126+
extension.metadata = require('./package.json');
127+
128+
const clusterPage: ProductChildResourcePage = {
129+
type: 'provisioning.cattle.io.cluster',
130+
weight: 2,
131+
config: {
132+
displayName: 'Clusters',
133+
isCreatable: true,
134+
isEditable: true,
135+
isRemovable: true,
136+
canYaml: true,
137+
showState: true,
138+
showAge: true,
139+
},
140+
};
141+
142+
const secretsPage: ProductChildResourcePage = {
143+
type: 'secret',
144+
weight: 1,
145+
};
146+
147+
const product: ProductMetadata = {
148+
name: 'cluster-tools',
149+
label: 'Cluster Tools',
150+
};
151+
152+
extension.addProduct(product, [clusterPage, secretsPage]);
153+
}
154+
```
155+
156+
**Result:** A product with two resource pages. Clicking a resource in the side-menu shows Rancher's standard list view; clicking a row opens the detail view.
157+
158+
---
159+
160+
## Mixed pages: custom + resource
161+
162+
Combine custom pages and resource pages in the same product:
163+
164+
```ts
165+
// ./index.ts
166+
import { importTypes } from '@rancher/auto-import';
167+
import { IPlugin } from '@shell/core/types';
168+
import {
169+
ProductMetadata,
170+
ProductChildCustomPage,
171+
ProductChildResourcePage
172+
} from '@shell/core/plugin-types';
173+
174+
export default function(extension: IPlugin) {
175+
importTypes(extension);
176+
extension.metadata = require('./package.json');
177+
178+
const dashboardPage: ProductChildCustomPage = {
179+
name: 'dashboard',
180+
label: 'Dashboard',
181+
component: () => import('./pages/Dashboard.vue'),
182+
weight: 3,
183+
};
184+
185+
const clusterPage: ProductChildResourcePage = {
186+
type: 'provisioning.cattle.io.cluster',
187+
weight: 2,
188+
};
189+
190+
const settingsPage: ProductChildCustomPage = {
191+
name: 'settings',
192+
label: 'Settings',
193+
component: () => import('./pages/Settings.vue'),
194+
weight: 1,
195+
};
196+
197+
const product: ProductMetadata = {
198+
name: 'my-platform',
199+
label: 'My Platform',
200+
};
201+
202+
extension.addProduct(product, [dashboardPage, clusterPage, settingsPage]);
203+
}
204+
```
205+
206+
---
207+
208+
## Pages organized in groups
209+
210+
Use groups to create collapsible folder/groups in the side-menu:
211+
212+
```ts
213+
// ./index.ts
214+
import { importTypes } from '@rancher/auto-import';
215+
import { IPlugin } from '@shell/core/types';
216+
import {
217+
ProductMetadata,
218+
ProductChildCustomPage,
219+
ProductChildGroup
220+
} from '@shell/core/plugin-types';
221+
222+
export default function(extension: IPlugin) {
223+
importTypes(extension);
224+
extension.metadata = require('./package.json');
225+
226+
// Standalone page (outside any group)
227+
const homePage: ProductChildCustomPage = {
228+
name: 'home',
229+
label: 'Home',
230+
component: () => import('./pages/Home.vue'),
231+
weight: 10,
232+
};
233+
234+
// "Monitoring" group with two pages
235+
const alertsPage: ProductChildCustomPage = {
236+
name: 'alerts',
237+
label: 'Alerts',
238+
component: () => import('./pages/Alerts.vue'),
239+
};
240+
241+
const metricsPage: ProductChildCustomPage = {
242+
name: 'metrics',
243+
label: 'Metrics',
244+
component: () => import('./pages/Metrics.vue'),
245+
};
246+
247+
const monitoringGroup: ProductChildGroup = {
248+
name: 'monitoring',
249+
label: 'Monitoring',
250+
weight: 5,
251+
children: [alertsPage, metricsPage],
252+
};
253+
254+
// "Admin" group with two pages
255+
const usersPage: ProductChildCustomPage = {
256+
name: 'users',
257+
label: 'Users',
258+
component: () => import('./pages/Users.vue'),
259+
};
260+
261+
const rolesPage: ProductChildCustomPage = {
262+
name: 'roles',
263+
label: 'Roles',
264+
component: () => import('./pages/Roles.vue'),
265+
};
266+
267+
const adminGroup: ProductChildGroup = {
268+
name: 'admin',
269+
label: 'Administration',
270+
weight: 1,
271+
children: [usersPage, rolesPage],
272+
};
273+
274+
const product: ProductMetadata = {
275+
name: 'my-platform',
276+
label: 'My Platform',
277+
};
278+
279+
extension.addProduct(product, [homePage, monitoringGroup, adminGroup]);
280+
}
281+
```
282+
283+
**Result:** The side-menu will look like:
284+
285+
```
286+
Home
287+
▾ Monitoring
288+
Alerts
289+
Metrics
290+
▾ Administration
291+
Users
292+
Roles
293+
```
294+
295+
---
296+
297+
## Group with its own page
298+
299+
A group can have its own component that renders when the group header is clicked, instead of navigating to the first child:
300+
301+
```ts
302+
const monitoringGroup: ProductChildGroup = {
303+
name: 'monitoring',
304+
label: 'Monitoring',
305+
component: () => import('./pages/MonitoringOverview.vue'), // group's own page
306+
children: [alertsPage, metricsPage],
307+
};
308+
```
309+
310+
**Result:** Clicking "Monitoring" in the side-menu shows the `MonitoringOverview` component. Expanding the group reveals the child pages.
311+
312+
---
313+
314+
## Extending Cluster Explorer
315+
316+
Add a standalone custom page to the Cluster Explorer product:
317+
318+
```ts
319+
// ./index.ts
320+
import { importTypes } from '@rancher/auto-import';
321+
import { IPlugin } from '@shell/core/types';
322+
import { ProductChildCustomPage } from '@shell/core/plugin-types';
323+
324+
export default function(extension: IPlugin) {
325+
importTypes(extension);
326+
extension.metadata = require('./package.json');
327+
328+
const customPage: ProductChildCustomPage = {
329+
name: 'cost-analysis',
330+
label: 'Cost Analysis',
331+
component: () => import('./pages/CostAnalysis.vue'),
332+
};
333+
334+
extension.extendProduct('explorer', [customPage]);
335+
}
336+
```
337+
338+
Or, to add pages inside a group:
339+
340+
```ts
341+
// ./index.ts
342+
import { importTypes } from '@rancher/auto-import';
343+
import { IPlugin } from '@shell/core/types';
344+
import {
345+
ProductChildCustomPage,
346+
ProductChildGroup
347+
} from '@shell/core/plugin-types';
348+
349+
export default function(extension: IPlugin) {
350+
importTypes(extension);
351+
extension.metadata = require('./package.json');
352+
353+
const costPage: ProductChildCustomPage = {
354+
name: 'cost-analysis',
355+
label: 'Cost Analysis',
356+
component: () => import('./pages/CostAnalysis.vue'),
357+
};
358+
359+
const usagePage: ProductChildCustomPage = {
360+
name: 'usage-report',
361+
label: 'Usage Report',
362+
component: () => import('./pages/UsageReport.vue'),
363+
};
364+
365+
const insightsGroup: ProductChildGroup = {
366+
name: 'insights',
367+
label: 'Insights',
368+
children: [costPage, usagePage],
369+
};
370+
371+
extension.extendProduct('explorer', [insightsGroup]);
372+
}
373+
```
374+
375+
The products available for extension are: `'explorer'`, `'manager'`, `'settings'`, and `'auth'`.
376+
377+
---
378+
379+
## Using translation keys instead of labels
380+
381+
For i18n support, use `labelKey` instead of `label`. The key will be resolved from your Extension's translation files:
382+
383+
```ts
384+
const product: ProductMetadata = {
385+
name: 'my-app',
386+
labelKey: 'product.myApp.label',
387+
icon: 'gear',
388+
};
389+
390+
const overviewPage: ProductChildCustomPage = {
391+
name: 'overview',
392+
labelKey: 'product.myApp.overview',
393+
component: () => import('./pages/Overview.vue'),
394+
};
395+
396+
extension.addProduct(product, [overviewPage]);
397+
```
398+
399+
> Note: See the [Localization documentation](../advanced/localization.md) for details on setting up translation files.

0 commit comments

Comments
 (0)