[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:
<img width="1495" alt="Screenshot 2023-09-28 at 20 56 25"
src="374b3bbd-7bf6-4de7-8129-8b293dd1698e">

### Added an Advanced Settings card to the Management landing page:
<img width="1575" alt="Screenshot 2023-09-28 at 12 24 23"
src="c08b8b36-ff40-4772-87d6-597629d78342">




<!--- ### Checklist

Delete any items that are not applicable to this PR.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)


### Risk Matrix

Delete this section if it is not applicable to this PR.

Before closing this PR, invite QA, stakeholders, and other developers to
identify risks that should be tested prior to the change/feature
release.

When forming the risk matrix, consider some of the following examples
and how they may potentially impact the change:

| Risk | Probability | Severity | Mitigation/Notes |

|---------------------------|-------------|----------|-------------------------|
| Multiple Spaces&mdash;unexpected behavior in non-default Kibana Space.
| Low | High | Integration tests will verify that all features are still
supported in non-default Kibana Space and when user switches between
spaces. |
| Multiple nodes&mdash;Elasticsearch polling might have race conditions
when multiple Kibana nodes are polling for the same tasks. | High | Low
| Tasks are idempotent, so executing them multiple times will not result
in logical error, but will degrade performance. To test for this case we
add plenty of unit tests around this logic and document manual testing
procedure. |
| Code should gracefully handle cases when feature X or plugin Y are
disabled. | Medium | High | Unit tests will verify that any feature flag
or plugin combination still results in our service operational. |
| [See more potential risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) |


### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

-->

---------

Co-authored-by: Clint Andrew Hall <clint@clintandrewhall.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Clint Andrew Hall <clint.hall@elastic.co>
This commit is contained in:
Elena Stoeva 2023-09-29 16:48:37 +01:00 committed by GitHub
parent aa065b710c
commit d37d8ae85c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 510 additions and 83 deletions

1
.github/CODEOWNERS vendored
View file

@ -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

View file

@ -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",

View file

@ -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<AppId, AppDefinition> = {
}),
icon: <EuiIcon size="l" type="lockOpen" />,
},
[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: <EuiIcon size="l" type="gear" />,
},
};
// Compose a list of app ids that belong to a given category

View file

@ -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.

View file

@ -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 (
<div>
<EuiText>
<h1 data-test-subj={DATA_TEST_SUBJ_SETTINGS_TITLE}>{title}</h1>
</EuiText>
<EuiSpacer size="xxl" />
<Form fields={fields} isSavingEnabled={true} />
</div>
);
};

View file

@ -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';

View 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.
*/
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<FieldDefinition<SettingType>> => {
const { isCustomSetting: isCustom, isOverriddenSetting: isOverridden } = useServices();
const settings = useSettings();
return getFieldDefinitions(settings, { isCustom, isOverridden });
};

View file

@ -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;
};

View file

@ -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) => (
<SettingsApplicationKibanaProvider {...{ settings, theme, i18n, notifications, docLinks }}>
<SettingsApplication />
</SettingsApplicationKibanaProvider>
);

