mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Deployment Management] Add cards navigation in management landing page for serverless (#160096)
This commit is contained in:
parent
3382061db4
commit
ec620e7fb3
47 changed files with 1097 additions and 50 deletions
|
@ -35,6 +35,7 @@ const STORYBOOKS = [
|
|||
'expression_reveal_image',
|
||||
'expression_shape',
|
||||
'expression_tagcloud',
|
||||
'management',
|
||||
'fleet',
|
||||
'grouping',
|
||||
'home',
|
||||
|
|
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -471,7 +471,9 @@ packages/kbn-logging-mocks @elastic/kibana-core
|
|||
x-pack/plugins/logstash @elastic/logstash
|
||||
packages/kbn-managed-vscode-config @elastic/kibana-operations
|
||||
packages/kbn-managed-vscode-config-cli @elastic/kibana-operations
|
||||
packages/kbn-management/cards_navigation @elastic/platform-deployment-management
|
||||
src/plugins/management @elastic/platform-deployment-management
|
||||
packages/kbn-management/storybook/config @elastic/platform-deployment-management
|
||||
test/plugin_functional/plugins/management_test_plugin @elastic/kibana-app-services
|
||||
packages/kbn-mapbox-gl @elastic/kibana-gis
|
||||
x-pack/examples/third_party_maps_source_example @elastic/kibana-gis
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
"kibanaOverview": "src/plugins/kibana_overview",
|
||||
"lists": "packages/kbn-securitysolution-list-utils/src",
|
||||
"exceptionList-components": "packages/kbn-securitysolution-exception-list-components/src",
|
||||
"management": ["src/legacy/core_plugins/management", "src/plugins/management"],
|
||||
"management": ["src/legacy/core_plugins/management", "src/plugins/management", "packages/kbn-management"],
|
||||
"monaco": "packages/kbn-monaco/src",
|
||||
"navigation": "src/plugins/navigation",
|
||||
"newsfeed": "src/plugins/newsfeed",
|
||||
|
|
|
@ -236,7 +236,7 @@ in Kibana, e.g. visualizations. It has the form of a flyout panel.
|
|||
|
||||
|{kib-repo}blob/{branch}/src/plugins/management/README.md[management]
|
||||
|This plugins contains the "Stack Management" page framework. It offers navigation and an API
|
||||
to link individual managment section into it. This plugin does not contain any individual
|
||||
to link individual management section into it. This plugin does not contain any individual
|
||||
management section itself.
|
||||
|
||||
|
||||
|
|
|
@ -488,6 +488,7 @@
|
|||
"@kbn/logging": "link:packages/kbn-logging",
|
||||
"@kbn/logging-mocks": "link:packages/kbn-logging-mocks",
|
||||
"@kbn/logstash-plugin": "link:x-pack/plugins/logstash",
|
||||
"@kbn/management-cards-navigation": "link:packages/kbn-management/cards_navigation",
|
||||
"@kbn/management-plugin": "link:src/plugins/management",
|
||||
"@kbn/management-test-plugin": "link:test/plugin_functional/plugins/management_test_plugin",
|
||||
"@kbn/mapbox-gl": "link:packages/kbn-mapbox-gl",
|
||||
|
@ -1143,6 +1144,7 @@
|
|||
"@kbn/lint-ts-projects-cli": "link:packages/kbn-lint-ts-projects-cli",
|
||||
"@kbn/managed-vscode-config": "link:packages/kbn-managed-vscode-config",
|
||||
"@kbn/managed-vscode-config-cli": "link:packages/kbn-managed-vscode-config-cli",
|
||||
"@kbn/management-storybook-config": "link:packages/kbn-management/storybook/config",
|
||||
"@kbn/optimizer": "link:packages/kbn-optimizer",
|
||||
"@kbn/optimizer-webpack-helpers": "link:packages/kbn-optimizer-webpack-helpers",
|
||||
"@kbn/peggy": "link:packages/kbn-peggy",
|
||||
|
|
41
packages/kbn-management/cards_navigation/README.mdx
Normal file
41
packages/kbn-management/cards_navigation/README.mdx
Normal file
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
id: kbn-management/components/CardsNavigation
|
||||
slug: /kbn-management/components/cards_navigation
|
||||
title: Cards Navigation
|
||||
description: A component that allows the users to navigate to other management apps
|
||||
tags: ['management', 'component']
|
||||
date: 2023-04-23
|
||||
---
|
||||
|
||||
This component is simply in charge of rendering a list of links to other management apps. It also
|
||||
makes sure that the apps are enabled before doing so and it also aggregates them into predefined
|
||||
categories.
|
||||
|
||||
### Adding new items to the navigation
|
||||
|
||||
For adding a new item to the navigation all you have to do is edit the `cards_navigation/src/consts.tsx`
|
||||
file and add two things:
|
||||
|
||||
* Add the app id into the `appIds` enum (make sure that the app_id value matches the one from the plugin)
|
||||
* Add a new entry to the `appDefinitions` object. In here you can specify the category where you want it to be, icon and description.
|
||||
|
||||
### Removing an item from the navigation
|
||||
|
||||
If an item needs to be hidden from the navigation you can specify that by using the `hideLinksTo` prop:
|
||||
|
||||
```typescript
|
||||
<CardsNavigation
|
||||
sections={sections}
|
||||
appBasePath={appBasePath}
|
||||
hideLinksTo={[ appIds.RULES, appIds.TAGS ]}
|
||||
/>
|
||||
|
||||
```
|
||||
|
||||
In case an app needs to be removed all together from all the solutions you can remove its
|
||||
definition from the `consts.tsx` file. The app might still be visible in the side nav, so if you
|
||||
want to remove all links to it from management but without disabling the plugin you will have
|
||||
to remove it from the side nav too.
|
||||
|
||||
Bare in mind that if the app is disabled then it will be hidden anyway from the cards navigation
|
||||
and from the sidenav.
|
12
packages/kbn-management/cards_navigation/index.ts
Normal file
12
packages/kbn-management/cards_navigation/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type { AppId, CardsNavigationComponentProps } from './src';
|
||||
|
||||
export { appIds } from './src';
|
||||
export { CardsNavigation } from './src';
|
13
packages/kbn-management/cards_navigation/jest.config.js
Normal file
13
packages/kbn-management/cards_navigation/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/packages/kbn-management/cards_navigation'],
|
||||
};
|
5
packages/kbn-management/cards_navigation/kibana.jsonc
Normal file
5
packages/kbn-management/cards_navigation/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/management-cards-navigation",
|
||||
"owner": "@elastic/platform-deployment-management"
|
||||
}
|
90
packages/kbn-management/cards_navigation/mocks/mocks.ts
Normal file
90
packages/kbn-management/cards_navigation/mocks/mocks.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const APP_BASE_PATH = 'http://localhost:9001';
|
||||
|
||||
export const sectionsMock = [
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
apps: [
|
||||
{
|
||||
id: 'ingest_pipelines',
|
||||
title: 'Ingest pipelines',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines',
|
||||
},
|
||||
{
|
||||
id: 'pipelines',
|
||||
title: 'Pipelines',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
{
|
||||
id: 'index_management',
|
||||
title: 'Index Management',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
{
|
||||
id: 'transform',
|
||||
title: 'Transforms',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
{
|
||||
id: 'jobsListLink',
|
||||
title: 'Machine Learning',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
{
|
||||
id: 'data_view',
|
||||
title: 'Data View',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Content',
|
||||
apps: [
|
||||
{
|
||||
id: 'objects',
|
||||
title: 'Saved Objects',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
{
|
||||
id: 'filesManagement',
|
||||
title: 'Files Management',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
title: 'Other',
|
||||
apps: [
|
||||
{
|
||||
id: 'api_keys',
|
||||
title: 'API Keys',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines_logstash',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { APP_BASE_PATH, sectionsMock } from './mocks';
|
||||
|
||||
export const mockProps = {
|
||||
appBasePath: APP_BASE_PATH,
|
||||
sections: sectionsMock,
|
||||
onCardClick: (e: any) => {
|
||||
e.preventDefault();
|
||||
action('Navigate to: ', e.target.href);
|
||||
},
|
||||
};
|
6
packages/kbn-management/cards_navigation/package.json
Normal file
6
packages/kbn-management/cards_navigation/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/management-cards-navigation",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { CardsNavigation as Component } from './cards_navigation';
|
||||
import { mockProps } from '../mocks/storybook.mock';
|
||||
|
||||
import mdx from '../README.mdx';
|
||||
|
||||
export default {
|
||||
title: 'Developer/Cards Navigation',
|
||||
description: '',
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CardsNavigationWillAllLinks = () => {
|
||||
return <Component {...mockProps} />;
|
||||
};
|
||||
|
||||
export const CardsNavigationWithSomeLinks = () => {
|
||||
return <Component {...mockProps} sections={[{ apps: mockProps.sections[1].apps }]} />;
|
||||
};
|
||||
|
||||
export const CardsNavigationWithHiddenLinks = () => {
|
||||
return <Component {...mockProps} hideLinksTo={['api_keys']} />;
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen, cleanup } from '@testing-library/react';
|
||||
|
||||
import { APP_BASE_PATH, sectionsMock } from '../mocks/mocks';
|
||||
import { CardsNavigation } from './cards_navigation';
|
||||
import { CardsNavigationComponentProps } from './types';
|
||||
|
||||
const renderCardsNavigationComponent = (props: CardsNavigationComponentProps) => {
|
||||
return [render(<CardsNavigation {...props} />)];
|
||||
};
|
||||
|
||||
describe('Cards Navigation', () => {
|
||||
describe('Component', () => {
|
||||
test('is rendered', () => {
|
||||
expect(() =>
|
||||
render(<CardsNavigation appBasePath={APP_BASE_PATH} sections={sectionsMock} />)
|
||||
).not.toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('States', () => {
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('it renders categories and cards', () => {
|
||||
renderCardsNavigationComponent({ sections: sectionsMock, appBasePath: APP_BASE_PATH });
|
||||
|
||||
const dataCategory = screen.queryByTestId('category-data');
|
||||
const dataPipelinesApp = screen.queryByTestId('app-card-pipelines');
|
||||
|
||||
expect(dataCategory).not.toBeNull();
|
||||
expect(dataPipelinesApp).not.toBeNull();
|
||||
});
|
||||
|
||||
test('it doesnt show empty categories', () => {
|
||||
renderCardsNavigationComponent({
|
||||
sections: [
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
apps: [],
|
||||
},
|
||||
],
|
||||
appBasePath: APP_BASE_PATH,
|
||||
});
|
||||
|
||||
const dataCategory = screen.queryByTestId('category-data');
|
||||
|
||||
expect(dataCategory).toBeNull();
|
||||
});
|
||||
|
||||
test('it allows to disable certain apps', () => {
|
||||
renderCardsNavigationComponent({
|
||||
sections: sectionsMock,
|
||||
appBasePath: APP_BASE_PATH,
|
||||
hideLinksTo: ['pipelines'],
|
||||
});
|
||||
|
||||
const dataPipelinesApp = screen.queryByTestId('app-card-pipelines');
|
||||
|
||||
expect(dataPipelinesApp).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { flatMap } from 'lodash';
|
||||
import {
|
||||
EuiPageSection,
|
||||
EuiPageHeader,
|
||||
EuiSpacer,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
EuiCard,
|
||||
EuiText,
|
||||
EuiHorizontalRule,
|
||||
} from '@elastic/eui';
|
||||
import { CardsNavigationComponentProps, AppRegistrySections, Application, AppProps } from './types';
|
||||
import { appCategories, appDefinitions, getAppIdsByCategory } from './consts';
|
||||
import type { AppId } from './consts';
|
||||
|
||||
// Retrieve the data we need from a given app from the management app registry
|
||||
const getDataFromManagementApp = (app: Application) => {
|
||||
return {
|
||||
id: app.id,
|
||||
title: app.title,
|
||||
href: app.basePath,
|
||||
};
|
||||
};
|
||||
|
||||
// Given a category and a list of apps, build an array of apps that belong to that category
|
||||
const getAppsForCategory = (category: string, filteredApps: { [key: string]: Application }) => {
|
||||
return getAppIdsByCategory(category)
|
||||
.map((appId: AppId) => {
|
||||
if (!filteredApps[appId]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...getDataFromManagementApp(filteredApps[appId]),
|
||||
...appDefinitions[appId],
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as AppProps[];
|
||||
};
|
||||
|
||||
const getEnabledAppsByCategory = (sections: AppRegistrySections[], hideLinksTo: string[]) => {
|
||||
// Flatten all apps into a single array
|
||||
const flattenApps = flatMap(sections, (section) => section.apps)
|
||||
// Remove all apps that the consumer wants to disable.
|
||||
.filter((app) => !hideLinksTo.includes(app.id));
|
||||
// Filter out apps that are not enabled and create an object with the
|
||||
// app id as the key so we can easily do app look up by id.
|
||||
const filteredApps: { [key: string]: Application } = flattenApps.reduce(
|
||||
(obj, item: Application) => {
|
||||
return item.enabled ? { ...obj, [item.id]: item } : obj;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// Build list of categories with apps that are enabled
|
||||
return [
|
||||
{
|
||||
id: appCategories.DATA,
|
||||
title: i18n.translate('management.landing.withCardNavigation.dataTitle', {
|
||||
defaultMessage: 'Data',
|
||||
}),
|
||||
apps: getAppsForCategory(appCategories.DATA, filteredApps),
|
||||
},
|
||||
{
|
||||
id: appCategories.CONTENT,
|
||||
title: i18n.translate('management.landing.withCardNavigation.contentTitle', {
|
||||
defaultMessage: 'Content',
|
||||
}),
|
||||
apps: getAppsForCategory(appCategories.CONTENT, filteredApps),
|
||||
},
|
||||
{
|
||||
id: appCategories.OTHER,
|
||||
title: i18n.translate('management.landing.withCardNavigation.otherTitle', {
|
||||
defaultMessage: 'Other',
|
||||
}),
|
||||
apps: getAppsForCategory(appCategories.OTHER, filteredApps),
|
||||
},
|
||||
// Filter out categories that don't have any apps since they dont need to be rendered
|
||||
].filter((category) => category.apps.length > 0);
|
||||
};
|
||||
|
||||
export const CardsNavigation = ({
|
||||
sections,
|
||||
appBasePath,
|
||||
onCardClick,
|
||||
hideLinksTo = [],
|
||||
}: CardsNavigationComponentProps) => {
|
||||
const appsByCategory = getEnabledAppsByCategory(sections, hideLinksTo);
|
||||
|
||||
return (
|
||||
<EuiPageSection color="transparent" paddingSize="none">
|
||||
<EuiPageHeader
|
||||
bottomBorder
|
||||
pageTitle={i18n.translate('management.landing.withCardNavigation.pageTitle', {
|
||||
defaultMessage: 'Management',
|
||||
})}
|
||||
description={i18n.translate('management.landing.withCardNavigation.pageDescription', {
|
||||
defaultMessage: 'Manage your indices, data views, saved objects, settings, and more.',
|
||||
})}
|
||||
/>
|
||||
|
||||
{appsByCategory.map((category, index) => (
|
||||
<div key={category.id}>
|
||||
{index === 0 ? (
|
||||
<EuiSpacer size="l" />
|
||||
) : (
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiHorizontalRule />
|
||||
</>
|
||||
)}
|
||||
<EuiText data-test-subj={`category-${category.id}`}>
|
||||
<h3>{category.title}</h3>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGrid columns={3}>
|
||||
{category.apps.map((app: AppProps) => (
|
||||
<EuiFlexItem key={app.id}>
|
||||
<EuiCard
|
||||
data-test-subj={`app-card-${app.id}`}
|
||||
layout="horizontal"
|
||||
icon={app.icon}
|
||||
titleSize="xs"
|
||||
title={app.title}
|
||||
description={app.description}
|
||||
href={appBasePath + app.href}
|
||||
onClick={onCardClick}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
</div>
|
||||
))}
|
||||
</EuiPageSection>
|
||||
);
|
||||
};
|
171
packages/kbn-management/cards_navigation/src/consts.tsx
Normal file
171
packages/kbn-management/cards_navigation/src/consts.tsx
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
|
||||
import { AppDefinition } from './types';
|
||||
|
||||
export enum appIds {
|
||||
INGEST_PIPELINES = 'ingest_pipelines',
|
||||
PIPELINES = 'pipelines',
|
||||
INDEX_MANAGEMENT = 'index_management',
|
||||
TRANSFORM = 'transform',
|
||||
ML = 'jobsListLink',
|
||||
DATA_VIEW = 'data_view',
|
||||
SAVED_OBJECTS = 'objects',
|
||||
TAGS = 'tags',
|
||||
FILES_MANAGEMENT = 'filesManagement',
|
||||
API_KEYS = 'api_keys',
|
||||
DATA_VIEWS = 'dataViews',
|
||||
REPORTING = 'reporting',
|
||||
CONNECTORS = 'triggersActionsConnectors',
|
||||
RULES = 'triggersActions',
|
||||
MAINTENANCE_WINDOWS = 'maintenanceWindows',
|
||||
}
|
||||
|
||||
// Create new type that is a union of all the appId values
|
||||
export type AppId = `${appIds}`;
|
||||
|
||||
export const appCategories = {
|
||||
DATA: 'data',
|
||||
CONTENT: 'content',
|
||||
OTHER: 'other',
|
||||
};
|
||||
|
||||
export const appDefinitions: Record<AppId, AppDefinition> = {
|
||||
[appIds.INGEST_PIPELINES]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate(
|
||||
'management.landing.withCardNavigation.ingestPipelinesDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Use pipelines to remove or transform fields, extract values from text, and enrich your data before indexing.',
|
||||
}
|
||||
),
|
||||
icon: <EuiIcon size="l" type="logstashInput" />,
|
||||
},
|
||||
[appIds.PIPELINES]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate('management.landing.withCardNavigation.ingestDescription', {
|
||||
defaultMessage: 'Manage Logstash event processing and see the result visually.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="logstashQueue" />,
|
||||
},
|
||||
[appIds.INDEX_MANAGEMENT]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate(
|
||||
'management.landing.withCardNavigation.indexmanagementDescription',
|
||||
{
|
||||
defaultMessage: 'Update your Elasticsearch indices individually or in bulk.',
|
||||
}
|
||||
),
|
||||
icon: <EuiIcon size="l" type="indexSettings" />,
|
||||
},
|
||||
[appIds.TRANSFORM]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate('management.landing.withCardNavigation.transformDescription', {
|
||||
defaultMessage:
|
||||
'Transforms pivot indices into summarized, entity-centric indices, or create an indexed view of the latest documents.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="indexFlush" />,
|
||||
},
|
||||
[appIds.ML]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate('management.landing.withCardNavigation.mlDescription', {
|
||||
defaultMessage:
|
||||
'View, export, and import machine learning analytics and anomaly detection items.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="indexMapping" />,
|
||||
},
|
||||
[appIds.DATA_VIEW]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate('management.landing.withCardNavigation.dataViewsDescription', {
|
||||
defaultMessage:
|
||||
'Create and manage the data views that help you retrieve your data from Elasticsearch.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="indexEdit" />,
|
||||
},
|
||||
[appIds.SAVED_OBJECTS]: {
|
||||
category: appCategories.CONTENT,
|
||||
description: i18n.translate('management.landing.withCardNavigation.objectsDescription', {
|
||||
defaultMessage:
|
||||
'Manage and share your saved objects. To edit the underlying data of an object, go to its associated application.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="save" />,
|
||||
},
|
||||
[appIds.TAGS]: {
|
||||
category: appCategories.CONTENT,
|
||||
description: i18n.translate('management.landing.withCardNavigation.tagsDescription', {
|
||||
defaultMessage: 'Use tags to categorize and easily find your objects.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="tag" />,
|
||||
},
|
||||
[appIds.FILES_MANAGEMENT]: {
|
||||
category: appCategories.CONTENT,
|
||||
description: i18n.translate('management.landing.withCardNavigation.fileManagementDescription', {
|
||||
defaultMessage: 'Any files created will be listed here.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="documents" />,
|
||||
},
|
||||
[appIds.API_KEYS]: {
|
||||
category: appCategories.OTHER,
|
||||
description: i18n.translate('management.landing.withCardNavigation.apiKeysDescription', {
|
||||
defaultMessage: 'Allow applications to access Elastic on your behalf.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="lockOpen" />,
|
||||
},
|
||||
[appIds.DATA_VIEWS]: {
|
||||
category: appCategories.DATA,
|
||||
description: i18n.translate('management.landing.withCardNavigation.dataViewsDescription', {
|
||||
defaultMessage:
|
||||
'Create and manage the data views that help you retrieve your data from Elasticsearch.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="indexEdit" />,
|
||||
},
|
||||
[appIds.CONNECTORS]: {
|
||||
category: appCategories.OTHER,
|
||||
description: i18n.translate('management.landing.withCardNavigation.connectorsDescription', {
|
||||
defaultMessage: 'Connect third-party software with your alerting data.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="desktop" />,
|
||||
},
|
||||
[appIds.RULES]: {
|
||||
category: appCategories.OTHER,
|
||||
description: i18n.translate('management.landing.withCardNavigation.rulesDescription', {
|
||||
defaultMessage: 'Detect conditions using rules.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="editorChecklist" />,
|
||||
},
|
||||
[appIds.MAINTENANCE_WINDOWS]: {
|
||||
category: appCategories.OTHER,
|
||||
description: i18n.translate(
|
||||
'management.landing.withCardNavigation.maintenanceWindowsDescription',
|
||||
{
|
||||
defaultMessage: 'Suppress rule notifications for scheduled periods of time.',
|
||||
}
|
||||
),
|
||||
icon: <EuiIcon size="l" type="wrench" />,
|
||||
},
|
||||
[appIds.REPORTING]: {
|
||||
category: appCategories.CONTENT,
|
||||
description: i18n.translate('management.landing.withCardNavigation.reportingDescription', {
|
||||
defaultMessage: 'Get reports generated in applications.',
|
||||
}),
|
||||
icon: <EuiIcon size="l" type="visPie" />,
|
||||
},
|
||||
};
|
||||
|
||||
// Compose a list of app ids that belong to a given category
|
||||
export const getAppIdsByCategory = (category: string) => {
|
||||
const appKeys = Object.keys(appDefinitions) as AppId[];
|
||||
return appKeys.filter((appId: AppId) => {
|
||||
return appDefinitions[appId].category === category;
|
||||
});
|
||||
};
|
13
packages/kbn-management/cards_navigation/src/index.ts
Normal file
13
packages/kbn-management/cards_navigation/src/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type { CardsNavigationComponentProps } from './types';
|
||||
export type { AppId } from './consts';
|
||||
|
||||
export { appIds } from './consts';
|
||||
export { CardsNavigation } from './cards_navigation';
|
42
packages/kbn-management/cards_navigation/src/types.ts
Normal file
42
packages/kbn-management/cards_navigation/src/types.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import type { AppId } from './consts';
|
||||
|
||||
export interface Application {
|
||||
id: string;
|
||||
title: string;
|
||||
basePath: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface AppRegistrySections {
|
||||
id?: string;
|
||||
title?: string;
|
||||
apps: Application[];
|
||||
}
|
||||
|
||||
export interface CardsNavigationComponentProps {
|
||||
sections: AppRegistrySections[];
|
||||
appBasePath: string;
|
||||
onCardClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
|
||||
hideLinksTo?: AppId[];
|
||||
}
|
||||
|
||||
export interface ManagementAppProps {
|
||||
id: string;
|
||||
title: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface AppDefinition {
|
||||
category: string;
|
||||
description: string;
|
||||
icon: React.ReactElement;
|
||||
}
|
||||
|
||||
export type AppProps = ManagementAppProps & AppDefinition;
|
22
packages/kbn-management/cards_navigation/tsconfig.json
Normal file
22
packages/kbn-management/cards_navigation/tsconfig.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
"@kbn/ambient-ui-types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/i18n",
|
||||
]
|
||||
}
|
5
packages/kbn-management/storybook/config/README.mdx
Normal file
5
packages/kbn-management/storybook/config/README.mdx
Normal file
|
@ -0,0 +1,5 @@
|
|||
# kbn-management storybook config
|
||||
|
||||
This directory contains the configuration for the Storybook deployment for all kbn-management component packages.
|
||||
|
||||
For more information, refer to the [Storybook documentation](https://storybook.js.org/docs/react/configure/overview) and the `@kbn/storybook` package.
|
13
packages/kbn-management/storybook/config/constants.ts
Normal file
13
packages/kbn-management/storybook/config/constants.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/** The title of the Storybook. */
|
||||
export const TITLE = 'kbn-management storybook';
|
||||
|
||||
/** The remote URL of the root from which Storybook loads stories for kbn-management. */
|
||||
export const URL = 'https://github.com/elastic/kibana/tree/main/packages/kbn-management';
|
9
packages/kbn-management/storybook/config/index.ts
Executable file
9
packages/kbn-management/storybook/config/index.ts
Executable file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { TITLE, URL } from './constants';
|
6
packages/kbn-management/storybook/config/kibana.jsonc
Normal file
6
packages/kbn-management/storybook/config/kibana.jsonc
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/management-storybook-config",
|
||||
"owner": "@elastic/platform-deployment-management",
|
||||
"devOnly": true
|
||||
}
|
17
packages/kbn-management/storybook/config/main.ts
Normal file
17
packages/kbn-management/storybook/config/main.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { defaultConfig } from '@kbn/storybook';
|
||||
|
||||
module.exports = {
|
||||
...defaultConfig,
|
||||
stories: ['../../**/*.stories.+(tsx|mdx)'],
|
||||
reactOptions: {
|
||||
strictMode: true,
|
||||
},
|
||||
};
|
23
packages/kbn-management/storybook/config/manager.ts
Normal file
23
packages/kbn-management/storybook/config/manager.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { addons } from '@storybook/addons';
|
||||
import { create } from '@storybook/theming';
|
||||
import { PANEL_ID as selectedPanel } from '@storybook/addon-actions';
|
||||
|
||||
import { TITLE as brandTitle, URL as brandUrl } from './constants';
|
||||
|
||||
addons.setConfig({
|
||||
theme: create({
|
||||
base: 'light',
|
||||
brandTitle,
|
||||
brandUrl,
|
||||
}),
|
||||
selectedPanel,
|
||||
showPanel: true.valueOf,
|
||||
});
|
6
packages/kbn-management/storybook/config/package.json
Normal file
6
packages/kbn-management/storybook/config/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/management-storybook-config",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
22
packages/kbn-management/storybook/config/preview.ts
Normal file
22
packages/kbn-management/storybook/config/preview.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-namespace,@typescript-eslint/no-empty-interface */
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Global {}
|
||||
interface InspectOptions {}
|
||||
type ConsoleConstructor = console.ConsoleConstructor;
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-enable */
|
||||
import jest from 'jest-mock';
|
||||
|
||||
/* @ts-expect-error TS doesn't see jest as a property of window, and I don't want to edit our global config. */
|
||||
window.jest = jest;
|
19
packages/kbn-management/storybook/config/tsconfig.json
Normal file
19
packages/kbn-management/storybook/config/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/storybook"
|
||||
]
|
||||
}
|
|
@ -46,6 +46,7 @@ export const storybookAliases = {
|
|||
infra: 'x-pack/plugins/infra/.storybook',
|
||||
kibana_react: 'src/plugins/kibana_react/.storybook',
|
||||
lists: 'x-pack/plugins/lists/.storybook',
|
||||
management: 'packages/kbn-management/storybook/config',
|
||||
observability: 'x-pack/plugins/observability/.storybook',
|
||||
presentation: 'src/plugins/presentation_util/storybook',
|
||||
random_sampling: 'x-pack/packages/kbn-random-sampling/.storybook',
|
||||
|
|
|
@ -1,5 +1,41 @@
|
|||
# Management Plugin
|
||||
|
||||
This plugins contains the "Stack Management" page framework. It offers navigation and an API
|
||||
to link individual managment section into it. This plugin does not contain any individual
|
||||
management section itself.
|
||||
to link individual management section into it. This plugin does not contain any individual
|
||||
management section itself.
|
||||
|
||||
## Cards navigation
|
||||
|
||||
This plugin offers a special version of its landing page with a special feature called "cards navigation".
|
||||
This feature can be enabled by calling the `setupCardsNavigation` method from the `management` plugin from
|
||||
your plugin's `setup` method:
|
||||
|
||||
```
|
||||
management.setupCardsNavigation({ enabled: true });
|
||||
```
|
||||
|
||||
The cards that will be shown are defined in the `packages/kbn-management/cards_navigation/src/consts.tsx` file
|
||||
and they are grouped into categories. These cards are computed based on the `SectionsService` that is provided
|
||||
in the `management` plugin.
|
||||
|
||||
### Adding a new card to the navigation
|
||||
|
||||
For adding a new item to the navigation all you have to do is edit the `packages/kbn-management/cards_navigation/src/consts.tsx`
|
||||
file and add two things:
|
||||
|
||||
* Add the app id into the `appIds` enum (make sure that the app_id value matches the one from the plugin)
|
||||
* Add a new entry to the `appDefinitions` object. In here you can specify the category where you want it to be, icon and description.
|
||||
|
||||
|
||||
### Removing an item from the navigation
|
||||
|
||||
If card needs to be hidden from the navigation you can specify that by using the `hideLinksTo` prop:
|
||||
|
||||
```
|
||||
management.setupCardsNavigation({
|
||||
enabled: true,
|
||||
hideLinksTo: [appIds.MAINTENANCE_WINDOWS],
|
||||
});
|
||||
```
|
||||
|
||||
More specifics about the `setupCardsNavigation` can be found in `packages/kbn-management/cards_navigation/readme.mdx`.
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { merge } from 'lodash';
|
||||
import { registerTestBed, AsyncTestBedConfig, TestBed } from '@kbn/test-jest-helpers';
|
||||
|
||||
import { AppContextProvider } from '../management_app/management_context';
|
||||
import { ManagementLandingPage } from './landing';
|
||||
|
||||
const sectionsMock = [
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
apps: [
|
||||
{
|
||||
id: 'ingest_pipelines',
|
||||
title: 'Ingest pipelines',
|
||||
enabled: true,
|
||||
basePath: '/app/management/ingest/pipelines',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const testBedConfig: AsyncTestBedConfig = {
|
||||
memoryRouter: {
|
||||
initialEntries: [`/management_landing`],
|
||||
componentRoutePath: '/management_landing',
|
||||
},
|
||||
doMountAsync: true,
|
||||
};
|
||||
|
||||
export const WithAppDependencies =
|
||||
(Comp: any, overrides: Record<string, unknown> = {}) =>
|
||||
(props: Record<string, unknown>) => {
|
||||
const contextDependencies = {
|
||||
appBasePath: 'http://localhost:9001',
|
||||
kibanaVersion: '8.10.0',
|
||||
cardsNavigationConfig: { enabled: true },
|
||||
sections: sectionsMock,
|
||||
};
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<AppContextProvider value={merge(contextDependencies, overrides)}>
|
||||
<Comp {...props} setBreadcrumbs={jest.fn()} onAppMounted={jest.fn()} />
|
||||
</AppContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const setupLandingPage = async (overrides?: Record<string, unknown>): Promise<TestBed> => {
|
||||
const initTestBed = registerTestBed(
|
||||
WithAppDependencies(ManagementLandingPage, overrides),
|
||||
testBedConfig
|
||||
);
|
||||
const testBed = await initTestBed();
|
||||
|
||||
return {
|
||||
...testBed,
|
||||
};
|
||||
};
|
||||
|
||||
describe('Landing Page', () => {
|
||||
let testBed: TestBed;
|
||||
|
||||
describe('Can be configured through cardsNavigationConfig', () => {
|
||||
beforeEach(async () => {
|
||||
testBed = await setupLandingPage();
|
||||
});
|
||||
|
||||
test('Shows cards navigation when feature is enabled', async () => {
|
||||
const { exists } = testBed;
|
||||
expect(exists('cards-navigation-page')).toBe(true);
|
||||
});
|
||||
|
||||
test('Hide cards navigation when feature is disabled', async () => {
|
||||
testBed = await setupLandingPage({ cardsNavigationConfig: { enabled: false } });
|
||||
const { exists } = testBed;
|
||||
|
||||
expect(exists('cards-navigation-page')).toBe(false);
|
||||
expect(exists('managementHome')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -10,24 +10,38 @@ import React, { useEffect } from 'react';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiHorizontalRule } from '@elastic/eui';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { EuiPageBody } from '@elastic/eui';
|
||||
import { CardsNavigation } from '@kbn/management-cards-navigation';
|
||||
import { useAppContext } from '../management_app/management_context';
|
||||
|
||||
interface ManagementLandingPageProps {
|
||||
version: string;
|
||||
onAppMounted: (id: string) => void;
|
||||
setBreadcrumbs: () => void;
|
||||
}
|
||||
|
||||
export const ManagementLandingPage = ({
|
||||
version,
|
||||
setBreadcrumbs,
|
||||
onAppMounted,
|
||||
}: ManagementLandingPageProps) => {
|
||||
const { appBasePath, sections, kibanaVersion, cardsNavigationConfig } = useAppContext();
|
||||
setBreadcrumbs();
|
||||
|
||||
useEffect(() => {
|
||||
onAppMounted('');
|
||||
}, [onAppMounted]);
|
||||
|
||||
if (cardsNavigationConfig?.enabled) {
|
||||
return (
|
||||
<EuiPageBody restrictWidth={true} data-test-subj="cards-navigation-page">
|
||||
<CardsNavigation
|
||||
sections={sections}
|
||||
appBasePath={appBasePath}
|
||||
hideLinksTo={cardsNavigationConfig?.hideLinksTo}
|
||||
/>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<KibanaPageTemplate.EmptyPrompt
|
||||
data-test-subj="managementHome"
|
||||
|
@ -37,7 +51,7 @@ export const ManagementLandingPage = ({
|
|||
<FormattedMessage
|
||||
id="management.landing.header"
|
||||
defaultMessage="Welcome to Stack Management {version}"
|
||||
values={{ version }}
|
||||
values={{ version: kibanaVersion }}
|
||||
/>
|
||||
</h1>
|
||||
}
|
||||
|
|
|
@ -13,10 +13,13 @@ import { BehaviorSubject } from 'rxjs';
|
|||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from '@kbn/core/public';
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
|
||||
import { reactRouterNavigate, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { KibanaPageTemplate, KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { AppContextProvider } from './management_context';
|
||||
import {
|
||||
ManagementSection,
|
||||
MANAGEMENT_BREADCRUMB,
|
||||
|
@ -24,7 +27,7 @@ import {
|
|||
} from '../../utils';
|
||||
import { ManagementRouter } from './management_router';
|
||||
import { managementSidebarNav } from '../management_sidebar_nav/management_sidebar_nav';
|
||||
import { SectionsServiceStart } from '../../types';
|
||||
import { SectionsServiceStart, NavigationCardsSubject } from '../../types';
|
||||
|
||||
interface ManagementAppProps {
|
||||
appBasePath: string;
|
||||
|
@ -36,15 +39,23 @@ interface ManagementAppProps {
|
|||
export interface ManagementAppDependencies {
|
||||
sections: SectionsServiceStart;
|
||||
kibanaVersion: string;
|
||||
coreStart: CoreStart;
|
||||
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void;
|
||||
isSidebarEnabled$: BehaviorSubject<boolean>;
|
||||
cardsNavigationConfig$: BehaviorSubject<NavigationCardsSubject>;
|
||||
}
|
||||
|
||||
export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppProps) => {
|
||||
const { setBreadcrumbs, isSidebarEnabled$ } = dependencies;
|
||||
export const ManagementApp = ({
|
||||
dependencies,
|
||||
history,
|
||||
theme$,
|
||||
appBasePath,
|
||||
}: ManagementAppProps) => {
|
||||
const { setBreadcrumbs, isSidebarEnabled$, cardsNavigationConfig$ } = dependencies;
|
||||
const [selectedId, setSelectedId] = useState<string>('');
|
||||
const [sections, setSections] = useState<ManagementSection[]>();
|
||||
const isSidebarEnabled = useObservable(isSidebarEnabled$);
|
||||
const cardsNavigationConfig = useObservable(cardsNavigationConfig$);
|
||||
|
||||
const onAppMounted = useCallback((id: string) => {
|
||||
setSelectedId(id);
|
||||
|
@ -95,26 +106,36 @@ export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppPr
|
|||
}
|
||||
: undefined;
|
||||
|
||||
const contextDependencies = {
|
||||
appBasePath,
|
||||
sections,
|
||||
cardsNavigationConfig,
|
||||
kibanaVersion: dependencies.kibanaVersion,
|
||||
};
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<KibanaThemeProvider theme$={theme$}>
|
||||
<KibanaPageTemplate
|
||||
restrictWidth={false}
|
||||
solutionNav={solution}
|
||||
// @ts-expect-error Techincally `paddingSize` isn't supported but it is passed through,
|
||||
// this is a stop-gap for Stack managmement specifically until page components can be converted to template components
|
||||
mainProps={{ paddingSize: 'l' }}
|
||||
>
|
||||
<ManagementRouter
|
||||
history={history}
|
||||
theme$={theme$}
|
||||
setBreadcrumbs={setBreadcrumbsScoped}
|
||||
onAppMounted={onAppMounted}
|
||||
sections={sections}
|
||||
dependencies={dependencies}
|
||||
/>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaThemeProvider>
|
||||
</I18nProvider>
|
||||
<RedirectAppLinks coreStart={dependencies.coreStart}>
|
||||
<I18nProvider>
|
||||
<AppContextProvider value={contextDependencies}>
|
||||
<KibanaThemeProvider theme$={theme$}>
|
||||
<KibanaPageTemplate
|
||||
restrictWidth={false}
|
||||
solutionNav={solution}
|
||||
// @ts-expect-error Techincally `paddingSize` isn't supported but it is passed through,
|
||||
// this is a stop-gap for Stack managmement specifically until page components can be converted to template components
|
||||
mainProps={{ paddingSize: 'l' }}
|
||||
>
|
||||
<ManagementRouter
|
||||
history={history}
|
||||
theme$={theme$}
|
||||
setBreadcrumbs={setBreadcrumbsScoped}
|
||||
onAppMounted={onAppMounted}
|
||||
sections={sections}
|
||||
/>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaThemeProvider>
|
||||
</AppContextProvider>
|
||||
</I18nProvider>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { AppDependencies } from '../../types';
|
||||
|
||||
export const AppContext = createContext<AppDependencies | undefined>(undefined);
|
||||
|
||||
export const AppContextProvider = ({
|
||||
children,
|
||||
value,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
value: AppDependencies;
|
||||
}) => {
|
||||
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAppContext = () => {
|
||||
const ctx = useContext(AppContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppContext must be called from inside AppContext');
|
||||
}
|
||||
return ctx;
|
||||
};
|
|
@ -12,27 +12,18 @@ import { Router, Routes, Route } from '@kbn/shared-ux-router';
|
|||
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from '@kbn/core/public';
|
||||
import { ManagementAppWrapper } from '../management_app_wrapper';
|
||||
import { ManagementLandingPage } from '../landing';
|
||||
import { ManagementAppDependencies } from './management_app';
|
||||
import { ManagementSection } from '../../utils';
|
||||
|
||||
interface ManagementRouterProps {
|
||||
history: AppMountParameters['history'];
|
||||
theme$: AppMountParameters['theme$'];
|
||||
dependencies: ManagementAppDependencies;
|
||||
setBreadcrumbs: (crumbs?: ChromeBreadcrumb[], appHistory?: ScopedHistory) => void;
|
||||
onAppMounted: (id: string) => void;
|
||||
sections: ManagementSection[];
|
||||
}
|
||||
|
||||
export const ManagementRouter = memo(
|
||||
({
|
||||
dependencies,
|
||||
history,
|
||||
setBreadcrumbs,
|
||||
onAppMounted,
|
||||
sections,
|
||||
theme$,
|
||||
}: ManagementRouterProps) => (
|
||||
({ history, setBreadcrumbs, onAppMounted, sections, theme$ }: ManagementRouterProps) => (
|
||||
<Router history={history}>
|
||||
<Routes>
|
||||
{sections.map((section) =>
|
||||
|
@ -62,11 +53,7 @@ export const ManagementRouter = memo(
|
|||
<Route
|
||||
path={'/'}
|
||||
component={() => (
|
||||
<ManagementLandingPage
|
||||
version={dependencies.kibanaVersion}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
onAppMounted={onAppMounted}
|
||||
/>
|
||||
<ManagementLandingPage setBreadcrumbs={setBreadcrumbs} onAppMounted={onAppMounted} />
|
||||
)}
|
||||
/>
|
||||
</Routes>
|
||||
|
|
|
@ -43,6 +43,7 @@ const createSetupContract = (): ManagementSetup => ({
|
|||
|
||||
const createStartContract = (): ManagementStart => ({
|
||||
setIsSidebarEnabled: jest.fn(),
|
||||
setupCardsNavigation: jest.fn(),
|
||||
});
|
||||
|
||||
export const managementPluginMock = {
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
AppNavLinkStatus,
|
||||
AppDeepLink,
|
||||
} from '@kbn/core/public';
|
||||
import { ManagementSetup, ManagementStart } from './types';
|
||||
import { ManagementSetup, ManagementStart, NavigationCardsSubject } from './types';
|
||||
|
||||
import { MANAGEMENT_APP_ID } from '../common/contants';
|
||||
import { ManagementAppLocatorDefinition } from '../common/locator';
|
||||
|
@ -72,6 +72,10 @@ export class ManagementPlugin
|
|||
private hasAnyEnabledApps = true;
|
||||
|
||||
private isSidebarEnabled$ = new BehaviorSubject<boolean>(true);
|
||||
private cardsNavigationConfig$ = new BehaviorSubject<NavigationCardsSubject>({
|
||||
enabled: false,
|
||||
hideLinksTo: [],
|
||||
});
|
||||
|
||||
constructor(private initializerContext: PluginInitializerContext) {}
|
||||
|
||||
|
@ -116,8 +120,10 @@ export class ManagementPlugin
|
|||
return renderApp(params, {
|
||||
sections: getSectionsServiceStartPrivate(),
|
||||
kibanaVersion,
|
||||
coreStart,
|
||||
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
|
||||
isSidebarEnabled$: managementPlugin.isSidebarEnabled$,
|
||||
cardsNavigationConfig$: managementPlugin.cardsNavigationConfig$,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -146,6 +152,8 @@ export class ManagementPlugin
|
|||
return {
|
||||
setIsSidebarEnabled: (isSidebarEnabled: boolean) =>
|
||||
this.isSidebarEnabled$.next(isSidebarEnabled),
|
||||
setupCardsNavigation: ({ enabled, hideLinksTo }) =>
|
||||
this.cardsNavigationConfig$.next({ enabled, hideLinksTo }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { Observable } from 'rxjs';
|
|||
import { ScopedHistory, Capabilities } from '@kbn/core/public';
|
||||
import type { LocatorPublic } from '@kbn/share-plugin/common';
|
||||
import { ChromeBreadcrumb, CoreTheme } from '@kbn/core/public';
|
||||
import type { AppId } from '@kbn/management-cards-navigation';
|
||||
import { ManagementSection, RegisterManagementSectionArgs } from './utils';
|
||||
import type { ManagementAppLocatorParams } from '../common/locator';
|
||||
|
||||
|
@ -29,6 +30,7 @@ export interface DefinedSections {
|
|||
|
||||
export interface ManagementStart {
|
||||
setIsSidebarEnabled: (enabled: boolean) => void;
|
||||
setupCardsNavigation: ({ enabled, hideLinksTo }: NavigationCardsSubject) => void;
|
||||
}
|
||||
|
||||
export interface ManagementSectionsStartPrivate {
|
||||
|
@ -78,3 +80,15 @@ export interface CreateManagementItemArgs {
|
|||
capabilitiesId?: string; // overrides app id
|
||||
redirectFrom?: string; // redirects from an old app id to the current app id
|
||||
}
|
||||
|
||||
export interface NavigationCardsSubject {
|
||||
enabled: boolean;
|
||||
hideLinksTo?: AppId[];
|
||||
}
|
||||
|
||||
export interface AppDependencies {
|
||||
appBasePath: string;
|
||||
kibanaVersion: string;
|
||||
sections: ManagementSection[];
|
||||
cardsNavigationConfig?: NavigationCardsSubject;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
"@kbn/i18n",
|
||||
"@kbn/i18n-react",
|
||||
"@kbn/shared-ux-page-kibana-template",
|
||||
"@kbn/shared-ux-router"
|
||||
"@kbn/shared-ux-router",
|
||||
"@kbn/management-cards-navigation",
|
||||
"@kbn/shared-ux-link-redirect-app",
|
||||
"@kbn/test-jest-helpers"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -936,8 +936,12 @@
|
|||
"@kbn/managed-vscode-config/*": ["packages/kbn-managed-vscode-config/*"],
|
||||
"@kbn/managed-vscode-config-cli": ["packages/kbn-managed-vscode-config-cli"],
|
||||
"@kbn/managed-vscode-config-cli/*": ["packages/kbn-managed-vscode-config-cli/*"],
|
||||
"@kbn/management-cards-navigation": ["packages/kbn-management/cards_navigation"],
|
||||
"@kbn/management-cards-navigation/*": ["packages/kbn-management/cards_navigation/*"],
|
||||
"@kbn/management-plugin": ["src/plugins/management"],
|
||||
"@kbn/management-plugin/*": ["src/plugins/management/*"],
|
||||
"@kbn/management-storybook-config": ["packages/kbn-management/storybook/config"],
|
||||
"@kbn/management-storybook-config/*": ["packages/kbn-management/storybook/config/*"],
|
||||
"@kbn/management-test-plugin": ["test/plugin_functional/plugins/management_test_plugin"],
|
||||
"@kbn/management-test-plugin/*": ["test/plugin_functional/plugins/management_test_plugin/*"],
|
||||
"@kbn/mapbox-gl": ["packages/kbn-mapbox-gl"],
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"server": true,
|
||||
"browser": true,
|
||||
"configPath": ["xpack", "serverless", "observability"],
|
||||
"requiredPlugins": ["serverless", "observabilityShared", "kibanaReact"],
|
||||
"requiredPlugins": ["serverless", "observabilityShared", "kibanaReact", "management"],
|
||||
"optionalPlugins": [],
|
||||
"requiredBundles": []
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import { appIds } from '@kbn/management-cards-navigation';
|
||||
import { getObservabilitySideNavComponent } from './components/side_navigation';
|
||||
import {
|
||||
ServerlessObservabilityPluginSetup,
|
||||
|
@ -28,10 +29,14 @@ export class ServerlessObservabilityPlugin
|
|||
core: CoreStart,
|
||||
setupDeps: ServerlessObservabilityPluginStartDependencies
|
||||
): ServerlessObservabilityPluginStart {
|
||||
const { observabilityShared, serverless } = setupDeps;
|
||||
const { observabilityShared, serverless, management } = setupDeps;
|
||||
observabilityShared.setIsSidebarEnabled(false);
|
||||
serverless.setProjectHome('/app/observability/landing');
|
||||
serverless.setSideNavComponent(getObservabilitySideNavComponent(core, { serverless }));
|
||||
management.setupCardsNavigation({
|
||||
enabled: true,
|
||||
hideLinksTo: [appIds.RULES],
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
ObservabilitySharedPluginSetup,
|
||||
ObservabilitySharedPluginStart,
|
||||
} from '@kbn/observability-shared-plugin/public';
|
||||
import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface ServerlessObservabilityPluginSetup {}
|
||||
|
@ -20,9 +21,11 @@ export interface ServerlessObservabilityPluginStart {}
|
|||
export interface ServerlessObservabilityPluginSetupDependencies {
|
||||
observabilityShared: ObservabilitySharedPluginSetup;
|
||||
serverless: ServerlessPluginSetup;
|
||||
management: ManagementSetup;
|
||||
}
|
||||
|
||||
export interface ServerlessObservabilityPluginStartDependencies {
|
||||
observabilityShared: ObservabilitySharedPluginStart;
|
||||
serverless: ServerlessPluginStart;
|
||||
management: ManagementStart;
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/management-plugin",
|
||||
"@kbn/serverless",
|
||||
"@kbn/observability-shared-plugin",
|
||||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/shared-ux-chrome-navigation",
|
||||
"@kbn/i18n",
|
||||
"@kbn/management-cards-navigation",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { appIds } from '@kbn/management-cards-navigation';
|
||||
import { createServerlessSearchSideNavComponent as createComponent } from './layout/nav';
|
||||
import { docLinks } from '../common/doc_links';
|
||||
import {
|
||||
|
@ -68,10 +69,14 @@ export class ServerlessSearchPlugin
|
|||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
{ serverless }: ServerlessSearchPluginStartDependencies
|
||||
{ serverless, management }: ServerlessSearchPluginStartDependencies
|
||||
): ServerlessSearchPluginStart {
|
||||
serverless.setProjectHome('/app/elasticsearch');
|
||||
serverless.setSideNavComponent(createComponent(core, { serverless }));
|
||||
management.setupCardsNavigation({
|
||||
enabled: true,
|
||||
hideLinksTo: [appIds.MAINTENANCE_WINDOWS],
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"@kbn/security-plugin",
|
||||
"@kbn/cloud-plugin",
|
||||
"@kbn/share-plugin",
|
||||
"@kbn/management-cards-navigation",
|
||||
"@kbn/core-elasticsearch-server",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4630,10 +4630,18 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/management-cards-navigation@link:packages/kbn-management/cards_navigation":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/management-plugin@link:src/plugins/management":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/management-storybook-config@link:packages/kbn-management/storybook/config":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/management-test-plugin@link:test/plugin_functional/plugins/management_test_plugin":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue