Improve and cleanup chrome helpMenu links (#82300) (#85698)

* Improve and cleanup chrome helpMenu links

* update doc due to merge

* remove dev dependencies from test plugin

* update generated doc after merge

* update generated doc

* generated doc

* generated doc
This commit is contained in:
Pierre Gayvallet 2020-12-11 14:23:29 +01:00 committed by GitHub
parent 306bed8b3c
commit 17722f711a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 737 additions and 1089 deletions

View file

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionLinkBase](./kibana-plugin-core-public.chromehelpextensionlinkbase.md)
## ChromeHelpExtensionLinkBase type
<b>Signature:</b>
```typescript
export declare type ChromeHelpExtensionLinkBase = Pick<EuiButtonEmptyProps, 'iconType' | 'target' | 'rel' | 'data-test-subj'>;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.md) &gt; [content](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.content.md)
## ChromeHelpExtensionMenuCustomLink.content property
Content of the button (in lieu of `children`<!-- -->)
<b>Signature:</b>
```typescript
content: React.ReactNode;
```

View file

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

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.md) &gt; [linkType](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.linktype.md)
## ChromeHelpExtensionMenuCustomLink.linkType property
Extend EuiButtonEmpty to provide extra functionality
<b>Signature:</b>
```typescript
linkType: 'custom';
```

View file

@ -2,14 +2,20 @@
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.md)
## ChromeHelpExtensionMenuCustomLink type
## ChromeHelpExtensionMenuCustomLink interface
<b>Signature:</b>
```typescript
export declare type ChromeHelpExtensionMenuCustomLink = EuiButtonEmptyProps & {
linkType: 'custom';
content: React.ReactNode;
};
export interface ChromeHelpExtensionMenuCustomLink extends ChromeHelpExtensionLinkBase
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [content](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.content.md) | <code>React.ReactNode</code> | Content of the button (in lieu of <code>children</code>) |
| [href](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.href.md) | <code>string</code> | URL of the link |
| [linkType](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.linktype.md) | <code>'custom'</code> | Extend EuiButtonEmpty to provide extra functionality |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.md) &gt; [href](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.href.md)
## ChromeHelpExtensionMenuDiscussLink.href property
URL to discuss page. i.e. `https://discuss.elastic.co/c/${appName}`
<b>Signature:</b>
```typescript
href: string;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.md) &gt; [linkType](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.linktype.md)
## ChromeHelpExtensionMenuDiscussLink.linkType property
Creates a generic give feedback link with comment icon
<b>Signature:</b>
```typescript
linkType: 'discuss';
```

View file

@ -2,14 +2,19 @@
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.md)
## ChromeHelpExtensionMenuDiscussLink type
## ChromeHelpExtensionMenuDiscussLink interface
<b>Signature:</b>
```typescript
export declare type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & {
linkType: 'discuss';
href: string;
};
export interface ChromeHelpExtensionMenuDiscussLink extends ChromeHelpExtensionLinkBase
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [href](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.href.md) | <code>string</code> | URL to discuss page. i.e. <code>https://discuss.elastic.co/c/${appName}</code> |
| [linkType](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.linktype.md) | <code>'discuss'</code> | Creates a generic give feedback link with comment icon |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.md) &gt; [href](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.href.md)
## ChromeHelpExtensionMenuDocumentationLink.href property
URL to documentation page. i.e. `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/${appName}.html`<!-- -->,
<b>Signature:</b>
```typescript
href: string;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.md) &gt; [linkType](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.linktype.md)
## ChromeHelpExtensionMenuDocumentationLink.linkType property
Creates a deep-link to app-specific documentation
<b>Signature:</b>
```typescript
linkType: 'documentation';
```

View file

@ -2,14 +2,19 @@
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.md)
## ChromeHelpExtensionMenuDocumentationLink type
## ChromeHelpExtensionMenuDocumentationLink interface
<b>Signature:</b>
```typescript
export declare type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & {
linkType: 'documentation';
href: string;
};
export interface ChromeHelpExtensionMenuDocumentationLink extends ChromeHelpExtensionLinkBase
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [href](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.href.md) | <code>string</code> | URL to documentation page. i.e. <code>${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/${appName}.html</code>, |
| [linkType](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.linktype.md) | <code>'documentation'</code> | Creates a deep-link to app-specific documentation |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md) &gt; [labels](./kibana-plugin-core-public.chromehelpextensionmenugithublink.labels.md)
## ChromeHelpExtensionMenuGitHubLink.labels property
Include at least one app-specific label to be applied to the new github issue
<b>Signature:</b>
```typescript
labels: string[];
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md) &gt; [linkType](./kibana-plugin-core-public.chromehelpextensionmenugithublink.linktype.md)
## ChromeHelpExtensionMenuGitHubLink.linkType property
Creates a link to a new github issue in the Kibana repo
<b>Signature:</b>
```typescript
linkType: 'github';
```

View file

@ -2,15 +2,20 @@
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md)
## ChromeHelpExtensionMenuGitHubLink type
## ChromeHelpExtensionMenuGitHubLink interface
<b>Signature:</b>
```typescript
export declare type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & {
linkType: 'github';
labels: string[];
title?: string;
};
export interface ChromeHelpExtensionMenuGitHubLink extends ChromeHelpExtensionLinkBase
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [labels](./kibana-plugin-core-public.chromehelpextensionmenugithublink.labels.md) | <code>string[]</code> | Include at least one app-specific label to be applied to the new github issue |
| [linkType](./kibana-plugin-core-public.chromehelpextensionmenugithublink.linktype.md) | <code>'github'</code> | Creates a link to a new github issue in the Kibana repo |
| [title](./kibana-plugin-core-public.chromehelpextensionmenugithublink.title.md) | <code>string</code> | Provides initial text for the title of the issue |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md) &gt; [title](./kibana-plugin-core-public.chromehelpextensionmenugithublink.title.md)
## ChromeHelpExtensionMenuGitHubLink.title property
Provides initial text for the title of the issue
<b>Signature:</b>
```typescript
title?: string;
```

