Remove final legacy dependencies from Header UI (#38175)

* Remove final legacy dependencies from Header UI

* Address @eliperelman comments

* Move files for x-pack move
This commit is contained in:
Josh Dover 2019-06-20 15:57:02 -05:00 committed by GitHub
parent 48c5df2e98
commit 5941436e1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
98 changed files with 2351 additions and 270 deletions

View file

@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavControl](./kibana-plugin-public.chromenavcontrol.md)
## ChromeNavControl interface
<b>Signature:</b>
```typescript
export interface ChromeNavControl
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [order](./kibana-plugin-public.chromenavcontrol.order.md) | <code>number</code> | |
## Methods
| Method | Description |
| --- | --- |
| [mount(targetDomElement)](./kibana-plugin-public.chromenavcontrol.mount.md) | |

View file

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavControl](./kibana-plugin-public.chromenavcontrol.md) &gt; [mount](./kibana-plugin-public.chromenavcontrol.mount.md)
## ChromeNavControl.mount() method
<b>Signature:</b>
```typescript
mount(targetDomElement: HTMLElement): () => void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| targetDomElement | <code>HTMLElement</code> | |
<b>Returns:</b>
`() => void`

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavControl](./kibana-plugin-public.chromenavcontrol.md) &gt; [order](./kibana-plugin-public.chromenavcontrol.order.md)
## ChromeNavControl.order property
<b>Signature:</b>
```typescript
order?: number;
```

View file

@ -0,0 +1,35 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavControls](./kibana-plugin-public.chromenavcontrols.md)
## ChromeNavControls interface
[APIs](./kibana-plugin-public.chromenavcontrols.md) for registering new controls to be displayed in the navigation bar.
<b>Signature:</b>
```typescript
export interface ChromeNavControls
```
## Methods
| Method | Description |
| --- | --- |
| [registerLeft(navControl)](./kibana-plugin-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the left side of the chrome header. |
| [registerRight(navControl)](./kibana-plugin-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the right side of the chrome header. |
## Example
Register a left-side nav control rendered with React.
```jsx
chrome.navControls.registerLeft({
mount(targetDomElement) {
ReactDOM.mount(<MyControl />, targetDomElement);
return () => ReactDOM.unmountComponentAtNode(targetDomElement);
}
})
```

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavControls](./kibana-plugin-public.chromenavcontrols.md) &gt; [registerLeft](./kibana-plugin-public.chromenavcontrols.registerleft.md)
## ChromeNavControls.registerLeft() method
Register a nav control to be presented on the left side of the chrome header.
<b>Signature:</b>
```typescript
registerLeft(navControl: ChromeNavControl): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| navControl | <code>ChromeNavControl</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavControls](./kibana-plugin-public.chromenavcontrols.md) &gt; [registerRight](./kibana-plugin-public.chromenavcontrols.registerright.md)
## ChromeNavControls.registerRight() method
Register a nav control to be presented on the right side of the chrome header.
<b>Signature:</b>
```typescript
registerRight(navControl: ChromeNavControl): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| navControl | <code>ChromeNavControl</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [enableForcedAppSwitcherNavigation](./kibana-plugin-public.chromenavlinks.enableforcedappswitchernavigation.md)
## ChromeNavLinks.enableForcedAppSwitcherNavigation() method
Enable forced navigation mode, which will trigger a page refresh when a nav link is clicked and only the hash is updated.
<b>Signature:</b>
```typescript
enableForcedAppSwitcherNavigation(): void;
```
<b>Returns:</b>
`void`
## Remarks
This is only necessary when rendering the status page in place of another app, as links to that app will set the current URL and change the hash, but the routes for the correct are not loaded so nothing will happen. https://github.com/elastic/kibana/pull/29770
Used only by status\_page plugin

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [get](./kibana-plugin-public.chromenavlinks.get.md)
## ChromeNavLinks.get() method
Get the state of a navlink at this point in time.
<b>Signature:</b>
```typescript
get(id: string): ChromeNavLink | undefined;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string</code> | |
<b>Returns:</b>
`ChromeNavLink | undefined`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [getAll](./kibana-plugin-public.chromenavlinks.getall.md)
## ChromeNavLinks.getAll() method
Get the current state of all navlinks.
<b>Signature:</b>
```typescript
getAll(): Array<Readonly<ChromeNavLink>>;
```
<b>Returns:</b>
`Array<Readonly<ChromeNavLink>>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [getForceAppSwitcherNavigation$](./kibana-plugin-public.chromenavlinks.getforceappswitchernavigation$.md)
## ChromeNavLinks.getForceAppSwitcherNavigation$() method
An observable of the forced app switcher state.
<b>Signature:</b>
```typescript
getForceAppSwitcherNavigation$(): Observable<boolean>;
```
<b>Returns:</b>
`Observable<boolean>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [getNavLinks$](./kibana-plugin-public.chromenavlinks.getnavlinks$.md)
## ChromeNavLinks.getNavLinks$() method
Get an observable for a sorted list of navlinks.
<b>Signature:</b>
```typescript
getNavLinks$(): Observable<Array<Readonly<ChromeNavLink>>>;
```
<b>Returns:</b>
`Observable<Array<Readonly<ChromeNavLink>>>`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [has](./kibana-plugin-public.chromenavlinks.has.md)
## ChromeNavLinks.has() method
Check whether or not a navlink exists.
<b>Signature:</b>
```typescript
has(id: string): boolean;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string</code> | |
<b>Returns:</b>
`boolean`

View file

@ -0,0 +1,27 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md)
## ChromeNavLinks interface
[APIs](./kibana-plugin-public.chromenavlinks.md) for manipulating nav links.
<b>Signature:</b>
```typescript
export interface ChromeNavLinks
```
## Methods
| Method | Description |
| --- | --- |
| [enableForcedAppSwitcherNavigation()](./kibana-plugin-public.chromenavlinks.enableforcedappswitchernavigation.md) | Enable forced navigation mode, which will trigger a page refresh when a nav link is clicked and only the hash is updated. |
| [get(id)](./kibana-plugin-public.chromenavlinks.get.md) | Get the state of a navlink at this point in time. |
| [getAll()](./kibana-plugin-public.chromenavlinks.getall.md) | Get the current state of all navlinks. |
| [getForceAppSwitcherNavigation$()](./kibana-plugin-public.chromenavlinks.getforceappswitchernavigation$.md) | An observable of the forced app switcher state. |
| [getNavLinks$()](./kibana-plugin-public.chromenavlinks.getnavlinks$.md) | Get an observable for a sorted list of navlinks. |
| [has(id)](./kibana-plugin-public.chromenavlinks.has.md) | Check whether or not a navlink exists. |
| [showOnly(id)](./kibana-plugin-public.chromenavlinks.showonly.md) | Remove all navlinks except the one matching the given id. |
| [update(id, values)](./kibana-plugin-public.chromenavlinks.update.md) | Update the navlink for the given id with the updated attributes. Returns the updated navlink or <code>undefined</code> if it does not exist. |

View file

@ -0,0 +1,28 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [showOnly](./kibana-plugin-public.chromenavlinks.showonly.md)
## ChromeNavLinks.showOnly() method
Remove all navlinks except the one matching the given id.
<b>Signature:</b>
```typescript
showOnly(id: string): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string</code> | |
<b>Returns:</b>
`void`
## Remarks
NOTE: this is not reversible.

View file

@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) &gt; [update](./kibana-plugin-public.chromenavlinks.update.md)
## ChromeNavLinks.update() method
Update the navlink for the given id with the updated attributes. Returns the updated navlink or `undefined` if it does not exist.
<b>Signature:</b>
```typescript
update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string</code> | |
| values | <code>ChromeNavLinkUpdateableFields</code> | |
<b>Returns:</b>
`ChromeNavLink | undefined`

View file

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeNavLinkUpdateableFields](./kibana-plugin-public.chromenavlinkupdateablefields.md)
## ChromeNavLinkUpdateableFields type
<b>Signature:</b>
```typescript
export declare type ChromeNavLinkUpdateableFields = Partial<Pick<ChromeNavLink, 'active' | 'disabled' | 'hidden' | 'url' | 'subUrlBase'>>;
```

View file

