diff --git a/frontend/packages/console-demo-plugin/src/dashboards/foo-card.tsx b/frontend/packages/console-demo-plugin/src/dashboards/foo-card.tsx
new file mode 100644
index 00000000000..7d8fe44c3fe
--- /dev/null
+++ b/frontend/packages/console-demo-plugin/src/dashboards/foo-card.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+import {
+ DashboardCard,
+ DashboardCardTitle,
+ DashboardCardBody,
+ DashboardCardHeader,
+} from '@console/internal/components/dashboard/dashboard-card';
+
+export const FooCard: React.FC<{}> = () => (
+
+
+ Foo Card
+
+
+ foo content
+
+
+);
diff --git a/frontend/packages/console-demo-plugin/src/plugin.tsx b/frontend/packages/console-demo-plugin/src/plugin.tsx
index f7dd18acc7d..0892aa266a2 100644
--- a/frontend/packages/console-demo-plugin/src/plugin.tsx
+++ b/frontend/packages/console-demo-plugin/src/plugin.tsx
@@ -14,11 +14,14 @@ import {
RoutePage,
DashboardsOverviewHealthPrometheusSubsystem,
DashboardsOverviewHealthURLSubsystem,
+ DashboardsCard,
+ DashboardsTab,
} from '@console/plugin-sdk';
// TODO(vojtech): internal code needed by plugins should be moved to console-shared package
import { PodModel } from '@console/internal/models';
import { FLAGS } from '@console/internal/const';
+import { GridPosition } from '@console/internal/components/dashboard/grid';
import { FooBarModel } from './models';
import { yamlTemplates } from './yaml-templates';
@@ -37,7 +40,9 @@ type ConsumedExtensions =
| YAMLTemplate
| RoutePage
| DashboardsOverviewHealthPrometheusSubsystem
- | DashboardsOverviewHealthURLSubsystem;
+ | DashboardsOverviewHealthURLSubsystem
+ | DashboardsTab
+ | DashboardsCard;
const plugin: Plugin = [
{
@@ -167,6 +172,22 @@ const plugin: Plugin = [
render: () => Test Page
,
},
},
+ {
+ type: 'Dashboards/Tab',
+ properties: {
+ id: 'foo-tab',
+ title: 'Foo',
+ },
+ },
+ {
+ type: 'Dashboards/Card',
+ properties: {
+ tab: 'foo-tab',
+ position: GridPosition.MAIN,
+ loader: () =>
+ import('./dashboards/foo-card' /* webpackChunkName: "demo-card" */).then((m) => m.FooCard),
+ },
+ },
];
export default plugin;
diff --git a/frontend/packages/console-plugin-sdk/src/registry.ts b/frontend/packages/console-plugin-sdk/src/registry.ts
index 50d376b8b82..81348b68f10 100644
--- a/frontend/packages/console-plugin-sdk/src/registry.ts
+++ b/frontend/packages/console-plugin-sdk/src/registry.ts
@@ -12,6 +12,8 @@ import {
isYAMLTemplate,
isRoutePage,
isDashboardsOverviewHealthSubsystem,
+ isDashboardsCard,
+ isDashboardsTab,
} from './typings';
/**
@@ -59,4 +61,12 @@ export class ExtensionRegistry {
public getDashboardsOverviewHealthSubsystems() {
return this.extensions.filter(isDashboardsOverviewHealthSubsystem);
}
+
+ public getDashboardsTabs() {
+ return this.extensions.filter(isDashboardsTab);
+ }
+
+ public getDashboardsCards() {
+ return this.extensions.filter(isDashboardsCard);
+ }
}
diff --git a/frontend/packages/console-plugin-sdk/src/typings/dashboards.ts b/frontend/packages/console-plugin-sdk/src/typings/dashboards.ts
index 4dba964e9e3..c1a8b8b9611 100644
--- a/frontend/packages/console-plugin-sdk/src/typings/dashboards.ts
+++ b/frontend/packages/console-plugin-sdk/src/typings/dashboards.ts
@@ -1,5 +1,8 @@
import { SubsystemHealth } from '@console/internal/components/dashboards-page/overview-dashboard/health-card';
+import { GridPosition } from '@console/internal/components/dashboard/grid';
+
import { Extension } from './extension';
+import { LazyLoader } from './types';
namespace ExtensionProperties {
interface DashboardsOverviewHealthSubsystem {
@@ -31,6 +34,25 @@ namespace ExtensionProperties {
/** The Prometheus query */
query: string;
}
+
+ export interface DashboardsTab {
+ /** The tab's ID which will be used as part of href within dashboards page */
+ id: string;
+
+ /** The tab title */
+ title: string;
+ }
+
+ export interface DashboardsCard {
+ /** The tab's ID where this card should be rendered */
+ tab: string;
+
+ /** The card position in the tab */
+ position: GridPosition;
+
+ /** Loader for the corresponding dashboard card component. */
+ loader: LazyLoader;
+ }
}
export interface DashboardsOverviewHealthURLSubsystem
@@ -60,3 +82,17 @@ export const isDashboardsOverviewHealthSubsystem = (
e: Extension,
): e is DashboardsOverviewHealthSubsystem =>
isDashboardsOverviewHealthURLSubsystem(e) || isDashboardsOverviewHealthPrometheusSubsystem(e);
+
+export interface DashboardsTab extends Extension {
+ type: 'Dashboards/Tab';
+}
+
+export const isDashboardsTab = (e: Extension): e is DashboardsTab =>
+ e.type === 'Dashboards/Tab';
+
+export interface DashboardsCard extends Extension {
+ type: 'Dashboards/Card';
+}
+
+export const isDashboardsCard = (e: Extension): e is DashboardsCard =>
+ e.type === 'Dashboards/Card';
diff --git a/frontend/packages/console-plugin-sdk/src/typings/pages.ts b/frontend/packages/console-plugin-sdk/src/typings/pages.ts
index 8adcbb0522c..de88694ed41 100644
--- a/frontend/packages/console-plugin-sdk/src/typings/pages.ts
+++ b/frontend/packages/console-plugin-sdk/src/typings/pages.ts
@@ -1,9 +1,7 @@
-import * as React from 'react';
import { RouteProps, RouteComponentProps } from 'react-router-dom';
import { K8sKind, K8sResourceKindReference } from '@console/internal/module/k8s';
import { Extension } from './extension';
-
-type LazyLoader = () => Promise>>;
+import { LazyLoader } from './types';
namespace ExtensionProperties {
export interface ResourcePage {
diff --git a/frontend/packages/console-plugin-sdk/src/typings/types.ts b/frontend/packages/console-plugin-sdk/src/typings/types.ts
new file mode 100644
index 00000000000..81742885303
--- /dev/null
+++ b/frontend/packages/console-plugin-sdk/src/typings/types.ts
@@ -0,0 +1 @@
+export type LazyLoader = () => Promise>>;
diff --git a/frontend/public/components/app-contents.tsx b/frontend/public/components/app-contents.tsx
index bf9894383c3..1b5020b4832 100644
--- a/frontend/public/components/app-contents.tsx
+++ b/frontend/public/components/app-contents.tsx
@@ -118,7 +118,7 @@ const AppContents = withRouter(React.memo(() => (
- import('./dashboards-page/dashboards' /* webpackChunkName: "dashboards" */).then(m => m.DashboardsPage)} />
+ import('./dashboards-page/dashboards' /* webpackChunkName: "dashboards" */).then(m => m.DashboardsPage)} />
import('./cluster-overview' /* webpackChunkName: "cluster-overview" */).then(m => m.ClusterOverviewPage)} />
diff --git a/frontend/public/components/dashboard/dashboard-card/card-body.tsx b/frontend/public/components/dashboard/dashboard-card/card-body.tsx
index d03170c4316..50f0aa8f05c 100644
--- a/frontend/public/components/dashboard/dashboard-card/card-body.tsx
+++ b/frontend/public/components/dashboard/dashboard-card/card-body.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import classNames from 'classnames';
import { CardBody, CardBodyProps } from '@patternfly/react-core';
-import { LoadingInline } from '../../utils';
+import { LoadingInline } from '../../utils/status-box';
export const DashboardCardBody: React.FC = React.memo(({ isLoading, classname, children, ...props }) => (
diff --git a/frontend/public/components/dashboard/grid.tsx b/frontend/public/components/dashboard/grid.tsx
index f73c3afc441..30a6f7ced4d 100644
--- a/frontend/public/components/dashboard/grid.tsx
+++ b/frontend/public/components/dashboard/grid.tsx
@@ -1,35 +1,58 @@
import * as React from 'react';
import { Grid, GridItem } from '@patternfly/react-core';
-import { useRefWidth } from '../utils';
+import { useRefWidth } from '../utils/ref-width-hook';
export const MEDIA_QUERY_LG = 992;
-export const DashboardGrid: React.FC = ({ mainCards, leftCards, rightCards }) => {
+export enum GridPosition {
+ MAIN = 'MAIN',
+ LEFT = 'LEFT',
+ RIGHT = 'RIGHT',
+}
+
+const mapCardsToGrid = (cards: React.ComponentType[], keyPrefix: string): React.ReactNode[] =>
+ cards.map((Card, index) => (
+
+ ));
+
+export const DashboardGrid: React.FC = ({ mainCards, leftCards = [], rightCards = [] }) => {
const [containerRef, width] = useRefWidth();
const grid = width <= MEDIA_QUERY_LG ?
(
- {mainCards}
+
+ {mapCardsToGrid(mainCards, 'main')}
+
- {leftCards}
+
+ {mapCardsToGrid(leftCards, 'left')}
+
- {rightCards}
+
+ {mapCardsToGrid(rightCards, 'right')}
+
) : (
- {leftCards}
+
+ {mapCardsToGrid(leftCards, 'left')}
+
- {mainCards}
+
+ {mapCardsToGrid(mainCards, 'main')}
+
- {rightCards}
+
+ {mapCardsToGrid(rightCards, 'right')}
+
);
@@ -38,7 +61,7 @@ export const DashboardGrid: React.FC = ({ mainCards, leftCar
};
type DashboardGridProps = {
- mainCards: React.ReactNode,
- leftCards?: React.ReactNode,
- rightCards?: React.ReactNode,
+ mainCards: React.ComponentType[],
+ leftCards?: React.ComponentType[],
+ rightCards?: React.ComponentType[],
};
diff --git a/frontend/public/components/dashboards-page/dashboards.tsx b/frontend/public/components/dashboards-page/dashboards.tsx
index bb0379180a9..4dc53912bc1 100644
--- a/frontend/public/components/dashboards-page/dashboards.tsx
+++ b/frontend/public/components/dashboards-page/dashboards.tsx
@@ -2,18 +2,46 @@ import * as React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { connect } from 'react-redux';
+import * as plugins from '../../plugins';
import { OverviewDashboard } from './overview-dashboard/overview-dashboard';
-import { HorizontalNav, PageHeading, LoadingBox } from '../utils';
+import { HorizontalNav, PageHeading, LoadingBox, Page, AsyncComponent } from '../utils';
+import { Dashboard } from '../dashboard/dashboard';
+import { DashboardGrid, GridPosition } from '../dashboard/grid';
+import { DashboardsCard } from '@console/plugin-sdk';
-const tabs = [
+const getCardsOnPosition = (cards: DashboardsCard[], position: GridPosition): React.ComponentType[] =>
+ cards.filter(c => c.properties.position === position).map(c => () => );
+
+const getPluginTabPages = (): Page[] => {
+ const cards = plugins.registry.getDashboardsCards();
+ return plugins.registry.getDashboardsTabs().map(tab => {
+ const tabCards = cards.filter(c => c.properties.tab === tab.properties.id);
+ return {
+ href: tab.properties.id,
+ name: tab.properties.title,
+ component: () => (
+
+
+
+ ),
+ };
+ });
+};
+
+const tabs: Page[] = [
{
href: '',
name: 'Overview',
component: OverviewDashboard,
},
+ ...getPluginTabPages(),
];
-const _DashboardsPage: React.FC = ({ match, kindsInFlight }) => {
+const DashboardsPage_: React.FC = ({ match, kindsInFlight }) => {
return kindsInFlight
?
: (
@@ -28,7 +56,7 @@ const mapStateToProps = ({k8s}) => ({
kindsInFlight: k8s.getIn(['RESOURCES', 'inFlight']),
});
-export const DashboardsPage = connect(mapStateToProps)(_DashboardsPage);
+export const DashboardsPage = connect(mapStateToProps)(DashboardsPage_);
type DashboardsPageProps = RouteComponentProps & {
kindsInFlight: boolean;
diff --git a/frontend/public/components/dashboards-page/overview-dashboard/overview-dashboard.tsx b/frontend/public/components/dashboards-page/overview-dashboard/overview-dashboard.tsx
index e62acf723a5..a912359af4a 100644
--- a/frontend/public/components/dashboards-page/overview-dashboard/overview-dashboard.tsx
+++ b/frontend/public/components/dashboards-page/overview-dashboard/overview-dashboard.tsx
@@ -5,13 +5,8 @@ import { HealthCard } from './health-card';
import { DetailsCard } from './details-card';
export const OverviewDashboard: React.FC<{}> = () => {
- const mainCards = [
- ,
- ];
-
- const leftCards = [
- ,
- ];
+ const mainCards = [HealthCard];
+ const leftCards = [DetailsCard];
return (
diff --git a/frontend/public/components/utils/horizontal-nav.tsx b/frontend/public/components/utils/horizontal-nav.tsx
index 8ff063fa165..113877db532 100644
--- a/frontend/public/components/utils/horizontal-nav.tsx
+++ b/frontend/public/components/utils/horizontal-nav.tsx
@@ -26,7 +26,7 @@ class PodsComponent extends React.PureComponent {
}
}
-type Page = {
+export type Page = {
href: string;
name: string;
component?: React.ComponentType;