Skip to content

Commit dc32112

Browse files
[Fleet + Integrations UI] Migrate Fleet UI to new tabbed layout (#101828)
* WIP: Migrate fleet to new page layout system * Add 'Add Agent' button to agents table * Fix flyout import in search and filter bar * Place settings/feedback in header * Move actions to top nav * Fix i18n + types + unit test failures * Remove unused props in DefaultLayout * Fix background height in Fleet layout This is fixed through a hack for now, because Kibana's layout doesn't allow apps to flex the top-level wrapper via `flex: 1`. The same behavior reported in the original issue (#101781) is present in all other Kibana apps. Fixes #101781 * Use euiHeaderHeightCompensation for min-height calc * Move settings portal to app component * Fix agent details URL in failing unit test * Remove unreferenced overview files + update functional tests * Remove unneeded fragment * Remove beta badges in Fleet + Integrations Fixes #100731 * Fix i18n * Fix page path reference * Fix failing tests * Re-fix i18n post merge Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> # Conflicts: # x-pack/plugins/translations/translations/ja-JP.json # x-pack/plugins/translations/translations/zh-CN.json
1 parent 243b167 commit dc32112

44 files changed

Lines changed: 421 additions & 1242 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

x-pack/plugins/fleet/kibana.json

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,8 @@
44
"server": true,
55
"ui": true,
66
"configPath": ["xpack", "fleet"],
7-
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects"],
8-
"optionalPlugins": [
9-
"security",
10-
"features",
11-
"cloud",
12-
"usageCollection",
13-
"home"
14-
],
7+
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation"],
8+
"optionalPlugins": ["security", "features", "cloud", "usageCollection", "home"],
159
"extraPublicDirs": ["common"],
1610
"requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils"]
1711
}

x-pack/plugins/fleet/public/applications/fleet/app.tsx

Lines changed: 88 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import React, { memo, useEffect, useState } from 'react';
99
import type { AppMountParameters } from 'kibana/public';
10-
import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel } from '@elastic/eui';
10+
import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui';
1111
import type { History } from 'history';
1212
import { createHashHistory } from 'history';
1313
import { Router, Redirect, Route, Switch } from 'react-router-dom';
@@ -16,11 +16,13 @@ import { i18n } from '@kbn/i18n';
1616
import styled from 'styled-components';
1717
import useObservable from 'react-use/lib/useObservable';
1818

19+
import type { TopNavMenuData } from 'src/plugins/navigation/public';
20+
1921
import type { FleetConfigType, FleetStartServices } from '../../plugin';
2022
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
2123
import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
2224

23-
import { PackageInstallProvider } from '../integrations/hooks';
25+
import { PackageInstallProvider, useUrlModal } from '../integrations/hooks';
2426

