Kibana Plugin API Changes in 7.4
Timefilter - replace SimpleEmitter with observables
Timefilter
exposes 5 events:
getEnabledUpdated$
- Fired whenisTimeRangeSelectorEnabled
\isAutoRefreshSelectorEnabled
are toggled.getTimeUpdate$
- Fired when a user changes the timerange.getRefreshIntervalUpdate$
- Fired when a user changes the the autorefresh settings.getAutoRefreshFetch$
- Used when search_poll triggers an auto refresh.getFetch$
- Used when data is set, or the first time auto-refresh is enabled.
Old subscription:
timefilter.on('fetch', ...);
timefilter.off('fetch', ...);
New subscription:
const fetchSub = timefilter.getFetch$().subscrible(...);
fetchSub.unsubscribe();
via #43748
Add support for dynamic imports
Plugins may now use dynamic imports in client side code in order to split up their application bundles. This can be used to improve the UX for large UI applications:
const myModule = await import('./my_module');
via #43716
Add x-pack plugin for new platform server licensing information
Add x-pack plugin for new platform server licensing information. This will eventually replace the licensing information consumed via xpack_main
. Upon setup, this plugin exposes an observable API for inspecting and making checks against the license information.
license$.subscribe(license => {
console.log(license.uid);
console.log(license.isActive);
console.log(license.type);
const { check } = license.check('my-plugin', LICENSE_TYPE.gold);
if (check !== LICENSE_STATUS.Valid) {
disableSomething();
}
});
via #43623
Replace CSP 'nonce-<base64>' directive with 'self' directive
Kibana no longer supports the {nonce}
notation in the csp.rules
configuration. These will be replaced with the 'self'
source directive automatically and log a deprecation warning. The {nonce}
notation must be removed before upgrading to 8.0.
via #43553
Extend request handler with request scoped core capabilities
Kibana facilitates new Handler context pattern to provide core services API to plugins. The central purpose of this pattern is to hide some implementation details and provide a set of API specific for the current context. For example, RequestHandlerContext
contains tailored elasticsearch service API, letting a plugin to perform a request to elasticsearch on behalf of the user requesting Kibana. RequestHandlerContext
is exposed to the route handlers as the very first argument.
class MyPlugin {
setup(core) {
const router = core.http.createRouter();
router.get({ path: '/ping', validate: false }, async (context, req, res) => {
const response = await context.core.elasticsearch.adminClient.callAsInternalUser('ping');
return res.ok({ body: `Pong: ${response}` });
});
}
}
via #43103
add socket.getPeerCertificate to KibanaRequest
KibanaRequest object can provide peer certificate
const cert = request.socket.getPeerCertificate();
via #42929
Allowing individual privileges to be excluded from base privileges
Individual feature's privileges can now be excluded from the base privileges using excludeFromBasePrivilege
via #42470
Unify response interface in handler and request interceptors
New platform plugins may want to extend Kibana capabilities with implementing a custom logic over an incoming request, before it hits a resource endpoint. For this purpose, KIbana introduced interceptors concept. Interceptors cannot change or mutate request object directly but may redirect, reject or allow a request to pass through. An interceptor may be created for different lifecycle stages of an incoming request:
- before a user requesting a resource is authenticated
- authentication
- after a user was successfully authenticated.
// pre-authentication interceptor.
registerOnPreAuth((req, res, toolkit) => {
if(req.headers.something) return res.badRequest({ body: '"something" headers is required' });
return toolkit.next();
})
// authentication interceptor
registerAuth((req, res, toolkit) => {
const authResult = authenticate(req.headers.authorization);
if(authResult.succeeded) return toolkit.authenticated(...);
return res.unauthorized();
});
// post-authentication interceptor.
registerOnPostAuth((req, res, toolkit) => {
if(deps.security.getUser(req).roles.length === 0) return res.notFound();
return toolkit.next();
});
via #42442
revert PR 36804 'Create additional HTTP servers'
New platform HTTP service no longer provides createNewServer
method.
via #42333
Inspector 👉 New Platform inspector
plugin
- Inspector ui-public module has been moved to a New Platform plugin.
- You can find its documentation in
src/plugins/inspector/README.md
. - Old Inspector module still works as before, but all methods are now marked as deprecated. During one of upcoming
7.x
releases it will be completely deleted.
If you need to use inspector
plugin from within src/legacy
location, use New Platform plugin backdoor.
import { npSetup, npStart } from 'ui/new_platform';
npSetup.plugins.inspector.registerView(view);
npStart.plugins.inspector.isAvailable(adapters);
npStart.plugins.inspector.open( /* ... */ );
via #42164
Telemetry/opt in welcome screen
Added Telemetry Opt in card to welcome screen. The aim is to increate increase opt in rate. Added tracking to measure the success of this change.
via #42110
Http server route handler implementation
Kibana HTTP Service in the New platform provides own abstraction for work with HTTP stack.
Plugins don't have direct access to hapi
server and its primitives anymore. Moreover,
plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood.
This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins.
If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs.
To handle an incoming request in your plugin you should:
- Create a
Router
instance. Router is already configured to useplugin-id
to prefix path segment for your routes.
const router = httpSetup.createRouter();
- Use
@kbn/config-schema
package to create a schema to validate the requestparams
,query
, andbody
. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with400
status andBad request
error without calling the route's handler. To opt out of validating the request, specifyfalse
.
import { schema, TypeOf } from '@kbn/config-schema';
const validate = {
params: schema.object({
id: schema.string(),
}),
};
- Declare a function to respond to incoming request.
The function will receive:
1.
context
runtime context providing access to Kibana API, specific for a route handler. 2.request
object containing request details: url, headers, matched route, as well as validatedparams
,query
,body
. 3.response
object instructing HTTP server to create HTTP response with information sent back to the client as the response body, headers, and HTTP status. Unlike,hapi
route handler in the Legacy platform, any exception raised during the handler call will generate500 Server error
response and log error details for further investigation. See below for returning custom error responses.
const handler = async (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => {
const data = await findObject(request.params.id);
// creates a command to respond with 'not found' error
if (!data) return response.notFound();
// creates a command to send found data to the client and set response headers
return response.ok({
body: data,
headers: {
'content-type': 'application/json'
}
});
}
- Register route handler for GET request to 'my-app/path/{id}' path
import { schema, TypeOf } from '@kbn/config-schema';
const router = httpSetup.createRouter();
const validate = {
params: schema.object({
id: schema.string(),
}),
};
router.get({
path: 'path/{id}',
validate
},
async (context, request, response) => {
const data = await findObject(request.params.id);
if (!data) return response.notFound();
return response.ok({
body: data,
headers: {
'content-type': 'application/json'
}
});
});
via #41894
API change of React visualization editor
The properties that get passed into a React visualization editor have slightly been changed:
editorState.params
→stateParams
stageEditorParams(newParams)
→setValue(paramName: string, paramValue: unknown)
(meaning that you can now set individual parameters in the editor without needing to copy over the previous params.scope
has been removed, which gave direct access to an Angular scope.vis
has been added to the properties to access the underlyingVis
object directly.
You can check the custom visualization sample plugin for an always up to date example of building custom visualizations.
via #41746
Remove notifications plugin
The notifications functionality has been replaced by the features of the actions plugin. This notifications plugin was never actually used by end-user facing features of Kibana, but it may have been in use by third party plugins.
via #41674
GoodBye Notifier
The old notification system called Notifier
has been removed. If you used Notifier
or notify
from the ui/notify
package, you need to change that code to use the new toastNotifications
from ui/notify
instead.
via #41663
Adding "style-src 'unsafe-inline' 'self'" to default CSP rules
Adding style-src 'unsafe-inline' 'self'
to the default CSP rules
via #41305
Embeddables -> NP-ready
embeddable_api
and dashboard_embeddable_container
plugins have been refactored to prepare them for the New Platform Migration.
Embeddable API is still considered unstable and will change during 7.4 and possibly 7.5 releases.
Now you use Embeddables as if they were a New Platform plugin:
import { setup, start } from 'plugins/embeddable_api/np_ready/public/legacy';
setup.registerTrigger(trigger);
start.executeTriggerActions(/* ... */);
In one of the subsequent Embeddables PRs we expect to move them to a real New Platform plugin. In legacy platform you will use Embeddable plugin as follows:
import { npSetup, npStart } from 'ui/new_platform';
npSetup.plugins.embeddable.registerTrigger(trigger);
npStart.plugins.embeddable.executeTriggerActions(/* ... */);
via #41272
Add ContextService
New Platform plugins may now implement their own context API on the server or client.
A IContextContainer
can be used by any Core service or plugin (known as the "service owner") which wishes to expose APIs in a handler function. The container object will manage registering context providers and configuring a handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the dependencies that the handler's plugin declares.
class MyPlugin {
private readonly handlers = new Map();
setup(core) {
this.contextContainer = core.context.createContextContainer();
return {
registerContext(pluginOpaqueId, contextName, provider) {
this.contextContainer.registerContext(pluginOpaqueId, contextName, provider);
},
registerRoute(pluginOpaqueId, path, handler) {
this.handlers.set(
path,
this.contextContainer.createHandler(pluginOpaqueId, handler)
);
}
}
}
}
via #41251
Schema.deprecate
removed
The deprecate
boolean flag on Schema
when declaring schemas for visualizations has been removed.
via #40866
decouple sessiontStorageFactory creation from registerAuth
Implementing custom authentication logic might require session storage. If you want to use session storage, based on cookie mechanism, you can create one via new platform API:
const { createCookieSessionStorageFactory } = await server.setup(config);
const sessionStorageFactory = await createCookieSessionStorageFactory(cookieOptions);
// in authenticator
const sessionStorage = sessionStorageFactory.asScoped(request);
await sessionStorage.get();
via #40852
Add scoped services and plugin contracts
New Platform Plugins may now return a factory function for creating an instance of their public contract from lifecycle methods. This allows plugins to offer scoped versions of their APIs to dependencies.
via #40822
expose ES createClient to plugins
New platform plugins may create their custom elasticsearch clients with elasticsearch.createClient
.
class MyPlugin {
public async setup(core: CoreSetup) {
this.clusterClient = core.elasticsearch.createClient('name', configObject);
const result = await this.clusterClient.callAsInternalUser('/endpoint');
via #40717
Moved kbnGlobalTimepicker into old kbn_top_nav (both do be deprecated)
- Deleted
search-bar
directive - Deleted
query-bar
directive
via #40603
Reactify Top Nav Menu (kbn_top_nav)
Top Nav Menu
is a configurable component that conveniently renders the elements that are often shared by applications: Option menus, query input, filters and time range selection.
Show a simple options menu
Define your navigation menu items as an array of TopNavMenuData
elements and pass it down to the TopNavMenu
component.
import { TopNavMenu, TopNavMenuData } from 'plugins/kibana_react';
const navConfig: TopNavMenuData[] = [{
id: 'new-item',
label: 'New',
run: () => {
showNewItemModal()
}
}, {
id: 'save-item',
label: 'Save',
run: () => {
if(hasChanges()) {
saveChanges();
}
},
disableButton: () => {
return !hasChanges();
},
tooltip: () => {
return !hasChanges() ? 'Nothing to save' : '';
}
}];
function MyAppComponent(props: Props) {
return (
// ... other components rendering ...
<TopNavMenu
appName="my-app"
config={navConfig}
/>
);
}
Show a full menu with SearchBar
Search Bar is another convenience component that displays FilterBar
, QueryBarInput
and EuiSuperDatePicker
in a standard layout.
This is the full configuration required to render all 3 components.
function MyAppComponent(props: Props) {
const query: Query = {
query: 'response:200',
language: 'kuery',
};
const filters: Filter[] = [];
const indexPatterns: IndexPattern[] = [];
const queryHandler = ({ query, dateRange }) => {};
const filterHandler = (filters) => {};
const refreshHandler = ({ isPaused, refreshInterval }) => {};
return (
// ... other components rendering ...
<TopNavMenu
appName='my-app'
config={navConfig}
screenTitle='My App Page'
store={localStorage}
indexPatterns={indexPatterns}
filters={filters}
query={query}
onQuerySubmit={queryHandler}
onFiltersUpdated={filterHandler}
dateRangeFrom='now-7d'
dateRangeTo='now'
isRefreshPaused={false}
refreshInterval={30}
onRefreshChange={refreshHandler}
/>
);
}
Migrating the Angular directive
The kbn-top-nav
directive is still defined, however, its API has slightly changed.
While it will be deprecated during the lifetime of 7.x, you may still use it, if you take the following steps.
Key attribute is deprecated
Change the key
attribute of the navigation configuration to id
. It's a reserved React keyword and hence it's not supported by the TopNavMenu
component. While the both attributes will work with the directive, it's better to update to id
right away.
// Old
const navConfig = [{
key: 'new-item',
label: 'new',
run: () => {},
}];
// New
const navConfig = [{
id: 'new-item',
label: 'new',
run: () => {},
}];
Add a label
While the TopNavMenuItem
component still currently falls back to using key\id if label
is not provided, you should always specify a label
. This is because label
is an i18n
string and id
is not.
Transcluded views are deprecated
Either move them elsewhere or use the SearchBar options.
// Old
<kbn-top-nav config="topNavConfig">
<search-bar
... search bar options...
/>
<my-custom-directive/>
</kbn-top-nav>
// New
<kbn-top-nav
config="topNavConfig"
... search bar options...
/>
<my-custom-directive/>
Template menu items are deprecated.
Render your views from your app's code and then use the menu's run
function to toggle visibility.
// Old
const newTemplateForm = require(....);
const navConfig = [{
key: 'new-item',
label: 'new',
template: newTemplateForm,
}];
// New
const navConfig = [{
id: 'new-item',
label: 'new',
run: () => toggleMyForm,
}];
via #40262
Expose elasticsearch error wrapper
When elasticsearch client requests elasticsearch API it may throw 2 kinds of exceptions:
- Boom error when a user is unauthorized to access a resource.
- native error in other cases.
We added ElasticsearchErrorHelpers.isNotAuthorizedError(error)
method to distinguish between them. Use this method if you need access to authorization error properties. In the future, we are going to get rid of Boom error object as a part of the public contract and your code will be ready for those changes.
try {
await client.asScoped(request).callAsCurrentUser(...);
} catch (err) {
if (ElasticsearchErrorHelpers.isNotAuthorizedError(err)) {
const authHeader = err.output.headers['WWW-Authenticate'];
//...
}
}
via #40242
cancellation of interpreter execution
The interpreter now accepts an AbortController
signal inside handlers
for aborting execution. Since handlers
is passed down to each function in the interpreter, functions themselves can handle additional cleanup using handlers.abortSignal
if it is available.
via #40238
ui/public
cleanup
Relocated modules
In preparation for Kibana's upcoming new platform, we are in the process of migrating away from the ui/public
directory. Over time, the contents of this directory will be either deprecated or housed inside a parent plugin. If your plugin imports from any of the following ui/public
modules, you will need to update your import statements as indicated below, so that you are pulling these modules from their new locations.
ui/index_patterns
#42994
// deprecated
import * from 'ui/index_patterns/**/*';
// new location (stateful code)
import { setup as data } from '../../core_plugins/data/public';
const {
FieldList,
flattenHitWrapper,
formatHitProvider,
IndexPatternSelect,
} = data.indexPatterns;
// new location (static code)
import {
CONTAINS_SPACES,
getFromSavedObject,
getRoutes,
isFilterable,
IndexPatternsProvider,
validateIndexPattern,
ILLEGAL_CHARACTERS,
INDEX_PATTERN_ILLEGAL_CHARACTERS,
INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE,
IndexPatternAlreadyExists,
IndexPatternMissingIndices,
NoDefaultIndexPattern,
NoDefinedIndexPatterns,
} from '../../core_plugins/data/public';
// new location (types)
import {
Field,
FieldType,
IndexPattern,
IndexPatterns,
StaticIndexPattern,
} from '../../core_plugins/data/public';
ui/vis
#43730
// deprecated
import {
VisProvider,
Vis,
VisParams,
VisState,
} from 'ui/vis';
import { VisFactoryProvider } from 'ui/vis/vis_factory';
import { VisTypesRegistry, VisTypesRegistryProvider } from 'ui/registry/vis_types';
import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
import { Status } from 'ui/vis/update_status';
import { VisualizationController, VisType } from 'ui/vis/vis_types/vis_type';
import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters';
// new location (stateful code)
import { setup as visualizations } from '../core_plugins/visualizations/public/legacy';
const { VisProvider, VisTypesRegistryProvider } = visualizations.types;
const { VisFactoryProvider } = visualizations.types.__LEGACY;
const { VisFiltersProvider, createFilter } = visualizations.filters;
// new location (static code)
import { DefaultEditorSize, defaultFeedbackMessage, visFactory } from '../core_plugins/visualizations/public';
// new location (types)
import {
Vis,
VisParams,
VisProvider,
VisState,
VisType,
VisTypesRegistry,
VisualizationController,
Status,
} from '../core_plugins/visualizations/public';
Angular Cleanup
Removed / moved Angular items
In the process of removing Angular from the Kibana core, the following items have been deleted or relocated. If you have used any of those in your plugin and want to keep using them, please copy over the source from the previous Kibana version directly into your plugin code, or import them from their new location indicated below. Please note that these will be deprecated over the lifetime of 7.x
.
Directives
bind
decorator moved into directives
folder #41638
// Old
import 'ui/bind';
// New
import 'ui/directives/bind';
$scope.$bind(local, attr);
debounce
service moved into directives
folder #41638
// Old
import { DebounceProvider } from 'ui/debounce';
// New
import { DebounceProvider } from 'ui/directives/debounce';
listen
rootScope function moved into directives
folder #41638
// Old
import 'ui/directives/listen';
// New
import 'ui/listen';
$scope.$listen(obj, 'event', handler);
renderDirective
directive moved into directives
folder #41638
// Old
import 'ui/render_directive';
// New
import 'ui/directives/render_directive';
<render-directive ... >
watchMulti
rootScope function moved into directives
folder #41638
// Old
import 'ui/directives/watch_multi';
// New
import 'ui/watch_multi';
$scope.$watchMulti(eventNames, eventHandler);
via #39907
Task manager now uses saved objects
Starting up kibana will convert .kibana_task_manager
to an alias and indices will follow the .kibana_task_manager_1
syntax. A migration will execute to prefix the ids with task:
as it converts to saved objects.
via #39829
[i18n] .i18nrc file as the source of truth and enhance tooling
Currently there is a disconnect disconnect between i18n tooling (i18nrc.json
) and top-level/translations
convention used by the kibana i18n
service. This change attempts to fix it, tidying up the code a little, and enhancing the i18n tooling as a result of this change.
Use .i18nrc.json
as the source of truth
Currently we assume that internal and external plugins to follow this convention of putting translations following this pattern **/*/translations/${locale}.json
.
We also have to keep the .i18nrc
in sync with the location of each of these translation files.
This change makes the i18n
service look into top level .i18nrc.json
files and parses the translations
instead of traversing the source code directories to look for translation files.
This change discards the translation paths assumptions and allows developers to explicitly specify translation paths inside .i18nrc
file. Previously the translations
paths in the .i18nrc
was only used for the scripts/i18n_check.js
tooling.
This change also allows for a better third party plugin development experience as developers can fully control their own translation paths using the .i18nrc.json
file.
Enhance i18n tooling (namespaces prefix checking, better logging and error handling for parsing .i18nrc
files)
Introducing top level .i18nrc
files for plugins allows adding a namespaces prefix
check.
This PR adds a prefix check that all x-pack
plugin namespaces (label ids) start with xpack.*
Introducing top level .i18nrc
files for plugins allows for a better experience in merging configs. This PR adds a more verbose logging while merging configs and better error handling.
via #39774
Minor changes to Index Patterns API
During our efforts to rewrite the index patterns service in TypeScript, we made a few minor changes to the API:
IndexPatterns.cache
has been deprecated in favor of the newIndexPatterns.clearCache
method.IndexPatterns.delete
has been deprecated. Instead, use theIndexPattern.destroy
method on the specific index pattern you which to remove.
import { IndexPatterns, IndexPattern } from 'ui/index_patterns';
const indexPatterns = new IndexPatterns(config, savedObjectsClient);
const indexPattern = new IndexPattern('some-id', config, savedObjectsClient, ...etc);
// old
indexPatterns.cache.clear('some-id');
indexPatterns.delete(indexPattern);
// new
indexPatterns.clearCache('some-id'); // clears pattern matching specific ID from cache
indexPatterns.clearCache(); // clears everything
indexPattern.destroy(); // destroys pattern it is called on
via #39247
Use embeddable v2
Any plugins written on the old embeddable infrastructure will need to be updated to use the new embeddable infrastructure. The Embeddables API allows you to add your own custom UI components to a dashboard, without having to go through the visualization plugin system. This infrastructure is highly unstable at the moment. Expect this API to change frequently over the ensuing months.
via #39126
kibana-react
Tools for building React applications in Kibana.
Context
You can create React context that holds Core or plugin services that your plugin depends on.
import { createKibanaReactContext } from 'kibana-react';
class MyPlugin {
start(core, plugins) {
const context = createKibanaReactContext({ ...core, ...plugins });
}
}
You may also want to be explicit about services you depend on.
import { createKibanaReactContext } from 'kibana-react';
class MyPlugin {
start({ notifications, overlays }, { embeddable }) {
const context = createKibanaReactContext({ notifications, overlays, embeddable });
}
}
Wrap your React application in the created context.
<context.Provider>
<KibanaApplication />
</context.Provider>
Or use already pre-created <KibanaContextProvider>
component.
import { KibanaContextProvider } from 'kibana-react';
<KibanaContextProvider services={{ ...core, ...plugins }}>
<KibanaApplication />
</KibanaContextProvider>
<KibanaContextProvider services={{ notifications, overlays, embeddable }}>
<KibanaApplication />
</KibanaContextProvider>
Accessing context
Using useKibana
hook.
import { useKibana } from 'kibana-react';
const Demo = () => {
const kibana = useKibana();
return (
<div>
{kibana.services.uiSettings.get('theme:darkMode') ? 'dark' : 'light'}
</div>
);
};
Using withKibana()
higher order component.
import { withKibana } from 'kibana-react';
const Demo = ({ kibana }) => {
return (
<div>
{kibana.services.uiSettings.get('theme:darkMode') ? 'dark' : 'light'}
</div>
);
};
export default withKibana(Demo);
Using <UseKibana>
render prop.
import { UseKibana } from 'kibana-react';
const Demo = () => {
return (
<UseKibana>{kibana =>
<div>
{kibana.services.uiSettings.get('theme:darkMode') ? 'dark' : 'light'}
</div>
}</UseKibana>
);
};
uiSettings
service
Wrappers around Core's uiSettings
service.
useUiSetting
hook
useUiSetting
synchronously returns the latest setting from CoreStart['uiSettings']
service.
import { useUiSetting } from 'kibana-react';
const Demo = () => {
const darkMode = useUiSetting<boolean>('theme:darkMode');
return (
<div>
{darkMode ? 'dark' : 'light'}
</div>
);
};
Reference
useUiSetting<T>(key: string, defaultValue: T): T;
useUiSetting$
hook
useUiSetting$
synchronously returns the latest setting from CoreStart['uiSettings']
service and
subscribes to changes, re-rendering your component with latest values.
import { useUiSetting$ } from 'kibana-react';
const Demo = () => {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
return (
<div>
{darkMode ? 'dark' : 'light'}
</div>
);
};
Reference
useUiSetting$<T>(key: string, defaultValue: T): [T, (newValue: T) => void];
overlays
service
Wrapper around Core's overlays
service, allows you to display React modals and flyouts
directly without having to use react-dom
library to mount to DOM nodes.
import { createKibanaReactContext } from 'kibana-react';
class MyPlugin {
start(core) {
const { value: { overlays } } = createKibanaReactContext(core);
overlays.openModal(
<div>
Hello world!
</div>
);
}
}
overlays.openModal
— opens modal window.overlays.openFlyout
— opens right side panel.
You can access overlays
service through React context.
const Demo = () => {
const { overlays } = useKibana();
useEffect(() => {
overlays.openModal(
<div>
Oooops! {errorMessage}
</div>
);
}, [errorMessage]);
};
notifications
service
Wrapper around Core's notifications
service, allows you to render React elements
directly without having to use react-dom
library to mount to DOM nodes.
import { createKibanaReactContext } from 'kibana-react';
class MyPlugin {
start(core) {
const { value: { notifications } } = createKibanaReactContext(core);
notifications.toasts.show({
title: <div>Hello</div>,
body: <div>world!</div>
});
}
}
notifications.toasts.show()
— show generic toast message.notifications.toasts.success()
— show positive toast message.notifications.toasts.warning()
— show warning toast message.notifications.toasts.danger()
— show error toast message.
You can access notifications
service through React context.
const Demo = () => {
const { notifications } = useKibana();
useEffect(() => {
notifications.toasts.danger({
title: 'Oooops!',
body: errorMessage,
});
}, [errorMessage]);
};
via #43272