Merge branch 'master' of github.com:elastic/kibana into feature-integrations-manager

This commit is contained in:
John Schulz 2019-11-15 09:56:17 -05:00
commit 505ef0d52f
992 changed files with 19167 additions and 8059 deletions

5
.github/CODEOWNERS vendored
View file

@ -28,11 +28,6 @@
# Canvas
/x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas
# Code
/x-pack/legacy/plugins/code/ @teams/code
/x-pack/test/functional/apps/code/ @teams/code
/x-pack/test/api_integration/apis/code/ @teams/code
# Logs & Metrics UI
/x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/legacy/plugins/integrations_manager/ @elastic/epm

View file

@ -20,6 +20,7 @@
"kibana_react": "src/legacy/core_plugins/kibana_react",
"kibana-react": "src/plugins/kibana_react",
"navigation": "src/legacy/core_plugins/navigation",
"newsfeed": "src/plugins/newsfeed",
"regionMap": "src/legacy/core_plugins/region_map",
"server": "src/legacy/server",
"statusPage": "src/legacy/core_plugins/status_page",

View file

@ -2,7 +2,6 @@ files:
include:
- 'src/legacy/core_plugins/metrics/**/*.s+(a|c)ss'
- 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss'
- 'src/legacy/ui/public/query_bar/**/*.s+(a|c)ss'
- 'src/legacy/ui/public/vislib/**/*.s+(a|c)ss'
- 'x-pack/legacy/plugins/rollup/**/*.s+(a|c)ss'
- 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss'

View file

@ -120,6 +120,7 @@ You should prefer modern language features in a lot of cases, e.g.:
* Prefer arrow function over storing `this` (no `const self = this;`)
* Prefer template strings over string concatenation
* Prefer the spread operator for copying arrays (`[...arr]`) over `arr.slice()`
* Use optional chaining (`?.`) and nullish Coalescing (`??`) over `lodash.get` (and similar utilities)
### Avoid mutability and state

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [App](./kibana-plugin-public.app.md) &gt; [chromeless](./kibana-plugin-public.app.chromeless.md)
## App.chromeless property
Hide the UI chrome when the application is mounted. Defaults to `false`<!-- -->. Takes precedence over chrome service visibility settings.
<b>Signature:</b>
```typescript
chromeless?: boolean;
```

View file

@ -16,5 +16,6 @@ export interface App extends AppBase
| Property | Type | Description |
| --- | --- | --- |
| [chromeless](./kibana-plugin-public.app.chromeless.md) | <code>boolean</code> | Hide the UI chrome when the application is mounted. Defaults to <code>false</code>. Takes precedence over chrome service visibility settings. |
| [mount](./kibana-plugin-public.app.mount.md) | <code>(context: AppMountContext, params: AppMountParameters) =&gt; AppUnmount &#124; Promise&lt;AppUnmount&gt;</code> | A mount function called when the user navigates to this app's route. |

View file

@ -21,12 +21,13 @@ How to configure react-router with a base path:
export class MyPlugin implements Plugin {
setup({ application }) {
application.register({
id: 'my-app',
async mount(context, params) {
const { renderApp } = await import('./application');
return renderApp(context, params);
},
});
id: 'my-app',
async mount(context, params) {
const { renderApp } = await import('./application');
return renderApp(context, params);
},
});
}
}
```

View file

@ -109,17 +109,18 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [HttpStart](./kibana-plugin-public.httpstart.md) | See [HttpServiceBase](./kibana-plugin-public.httpservicebase.md) |
| [IContextProvider](./kibana-plugin-public.icontextprovider.md) | A function that returns a context value for a specific key of given context type. |
| [IToasts](./kibana-plugin-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-public.toastsapi.md)<!-- -->. |
| [OverlayBannerMount](./kibana-plugin-public.overlaybannermount.md) | A function that will mount the banner inside the provided element. |
| [OverlayBannerUnmount](./kibana-plugin-public.overlaybannerunmount.md) | A function that will unmount the banner from the element. |
| [MountPoint](./kibana-plugin-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. |
| [PluginInitializer](./kibana-plugin-public.plugininitializer.md) | The <code>plugin</code> export at the root of a plugin's <code>public</code> directory should conform to this interface. |
| [PluginOpaqueId](./kibana-plugin-public.pluginopaqueid.md) | |
| [RecursiveReadonly](./kibana-plugin-public.recursivereadonly.md) | |
| [SavedObjectAttribute](./kibana-plugin-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value |
| [SavedObjectAttributeSingle](./kibana-plugin-public.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-public.savedobjectattribute.md) |
| [SavedObjectsClientContract](./kibana-plugin-public.savedobjectsclientcontract.md) | SavedObjectsClientContract as implemented by the [SavedObjectsClient](./kibana-plugin-public.savedobjectsclient.md) |
| [Toast](./kibana-plugin-public.toast.md) | |
| [ToastInput](./kibana-plugin-public.toastinput.md) | Inputs for [IToasts](./kibana-plugin-public.itoasts.md) APIs. |
| [ToastInputFields](./kibana-plugin-public.toastinputfields.md) | Allowed fields for [ToastInput](./kibana-plugin-public.toastinput.md)<!-- -->. |
| [ToastsSetup](./kibana-plugin-public.toastssetup.md) | [IToasts](./kibana-plugin-public.itoasts.md) |
| [ToastsStart](./kibana-plugin-public.toastsstart.md) | [IToasts](./kibana-plugin-public.itoasts.md) |
| [UiSettingsClientContract](./kibana-plugin-public.uisettingsclientcontract.md) | Client-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. [UiSettingsClient](./kibana-plugin-public.uisettingsclient.md) |
| [UnmountCallback](./kibana-plugin-public.unmountcallback.md) | A function that will unmount the element previously mounted by the associated [MountPoint](./kibana-plugin-public.mountpoint.md) |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [MountPoint](./kibana-plugin-public.mountpoint.md)
## MountPoint type
A function that should mount DOM content inside the provided container element and return a handler to unmount it.
<b>Signature:</b>
```typescript
export declare type MountPoint = (element: HTMLElement) => UnmountCallback;
```

View file

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [OverlayBannerMount](./kibana-plugin-public.overlaybannermount.md)
## OverlayBannerMount type
A function that will mount the banner inside the provided element.
<b>Signature:</b>
```typescript
export declare type OverlayBannerMount = (element: HTMLElement) => OverlayBannerUnmount;
```

View file

@ -9,14 +9,14 @@ Add a new banner
<b>Signature:</b>
```typescript
add(mount: OverlayBannerMount, priority?: number): string;
add(mount: MountPoint, priority?: number): string;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| mount | <code>OverlayBannerMount</code> | |
| mount | <code>MountPoint</code> | |
| priority | <code>number</code> | |
<b>Returns:</b>

View file

@ -9,7 +9,7 @@ Replace a banner in place
<b>Signature:</b>
```typescript
replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): string;
replace(id: string | undefined, mount: MountPoint, priority?: number): string;
```
## Parameters
@ -17,7 +17,7 @@ replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): s
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string &#124; undefined</code> | |
| mount | <code>OverlayBannerMount</code> | |
| mount | <code>MountPoint</code> | |
| priority | <code>number</code> | |
<b>Returns:</b>

View file

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [OverlayBannerUnmount](./kibana-plugin-public.overlaybannerunmount.md)
## OverlayBannerUnmount type
A function that will unmount the banner from the element.
<b>Signature:</b>
```typescript
export declare type OverlayBannerUnmount = () => void;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [Toast](./kibana-plugin-public.toast.md)
## Toast type
<b>Signature:</b>
```typescript
export declare type Toast = ToastInputFields & {
id: string;
};
```

View file

@ -9,5 +9,5 @@ Inputs for [IToasts](./kibana-plugin-public.itoasts.md) APIs.
<b>Signature:</b>
```typescript
export declare type ToastInput = string | ToastInputFields | Promise<ToastInputFields>;
export declare type ToastInput = string | ToastInputFields;
```

View file

@ -9,7 +9,10 @@ Allowed fields for [ToastInput](./kibana-plugin-public.toastinput.md)<!-- -->.
<b>Signature:</b>
```typescript
export declare type ToastInputFields = Pick<Toast, Exclude<keyof Toast, 'id'>>;
export declare type ToastInputFields = Pick<EuiToast, Exclude<keyof EuiToast, 'id' | 'text' | 'title'>> & {
title?: string | MountPoint;
text?: string | MountPoint;
};
```
## Remarks

View file

@ -22,5 +22,5 @@ add(toastOrTitle: ToastInput): Toast;
`Toast`
a
a [Toast](./kibana-plugin-public.toast.md)

View file

@ -22,5 +22,5 @@ addDanger(toastOrTitle: ToastInput): Toast;
`Toast`
a
a [Toast](./kibana-plugin-public.toast.md)

View file

@ -23,5 +23,5 @@ addError(error: Error, options: ErrorToastOptions): Toast;
`Toast`
a
a [Toast](./kibana-plugin-public.toast.md)

View file

@ -22,5 +22,5 @@ addSuccess(toastOrTitle: ToastInput): Toast;
`Toast`
a
a [Toast](./kibana-plugin-public.toast.md)

View file

@ -22,5 +22,5 @@ addWarning(toastOrTitle: ToastInput): Toast;
`Toast`
a
a [Toast](./kibana-plugin-public.toast.md)

View file

@ -28,5 +28,5 @@ export declare class ToastsApi implements IToasts
| [addSuccess(toastOrTitle)](./kibana-plugin-public.toastsapi.addsuccess.md) | | Adds a new toast pre-configured with the success color and check icon. |
| [addWarning(toastOrTitle)](./kibana-plugin-public.toastsapi.addwarning.md) | | Adds a new toast pre-configured with the warning color and help icon. |
| [get$()](./kibana-plugin-public.toastsapi.get_.md) | | Observable of the toast messages to show to the user. |
| [remove(toast)](./kibana-plugin-public.toastsapi.remove.md) | | Removes a toast from the current array of toasts if present. |
| [remove(toastOrId)](./kibana-plugin-public.toastsapi.remove.md) | | Removes a toast from the current array of toasts if present. |

View file

@ -9,14 +9,14 @@ Removes a toast from the current array of toasts if present.
<b>Signature:</b>
```typescript
remove(toast: Toast): void;
remove(toastOrId: Toast | string): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| toast | <code>Toast</code> | a returned by |
| toastOrId | <code>Toast &#124; string</code> | a [Toast](./kibana-plugin-public.toast.md) returned by [ToastsApi.add()](./kibana-plugin-public.toastsapi.add.md) or its id |
<b>Returns:</b>

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [UnmountCallback](./kibana-plugin-public.unmountcallback.md)
## UnmountCallback type
A function that will unmount the element previously mounted by the associated [MountPoint](./kibana-plugin-public.mountpoint.md)
<b>Signature:</b>
```typescript
export declare type UnmountCallback = () => void;
```

View file