2527
import {
2628
ConfigContext,
@@ -30,25 +32,25 @@ import {
3032
sendGetPermissionsCheck,
3133
sendSetup,
3234
useBreadcrumbs,
33-
useConfig,
3435
useStartServices,
3536
UIExtensionsContext,
3637
} from './hooks';
37-
import { Error, Loading } from './components';
38+
import { Error, Loading, SettingFlyout } from './components';
3839
import type { UIExtensionsStorage } from './types';
3940

4041
import { FLEET_ROUTING_PATHS } from './constants';
4142
import { DefaultLayout, WithoutHeaderLayout } from './layouts';
4243
import { AgentPolicyApp } from './sections/agent_policy';
4344
import { DataStreamApp } from './sections/data_stream';
44-
import { FleetApp } from './sections/agents';
45-
import { IngestManagerOverview } from './sections/overview';
46-
import { ProtectedRoute } from './index';
45+
import { AgentsApp } from './sections/agents';
4746
import { CreatePackagePolicyPage } from './sections/agent_policy/create_package_policy_page';
47+
import { EnrollmentTokenListPage } from './sections/agents/enrollment_token_list_page';
48+
49+
const FEEDBACK_URL = 'https://ela.st/fleet-feedback';
4850

4951
const ErrorLayout = ({ children }: { children: JSX.Element }) => (
5052
<EuiErrorBoundary>
51-
<DefaultLayout showSettings={false}>
53+
<DefaultLayout>
5254
<WithoutHeaderLayout>{children}</WithoutHeaderLayout>
5355
</DefaultLayout>
5456
</EuiErrorBoundary>
@@ -233,37 +235,82 @@ export const FleetAppContext: React.FC<{
233235
}
234236
);
235237

236-
export const AppRoutes = memo(() => {
237-
const { agents } = useConfig();
238+
const FleetTopNav = memo(
239+
({ setHeaderActionMenu }: { setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'] }) => {
240+
const { getModalHref } = useUrlModal();
241+
const services = useStartServices();
238242

239-
return (
240-
<Switch>
241-
<Route path={FLEET_ROUTING_PATHS.policies}>
242-
<DefaultLayout section="agent_policy">
243-
<AgentPolicyApp />
244-
</DefaultLayout>
245-
</Route>
246-
<Route path={FLEET_ROUTING_PATHS.data_streams}>
247-
<DefaultLayout section="data_stream">
248-
<DataStreamApp />
249-
</DefaultLayout>
250-
</Route>
251-
<ProtectedRoute path={FLEET_ROUTING_PATHS.fleet} isAllowed={agents.enabled}>
252-
<DefaultLayout section="fleet">
253-
<FleetApp />
254-
</DefaultLayout>
255-
</ProtectedRoute>
256-
<Route exact path={FLEET_ROUTING_PATHS.overview}>
257-
<DefaultLayout section="overview">
258-
<IngestManagerOverview />
259-
</DefaultLayout>
260-
</Route>
261-
<Route path={FLEET_ROUTING_PATHS.add_integration_to_policy}>
262-
<DefaultLayout showNav={false}>
263-
<CreatePackagePolicyPage />
264-
</DefaultLayout>
265-
</Route>
266-
<Redirect to="/" />
267-
</Switch>
268-
);
269-
});
243+
const { TopNavMenu } = services.navigation.ui;
244+
245+
const topNavConfig: TopNavMenuData[] = [
246+
{
247+
label: i18n.translate('xpack.fleet.appNavigation.sendFeedbackButton', {
248+
defaultMessage: 'Send Feedback',
249+
}),
250+
iconType: 'popout',
251+
run: () => window.open(FEEDBACK_URL),
252+
},
253+
254+
{
255+
label: i18n.translate('xpack.fleet.appNavigation.settingsButton', {
256+
defaultMessage: 'Fleet settings',
257+
}),
258+
iconType: 'gear',
259+
run: () => (window.location.href = getModalHref('settings')),
260+
},
261+
];
262+
return (
263+
<TopNavMenu
264+
appName={i18n.translate('xpack.fleet.appTitle', { defaultMessage: 'Fleet' })}
265+
config={topNavConfig}
266+
setMenuMountPoint={setHeaderActionMenu}
267+
/>
268+
);
269+
}
270+
);
271+
272+
export const AppRoutes = memo(
273+
({ setHeaderActionMenu }: { setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'] }) => {
274+
const { modal, setModal } = useUrlModal();
275+
276+
return (
277+
<>
278+
<FleetTopNav setHeaderActionMenu={setHeaderActionMenu} />
279+
280+
{modal === 'settings' && (
281+
<EuiPortal>
282+
<SettingFlyout
283+
onClose={() => {
284+
setModal(null);
285+
}}
286+
/>
287+
</EuiPortal>
288+
)}
289+
290+
<Switch>
291+
<Route path={FLEET_ROUTING_PATHS.agents}>
292+
<AgentsApp />
293+
</Route>
294+
<Route path={FLEET_ROUTING_PATHS.policies}>
295+
<AgentPolicyApp />
296+
</Route>
297+
<Route path={FLEET_ROUTING_PATHS.enrollment_tokens}>
298+
<EnrollmentTokenListPage />
299+
</Route>
300+
<Route path={FLEET_ROUTING_PATHS.data_streams}>
301+
<DataStreamApp />
302+
</Route>
303+
304+
{/* TODO: Move this route to the Integrations app */}
305+
<Route path={FLEET_ROUTING_PATHS.add_integration_to_policy}>
306+
<DefaultLayout>
307+
<CreatePackagePolicyPage />
308+
</DefaultLayout>
309+
</Route>
310+
311+
<Redirect to={FLEET_ROUTING_PATHS.agents} />
312+
</Switch>
313+
</>
314+
);
315+
}
316+
);

x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ interface AdditionalBreadcrumbOptions {
2020
type Breadcrumb = ChromeBreadcrumb & Partial<AdditionalBreadcrumbOptions>;
2121

2222
const BASE_BREADCRUMB: Breadcrumb = {
23-
href: pagePathGetters.overview()[1],
23+
href: pagePathGetters.base()[1],
2424
text: i18n.translate('xpack.fleet.breadcrumbs.appTitle', {
2525
defaultMessage: 'Fleet',
2626
}),
@@ -38,15 +38,6 @@ const breadcrumbGetters: {
3838
[key in Page]?: (values: DynamicPagePathValues) => Breadcrumb[];
3939
} = {
4040
base: () => [BASE_BREADCRUMB],
41-
overview: () => [
42-
BASE_BREADCRUMB,
43-
{
44-
text: i18n.translate('xpack.fleet.breadcrumbs.overviewPageTitle', {
45-
defaultMessage: 'Overview',
46-
}),
47-
},
48-
],
49-
5041
policies: () => [
5142
BASE_BREADCRUMB,
5243
{
@@ -122,40 +113,26 @@ const breadcrumbGetters: {
122113
}),
123114
},
124115
],
125-
fleet: () => [
126-
BASE_BREADCRUMB,
127-
{
128-
text: i18n.translate('xpack.fleet.breadcrumbs.agentsPageTitle', {
129-
defaultMessage: 'Agents',
130-
}),
131-
},
132-
],
133-
fleet_agent_list: () => [
116+
agent_list: () => [
134117
BASE_BREADCRUMB,
135118
{
136119
text: i18n.translate('xpack.fleet.breadcrumbs.agentsPageTitle', {
137120
defaultMessage: 'Agents',
138121
}),
139122
},
140123
],
141-
fleet_agent_details: ({ agentHost }) => [
124+
agent_details: ({ agentHost }) => [
142125
BASE_BREADCRUMB,
143126
{
144-
href: pagePathGetters.fleet()[1],
127+
href: pagePathGetters.agent_list({})[1],
145128
text: i18n.translate('xpack.fleet.breadcrumbs.agentsPageTitle', {
146129
defaultMessage: 'Agents',
147130
}),
148131
},
149132
{ text: agentHost },
150133
],
151-
fleet_enrollment_tokens: () => [
134+
enrollment_tokens: () => [
152135
BASE_BREADCRUMB,
153-
{
154-
href: pagePathGetters.fleet()[1],
155-
text: i18n.translate('xpack.fleet.breadcrumbs.agentsPageTitle', {
156-
defaultMessage: 'Agents',
157-
}),
158-
},
159136
{
160137
text: i18n.translate('xpack.fleet.breadcrumbs.enrollmentTokensPageTitle', {
161138
defaultMessage: 'Enrollment tokens',

x-pack/plugins/fleet/public/applications/fleet/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface FleetAppProps {
3737
history: AppMountParameters['history'];
3838
kibanaVersion: string;
3939
extensions: UIExtensionsStorage;
40+
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
4041
}
4142
const FleetApp = ({
4243
basepath,
@@ -45,6 +46,7 @@ const FleetApp = ({
4546
history,
4647
kibanaVersion,
4748
extensions,
49+
setHeaderActionMenu,
4850
}: FleetAppProps) => {
4951
return (
5052
<FleetAppContext
@@ -56,15 +58,15 @@ const FleetApp = ({
5658
extensions={extensions}
5759
>
5860
<WithPermissionsAndSetup>
59-
<AppRoutes />
61+
<AppRoutes setHeaderActionMenu={setHeaderActionMenu} />
6062
</WithPermissionsAndSetup>
6163
</FleetAppContext>
6264
);
6365
};
6466

6567
export function renderApp(
6668
startServices: FleetStartServices,
67-
{ element, appBasePath, history }: AppMountParameters,
69+
{ element, appBasePath, history, setHeaderActionMenu }: AppMountParameters,
6870
config: FleetConfigType,
6971
kibanaVersion: string,
7072
extensions: UIExtensionsStorage
@@ -77,6 +79,7 @@ export function renderApp(
7779
history={history}
7880
kibanaVersion={kibanaVersion}
7981
extensions={extensions}
82+
setHeaderActionMenu={setHeaderActionMenu}
8083
/>,
8184
element
8285
);

0 commit comments

Comments
 (0)