@ -0,0 +1,34 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessed](./kibana-plugin-public.chromerecentlyaccessed.md) &gt; [add](./kibana-plugin-public.chromerecentlyaccessed.add.md)
## ChromeRecentlyAccessed.add() method
Adds a new item to the recently accessed history.
<b>Signature:</b>
```typescript
add(link: string, label: string, id: string): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| link | <code>string</code> | |
| label | <code>string</code> | |
| id | <code>string</code> | |
<b>Returns:</b>
`void`
## Example
```js
chrome.recentlyAccessed.add('/app/map/1234', 'Map 1234', '1234');
```

View file

@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessed](./kibana-plugin-public.chromerecentlyaccessed.md) &gt; [get$](./kibana-plugin-public.chromerecentlyaccessed.get$.md)
## ChromeRecentlyAccessed.get$() method
Gets an Observable of the array of recently accessed history.
<b>Signature:</b>
```typescript
get$(): Observable<ChromeRecentlyAccessedHistoryItem[]>;
```
<b>Returns:</b>
`Observable<ChromeRecentlyAccessedHistoryItem[]>`
## Example
```js
chrome.recentlyAccessed.get$().subscribe(console.log);
```

View file

@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessed](./kibana-plugin-public.chromerecentlyaccessed.md) &gt; [get](./kibana-plugin-public.chromerecentlyaccessed.get.md)
## ChromeRecentlyAccessed.get() method
Gets an Array of the current recently accessed history.
<b>Signature:</b>
```typescript
get(): ChromeRecentlyAccessedHistoryItem[];
```
<b>Returns:</b>
`ChromeRecentlyAccessedHistoryItem[]`
## Example
```js
chrome.recentlyAccessed.get().forEach(console.log);
```

View file

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessed](./kibana-plugin-public.chromerecentlyaccessed.md)
## ChromeRecentlyAccessed interface
[APIs](./kibana-plugin-public.chromerecentlyaccessed.md) for recently accessed history.
<b>Signature:</b>
```typescript
export interface ChromeRecentlyAccessed
```
## Methods
| Method | Description |
| --- | --- |
| [add(link, label, id)](./kibana-plugin-public.chromerecentlyaccessed.add.md) | Adds a new item to the recently accessed history. |
| [get()](./kibana-plugin-public.chromerecentlyaccessed.get.md) | Gets an Array of the current recently accessed history. |
| [get$()](./kibana-plugin-public.chromerecentlyaccessed.get$.md) | Gets an Observable of the array of recently accessed history. |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessedHistoryItem](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.md) &gt; [id](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.id.md)
## ChromeRecentlyAccessedHistoryItem.id property
<b>Signature:</b>
```typescript
id: string;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessedHistoryItem](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.md) &gt; [label](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.label.md)
## ChromeRecentlyAccessedHistoryItem.label property
<b>Signature:</b>
```typescript
label: string;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessedHistoryItem](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.md) &gt; [link](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.link.md)
## ChromeRecentlyAccessedHistoryItem.link property
<b>Signature:</b>
```typescript
link: string;
```

View file

@ -0,0 +1,21 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeRecentlyAccessedHistoryItem](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.md)
## ChromeRecentlyAccessedHistoryItem interface
<b>Signature:</b>
```typescript
export interface ChromeRecentlyAccessedHistoryItem
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [id](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.id.md) | <code>string</code> | |
| [label](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.label.md) | <code>string</code> | |
| [link](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.link.md) | <code>string</code> | |

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [addApplicationClass](./kibana-plugin-public.chromestart.addapplicationclass.md)
## ChromeStart.addApplicationClass() method
Add a className that should be set on the application container.
<b>Signature:</b>
```typescript
addApplicationClass(className: string): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| className | <code>string</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getApplicationClasses$](./kibana-plugin-public.chromestart.getapplicationclasses$.md)
## ChromeStart.getApplicationClasses$() method
Get the current set of classNames that will be set on the application container.
<b>Signature:</b>
```typescript
getApplicationClasses$(): Observable<string[]>;
```
<b>Returns:</b>
`Observable<string[]>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getBadge$](./kibana-plugin-public.chromestart.getbadge$.md)
## ChromeStart.getBadge$() method
Get an observable of the current badge
<b>Signature:</b>
```typescript
getBadge$(): Observable<ChromeBadge | undefined>;
```
<b>Returns:</b>
`Observable<ChromeBadge | undefined>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getBrand$](./kibana-plugin-public.chromestart.getbrand$.md)
## ChromeStart.getBrand$() method
Get an observable of the current brand information.
<b>Signature:</b>
```typescript
getBrand$(): Observable<ChromeBrand>;
```
<b>Returns:</b>
`Observable<ChromeBrand>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getBreadcrumbs$](./kibana-plugin-public.chromestart.getbreadcrumbs$.md)
## ChromeStart.getBreadcrumbs$() method
Get an observable of the current list of breadcrumbs
<b>Signature:</b>
```typescript
getBreadcrumbs$(): Observable<ChromeBreadcrumb[]>;
```
<b>Returns:</b>
`Observable<ChromeBreadcrumb[]>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getHelpExtension$](./kibana-plugin-public.chromestart.gethelpextension$.md)
## ChromeStart.getHelpExtension$() method
Get an observable of the current custom help conttent
<b>Signature:</b>
```typescript
getHelpExtension$(): Observable<ChromeHelpExtension | undefined>;
```
<b>Returns:</b>
`Observable<ChromeHelpExtension | undefined>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getIsCollapsed$](./kibana-plugin-public.chromestart.getiscollapsed$.md)
## ChromeStart.getIsCollapsed$() method
Get an observable of the current collapsed state of the chrome.
<b>Signature:</b>
```typescript
getIsCollapsed$(): Observable<boolean>;
```
<b>Returns:</b>
`Observable<boolean>`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [getIsVisible$](./kibana-plugin-public.chromestart.getisvisible$.md)
## ChromeStart.getIsVisible$() method
Get an observable of the current visibility state of the chrome.
<b>Signature:</b>
```typescript
getIsVisible$(): Observable<boolean>;
```
<b>Returns:</b>
`Observable<boolean>`

View file

@ -2,11 +2,40 @@
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md)
## ChromeStart type
## ChromeStart interface
<b>Signature:</b>
```typescript
export declare type ChromeStart = ReturnType<ChromeService['start']>;
export interface ChromeStart
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [navControls](./kibana-plugin-public.chromestart.navcontrols.md) | <code>ChromeNavControls</code> | [APIs](./kibana-plugin-public.chromenavcontrols.md) for registering new controls to be displayed in the navigation bar. |
| [navLinks](./kibana-plugin-public.chromestart.navlinks.md) | <code>ChromeNavLinks</code> | [APIs](./kibana-plugin-public.chromenavlinks.md) for manipulating nav links. |
| [recentlyAccessed](./kibana-plugin-public.chromestart.recentlyaccessed.md) | <code>ChromeRecentlyAccessed</code> | [APIs](./kibana-plugin-public.chromerecentlyaccessed.md) for recently accessed history. |
## Methods
| Method | Description |
| --- | --- |
| [addApplicationClass(className)](./kibana-plugin-public.chromestart.addapplicationclass.md) | Add a className that should be set on the application container. |
| [getApplicationClasses$()](./kibana-plugin-public.chromestart.getapplicationclasses$.md) | Get the current set of classNames that will be set on the application container. |
| [getBadge$()](./kibana-plugin-public.chromestart.getbadge$.md) | Get an observable of the current badge |
| [getBrand$()](./kibana-plugin-public.chromestart.getbrand$.md) | Get an observable of the current brand information. |
| [getBreadcrumbs$()](./kibana-plugin-public.chromestart.getbreadcrumbs$.md) | Get an observable of the current list of breadcrumbs |
| [getHelpExtension$()](./kibana-plugin-public.chromestart.gethelpextension$.md) | Get an observable of the current custom help conttent |
| [getIsCollapsed$()](./kibana-plugin-public.chromestart.getiscollapsed$.md) | Get an observable of the current collapsed state of the chrome. |
| [getIsVisible$()](./kibana-plugin-public.chromestart.getisvisible$.md) | Get an observable of the current visibility state of the chrome. |
| [removeApplicationClass(className)](./kibana-plugin-public.chromestart.removeapplicationclass.md) | Remove a className added with <code>addApplicationClass()</code>. If className is unknown it is ignored. |
| [setBadge(badge)](./kibana-plugin-public.chromestart.setbadge.md) | Override the current badge |
| [setBrand(brand)](./kibana-plugin-public.chromestart.setbrand.md) | Set the brand configuration. |
| [setBreadcrumbs(newBreadcrumbs)](./kibana-plugin-public.chromestart.setbreadcrumbs.md) | Override the current set of breadcrumbs |
| [setHelpExtension(helpExtension)](./kibana-plugin-public.chromestart.sethelpextension.md) | Override the current set of custom help content |
| [setIsCollapsed(isCollapsed)](./kibana-plugin-public.chromestart.setiscollapsed.md) | Set the collapsed state of the chrome navigation. |
| [setIsVisible(isVisible)](./kibana-plugin-public.chromestart.setisvisible.md) | Set the temporary visibility for the chrome. This does nothing if the chrome is hidden by default and should be used to hide the chrome for things like full-screen modes with an exit button. |

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; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [navControls](./kibana-plugin-public.chromestart.navcontrols.md)
## ChromeStart.navControls property
[APIs](./kibana-plugin-public.chromenavcontrols.md) for registering new controls to be displayed in the navigation bar.
<b>Signature:</b>
```typescript
navControls: ChromeNavControls;
```

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; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [navLinks](./kibana-plugin-public.chromestart.navlinks.md)
## ChromeStart.navLinks property
[APIs](./kibana-plugin-public.chromenavlinks.md) for manipulating nav links.
<b>Signature:</b>
```typescript
navLinks: ChromeNavLinks;
```

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; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [recentlyAccessed](./kibana-plugin-public.chromestart.recentlyaccessed.md)
## ChromeStart.recentlyAccessed property
[APIs](./kibana-plugin-public.chromerecentlyaccessed.md) for recently accessed history.
<b>Signature:</b>
```typescript
recentlyAccessed: ChromeRecentlyAccessed;
```

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [removeApplicationClass](./kibana-plugin-public.chromestart.removeapplicationclass.md)
## ChromeStart.removeApplicationClass() method
Remove a className added with `addApplicationClass()`<!-- -->. If className is unknown it is ignored.
<b>Signature:</b>
```typescript
removeApplicationClass(className: string): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| className | <code>string</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [setBadge](./kibana-plugin-public.chromestart.setbadge.md)
## ChromeStart.setBadge() method
Override the current badge
<b>Signature:</b>
```typescript
setBadge(badge?: ChromeBadge): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| badge | <code>ChromeBadge</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,39 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [setBrand](./kibana-plugin-public.chromestart.setbrand.md)
## ChromeStart.setBrand() method
Set the brand configuration.
<b>Signature:</b>
```typescript
setBrand(brand: ChromeBrand): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| brand | <code>ChromeBrand</code> | |
<b>Returns:</b>
`void`
## Remarks
Normally the `logo` property will be rendered as the CSS background for the home link in the chrome navigation, but when the page is rendered in a small window the `smallLogo` will be used and rendered at about 45px wide.
## Example
```js
chrome.setBrand({
logo: 'url(/plugins/app/logo.png) center no-repeat'
smallLogo: 'url(/plugins/app/logo-small.png) center no-repeat'
})
```

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [setBreadcrumbs](./kibana-plugin-public.chromestart.setbreadcrumbs.md)
## ChromeStart.setBreadcrumbs() method
Override the current set of breadcrumbs
<b>Signature:</b>
```typescript
setBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| newBreadcrumbs | <code>ChromeBreadcrumb[]</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [setHelpExtension](./kibana-plugin-public.chromestart.sethelpextension.md)
## ChromeStart.setHelpExtension() method
Override the current set of custom help content
<b>Signature:</b>
```typescript
setHelpExtension(helpExtension?: ChromeHelpExtension): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| helpExtension | <code>ChromeHelpExtension</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [setIsCollapsed](./kibana-plugin-public.chromestart.setiscollapsed.md)
## ChromeStart.setIsCollapsed() method
Set the collapsed state of the chrome navigation.
<b>Signature:</b>
```typescript
setIsCollapsed(isCollapsed: boolean): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| isCollapsed | <code>boolean</code> | |
<b>Returns:</b>
`void`

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ChromeStart](./kibana-plugin-public.chromestart.md) &gt; [setIsVisible](./kibana-plugin-public.chromestart.setisvisible.md)
## ChromeStart.setIsVisible() method
Set the temporary visibility for the chrome. This does nothing if the chrome is hidden by default and should be used to hide the chrome for things like full-screen modes with an exit button.
<b>Signature:</b>
```typescript
setIsVisible(isVisible: boolean): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| isVisible | <code>boolean</code> | |
<b>Returns:</b>
`void`