@ -9,11 +9,12 @@ for displayed decimal values.
. Scroll or search for the setting you want to modify.
. Enter a new value for the setting.
[float]
[[settings-read-only-access]]
=== [xpack]#Read only access#
When you have insufficient privileges to edit advanced settings, the following
indicator in Kibana will be displayed. The buttons to edit settings won't be visible.
When you have insufficient privileges to edit advanced settings, the following
indicator in Kibana will be displayed. The buttons to edit settings won't be visible.
For more information on granting access to Kibana see <<xpack-security-authorization>>.
[role="screenshot"]
@ -25,9 +26,9 @@ image::images/settings-read-only-badge.png[Example of Advanced Settings Manageme
WARNING: Modifying a setting can affect {kib}
performance and cause problems that are
difficult to diagnose. Setting a property value to a blank field reverts
difficult to diagnose. Setting a property value to a blank field reverts
to the default behavior, which might not be
compatible with other configuration settings. Deleting a custom setting
compatible with other configuration settings. Deleting a custom setting
removes it from {kib} permanently.
@ -44,7 +45,7 @@ removes it from {kib} permanently.
adapt to the interval between measurements. Keys are http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals].
`dateFormat:tz`:: The timezone that Kibana uses. The default value of `Browser` uses the timezone detected by the browser.
`dateNanosFormat`:: The format to use for displaying https://momentjs.com/docs/#/displaying/format/[pretty formatted dates] of {ref}/date_nanos.html[Elasticsearch date_nanos type].
`defaultIndex`:: The index to access if no index is set. The default is `null`.
`defaultIndex`:: The index to access if no index is set. The default is `null`.
`fields:popularLimit`:: The top N most popular fields to show.
`filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields.
`filters:pinnedByDefault`:: Set this property to `true` to make filters have a global state (be pinned) by default.
@ -59,46 +60,46 @@ mentioned use "\_default_".
`histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values
when necessary.
`history:limit`:: In fields that have history, such as query inputs, show this many recent values.
`indexPattern:fieldMapping:lookBack`:: For index patterns containing timestamps in their names,
`indexPattern:fieldMapping:lookBack`:: For index patterns containing timestamps in their names,
look for this many recent matching patterns from which to query the field mapping.
`indexPattern:placeholder`:: The default placeholder value to use in Management > Index Patterns > Create Index Pattern.
`metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields
`metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields
into the document when displaying it.
`metrics:max_buckets`:: The maximum numbers of buckets that a single
data source can return. This might arise when the user selects a
data source can return. This might arise when the user selects a
short interval (for example, 1s) for a long time period (1 year).
`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character
in a query clause. Only applies when experimental query features are
enabled in the query bar. To disallow leading wildcards in Lucene queries,
`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character
in a query clause. Only applies when experimental query features are
enabled in the query bar. To disallow leading wildcards in Lucene queries,
use `query:queryString:options`.
`query:queryString:options`:: Options for the Lucene query string parser. Only
used when "Query language" is set to Lucene.
`savedObjects:listingLimit`:: The number of objects to fetch for lists of saved objects.
`savedObjects:listingLimit`:: The number of objects to fetch for lists of saved objects.
The default value is 1000. Do not set above 10000.
`savedObjects:perPage`:: The number of objects to show on each page of the
`savedObjects:perPage`:: The number of objects to show on each page of the
list of saved objects. The default is 5.
`search:queryLanguage`:: The query language to use in the query bar.
Choices are <<kuery-query, KQL>>, a language built specifically for {kib}, and the <<lucene-query, Lucene
Choices are <<kuery-query, KQL>>, a language built specifically for {kib}, and the <<lucene-query, Lucene
query syntax>>.
`shortDots:enable`:: Set this property to `true` to shorten long
`shortDots:enable`:: Set this property to `true` to shorten long
field names in visualizations. For example, show `f.b.baz` instead of `foo.bar.baz`.
`sort:options`:: Options for the Elasticsearch {ref}/search-request-body.html#request-body-search-sort[sort] parameter.
`state:storeInSessionStorage`:: [experimental] Kibana tracks UI state in the
URL, which can lead to problems when there is a lot of state information,
and the URL gets very long.
Enabling this setting stores part of the URL in your browser session to keep the
`state:storeInSessionStorage`:: [experimental] Kibana tracks UI state in the
URL, which can lead to problems when there is a lot of state information,
and the URL gets very long.
Enabling this setting stores part of the URL in your browser session to keep the
URL short.
`theme:darkMode`:: Set to `true` to enable a dark mode for the {kib} UI. You must
refresh the page to apply the setting.
`timepicker:quickRanges`:: The list of ranges to show in the Quick section of
the time filter. This should be an array of objects, with each object containing
`from`, `to` (see {ref}/common-options.html#date-math[accepted formats]),
`timepicker:quickRanges`:: The list of ranges to show in the Quick section of
the time filter. This should be an array of objects, with each object containing
`from`, `to` (see {ref}/common-options.html#date-math[accepted formats]),
and `display` (the title to be displayed).
`timepicker:refreshIntervalDefaults`:: The default refresh interval for the time filter. Example: `{ "display": "15 seconds", "pause": true, "value": 15000 }`.
`timepicker:timeDefaults`:: The default selection in the time filter.
`truncate:maxHeight`:: The maximum height that a cell occupies in a table. Set to 0 to disable
truncation.
`xPack:defaultAdminEmail`:: Email address for X-Pack admin operations, such as
`xPack:defaultAdminEmail`:: Email address for X-Pack admin operations, such as
cluster alert notifications from Monitoring.
@ -107,7 +108,7 @@ cluster alert notifications from Monitoring.
=== Accessibility settings
[horizontal]
`accessibility:disableAnimations`:: Turns off all unnecessary animations in the
`accessibility:disableAnimations`:: Turns off all unnecessary animations in the
{kib} UI. Refresh the page to apply the changes.
[float]
@ -124,21 +125,21 @@ cluster alert notifications from Monitoring.
[horizontal]
`context:defaultSize`:: The number of surrounding entries to display in the context view. The default value is 5.
`context:step`:: The number by which to increment or decrement the context size. The default value is 5.
`context:tieBreakerFields`:: A comma-separated list of fields to use
for breaking a tie between documents that have the same timestamp value. The first
`context:tieBreakerFields`:: A comma-separated list of fields to use
for breaking a tie between documents that have the same timestamp value. The first
field that is present and sortable in the current index pattern is used.
`defaultColumns`:: The columns that appear by default on the Discover page.
The default is `_source`.
`discover:aggs:terms:size`:: The number terms that are visualized when clicking
The default is `_source`.
`discover:aggs:terms:size`:: The number terms that are visualized when clicking
the Visualize button in the field drop down. The default is `20`.
`discover:sampleSize`:: The number of rows to show in the Discover table.
`discover:sort:defaultOrder`:: The default sort direction for time-based index patterns.
`discover:searchOnPageLoad`:: Controls whether a search is executed when Discover first loads.
`discover:searchOnPageLoad`:: Controls whether a search is executed when Discover first loads.
This setting does not have an effect when loading a saved search.
`doc_table:hideTimeColumn`:: Hides the "Time" column in Discover and in all saved searches on dashboards.
`doc_table:highlight`:: Highlights results in Discover and saved searches on dashboards.
`doc_table:highlight`:: Highlights results in Discover and saved searches on dashboards.
Highlighting slows requests when
working on big documents.
working on big documents.
@ -150,14 +151,14 @@ working on big documents.
[horizontal]
`notifications:banner`:: A custom banner intended for temporary notices to all users.
Supports https://help.github.com/en/articles/basic-writing-and-formatting-syntax[Markdown].
`notifications:lifetime:banner`:: The duration, in milliseconds, for banner
notification displays. The default value is 3000000. Set this field to `Infinity`
`notifications:lifetime:banner`:: The duration, in milliseconds, for banner
notification displays. The default value is 3000000. Set this field to `Infinity`
to disable banner notifications.
`notifications:lifetime:error`:: The duration, in milliseconds, for error
`notifications:lifetime:error`:: The duration, in milliseconds, for error
notification displays. The default value is 300000. Set this field to `Infinity` to disable error notifications.
`notifications:lifetime:info`:: The duration, in milliseconds, for information notification displays.
`notifications:lifetime:info`:: The duration, in milliseconds, for information notification displays.
The default value is 5000. Set this field to `Infinity` to disable information notifications.
`notifications:lifetime:warning`:: The duration, in milliseconds, for warning notification
`notifications:lifetime:warning`:: The duration, in milliseconds, for warning notification
displays. The default value is 10000. Set this field to `Infinity` to disable warning notifications.
@ -175,8 +176,8 @@ displays. The default value is 10000. Set this field to `Infinity` to disable wa
=== Rollup settings
[horizontal]
`rollups:enableIndexPatterns`:: Enables the creation of index patterns that
capture rollup indices, which in turn enables visualizations based on rollup data.
`rollups:enableIndexPatterns`:: Enables the creation of index patterns that
capture rollup indices, which in turn enables visualizations based on rollup data.
Refresh the page to apply the changes.
@ -188,22 +189,22 @@ Refresh the page to apply the changes.
`courier:batchSearches`:: When disabled, dashboard panels will load individually, and search requests will terminate when
users navigate away or update the query. When enabled, dashboard panels will load together when all of the data is loaded,
and searches will not terminate.
`courier:customRequestPreference`:: {ref}/search-request-body.html#request-body-search-preference[Request preference]
`courier:customRequestPreference`:: {ref}/search-request-body.html#request-body-search-preference[Request preference]
to use when `courier:setRequestPreference` is set to "custom".
`courier:ignoreFilterIfFieldNotInIndex`:: Skips filters that apply to fields that don't exist in the index for a visualization.
`courier:ignoreFilterIfFieldNotInIndex`:: Skips filters that apply to fields that don't exist in the index for a visualization.
Useful when dashboards consist of visualizations from multiple index patterns.
`courier:maxConcurrentShardRequests`:: Controls the {ref}/search-multi-search.html[max_concurrent_shard_requests]
setting used for `_msearch` requests sent by {kib}. Set to 0 to disable this
`courier:maxConcurrentShardRequests`:: Controls the {ref}/search-multi-search.html[max_concurrent_shard_requests]
setting used for `_msearch` requests sent by {kib}. Set to 0 to disable this
config and use the {es} default.
`courier:setRequestPreference`:: Enables you to set which shards handle your search requests.
* *Session ID:* Restricts operations to execute all search requests on the same shards.
* *Session ID:* Restricts operations to execute all search requests on the same shards.
This has the benefit of reusing shard caches across requests.
* *Custom:* Allows you to define your own preference. Use `courier:customRequestPreference`
* *Custom:* Allows you to define your own preference. Use `courier:customRequestPreference`
to customize your preference value.
* *None:* Do not set a preference. This might provide better performance
because requests can be spread across all shard copies. However, results might
* *None:* Do not set a preference. This might provide better performance
because requests can be spread across all shard copies. However, results might
be inconsistent because different shards might be in different refresh states.
`search:includeFrozen`:: Includes {ref}/frozen-indices.html[frozen indices] in results.
`search:includeFrozen`:: Includes {ref}/frozen-indices.html[frozen indices] in results.
Searching through frozen indices
might increase the search time. This setting is off by default. Users must opt-in to include frozen indices.
@ -212,8 +213,8 @@ might increase the search time. This setting is off by default. Users must opt-i
=== SIEM settings
[horizontal]
`siem:defaultAnomalyScore`:: The threshold above which anomalies are displayed in the SIEM app.
`siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events.
`siem:defaultAnomalyScore`:: The threshold above which Machine Learning job anomalies are displayed in the SIEM app.
`siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events.
`siem:refreshIntervalDefaults`:: The default refresh interval for the SIEM time filter, in milliseconds.
`siem:timeDefaults`:: The default period of time in the SIEM time filter.
@ -226,16 +227,16 @@ might increase the search time. This setting is off by default. Users must opt-i
`timelion:default_rows`:: The default number of rows to use on a Timelion sheet.
`timelion:es.default_index`:: The default index when using the `.es()` query.
`timelion:es.timefield`:: The default field containing a timestamp when using the `.es()` query.
`timelion:graphite.url`:: [experimental] Used with graphite queries, this is the URL of your graphite host
in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can be
`timelion:graphite.url`:: [experimental] Used with graphite queries, this is the URL of your graphite host
in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can be
selected from a whitelist configured in the `kibana.yml` under `timelion.graphiteUrls`.
`timelion:max_buckets`:: The maximum number of buckets a single data source can return.
This value is used for calculating automatic intervals in visualizations.
`timelion:min_interval`:: The smallest interval to calculate when using "auto".
`timelion:quandl.key`:: [experimental] Used with quandl queries, this is your API key from https://www.quandl.com/[www.quandl.com].
`timelion:showTutorial`:: Shows the Timelion tutorial
`timelion:showTutorial`:: Shows the Timelion tutorial
to users when they first open the Timelion app.
`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations,
`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations,
this is the number of buckets to try to represent.
@ -246,18 +247,18 @@ this is the number of buckets to try to represent.
[horizontal]
`visualization:colorMapping`:: Maps values to specified colors in visualizations.
`visualization:dimmingOpacity`:: The opacity of the chart items that are dimmed
when highlighting another element of the chart. The lower this number, the more
`visualization:dimmingOpacity`:: The opacity of the chart items that are dimmed
when highlighting another element of the chart. The lower this number, the more
the highlighted element stands out. This must be a number between 0 and 1.
`visualization:loadingDelay`:: The time to wait before dimming visualizations
`visualization:loadingDelay`:: The time to wait before dimming visualizations
during a query.
`visualization:regionmap:showWarnings`:: Shows
`visualization:regionmap:showWarnings`:: Shows
a warning in a region map when terms cannot be joined to a shape.
`visualization:tileMap:WMSdefaults`:: The default properties for the WMS map server support in the coordinate map.
`visualization:tileMap:maxPrecision`:: The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high,
and 12 is the maximum. See this
and 12 is the maximum. See this
{ref}/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[explanation of cell dimensions].
`visualize:enableLabs`:: Enables users to create, view, and edit experimental visualizations.
`visualize:enableLabs`:: Enables users to create, view, and edit experimental visualizations.
If disabled, only visualizations that are considered production-ready are available to the user.
@ -265,6 +266,5 @@ If disabled, only visualizations that are considered production-ready are availa
[[kibana-telemetry-settings]]
=== Usage data settings
Helps improve the Elastic Stack by providing usage statistics for
Helps improve the Elastic Stack by providing usage statistics for
basic features. This data will not be shared outside of Elastic.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

View file

@ -1,85 +0,0 @@
[role="xpack"]
[[xpack-dashboard-only-mode]]
== Dashboard-only mode
deprecated[7.4.0, "Using the `kibana_dashboard_only_user` role is deprecated. Use <<kibana-feature-privileges,feature privileges>> instead."]
In dashboard-only mode, users have access to only the *Dashboard* app.
Users can view and filter the dashboards, but cannot create, edit, or delete
them. This enables you to:
* Show off your dashboards without giving users access to all of {kib}
* Share your {kib} dashboards without the risk of users accidentally
editing or deleting them
Dashboard-only mode pairs well with fullscreen mode.
You can share your dashboard with the team responsible
for showing the dashboard on a big-screen monitor, and not worry about it being modified.
[role="screenshot"]
image:management/dashboard_only_mode/images/view_only_dashboard.png["View Only Dashboard"]
[[setup-dashboard-only-mode]]
[float]
=== Assign dashboard-only mode
With {security} enabled, you can restrict users to dashboard-only mode by assigning
them the built-in `kibana_dashboard_only_user` role.
. Go to *Management > Security > Users*.
. Create or edit a user.
. Assign the `kibana_dashboard_only_user` role and a role that <<grant-read-access-to-indices, grants `read` access to the data indices>>.
+
For example,
to enable users to view the dashboards in the sample data sets, you must assign them
the `kibana_dashboard_only_user` role and a role that has
`read` access to the kibana_* indices.
+
[role="screenshot"]
image:management/dashboard_only_mode/images/dashboard-only-user-role.png["Dashboard Only mode has no editing controls"]
[IMPORTANT]
===========================================
* If you assign users the `kibana_dashboard_only_user` role and a role
with write permissions to {kib}, they *will* have write access,
even though the controls remain hidden in {kib}.
* If you also assign users the reserved `superuser` role, they will have full
access to {kib}.
===========================================
[float]
[[grant-read-access-to-indices]]
=== Grant read access to indices
The `kibana_dashboard_only_user` role
does not provide access to data indices.
You must also assign the user a role that grants `read` access
to each index you are using. Use *Management > Security > Roles* to create or edit a
role and assign index privileges.
For information on roles and privileges, see {ref}/authorization.html[User authorization].
[role="screenshot"]
image:management/dashboard_only_mode/images/custom_dashboard_mode_role.png["Dashboard Only mode has no editing controls"]
[float]
[[advanced-dashboard-mode-configuration]]
=== Advanced settings for dashboard only mode
The `kibana_dashboard_only_user` role grants access to all spaces.
If your setup requires access to a
subset of spaces, you can create a custom role, and then tag it as Dashboard only mode.
. Go to *Management > Advanced Settings*, and search for `xpackDashboardMode:roles`.
+
By
default, this is set to
`kibana_dashboard_only_user`.
. Add as many roles as you require.
+
[role="screenshot"]
image:management/dashboard_only_mode/images/advanced_dashboard_mode_role_setup.png["Advanced dashboard mode role setup"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 KiB

View file

@ -7,6 +7,14 @@ Use {ref}/search-aggregations.html[aggregations] to plot large data sets without
Aggregations group your documents into buckets and calculate metrics for each bucket.
Your documents stay in Elasticsearch and only the metrics for each group are returned to your computer.
Use aggregated layers with document layers to show aggregated views when the map shows larger
amounts of the globe and individual documents when the map shows smaller regions.
In the following example, the Grid aggregation layer is only visible when the map is at zoom levels 0 through 5. The Documents layer is only visible when the map is at zoom levels 4 through 24.
See the <<maps-add-elasticsearch-layer, Getting started>> tutorial for more details on configuring the layers.
[role="screenshot"]
image::maps/images/grid_to_docs.gif[]
[role="xpack"]
[[maps-grid-aggregation]]

View file

@ -150,6 +150,7 @@ image::maps/images/grid_metrics_both.png[]
. In the map legend, click *Add layer*.
. Click the *Grid aggregation* data source.
. Set *Index pattern* to *kibana_sample_data_logs*.
. Set *Show as* to *points*.
. Click the *Add layer* button.
. Set *Layer name* to `Total Requests and Bytes`.
. Set *Zoom range for layer visibility* to the range [0, 9].
@ -181,7 +182,7 @@ Now that your map is complete, you'll want to save it so others can use it.
. In the application toolbar, click *Save*.
. Enter `Tutorial web logs map` for the title.
. Click *Confirm Save*.
. Click *Save*.
+
You have completed the steps for re-creating the sample data map.

View file

@ -38,4 +38,10 @@ This page has moved. Please see <<xpack-logs-configuring>>.
[role="exclude",id="extend"]
== Extend your use case
This page was deleted. See <<xpack-graph>> and <<xpack-ml>>.
This page was deleted. See <<xpack-graph>> and <<xpack-ml>>.
[role="exclude",id="xpack-dashboard-only-mode"]
== Dashboard-only mode
Using the `kibana_dashboard_only_user` role is deprecated.
Use <<kibana-feature-privileges,feature privileges>> instead.

View file

@ -32,7 +32,8 @@ strongly recommend that you keep the default CSP rules that ship with Kibana.
`csp.strict:`:: *Default: `true`* Blocks access to Kibana to any browser that
does not enforce even rudimentary CSP rules. In practice, this will disable
support for older, less safe browsers like Internet Explorer.
support for older, less safe browsers like Internet Explorer.
See <<csp-strict-mode, Content Security Policy>> for more information.
`csp.warnLegacyBrowsers:`:: *Default: `true`* Shows a warning message after
loading Kibana to any browser that does not enforce even rudimentary CSP rules,
@ -240,6 +241,10 @@ Kibana reads this url from an external metadata service, but users can still
override this parameter to use their own Tile Map Service. For example:
`"https://tiles.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana"`
`newsfeed.enabled:` :: *Default: `true`* Controls whether to enable the newsfeed
system for the Kibana UI notification center. Set to `false` to disable the
newsfeed system.
`path.data:`:: *Default: `data`* The path where Kibana stores persistent data
not saved in Elasticsearch.
@ -315,6 +320,18 @@ supported protocols with versions. Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2
setting this to `true` enables unauthenticated users to access the Kibana
server status API and status page.
`telemetry.allowChangingOptInStatus`:: *Default: true*. If `true`,
users are able to change the telemetry setting at a later time in
<<advanced-options, Advanced Settings>>. If `false`,
{kib} looks at the value of `telemetry.optIn` to determine whether to send
telemetry data or not. `telemetry.allowChangingOptInStatus` and `telemetry.optIn`
cannot be `false` at the same time.
`telemetry.optIn`:: *Default: true* If `true`, telemetry data is sent to Elastic.
If `false`, collection of telemetry data is disabled.
To enable telemetry and prevent users from disabling it,
set `telemetry.allowChangingOptInStatus` to `false` and `telemetry.optIn` to `true`.
`vega.enableExternalUrls:`:: *Default: false* Set this value to true to allow Vega to use any URL to access external data sources and images. If false, Vega can only get data from Elasticsearch.
`xpack.license_management.enabled`:: *Default: true* Set this value to false to

View file

@ -2,7 +2,15 @@
== Upgrading {kib}
Depending on the {kib} version you're upgrading from, the upgrade process to 7.0
varies.
varies.
NOTE: {kib} upgrades automatically when starting a new version, as described in
<<upgrade-migrations, this document>>.
Although you do not need to manually back up {kib} before upgrading, we recommend
that you have a backup on hand. You can use
<<snapshot-repositories, Snapshot and Restore>> to back up {kib}
data by targeting `.kibana*` indices. If you are using the Reporting plugin,
you can also target `.reporting*` indices.
[float]
[[upgrade-before-you-begin]]
@ -12,7 +20,7 @@ Before you upgrade {kib}:
* Consult the <<breaking-changes,breaking changes>>.
* Before you upgrade production servers, test the upgrades in a dev environment.
* Backup your data with {es} {ref}/modules-snapshots.html[snapshots].
* Back up your data with {es} {ref}/modules-snapshots.html[snapshots].
To roll back to an earlier version, you **must** have a backup of your data.
* If you are using custom plugins, check that a compatible version is
available.

View file

@ -154,7 +154,6 @@ To open an element for editing, put the dashboard in *Edit* mode,
and then select *Edit* from the panel menu. The changes you make appear in
every dashboard that uses the element.
include::{kib-repo-dir}/management/dashboard_only_mode/index.asciidoc[]

View file

@ -59,14 +59,14 @@ image::user/graph/images/graph-read-only-badge.png[Example of Graph's read only
[discrete]
[[disable-drill-down]]
=== Disabling drill down configuration
=== Disabling drilldown configuration
By default, users can configure _drill down_ URLs to display additional
By default, users can configure _drilldown_ URLs to display additional
information about a selected vertex in a new browser window. For example,
you could configure a drill down URL to perform a web search for the selected
you could configure a drilldown URL to perform a web search for the selected
vertex term.
To prevent users from adding drill down URLs, set
To prevent users from adding drilldown URLs, set
`xpack.graph.canEditDrillDownUrls` to `false` in `kibana.yml`:
[source,yaml]

View file

@ -2,30 +2,30 @@
[[graph-getting-started]]
== Using Graph
Graph is automatically enabled in {es} and {kib}.
You must index data into {es} before you can create a graph.
<<index-patterns, Learn how>> or get started with a <<add-sample-data, sample data set>>.
[float]
[[exploring-connections]]
To start exploring connections in your data:
=== Graph connections in your data
. From the side navigation, open the graph explorer.
. Select an index pattern to specify what indices you want to explore.
. From the side navigation, open *Graph*.
+
For example, if you are indexing log data with Logstash, you could select the
`logstash-*` index pattern to visualize connections within the log entries.
If this is your first graph, follow the prompts to create it.
For subsequent graphs, click *New*.
. Select one or more multi-value fields that contain the terms you want to
. Select a data source to explore.
. Add one or more multi-value fields that contain the terms you want to
graph.
+
The vertices in the graph are selected from these terms. If you're
visualizing connections between Apache log entries, you could select the
`url.raw` field and the `geo.src` field so you can look at which pages are
being accessed from different locations.
The vertices in the graph are selected from these terms.
. Enter a search query to discover relationships between terms in the selected
fields.
+
For example, to generate a graph of the successful requests to
For example, if you are using the {kib} sample web logs data set, and you want
to generate a graph of the successful requests to
particular pages from different locations, you could search for the 200
response code. The weight of the connection between two vertices indicates how strongly they
are related.
@ -38,25 +38,86 @@ image::user/graph/images/graph-url-connections.png["URL connections"]
[role="screenshot"]
image::user/graph/images/graph-link-summary.png["Link summary"]
. Use the toolbar buttons to explore
. Use the control bar on the right to explore
additional connections:
+
* To display additional vertices that connect to your graph, click Expand
image:user/graph/images/graph-expand-button.jpg[Expand Selection].
* To display additional vertices that connect to your graph, click the expand icon
image:user/graph/images/graph-expand-button.png[Expand Selection].
* To display additional
connections between the displayed vertices, click Link
image:user/graph/images/graph-link-button.jpg[Add links to existing terms]
connections between the displayed vertices, click the link icon
image:user/graph/images/graph-link-button.png[Add links to existing terms].
* To explore a particular area of the
graph, select the vertices you are interested in, and click Expand or Link.
* To step back through your changes to the graph, click Undo
image:user/graph/images/graph-undo-button.jpg[Undo].
graph, select the vertices you are interested in, and then click expand or link.
* To step back through your changes to the graph, click undo
image:user/graph/images/graph-undo-button.png[Undo] and redo
image:user/graph/images/graph-redo-button.png[Redo].
. To see more relationships in your data, submit additional queries.
+
[role="screenshot"]
image::user/graph/images/graph-add-query.png["Adding networks"]
. *Save* your graph.
NOTE: By default, when you submit a search query, Graph searches all available
fields. You can constrain your search to a particular field using the Lucene
query syntax. For example, `machine.os: osx`.
[float]
[[style-vertex-properties]]
=== Style vertex properties
Each vertex has a color, icon, and label. To change
the color or icon of all vertices
of a certain field, click the field badge below the search bar, and then
select *Edit settings*.
To change the color and label of selected vertices,
click the style icon image:user/graph/images/graph-style-button.png[Style]
in the control bar on the right.
[float]
[[edit-graph-settings]]
=== Edit graph settings
By default, *Graph* is configured to tune out noise in your data.
If this isn't a good fit for your data, use *Settings > Advanced settings*
to adjust the way *Graph* queries your data. You can tune the graph to show
only the results relevant to you and to improve performance.
For more information, see <<graph-troubleshooting, Graph troubleshooting>>.
You can configure the number of vertices that a search or
expand operation adds to the graph.
By default, only the five most relevant terms for any given field are added
at a time. This keeps the graph from overflowing. To increase this number, click
a field below the search bar, select *Edit Settings*, and change *Terms per hop*.
[float]
[[graph-block-terms]]
=== Block terms from the graph
Documents that match a blocked term are not allowed in the graph.
To block a term, select its vertex and click
the block icon
image:user/graph/images/graph-block-button.png[Block selection]
in the control panel.
For a list of blocked terms, go to *Settings > Blocked terms*.
[float]
[[graph-drill-down]]
=== Drill down into raw documents
With drilldowns, you can display additional information about a
selected vertex in a new browser window. For example, you might
configure a drilldown URL to perform a web search for the selected vertex term.
Use the drilldown icon image:user/graph/images/graph-info-icon.png[Drilldown selection]
in the control panel to show the drilldown buttons for the selected vertices.
To configure drilldowns, go to *Settings > Drilldowns*. See also
<<disable-drill-down, Disabling drilldown configuration>>.
[float]
[[graph-run-layout]]
=== Run and pause layout
Graph uses a "force layout", where vertices behave like magnets,
pushing off of one another. By default, when you add a new vertex to
the graph, all vertices begin moving. In some cases, the movement might
go on for some time. To freeze the current vertex position,
click the pause icon
image:user/graph/images/graph-pause-button.png[Block selection]
in the control panel.

BIN
docs/user/graph/images/graph-add-query.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

After

Width:  |  Height:  |  Size: 303 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

BIN
docs/user/graph/images/graph-link-summary.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 242 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

BIN
docs/user/graph/images/graph-url-connections.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Before After
Before After

View file

@ -1,6 +1,6 @@
[role="xpack"]
[[graph-troubleshooting]]
== Graph Troubleshooting
== Graph troubleshooting
++++
<titleabbrev>Troubleshooting</titleabbrev>
++++

View file

@ -23,6 +23,23 @@ You cannot monitor a version 6.3 or later cluster from {kib} version 6.2 or earl
To resolve this issue, upgrade {kib} to 6.3 or later. See
{stack-ref}/upgrading-elastic-stack.html[Upgrading the {stack}].
[float]
=== {filebeat} index is corrupt
*Symptoms:*
The *Stack Monitoring* application displays a Monitoring Request error indicating
that an illegal argument exception has occurred because fielddata is disabled on
text fields by default.
*Resolution*
. Stop all your {filebeat} instances.
. Delete indices beginning with `filebeat-$VERSION`, where `VERSION` corresponds
to the version of {filebeat} running.
. Restart all your {filebeat} instances.
[float]
=== No monitoring data is visible in {kib}

View file

@ -82,7 +82,7 @@
"**/@types/react": "16.8.3",
"**/@types/hapi": "^17.0.18",
"**/@types/angular": "^1.6.56",
"**/typescript": "3.5.3",
"**/typescript": "3.7.2",
"**/graphql-toolkit/lodash": "^4.17.13",
"**/isomorphic-git/**/base64-js": "^1.2.1",
"**/image-diff/gm/debug": "^2.6.9"
@ -109,7 +109,7 @@
"@elastic/charts": "^14.0.0",
"@elastic/datemath": "5.0.2",
"@elastic/ems-client": "1.0.5",
"@elastic/eui": "14.8.0",
"@elastic/eui": "14.9.0",
"@elastic/filesaver": "1.1.2",
"@elastic/good": "8.1.1-kibana2",
"@elastic/numeral": "2.3.3",
@ -348,8 +348,8 @@
"@types/uuid": "^3.4.4",
"@types/vinyl-fs": "^2.4.11",
"@types/zen-observable": "^0.8.0",
"@typescript-eslint/eslint-plugin": "^2.5.0",
"@typescript-eslint/parser": "^2.5.0",
"@typescript-eslint/eslint-plugin": "^2.7.0",
"@typescript-eslint/parser": "^2.7.0",
"angular-mocks": "^1.7.8",
"archiver": "^3.1.1",
"axe-core": "^3.3.2",
@ -362,7 +362,7 @@
"chance": "1.0.18",
"cheerio": "0.22.0",
"chokidar": "3.2.1",
"chromedriver": "^77.0.0",
"chromedriver": "78.0.1",
"classnames": "2.2.6",
"dedent": "^0.7.0",
"delete-empty": "^2.0.0",
@ -436,7 +436,7 @@
"pngjs": "^3.4.0",
"postcss": "^7.0.5",
"postcss-url": "^8.0.0",
"prettier": "^1.18.2",
"prettier": "^1.19.1",
"proxyquire": "1.8.0",
"regenerate": "^1.4.0",
"sass-lint": "^1.12.1",
@ -447,7 +447,7 @@
"supertest": "^3.1.0",
"supertest-as-promised": "^4.0.2",
"tree-kill": "^1.2.1",
"typescript": "3.5.3",
"typescript": "3.7.2",
"typings-tester": "^0.3.2",
"vinyl-fs": "^3.0.3",
"xml2js": "^0.4.22",

View file

@ -15,8 +15,8 @@
},
"homepage": "https://github.com/elastic/eslint-config-kibana#readme",
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^2.5.0",
"@typescript-eslint/parser": "^2.5.0",
"@typescript-eslint/eslint-plugin": "^2.7.0",
"@typescript-eslint/parser": "^2.7.0",
"babel-eslint": "^10.0.3",
"eslint": "^6.5.1",
"eslint-plugin-babel": "^5.3.0",

View file

@ -17,6 +17,6 @@
"@babel/cli": "7.5.5",
"@kbn/dev-utils": "1.0.0",
"@kbn/babel-preset": "1.0.0",
"typescript": "3.5.3"
"typescript": "3.7.2"
}
}

View file

@ -10,7 +10,7 @@
"kbn:bootstrap": "yarn build"
},
"devDependencies": {
"typescript": "3.5.3"
"typescript": "3.7.2"
},
"peerDependencies": {
"joi": "^13.5.2",

View file

@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`#scheme returns error when shorter string 1`] = `"expected URI with scheme [http|https] but but got [ftp://elastic.co]."`;
exports[`#scheme returns error when shorter string 1`] = `"expected URI with scheme [http|https] but got [ftp://elastic.co]."`;
exports[`#scheme returns error when shorter string 2`] = `"expected URI with scheme [http|https] but but got [file:///kibana.log]."`;
exports[`#scheme returns error when shorter string 2`] = `"expected URI with scheme [http|https] but got [file:///kibana.log]."`;
exports[`#validate throws when returns string 1`] = `"validator failure"`;

