From d37d8ae85ce42b06aefc6398fda13d88119612b7 Mon Sep 17 00:00:00 2001 From: Elena Stoeva <59341489+ElenaStoeva@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:48:37 +0100 Subject: [PATCH] [serverless] Add Advanced Settings page (#167383) ## Summary This PR adds a Settings application component for rendering the Advanced Settings page in serverless. ### How to test: 1. Start Es with `yarn es serverless` and Kibana with `yarn serverless-{es/oblt/security}` 2. Go to Management -> Advanced Settings 3. Verify that the settings can be changed and saved. ### Advanced Settings page: Screenshot 2023-09-28 at 20 56 25 ### Added an Advanced Settings card to the Management landing page: Screenshot 2023-09-28 at 12 24 23 --------- Co-authored-by: Clint Andrew Hall Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Clint Andrew Hall --- .github/CODEOWNERS | 1 + package.json | 1 + .../cards_navigation/src/consts.tsx | 9 ++ .../settings/application/README.md | 13 ++ .../settings/application/application.tsx | 38 ++++++ .../settings/application/hooks/index.ts | 10 ++ .../settings/application/hooks/use_fields.ts | 22 ++++ .../application/hooks/use_settings.ts | 35 +++++ .../settings/application/index.tsx | 34 +++++ .../settings/application/jest.config.js | 13 ++ .../settings/application/kibana.jsonc | 5 + .../settings/application/package.json | 6 + .../settings/application/services.tsx | 121 ++++++++++++++++++ .../settings/application/tsconfig.json | 26 ++++ .../components/field_input/services.tsx | 2 +- .../settings/components/field_input/types.ts | 4 +- .../components/field_row/services.tsx | 4 +- .../settings/components/form/services.tsx | 29 ++--- .../settings/components/form/types.ts | 10 +- .../settings/utilities/mocks/settings.mock.ts | 39 +++--- .../public/{plugin.ts => plugin.tsx} | 47 ++++++- src/plugins/management/tsconfig.json | 5 +- tsconfig.base.json | 2 + .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - .../test_serverless/functional/config.base.ts | 2 +- .../common/management/advanced_settings.ts | 44 +++++-- .../observability/advanced_settings.ts | 19 ++- .../test_suites/search/advanced_settings.ts | 19 ++- .../test_suites/security/advanced_settings.ts | 19 ++- x-pack/test_serverless/tsconfig.json | 1 + yarn.lock | 4 + 33 files changed, 510 insertions(+), 83 deletions(-) create mode 100644 packages/kbn-management/settings/application/README.md create mode 100644 packages/kbn-management/settings/application/application.tsx create mode 100644 packages/kbn-management/settings/application/hooks/index.ts create mode 100644 packages/kbn-management/settings/application/hooks/use_fields.ts create mode 100644 packages/kbn-management/settings/application/hooks/use_settings.ts create mode 100644 packages/kbn-management/settings/application/index.tsx create mode 100644 packages/kbn-management/settings/application/jest.config.js create mode 100644 packages/kbn-management/settings/application/kibana.jsonc create mode 100644 packages/kbn-management/settings/application/package.json create mode 100644 packages/kbn-management/settings/application/services.tsx create mode 100644 packages/kbn-management/settings/application/tsconfig.json rename src/plugins/management/public/{plugin.ts => plugin.tsx} (76%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 908ade51321b..c0ba6f69d592 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -485,6 +485,7 @@ 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/settings/application @elastic/platform-deployment-management packages/kbn-management/settings/components/field_category @elastic/platform-deployment-management packages/kbn-management/settings/components/field_input @elastic/platform-deployment-management packages/kbn-management/settings/components/field_row @elastic/platform-deployment-management diff --git a/package.json b/package.json index 6ea5ef3486ef..5e9da822b1fc 100644 --- a/package.json +++ b/package.json @@ -507,6 +507,7 @@ "@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-settings-application": "link:packages/kbn-management/settings/application", "@kbn/management-settings-components-field-category": "link:packages/kbn-management/settings/components/field_category", "@kbn/management-settings-components-field-input": "link:packages/kbn-management/settings/components/field_input", "@kbn/management-settings-components-field-row": "link:packages/kbn-management/settings/components/field_row", diff --git a/packages/kbn-management/cards_navigation/src/consts.tsx b/packages/kbn-management/cards_navigation/src/consts.tsx index 8a26334eae8c..45deab542b0a 100644 --- a/packages/kbn-management/cards_navigation/src/consts.tsx +++ b/packages/kbn-management/cards_navigation/src/consts.tsx @@ -27,6 +27,7 @@ export enum appIds { CONNECTORS = 'triggersActionsConnectors', RULES = 'triggersActions', MAINTENANCE_WINDOWS = 'maintenanceWindows', + SERVERLESS_SETTINGS = 'settings', } // Create new type that is a union of all the appId values @@ -155,6 +156,14 @@ export const appDefinitions: Record = { }), icon: , }, + + [appIds.SERVERLESS_SETTINGS]: { + category: appCategories.OTHER, + description: i18n.translate('management.landing.withCardNavigation.settingsDescription', { + defaultMessage: 'Control project behavior, such as date display and default sorting.', + }), + icon: , + }, }; // Compose a list of app ids that belong to a given category diff --git a/packages/kbn-management/settings/application/README.md b/packages/kbn-management/settings/application/README.md new file mode 100644 index 000000000000..ebbc45c1707b --- /dev/null +++ b/packages/kbn-management/settings/application/README.md @@ -0,0 +1,13 @@ +--- +id: management/settings/application +slug: /management/settings/application +title: Management Settings Application +description: A package containing a component for rendering the Settings page. +tags: ['management', 'settings'] +date: 2023-09-27 +--- + +## Description + +This package contains a component for rendering the Settings page that contains a `Form` component for displaying and changing the available uiSettings. +The settings application also handles the logic for filtering out the uiSettings that are not in the allowlist. diff --git a/packages/kbn-management/settings/application/application.tsx b/packages/kbn-management/settings/application/application.tsx new file mode 100644 index 000000000000..603f5f0f36f4 --- /dev/null +++ b/packages/kbn-management/settings/application/application.tsx @@ -0,0 +1,38 @@ +/* + * 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 { Form } from '@kbn/management-settings-components-form'; + +import { EuiText, EuiSpacer } from '@elastic/eui'; +import { i18n as i18nLib } from '@kbn/i18n'; +import { useFields } from './hooks/use_fields'; + +const title = i18nLib.translate('management.settings.advancedSettingsLabel', { + defaultMessage: 'Advanced Settings', +}); + +export const DATA_TEST_SUBJ_SETTINGS_TITLE = 'managementSettingsTitle'; + +/** + * Component for displaying a {@link Form} component. + * @param props The {@link SettingsApplicationProps} for the {@link SettingsApplication} component. + */ +export const SettingsApplication = () => { + const fields = useFields(); + + return ( +
+ +

{title}

+
+ +
+
+ ); +}; diff --git a/packages/kbn-management/settings/application/hooks/index.ts b/packages/kbn-management/settings/application/hooks/index.ts new file mode 100644 index 000000000000..2b8e1b6ad102 --- /dev/null +++ b/packages/kbn-management/settings/application/hooks/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { useFields } from './use_fields'; +export { useSettings } from './use_settings'; diff --git a/packages/kbn-management/settings/application/hooks/use_fields.ts b/packages/kbn-management/settings/application/hooks/use_fields.ts new file mode 100644 index 000000000000..551582594107 --- /dev/null +++ b/packages/kbn-management/settings/application/hooks/use_fields.ts @@ -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. + */ + +import { getFieldDefinitions } from '@kbn/management-settings-field-definition'; +import { FieldDefinition, SettingType } from '@kbn/management-settings-types'; +import { useServices } from '../services'; +import { useSettings } from './use_settings'; + +/** + * React hook which retrieves settings and returns an observed collection of + * {@link FieldDefinition} objects derived from those settings. + */ +export const useFields = (): Array> => { + const { isCustomSetting: isCustom, isOverriddenSetting: isOverridden } = useServices(); + const settings = useSettings(); + return getFieldDefinitions(settings, { isCustom, isOverridden }); +}; diff --git a/packages/kbn-management/settings/application/hooks/use_settings.ts b/packages/kbn-management/settings/application/hooks/use_settings.ts new file mode 100644 index 000000000000..e00cbad9ebaf --- /dev/null +++ b/packages/kbn-management/settings/application/hooks/use_settings.ts @@ -0,0 +1,35 @@ +/* + * 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 { useState } from 'react'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; + +import { useServices } from '../services'; + +/** + * React hook which retrieves settings from a particular {@link IUiSettingsClient}, + * normalizes them to a predictable format, {@link UiSettingMetadata}, and returns + * them as an observed collection. + */ +export const useSettings = () => { + const { getAllowlistedSettings, subscribeToUpdates } = useServices(); + + const [settings, setSettings] = useState(getAllowlistedSettings()); + + useEffectOnce(() => { + const subscription = subscribeToUpdates(() => { + setSettings(getAllowlistedSettings()); + }); + + return () => { + subscription.unsubscribe(); + }; + }); + + return settings; +}; diff --git a/packages/kbn-management/settings/application/index.tsx b/packages/kbn-management/settings/application/index.tsx new file mode 100644 index 000000000000..0f491a5d8cff --- /dev/null +++ b/packages/kbn-management/settings/application/index.tsx @@ -0,0 +1,34 @@ +/* + * 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 { SettingsApplication } from './application'; +import { + SettingsApplicationKibanaDependencies, + SettingsApplicationKibanaProvider, +} from './services'; + +export { SettingsApplication } from './application'; +export { + SettingsApplicationProvider, + SettingsApplicationKibanaProvider, + type SettingsApplicationServices, + type SettingsApplicationKibanaDependencies, +} from './services'; + +export const KibanaSettingsApplication = ({ + docLinks, + i18n, + notifications, + settings, + theme, +}: SettingsApplicationKibanaDependencies) => ( + + + +); diff --git a/packages/kbn-management/settings/application/jest.config.js b/packages/kbn-management/settings/application/jest.config.js new file mode 100644 index 000000000000..aa4230234635 --- /dev/null +++ b/packages/kbn-management/settings/application/jest.config.js @@ -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: ['/packages/kbn-management/settings/application'], +}; diff --git a/packages/kbn-management/settings/application/kibana.jsonc b/packages/kbn-management/settings/application/kibana.jsonc new file mode 100644 index 000000000000..ab9c47c87f60 --- /dev/null +++ b/packages/kbn-management/settings/application/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/management-settings-application", + "owner": "@elastic/platform-deployment-management" +} diff --git a/packages/kbn-management/settings/application/package.json b/packages/kbn-management/settings/application/package.json new file mode 100644 index 000000000000..f66c66a6269f --- /dev/null +++ b/packages/kbn-management/settings/application/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/management-settings-application", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-management/settings/application/services.tsx b/packages/kbn-management/settings/application/services.tsx new file mode 100644 index 000000000000..b88e0787769e --- /dev/null +++ b/packages/kbn-management/settings/application/services.tsx @@ -0,0 +1,121 @@ +/* + * 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, { FC, useContext } from 'react'; + +import { + FormProvider, + FormKibanaProvider, + type FormKibanaDependencies, + type FormServices, +} from '@kbn/management-settings-components-form'; +import { UiSettingMetadata } from '@kbn/management-settings-types'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { normalizeSettings } from '@kbn/management-settings-utilities'; +import { Subscription } from 'rxjs'; + +export interface Services { + getAllowlistedSettings: () => Record; + subscribeToUpdates: (fn: () => void) => Subscription; + isCustomSetting: (key: string) => boolean; + isOverriddenSetting: (key: string) => boolean; +} + +export type SettingsApplicationServices = Services & FormServices; + +export interface KibanaDependencies { + settings: { + client: Pick; + }; +} + +export type SettingsApplicationKibanaDependencies = KibanaDependencies & FormKibanaDependencies; + +const SettingsApplicationContext = React.createContext(null); + +/** + * A Context Provider that provides services to the component and its dependencies. + */ +export const SettingsApplicationProvider: FC = ({ + children, + ...services +}) => { + // Destructure the services to avoid a type-widening inclusion of unrelated services. + const { + saveChanges, + showError, + showReloadPagePrompt, + links, + showDanger, + getAllowlistedSettings, + subscribeToUpdates, + isCustomSetting, + isOverriddenSetting, + } = services; + + return ( + + + {children} + + + ); +}; + +/** + * Kibana-specific Provider that maps dependencies to services. + */ +export const SettingsApplicationKibanaProvider: FC = ({ + children, + ...dependencies +}) => { + const { docLinks, notifications, theme, i18n, settings } = dependencies; + const { client } = settings; + + const getAllowlistedSettings = () => { + const rawSettings = Object.fromEntries( + Object.entries(client.getAll()).filter( + ([settingId, settingDef]) => !settingDef.readonly && !client.isCustom(settingId) + ) + ); + + return normalizeSettings(rawSettings); + }; + + const services: Services = { + getAllowlistedSettings, + isCustomSetting: (key: string) => client.isCustom(key), + isOverriddenSetting: (key: string) => client.isOverridden(key), + subscribeToUpdates: (fn: () => void) => client.getUpdate$().subscribe(fn), + }; + + return ( + + + {children} + + + ); +}; + +/** + * React hook for accessing pre-wired services. + */ +export const useServices = () => { + const context = useContext(SettingsApplicationContext); + + if (!context) { + throw new Error( + 'SettingsApplicationContext is missing. Ensure your component or React root is wrapped with SettingsApplicationProvider.' + ); + } + + return context; +}; diff --git a/packages/kbn-management/settings/application/tsconfig.json b/packages/kbn-management/settings/application/tsconfig.json new file mode 100644 index 000000000000..3893085e710e --- /dev/null +++ b/packages/kbn-management/settings/application/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core-ui-settings-browser", + "@kbn/management-settings-types", + "@kbn/management-settings-field-definition", + "@kbn/management-settings-utilities", + "@kbn/management-settings-components-form", + "@kbn/i18n", + ] +} diff --git a/packages/kbn-management/settings/components/field_input/services.tsx b/packages/kbn-management/settings/components/field_input/services.tsx index b76c9b7a9a6a..95a6474f89b5 100644 --- a/packages/kbn-management/settings/components/field_input/services.tsx +++ b/packages/kbn-management/settings/components/field_input/services.tsx @@ -27,7 +27,7 @@ export const FieldInputProvider: FC = ({ children, ...servic */ export const FieldInputKibanaProvider: FC = ({ children, - toasts, + notifications: { toasts }, }) => { return ( ; + notifications: { + toasts: Pick; + }; } /** diff --git a/packages/kbn-management/settings/components/field_row/services.tsx b/packages/kbn-management/settings/components/field_row/services.tsx index e138307979db..10963041837e 100644 --- a/packages/kbn-management/settings/components/field_row/services.tsx +++ b/packages/kbn-management/settings/components/field_row/services.tsx @@ -44,7 +44,7 @@ export const FieldRowProvider = ({ children, ...services }: FieldRowProviderProp export const FieldRowKibanaProvider: FC = ({ children, docLinks, - toasts, + notifications, }) => { return ( = ({ links: docLinks.links.management, }} > - {children} + {children} ); }; diff --git a/packages/kbn-management/settings/components/form/services.tsx b/packages/kbn-management/settings/components/form/services.tsx index 73100c1729da..e0a48807b351 100644 --- a/packages/kbn-management/settings/components/form/services.tsx +++ b/packages/kbn-management/settings/components/form/services.tsx @@ -7,7 +7,6 @@ */ import React, { FC, useContext } from 'react'; -import { UnsavedFieldChange } from '@kbn/management-settings-types'; import { FieldCategoryKibanaProvider, @@ -42,22 +41,22 @@ export const FormProvider = ({ children, ...services }: FormProviderProps) => { * Kibana-specific Provider that maps Kibana plugins and services to a {@link FormProvider}. */ export const FormKibanaProvider: FC = ({ children, ...deps }) => { - const { settings, toasts, docLinks, theme, i18nStart } = deps; + const { settings, notifications, docLinks, theme, i18n } = deps; + + const services: Services = { + saveChanges: (changes) => { + const arr = Object.entries(changes).map(([key, value]) => + settings.client.set(key, value.unsavedValue) + ); + return Promise.all(arr); + }, + showError: (message: string) => notifications.toasts.addDanger(message), + showReloadPagePrompt: () => notifications.toasts.add(reloadPageToast(theme, i18n)), + }; return ( - ) => { - const arr = Object.entries(changes).map(([key, value]) => - settings.client.set(key, value.unsavedValue) - ); - return Promise.all(arr); - }, - showError: (message: string) => toasts.addDanger(message), - showReloadPagePrompt: () => toasts.add(reloadPageToast(theme, i18nStart)), - }} - > - + + {children} diff --git a/packages/kbn-management/settings/components/form/types.ts b/packages/kbn-management/settings/components/form/types.ts index 155420f5effe..257c64cc02a7 100644 --- a/packages/kbn-management/settings/components/form/types.ts +++ b/packages/kbn-management/settings/components/form/types.ts @@ -11,7 +11,7 @@ import type { FieldRowServices, } from '@kbn/management-settings-components-field-row'; import { UnsavedFieldChange } from '@kbn/management-settings-types'; -import { SettingsStart } from '@kbn/core-ui-settings-browser'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { I18nStart } from '@kbn/core-i18n-browser'; import { ThemeServiceStart } from '@kbn/core-theme-browser'; import { ToastsStart } from '@kbn/core-notifications-browser'; @@ -36,12 +36,14 @@ export type FormServices = FieldRowServices & Services; */ interface KibanaDependencies { settings: { - client: SettingsStart['client']; + client: Pick; }; theme: ThemeServiceStart; - i18nStart: I18nStart; + i18n: I18nStart; /** The portion of the {@link ToastsStart} contract used by this component. */ - toasts: Pick; + notifications: { + toasts: Pick; + }; } /** diff --git a/packages/kbn-management/settings/utilities/mocks/settings.mock.ts b/packages/kbn-management/settings/utilities/mocks/settings.mock.ts index ca2a2892836c..cbab28536346 100644 --- a/packages/kbn-management/settings/utilities/mocks/settings.mock.ts +++ b/packages/kbn-management/settings/utilities/mocks/settings.mock.ts @@ -9,7 +9,7 @@ import { KnownTypeToMetadata, SettingType } from '@kbn/management-settings-types'; type Settings = { - [key in SettingType]: KnownTypeToMetadata; + [key in Exclude]: KnownTypeToMetadata; }; /** @@ -71,24 +71,25 @@ export const getSettingsMock = ( category: ['dashboard', 'discover'], ...defaults, }, - json: { - name: 'json:test:setting', - description: 'Description for Json test setting', - type: 'json', - userValue: null, - value: '{"foo": "bar"}', - category: ['dashboard', 'discover'], - ...defaults, - }, - markdown: { - name: 'markdown:test:setting', - description: 'Description for Markdown test setting', - type: 'markdown', - userValue: null, - value: '', - category: ['notifications', 'search'], - ...defaults, - }, + // These are notoriously difficult to test, in both Jest and Storybook. + // json: { + // name: 'json:test:setting', + // description: 'Description for Json test setting', + // type: 'json', + // userValue: null, + // value: '{"foo": "bar"}', + // category: ['dashboard', 'discover'], + // ...defaults, + // }, + // markdown: { + // name: 'markdown:test:setting', + // description: 'Description for Markdown test setting', + // type: 'markdown', + // userValue: null, + // value: '', + // category: ['notifications', 'search'], + // ...defaults, + // }, select: { description: 'Description for Select test setting', name: 'select:test:setting', diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.tsx similarity index 76% rename from src/plugins/management/public/plugin.ts rename to src/plugins/management/public/plugin.tsx index d4ef130eb4de..e0c65d66d369 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.tsx @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { i18n as kbnI18n } from '@kbn/i18n'; import { BehaviorSubject } from 'rxjs'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import { HomePublicPluginSetup } from '@kbn/home-plugin/public'; @@ -23,6 +25,8 @@ import { AppNavLinkStatus, AppDeepLink, } from '@kbn/core/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { withSuspense } from '@kbn/shared-ux-utility'; import { ConfigSchema, ManagementSetup, ManagementStart, NavigationCardsSubject } from './types'; import { MANAGEMENT_APP_ID } from '../common/contants'; @@ -43,6 +47,12 @@ interface ManagementStartDependencies { serverless?: ServerlessPluginStart; } +const LazyKibanaSettingsApplication = React.lazy(async () => ({ + default: (await import('@kbn/management-settings-application')).KibanaSettingsApplication, +})); + +const KibanaSettingsApplication = withSuspense(LazyKibanaSettingsApplication); + export class ManagementPlugin implements Plugin< @@ -99,10 +109,10 @@ export class ManagementPlugin if (home) { home.featureCatalogue.register({ id: 'stack-management', - title: i18n.translate('management.stackManagement.managementLabel', { + title: kbnI18n.translate('management.stackManagement.managementLabel', { defaultMessage: 'Stack Management', }), - description: i18n.translate('management.stackManagement.managementDescription', { + description: kbnI18n.translate('management.stackManagement.managementDescription', { defaultMessage: 'Your center console for managing the Elastic Stack.', }), icon: 'managementApp', @@ -115,7 +125,7 @@ export class ManagementPlugin core.application.register({ id: MANAGEMENT_APP_ID, - title: i18n.translate('management.stackManagement.title', { + title: kbnI18n.translate('management.stackManagement.title', { defaultMessage: 'Stack Management', }), order: 9040, @@ -152,7 +162,7 @@ export class ManagementPlugin }; } - public start(core: CoreStart, _plugins: ManagementStartDependencies): ManagementStart { + public start(core: CoreStart, plugins: ManagementStartDependencies): ManagementStart { this.managementSections.start({ capabilities: core.application.capabilities }); this.hasAnyEnabledApps = getSectionsServiceStartPrivate() .getSectionsEnabled() @@ -167,6 +177,33 @@ export class ManagementPlugin }); } + // Register the Settings app only if in serverless, until we integrate the SettingsApplication into the Advanced settings plugin + // Otherwise, it will be double registered from the Advanced settings plugin + if (plugins.serverless) { + const title = kbnI18n.translate('management.settings.settingsLabel', { + defaultMessage: 'Advanced Settings', + }); + + this.managementSections.definedSections.kibana.registerApp({ + id: 'settings', + title, + order: 3, + async mount({ element, setBreadcrumbs }) { + setBreadcrumbs([{ text: title }]); + + ReactDOM.render( + + + , + element + ); + return () => { + ReactDOM.unmountComponentAtNode(element); + }; + }, + }); + } + return { setIsSidebarEnabled: (isSidebarEnabled: boolean) => this.isSidebarEnabled$.next(isSidebarEnabled), diff --git a/src/plugins/management/tsconfig.json b/src/plugins/management/tsconfig.json index 77c3752e7b0c..0f060475796c 100644 --- a/src/plugins/management/tsconfig.json +++ b/src/plugins/management/tsconfig.json @@ -26,7 +26,10 @@ "@kbn/config-schema", "@kbn/core-application-browser", "@kbn/core-http-browser", - "@kbn/serverless" + "@kbn/serverless", + "@kbn/management-settings-application", + "@kbn/react-kibana-context-render", + "@kbn/shared-ux-utility" ], "exclude": [ "target/**/*" diff --git a/tsconfig.base.json b/tsconfig.base.json index 2ea42b377596..c5bdbcfd4f53 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -964,6 +964,8 @@ "@kbn/management-cards-navigation/*": ["packages/kbn-management/cards_navigation/*"], "@kbn/management-plugin": ["src/plugins/management"], "@kbn/management-plugin/*": ["src/plugins/management/*"], + "@kbn/management-settings-application": ["packages/kbn-management/settings/application"], + "@kbn/management-settings-application/*": ["packages/kbn-management/settings/application/*"], "@kbn/management-settings-components-field-category": ["packages/kbn-management/settings/components/field_category"], "@kbn/management-settings-components-field-category/*": ["packages/kbn-management/settings/components/field_category/*"], "@kbn/management-settings-components-field-input": ["packages/kbn-management/settings/components/field_input"], diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 427014326ed7..2391c36acc26 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -4738,9 +4738,6 @@ "management.sections.section.title": "Sécurité", "management.sections.stackTip": "Gérez votre licence et mettez la Suite à niveau.", "management.sections.stackTitle": "Suite", - "management.stackManagement.managementDescription": "La console centrale de gestion de la Suite Elastic.", - "management.stackManagement.managementLabel": "Gestion de la Suite", - "management.stackManagement.title": "Gestion de la Suite", "newsfeed.flyoutList.versionTextLabel": "{version}", "newsfeed.emptyPrompt.noNewsText": "Si votre instance Kibana n'a pas accès à Internet, demandez à votre administrateur de désactiver cette fonctionnalité. Sinon, nous continuerons d'essayer de récupérer les actualités.", "newsfeed.emptyPrompt.noNewsTitle": "Pas d'actualités ?", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b396434aa2f3..ebd523dace81 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4754,9 +4754,6 @@ "management.sections.section.title": "セキュリティ", "management.sections.stackTip": "ライセンスを管理し、スタックをアップグレードします", "management.sections.stackTitle": "スタック", - "management.stackManagement.managementDescription": "Elastic Stack の管理を行うセンターコンソールです。", - "management.stackManagement.managementLabel": "スタック管理", - "management.stackManagement.title": "スタック管理", "newsfeed.flyoutList.versionTextLabel": "{version}", "newsfeed.emptyPrompt.noNewsText": "Kibana インスタンスがインターネットにアクセスできない場合、管理者にこの機能を無効にするように依頼してください。そうでない場合は、ニュースを取り込み続けます。", "newsfeed.emptyPrompt.noNewsTitle": "ニュースがない場合", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 85371195ebcb..96adbe87e50d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4753,9 +4753,6 @@ "management.sections.section.title": "安全", "management.sections.stackTip": "管理您的许可并升级 Stack", "management.sections.stackTitle": "Stack", - "management.stackManagement.managementDescription": "您用于管理 Elastic Stack 的中心控制台。", - "management.stackManagement.managementLabel": "Stack Management", - "management.stackManagement.title": "Stack Management", "newsfeed.flyoutList.versionTextLabel": "{version}", "newsfeed.emptyPrompt.noNewsText": "如果您的 Kibana 实例没有 Internet 连接,请让您的管理员禁用此功能。否则,我们将不断尝试获取新闻。", "newsfeed.emptyPrompt.noNewsTitle": "无新闻?", diff --git a/x-pack/test_serverless/functional/config.base.ts b/x-pack/test_serverless/functional/config.base.ts index 3db533e9d930..6f45ac8e2ca8 100644 --- a/x-pack/test_serverless/functional/config.base.ts +++ b/x-pack/test_serverless/functional/config.base.ts @@ -72,7 +72,7 @@ export function createTestConfig(options: CreateTestConfigOptions) { connectors: { pathname: '/app/management/insightsAndAlerting/triggersActionsConnectors/', }, - advancedSettings: { + settings: { pathname: '/app/management/kibana/settings', }, login: { diff --git a/x-pack/test_serverless/functional/test_suites/common/management/advanced_settings.ts b/x-pack/test_serverless/functional/test_suites/common/management/advanced_settings.ts index 0ea1e6339772..aad12001ed52 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/advanced_settings.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/advanced_settings.ts @@ -7,18 +7,37 @@ import expect from '@kbn/expect'; import { ALL_COMMON_SETTINGS } from '@kbn/serverless-common-settings'; +import * as settings from '@kbn/management-settings-ids'; import { FtrProviderContext } from '../../../ftr_provider_context'; +const editorSettings = new Set([ + settings.BANNERS_TEXT_CONTENT_ID, + settings.DATE_FORMAT_SCALED_ID, + settings.ML_ANOMALY_DETECTION_RESULTS_TIME_DEFAULTS_ID, + settings.NOTIFICATIONS_BANNER_ID, + settings.TIMEPICKER_TIME_DEFAULTS_ID, + settings.TIMEPICKER_QUICK_RANGES_ID, + settings.SECURITY_SOLUTION_REFRESH_INTERVAL_DEFAULTS_ID, + settings.SECURITY_SOLUTION_TIME_DEFAULTS_ID, + settings.SECURITY_SOLUTION_RULES_TABLE_REFRESH_ID, + settings.SECURITY_SOLUTION_IP_REPUTATION_LINKS_ID, +]); +export const isEditorFieldSetting = (settingId: string) => editorSettings.has(settingId); + export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); - const pageObjects = getPageObjects(['common']); + const pageObjects = getPageObjects(['svlCommonPage', 'common']); const browser = getService('browser'); const retry = getService('retry'); - // Skip until we enable the Advanced settings app in serverless - describe.skip('Common advanced settings', function () { + describe('Common advanced settings', function () { before(async () => { - await pageObjects.common.navigateToApp('advancedSettings'); + await pageObjects.svlCommonPage.login(); + await pageObjects.common.navigateToApp('settings'); + }); + + after(async () => { + await pageObjects.svlCommonPage.forceLogout(); }); it('renders the page', async () => { @@ -32,13 +51,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('renders common settings', () => { for (const settingId of ALL_COMMON_SETTINGS) { + // Code editors don't have their test subjects rendered + if (isEditorFieldSetting(settingId)) { + continue; + } + const isColorPickerField = + settingId === settings.BANNERS_TEXT_COLOR_ID || + settingId === settings.BANNERS_BACKGROUND_COLOR_ID; + const fieldTestSubj = + (isColorPickerField ? 'euiColorPickerAnchor ' : '') + + 'management-settings-editField-' + + settingId; it('renders ' + settingId + ' edit field', async () => { - const isColorPickerField = - settingId === 'banners:textColor' || settingId === 'banners:backgroundColor'; - const fieldTestSubj = - (isColorPickerField ? 'euiColorPickerAnchor ' : '') + - 'advancedSetting-editField-' + - settingId; expect(await testSubjects.exists(fieldTestSubj)).to.be(true); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/advanced_settings.ts b/x-pack/test_serverless/functional/test_suites/observability/advanced_settings.ts index e723b81dbe20..d6f2ef6502f2 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/advanced_settings.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/advanced_settings.ts @@ -8,17 +8,22 @@ import expect from '@kbn/expect'; import { OBSERVABILITY_PROJECT_SETTINGS } from '@kbn/serverless-observability-settings'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { isEditorFieldSetting } from '../common/management/advanced_settings'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); - const pageObjects = getPageObjects(['common']); + const pageObjects = getPageObjects(['svlCommonPage', 'common']); const browser = getService('browser'); const retry = getService('retry'); - // Skip until we enable the Advanced settings app in serverless - describe.skip('Observability advanced settings', function () { + describe('Observability advanced settings', function () { before(async () => { - await pageObjects.common.navigateToApp('advancedSettings'); + await pageObjects.svlCommonPage.login(); + await pageObjects.common.navigateToApp('settings'); + }); + + after(async () => { + await pageObjects.svlCommonPage.forceLogout(); }); it('renders the page', async () => { @@ -32,8 +37,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('renders observability settings', () => { for (const settingId of OBSERVABILITY_PROJECT_SETTINGS) { + // Code editors don't have their test subjects rendered + if (isEditorFieldSetting(settingId)) { + continue; + } it('renders ' + settingId + ' edit field', async () => { - const fieldTestSubj = 'advancedSetting-editField-' + settingId; + const fieldTestSubj = 'management-settings-editField-' + settingId; expect(await testSubjects.exists(fieldTestSubj)).to.be(true); }); } diff --git a/x-pack/test_serverless/functional/test_suites/search/advanced_settings.ts b/x-pack/test_serverless/functional/test_suites/search/advanced_settings.ts index 63b6053589ea..2f2b76b21eda 100644 --- a/x-pack/test_serverless/functional/test_suites/search/advanced_settings.ts +++ b/x-pack/test_serverless/functional/test_suites/search/advanced_settings.ts @@ -8,17 +8,22 @@ import expect from '@kbn/expect'; import { SEARCH_PROJECT_SETTINGS } from '@kbn/serverless-search-settings'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { isEditorFieldSetting } from '../common/management/advanced_settings'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); - const pageObjects = getPageObjects(['common']); + const pageObjects = getPageObjects(['svlCommonPage', 'common']); const browser = getService('browser'); const retry = getService('retry'); - // Skip until we enable the Advanced settings app in serverless - describe.skip('Search advanced settings', function () { + describe('Search advanced settings', function () { before(async () => { - await pageObjects.common.navigateToApp('advancedSettings'); + await pageObjects.svlCommonPage.login(); + await pageObjects.common.navigateToApp('settings'); + }); + + after(async () => { + await pageObjects.svlCommonPage.forceLogout(); }); it('renders the page', async () => { @@ -32,8 +37,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('renders search settings', () => { for (const settingId of SEARCH_PROJECT_SETTINGS) { + // Code editors don't have their test subjects rendered + if (isEditorFieldSetting(settingId)) { + continue; + } it('renders ' + settingId + ' edit field', async () => { - const fieldTestSubj = 'advancedSetting-editField-' + settingId; + const fieldTestSubj = 'management-settings-editField-' + settingId; expect(await testSubjects.exists(fieldTestSubj)).to.be(true); }); } diff --git a/x-pack/test_serverless/functional/test_suites/security/advanced_settings.ts b/x-pack/test_serverless/functional/test_suites/security/advanced_settings.ts index 27fa42549dcc..63e3a8a9a667 100644 --- a/x-pack/test_serverless/functional/test_suites/security/advanced_settings.ts +++ b/x-pack/test_serverless/functional/test_suites/security/advanced_settings.ts @@ -8,17 +8,22 @@ import expect from '@kbn/expect'; import { SECURITY_PROJECT_SETTINGS } from '@kbn/serverless-security-settings'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { isEditorFieldSetting } from '../common/management/advanced_settings'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); - const pageObjects = getPageObjects(['common']); + const pageObjects = getPageObjects(['svlCommonPage', 'common']); const browser = getService('browser'); const retry = getService('retry'); - // Skip until we enable the Advanced settings app in serverless - describe.skip('Security advanced settings', function () { + describe('Security advanced settings', function () { before(async () => { - await pageObjects.common.navigateToApp('advancedSettings'); + await pageObjects.svlCommonPage.login(); + await pageObjects.common.navigateToApp('settings'); + }); + + after(async () => { + await pageObjects.svlCommonPage.forceLogout(); }); it('renders the page', async () => { @@ -32,8 +37,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('renders security settings', () => { for (const settingId of SECURITY_PROJECT_SETTINGS) { + // Code editors don't have their test subjects rendered + if (isEditorFieldSetting(settingId)) { + continue; + } it('renders ' + settingId + ' edit field', async () => { - const fieldTestSubj = 'advancedSetting-editField-' + settingId; + const fieldTestSubj = 'management-settings-editField-' + settingId; expect(await testSubjects.exists(fieldTestSubj)).to.be(true); }); } diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index bf23a1889940..66bef3ea9911 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -57,5 +57,6 @@ "@kbn/serverless-observability-settings", "@kbn/serverless-search-settings", "@kbn/serverless-security-settings", + "@kbn/management-settings-ids", ] } diff --git a/yarn.lock b/yarn.lock index d85a677823ca..88a5a21cbdb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4871,6 +4871,10 @@ version "0.0.0" uid "" +"@kbn/management-settings-application@link:packages/kbn-management/settings/application": + version "0.0.0" + uid "" + "@kbn/management-settings-components-field-category@link:packages/kbn-management/settings/components/field_category": version "0.0.0" uid ""