View file

@ -8,5 +8,5 @@
<b>Signature:</b>
```typescript
export declare type ChromeHelpExtensionMenuLink = ExclusiveUnion<ChromeHelpExtensionMenuGitHubLink, ExclusiveUnion<ChromeHelpExtensionMenuDiscussLink, ExclusiveUnion<ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuCustomLink>>>;
export declare type ChromeHelpExtensionMenuLink = ChromeHelpExtensionMenuGitHubLink | ChromeHelpExtensionMenuDiscussLink | ChromeHelpExtensionMenuDocumentationLink | ChromeHelpExtensionMenuCustomLink;
```

View file

@ -4,7 +4,7 @@
## IExternalUrlPolicy.host property
Optional host describing the external destination. May be combined with `protocol`<!-- -->. Required if `protocol` is not defined.
Optional host describing the external destination. May be combined with `protocol`<!-- -->.
<b>Signature:</b>

View file

@ -17,6 +17,6 @@ export interface IExternalUrlPolicy
| Property | Type | Description |
| --- | --- | --- |
| [allow](./kibana-plugin-core-public.iexternalurlpolicy.allow.md) | <code>boolean</code> | Indicates if this policy allows or denies access to the described destination. |
| [host](./kibana-plugin-core-public.iexternalurlpolicy.host.md) | <code>string</code> | Optional host describing the external destination. May be combined with <code>protocol</code>. Required if <code>protocol</code> is not defined. |
| [protocol](./kibana-plugin-core-public.iexternalurlpolicy.protocol.md) | <code>string</code> | Optional protocol describing the external destination. May be combined with <code>host</code>. Required if <code>host</code> is not defined. |
| [host](./kibana-plugin-core-public.iexternalurlpolicy.host.md) | <code>string</code> | Optional host describing the external destination. May be combined with <code>protocol</code>. |
| [protocol](./kibana-plugin-core-public.iexternalurlpolicy.protocol.md) | <code>string</code> | Optional protocol describing the external destination. May be combined with <code>host</code>. |

View file

@ -4,7 +4,7 @@
## IExternalUrlPolicy.protocol property
Optional protocol describing the external destination. May be combined with `host`<!-- -->. Required if `host` is not defined.
Optional protocol describing the external destination. May be combined with `host`<!-- -->.
<b>Signature:</b>

View file