View file

@ -36,7 +36,7 @@ export class URIType extends Type<string> {
case 'string.base':
return `expected value of type [string] but got [${typeDetect(value)}].`;
case 'string.uriCustomScheme':
return `expected URI with scheme [${scheme}] but but got [${value}].`;
return `expected URI with scheme [${scheme}] but got [${value}].`;
case 'string.uri':
return `value is [${value}] but it must be a valid URI (see RFC 3986).`;
}

View file

@ -21,7 +21,7 @@
"tslib": "^1.9.3"
},
"devDependencies": {
"typescript": "3.5.3",
"typescript": "3.7.2",
"@kbn/expect": "1.0.0",
"chance": "1.0.18"
}

View file

@ -29,10 +29,7 @@ import { first, ignoreElements, mergeMap } from 'rxjs/operators';
*/
export function observeReadable(readable: Readable): Rx.Observable<never> {
return Rx.race(
Rx.fromEvent(readable, 'end').pipe(
first(),
ignoreElements()
),
Rx.fromEvent(readable, 'end').pipe(first(), ignoreElements()),
Rx.fromEvent(readable, 'error').pipe(
first(),

View file

@ -112,10 +112,7 @@ describe('#getWritten$()', () => {
const done$ = new Rx.Subject();
const promise = log
.getWritten$()
.pipe(
takeUntil(done$),
toArray()
)
.pipe(takeUntil(done$), toArray())
.toPromise();
log.debug('foo');

View file

@ -20,7 +20,7 @@
"@babel/core": "^7.5.5",
"@babel/plugin-transform-async-to-generator": "^7.5.0",
"jest": "^24.9.0",
"typescript": "3.5.3"
"typescript": "3.7.2"
},
"jest": {
"testEnvironment": "node"

View file

@ -21,7 +21,7 @@
"del": "^5.1.0",
"getopts": "^2.2.4",
"supports-color": "^7.0.0",
"typescript": "3.5.3"
"typescript": "3.7.2"
},
"dependencies": {
"intl-format-cache": "^2.1.0",

View file

@ -50,7 +50,7 @@
"log-symbols": "^2.2.0",
"ncp": "^2.0.0",
"ora": "^1.4.0",
"prettier": "^1.18.2",
"prettier": "^1.19.1",
"read-pkg": "^5.2.0",
"rxjs": "^6.5.3",
"spawn-sync": "^1.0.15",
@ -58,7 +58,7 @@
"strip-ansi": "^4.0.0",
"strong-log-transformer": "^2.1.0",
"tempy": "^0.3.0",
"typescript": "3.5.3",
"typescript": "3.7.2",
"unlazy-loader": "^0.1.3",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",

View file

@ -101,7 +101,12 @@ test('handles dependencies of dependencies', async () => {
'packages/baz'
);
const projects = new Map([['kibana', kibana], ['foo', foo], ['bar', bar], ['baz', baz]]);
const projects = new Map([
['kibana', kibana],
['foo', foo],
['bar', bar],
['baz', baz],
]);
const projectGraph = buildProjectGraph(projects);
const logMock = jest.spyOn(console, 'log').mockImplementation(noop);
@ -133,7 +138,10 @@ test('does not run installer if no deps in package', async () => {
'packages/bar'
);
const projects = new Map([['kibana', kibana], ['bar', bar]]);
const projects = new Map([
['kibana', kibana],
['bar', bar],
]);
const projectGraph = buildProjectGraph(projects);
const logMock = jest.spyOn(console, 'log').mockImplementation(noop);
@ -193,7 +201,10 @@ test('calls "kbn:bootstrap" scripts and links executables after installing deps'
'packages/bar'
);
const projects = new Map([['kibana', kibana], ['bar', bar]]);
const projects = new Map([
['kibana', kibana],
['bar', bar],
]);
const projectGraph = buildProjectGraph(projects);
jest.spyOn(console, 'log').mockImplementation(noop);

View file

@ -18,7 +18,7 @@
"homepage": "https://github.com/jbudz/spec-to-console#readme",
"devDependencies": {
"jest": "^24.9.0",
"prettier": "^1.18.2"
"prettier": "^1.19.1"
},
"dependencies": {
"commander": "^3.0.0",

View file

@ -23,4 +23,5 @@ require('@kbn/test').runTestsCli([
require.resolve('../test/api_integration/config.js'),
require.resolve('../test/plugin_functional/config.js'),
require.resolve('../test/interpreter_functional/config.js'),
require.resolve('../test/ui_capabilities/newsfeed_err/config.ts'),
]);

View file

@ -1130,7 +1130,7 @@ import { setup, start } from '../core_plugins/visualizations/public/legacy';
| ------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `import 'ui/apply_filters'` | `import { ApplyFiltersPopover } from '../data/public'` | `import '../data/public/legacy` should be called to load legacy directives |
| `import 'ui/filter_bar'` | `import { FilterBar } from '../data/public'` | `import '../data/public/legacy` should be called to load legacy directives |
| `import 'ui/query_bar'` | `import { QueryBar, QueryBarInput } from '../data/public'` | Directives are deprecated. |
| `import 'ui/query_bar'` | `import { QueryBarInput } from '../data/public'` | Directives are deprecated. |
| `import 'ui/search_bar'` | `import { SearchBar } from '../data/public'` | Directive is deprecated. |
| `import 'ui/kbn_top_nav'` | `import { TopNavMenu } from '../navigation/public'` | Directive is still available in `ui/kbn_top_nav`. |
| `ui/saved_objects/components/saved_object_finder` | `import { SavedObjectFinder } from '../kibana_react/public'` | |

View file

@ -80,6 +80,12 @@ export interface App extends AppBase {
* @returns An unmounting function that will be called to unmount the application.
*/
mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise<AppUnmount>;
/**
* Hide the UI chrome when the application is mounted. Defaults to `false`.
* Takes precedence over chrome service visibility settings.
*/
chromeless?: boolean;
}
/** @internal */
@ -145,12 +151,13 @@ export interface AppMountParameters {
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* async mount(context, params) {
* const { renderApp } = await import('./application');
* return renderApp(context, params);
* },
* });
* id: 'my-app',
* async mount(context, params) {
* const { renderApp } = await import('./application');
* return renderApp(context, params);
* },
* });
* }
* }
* ```
*

View file

@ -26,351 +26,423 @@ import { applicationServiceMock } from '../application/application_service.mock'
import { httpServiceMock } from '../http/http_service.mock';
import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock';
import { notificationServiceMock } from '../notifications/notifications_service.mock';
import { ChromeService } from './chrome_service';
import { docLinksServiceMock } from '../doc_links/doc_links_service.mock';
import { ChromeService } from './chrome_service';
import { App } from '../application';
class FakeApp implements App {
public title = `${this.id} App`;
public mount = () => () => {};
constructor(public id: string, public chromeless?: boolean) {}
}
const store = new Map();
const originalLocalStorage = window.localStorage;
(window as any).localStorage = {
setItem: (key: string, value: string) => store.set(String(key), String(value)),
getItem: (key: string) => store.get(String(key)),
removeItem: (key: string) => store.delete(String(key)),
};
function defaultStartDeps() {
return {
function defaultStartDeps(availableApps?: App[]) {
const deps = {
application: applicationServiceMock.createInternalStartContract(),
docLinks: docLinksServiceMock.createStartContract(),
http: httpServiceMock.createStartContract(),
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
notifications: notificationServiceMock.createStartContract(),
};
if (availableApps) {
deps.application.availableApps = new Map(availableApps.map(app => [app.id, app]));
}
return deps;
}
async function start({
options = { browserSupportsCsp: true },
cspConfigMock = { warnLegacyBrowsers: true },
startDeps = defaultStartDeps(),
}: { options?: any; cspConfigMock?: any; startDeps?: ReturnType<typeof defaultStartDeps> } = {}) {
const service = new ChromeService(options);
if (cspConfigMock) {
startDeps.injectedMetadata.getCspConfig.mockReturnValue(cspConfigMock);
}
return {
service,
startDeps,
chrome: await service.start(startDeps),
};
}
beforeEach(() => {
store.clear();
window.history.pushState(undefined, '', '#/home?a=b');
});
afterAll(() => {
(window as any).localStorage = originalLocalStorage;
});
describe('start', () => {
it('adds legacy browser warning if browserSupportsCsp is disabled and warnLegacyBrowsers is enabled', async () => {
const service = new ChromeService({ browserSupportsCsp: false });
const startDeps = defaultStartDeps();
startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
await service.start(startDeps);
const { startDeps } = await start({ options: { browserSupportsCsp: false } });
expect(startDeps.notifications.toasts.addWarning.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Your browser does not meet the security requirements for Kibana.",
],
]
`);
Array [
Array [
"Your browser does not meet the security requirements for Kibana.",
],
]
`);
});
it('does not add legacy browser warning if browser supports CSP', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const startDeps = defaultStartDeps();
startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
await service.start(startDeps);
const { startDeps } = await start();
expect(startDeps.notifications.toasts.addWarning).not.toBeCalled();
});
it('does not add legacy browser warning if warnLegacyBrowsers is disabled', async () => {
const service = new ChromeService({ browserSupportsCsp: false });
const startDeps = defaultStartDeps();
startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: false });
await service.start(startDeps);
const { startDeps } = await start({
options: { browserSupportsCsp: false },
cspConfigMock: { warnLegacyBrowsers: false },
});
expect(startDeps.notifications.toasts.addWarning).not.toBeCalled();
});
describe('getComponent', () => {
it('returns a renderable React component', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const { chrome } = await start();
// Have to do some fanagling to get the type system and enzyme to accept this.
// Don't capture the snapshot because it's 600+ lines long.
expect(shallow(React.createElement(() => start.getHeaderComponent()))).toBeDefined();
expect(shallow(React.createElement(() => chrome.getHeaderComponent()))).toBeDefined();
});
});
describe('brand', () => {
it('updates/emits the brand as it changes', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getBrand$()
.pipe(toArray())
.toPromise();
start.setBrand({
chrome.setBrand({
logo: 'big logo',
smallLogo: 'not so big logo',
});
start.setBrand({
chrome.setBrand({
logo: 'big logo without small logo',
});
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
Object {},
Object {
"logo": "big logo",
"smallLogo": "not so big logo",
},
Object {
"logo": "big logo without small logo",
"smallLogo": undefined,
},
]
`);
Array [
Object {},
Object {
"logo": "big logo",
"smallLogo": "not so big logo",
},
Object {
"logo": "big logo without small logo",
"smallLogo": undefined,
},
]
`);
});
});
describe('visibility', () => {
it('updates/emits the visibility', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getIsVisible$()
.pipe(toArray())
.toPromise();
start.setIsVisible(true);
start.setIsVisible(false);
start.setIsVisible(true);
chrome.setIsVisible(true);
chrome.setIsVisible(false);
chrome.setIsVisible(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
true,
true,
false,
true,
]
`);
Array [
true,
true,
false,
true,
]
`);
});
it('always emits false if embed query string is in hash when set up', async () => {
it('always emits false if embed query string is preset when set up', async () => {
window.history.pushState(undefined, '', '#/home?a=b&embed=true');
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getIsVisible$()
.pipe(toArray())
.toPromise();
start.setIsVisible(true);
start.setIsVisible(false);
start.setIsVisible(true);
chrome.setIsVisible(true);
chrome.setIsVisible(false);
chrome.setIsVisible(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
false,
false,
false,
false,
]
`);
Array [
false,
false,
false,
false,
]
`);
});
it('application-specified visibility on mount', async () => {
const startDeps = defaultStartDeps([
new FakeApp('alpha'), // An undefined `chromeless` is the same as setting to false.
new FakeApp('beta', true),
new FakeApp('gamma', false),
]);
const { availableApps, currentAppId$ } = startDeps.application;
const { chrome, service } = await start({ startDeps });
const promise = chrome
.getIsVisible$()
.pipe(toArray())
.toPromise();
[...availableApps.keys()].forEach(appId => currentAppId$.next(appId));
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
true,
true,
false,
true,
]
`);
});
it('changing visibility has no effect on chrome-hiding application', async () => {
const startDeps = defaultStartDeps([new FakeApp('alpha', true)]);
const { currentAppId$ } = startDeps.application;
const { chrome, service } = await start({ startDeps });
const promise = chrome
.getIsVisible$()
.pipe(toArray())
.toPromise();
currentAppId$.next('alpha');
chrome.setIsVisible(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
true,
false,
false,
]
`);
});
});
describe('is collapsed', () => {
it('updates/emits isCollapsed', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getIsCollapsed$()
.pipe(toArray())
.toPromise();
start.setIsCollapsed(true);
start.setIsCollapsed(false);
start.setIsCollapsed(true);
chrome.setIsCollapsed(true);
chrome.setIsCollapsed(false);
chrome.setIsCollapsed(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
false,
true,
false,
true,
]
`);
Array [
false,
true,
false,
true,
]
`);
});
it('only stores true in localStorage', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const { chrome } = await start();
start.setIsCollapsed(true);
chrome.setIsCollapsed(true);
expect(store.size).toBe(1);
start.setIsCollapsed(false);
chrome.setIsCollapsed(false);
expect(store.size).toBe(0);
});
});
describe('application classes', () => {
it('updates/emits the application classes', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getApplicationClasses$()
.pipe(toArray())
.toPromise();
start.addApplicationClass('foo');
start.addApplicationClass('foo');
start.addApplicationClass('bar');
start.addApplicationClass('bar');
start.addApplicationClass('baz');
start.removeApplicationClass('bar');
start.removeApplicationClass('foo');
chrome.addApplicationClass('foo');
chrome.addApplicationClass('foo');
chrome.addApplicationClass('bar');
chrome.addApplicationClass('bar');
chrome.addApplicationClass('baz');
chrome.removeApplicationClass('bar');
chrome.removeApplicationClass('foo');
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
Array [],
Array [
"foo",
],
Array [
"foo",
],
Array [
"foo",
"bar",
],
Array [
"foo",
"bar",
],
Array [
"foo",
"bar",
"baz",
],
Array [
"foo",
"baz",
],
Array [
"baz",
],
]
`);
Array [
Array [],
Array [
"foo",
],
Array [
"foo",
],
Array [
"foo",
"bar",
],
Array [
"foo",
"bar",
],
Array [
"foo",
"bar",
"baz",
],
Array [
"foo",
"baz",
],
Array [
"baz",
],
]
`);
});
});
describe('badge', () => {
it('updates/emits the current badge', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getBadge$()
.pipe(toArray())
.toPromise();
start.setBadge({ text: 'foo', tooltip: `foo's tooltip` });
start.setBadge({ text: 'bar', tooltip: `bar's tooltip` });
start.setBadge(undefined);
chrome.setBadge({ text: 'foo', tooltip: `foo's tooltip` });
chrome.setBadge({ text: 'bar', tooltip: `bar's tooltip` });
chrome.setBadge(undefined);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
undefined,
Object {
"text": "foo",
"tooltip": "foo's tooltip",
},
Object {
"text": "bar",
"tooltip": "bar's tooltip",
},
undefined,
]
`);
Array [
undefined,
Object {
"text": "foo",
"tooltip": "foo's tooltip",
},
Object {
"text": "bar",
"tooltip": "bar's tooltip",
},
undefined,
]
`);
});
});
describe('breadcrumbs', () => {
it('updates/emits the current set of breadcrumbs', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getBreadcrumbs$()
.pipe(toArray())
.toPromise();
start.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]);
start.setBreadcrumbs([{ text: 'foo' }]);
start.setBreadcrumbs([{ text: 'bar' }]);
start.setBreadcrumbs([]);
chrome.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]);
chrome.setBreadcrumbs([{ text: 'foo' }]);
chrome.setBreadcrumbs([{ text: 'bar' }]);
chrome.setBreadcrumbs([]);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
Array [],
Array [
Object {
"text": "foo",
},
Object {
"text": "bar",
},
],
Array [
Object {
"text": "foo",
},
],
Array [
Object {
"text": "bar",
},
],
Array [],
]
`);
Array [
Array [],
Array [
Object {
"text": "foo",
},
Object {
"text": "bar",
},
],
Array [
Object {
"text": "foo",
},
],
Array [
Object {
"text": "bar",
},
],
Array [],
]
`);
});
});
describe('help extension', () => {
it('updates/emits the current help extension', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const promise = start
const { chrome, service } = await start();
const promise = chrome
.getHelpExtension$()
.pipe(toArray())
.toPromise();
start.setHelpExtension(() => () => undefined);
start.setHelpExtension(undefined);
chrome.setHelpExtension(() => () => undefined);
chrome.setHelpExtension(undefined);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
Array [
undefined,
[Function],
undefined,
]
`);
Array [
undefined,
[Function],
undefined,
]
`);
});
});
});
describe('stop', () => {
it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const { chrome, service } = await start();
const promise = Rx.combineLatest(
start.getBrand$(),
start.getApplicationClasses$(),
start.getIsCollapsed$(),
start.getBreadcrumbs$(),
start.getIsVisible$(),
start.getHelpExtension$()
chrome.getBrand$(),
chrome.getApplicationClasses$(),
chrome.getIsCollapsed$(),
chrome.getBreadcrumbs$(),
chrome.getIsVisible$(),
chrome.getHelpExtension$()
).toPromise();
service.stop();
@ -378,18 +450,17 @@ describe('stop', () => {
});
it('completes immediately if service already stopped', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = await service.start(defaultStartDeps());
const { chrome, service } = await start();
service.stop();
await expect(
Rx.combineLatest(
start.getBrand$(),
start.getApplicationClasses$(),
start.getIsCollapsed$(),
start.getBreadcrumbs$(),
start.getIsVisible$(),
start.getHelpExtension$()
chrome.getBrand$(),
chrome.getApplicationClasses$(),
chrome.getIsCollapsed$(),
chrome.getBreadcrumbs$(),
chrome.getIsVisible$(),
chrome.getHelpExtension$()
).toPromise()
).resolves.toBe(undefined);
});

View file

@ -18,9 +18,9 @@
*/
import React from 'react';
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
import { BehaviorSubject, Observable, ReplaySubject, combineLatest, of, merge } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import * as Url from 'url';
import { parse } from 'url';
import { i18n } from '@kbn/i18n';
import { IconType, Breadcrumb as EuiBreadcrumb } from '@elastic/eui';
@ -41,11 +41,6 @@ export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle };
const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed';
function isEmbedParamInHash() {
const { query } = Url.parse(String(window.location.hash).slice(1), true);
return Boolean(query.embed);
}
/** @public */
export interface ChromeBadge {
text: string;
@ -79,6 +74,9 @@ interface StartDeps {
/** @internal */
export class ChromeService {
private isVisible$!: Observable<boolean>;
private appHidden$!: Observable<boolean>;
private toggleHidden$!: BehaviorSubject<boolean>;
private readonly stop$ = new ReplaySubject(1);
private readonly navControls = new NavControlsService();
private readonly navLinks = new NavLinksService();
@ -87,6 +85,38 @@ export class ChromeService {
constructor(private readonly params: ConstructorParams) {}
/**
* These observables allow consumers to toggle the chrome visibility via either:
* 1. Using setIsVisible() to trigger the next chromeHidden$
* 2. Setting `chromeless` when registering an application, which will
* reset the visibility whenever the next application is mounted
* 3. Having "embed" in the query string
*/
private initVisibility(application: StartDeps['application']) {
// Start off the chrome service hidden if "embed" is in the hash query string.
const isEmbedded = 'embed' in parse(location.hash.slice(1), true).query;
this.toggleHidden$ = new BehaviorSubject(isEmbedded);
this.appHidden$ = merge(
// Default the app being hidden to the same value initial value as the chrome visibility
// in case the application service has not emitted an app ID yet, since we want to trigger
// combineLatest below regardless of having an application value yet.
of(isEmbedded),
application.currentAppId$.pipe(
map(
appId =>
!!appId &&
application.availableApps.has(appId) &&
!!application.availableApps.get(appId)!.chromeless
)
)
);
this.isVisible$ = combineLatest(this.appHidden$, this.toggleHidden$).pipe(
map(([appHidden, chromeHidden]) => !(appHidden || chromeHidden)),
takeUntil(this.stop$)
);
}
public async start({
application,
docLinks,
@ -94,11 +124,10 @@ export class ChromeService {
injectedMetadata,
notifications,
}: StartDeps): Promise<InternalChromeStart> {
const FORCE_HIDDEN = isEmbedParamInHash();
this.initVisibility(application);
const appTitle$ = new BehaviorSubject<string>('Kibana');
const brand$ = new BehaviorSubject<ChromeBrand>({});
const isVisible$ = new BehaviorSubject(true);
const isCollapsed$ = new BehaviorSubject(!!localStorage.getItem(IS_COLLAPSED_KEY));
const applicationClasses$ = new BehaviorSubject<Set<string>>(new Set());
const helpExtension$ = new BehaviorSubject<ChromeHelpExtension | undefined>(undefined);
@ -139,10 +168,7 @@ export class ChromeService {
forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()}
helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))}
homeHref={http.basePath.prepend('/app/kibana#/home')}
isVisible$={isVisible$.pipe(
map(visibility => (FORCE_HIDDEN ? false : visibility)),
takeUntil(this.stop$)
)}
isVisible$={this.isVisible$}
kibanaVersion={injectedMetadata.getKibanaVersion()}
legacyMode={injectedMetadata.getLegacyMode()}
navLinks$={navLinks.getNavLinks$()}
@ -166,15 +192,9 @@ export class ChromeService {
);
},
getIsVisible$: () =>
isVisible$.pipe(
map(visibility => (FORCE_HIDDEN ? false : visibility)),
takeUntil(this.stop$)
),
getIsVisible$: () => this.isVisible$,
setIsVisible: (visibility: boolean) => {
isVisible$.next(visibility);
},
setIsVisible: (isVisible: boolean) => this.toggleHidden$.next(!isVisible),
getIsCollapsed$: () => isCollapsed$.pipe(takeUntil(this.stop$)),

View file

@ -18,6 +18,6 @@
*/
export const ELASTIC_SUPPORT_LINK = 'https://support.elastic.co/';
export const KIBANA_FEEDBACK_LINK = 'https://www.elastic.co/kibana/feedback';
export const KIBANA_ASK_ELASTIC_LINK = 'https://www.elastic.co/kibana/ask-elastic';
export const KIBANA_FEEDBACK_LINK = 'https://www.elastic.co/products/kibana/feedback';
export const KIBANA_ASK_ELASTIC_LINK = 'https://www.elastic.co/products/kibana/ask-elastic';
export const GITHUB_CREATE_ISSUE_LINK = 'https://github.com/elastic/kibana/issues/new/choose';

View file

@ -19,27 +19,34 @@
import { NavLinksService } from './nav_links_service';
import { take, map, takeLast } from 'rxjs/operators';
import { LegacyApp } from '../../application';
import { App, LegacyApp } from '../../application';
const mockAppService = {
availableApps: new Map(),
availableLegacyApps: new Map<string, LegacyApp>([
[
'legacyApp1',
{ id: 'legacyApp1', order: 0, title: 'Legacy App 1', icon: 'legacyApp1', appUrl: '/app1' },
],
[
'legacyApp2',
availableApps: new Map<string, App>(
([
{ id: 'app1', order: 0, title: 'App 1', icon: 'app1' },
{
id: 'app2',
order: -10,
title: 'App 2',
euiIconType: 'canvasApp',
},
{ id: 'chromelessApp', order: 20, title: 'Chromless App', chromeless: true },
] as App[]).map(app => [app.id, app])
),
availableLegacyApps: new Map<string, LegacyApp>(
([
{ id: 'legacyApp1', order: 5, title: 'Legacy App 1', icon: 'legacyApp1', appUrl: '/app1' },
{
id: 'legacyApp2',
order: -10,
order: -5,
title: 'Legacy App 2',
euiIconType: 'canvasApp',
appUrl: '/app2',
},
],
['legacyApp3', { id: 'legacyApp3', order: 20, title: 'Legacy App 3', appUrl: '/app3' }],
]),
{ id: 'legacyApp3', order: 15, title: 'Legacy App 3', appUrl: '/app3' },
] as LegacyApp[]).map(app => [app.id, app])
),
} as any;
const mockHttp = {
@ -58,6 +65,18 @@ describe('NavLinksService', () => {
});
describe('#getNavLinks$()', () => {
it('does not include `chromeless` applications', async () => {
expect(
await start
.getNavLinks$()
.pipe(
take(1),
map(links => links.map(l => l.id))
)
.toPromise()
).not.toContain('chromelessApp');
});
it('sorts navlinks by `order` property', async () => {
expect(
await start
@ -67,7 +86,7 @@ describe('NavLinksService', () => {
map(links => links.map(l => l.id))
)
.toPromise()
).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']);
).toEqual(['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3']);
});
it('emits multiple values', async () => {
@ -78,8 +97,8 @@ describe('NavLinksService', () => {
service.stop();
expect(emittedLinks).toEqual([
['legacyApp2', 'legacyApp1', 'legacyApp3'],
['legacyApp2', 'legacyApp1', 'legacyApp3'],
['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3'],
['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3'],
]);
});
@ -105,7 +124,13 @@ describe('NavLinksService', () => {
describe('#getAll()', () => {
it('returns a sorted array of navlinks', () => {
expect(start.getAll().map(l => l.id)).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']);
expect(start.getAll().map(l => l.id)).toEqual([
'app2',
'legacyApp2',
'app1',
'legacyApp1',
'legacyApp3',
]);
});
});
@ -130,7 +155,20 @@ describe('NavLinksService', () => {
map(links => links.map(l => l.id))
)
.toPromise()
).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']);
).toEqual(['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3']);
});
it('does nothing on chromeless applications', async () => {
start.showOnly('chromelessApp');
expect(
await start
.getNavLinks$()
.pipe(
take(1),
map(links => links.map(l => l.id))
)
.toPromise()
).toEqual(['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3']);
});
it('removes all other links', async () => {
@ -157,7 +195,7 @@ describe('NavLinksService', () => {
"icon": "legacyApp1",
"id": "legacyApp1",
"legacy": true,
"order": 0,
"order": 5,
"title": "Legacy App 1",
}
`);

View file

@ -99,17 +99,19 @@ export class NavLinksService {
private readonly stop$ = new ReplaySubject(1);
public start({ application, http }: StartDeps): ChromeNavLinks {
const appLinks = [...application.availableApps].map(
([appId, app]) =>
[
appId,
new NavLinkWrapper({
...app,
legacy: false,
baseUrl: relativeToAbsolute(http.basePath.prepend(`/app/${appId}`)),
}),
] as [string, NavLinkWrapper]
);
const appLinks = [...application.availableApps]
.filter(([, app]) => !app.chromeless)
.map(
([appId, app]) =>
[
appId,
new NavLinkWrapper({
...app,
legacy: false,
baseUrl: relativeToAbsolute(http.basePath.prepend(`/app/${appId}`)),
}),
] as [string, NavLinkWrapper]
);
const legacyAppLinks = [...application.availableLegacyApps].map(
([appId, app]) =>
@ -130,10 +132,7 @@ export class NavLinksService {
return {
getNavLinks$: () => {
return navLinks$.pipe(
map(sortNavLinks),
takeUntil(this.stop$)
);
return navLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$));
},
get(id: string) {
@ -192,7 +191,10 @@ export class NavLinksService {
}
function sortNavLinks(navLinks: ReadonlyMap<string, NavLinkWrapper>) {
return sortBy([...navLinks.values()].map(link => link.properties), 'order');
return sortBy(
[...navLinks.values()].map(link => link.properties),
'order'
);
}
function relativeToAbsolute(url: string) {

View file

@ -106,10 +106,7 @@ describe('RecentlyAccessed#start()', () => {
const stop$ = new Subject();
const observedValues$ = recentlyAccessed
.get$()
.pipe(
bufferCount(3),
takeUntil(stop$)
)
.pipe(bufferCount(3), takeUntil(stop$))
.toPromise();
recentlyAccessed.add('/app/item1', 'Item 1', 'item1');
recentlyAccessed.add('/app/item2', 'Item 2', 'item2');

View file

@ -174,7 +174,10 @@ describe('#setup()', () => {
it('injects legacy dependency to context#setup()', async () => {
const pluginA = Symbol();
const pluginB = Symbol();
const pluginDependencies = new Map<symbol, symbol[]>([[pluginA, []], [pluginB, [pluginA]]]);
const pluginDependencies = new Map<symbol, symbol[]>([
[pluginA, []],
[pluginB, [pluginA]],
]);
MockPluginsService.getOpaqueIds.mockReturnValue(pluginDependencies);
await setupCore();

View file

@ -117,13 +117,7 @@ export {
InterceptedHttpResponse,
} from './http';
export {
OverlayStart,
OverlayBannerMount,
OverlayBannerUnmount,
OverlayBannersStart,
OverlayRef,
} from './overlays';
export { OverlayStart, OverlayBannersStart, OverlayRef } from './overlays';
export {
Toast,
@ -136,6 +130,8 @@ export {
ErrorToastOptions,
} from './notifications';
export { MountPoint, UnmountCallback } from './types';
/**
* Core services exposed to the `Plugin` setup lifecycle
*

View file

@ -68,18 +68,27 @@ describe('setup.getPlugins()', () => {
it('returns injectedMetadata.uiPlugins', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
uiPlugins: [{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }],
uiPlugins: [
{ id: 'plugin-1', plugin: {} },
{ id: 'plugin-2', plugin: {} },
],
},
} as any);
const plugins = injectedMetadata.setup().getPlugins();
expect(plugins).toEqual([{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }]);
expect(plugins).toEqual([
{ id: 'plugin-1', plugin: {} },
{ id: 'plugin-2', plugin: {} },
]);
});
it('returns frozen version of uiPlugins', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
uiPlugins: [{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }],
uiPlugins: [
{ id: 'plugin-1', plugin: {} },
{ id: 'plugin-2', plugin: {} },
],
},
} as any);

View file

@ -3,7 +3,7 @@
exports[`renders matching snapshot 1`] = `
<EuiGlobalToastList
data-test-subj="globalToastList"
dismissToast={[MockFunction]}
dismissToast={[Function]}
toastLifeTimeMs={Infinity}
toasts={Array []}
/>

View file

@ -57,9 +57,9 @@ it('subscribes to toasts$ on mount and unsubscribes on unmount', () => {
it('passes latest value from toasts$ to <EuiGlobalToastList />', () => {
const el = shallow(
render({
toasts$: Rx.from([[], [1], [1, 2]]) as any,
toasts$: Rx.from([[], [{ id: 1 }], [{ id: 1 }, { id: 2 }]]) as any,
})
);
expect(el.find(EuiGlobalToastList).prop('toasts')).toEqual([1, 2]);
expect(el.find(EuiGlobalToastList).prop('toasts')).toEqual([{ id: 1 }, { id: 2 }]);
});

View file

@ -17,20 +17,28 @@
* under the License.
*/
import { EuiGlobalToastList, EuiGlobalToastListToast as Toast } from '@elastic/eui';
import { EuiGlobalToastList, EuiGlobalToastListToast as EuiToast } from '@elastic/eui';
import React from 'react';
import * as Rx from 'rxjs';
import { MountWrapper } from '../../utils';
import { Toast } from './toasts_api';
interface Props {
toasts$: Rx.Observable<Toast[]>;
dismissToast: (t: Toast) => void;
dismissToast: (toastId: string) => void;
}
interface State {
toasts: Toast[];
}
const convertToEui = (toast: Toast): EuiToast => ({
...toast,
title: typeof toast.title === 'function' ? <MountWrapper mount={toast.title} /> : toast.title,
text: typeof toast.text === 'function' ? <MountWrapper mount={toast.text} /> : toast.text,
});
export class GlobalToastList extends React.Component<Props, State> {
public state: State = {
toasts: [],
@ -54,8 +62,8 @@ export class GlobalToastList extends React.Component<Props, State> {
return (
<EuiGlobalToastList
data-test-subj="globalToastList"
toasts={this.state.toasts}
dismissToast={this.props.dismissToast}
toasts={this.state.toasts.map(convertToEui)}
dismissToast={({ id }) => this.props.dismissToast(id)}
/**
* This prop is overriden by the individual toasts that are added.
* Use `Infinity` here so that it's obvious a timeout hasn't been

View file

@ -18,5 +18,11 @@
*/
export { ToastsService, ToastsSetup, ToastsStart } from './toasts_service';
export { ErrorToastOptions, ToastsApi, ToastInput, IToasts, ToastInputFields } from './toasts_api';
export { EuiGlobalToastListToast as Toast } from '@elastic/eui';
export {
ErrorToastOptions,
ToastsApi,
ToastInput,
IToasts,
ToastInputFields,
Toast,
} from './toasts_api';

View file

@ -91,7 +91,7 @@ describe('#get$()', () => {
toasts.add('foo');
onToasts.mockClear();
toasts.remove({ id: 'bar' });
toasts.remove('bar');
expect(onToasts).not.toHaveBeenCalled();
});
});
@ -136,7 +136,7 @@ describe('#remove()', () => {
it('ignores unknown toast', async () => {
const toasts = new ToastsApi(toastDeps());
toasts.add('Test');
toasts.remove({ id: 'foo' });
toasts.remove('foo');
const currentToasts = await getCurrentToasts(toasts);
expect(currentToasts).toHaveLength(1);

View file

@ -17,11 +17,13 @@
* under the License.
*/
import { EuiGlobalToastListToast as Toast } from '@elastic/eui';
import { EuiGlobalToastListToast as EuiToast } from '@elastic/eui';
import React from 'react';
import * as Rx from 'rxjs';
import { ErrorToast } from './error_toast';
import { MountPoint } from '../../types';
import { mountReactNode } from '../../utils';
import { UiSettingsClientContract } from '../../ui_settings';
import { OverlayStart } from '../../overlays';
@ -33,13 +35,20 @@ import { OverlayStart } from '../../overlays';
*
* @public
*/
export type ToastInputFields = Pick<Toast, Exclude<keyof Toast, 'id'>>;
export type ToastInputFields = Pick<EuiToast, Exclude<keyof EuiToast, 'id' | 'text' | 'title'>> & {
title?: string | MountPoint;
text?: string | MountPoint;
};
export type Toast = ToastInputFields & {
id: string;
};
/**
* Inputs for {@link IToasts} APIs.
* @public
*/
export type ToastInput = string | ToastInputFields | Promise<ToastInputFields>;
export type ToastInput = string | ToastInputFields;
/**
* Options available for {@link IToasts} APIs.
@ -59,13 +68,12 @@ export interface ErrorToastOptions {
toastMessage?: string;
}
const normalizeToast = (toastOrTitle: ToastInput) => {
const normalizeToast = (toastOrTitle: ToastInput): ToastInputFields => {
if (typeof toastOrTitle === 'string') {
return {
title: toastOrTitle,
};
}
return toastOrTitle;
};
@ -123,11 +131,12 @@ export class ToastsApi implements IToasts {
/**
* Removes a toast from the current array of toasts if present.
* @param toast - a {@link Toast} returned by {@link ToastApi.add}
* @param toastOrId - a {@link Toast} returned by {@link ToastsApi.add} or its id
*/
public remove(toast: Toast) {
public remove(toastOrId: Toast | string) {
const toRemove = typeof toastOrId === 'string' ? toastOrId : toastOrId.id;
const list = this.toasts$.getValue();
const listWithoutToast = list.filter(t => t !== toast);
const listWithoutToast = list.filter(t => t.id !== toRemove);
if (listWithoutToast.length !== list.length) {
this.toasts$.next(listWithoutToast);
}
@ -191,7 +200,7 @@ export class ToastsApi implements IToasts {
iconType: 'alert',
title: options.title,
toastLifeTimeMs: this.uiSettings.get('notifications:lifetime:error'),
text: (
text: mountReactNode(
<ErrorToast
openModal={this.openModal.bind(this)}
error={error}

View file

@ -20,7 +20,6 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { EuiGlobalToastListToast as Toast } from '@elastic/eui';
import { I18nStart } from '../../i18n';
import { UiSettingsClientContract } from '../../ui_settings';
import { GlobalToastList } from './global_toast_list';
@ -65,7 +64,7 @@ export class ToastsService {
render(
<i18n.Context>
<GlobalToastList
dismissToast={(toast: Toast) => this.api!.remove(toast)}
dismissToast={(toastId: string) => this.api!.remove(toastId)}
toasts$={this.api!.get$()}
/>
</i18n.Context>,

View file

@ -25,33 +25,20 @@ import { PriorityMap } from './priority_map';
import { BannersList } from './banners_list';
import { UiSettingsClientContract } from '../../ui_settings';
import { I18nStart } from '../../i18n';
import { MountPoint } from '../../types';
import { UserBannerService } from './user_banner_service';
/**
* A function that will unmount the banner from the element.
* @public
*/
export type OverlayBannerUnmount = () => void;
/**
* A function that will mount the banner inside the provided element.
* @param element an element to render into
* @returns a {@link OverlayBannerUnmount}
* @public
*/
export type OverlayBannerMount = (element: HTMLElement) => OverlayBannerUnmount;
/** @public */
export interface OverlayBannersStart {
/**
* Add a new banner
*
* @param mount {@link OverlayBannerMount}
* @param mount {@link MountPoint}
* @param priority optional priority order to display this banner. Higher priority values are shown first.
* @returns a unique identifier for the given banner to be used with {@link OverlayBannersStart.remove} and
* {@link OverlayBannersStart.replace}
*/
add(mount: OverlayBannerMount, priority?: number): string;
add(mount: MountPoint, priority?: number): string;
/**
* Remove a banner
@ -65,12 +52,12 @@ export interface OverlayBannersStart {
* Replace a banner in place
*
* @param id the unique identifier for the banner returned by {@link OverlayBannersStart.add}
* @param mount {@link OverlayBannerMount}
* @param mount {@link MountPoint}
* @param priority optional priority order to display this banner. Higher priority values are shown first.
* @returns a new identifier for the given banner to be used with {@link OverlayBannersStart.remove} and
* {@link OverlayBannersStart.replace}
*/
replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): string;
replace(id: string | undefined, mount: MountPoint, priority?: number): string;
/** @internal */
get$(): Observable<OverlayBanner[]>;
@ -80,7 +67,7 @@ export interface OverlayBannersStart {
/** @internal */
export interface OverlayBanner {
readonly id: string;
readonly mount: OverlayBannerMount;
readonly mount: MountPoint;
readonly priority: number;
}
@ -116,7 +103,7 @@ export class OverlayBannersService {
return true;
},
replace(id: string | undefined, mount: OverlayBannerMount, priority = 0) {
replace(id: string | undefined, mount: MountPoint, priority = 0) {
if (!id || !banners$.value.has(id)) {
return this.add(mount, priority);
}

View file

@ -17,9 +17,4 @@
* under the License.
*/
export {
OverlayBannerMount,
OverlayBannerUnmount,
OverlayBannersStart,
OverlayBannersService,
} from './banners_service';
export { OverlayBannersStart, OverlayBannersService } from './banners_service';

View file

@ -42,7 +42,10 @@ describe('PriorityMap', () => {
map = map.add('b', { priority: 3 });
map = map.add('c', { priority: 2 });
map = map.remove('c');
expect([...map]).toEqual([['b', { priority: 3 }], ['a', { priority: 1 }]]);
expect([...map]).toEqual([
['b', { priority: 3 }],
['a', { priority: 1 }],
]);
});
it('adds duplicate priorities to end', () => {

View file

@ -17,5 +17,5 @@
* under the License.
*/
export { OverlayBannerMount, OverlayBannerUnmount, OverlayBannersStart } from './banners';
export { OverlayBannersStart } from './banners';
export { OverlayService, OverlayStart, OverlayRef } from './overlay_service';

View file

@ -223,10 +223,13 @@ test('`PluginsService.setup` exposes dependent setup contracts to plugins', asyn
test('`PluginsService.setup` does not set missing dependent setup contracts', async () => {
plugins = [{ id: 'pluginD', plugin: createManifest('pluginD', { optional: ['missing'] }) }];
mockPluginInitializers.set('pluginD', jest.fn(() => ({
setup: jest.fn(),
start: jest.fn(),
})) as any);
mockPluginInitializers.set(
'pluginD',
jest.fn(() => ({
setup: jest.fn(),
start: jest.fn(),
})) as any
);
const pluginsService = new PluginsService(mockCoreContext, plugins);
await pluginsService.setup(mockSetupDeps);
@ -268,10 +271,13 @@ test('`PluginsService.start` exposes dependent start contracts to plugins', asyn
test('`PluginsService.start` does not set missing dependent start contracts', async () => {
plugins = [{ id: 'pluginD', plugin: createManifest('pluginD', { optional: ['missing'] }) }];
mockPluginInitializers.set('pluginD', jest.fn(() => ({
setup: jest.fn(),
start: jest.fn(),
})) as any);
mockPluginInitializers.set(
'pluginD',
jest.fn(() => ({
setup: jest.fn(),
start: jest.fn(),
})) as any
);
const pluginsService = new PluginsService(mockCoreContext, plugins);
await pluginsService.setup(mockSetupDeps);

View file

@ -5,17 +5,18 @@
```ts
import { Breadcrumb } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { IconType } from '@elastic/eui';
import { Observable } from 'rxjs';
import React from 'react';
import * as Rx from 'rxjs';
import { ShallowPromise } from '@kbn/utility-types';
import { EuiGlobalToastListToast as Toast } from '@elastic/eui';
import { UiSettingsParams as UiSettingsParams_2 } from 'src/core/server/types';
import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types';
// @public
export interface App extends AppBase {
chromeless?: boolean;
mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise<AppUnmount>;
}
@ -618,6 +619,9 @@ export interface LegacyNavLink {
url: string;
}
// @public
export type MountPoint = (element: HTMLElement) => UnmountCallback;
// @public (undocumented)
export interface NotificationsSetup {
// (undocumented)
@ -630,12 +634,9 @@ export interface NotificationsStart {
toasts: ToastsStart;
}
// @public
export type OverlayBannerMount = (element: HTMLElement) => OverlayBannerUnmount;
// @public (undocumented)
export interface OverlayBannersStart {
add(mount: OverlayBannerMount, priority?: number): string;
add(mount: MountPoint, priority?: number): string;
// Warning: (ae-forgotten-export) The symbol "OverlayBanner" needs to be exported by the entry point index.d.ts
//
// @internal (undocumented)
@ -643,12 +644,9 @@ export interface OverlayBannersStart {
// (undocumented)
getComponent(): JSX.Element;
remove(id: string): boolean;
replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): string;
replace(id: string | undefined, mount: MountPoint, priority?: number): string;
}
// @public
export type OverlayBannerUnmount = () => void;
// @public
export interface OverlayRef {
close(): Promise<void>;
@ -916,35 +914,36 @@ export class SimpleSavedObject<T extends SavedObjectAttributes> {
_version?: SavedObject<T>['version'];
}
export { Toast }
// Warning: (ae-missing-release-tag) "Toast" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type Toast = ToastInputFields & {
id: string;
};
// @public
export type ToastInput = string | ToastInputFields | Promise<ToastInputFields>;
export type ToastInput = string | ToastInputFields;
// @public
export type ToastInputFields = Pick<Toast, Exclude<keyof Toast, 'id'>>;
export type ToastInputFields = Pick<EuiGlobalToastListToast, Exclude<keyof EuiGlobalToastListToast, 'id' | 'text' | 'title'>> & {
title?: string | MountPoint;
text?: string | MountPoint;
};
// @public
export class ToastsApi implements IToasts {
constructor(deps: {
uiSettings: UiSettingsClientContract;
});
// Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported
add(toastOrTitle: ToastInput): Toast;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported
addDanger(toastOrTitle: ToastInput): Toast;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported
addError(error: Error, options: ErrorToastOptions): Toast;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported
addSuccess(toastOrTitle: ToastInput): Toast;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported
addWarning(toastOrTitle: ToastInput): Toast;
get$(): Rx.Observable<Toast[]>;
// @internal (undocumented)
registerOverlays(overlays: OverlayStart): void;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported
// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ToastApi"
remove(toast: Toast): void;
remove(toastOrId: Toast | string): void;
}
// @public (undocumented)
@ -990,5 +989,8 @@ export interface UiSettingsState {
[key: string]: UiSettingsParams_2 & UserProvidedValues_2;
}
// @public
export type UnmountCallback = () => void;
```

View file

@ -17,34 +17,21 @@
* under the License.
*/
import { fromUser, toUser, getQueryLog } from './query_bar';
/**
* A function that should mount DOM content inside the provided container element
* and return a handler to unmount it.
*
* @param element the container element to render into
* @returns a {@link UnmountCallback} that unmount the element on call.
*
* @public
*/
export type MountPoint = (element: HTMLElement) => UnmountCallback;
/**
* Query Service
* A function that will unmount the element previously mounted by
* the associated {@link MountPoint}
*
* @internal
* @public
*/
export class QueryService {
public setup() {
return {
helpers: {
fromUser,
toUser,
getQueryLog,
},
};
}
public start() {
// nothing to do here yet
}
public stop() {
// nothing to do here yet
}
}
/** @public */
export type QuerySetup = ReturnType<QueryService['setup']>;
export * from './query_bar';
export type UnmountCallback = () => void;

View file

@ -183,10 +183,7 @@ describe('#getLoadingCount$()', () => {
const done$ = new Rx.Subject();
const promise = uiSettingsApi
.getLoadingCount$()
.pipe(
takeUntil(done$),
toArray()
)
.pipe(takeUntil(done$), toArray())
.toPromise();
await uiSettingsApi.batchSet('foo', 'bar');
@ -214,10 +211,7 @@ describe('#getLoadingCount$()', () => {
const done$ = new Rx.Subject();
const promise = uiSettingsApi
.getLoadingCount$()
.pipe(
takeUntil(done$),
toArray()
)
.pipe(takeUntil(done$), toArray())
.toPromise();
await uiSettingsApi.batchSet('foo', 'bar');
@ -250,7 +244,10 @@ describe('#stop', () => {
uiSettingsApi.stop();
// both observables should emit the same values, and complete before the request is done loading
await expect(promise).resolves.toEqual([[0, 1], [0, 1]]);
await expect(promise).resolves.toEqual([
[0, 1],
[0, 1],
]);
await batchSetPromise;
});
});

View file

@ -83,10 +83,7 @@ describe('#get$', () => {
const { config } = setup();
const values = await config
.get$('dateFormat')
.pipe(
take(1),
toArray()
)
.pipe(take(1), toArray())
.toPromise();
expect(values).toEqual(['Browser']);
@ -122,10 +119,7 @@ You can use \`config.get("unknown key", defaultValue)\`, which will just return
const values = await config
.get$('dateFormat')
.pipe(
take(2),
toArray()
)
.pipe(take(2), toArray())
.toPromise();
expect(values).toEqual(['Browser', 'new format']);
@ -144,10 +138,7 @@ You can use \`config.get("unknown key", defaultValue)\`, which will just return
const values = await config
.get$('dateFormat', 'my default')
.pipe(
take(3),
toArray()
)
.pipe(take(3), toArray())
.toPromise();
expect(values).toEqual(['my default', 'new format', 'my default']);

View file

@ -19,3 +19,4 @@
export { shareWeakReplay } from './share_weak_replay';
export { Sha256 } from './crypto';
export { MountWrapper, mountReactNode } from './mount';

View file

@ -0,0 +1,44 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { useEffect, useRef } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { MountPoint } from '../types';
/**
* MountWrapper is a react component to mount a {@link MountPoint} inside a react tree.
*/
export const MountWrapper: React.FunctionComponent<{ mount: MountPoint }> = ({ mount }) => {
const element = useRef(null);
useEffect(() => mount(element.current!), [mount]);
return <div className="kbnMountWrapper" ref={element} />;
};
/**
* Mount converter for react components.
*
* @param component to get a mount for
*/
export const mountReactNode = (component: React.ReactNode): MountPoint => (
element: HTMLElement
) => {
render(<I18nProvider>{component}</I18nProvider>, element);
return () => unmountComponentAtNode(element);
};

View file

@ -153,10 +153,7 @@ Array [
});
it('resubscribes if parent completes', async () => {
const shared = counter().pipe(
take(4),
shareWeakReplay(4)
);
const shared = counter().pipe(take(4), shareWeakReplay(4));
await expect(Promise.all([record(shared.pipe(take(1))), record(shared)])).resolves
.toMatchInlineSnapshot(`
@ -199,10 +196,7 @@ Array [
it('supports parents that complete synchronously', async () => {
const next = jest.fn();
const complete = jest.fn();
const shared = counter({ async: false }).pipe(
take(3),
shareWeakReplay(1)
);
const shared = counter({ async: false }).pipe(take(3), shareWeakReplay(1));
shared.subscribe({ next, complete });
expect(next.mock.calls).toMatchInlineSnapshot(`

Some files were not shown because too many files have changed in this diff Show more