View 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/settings/application'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/management-settings-application",
"owner": "@elastic/platform-deployment-management"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/management-settings-application",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -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<string, UiSettingMetadata>;
subscribeToUpdates: (fn: () => void) => Subscription;
isCustomSetting: (key: string) => boolean;
isOverriddenSetting: (key: string) => boolean;
}
export type SettingsApplicationServices = Services & FormServices;
export interface KibanaDependencies {
settings: {
client: Pick<IUiSettingsClient, 'getAll' | 'isCustom' | 'isOverridden' | 'getUpdate$'>;
};
}
export type SettingsApplicationKibanaDependencies = KibanaDependencies & FormKibanaDependencies;
const SettingsApplicationContext = React.createContext<Services | null>(null);
/**
* A Context Provider that provides services to the component and its dependencies.
*/
export const SettingsApplicationProvider: FC<SettingsApplicationServices> = ({
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 (
<SettingsApplicationContext.Provider
value={{ getAllowlistedSettings, subscribeToUpdates, isCustomSetting, isOverriddenSetting }}
>
<FormProvider {...{ saveChanges, showError, showReloadPagePrompt, links, showDanger }}>
{children}
</FormProvider>
</SettingsApplicationContext.Provider>
);
};
/**
* Kibana-specific Provider that maps dependencies to services.
*/
export const SettingsApplicationKibanaProvider: FC<SettingsApplicationKibanaDependencies> = ({
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 (
<SettingsApplicationContext.Provider value={services}>
<FormKibanaProvider {...{ docLinks, notifications, theme, i18n, settings }}>
{children}
</FormKibanaProvider>
</SettingsApplicationContext.Provider>
);
};
/**
* 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;
};

View file

@ -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",
]
}

View file

@ -27,7 +27,7 @@ export const FieldInputProvider: FC<FieldInputServices> = ({ children, ...servic
*/
export const FieldInputKibanaProvider: FC<FieldInputKibanaDependencies> = ({
children,
toasts,
notifications: { toasts },
}) => {
return (
<FieldInputContext.Provider

View file

@ -31,7 +31,9 @@ export interface FieldInputServices {
*/
export interface FieldInputKibanaDependencies {
/** The portion of the {@link ToastsStart} contract used by this component. */
toasts: Pick<ToastsStart, 'addDanger'>;
notifications: {
toasts: Pick<ToastsStart, 'addDanger'>;
};
}
/**

View file

@ -44,7 +44,7 @@ export const FieldRowProvider = ({ children, ...services }: FieldRowProviderProp
export const FieldRowKibanaProvider: FC<FieldRowKibanaDependencies> = ({
children,
docLinks,
toasts,
notifications,
}) => {
return (
<FieldRowContext.Provider
@ -52,7 +52,7 @@ export const FieldRowKibanaProvider: FC<FieldRowKibanaDependencies> = ({
links: docLinks.links.management,
}}
>
<FieldInputKibanaProvider {...{ toasts }}>{children}</FieldInputKibanaProvider>
<FieldInputKibanaProvider {...{ notifications }}>{children}</FieldInputKibanaProvider>
</FieldRowContext.Provider>
);
};

View file

@ -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<FormKibanaDependencies> = ({ 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 (
<FormContext.Provider
value={{
saveChanges: (changes: Record<string, UnsavedFieldChange>) => {
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)),
}}
>
<FieldCategoryKibanaProvider {...{ docLinks, toasts }}>
<FormContext.Provider value={services}>
<FieldCategoryKibanaProvider {...{ docLinks, notifications }}>
{children}
</FieldCategoryKibanaProvider>
</FormContext.Provider>

View file

@ -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<IUiSettingsClient, 'set'>;
};
theme: ThemeServiceStart;
i18nStart: I18nStart;
i18n: I18nStart;
/** The portion of the {@link ToastsStart} contract used by this component. */
toasts: Pick<ToastsStart, 'addError' | 'add'>;
notifications: {
toasts: Pick<ToastsStart, 'addError' | 'add'>;
};
}
/**

View file

@ -9,7 +9,7 @@
import { KnownTypeToMetadata, SettingType } from '@kbn/management-settings-types';
type Settings = {
[key in SettingType]: KnownTypeToMetadata<key>;
[key in Exclude<SettingType, 'json' | 'markdown'>]: KnownTypeToMetadata<key>;
};
/**
@ -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',

View file

@ -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(
<KibanaRenderContextProvider {...core}>
<KibanaSettingsApplication {...core} />
</KibanaRenderContextProvider>,
element
);
return () => {
ReactDOM.unmountComponentAtNode(element);
};
},
});
}
return {
setIsSidebarEnabled: (isSidebarEnabled: boolean) =>
this.isSidebarEnabled$.next(isSidebarEnabled),

View file

@ -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/**/*"

View file

@ -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"],

View file

@ -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 ?",

View file

@ -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": "ニュースがない場合",

View file

@ -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": "无新闻?",

View file

@ -72,7 +72,7 @@ export function createTestConfig(options: CreateTestConfigOptions) {
connectors: {
pathname: '/app/management/insightsAndAlerting/triggersActionsConnectors/',
},
advancedSettings: {
settings: {
pathname: '/app/management/kibana/settings',
},
login: {

View file

@ -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<string>([
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);
});
}

View file

@ -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);
});
}

View file

@ -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);
});
}

View file

@ -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);
});
}

View file

@ -57,5 +57,6 @@
"@kbn/serverless-observability-settings",
"@kbn/serverless-search-settings",
"@kbn/serverless-security-settings",
"@kbn/management-settings-ids",
]
}

View file

@ -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 ""