@ -44,6 +44,10 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ChromeBrand](./kibana-plugin-core-public.chromebrand.md) | |
| [ChromeDocTitle](./kibana-plugin-core-public.chromedoctitle.md) | APIs for accessing and updating the document title. |
| [ChromeHelpExtension](./kibana-plugin-core-public.chromehelpextension.md) | |
| [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.md) | |
| [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.md) | |
| [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.md) | |
| [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md) | |
| [ChromeNavControl](./kibana-plugin-core-public.chromenavcontrol.md) | |
| [ChromeNavControls](./kibana-plugin-core-public.chromenavcontrols.md) | [APIs](./kibana-plugin-core-public.chromenavcontrols.md) for registering new controls to be displayed in the navigation bar. |
| [ChromeNavLink](./kibana-plugin-core-public.chromenavlink.md) | |
@ -145,10 +149,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [AppUpdatableFields](./kibana-plugin-core-public.appupdatablefields.md) | Defines the list of fields that can be updated via an [AppUpdater](./kibana-plugin-core-public.appupdater.md)<!-- -->. |
| [AppUpdater](./kibana-plugin-core-public.appupdater.md) | Updater for applications. see [ApplicationSetup](./kibana-plugin-core-public.applicationsetup.md) |
| [ChromeBreadcrumb](./kibana-plugin-core-public.chromebreadcrumb.md) | |
| [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-core-public.chromehelpextensionmenucustomlink.md) | |
| [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-core-public.chromehelpextensionmenudiscusslink.md) | |
| [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-core-public.chromehelpextensionmenudocumentationlink.md) | |
| [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md) | |
| [ChromeHelpExtensionLinkBase](./kibana-plugin-core-public.chromehelpextensionlinkbase.md) | |
| [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | |
| [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) | |
| [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. |

View file

@ -4,7 +4,7 @@
## Embeddable.getUpdated$() method
Merges input$ and output$ streams and denounces emit till next macro-task Could be useful to batch reactions to input$ and output$ updates that happen separately but synchronously In case corresponding state change triggered `reload` this stream is guarantied to emit later which allows to skip any state handling in case `reload` already handled it
Merges input$ and output$ streams and debounces emit till next macro-task. Could be useful to batch reactions to input$ and output$ updates that happen separately but synchronously. In case corresponding state change triggered `reload` this stream is guarantied to emit later, which allows to skip any state handling in case `reload` already handled it.
<b>Signature:</b>

View file

@ -44,7 +44,7 @@ export declare abstract class Embeddable<TEmbeddableInput extends EmbeddableInpu
| [getOutput$()](./kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md) | | |
| [getRoot()](./kibana-plugin-plugins-embeddable-public.embeddable.getroot.md) | | Returns the top most parent embeddable, or itself if this embeddable is not within a parent. |
| [getTitle()](./kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md) | | |
| [getUpdated$()](./kibana-plugin-plugins-embeddable-public.embeddable.getupdated_.md) | | Merges input$ and output$ streams and denounces emit till next macro-task Could be useful to batch reactions to input$ and output$ updates that happen separately but synchronously In case corresponding state change triggered <code>reload</code> this stream is guarantied to emit later which allows to skip any state handling in case <code>reload</code> already handled it |
| [getUpdated$()](./kibana-plugin-plugins-embeddable-public.embeddable.getupdated_.md) | | Merges input$ and output$ streams and debounces emit till next macro-task. Could be useful to batch reactions to input$ and output$ updates that happen separately but synchronously. In case corresponding state change triggered <code>reload</code> this stream is guarantied to emit later, which allows to skip any state handling in case <code>reload</code> already handled it. |
| [onFatalError(e)](./kibana-plugin-plugins-embeddable-public.embeddable.onfatalerror.md) | | |
| [reload()](./kibana-plugin-plugins-embeddable-public.embeddable.reload.md) | | Reload will be called when there is a request to refresh the data or view, even if the input data did not change.<!-- -->In case if input data did change and reload is requested input$ and output$ would still emit before <code>reload</code> is called<!-- -->The order would be as follows: input$ output$ reload() \-\-\-- updated$ |
| [render(el)](./kibana-plugin-plugins-embeddable-public.embeddable.render.md) | | |

View file

@ -27,6 +27,7 @@ export {
ChromeHelpExtension,
} from './chrome_service';
export {
ChromeHelpExtensionLinkBase,
ChromeHelpExtensionMenuLink,
ChromeHelpExtensionMenuCustomLink,
ChromeHelpExtensionMenuDiscussLink,

View file

@ -2041,7 +2041,7 @@ exports[`Header renders 1`] = `
}
/>
</EuiHideFor>,
<InjectIntl(HeaderHelpMenuUI)
<HeaderHelpMenu
helpExtension$={
BehaviorSubject {
"_isScalar": false,
@ -2742,7 +2742,7 @@ exports[`Header renders 1`] = `
}
kibanaDocLink="/docs"
kibanaVersion="1.0.0"
useDefaultContent={true}
navigateToUrl={[MockFunction]}
/>,
<HeaderNavControls
navControls$={
@ -3225,7 +3225,7 @@ exports[`Header renders 1`] = `
<div
className="euiHeaderSectionItem"
>
<InjectIntl(HeaderHelpMenuUI)
<HeaderHelpMenu
helpExtension$={
BehaviorSubject {
"_isScalar": false,
@ -3926,885 +3926,79 @@ exports[`Header renders 1`] = `
}
kibanaDocLink="/docs"
kibanaVersion="1.0.0"
useDefaultContent={true}
navigateToUrl={[MockFunction]}
>
<HeaderHelpMenuUI
helpExtension$={
BehaviorSubject {
"_isScalar": false,
"_value": undefined,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
InnerSubscriber {
"_parentOrParents": CombineLatestSubscriber {
"_parentOrParents": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"_subscriptions": Array [
[Circular],
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": BehaviorSubject {
"_isScalar": false,
"_value": "",
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
[Circular],
],
"thrownError": null,
},
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 1,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"active": 2,
"closed": false,
"destination": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": true,
"observables": Array [
[Circular],
BehaviorSubject {
"_isScalar": false,
"_value": "",
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 1,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
},
],
"resultSelector": undefined,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
"toRespond": 0,
"values": Array [
undefined,
"",
],
},
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 0,
"outerValue": undefined,
"parent": CombineLatestSubscriber {
"_parentOrParents": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"_subscriptions": Array [
[Circular],
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": BehaviorSubject {
"_isScalar": false,
"_value": "",
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
[Circular],
],
"thrownError": null,
},
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 1,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"active": 2,
"closed": false,
"destination": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": true,
"observables": Array [
[Circular],
BehaviorSubject {
"_isScalar": false,
"_value": "",
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 1,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
},
],
"resultSelector": undefined,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
"toRespond": 0,
"values": Array [
undefined,
"",
],
},
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
}
<EuiPopover
anchorPosition="downRight"
button={
<EuiHeaderSectionItemButton
aria-expanded={false}
aria-haspopup="true"
aria-label="Help menu"
onClick={[Function]}
>
<EuiIcon
size="m"
type="help"
/>
</EuiHeaderSectionItemButton>
}
helpSupportUrl$={
BehaviorSubject {
"_isScalar": false,
"_value": "",
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
InnerSubscriber {
"_parentOrParents": CombineLatestSubscriber {
"_parentOrParents": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"_subscriptions": Array [
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": BehaviorSubject {
"_isScalar": false,
"_value": undefined,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
[Circular],
],
"thrownError": null,
},
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 0,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
[Circular],
],
"active": 2,
"closed": false,
"destination": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": true,
"observables": Array [
BehaviorSubject {
"_isScalar": false,
"_value": undefined,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 0,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
},
[Circular],
],
"resultSelector": undefined,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
"toRespond": 0,
"values": Array [
undefined,
"",
],
},
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 1,
"outerValue": undefined,
"parent": CombineLatestSubscriber {
"_parentOrParents": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"_subscriptions": Array [
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": BehaviorSubject {
"_isScalar": false,
"_value": undefined,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
[Circular],
],
"thrownError": null,
},
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 0,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
[Circular],
],
"active": 2,
"closed": false,
"destination": Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
[Circular],
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": true,
"observables": Array [
BehaviorSubject {
"_isScalar": false,
"_value": undefined,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
InnerSubscriber {
"_parentOrParents": [Circular],
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"index": 1,
"isStopped": false,
"outerIndex": 0,
"outerValue": undefined,
"parent": [Circular],
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
},
[Circular],
],
"resultSelector": undefined,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
"toRespond": 0,
"values": Array [
undefined,
"",
],
},
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
}
}
intl={
Object {
"defaultFormats": Object {},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"relative": Object {
"days": Object {
"units": "day",
},
"hours": Object {
"units": "hour",
},
"minutes": Object {
"units": "minute",
},
"months": Object {
"units": "month",
},
"seconds": Object {
"units": "second",
},
"years": Object {
"units": "year",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": Symbol(react.fragment),
"timeZone": null,
}
}
kibanaDocLink="/docs"
kibanaVersion="1.0.0"
useDefaultContent={true}
closePopover={[Function]}
data-test-subj="helpMenuButton"
display="inlineBlock"
hasArrow={true}
id="headerHelpMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="m"
repositionOnScroll={true}
>
<EuiPopover
anchorPosition="downRight"
button={
<EuiHeaderSectionItemButton
aria-expanded={false}
aria-haspopup="true"
aria-label="Help menu"
onClick={[Function]}
>
<EuiIcon
size="m"
type="help"
/>
</EuiHeaderSectionItemButton>
}
closePopover={[Function]}
data-test-subj="helpMenuButton"
display="inlineBlock"
hasArrow={true}
id="headerHelpMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="m"
repositionOnScroll={true}
<EuiOutsideClickDetector
onOutsideClick={[Function]}
>
<EuiOutsideClickDetector
onOutsideClick={[Function]}
<div
className="euiPopover euiPopover--anchorDownRight"
data-test-subj="helpMenuButton"
id="headerHelpMenu"
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
>
<div
className="euiPopover euiPopover--anchorDownRight"
data-test-subj="helpMenuButton"
id="headerHelpMenu"
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
className="euiPopover__anchor"
>
<div
className="euiPopover__anchor"
<EuiHeaderSectionItemButton
aria-expanded={false}
aria-haspopup="true"
aria-label="Help menu"
onClick={[Function]}
>
<EuiHeaderSectionItemButton
<button
aria-expanded={false}
aria-haspopup="true"
aria-label="Help menu"
className="euiHeaderSectionItem__button"
onClick={[Function]}
type="button"
>
<button
aria-expanded={false}
aria-haspopup="true"
aria-label="Help menu"
className="euiHeaderSectionItem__button"
onClick={[Function]}
type="button"
<EuiIcon
size="m"
type="help"
>
<EuiIcon
<span
data-euiicon-type="help"
size="m"
type="help"
>
<span
data-euiicon-type="help"
size="m"
/>
</EuiIcon>
</button>
</EuiHeaderSectionItemButton>
</div>
/>
</EuiIcon>
</button>
</EuiHeaderSectionItemButton>
</div>
</EuiOutsideClickDetector>
</EuiPopover>
</HeaderHelpMenuUI>
</InjectIntl(HeaderHelpMenuUI)>
</div>
</EuiOutsideClickDetector>
</EuiPopover>
</HeaderHelpMenu>
</div>
</EuiHeaderSectionItem>
<EuiHeaderSectionItem

View file

@ -138,6 +138,7 @@ export function Header({
helpSupportUrl$={observables.helpSupportUrl$}
kibanaDocLink={kibanaDocLink}
kibanaVersion={kibanaVersion}
navigateToUrl={application.navigateToUrl}
/>,
<HeaderNavControls navControls$={observables.navControlsRight$} />,
],

View file

@ -17,10 +17,10 @@
* under the License.
*/
import * as Rx from 'rxjs';
import React, { Component, Fragment } from 'react';
import { combineLatest, Observable, Subscription } from 'rxjs';
import { i18n } from '@kbn/i18n';
import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiButtonEmpty,
EuiButtonEmptyProps,
@ -35,14 +35,20 @@ import {
EuiHorizontalRule,
} from '@elastic/eui';
import { ExclusiveUnion } from '@elastic/eui';
import { combineLatest } from 'rxjs';
import { HeaderExtension } from './header_extension';
import { ChromeHelpExtension } from '../../chrome_service';
import { InternalApplicationStart } from '../../../application';
import { GITHUB_CREATE_ISSUE_LINK, KIBANA_FEEDBACK_LINK } from '../../constants';
import { ChromeHelpExtension } from '../../chrome_service';
import { HeaderExtension } from './header_extension';
import { isModifiedOrPrevented } from './nav_link';
/** @public */
export type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & {
export type ChromeHelpExtensionLinkBase = Pick<
EuiButtonEmptyProps,
'iconType' | 'target' | 'rel' | 'data-test-subj'
>;
/** @public */
export interface ChromeHelpExtensionMenuGitHubLink extends ChromeHelpExtensionLinkBase {
/**
* Creates a link to a new github issue in the Kibana repo
*/
@ -55,10 +61,10 @@ export type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & {
* Provides initial text for the title of the issue
*/
title?: string;
};
}
/** @public */
export type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & {
export interface ChromeHelpExtensionMenuDiscussLink extends ChromeHelpExtensionLinkBase {
/**
* Creates a generic give feedback link with comment icon
*/
@ -68,10 +74,10 @@ export type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & {
* i.e. `https://discuss.elastic.co/c/${appName}`
*/
href: string;
};
}
/** @public */
export type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & {
export interface ChromeHelpExtensionMenuDocumentationLink extends ChromeHelpExtensionLinkBase {
/**
* Creates a deep-link to app-specific documentation
*/
@ -81,35 +87,36 @@ export type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & {
* i.e. `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/${appName}.html`,
*/
href: string;
};
}
/** @public */
export type ChromeHelpExtensionMenuCustomLink = EuiButtonEmptyProps & {
export interface ChromeHelpExtensionMenuCustomLink extends ChromeHelpExtensionLinkBase {
/**
* Extend EuiButtonEmpty to provide extra functionality
*/
linkType: 'custom';
/**
* URL of the link
*/
href: string;
/**
* Content of the button (in lieu of `children`)
*/
content: React.ReactNode;
};
}
/** @public */
export type ChromeHelpExtensionMenuLink = ExclusiveUnion<
ChromeHelpExtensionMenuGitHubLink,
ExclusiveUnion<
ChromeHelpExtensionMenuDiscussLink,
ExclusiveUnion<ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuCustomLink>
>
>;
export type ChromeHelpExtensionMenuLink =
| ChromeHelpExtensionMenuGitHubLink
| ChromeHelpExtensionMenuDiscussLink
| ChromeHelpExtensionMenuDocumentationLink
| ChromeHelpExtensionMenuCustomLink;
interface Props {
helpExtension$: Rx.Observable<ChromeHelpExtension | undefined>;
helpSupportUrl$: Rx.Observable<string>;
intl: InjectedIntl;
navigateToUrl: InternalApplicationStart['navigateToUrl'];
helpExtension$: Observable<ChromeHelpExtension | undefined>;
helpSupportUrl$: Observable<string>;
kibanaVersion: string;
useDefaultContent?: boolean;
kibanaDocLink: string;
}
@ -119,8 +126,8 @@ interface State {
helpSupportUrl: string;
}
class HeaderHelpMenuUI extends Component<Props, State> {
private subscription?: Rx.Subscription;
export class HeaderHelpMenu extends Component<Props, State> {
private subscription?: Subscription;
constructor(props: Props) {
super(props);
@ -151,158 +158,17 @@ class HeaderHelpMenuUI extends Component<Props, State> {
}
}
createGithubUrl = (labels: string[], title?: string) => {
const url = new URL('https://github.com/elastic/kibana/issues/new?');
if (labels.length) {
url.searchParams.set('labels', labels.join(','));
}
if (title) {
url.searchParams.set('title', title);
}
return url.toString();
};
createCustomLink = (
index: number,
text: React.ReactNode,
addSpacer?: boolean,
buttonProps?: EuiButtonEmptyProps
) => {
return (
<Fragment key={`helpButton${index}`}>
<EuiButtonEmpty {...buttonProps} size="xs" flush="left">
{text}
</EuiButtonEmpty>
{addSpacer && <EuiSpacer size="xs" />}
</Fragment>
);
};
public render() {
const { intl, kibanaVersion, useDefaultContent, kibanaDocLink } = this.props;
const { helpExtension, helpSupportUrl } = this.state;
const { kibanaVersion } = this.props;
const defaultContent = useDefaultContent ? (
<Fragment>
<EuiButtonEmpty href={kibanaDocLink} target="_blank" size="xs" flush="left">
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuKibanaDocumentationTitle"
defaultMessage="Kibana documentation"
/>
</EuiButtonEmpty>
<EuiSpacer size="xs" />
<EuiButtonEmpty href={helpSupportUrl} target="_blank" size="xs" flush="left">
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuAskElasticTitle"
defaultMessage="Ask Elastic"
/>
</EuiButtonEmpty>
<EuiSpacer size="xs" />
<EuiButtonEmpty href={KIBANA_FEEDBACK_LINK} target="_blank" size="xs" flush="left">
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuGiveFeedbackTitle"
defaultMessage="Give feedback"
/>
</EuiButtonEmpty>
<EuiSpacer size="xs" />
<EuiButtonEmpty
href={GITHUB_CREATE_ISSUE_LINK}
target="_blank"
size="xs"
iconType="logoGithub"
flush="left"
>
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuOpenGitHubIssueTitle"
defaultMessage="Open an issue in GitHub"
/>
</EuiButtonEmpty>
</Fragment>
) : null;
let customContent;
if (helpExtension) {
const { appName, links, content } = helpExtension;
const getFeedbackText = () =>
i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuGiveFeedbackOnApp', {
defaultMessage: 'Give feedback on {appName}',
values: { appName: helpExtension.appName },
});
const customLinks =
links &&
links.map((link, index) => {
const { linkType, title, labels = [], content: text, ...rest } = link;
switch (linkType) {
case 'documentation':
return this.createCustomLink(
index,
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuDocumentation"
defaultMessage="Documentation"
/>,
index < links.length - 1,
{
target: '_blank',
rel: 'noopener',
...rest,
}
);
case 'github':
return this.createCustomLink(index, getFeedbackText(), index < links.length - 1, {
iconType: 'logoGithub',
href: this.createGithubUrl(labels, title),
target: '_blank',
rel: 'noopener',
...rest,
});
case 'discuss':
return this.createCustomLink(index, getFeedbackText(), index < links.length - 1, {
iconType: 'editorComment',
target: '_blank',
rel: 'noopener',
...rest,
});
case 'custom':
return this.createCustomLink(index, text, index < links.length - 1, { ...rest });
default:
break;
}
});
customContent = (
<>
<EuiTitle size="xxs">
<h3>{appName}</h3>
</EuiTitle>
<EuiSpacer size="s" />
{customLinks}
{content && (
<>
{customLinks && <EuiSpacer size="s" />}
<HeaderExtension extension={content} />
</>
)}
</>
);
}
const defaultContent = this.renderDefaultContent();
const customContent = this.renderCustomContent();
const button = (
<EuiHeaderSectionItemButton
aria-expanded={this.state.isOpen}
aria-haspopup="true"
aria-label={intl.formatMessage({
id: 'core.ui.chrome.headerGlobalNav.helpMenuButtonAriaLabel',
aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuButtonAriaLabel', {
defaultMessage: 'Help menu',
})}
onClick={this.onMenuButtonClick}
@ -350,6 +216,139 @@ class HeaderHelpMenuUI extends Component<Props, State> {
);
}
private renderDefaultContent() {
const { kibanaDocLink } = this.props;
const { helpSupportUrl } = this.state;
return (
<Fragment>
<EuiButtonEmpty href={kibanaDocLink} target="_blank" size="xs" flush="left">
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuKibanaDocumentationTitle"
defaultMessage="Kibana documentation"
/>
</EuiButtonEmpty>
<EuiSpacer size="xs" />
<EuiButtonEmpty href={helpSupportUrl} target="_blank" size="xs" flush="left">
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuAskElasticTitle"
defaultMessage="Ask Elastic"
/>
</EuiButtonEmpty>
<EuiSpacer size="xs" />
<EuiButtonEmpty href={KIBANA_FEEDBACK_LINK} target="_blank" size="xs" flush="left">
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuGiveFeedbackTitle"
defaultMessage="Give feedback"
/>
</EuiButtonEmpty>
<EuiSpacer size="xs" />
<EuiButtonEmpty
href={GITHUB_CREATE_ISSUE_LINK}
target="_blank"
size="xs"
iconType="logoGithub"
flush="left"
>
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuOpenGitHubIssueTitle"
defaultMessage="Open an issue in GitHub"
/>
</EuiButtonEmpty>
</Fragment>
);
}
private renderCustomContent() {
const { helpExtension } = this.state;
if (!helpExtension) {
return null;
}
const { navigateToUrl } = this.props;
const { appName, links, content } = helpExtension;
const getFeedbackText = () =>
i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuGiveFeedbackOnApp', {
defaultMessage: 'Give feedback on {appName}',
values: { appName: helpExtension.appName },
});
const customLinks =
links &&
links.map((link, index) => {
const addSpacer = index < links.length - 1;
switch (link.linkType) {
case 'documentation': {
const { linkType, ...rest } = link;
return createCustomLink(
index,
<FormattedMessage
id="core.ui.chrome.headerGlobalNav.helpMenuDocumentation"
defaultMessage="Documentation"
/>,
addSpacer,
{
target: '_blank',
rel: 'noopener',
...rest,
}
);
}
case 'github': {
const { linkType, labels, title, ...rest } = link;
return createCustomLink(index, getFeedbackText(), addSpacer, {
iconType: 'logoGithub',
href: createGithubUrl(labels, title),
target: '_blank',
rel: 'noopener',
...rest,
});
}
case 'discuss': {
const { linkType, ...rest } = link;
return createCustomLink(index, getFeedbackText(), addSpacer, {
iconType: 'editorComment',
target: '_blank',
rel: 'noopener',
...rest,
});
}
case 'custom': {
const { linkType, content: text, href, ...rest } = link;
return createCustomLink(index, text, addSpacer, {
href,
onClick: this.createOnClickHandler(href, navigateToUrl),
...rest,
});
}
default:
break;
}
});
return (
<>
<EuiTitle size="xxs">
<h3>{appName}</h3>
</EuiTitle>
<EuiSpacer size="s" />
{customLinks}
{content && (
<>
{customLinks && <EuiSpacer size="s" />}
<HeaderExtension extension={content} />
</>
)}
</>
);
}
private onMenuButtonClick = () => {
this.setState({
isOpen: !this.state.isOpen,
@ -361,10 +360,44 @@ class HeaderHelpMenuUI extends Component<Props, State> {
isOpen: false,
});
};
private createOnClickHandler(href: string, navigate: Props['navigateToUrl']) {
return (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
if (!isModifiedOrPrevented(event) && event.button === 0) {
event.preventDefault();
this.closeMenu();
navigate(href);
}
};
}
}
export const HeaderHelpMenu = injectI18n(HeaderHelpMenuUI);
const createGithubUrl = (labels: string[], title?: string) => {
const url = new URL('https://github.com/elastic/kibana/issues/new?');
HeaderHelpMenu.defaultProps = {
useDefaultContent: true,
if (labels.length) {
url.searchParams.set('labels', labels.join(','));
}
if (title) {
url.searchParams.set('title', title);
}
return url.toString();
};
const createCustomLink = (
index: number,
text: React.ReactNode,
addSpacer?: boolean,
buttonProps?: EuiButtonEmptyProps
) => {
return (
<Fragment key={`helpButton${index}`}>
<EuiButtonEmpty {...buttonProps} size="xs" flush="left">
{text}
</EuiButtonEmpty>
{addSpacer && <EuiSpacer size="xs" />}
</Fragment>
);
};

View file

@ -20,6 +20,7 @@
export { Header, HeaderProps } from './header';
export { OnIsLockedUpdate, NavType } from './types';
export {
ChromeHelpExtensionLinkBase,
ChromeHelpExtensionMenuLink,
ChromeHelpExtensionMenuCustomLink,
ChromeHelpExtensionMenuDiscussLink,

View file

@ -20,6 +20,7 @@
export { LoadingIndicator } from './loading_indicator';
export {
Header,
ChromeHelpExtensionLinkBase,
ChromeHelpExtensionMenuLink,
ChromeHelpExtensionMenuCustomLink,
ChromeHelpExtensionMenuDiscussLink,

View file

@ -43,6 +43,7 @@ import {
ChromeBreadcrumb,
ChromeHelpExtension,
ChromeHelpExtensionMenuLink,
ChromeHelpExtensionLinkBase,
ChromeHelpExtensionMenuCustomLink,
ChromeHelpExtensionMenuDiscussLink,
ChromeHelpExtensionMenuDocumentationLink,
@ -301,6 +302,7 @@ export {
ChromeBreadcrumb,
ChromeHelpExtension,
ChromeHelpExtensionMenuLink,
ChromeHelpExtensionLinkBase,
ChromeHelpExtensionMenuCustomLink,
ChromeHelpExtensionMenuDiscussLink,
ChromeHelpExtensionMenuDocumentationLink,

View file

@ -14,7 +14,6 @@ import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiFlyoutSize } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { ExclusiveUnion } from '@elastic/eui';
import { History } from 'history';
import { Href } from 'history';
import { IconType } from '@elastic/eui';
@ -250,32 +249,36 @@ export interface ChromeHelpExtension {
}
// @public (undocumented)
export type ChromeHelpExtensionMenuCustomLink = EuiButtonEmptyProps & {
linkType: 'custom';
export type ChromeHelpExtensionLinkBase = Pick<EuiButtonEmptyProps, 'iconType' | 'target' | 'rel' | 'data-test-subj'>;
// @public (undocumented)
export interface ChromeHelpExtensionMenuCustomLink extends ChromeHelpExtensionLinkBase {
content: React.ReactNode;
};
href: string;
linkType: 'custom';
}
// @public (undocumented)
export type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & {
export interface ChromeHelpExtensionMenuDiscussLink extends ChromeHelpExtensionLinkBase {
href: string;
linkType: 'discuss';
href: string;
};
}
// @public (undocumented)
export type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & {
export interface ChromeHelpExtensionMenuDocumentationLink extends ChromeHelpExtensionLinkBase {
href: string;
linkType: 'documentation';
href: string;
};
}
// @public (undocumented)
export type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & {
linkType: 'github';
export interface ChromeHelpExtensionMenuGitHubLink extends ChromeHelpExtensionLinkBase {
labels: string[];
linkType: 'github';
title?: string;
};
}
// @public (undocumented)
export type ChromeHelpExtensionMenuLink = ExclusiveUnion<ChromeHelpExtensionMenuGitHubLink, ExclusiveUnion<ChromeHelpExtensionMenuDiscussLink, ExclusiveUnion<ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuCustomLink>>>;
export type ChromeHelpExtensionMenuLink = ChromeHelpExtensionMenuGitHubLink | ChromeHelpExtensionMenuDiscussLink | ChromeHelpExtensionMenuDocumentationLink | ChromeHelpExtensionMenuCustomLink;
// @public (undocumented)
export interface ChromeNavControl {

View file

@ -31,7 +31,6 @@ import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiFlyoutSize } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { EventEmitter } from 'events';
import { ExclusiveUnion } from '@elastic/eui';
import { ExecutionContext } from 'src/plugins/expressions/common';
import { ExpressionAstExpression } from 'src/plugins/expressions/common';
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';

View file

@ -31,7 +31,6 @@ import { EuiContextMenuPanelDescriptor } from '@elastic/eui';
import { EuiFlyoutSize } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
import { EventEmitter } from 'events';
import { ExclusiveUnion } from '@elastic/eui';
import { ExpressionAstExpression } from 'src/plugins/expressions/common';
import { History } from 'history';
import { Href } from 'history';

View file

@ -0,0 +1,8 @@
{
"id": "core_plugin_helpmenu",
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["core_plugin_helpmenu"],
"server": false,
"ui": true
}

View file

@ -0,0 +1,14 @@
{
"name": "core_plugin_helpmenu",
"version": "1.0.0",
"main": "target/test/plugin_functional/plugins/core_plugin_helpmenu",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
},
"license": "Apache-2.0",
"scripts": {
"kbn": "node ../../../../scripts/kbn.js",
"build": "rm -rf './target' && tsc"
}
}

View file

@ -0,0 +1,63 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import {
EuiPage,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
EuiPageContentHeader,
EuiPageContentHeaderSection,
EuiPageHeader,
EuiPageHeaderSection,
EuiTitle,
} from '@elastic/eui';
import { AppMountParameters } from 'kibana/public';
const App = ({ appName }: { appName: string }) => (
<EuiPage>
<EuiPageBody data-test-subj="chromelessAppHome">
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l">
<h1>Welcome to {appName}!</h1>
</EuiTitle>
</EuiPageHeaderSection>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h2>{appName} home page section title</h2>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>{appName} page content</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
);
export const renderApp = (appName: string, { element }: AppMountParameters) => {
render(<App appName={appName} />, element);
return () => unmountComponentAtNode(element);
};

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.
*/
import { PluginInitializer } from 'kibana/public';
import { CoreHelpMenuPlugin, CoreHelpMenuPluginSetup, CoreHelpMenuPluginStart } from './plugin';
export const plugin: PluginInitializer<CoreHelpMenuPluginSetup, CoreHelpMenuPluginStart> = () =>
new CoreHelpMenuPlugin();

View file

@ -0,0 +1,56 @@
/*
* 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 { Plugin, CoreSetup } from 'kibana/public';
export class CoreHelpMenuPlugin
implements Plugin<CoreHelpMenuPluginSetup, CoreHelpMenuPluginStart> {
public setup(core: CoreSetup, deps: {}) {
core.application.register({
id: 'core_help_menu',
title: 'Help Menu Test App',
async mount(context, params) {
const [{ chrome, http }] = await core.getStartServices();
chrome.setHelpExtension({
appName: 'HelpMenuTestApp',
links: [
{
linkType: 'custom',
href: http.basePath.prepend('/app/management'),
content: 'Go to management',
'data-test-subj': 'coreHelpMenuInternalLinkTest',
},
],
});
const { renderApp } = await import('./application');
return renderApp('Help Menu Test App', params);
},
});
return {};
}
public start() {}
public stop() {}
}
export type CoreHelpMenuPluginSetup = ReturnType<CoreHelpMenuPlugin['setup']>;
export type CoreHelpMenuPluginStart = ReturnType<CoreHelpMenuPlugin['start']>;

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target",
"skipLibCheck": true
},
"include": [
"index.ts",
"public/**/*.ts",
"public/**/*.tsx",
"../../../../typings/**/*"
],
"exclude": [],
"references": [
{ "path": "../../../../src/core/tsconfig.json" }
]
}

View file

@ -0,0 +1,67 @@
/*
* 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 url from 'url';
import expect from '@kbn/expect';
import { PluginFunctionalProviderContext } from '../../services';
declare global {
interface Window {
_nonReloadedFlag?: boolean;
}
}
const getPathWithHash = (absoluteUrl: string) => {
const parsed = url.parse(absoluteUrl);
return `${parsed.path}${parsed.hash ?? ''}`;
};
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const PageObjects = getPageObjects(['common']);
const browser = getService('browser');
const testSubjects = getService('testSubjects');
const setNonReloadedFlag = () => {
return browser.executeAsync(async (cb) => {
window._nonReloadedFlag = true;
cb();
});
};
const wasReloaded = () => {
return browser.executeAsync<boolean>(async (cb) => {
const reloaded = window._nonReloadedFlag !== true;
cb(reloaded);
});
};
describe('chrome helpMenu links', () => {
beforeEach(async () => {
await PageObjects.common.navigateToApp('core_help_menu');
await setNonReloadedFlag();
});
it('navigates to internal custom links without performing a full page refresh', async () => {
await testSubjects.click('helpMenuButton');
await testSubjects.click('coreHelpMenuInternalLinkTest');
expect(getPathWithHash(await browser.getCurrentUrl())).to.eql('/app/management');
expect(await wasReloaded()).to.eql(false);
});
});
}

View file

@ -29,5 +29,6 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
loadTestFile(require.resolve('./application_leave_confirm'));
loadTestFile(require.resolve('./application_status'));
loadTestFile(require.resolve('./rendering'));
loadTestFile(require.resolve('./chrome_help_menu_links'));
});
}