View file

@ -27,7 +27,13 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ChromeBadge](./kibana-plugin-public.chromebadge.md) | |
| [ChromeBrand](./kibana-plugin-public.chromebrand.md) | |
| [ChromeBreadcrumb](./kibana-plugin-public.chromebreadcrumb.md) | |
| [ChromeNavControl](./kibana-plugin-public.chromenavcontrol.md) | |
| [ChromeNavControls](./kibana-plugin-public.chromenavcontrols.md) | [APIs](./kibana-plugin-public.chromenavcontrols.md) for registering new controls to be displayed in the navigation bar. |
| [ChromeNavLink](./kibana-plugin-public.chromenavlink.md) | |
| [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) | [APIs](./kibana-plugin-public.chromenavlinks.md) for manipulating nav links. |
| [ChromeRecentlyAccessed](./kibana-plugin-public.chromerecentlyaccessed.md) | [APIs](./kibana-plugin-public.chromerecentlyaccessed.md) for recently accessed history. |
| [ChromeRecentlyAccessedHistoryItem](./kibana-plugin-public.chromerecentlyaccessedhistoryitem.md) | |
| [ChromeStart](./kibana-plugin-public.chromestart.md) | |
| [CoreSetup](./kibana-plugin-public.coresetup.md) | Core services exposed to the <code>Plugin</code> setup lifecycle |
| [CoreStart](./kibana-plugin-public.corestart.md) | Core services exposed to the <code>Plugin</code> start lifecycle |
| [ErrorToastOptions](./kibana-plugin-public.errortoastoptions.md) | |
@ -50,7 +56,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| Type Alias | Description |
| --- | --- |
| [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) | |
| [ChromeStart](./kibana-plugin-public.chromestart.md) | |
| [ChromeNavLinkUpdateableFields](./kibana-plugin-public.chromenavlinkupdateablefields.md) | |
| [HttpSetup](./kibana-plugin-public.httpsetup.md) | |
| [HttpStart](./kibana-plugin-public.httpstart.md) | |
| [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. |

View file

@ -275,6 +275,7 @@
"@microsoft/api-extractor": "7.1.8",
"@octokit/rest": "^15.10.0",
"@percy/agent": "^0.1.16",
"@trust/webcrypto": "^0.9.2",
"@types/angular": "1.6.50",
"@types/angular-mocks": "^1.7.0",
"@types/babel__core": "^7.1.0",
@ -374,6 +375,7 @@
"eslint-plugin-react-hooks": "1.6.0",
"exit-hook": "^2.1.0",
"faker": "1.1.0",
"fast-text-encoding": "^1.0.0",
"fetch-mock": "7.3.3",
"geckodriver": "1.16.2",
"getopts": "^2.2.4",

View file

@ -37,6 +37,17 @@ const createStartContractMock = () => {
enableForcedAppSwitcherNavigation: jest.fn(),
getForceAppSwitcherNavigation$: jest.fn(),
},
recentlyAccessed: {
add: jest.fn(),
get: jest.fn(),
get$: jest.fn(),
},
navControls: {
registerLeft: jest.fn(),
registerRight: jest.fn(),
getLeft$: jest.fn(),
getRight$: jest.fn(),
},
setBrand: jest.fn(),
getBrand$: jest.fn(),
setIsVisible: jest.fn(),
@ -69,7 +80,7 @@ const createMock = () => {
start: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.start.mockResolvedValue(createStartContractMock());
return mocked;
};

View file

@ -24,6 +24,7 @@ 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';
const store = new Map();
(window as any).localStorage = {
@ -32,14 +33,12 @@ const store = new Map();
removeItem: (key: string) => store.delete(String(key)),
};
import { ChromeService } from './chrome_service';
function defaultStartDeps(): any {
return {
application: applicationServiceMock.createStartContract(),
http: httpServiceMock.createStartContract(),
notifications: notificationServiceMock.createStartContract(),
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
notifications: notificationServiceMock.createStartContract(),
};
}
@ -48,11 +47,11 @@ beforeEach(() => {
});
describe('start', () => {
it('adds legacy browser warning if browserSupportsCsp is disabled and warnLegacyBrowsers is enabled', () => {
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 });
service.start(startDeps);
await service.start(startDeps);
expect(startDeps.notifications.toasts.addWarning.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
@ -62,26 +61,26 @@ Array [
`);
});
it('does not add legacy browser warning if browser supports CSP', () => {
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 });
service.start(startDeps);
await service.start(startDeps);
expect(startDeps.notifications.toasts.addWarning).not.toBeCalled();
});
it('does not add legacy browser warning if warnLegacyBrowsers is disabled', () => {
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 });
service.start(startDeps);
await service.start(startDeps);
expect(startDeps.notifications.toasts.addWarning).not.toBeCalled();
});
describe('brand', () => {
it('updates/emits the brand as it changes', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getBrand$()
.pipe(toArray())
@ -115,7 +114,7 @@ Array [
describe('visibility', () => {
it('updates/emits the visibility', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getIsVisible$()
.pipe(toArray())
@ -140,7 +139,7 @@ Array [
window.history.pushState(undefined, '', '#/home?a=b&embed=true');
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getIsVisible$()
.pipe(toArray())
@ -165,7 +164,7 @@ Array [
describe('is collapsed', () => {
it('updates/emits isCollapsed', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getIsCollapsed$()
.pipe(toArray())
@ -188,7 +187,7 @@ Array [
it('only stores true in localStorage', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
start.setIsCollapsed(true);
expect(store.size).toBe(1);
@ -201,7 +200,7 @@ Array [
describe('application classes', () => {
it('updates/emits the application classes', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getApplicationClasses$()
.pipe(toArray())
@ -253,7 +252,7 @@ Array [
describe('badge', () => {
it('updates/emits the current badge', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getBadge$()
.pipe(toArray())
@ -284,7 +283,7 @@ Array [
describe('breadcrumbs', () => {
it('updates/emits the current set of breadcrumbs', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getBreadcrumbs$()
.pipe(toArray())
@ -326,7 +325,7 @@ Array [
describe('help extension', () => {
it('updates/emits the current help extension', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = start
.getHelpExtension$()
.pipe(toArray())
@ -350,7 +349,7 @@ Array [
describe('stop', () => {
it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
const promise = Rx.combineLatest(
start.getBrand$(),
start.getApplicationClasses$(),
@ -366,7 +365,7 @@ describe('stop', () => {
it('completes immediately if service already stopped', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const start = await service.start(defaultStartDeps());
service.stop();
await expect(

View file

@ -20,15 +20,21 @@
import * as Url from 'url';
import { i18n } from '@kbn/i18n';
import * as Rx from 'rxjs';
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { IconType } from '@elastic/eui';
import { InjectedMetadataStart } from '../injected_metadata';
import { NotificationsStart } from '../notifications';
import { NavLinksService } from './nav_links/nav_links_service';
import { ApplicationStart } from '../application';
import { HttpStart } from '../http';
import { ChromeNavLinks, NavLinksService } from './nav_links';
import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed';
import { NavControlsService, ChromeNavControls } from './nav_controls';
export { ChromeNavControls, ChromeRecentlyAccessed };
const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed';
function isEmbedParamInHash() {
@ -72,24 +78,31 @@ interface StartDeps {
/** @internal */
export class ChromeService {
private readonly stop$ = new Rx.ReplaySubject(1);
private readonly stop$ = new ReplaySubject(1);
private readonly browserSupportsCsp: boolean;
private readonly navControls = new NavControlsService();
private readonly navLinks = new NavLinksService();
private readonly recentlyAccessed = new RecentlyAccessedService();
public constructor({ browserSupportsCsp }: ConstructorParams) {
this.browserSupportsCsp = browserSupportsCsp;
}
public start({ application, http, injectedMetadata, notifications }: StartDeps) {
public async start({
application,
http,
injectedMetadata,
notifications,
}: StartDeps): Promise<ChromeStart> {
const FORCE_HIDDEN = isEmbedParamInHash();
const brand$ = new Rx.BehaviorSubject<ChromeBrand>({});
const isVisible$ = new Rx.BehaviorSubject(true);
const isCollapsed$ = new Rx.BehaviorSubject(!!localStorage.getItem(IS_COLLAPSED_KEY));
const applicationClasses$ = new Rx.BehaviorSubject<Set<string>>(new Set());
const helpExtension$ = new Rx.BehaviorSubject<ChromeHelpExtension | undefined>(undefined);
const breadcrumbs$ = new Rx.BehaviorSubject<ChromeBreadcrumb[]>([]);
const badge$ = new Rx.BehaviorSubject<ChromeBadge | undefined>(undefined);
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);
const breadcrumbs$ = new BehaviorSubject<ChromeBreadcrumb[]>([]);
const badge$ = new BehaviorSubject<ChromeBadge | undefined>(undefined);
if (!this.browserSupportsCsp && injectedMetadata.getCspConfig().warnLegacyBrowsers) {
notifications.toasts.addWarning(
@ -100,22 +113,12 @@ export class ChromeService {
}
return {
navControls: this.navControls.start(),
navLinks: this.navLinks.start({ application, http }),
recentlyAccessed: await this.recentlyAccessed.start({ http }),
getBrand$: () => brand$.pipe(takeUntil(this.stop$)),
/**
* Set the brand configuration. Normally the `logo` property will be rendered as the
* CSS background for the home link in the chrome navigation, but when the page is
* rendered in a small window the `smallLogo` will be used and rendered at about
* 45px wide.
*
* example:
*
* chrome.setBrand({
* logo: 'url(/plugins/app/logo.png) center no-repeat'
* smallLogo: 'url(/plugins/app/logo-small.png) center no-repeat'
* })
*
*/
setBrand: (brand: ChromeBrand) => {
brand$.next(
Object.freeze({
@ -125,32 +128,18 @@ export class ChromeService {
);
},
/**
* Get an observable of the current brand information.
*/
getBrand$: () => brand$.pipe(takeUntil(this.stop$)),
/**
* Set the temporary visibility for the chrome. This does nothing if the chrome is hidden
* by default and should be used to hide the chrome for things like full-screen modes
* with an exit button.
*/
setIsVisible: (visibility: boolean) => {
isVisible$.next(visibility);
},
/**
* Get an observable of the current visibility state of the chrome.
*/
getIsVisible$: () =>
isVisible$.pipe(
map(visibility => (FORCE_HIDDEN ? false : visibility)),
takeUntil(this.stop$)
),
/**
* Set the collapsed state of the chrome navigation.
*/
setIsVisible: (visibility: boolean) => {
isVisible$.next(visibility);
},
getIsCollapsed$: () => isCollapsed$.pipe(takeUntil(this.stop$)),
setIsCollapsed: (isCollapsed: boolean) => {
isCollapsed$.next(isCollapsed);
if (isCollapsed) {
@ -160,69 +149,38 @@ export class ChromeService {
}
},
/**
* Get an observable of the current collapsed state of the chrome.
*/
getIsCollapsed$: () => isCollapsed$.pipe(takeUntil(this.stop$)),
getApplicationClasses$: () =>
applicationClasses$.pipe(
map(set => [...set]),
takeUntil(this.stop$)
),
/**
* Add a className that should be set on the application container.
*/
addApplicationClass: (className: string) => {
const update = new Set([...applicationClasses$.getValue()]);
update.add(className);
applicationClasses$.next(update);
},
/**
* Remove a className added with `addApplicationClass()`. If className is unknown it is ignored.
*/
removeApplicationClass: (className: string) => {
const update = new Set([...applicationClasses$.getValue()]);
update.delete(className);
applicationClasses$.next(update);
},
/**
* Get the current set of classNames that will be set on the application container.
*/
getApplicationClasses$: () =>
applicationClasses$.pipe(
map(set => [...set]),
takeUntil(this.stop$)
),
/**
* Get an observable of the current badge
*/
getBadge$: () => badge$.pipe(takeUntil(this.stop$)),
/**
* Override the current badge
*/
setBadge: (badge: ChromeBadge | undefined) => {
setBadge: (badge: ChromeBadge) => {
badge$.next(badge);
},
/**
* Get an observable of the current list of breadcrumbs
*/
getBreadcrumbs$: () => breadcrumbs$.pipe(takeUntil(this.stop$)),
/**
* Override the current set of breadcrumbs
*/
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => {
breadcrumbs$.next(newBreadcrumbs);
},
/**
* Get an observable of the current custom help conttent
*/
getHelpExtension$: () => helpExtension$.pipe(takeUntil(this.stop$)),
/**
* Override the current set of breadcrumbs
*/
setHelpExtension: (helpExtension?: ChromeHelpExtension) => {
helpExtension$.next(helpExtension);
},
@ -236,4 +194,103 @@ export class ChromeService {
}
/** @public */
export type ChromeStart = ReturnType<ChromeService['start']>;
export interface ChromeStart {
/** {@inheritdoc ChromeNavLinks} */
navLinks: ChromeNavLinks;
/** {@inheritdoc ChromeNavControls} */
navControls: ChromeNavControls;
/** {@inheritdoc ChromeRecentlyAccessed} */
recentlyAccessed: ChromeRecentlyAccessed;
/**
* Get an observable of the current brand information.
*/
getBrand$(): Observable<ChromeBrand>;
/**
* Set the brand configuration.
*
* @remarks
* Normally the `logo` property will be rendered as the
* CSS background for the home link in the chrome navigation, but when the page is
* rendered in a small window the `smallLogo` will be used and rendered at about
* 45px wide.
*
* @example
* ```js
* chrome.setBrand({
* logo: 'url(/plugins/app/logo.png) center no-repeat'
* smallLogo: 'url(/plugins/app/logo-small.png) center no-repeat'
* })
* ```
*
*/
setBrand(brand: ChromeBrand): void;
/**
* Get an observable of the current visibility state of the chrome.
*/
getIsVisible$(): Observable<boolean>;
/**
* Set the temporary visibility for the chrome. This does nothing if the chrome is hidden
* by default and should be used to hide the chrome for things like full-screen modes
* with an exit button.
*/
setIsVisible(isVisible: boolean): void;
/**
* Get an observable of the current collapsed state of the chrome.
*/
getIsCollapsed$(): Observable<boolean>;
/**
* Set the collapsed state of the chrome navigation.
*/
setIsCollapsed(isCollapsed: boolean): void;
/**
* Get the current set of classNames that will be set on the application container.
*/
getApplicationClasses$(): Observable<string[]>;
/**
* Add a className that should be set on the application container.
*/
addApplicationClass(className: string): void;
/**
* Remove a className added with `addApplicationClass()`. If className is unknown it is ignored.
*/
removeApplicationClass(className: string): void;
/**
* Get an observable of the current badge
*/
getBadge$(): Observable<ChromeBadge | undefined>;
/**
* Override the current badge
*/
setBadge(badge?: ChromeBadge): void;
/**
* Get an observable of the current list of breadcrumbs
*/
getBreadcrumbs$(): Observable<ChromeBreadcrumb[]>;
/**
* Override the current set of breadcrumbs
*/
setBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]): void;
/**
* Get an observable of the current custom help conttent
*/
getHelpExtension$(): Observable<ChromeHelpExtension | undefined>;
/**
* Override the current set of custom help content
*/
setHelpExtension(helpExtension?: ChromeHelpExtension): void;
}

View file

@ -25,4 +25,6 @@ export {
ChromeBrand,
ChromeHelpExtension,
} from './chrome_service';
export { ChromeNavLink } from './nav_links';
export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links';
export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed';
export { ChromeNavControl, ChromeNavControls } from './nav_controls';

View file

@ -0,0 +1,20 @@
/*
* 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.
*/
export * from './nav_controls_service';

View file

@ -0,0 +1,87 @@
/*
* 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 { NavControlsService } from './nav_controls_service';
import { take } from 'rxjs/operators';
describe('RecentlyAccessed#start()', () => {
const getStart = () => {
return new NavControlsService().start();
};
describe('left side', () => {
it('allows registration', async () => {
const navControls = getStart();
const nc = { mount: jest.fn() };
navControls.registerLeft(nc);
expect(
await navControls
.getLeft$()
.pipe(take(1))
.toPromise()
).toEqual([nc]);
});
it('sorts controls by order property', async () => {
const navControls = getStart();
const nc1 = { mount: jest.fn(), order: 10 };
const nc2 = { mount: jest.fn(), order: 0 };
const nc3 = { mount: jest.fn(), order: 20 };
navControls.registerLeft(nc1);
navControls.registerLeft(nc2);
navControls.registerLeft(nc3);
expect(
await navControls
.getLeft$()
.pipe(take(1))
.toPromise()
).toEqual([nc2, nc1, nc3]);
});
});
describe('right side', () => {
it('allows registration', async () => {
const navControls = getStart();
const nc = { mount: jest.fn() };
navControls.registerRight(nc);
expect(
await navControls
.getRight$()
.pipe(take(1))
.toPromise()
).toEqual([nc]);
});
it('sorts controls by order property', async () => {
const navControls = getStart();
const nc1 = { mount: jest.fn(), order: 10 };
const nc2 = { mount: jest.fn(), order: 0 };
const nc3 = { mount: jest.fn(), order: 20 };
navControls.registerRight(nc1);
navControls.registerRight(nc2);
navControls.registerRight(nc3);
expect(
await navControls
.getRight$()
.pipe(take(1))
.toPromise()
).toEqual([nc2, nc1, nc3]);
});
});
});

View file

@ -0,0 +1,86 @@
/*
* 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 { sortBy } from 'lodash';
import { BehaviorSubject, ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
/** @public */
export interface ChromeNavControl {
order?: number;
mount(targetDomElement: HTMLElement): () => void;
}
/**
* {@link ChromeNavControls | APIs} for registering new controls to be displayed in the navigation bar.
*
* @example
* Register a left-side nav control rendered with React.
* ```jsx
* chrome.navControls.registerLeft({
* mount(targetDomElement) {
* ReactDOM.mount(<MyControl />, targetDomElement);
* return () => ReactDOM.unmountComponentAtNode(targetDomElement);
* }
* })
* ```
*
* @public
*/
export interface ChromeNavControls {
/** Register a nav control to be presented on the left side of the chrome header. */
registerLeft(navControl: ChromeNavControl): void;
/** Register a nav control to be presented on the right side of the chrome header. */
registerRight(navControl: ChromeNavControl): void;
}
/** @internal */
export class NavControlsService {
private readonly stop$ = new ReplaySubject(1);
public start() {
const navControlsLeft$ = new BehaviorSubject<ReadonlySet<ChromeNavControl>>(new Set());
const navControlsRight$ = new BehaviorSubject<ReadonlySet<ChromeNavControl>>(new Set());
return {
// In the future, registration should be moved to the setup phase. This
// is not possible until the legacy nav controls are no longer supported.
registerLeft: (navControl: ChromeNavControl) =>
navControlsLeft$.next(new Set([...navControlsLeft$.value.values(), navControl])),
registerRight: (navControl: ChromeNavControl) =>
navControlsRight$.next(new Set([...navControlsRight$.value.values(), navControl])),
getLeft$: () =>
navControlsLeft$.pipe(
map(controls => sortBy([...controls.values()], 'order')),
takeUntil(this.stop$)
),
getRight$: () =>
navControlsRight$.pipe(
map(controls => sortBy([...controls.values()], 'order')),
takeUntil(this.stop$)
),
};
}
public stop() {
this.stop$.next();
}
}

View file

@ -17,5 +17,5 @@
* under the License.
*/
export { ChromeNavLink } from './nav_link';
export { NavLinksService } from './nav_links_service';
export { ChromeNavLink, ChromeNavLinkUpdateableFields } from './nav_link';
export { ChromeNavLinks, NavLinksService } from './nav_links_service';

View file

@ -109,7 +109,8 @@ export interface ChromeNavLink {
readonly url?: string;
}
export type NavLinkUpdateableFields = Partial<
/** @public */
export type ChromeNavLinkUpdateableFields = Partial<
Pick<ChromeNavLink, 'active' | 'disabled' | 'hidden' | 'url' | 'subUrlBase'>
>;
@ -126,7 +127,7 @@ export class NavLinkWrapper {
this.properties = Object.freeze(properties);
}
public update(newProps: NavLinkUpdateableFields) {
public update(newProps: ChromeNavLinkUpdateableFields) {
// Enforce limited properties at runtime for JS code
newProps = pick(newProps, ['active', 'disabled', 'hidden', 'url', 'subUrlBase']);
return new NavLinkWrapper({ ...this.properties, ...newProps });

View file

@ -18,9 +18,9 @@
*/
import { sortBy } from 'lodash';
import { BehaviorSubject, ReplaySubject } from 'rxjs';
import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { NavLinkWrapper, NavLinkUpdateableFields } from './nav_link';
import { NavLinkWrapper, ChromeNavLinkUpdateableFields, ChromeNavLink } from './nav_link';
import { ApplicationStart } from '../../application';
import { HttpStart } from '../../http';
@ -29,10 +29,76 @@ interface StartDeps {
http: HttpStart;
}
/**
* {@link ChromeNavLinks | APIs} for manipulating nav links.
*
* @public
*/
export interface ChromeNavLinks {
/**
* Get an observable for a sorted list of navlinks.
*/
getNavLinks$(): Observable<Array<Readonly<ChromeNavLink>>>;
/**
* Get the state of a navlink at this point in time.
* @param id
*/
get(id: string): ChromeNavLink | undefined;
/**
* Get the current state of all navlinks.
*/
getAll(): Array<Readonly<ChromeNavLink>>;
/**
* Check whether or not a navlink exists.
* @param id
*/
has(id: string): boolean;
/**
* Remove all navlinks except the one matching the given id.
*
* @remarks
* NOTE: this is not reversible.
*
* @param id
*/
showOnly(id: string): void;
/**
* Update the navlink for the given id with the updated attributes.
* Returns the updated navlink or `undefined` if it does not exist.
* @param id
* @param values
*/
update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
/**
* Enable forced navigation mode, which will trigger a page refresh
* when a nav link is clicked and only the hash is updated.
*
* @remarks
* This is only necessary when rendering the status page in place of another
* app, as links to that app will set the current URL and change the hash, but
* the routes for the correct are not loaded so nothing will happen.
* https://github.com/elastic/kibana/pull/29770
*
* Used only by status_page plugin
*/
enableForcedAppSwitcherNavigation(): void;
/**
* An observable of the forced app switcher state.
*/
getForceAppSwitcherNavigation$(): Observable<boolean>;
}
export class NavLinksService {
private readonly stop$ = new ReplaySubject(1);
public start({ application, http }: StartDeps) {
public start({ application, http }: StartDeps): ChromeNavLinks {
const navLinks$ = new BehaviorSubject<ReadonlyMap<string, NavLinkWrapper>>(
new Map(
application.availableApps.map(
@ -51,9 +117,6 @@ export class NavLinksService {
const forceAppSwitcherNavigation$ = new BehaviorSubject(false);
return {
/**
* Get an observable for a sorted list of navlinks.
*/
getNavLinks$: () => {
return navLinks$.pipe(
map(sortNavLinks),
@ -61,35 +124,19 @@ export class NavLinksService {
);
},
/**
* Get the state of a navlink at this point in time.
* @param id
*/
get(id: string) {
const link = navLinks$.value.get(id);
return link && link.properties;
},
/**
* Get the current state of all navlinks.
*/
getAll() {
return sortNavLinks(navLinks$.value);
},
/**
* Check whether or not a navlink exists.
* @param id
*/
has(id: string) {
return navLinks$.value.has(id);
},
/**
* Remove all navlinks except the one matching the given id.
* NOTE: this is not reversible.
* @param id
*/
showOnly(id: string) {
if (!this.has(id)) {
return;
@ -98,13 +145,7 @@ export class NavLinksService {
navLinks$.next(new Map([...navLinks$.value.entries()].filter(([linkId]) => linkId === id)));
},
/**
* Update the navlink for the given id with the updated attributes.
* Returns the updated navlink or `undefined` if it does not exist.
* @param id
* @param values
*/
update(id: string, values: NavLinkUpdateableFields) {
update(id: string, values: ChromeNavLinkUpdateableFields) {
if (!this.has(id)) {
return;
}
@ -123,23 +164,10 @@ export class NavLinksService {
return this.get(id);
},
/**
* Enable forced navigation mode, which will trigger a page refresh
* when a nav link is clicked and only the hash is updated. This is only
* necessary when rendering the status page in place of another app, as
* links to that app will set the current URL and change the hash, but
* the routes for the correct are not loaded so nothing will happen.
* https://github.com/elastic/kibana/pull/29770
*
* Used only by status_page plugin
*/
enableForcedAppSwitcherNavigation() {
forceAppSwitcherNavigation$.next(true);
},
/**
* An observable of the forced app switcher state.
*/
getForceAppSwitcherNavigation$() {
return forceAppSwitcherNavigation$.asObservable();
},

View file

@ -20,16 +20,16 @@
import { createLogKey } from './create_log_key';
describe('createLogKey', () => {
it('should create a key starting with "kibana.history"', () => {
expect(createLogKey('foo', 'bar')).toMatch(/^kibana\.history/);
it('should create a key starting with "kibana.history"', async () => {
expect(await createLogKey('foo', 'bar')).toMatch(/^kibana\.history/);
});
it('should include a hashed suffix of the identifier when present', () => {
it('should include a hashed suffix of the identifier when present', async () => {
const expectedSuffix = `/N4rLtula/QIYB+3If6bXDONEO5CnqBPrlURto+/j7k=`;
expect(createLogKey('foo', 'bar')).toMatch(`kibana.history.foo-${expectedSuffix}`);
expect(await createLogKey('foo', 'bar')).toMatch(`kibana.history.foo-${expectedSuffix}`);
});
it('should not include a hashed suffix if the identifier is not present', () => {
expect(createLogKey('foo')).toEqual('kibana.history.foo');
it('should not include a hashed suffix if the identifier is not present', async () => {
expect(await createLogKey('foo')).toEqual('kibana.history.foo');
});
});

View file

@ -17,15 +17,17 @@
* under the License.
*/
import { Sha256 } from '../crypto';
export function createLogKey(type: string, optionalIdentifier: string) {
export async function createLogKey(type: string, optionalIdentifier?: string) {
const baseKey = `kibana.history.${type}`;
if (!optionalIdentifier) {
return baseKey;
}
const protectedIdentifier = new Sha256().update(optionalIdentifier, 'utf8').digest('base64');
const encoder = new TextEncoder();
const data = encoder.encode(optionalIdentifier);
const buffer = await window.crypto.subtle.digest({ name: 'SHA-256' }, data);
const protectedIdentifier = btoa(String.fromCharCode(...new Uint8Array(buffer)));
return `${baseKey}-${protectedIdentifier}`;
}

View file

@ -0,0 +1,24 @@
/*
* 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.
*/
export {
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,
RecentlyAccessedService,
} from './recently_accessed_service';

View file

@ -0,0 +1,140 @@
/*
* 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 { PersistedLog } from './persisted_log';
const createMockStorage = () => ({
clear: jest.fn(),
getItem: jest.fn(),
key: jest.fn(),
removeItem: jest.fn(),
setItem: jest.fn(),
length: 0,
});
jest.mock('ui/chrome', () => {
return {
getBasePath: () => `/some/base/path`,
};
});
const historyName = 'testHistory';
const historyLimit = 10;
const payload = [
{ first: 'clark', last: 'kent' },
{ first: 'peter', last: 'parker' },
{ first: 'bruce', last: 'wayne' },
];
describe('PersistedLog', () => {
let storage = createMockStorage();
beforeEach(() => {
storage = createMockStorage();
});
describe('expected API', () => {
test('has expected methods', () => {
const log = new PersistedLog(historyName, { maxLength: 10 }, storage);
expect(typeof log.add).toBe('function');
expect(typeof log.get).toBe('function');
});
});
describe('internal functionality', () => {
test('reads from storage', () => {
// @ts-ignore
const log = new PersistedLog(historyName, { maxLength: 10 }, storage);
expect(storage.getItem).toHaveBeenCalledTimes(1);
expect(storage.getItem).toHaveBeenCalledWith(historyName);
});
test('writes to storage', () => {
const log = new PersistedLog(historyName, { maxLength: 10 }, storage);
const newItem = { first: 'diana', last: 'prince' };
const data = log.add(newItem);
expect(storage.setItem).toHaveBeenCalledTimes(1);
expect(data).toEqual([newItem]);
});
});
describe('persisting data', () => {
test('fetches records from storage', () => {
storage.getItem.mockReturnValue(JSON.stringify(payload));
const log = new PersistedLog(historyName, { maxLength: 10 }, storage);
const items = log.get();
expect(items.length).toBe(3);
expect(items).toEqual(payload);
});
test('prepends new records', () => {
storage.getItem.mockReturnValue(JSON.stringify(payload.slice(0)));
const log = new PersistedLog(historyName, { maxLength: 10 }, storage);
const newItem = { first: 'selina', last: 'kyle' };
const items = log.add(newItem);
expect(items.length).toBe(payload.length + 1);
expect(items[0]).toEqual(newItem);
});
});
describe('stack options', () => {
test('should observe the maxLength option', () => {
const bulkData = [];
for (let i = 0; i < historyLimit; i++) {
bulkData.push(['record ' + i]);
}
storage.getItem.mockReturnValue(JSON.stringify(bulkData));
const log = new PersistedLog(historyName, { maxLength: historyLimit }, storage);
log.add(['new array 1']);
const items = log.add(['new array 2']);
expect(items.length).toBe(historyLimit);
});
test('should observe the filterDuplicates option', () => {
storage.getItem.mockReturnValue(JSON.stringify(payload.slice(0)));
const log = new PersistedLog(historyName, { maxLength: 10 }, storage);
const newItem = payload[1];
const items = log.add(newItem);
expect(items.length).toBe(payload.length);
});
test('should truncate the list upon initialization if too long', () => {
storage.getItem.mockReturnValue(JSON.stringify(payload.slice(0)));
const log = new PersistedLog(historyName, { maxLength: 1 }, storage);
const items = log.get();
expect(items.length).toBe(1);
});
test('should allow a maxLength of 0', () => {
storage.getItem.mockReturnValue(JSON.stringify(payload.slice(0)));
const log = new PersistedLog(historyName, { maxLength: 0 }, storage);
const items = log.get();
expect(items.length).toBe(0);
});
});
});

View file

@ -0,0 +1,86 @@
/*
* 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 { cloneDeep, isEqual, take } from 'lodash';
import * as Rx from 'rxjs';
import { map } from 'rxjs/operators';
interface PersistedLogOptions<T = any> {
maxLength: number | string;
isEqual?: (oldItem: T, newItem: T) => boolean;
}
export class PersistedLog<T = any> {
private name: string;
private maxLength: number;
private isEqual: (oldItem: T, newItem: T) => boolean;
private storage: Storage;
private items$: Rx.BehaviorSubject<T[]>;
constructor(name: string, options: PersistedLogOptions<T>, storage = localStorage) {
this.name = name;
this.maxLength =
typeof options.maxLength === 'string'
? (this.maxLength = parseInt(options.maxLength, 10))
: options.maxLength;
this.isEqual = options.isEqual || isEqual;
this.storage = storage;
this.items$ = new Rx.BehaviorSubject<T[]>(this.loadItems());
if (this.maxLength !== undefined && !isNaN(this.maxLength)) {
this.items$.next(take(this.items$.value, this.maxLength));
}
}
public add(val: T) {
if (val == null) {
return this.items$.value;
}
const nextItems = [
val,
// remove any duplicate items
...[...this.items$.value].filter(item => !this.isEqual(item, val)),
].slice(0, this.maxLength); // truncate
// Persist the stack to storage
this.storage.setItem(this.name, JSON.stringify(nextItems));
// Notify subscribers
this.items$.next(nextItems);
return nextItems;
}
public get() {
return cloneDeep(this.items$.value);
}
public get$() {
return this.items$.pipe(map(items => cloneDeep(items)));
}
private loadItems() {
try {
return JSON.parse(this.storage.getItem(this.name) || '[]');
} catch {
return [];
}
}
}

View file

@ -0,0 +1,143 @@
/*
* 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 { httpServiceMock } from '../../http/http_service.mock';
import { RecentlyAccessedService } from './recently_accessed_service';
import { Subject } from 'rxjs';
import { takeUntil, bufferCount } from 'rxjs/operators';
// Maybe this should be moved to our global jest polyfills?
class LocalStorageMock implements Storage {
private store = new Map<string, string>();
clear() {
this.store.clear();
}
getItem(key: string) {
return this.store.get(key) || null;
}
setItem(key: string, value: any) {
this.store.set(key, value.toString());
}
removeItem(key: string) {
this.store.delete(key);
}
key(index: number) {
return [...this.store.keys()][index] || null;
}
get length() {
return this.store.size;
}
}
describe('RecentlyAccessed#start()', () => {
let originalLocalStorage: Storage;
beforeAll(() => {
originalLocalStorage = window.localStorage;
// @ts-ignore
window.localStorage = new LocalStorageMock();
});
beforeEach(() => localStorage.clear());
// @ts-ignore
afterAll(() => (window.localStorage = originalLocalStorage));
const getStart = async () => {
const http = httpServiceMock.createStartContract();
const recentlyAccessed = await new RecentlyAccessedService().start({ http });
return { http, recentlyAccessed };
};
it('allows adding and getting items', async () => {
const { recentlyAccessed } = await getStart();
recentlyAccessed.add('/app/item1', 'Item 1', 'item1');
recentlyAccessed.add('/app/item2', 'Item 2', 'item2');
expect(recentlyAccessed.get()).toEqual([
{ link: '/app/item2', label: 'Item 2', id: 'item2' },
{ link: '/app/item1', label: 'Item 1', id: 'item1' },
]);
});
it('persists data to localStorage', async () => {
const { recentlyAccessed: ra1 } = await getStart();
ra1.add('/app/item1', 'Item 1', 'item1');
ra1.add('/app/item2', 'Item 2', 'item2');
const { recentlyAccessed: ra2 } = await getStart();
expect(ra2.get()).toEqual([
{ link: '/app/item2', label: 'Item 2', id: 'item2' },
{ link: '/app/item1', label: 'Item 1', id: 'item1' },
]);
});
it('de-duplicates the list', async () => {
const { recentlyAccessed } = await getStart();
recentlyAccessed.add('/app/item1', 'Item 1', 'item1');
recentlyAccessed.add('/app/item2', 'Item 2', 'item2');
recentlyAccessed.add('/app/item1', 'Item 1', 'item1');
expect(recentlyAccessed.get()).toEqual([
{ link: '/app/item1', label: 'Item 1', id: 'item1' },
{ link: '/app/item2', label: 'Item 2', id: 'item2' },
]);
});
it('exposes an observable', async () => {
const { recentlyAccessed } = await getStart();
const stop$ = new Subject();
const observedValues$ = recentlyAccessed
.get$()
.pipe(
bufferCount(3),
takeUntil(stop$)
)
.toPromise();
recentlyAccessed.add('/app/item1', 'Item 1', 'item1');
recentlyAccessed.add('/app/item2', 'Item 2', 'item2');
stop$.next();
expect(await observedValues$).toMatchInlineSnapshot(`
Array [
Array [],
Array [
Object {
"id": "item1",
"label": "Item 1",
"link": "/app/item1",
},
],
Array [
Object {
"id": "item2",
"label": "Item 2",
"link": "/app/item2",
},
Object {
"id": "item1",
"label": "Item 1",
"link": "/app/item1",
},
],
]
`);
});
});

View file

@ -0,0 +1,102 @@
/*
* 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 { Observable } from 'rxjs';
import { PersistedLog } from './persisted_log';
import { createLogKey } from './create_log_key';
import { HttpSetup } from '../../http';
/** @public */
export interface ChromeRecentlyAccessedHistoryItem {
link: string;
label: string;
id: string;
}
interface StartDeps {
http: HttpSetup;
}
/** @internal */
export class RecentlyAccessedService {
async start({ http }: StartDeps): Promise<ChromeRecentlyAccessed> {
const logKey = await createLogKey('recentlyAccessed', http.basePath.get());
const history = new PersistedLog<ChromeRecentlyAccessedHistoryItem>(logKey, {
maxLength: 20,
isEqual: (oldItem, newItem) => oldItem.id === newItem.id,
});
return {
/** Adds a new item to the history. */
add: (link: string, label: string, id: string) => {
history.add({
link,
label,
id,
});
},
/** Gets the current array of history items. */
get: () => history.get(),
/** Gets an observable of the current array of history items. */
get$: () => history.get$(),
};
}
}
/**
* {@link ChromeRecentlyAccessed | APIs} for recently accessed history.
* @public
*/
export interface ChromeRecentlyAccessed {
/**
* Adds a new item to the recently accessed history.
*
* @example
* ```js
* chrome.recentlyAccessed.add('/app/map/1234', 'Map 1234', '1234');
* ```
*
* @param link a relative URL to the resource (not including the {@link HttpStart.basePath | `http.basePath`})
* @param label the label to display in the UI
* @param id a unique string used to de-duplicate the recently accessed llist.
*/
add(link: string, label: string, id: string): void;
/**
* Gets an Array of the current recently accessed history.
*
* @example
* ```js
* chrome.recentlyAccessed.get().forEach(console.log);
* ```
*/
get(): ChromeRecentlyAccessedHistoryItem[];
/**
* Gets an Observable of the array of recently accessed history.
*
* @example
* ```js
* chrome.recentlyAccessed.get$().subscribe(console.log);
* ```
*/
get$(): Observable<ChromeRecentlyAccessedHistoryItem[]>;
}

View file

@ -40,8 +40,14 @@ import {
ChromeBrand,
ChromeBreadcrumb,
ChromeHelpExtension,
ChromeNavControl,
ChromeNavControls,
ChromeNavLink,
ChromeNavLinks,
ChromeNavLinkUpdateableFields,
ChromeStart,
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,
} from './chrome';
import { FatalErrorsSetup, FatalErrorInfo } from './fatal_errors';
import { HttpServiceBase, HttpSetup, HttpStart, HttpInterceptor } from './http';
@ -137,7 +143,13 @@ export {
ChromeBreadcrumb,
ChromeBrand,
ChromeHelpExtension,
ChromeNavControl,
ChromeNavControls,
ChromeNavLink,
ChromeNavLinks,
ChromeNavLinkUpdateableFields,
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,
I18nStart,
LegacyNavLink,
Plugin,

View file

@ -75,6 +75,20 @@ export interface ChromeBreadcrumb {
// @public (undocumented)
export type ChromeHelpExtension = (element: HTMLDivElement) => (() => void);
// @public (undocumented)
export interface ChromeNavControl {
// (undocumented)
mount(targetDomElement: HTMLElement): () => void;
// (undocumented)
order?: number;
}
// @public
export interface ChromeNavControls {
registerLeft(navControl: ChromeNavControl): void;
registerRight(navControl: ChromeNavControl): void;
}
// @public (undocumented)
export interface ChromeNavLink {
readonly active?: boolean;
@ -92,10 +106,60 @@ export interface ChromeNavLink {
readonly url?: string;
}
// Warning: (ae-forgotten-export) The symbol "ChromeService" needs to be exported by the entry point index.d.ts
//
// @public
export interface ChromeNavLinks {
enableForcedAppSwitcherNavigation(): void;
get(id: string): ChromeNavLink | undefined;
getAll(): Array<Readonly<ChromeNavLink>>;
getForceAppSwitcherNavigation$(): Observable<boolean>;
getNavLinks$(): Observable<Array<Readonly<ChromeNavLink>>>;
has(id: string): boolean;
showOnly(id: string): void;
update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
}
// @public (undocumented)
export type ChromeStart = ReturnType<ChromeService['start']>;
export type ChromeNavLinkUpdateableFields = Partial<Pick<ChromeNavLink, 'active' | 'disabled' | 'hidden' | 'url' | 'subUrlBase'>>;
// @public
export interface ChromeRecentlyAccessed {
// Warning: (ae-unresolved-link) The @link reference could not be resolved: No member was found with name "basePath"
add(link: string, label: string, id: string): void;
get$(): Observable<ChromeRecentlyAccessedHistoryItem[]>;
get(): ChromeRecentlyAccessedHistoryItem[];
}
// @public (undocumented)
export interface ChromeRecentlyAccessedHistoryItem {
// (undocumented)
id: string;
// (undocumented)
label: string;
// (undocumented)
link: string;
}
// @public (undocumented)
export interface ChromeStart {
addApplicationClass(className: string): void;
getApplicationClasses$(): Observable<string[]>;
getBadge$(): Observable<ChromeBadge | undefined>;
getBrand$(): Observable<ChromeBrand>;
getBreadcrumbs$(): Observable<ChromeBreadcrumb[]>;
getHelpExtension$(): Observable<ChromeHelpExtension | undefined>;
getIsCollapsed$(): Observable<boolean>;
getIsVisible$(): Observable<boolean>;
navControls: ChromeNavControls;
navLinks: ChromeNavLinks;
recentlyAccessed: ChromeRecentlyAccessed;
removeApplicationClass(className: string): void;
setBadge(badge?: ChromeBadge): void;
setBrand(brand: ChromeBrand): void;
setBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]): void;
setHelpExtension(helpExtension?: ChromeHelpExtension): void;
setIsCollapsed(isCollapsed: boolean): void;
setIsVisible(isVisible: boolean): void;
}
// @internal (undocumented)
export interface CoreContext {

View file

@ -26,3 +26,7 @@ const MutationObserver = require('mutation-observer');
Object.defineProperty(window, 'MutationObserver', { value: MutationObserver });
require('whatwg-fetch');
// TextEncoder + TextDecoder
require('fast-text-encoding');
window.crypto = require('@trust/webcrypto');

View file

@ -54,8 +54,6 @@ import { i18n } from '@kbn/i18n';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import chrome from 'ui/chrome';
import { HelpExtension } from 'ui/chrome';
import { RecentlyAccessedHistoryItem } from 'ui/persisted_log';
import { ChromeHeaderNavControlsRegistry } from 'ui/registry/chrome_header_nav_controls';
import { relativeToAbsolute } from 'ui/url/relative_to_absolute';
import { HeaderBadge } from './header_badge';
@ -63,22 +61,13 @@ import { HeaderBreadcrumbs } from './header_breadcrumbs';
import { HeaderHelpMenu } from './header_help_menu';
import { HeaderNavControls } from './header_nav_controls';
import { NavControlSide } from '../';
import { ChromeBadge, ChromeBreadcrumb, ChromeNavLink } from '../../../../../../../core/public';
interface Props {
appTitle?: string;
badge$: Rx.Observable<ChromeBadge | undefined>;
breadcrumbs$: Rx.Observable<ChromeBreadcrumb[]>;
homeHref: string;
isVisible: boolean;
navLinks$: Rx.Observable<ChromeNavLink[]>;
recentlyAccessed$: Rx.Observable<RecentlyAccessedHistoryItem[]>;
forceAppSwitcherNavigation$: Rx.Observable<boolean>;
helpExtension$: Rx.Observable<HelpExtension>;
navControls: ChromeHeaderNavControlsRegistry;
intl: InjectedIntl;
}
import {
ChromeBadge,
ChromeBreadcrumb,
ChromeNavLink,
ChromeRecentlyAccessedHistoryItem,
ChromeNavControl,
} from '../../../../../../../core/public';
// Providing a buffer between the limit and the cut off index
// protects from truncating just the last couple (6) characters
@ -87,7 +76,7 @@ const TRUNCATE_AT: number = 58;
function extendRecentlyAccessedHistoryItem(
navLinks: ChromeNavLink[],
recentlyAccessed: RecentlyAccessedHistoryItem
recentlyAccessed: ChromeRecentlyAccessedHistoryItem
) {
const href = relativeToAbsolute(chrome.addBasePath(recentlyAccessed.link));
const navLink = navLinks.find(nl => href.startsWith(nl.subUrlBase || nl.baseUrl));
@ -142,10 +131,28 @@ function truncateRecentItemLabel(label: string): string {
return label;
}
interface Props {
appTitle?: string;
badge$: Rx.Observable<ChromeBadge | undefined>;
breadcrumbs$: Rx.Observable<ChromeBreadcrumb[]>;
homeHref: string;
isVisible$: Rx.Observable<boolean>;
navLinks$: Rx.Observable<ChromeNavLink[]>;
recentlyAccessed$: Rx.Observable<ChromeRecentlyAccessedHistoryItem[]>;
forceAppSwitcherNavigation$: Rx.Observable<boolean>;
helpExtension$: Rx.Observable<HelpExtension>;
navControlsLeft$: Rx.Observable<ReadonlyArray<ChromeNavControl>>;
navControlsRight$: Rx.Observable<ReadonlyArray<ChromeNavControl>>;
intl: InjectedIntl;
}
interface State {
navLinks: Array<ReturnType<typeof extendNavLink>>;
recentlyAccessed: Array<ReturnType<typeof extendRecentlyAccessedHistoryItem>>;
isVisible: boolean;
navLinks: ReadonlyArray<ReturnType<typeof extendNavLink>>;
recentlyAccessed: ReadonlyArray<ReturnType<typeof extendRecentlyAccessedHistoryItem>>;
forceNavigation: boolean;
navControlsLeft: ReadonlyArray<ChromeNavControl>;
navControlsRight: ReadonlyArray<ChromeNavControl>;
}
class HeaderUI extends Component<Props, State> {
@ -156,25 +163,41 @@ class HeaderUI extends Component<Props, State> {
super(props);
this.state = {
isVisible: true,
navLinks: [],
recentlyAccessed: [],
forceNavigation: false,
navControlsLeft: [],
navControlsRight: [],
};
}
public componentDidMount() {
this.subscription = Rx.combineLatest(
this.props.isVisible$,
this.props.forceAppSwitcherNavigation$,
this.props.navLinks$,
this.props.recentlyAccessed$,
this.props.forceAppSwitcherNavigation$
this.props.navControlsLeft$,
this.props.navControlsRight$
).subscribe({
next: ([navLinks, recentlyAccessed, forceNavigation]) => {
next: ([
isVisible,
forceNavigation,
navLinks,
recentlyAccessed,
navControlsLeft,
navControlsRight,
]) => {
this.setState({
isVisible,
forceNavigation,
navLinks: navLinks.map(navLink => extendNavLink(navLink)),
recentlyAccessed: recentlyAccessed.map(ra =>
extendRecentlyAccessedHistoryItem(navLinks, ra)
),
navControlsLeft,
navControlsRight,
});
},
});
@ -214,24 +237,13 @@ class HeaderUI extends Component<Props, State> {
}
public render() {
const {
appTitle,
badge$,
breadcrumbs$,
isVisible,
navControls,
helpExtension$,
intl,
} = this.props;
const { navLinks, recentlyAccessed } = this.state;
const { appTitle, badge$, breadcrumbs$, helpExtension$, intl } = this.props;
const { isVisible, navLinks, recentlyAccessed, navControlsLeft, navControlsRight } = this.state;
if (!isVisible) {
return null;
}
const leftNavControls = navControls.bySide[NavControlSide.Left];
const rightNavControls = navControls.bySide[NavControlSide.Right];
const navLinksArray = navLinks
.filter(navLink => !navLink.hidden)
.map(navLink => ({
@ -289,7 +301,7 @@ class HeaderUI extends Component<Props, State> {
<EuiHeaderSectionItem border="right">{this.renderLogo()}</EuiHeaderSectionItem>
<HeaderNavControls navControls={leftNavControls} />
<HeaderNavControls side="left" navControls={navControlsLeft} />
</EuiHeaderSection>
<HeaderBreadcrumbs appTitle={appTitle} breadcrumbs$={breadcrumbs$} />
@ -301,7 +313,7 @@ class HeaderUI extends Component<Props, State> {
<HeaderHelpMenu helpExtension$={helpExtension$} />
</EuiHeaderSectionItem>
<HeaderNavControls navControls={rightNavControls} />
<HeaderNavControls side="right" navControls={navControlsRight} />
</EuiHeaderSection>
</EuiHeader>

View file

@ -24,11 +24,12 @@ import {
EuiHeaderSectionItem,
} from '@elastic/eui';
import { NavControl } from '../';
import { HeaderExtension } from './header_extension';
import { ChromeNavControl } from '../../../../../../../core/public';
interface Props {
navControls: NavControl[];
navControls: ReadonlyArray<ChromeNavControl>;
side: 'left' | 'right';
}
export class HeaderNavControls extends Component<Props> {
@ -42,12 +43,11 @@ export class HeaderNavControls extends Component<Props> {
return navControls.map(this.renderNavControl);
}
private renderNavControl = (navControl: NavControl) => (
<EuiHeaderSectionItem
key={navControl.name}
border={navControl.side === 'left' ? 'right' : 'left'}
>
<HeaderExtension extension={navControl.render} />
// It should be performant to use the index as the key since these are unlikely
// to change while Kibana is running.
private renderNavControl = (navControl: ChromeNavControl, index: number) => (
<EuiHeaderSectionItem key={index} border={this.props.side === 'left' ? 'right' : 'left'}>
<HeaderExtension extension={navControl.mount} />
</EuiHeaderSectionItem>
);
}

View file

@ -23,30 +23,44 @@ import { Header } from './components/header';
import { wrapInI18nContext } from 'ui/i18n';
import { chromeHeaderNavControlsRegistry } from 'ui/registry/chrome_header_nav_controls';
import { npStart } from '../../../new_platform';
import { NavControlSide } from '.';
const module = uiModules.get('kibana');
module.directive('headerGlobalNav', (reactDirective, chrome, Private, uiCapabilities) => {
const { recentlyAccessed } = require('ui/persisted_log');
module.directive('headerGlobalNav', (reactDirective, Private) => {
const newPlatform = npStart.core;
// Continue to support legacy nav controls not registered with the NP.
// NOTE: in future change this needs to be moved out of this directive.
const navControls = Private(chromeHeaderNavControlsRegistry);
const homeHref = chrome.addBasePath('/app/kibana#/home');
(navControls.bySide[NavControlSide.Left] || [])
.forEach(navControl => newPlatform.chrome.navControls.registerLeft({
order: navControl.order,
mount: navControl.render,
}));
(navControls.bySide[NavControlSide.Right] || [])
.forEach(navControl => newPlatform.chrome.navControls.registerRight({
order: navControl.order,
mount: navControl.render,
}));
return reactDirective(wrapInI18nContext(Header), [
// scope accepted by directive, passed in as React props
'appTitle',
'isVisible',
],
{},
// angular injected React props
{
badge$: chrome.badge.get$(),
breadcrumbs$: chrome.breadcrumbs.get$(),
helpExtension$: chrome.helpExtension.get$(),
navLinks$: npStart.core.chrome.navLinks.getNavLinks$(),
recentlyAccessed$: recentlyAccessed.get$(),
forceAppSwitcherNavigation$: npStart.core.chrome.navLinks.getForceAppSwitcherNavigation$(),
navControls,
homeHref,
uiCapabilities,
isVisible$: newPlatform.chrome.getIsVisible$(),
badge$: newPlatform.chrome.getBadge$(),
breadcrumbs$: newPlatform.chrome.getBreadcrumbs$(),
helpExtension$: newPlatform.chrome.getHelpExtension$(),
navLinks$: newPlatform.chrome.navLinks.getNavLinks$(),
forceAppSwitcherNavigation$: newPlatform.chrome.navLinks.getForceAppSwitcherNavigation$(),
homeHref: newPlatform.http.basePath.prepend('/app/kibana#/home'),
uiCapabilities: newPlatform.application.capabilities,
recentlyAccessed$: newPlatform.chrome.recentlyAccessed.get$(),
navControlsLeft$: newPlatform.chrome.navControls.getLeft$(),
navControlsRight$: newPlatform.chrome.navControls.getRight$(),
});
});

View file

@ -3,7 +3,6 @@
<header-global-nav
class="header-global-wrapper hide-for-sharing"
is-visible="chrome.getVisible()"
app-title="chrome.getAppTitle()"
data-test-subj="headerGlobalNav"
></header-global-nav>

View file

@ -17,6 +17,8 @@
* under the License.
*/
import './change_time_filter.test.mocks';
jest.mock('ui/chrome',
() => ({
getBasePath: () => `/some/base/path`,

View file

@ -0,0 +1,28 @@
/*
* 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 { chromeServiceMock } from '../../../../../../core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -21,4 +21,4 @@
import './directive';
export { PersistedLog } from './persisted_log';
export { recentlyAccessed, RecentlyAccessedHistoryItem } from './recently_accessed';
export { recentlyAccessed } from './recently_accessed';

View file

@ -16,45 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import chrome from 'ui/chrome';
import { PersistedLog } from './';
import { createLogKey } from './create_log_key';
export interface RecentlyAccessedHistoryItem {
link: string;
label: string;
id: string;
}
import { npStart } from '../new_platform';
class RecentlyAccessed {
public history: PersistedLog<RecentlyAccessedHistoryItem>;
constructor() {
const logKey = createLogKey('recentlyAccessed', chrome.getBasePath());
this.history = new PersistedLog(logKey, {
maxLength: 20,
filterDuplicates: true,
isDuplicate: (oldItem, newItem) => {
return oldItem.id === newItem.id;
},
});
}
public add(link: string, label: string, id: string) {
this.history.add({
link,
label,
id,
});
}
public get() {
return this.history.get();
}
public get$() {
return this.history.get$();
}
}
export const recentlyAccessed = new RecentlyAccessed();
export const recentlyAccessed = npStart.core.chrome.recentlyAccessed;

View file

@ -17,6 +17,8 @@
* under the License.
*/
import './timefilter.test.mocks';
jest.mock('ui/chrome',
() => ({
getBasePath: () => `/some/base/path`,

View file

@ -0,0 +1,28 @@
/*
* 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 { chromeServiceMock } from '../../../../core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -17,6 +17,8 @@
* under the License.
*/
import './brush_event.test.mocks';
jest.mock('ui/chrome',
() => ({
getBasePath: () => `/some/base/path`,

View file

@ -0,0 +1,28 @@
/*
* 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 { chromeServiceMock } from '../../../../../core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -6,6 +6,7 @@
import jobConfig from '../../../../common/types/__mocks__/job_config_farequote';
import mockAnnotations from './__mocks__/mock_annotations.json';
import './annotations_table.test.mocks';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -7,6 +7,7 @@
import { shallow } from 'enzyme';
import React from 'react';
import './job_list.test.mocks';
import { DataFrameJobList } from './job_list';
describe('Data Frame: Job List <DataFrameJobList />', () => {

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -7,6 +7,7 @@
import { shallow } from 'enzyme';
import React from 'react';
import './page.test.mocks';
import { Page } from './page';
describe('Data Frame: Job List <Page />', () => {

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './explorer_chart_distribution.test.mocks';
import { chartData as mockChartData } from './__mocks__/mock_chart_data_rare';
import seriesConfig from './__mocks__/mock_series_config_rare.json';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './explorer_chart_single_metric.test.mocks';
import { chartData as mockChartData } from './__mocks__/mock_chart_data';
import seriesConfig from './__mocks__/mock_series_config_filebeat.json';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './explorer_chart_single_metric.test.mocks';
import { chartData } from './__mocks__/mock_chart_data';
import seriesConfig from './__mocks__/mock_series_config_filebeat.json';
import seriesConfigRare from './__mocks__/mock_series_config_rare.json';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './explorer_charts_container_service.test.mocks';
import _ from 'lodash';
import mockAnomalyChartRecords from './__mocks__/mock_anomaly_chart_records.json';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './explorer_swimlane.test.mocks';
import mockOverallSwimlaneData from './__mocks__/mock_overall_swimlane.json';
import moment from 'moment-timezone';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -6,6 +6,7 @@
//import mockOverallSwimlaneData from './__mocks__/mock_overall_swimlane.json';
import './timeseries_chart.test.mocks';
import moment from 'moment-timezone';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './chart_utils.test.mocks';
import seriesConfig from '../explorer/explorer_charts/__mocks__/mock_series_config_filebeat';
// A copy of these mocks for ui/chrome and ui/timefilter are also

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { chromeServiceMock } from '../../../../../../src/core/public/mocks';
jest.doMock('ui/new_platform', () => ({
npStart: {
core: {
chrome: chromeServiceMock.createStartContract(),
},
},
}));

View file

@ -3173,6 +3173,26 @@
"@svgr/plugin-svgo" "^4.2.0"
loader-utils "^1.2.3"
"@trust/keyto@^0.3.4":
version "0.3.7"
resolved "https://registry.yarnpkg.com/@trust/keyto/-/keyto-0.3.7.tgz#e251264e302a7a6be64a3e208dacb2ef6268c946"
integrity sha512-t5kWWCTkPgg24JWVuCTPMx7l13F7YHdxBeJkT1vmoHjROgiOIEAN8eeY+iRmP1Hwsx+S7U55HyuqSsECr08a8A==
dependencies:
asn1.js "^5.0.1"
base64url "^3.0.1"
elliptic "^6.4.1"
"@trust/webcrypto@^0.9.2":
version "0.9.2"
resolved "https://registry.yarnpkg.com/@trust/webcrypto/-/webcrypto-0.9.2.tgz#c699d4c026a4446b04faa54d5389a81888ba713c"
integrity sha512-5iMAVcGYKhqLJGjefB1nzuQSqUJTru0nG4CytpBT/GGp1Piz/MVnj2jORdYf4JBYzggCIa8WZUr2rchP2Ngn/w==
dependencies:
"@trust/keyto" "^0.3.4"
base64url "^3.0.0"
elliptic "^6.4.0"
node-rsa "^0.4.0"
text-encoding "^0.6.1"
"@turf/bbox@6.x":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@turf/bbox/-/bbox-6.0.1.tgz#b966075771475940ee1c16be2a12cf389e6e923a"
@ -5601,6 +5621,20 @@ asn1.js@^4.0.0:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
asn1.js@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.0.1.tgz#7668b56416953f0ce3421adbb3893ace59c96f59"
integrity sha512-aO8EaEgbgqq77IEw+1jfx5c9zTbzvkfuRBuZsSsPnTHMkmd5AI4J6OtITLZFa381jReeaQL67J0GBTUu0+ZTVw==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
asn1@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
@ -6485,7 +6519,7 @@ base64id@1.0.0:
resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
integrity sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=
base64url@^3.0.0:
base64url@^3.0.0, base64url@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
@ -10532,6 +10566,19 @@ elliptic@^6.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
elliptic@^6.4.0, elliptic@^6.4.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a"
integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
hash.js "^1.0.0"
hmac-drbg "^1.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
emoji-regex@^7.0.1, emoji-regex@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
@ -11925,6 +11972,11 @@ fast-safe-stringify@^2.0.4:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2"
integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==
fast-text-encoding@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz#3e5ce8293409cfaa7177a71b9ca84e1b1e6f25ef"
integrity sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==
fastparse@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
@ -19562,6 +19614,13 @@ node-releases@^1.1.3:
dependencies:
semver "^5.3.0"
node-rsa@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-0.4.2.tgz#d6391729ec16a830ed5a38042b3157d2d5d72530"
integrity sha1-1jkXKewWqDDtWjgEKzFX0tXXJTA=
dependencies:
asn1 "0.2.3"
node-sass@^4.9.4:
version "4.9.4"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.4.tgz#349bd7f1c89422ffe7e1e4b60f2055a69fbc5512"
@ -26227,7 +26286,7 @@ test-exclude@^5.0.0:
read-pkg-up "^4.0.0"
require-main-filename "^1.0.1"
text-encoding@^0.6.4:
text-encoding@^0.6.1, text-encoding@^0.6.4:
version "0.6.4"
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
integrity sha1-45mpgiV6J22uQou5KEXLcb3CbRk=