mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Home] Elastic home page redesign (#70571)
Co-authored-by: Catherine Liu <catherine.liu@elastic.co> Co-authored-by: Ryan Keairns <contactryank@gmail.com> Co-authored-by: Catherine Liu <catherineqliu@outlook.com> Co-authored-by: Michael Marcialis <michael.marcialis@elastic.co>
This commit is contained in:
parent
638df5820c
commit
532f2d70e8
122 changed files with 4124 additions and 2954 deletions
|
@ -147,7 +147,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = `
|
|||
"baseUrl": "/",
|
||||
"category": Object {
|
||||
"euiIconType": "logoSecurity",
|
||||
"id": "security",
|
||||
"id": "securitySolution",
|
||||
"label": "Security",
|
||||
"order": 4000,
|
||||
},
|
||||
|
@ -1393,11 +1393,11 @@ exports[`CollapsibleNav renders links grouped by category 1`] = `
|
|||
</EuiAccordion>
|
||||
</EuiCollapsibleNavGroup>
|
||||
<EuiCollapsibleNavGroup
|
||||
data-test-subj="collapsibleNavGroup-security"
|
||||
data-test-subj="collapsibleNavGroup-securitySolution"
|
||||
iconType="logoSecurity"
|
||||
initialIsOpen={true}
|
||||
isCollapsible={true}
|
||||
key="security"
|
||||
key="securitySolution"
|
||||
onToggle={[Function]}
|
||||
title="Security"
|
||||
>
|
||||
|
@ -1433,7 +1433,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = `
|
|||
</EuiFlexGroup>
|
||||
}
|
||||
className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading"
|
||||
data-test-subj="collapsibleNavGroup-security"
|
||||
data-test-subj="collapsibleNavGroup-securitySolution"
|
||||
id="mockId"
|
||||
initialIsOpen={true}
|
||||
onToggle={[Function]}
|
||||
|
@ -1441,7 +1441,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = `
|
|||
>
|
||||
<div
|
||||
className="euiAccordion euiAccordion-isOpen euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading"
|
||||
data-test-subj="collapsibleNavGroup-security"
|
||||
data-test-subj="collapsibleNavGroup-securitySolution"
|
||||
onToggle={[Function]}
|
||||
>
|
||||
<div
|
||||
|
|
|
@ -2283,10 +2283,12 @@ exports[`Header renders 2`] = `
|
|||
data-test-subj="headerGlobalNav"
|
||||
>
|
||||
<EuiHeader
|
||||
id="headerGlobalNav"
|
||||
position="fixed"
|
||||
>
|
||||
<div
|
||||
className="euiHeader euiHeader--default euiHeader--fixed"
|
||||
id="headerGlobalNav"
|
||||
>
|
||||
<EuiHeaderSection
|
||||
grow={false}
|
||||
|
@ -6494,10 +6496,12 @@ exports[`Header renders 3`] = `
|
|||
data-test-subj="headerGlobalNav"
|
||||
>
|
||||
<EuiHeader
|
||||
id="headerGlobalNav"
|
||||
position="fixed"
|
||||
>
|
||||
<div
|
||||
className="euiHeader euiHeader--default euiHeader--fixed"
|
||||
id="headerGlobalNav"
|
||||
>
|
||||
<EuiHeaderSection
|
||||
grow={false}
|
||||
|
@ -11306,10 +11310,12 @@ exports[`Header renders 4`] = `
|
|||
data-test-subj="headerGlobalNav"
|
||||
>
|
||||
<EuiHeader
|
||||
id="headerGlobalNav"
|
||||
position="fixed"
|
||||
>
|
||||
<div
|
||||
className="euiHeader euiHeader--default euiHeader--fixed"
|
||||
id="headerGlobalNav"
|
||||
>
|
||||
<EuiHeaderSection
|
||||
grow={false}
|
||||
|
|
|
@ -125,7 +125,7 @@ export function Header({
|
|||
<>
|
||||
<LoadingIndicator loadingCount$={observables.loadingCount$} />
|
||||
<header className={className} data-test-subj="headerGlobalNav">
|
||||
<EuiHeader position="fixed">
|
||||
<EuiHeader position="fixed" id="headerGlobalNav">
|
||||
<EuiHeaderSection grow={false}>
|
||||
{navType === 'modern' ? (
|
||||
<EuiHeaderSectionItem border="right" className="header__toggleNavButtonSection">
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
}
|
||||
|
||||
.kbnGlobalBannerList__item + .kbnGlobalBannerList__item {
|
||||
margin-top: $euiSize;
|
||||
margin-top: $euiSizeS;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ export const DEFAULT_APP_CATEGORIES = Object.freeze({
|
|||
order: 3000,
|
||||
},
|
||||
security: {
|
||||
id: 'security',
|
||||
id: 'securitySolution',
|
||||
label: i18n.translate('core.ui.securityNavList.label', {
|
||||
defaultMessage: 'Security',
|
||||
}),
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["management"],
|
||||
"requiredBundles": ["kibanaReact"]
|
||||
"optionalPlugins": ["home"],
|
||||
"requiredBundles": ["kibanaReact", "home"]
|
||||
}
|
||||
|
|
|
@ -114,6 +114,21 @@ export class AdvancedSettingsComponent extends Component<
|
|||
filteredSettings: this.mapSettings(Query.execute(query, this.settings)),
|
||||
});
|
||||
});
|
||||
|
||||
// scrolls to setting provided in the URL hash
|
||||
const { hash } = window.location;
|
||||
if (hash !== '') {
|
||||
setTimeout(() => {
|
||||
const id = hash.replace('#', '');
|
||||
const element = document.getElementById(id);
|
||||
const globalNavOffset = document.getElementById('headerGlobalNav')?.offsetHeight || 0;
|
||||
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
window.scrollBy(0, -globalNavOffset); // offsets scroll by height of the global nav
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
|
@ -15,6 +15,7 @@ exports[`Field for array setting should render as read only if saving is disable
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="array:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -87,6 +88,7 @@ exports[`Field for array setting should render as read only with help text if ov
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="array:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -147,6 +149,7 @@ exports[`Field for array setting should render custom setting icon if it is cust
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="array:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -208,6 +211,7 @@ exports[`Field for array setting should render default value if there is no user
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="array:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -258,6 +262,7 @@ exports[`Field for array setting should render unsaved value if there are unsave
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="array:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -359,6 +364,7 @@ exports[`Field for array setting should render user value if there is user value
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="array:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -426,6 +432,7 @@ exports[`Field for boolean setting should render as read only if saving is disab
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="boolean:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -504,6 +511,7 @@ exports[`Field for boolean setting should render as read only with help text if
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="boolean:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -570,6 +578,7 @@ exports[`Field for boolean setting should render custom setting icon if it is cu
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="boolean:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -637,6 +646,7 @@ exports[`Field for boolean setting should render default value if there is no us
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="boolean:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -693,6 +703,7 @@ exports[`Field for boolean setting should render unsaved value if there are unsa
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="boolean:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -796,6 +807,7 @@ exports[`Field for boolean setting should render user value if there is user val
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="boolean:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -869,6 +881,7 @@ exports[`Field for image setting should render as read only if saving is disable
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="image:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -943,6 +956,7 @@ exports[`Field for image setting should render as read only with help text if ov
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="image:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1001,6 +1015,7 @@ exports[`Field for image setting should render custom setting icon if it is cust
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="image:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1064,6 +1079,7 @@ exports[`Field for image setting should render default value if there is no user
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="image:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1116,6 +1132,7 @@ exports[`Field for image setting should render unsaved value if there are unsave
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="image:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1214,6 +1231,7 @@ exports[`Field for image setting should render user value if there is user value
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="image:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1317,6 +1335,7 @@ exports[`Field for json setting should render as read only if saving is disabled
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="json:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1412,6 +1431,7 @@ exports[`Field for json setting should render as read only with help text if ove
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="json:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1492,6 +1512,7 @@ exports[`Field for json setting should render custom setting icon if it is custo
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="json:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1598,6 +1619,7 @@ exports[`Field for json setting should render default value if there is no user
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="json:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1685,6 +1707,7 @@ exports[`Field for json setting should render unsaved value if there are unsaved
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="json:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1809,6 +1832,7 @@ exports[`Field for json setting should render user value if there is user value
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="json:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1896,6 +1920,7 @@ exports[`Field for markdown setting should render as read only if saving is disa
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="markdown:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -1988,6 +2013,7 @@ exports[`Field for markdown setting should render as read only with help text if
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="markdown:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2068,6 +2094,7 @@ exports[`Field for markdown setting should render custom setting icon if it is c
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="markdown:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2149,6 +2176,7 @@ exports[`Field for markdown setting should render default value if there is no u
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="markdown:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2219,6 +2247,7 @@ exports[`Field for markdown setting should render unsaved value if there are uns
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="markdown:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2336,6 +2365,7 @@ exports[`Field for markdown setting should render user value if there is user va
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="markdown:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2423,6 +2453,7 @@ exports[`Field for number setting should render as read only if saving is disabl
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="number:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2495,6 +2526,7 @@ exports[`Field for number setting should render as read only with help text if o
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="number:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2555,6 +2587,7 @@ exports[`Field for number setting should render custom setting icon if it is cus
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="number:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2616,6 +2649,7 @@ exports[`Field for number setting should render default value if there is no use
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="number:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2666,6 +2700,7 @@ exports[`Field for number setting should render unsaved value if there are unsav
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="number:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2763,6 +2798,7 @@ exports[`Field for number setting should render user value if there is user valu
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="number:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2830,6 +2866,7 @@ exports[`Field for select setting should render as read only if saving is disabl
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="select:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2918,6 +2955,7 @@ exports[`Field for select setting should render as read only with help text if o
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="select:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -2994,6 +3032,7 @@ exports[`Field for select setting should render custom setting icon if it is cus
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="select:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3071,6 +3110,7 @@ exports[`Field for select setting should render default value if there is no use
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="select:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3137,6 +3177,7 @@ exports[`Field for select setting should render unsaved value if there are unsav
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="select:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3250,6 +3291,7 @@ exports[`Field for select setting should render user value if there is user valu
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="select:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3333,6 +3375,7 @@ exports[`Field for string setting should render as read only if saving is disabl
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3405,6 +3448,7 @@ exports[`Field for string setting should render as read only with help text if o
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3465,6 +3509,7 @@ exports[`Field for string setting should render custom setting icon if it is cus
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3526,6 +3571,7 @@ exports[`Field for string setting should render default value if there is no use
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3576,6 +3622,7 @@ exports[`Field for string setting should render unsaved value if there are unsav
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3673,6 +3720,7 @@ exports[`Field for string setting should render user value if there is user valu
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3740,6 +3788,7 @@ exports[`Field for stringWithValidation setting should render as read only if sa
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test-validation:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3812,6 +3861,7 @@ exports[`Field for stringWithValidation setting should render as read only with
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test-validation:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3872,6 +3922,7 @@ exports[`Field for stringWithValidation setting should render custom setting ico
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test-validation:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3933,6 +3984,7 @@ exports[`Field for stringWithValidation setting should render default value if t
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test-validation:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -3983,6 +4035,7 @@ exports[`Field for stringWithValidation setting should render unsaved value if t
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test-validation:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
@ -4080,6 +4133,7 @@ exports[`Field for stringWithValidation setting should render user value if ther
|
|||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
id="string:test-validation:setting"
|
||||
title={
|
||||
<h3>
|
||||
<span
|
||||
|
|
|
@ -673,6 +673,7 @@ export class Field extends PureComponent<FieldProps> {
|
|||
|
||||
return (
|
||||
<EuiDescribedFormGroup
|
||||
id={id}
|
||||
className={className}
|
||||
title={this.renderTitle(setting)}
|
||||
description={this.renderDescription(setting)}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CoreSetup, Plugin } from 'kibana/public';
|
||||
import { FeatureCatalogueCategory } from '../../home/public';
|
||||
import { ComponentRegistry } from './component_registry';
|
||||
import { AdvancedSettingsSetup, AdvancedSettingsStart, AdvancedSettingsPluginSetup } from './types';
|
||||
|
||||
|
@ -29,7 +30,7 @@ const title = i18n.translate('advancedSettings.advancedSettingsLabel', {
|
|||
|
||||
export class AdvancedSettingsPlugin
|
||||
implements Plugin<AdvancedSettingsSetup, AdvancedSettingsStart, AdvancedSettingsPluginSetup> {
|
||||
public setup(core: CoreSetup, { management }: AdvancedSettingsPluginSetup) {
|
||||
public setup(core: CoreSetup, { management, home }: AdvancedSettingsPluginSetup) {
|
||||
const kibanaSection = management.sections.section.kibana;
|
||||
|
||||
kibanaSection.registerApp({
|
||||
|
@ -44,6 +45,21 @@ export class AdvancedSettingsPlugin
|
|||
},
|
||||
});
|
||||
|
||||
if (home) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'advanced_settings',
|
||||
title,
|
||||
description: i18n.translate('advancedSettings.featureCatalogueTitle', {
|
||||
defaultMessage:
|
||||
'Customize your Kibana experience — change the date format, turn on dark mode, and more.',
|
||||
}),
|
||||
icon: 'gear',
|
||||
path: '/app/management/kibana/settings',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
component: component.setup,
|
||||
};
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
|
||||
import { ComponentRegistry } from './component_registry';
|
||||
import { HomePublicPluginSetup } from '../../home/public';
|
||||
|
||||
import { ManagementSetup } from '../../management/public';
|
||||
|
||||
export interface AdvancedSettingsSetup {
|
||||
|
@ -29,6 +31,7 @@ export interface AdvancedSettingsStart {
|
|||
|
||||
export interface AdvancedSettingsPluginSetup {
|
||||
management: ManagementSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
}
|
||||
|
||||
export { ComponentRegistry };
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"version": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["devTools", "home"],
|
||||
"optionalPlugins": ["usageCollection"],
|
||||
"requiredBundles": ["esUiShared", "kibanaReact", "kibanaUtils"]
|
||||
"requiredPlugins": ["devTools"],
|
||||
"optionalPlugins": ["usageCollection", "home"],
|
||||
"requiredBundles": ["esUiShared", "kibanaReact", "kibanaUtils", "home"]
|
||||
}
|
||||
|
|
|
@ -28,19 +28,21 @@ export class ConsoleUIPlugin implements Plugin<void, void, AppSetupUIPluginDepen
|
|||
{ notifications, getStartServices, http }: CoreSetup,
|
||||
{ devTools, home, usageCollection }: AppSetupUIPluginDependencies
|
||||
) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'console',
|
||||
title: i18n.translate('console.devToolsTitle', {
|
||||
defaultMessage: 'Console',
|
||||
}),
|
||||
description: i18n.translate('console.devToolsDescription', {
|
||||
defaultMessage: 'Skip cURL and use this JSON interface to work with your data directly.',
|
||||
}),
|
||||
icon: 'consoleApp',
|
||||
path: '/app/dev_tools#/console',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
if (home) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'console',
|
||||
title: i18n.translate('console.devToolsTitle', {
|
||||
defaultMessage: 'Interact with the Elasticsearch API',
|
||||
}),
|
||||
description: i18n.translate('console.devToolsDescription', {
|
||||
defaultMessage: 'Skip cURL and use a JSON interface to work with your data in Console.',
|
||||
}),
|
||||
icon: 'consoleApp',
|
||||
path: '/app/dev_tools#/console',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
}
|
||||
|
||||
devTools.register({
|
||||
id: 'console',
|
||||
|
|
|
@ -21,7 +21,7 @@ import { DevToolsSetup } from '../../../dev_tools/public';
|
|||
import { UsageCollectionSetup } from '../../../usage_collection/public';
|
||||
|
||||
export interface AppSetupUIPluginDependencies {
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
devTools: DevToolsSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
}
|
||||
|
|
|
@ -384,7 +384,7 @@ export class DashboardPlugin
|
|||
}),
|
||||
icon: 'dashboardApp',
|
||||
path: `/app/dashboards#${DashboardConstants.LANDING_PAGE_PATH}`,
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export function registerFeature(home: HomePublicPluginSetup) {
|
|||
}),
|
||||
icon: 'discoverApp',
|
||||
path: '/app/discover#/',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
}
|
||||
|
|
21
src/plugins/home/common/constants.ts
Normal file
21
src/plugins/home/common/constants.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 const PLUGIN_ID = 'home';
|
||||
export const HOME_APP_BASE_PATH = `/app/${PLUGIN_ID}`;
|
|
@ -35,15 +35,21 @@ export const renderApp = async (
|
|||
) => {
|
||||
const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: 'Home' });
|
||||
const { featureCatalogue, chrome } = getServices();
|
||||
const navLinks = chrome.navLinks.getAll();
|
||||
|
||||
// all the directories could be get in "start" phase of plugin after all of the legacy plugins will be moved to a NP
|
||||
const directories = featureCatalogue.get();
|
||||
|
||||
// Filters solutions by available nav links
|
||||
const solutions = featureCatalogue
|
||||
.getSolutions()
|
||||
.filter(({ id }) => navLinks.find(({ category, hidden }) => !hidden && category?.id === id));
|
||||
|
||||
chrome.setBreadcrumbs([{ text: homeTitle }]);
|
||||
|
||||
render(
|
||||
<KibanaContextProvider services={{ ...coreStart }}>
|
||||
<HomeApp directories={directories} />
|
||||
<HomeApp directories={directories} solutions={solutions} />
|
||||
</KibanaContextProvider>,
|
||||
element
|
||||
);
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@ exports[`props iconType 1`] = `
|
|||
<EuiCard
|
||||
betaBadgeLabel={null}
|
||||
className="homSynopsis__card homSynopsis__card--noPanel"
|
||||
data-test-subj="homeSynopsisLinkgreat tutorial"
|
||||
data-test-subj="homeSynopsisLinktutorial"
|
||||
description="this is a great tutorial about..."
|
||||
href="link_to_item"
|
||||
icon={
|
||||
|
@ -16,6 +16,7 @@ exports[`props iconType 1`] = `
|
|||
}
|
||||
layout="horizontal"
|
||||
title="Great tutorial"
|
||||
titleElement="h3"
|
||||
titleSize="xs"
|
||||
/>
|
||||
`;
|
||||
|
@ -24,7 +25,7 @@ exports[`props iconUrl 1`] = `
|
|||
<EuiCard
|
||||
betaBadgeLabel={null}
|
||||
className="homSynopsis__card homSynopsis__card--noPanel"
|
||||
data-test-subj="homeSynopsisLinkgreat tutorial"
|
||||
data-test-subj="homeSynopsisLinktutorial"
|
||||
description="this is a great tutorial about..."
|
||||
href="link_to_item"
|
||||
icon={
|
||||
|
@ -36,6 +37,7 @@ exports[`props iconUrl 1`] = `
|
|||
}
|
||||
layout="horizontal"
|
||||
title="Great tutorial"
|
||||
titleElement="h3"
|
||||
titleSize="xs"
|
||||
/>
|
||||
`;
|
||||
|
@ -44,11 +46,12 @@ exports[`props isBeta 1`] = `
|
|||
<EuiCard
|
||||
betaBadgeLabel="Beta"
|
||||
className="homSynopsis__card homSynopsis__card--noPanel"
|
||||
data-test-subj="homeSynopsisLinkgreat tutorial"
|
||||
data-test-subj="homeSynopsisLinktutorial"
|
||||
description="this is a great tutorial about..."
|
||||
href="link_to_item"
|
||||
layout="horizontal"
|
||||
title="Great tutorial"
|
||||
titleElement="h3"
|
||||
titleSize="xs"
|
||||
/>
|
||||
`;
|
||||
|
@ -57,11 +60,12 @@ exports[`render 1`] = `
|
|||
<EuiCard
|
||||
betaBadgeLabel={null}
|
||||
className="homSynopsis__card homSynopsis__card--noPanel"
|
||||
data-test-subj="homeSynopsisLinkgreat tutorial"
|
||||
data-test-subj="homeSynopsisLinktutorial"
|
||||
description="this is a great tutorial about..."
|
||||
href="link_to_item"
|
||||
layout="horizontal"
|
||||
title="Great tutorial"
|
||||
titleElement="h3"
|
||||
titleSize="xs"
|
||||
/>
|
||||
`;
|
||||
|
|
|
@ -1,63 +1,22 @@
|
|||
.homAddData__card {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.homAddData__cardDivider {
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 1px;
|
||||
right: -$euiSizeS;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: $euiBorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
.homAddData__icon {
|
||||
width: $euiSizeXL * 2;
|
||||
height: $euiSizeXL * 2;
|
||||
}
|
||||
|
||||
.homAddData__footerItem--highlight {
|
||||
background-color: tintOrShade($euiColorPrimary, 90%, 70%);
|
||||
padding: $euiSize;
|
||||
}
|
||||
|
||||
.homAddData__footerItem {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.homAddData__logo {
|
||||
margin-left: $euiSize;
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
.homeAddData__flexGroup {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xs', 's', 'm') {
|
||||
.homAddDat__flexTablet {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.homAddData__cardDivider:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.homAddData__cardDivider {
|
||||
flex-grow: 0 !important;
|
||||
flex-basis: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@include euiBreakpoint('l', 'xl') {
|
||||
.homeAddData__flexGroup {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.homDataAdd__content .euiIcon__fillSecondary {
|
||||
fill: $euiColorDarkestShade;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,73 @@
|
|||
@include euiBreakpoint('xs', 's', 'm') {
|
||||
.homHome__synopsisItem {
|
||||
flex-basis: 100% !important;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Local page variables
|
||||
$homePageWidth: 1200px;
|
||||
|
||||
.homWrapper {
|
||||
background-color: $euiColorEmptyShade;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: calc(100vh - #{$euiHeaderHeightCompensation});
|
||||
}
|
||||
|
||||
.homHeader {
|
||||
background-color: $euiPageBackgroundColor;
|
||||
border-bottom: $euiBorderWidthThin solid $euiColorLightShade;
|
||||
}
|
||||
|
||||
.homHeader__inner {
|
||||
margin: 0 auto;
|
||||
max-width: $homePageWidth;
|
||||
padding: $euiSizeXL $euiSize;
|
||||
|
||||
.homHeader--hasSolutions & {
|
||||
padding-bottom: $euiSizeXL + $euiSizeL;
|
||||
}
|
||||
}
|
||||
|
||||
#homHeader__title {
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.homHeader__actionItem {
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
margin-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.homContent {
|
||||
margin: 0 auto;
|
||||
max-width: $homePageWidth;
|
||||
padding: $euiSizeXL $euiSize;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.homData--expanded {
|
||||
flex-direction: column;
|
||||
|
||||
&,
|
||||
& > * {
|
||||
margin-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
// homChart__legend--small
|
||||
// homChart__legend-isLoading
|
||||
|
||||
@import 'add_data';
|
||||
@import 'home';
|
||||
@import 'add_data';
|
||||
@import 'manage_data';
|
||||
@import 'sample_data_set_cards';
|
||||
@import 'solutions_section';
|
||||
@import 'synopsis';
|
||||
@import 'welcome';
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.homDataManage__content .euiIcon__fillSecondary {
|
||||
fill: $euiColorDarkestShade;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.homSolutions {
|
||||
margin-top: -($euiSizeXL + $euiSizeL + $euiSizeM);
|
||||
}
|
||||
|
||||
.homSolutions__content {
|
||||
min-height: $euiSize * 16;
|
||||
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.homSolutions__group {
|
||||
max-width: 50%;
|
||||
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.homSolutionPanel {
|
||||
border-radius: $euiBorderRadius;
|
||||
color: inherit;
|
||||
flex: 1;
|
||||
transition: all $euiAnimSpeedFast $euiAnimSlightResistance;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@include euiSlightShadowHover;
|
||||
transform: translateY(-2px);
|
||||
|
||||
.euiTitle {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&,
|
||||
.euiPanel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.euiPanel {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.homSolutionPanel__header {
|
||||
color: $euiColorEmptyShade;
|
||||
padding: $euiSize;
|
||||
}
|
||||
|
||||
.homSolutionPanel__icon {
|
||||
background-color: $euiColorEmptyShade !important;
|
||||
box-shadow: none !important;
|
||||
margin: 0 auto $euiSizeS;
|
||||
padding: $euiSizeS;
|
||||
}
|
||||
|
||||
.homSolutionPanel__subtitle {
|
||||
margin-top: $euiSizeXS;
|
||||
}
|
||||
|
||||
.homSolutionPanel__content {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: $euiSize;
|
||||
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.homSolutionPanel__header {
|
||||
background-color: $euiColorPrimary;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQYAAAFjCAYAAADfDKXVAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAfiSURBVHgB7d1JblRZGobh45SrhJiWGOQ6EENLSKyABTBmOcwRq2EnTKtUdInBuEln/OkMdB3HTTjiNqd5HinA3YBUSq++24UPUkq/J+jE+/fv09HRURrD27dv0+vXr1OLfksAG4QByAgDkBEGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGICMMAAZYQAywgBkhAHICAOQEQZg05/CAGy6FAYgIwzApnNhADLCAGy6EAZgkzAAGVclgIyTj0DGYgAyZ8IADP2ZLAZgw1n8IQzA0Hn8IQzA0EX8IQzAkEMJICMMwDURhcv44DDBlp4+fZrevHmTxvLq1av04cOHRDHO1h8IA1t7/PhxOjo6SjTrdP2BQwlg7ddiEAYgxGXK8/UnwgCEs+EnwgCEk+EnwgCE0+EnwgDEYcTF8AvCAJxufkEYgO+bXxAG6Nu1y5RrwgB9O7npi8IAfTu+6YvCAP3KrkasCQP06/i2bwgD9Ov0tm8IA/QpLlFe3PZNYYA+/bjrm8IA/YmTjqd3/YAwQH+O7/sBYYC+xHmFH/f9kDBAX/7Y5oeEAfqx1VoIwgD9+L7tDwoD9GHrtRCEAfoQ5xYutv1hYYD2PWgtBGGA9m11JWJIGKBtD14LQRigbf9POxAGaNedT1DeRRigTRGEb2lHwgBtetDlyU3CAO35mXY44TgkDNCWy9XrS9qTMEBb9jqEWBMGaEdchbj3TVi2IQzQhr2uQmwSBmjD5zTCIcSaMED94rzCaRqRMEDd4tLkaIcQa8IA9YpDh70vTd5EGKBOcb9CPCA12nmFIWGAOo16snGTMEB94mTjSZqQMEBdIgqjn2zcJAxQj7ircfIoBGGAOsTtzl/TTIQByhe/nXqSy5K3EQYoW0Rhp/dt3MdhAkoVdzV+Slf3LMxKGKBMcU5h1sOHIYcSUJ5FoxAsBijLLPcp3EcYoBxxOXKUd2DalzDA8uLk4sc08nsq7EMYYFnxINRkT0nuShhgOfEgVDwlOfvlyPsIAyyjmPMJNxEGmFccMsRKKOZ8wk2EAeZT7KHDJmGA6UUI4v6EYg8dNgkDTCued4i7GIu66nAfYYBpVLcShoQBxlflShgSBhhP1SthSBhgHNWvhCFhgP00sxKGhAF29OTJkyKfcxiDN2qBHb18+TIOH5qLQhAGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGICMMNCTy0ePHhX/RqwlEAZ6EQ88/e/Zs2dniXt57JrWNfl+CVMTBloW6+BTavTR6CkJA62KlfAtsRNhoDWxEr788zc7EgZaYiWMRBhogZUwMmGgdlbCBISBWlkJExIGamQlTEwYqImVMBNhoBZx5+LXxCyEgdLFXYufV6/TxGyEgZLFSojzCZ6InJkwUCIrYWHCQGmshAIIA6WwEgoiDJTASiiMMLCYw8NDK6FQ3tqNxbx79+5jEoUiCQOLef78uUOHQgkDkBEGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGNjWxYsXL/5IdOFg9fo9wd3+fr+Ey8vLf63+/k8az38PDg5m/RX1q/+G+Pf/O43j++rf/yU1yPsxcBfvl9ApYeA23lWpY8LAJisBYeAaK4G/CQMhQhAr4SRBEgauYhBRsBL4RRj6ZSVwK2Hok5XAnYShL1YCWxGGflgJbE0Y2mcl8GDC0DYrgZ0IQ5usBPYiDO35uXrFE3+zPrVIW4ShHbES4nbm4wR7EoY2WAmMShjqZiUwCWGol5XAZIShPlYCkxOGulgJzEIY6mAlMCthKJ+VwOyEoVxWAosRhjJZCSxKGMpiJVAEYSiHlUAxhGF5VgLFEYZlWQkUSRiWYSVQNGGYn5VA8YRhPlYC1RCGeVgJVEUYpmUlUCVhmI6VQLWEYXxWAtUThnFZCTRBGMZhJdAUYdiflUBzhGF3VgLNEobdnK1en5KVQKOE4eFiJXxL0DBh2F6shC///A1NE4btWAl0RRjuZiXQJWG4nZVAt4QhZyXQPWG4zkqAJAxrVgIMCMPVnYtfE/BLz2GIuxY/r16nCbim1zDESojzCZeJ3sTh4lj/389To3oLg5XQuYODA4eNW+gpDFYCbKmHMFgJ8ECth8FKgB20GgYrAfbQYhisBNhTS2GwEmAkrYTBSoAR1R4GKwEmUHMYrASYSI1hsBJgYrWFwUqAGdQSBisBZlRDGKwEmFnJYbASYCGlhsFKgAWVFgYrAQpQUhisBChECWGwEqAwS4fBSoACLRUGKwEKtkQYrAQo3JxhsBKgEnOFwUqAikwdhghBrISTBFRjyjBEDCIKVgJUZoowOJcAlRs7DM4lQAPGCsPP1etbshKgCfuGIQ4bYiH8SEAzdg1DHCrEQjhODhugOQ8NgyBAB7YNgyBAR+4LgyBAh24Lg6sM0LFhGGIRfE9XdywKAnQswrBeB2fJ4QKQrsLwMQEM/JYANggDkBEGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGICMMAAZYQAywgBkhAHICAOQEQYgIwxARhiAjDAAGWEAMg/9bdf0LX4h0XkCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq8hdouPfUCk+KHQAAAABJRU5ErkJggg=='),
|
||||
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAFKCAYAAAAwgcXoAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAjGSURBVHgB7d27ctRaFoDh1WBzsQ9wLkVsngHHfglSIOUdgJQiJSfmIUjhJUwGjiGaU7hmzoB7vLA1GPCt21vSlvR9Vao+5lAFbbf+EktbUgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUaxYAEzCfz/NldX9biTK+zGaz/0aLSv1FAYbg6v52I8r4e39rNdCXAoAqCTRApQQaoFICDVApgQaolEADVEqgASol0ACVEmiASgk0QKUEGqBSAg1QKYEGqJRAA1RKoAEqJdAAlRJogEp5ogowJV/3t/9EGV8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFjELABOMZ/Po6TZrL/sDO29rATA6Vb3t/UoY29/+1f04DDO1w63Ev59uLVGoIGz5GHi9Sjja/QU6EPZvFLv5Uu07FIAUCWBBqiUQANUSqABKiXQAJUSaIBKCTRApQQaoFICDVApgQaolEADVEqgASol0ACVEmiASgl0ANRJoAEqJdAAlfJEFeAs+RSUv6OMsg8FXNw/Ue69/BMAAAAAAAAAAAAAAAAAAAAAQCdmAcBpspNn3flzL1q4EZRAA2PRhLR5vXz4683ryk+/7+j/ixO+vqiM9t6Rr78e899ffvq9za9/FWigdhnNbNVqfA/wyuHr5fgxxqMi0EDfMq4Z3ya4K4fbaMN7XgINdKGJ8OUjW/M1JxBooKSj4c3X5mhYa5bgmwYsoxlHXA0hbo1vJnCW5gRds10Jo4lOCDTws4xvRjiPjvOoeDXohUDDtB09Or4W31dTUAE/CJiWJshNjK8E1RJoGDdBHjCBhvHJCBtZjIAfHAxf7scZ46uHr/brkfCDhGFqVllcCWOL0RJoGIajs+S16Hjfnc3K/XHzefG7cp7b0N6HQEO9qhhdbG5uxosXL6KET58+xb1796IPGef79+/Ho0ePooSXL1/Gq1evok0rAdSkiXIeJVdxgm9tbS22traihJ2dnejTxsZGsffy+vXraJtAQ/+qizJ1EGjoRzNTvhGizAkEGrqVKy56OdHH8Ag0tC9vPpRBXg9RZgECDe3IEGeU82jZOmWWItBQlhEGxQg0XFyG+Lc4CLN9imJ8mGB5ebScc+W8kMTRMsUJNCzGbJnOCDScj5UYdE6g4XR5lHwjHC3TA4GGXx299FqY6Y1Aw3fNagxjDKog0CDMVEqgmbIcX+QY43pAhQSaKXLij0EQaKZEmJfU52OqShvSexFopkCYL2B7ezsePnwYJezu7kZfMsz5FJQPHz5ECe/evYu2OSHCmAkzgybQjJEwMwoCzZgIM6Mi0IxB3ifjZhxc/QejIdAMWX5+84h5PWCEBJohcuUfk+DDzdBklPOo2WeX0fMhZyjyxN/vcTBvhkkQaGq3GgcnAK3MYHIEmlo5AcjkCTQ1MmeGsANQlxxj5DhjNQCBpgr5ObwV7ssMPxBo+macASewU9AX982AMwg0XbM6A85JoOmSi01gAQJNF9xtDpYg0LTNSUBYkp2GtuRRc44znASEJQk0bXDUDAXYgSjJUTMUJNCU4qgZCrMzcVGOmqElAs1F5LK5jLPPEbTAjsUyXA0IHRBoFuVqQOiIQLOIPGr+LZic2axcKubzefSl5PtIbb+XSwFny6Plv0KcJ2lrayv29vaKbO/fv4++ZJyfPHlS7L08fvw42rYScDonAqEnAs1JnAiEngk0x8mRxh/h2YDQK4HmZ2txcGtQIw3omUBzVIbZSAMqIdAkIw2okEDjwhOolEBPW44zbgZQJYGepjwBeGt/ux5AtQR6epqrAo00oHICPS05b/4zLKGDQRDo6TBvhoER6GmwvhkGSKDHzfpmGDCBHi8nA2HgBHqcnAyEERDo8XEyEEZCoMfFI6loRZ+PqSptSO/FP4HHI++n4cpAWrGxsRGl7OzsRF/W1tbi9u3bUcLHjx9jd3c32iTQw5c/wzwZaKUGjIxAD5uVGjBiAj1c4gwjJ9DDlOOMjLOfH4yYHXx4PDMQJsJOPiwZ51sBTIJAD4c1zjAxAj0M4gwTJND1E2eYKIGum/s4w4QJdL1cug0TJ9B1EmdAoCskzsA3Al0XcQb+T6DrIc7ADwS6DuIM/EKg+yfOwLEEul/iDJxIoPsjzsCpLgV9yCsExRk4lUB3L++t4fJt4ExGHN1y4yPg3AS6O+IMLESguyHOwMLMoNuXj6kSZ2BhAt2ua+EZgsCSBLo9q3Gw1hlgKStBGy7vb3+EGT8jsL6+Hpubm1HK27dvoy8bGxvfthJ2dna+bW0S6PIyzn8dvsLg3b17N968eRMlZNDu3LkTfZjNZvHgwYN49uxZlPD06dN4/vx5tMmIo6w8YhZnoAiBLuvPEGegEIEuJ++vcSUAChHoMtxfAyhOoC8uw+xCFKA4gb6YXOt8MwBaINDLa9Y6A7RCoJdjOR3QOoFeTl7CLc5AqwR6cbli41oAtEygF2PFBtAZgT4/KzaATgn0+VixAXROoM8n4+ykINApgT5bjjVWA6BjAn269XCPDaAnAn2yHGk4KQj8Yj6fRxc8UeV4zVNRYPJ2d3dje3s7Smj7EVFn+fz5c5H3koHO70vbPDPveHlS0MUoQK+MOH7lSkGgCgL9o3wiiisFgSoI9Hc5d/49ACoh0N+5Qx1QFYE+kHNnD3wFqiLQ5s5ApaYeaHNnoFpTD3SONsydgSpNOdB5j43rAVCpqQbafTaA6k010O6zAVRvioE2dwYGYWqBtqQOGIwpBdqSOmBQphRoow1gUKYS6OthSR0wMFMIdB413wiAgZlCoI02gEEae6CNNoDBGnOgjTaAQRtzoI02gEEba6CNNoDBG2OgZ2G0AYzA/wBP5hsF50HhogAAAABJRU5ErkJggg==');
|
||||
background-repeat: no-repeat;
|
||||
background-position: top 0 left 0, bottom 0 right 0;
|
||||
background-size: $euiSizeXL * 4, $euiSizeXL * 6;
|
||||
|
||||
.homSolutionPanel--enterpriseSearch & {
|
||||
background-color: $euiColorSecondary;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAABKCAYAAADJwhY8AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAANMSURBVHgBzZp7buIwEMYHKC1QuqsF7Up7Hs4Al+Ff4DCcAonrtPuAPjcL1B/YyA0p2JkZK580SuQm6Y+xx4+xiSqumrG+d7/z/rYx9t+WZdZ2lFhXxq4jngfkP2Ov9qquq8jnm9Zu6eBNgD6TImwsoC80ibY1NIe1sRcSVsPYHfFVN9Yy1jG2pUPbFZEUoJMP+kYHWJakAZ0AemuvrPZZJ10B8jsdHFFKWh70BSe07X1GkUoBCCHib+w1qsq1qzivLkU6JDUgTSaT7m63C4ZMCmjgaDwe4zYYMhmgB+cEyPal95IAFsA5fTGQZ4dbdcAzcO7/9wxk7bMH8AfMTvJDUsO+fG2tSfJwvp5qtdqKPgEMEYABekeBo0IEnNODgTzpI0MBfaFhnwUtAQdlBvA+X1gG0Kmw0y0J57QykE9+ASdIHo3hF29cARMO6uYDhhvFGPwfjG0E4BxPxy/gVPFR8/m8MRwOsTosPa3y9KEtigBCpmoAJwV5jGixjtp8EG3xD8noOASKjiT2V6+Jr5YLFo2hDuvkDfEErqa7EZXx4naxWKyIr9b+e6QkU0U/iBcw+2jWnM08E09NtMMqA0JNNUC0ReInlRraE9bodXBOzaoD1rQBuVku9SpmZ7eSL9wjVa864LbqgOpVzJ4bagNyv7/RBozZgymSOuAN8bRTA7RrFM4+DJRpepBbvZAqIDf3jQmrThXbxCS3i5FdduYksXOAjUp5QJt75npvK75whwwclopd4uvV3YgB2m7lG8nouK0rAojkEcnlZTZ+plUCsDEajfom9SYBB31InXA/is64h+8sl0vKsowGgwExBO/99QvKDkXISHQpFxCz2Wx/nU6nVFIniacyHkTOpGevJ2J48sR7UKgHnceCRoiSnvxVVOj2P6C6ZyjHVKljwb7a54KDKtKT2MgpPDECD/ZJSYGedEdaCqW+437BkzgcdG/zOIVKciTgDOTawL2dezfVmYUiyHV+V6lIyQAhDxJwjyHvoPv4SWmFgAiCg1JnFqLgIO6qK1SIVowS0afjUgCin/tNJZOZ2oCIUlRr6aOlWoDwFjZz2CczpQHhKXjsYv8WKilAeAwBgL0R0ZPCHECAvFirzCFb/5hyknPV7zL4DLH0CVGgAAAAAElFTkSuQmCC'),
|
||||
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA+CAYAAACbQR1vAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACBSURBVHgB7dpBDcAwDMVQdyzGH9xojEQjtbIfBEu5fGUx62WPjyEPcgVArgDIFQC5AiBXAOQKgFwBkCsAcgVATh9gsW+4vFIngFwBkCsAcgVArgDIFQC5AiBXAORahJjVm9zpCoBcAZArAHIFQK4AyBUAuQIgVwDkCoBcAZDTB/gBLrgCk8OhuvYAAAAASUVORK5CYII=');
|
||||
background-position: top $euiSizeS left 0, bottom $euiSizeS right $euiSizeS;
|
||||
background-size: $euiSize * 1.25, $euiSizeXL;
|
||||
}
|
||||
|
||||
.homSolutionPanel--observability & {
|
||||
background-color: $euiColorAccent;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADNSURBVHgB7duxCcIAEEDR08IFrGyz/0pZwVYEIcYF/FWKwHtwXP/hyrvMQbZtu+3rPid3Hf4SKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgcJmDnt6WZfms6/qak/sFeswx3vs85+ScWBAoCBQECgIFgYJAQaAgUBAoCBQECgIFgYJAQaAgUBAoCBQECgKFLx4KCqcIFEJnAAAAAElFTkSuQmCC');
|
||||
background-position: top $euiSizeS right $euiSizeS;
|
||||
background-size: $euiSizeL * 1.5;
|
||||
}
|
||||
|
||||
.homSolutionPanel--securitySolution & {
|
||||
background-color: $euiColorDarkestShade;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGIAAABiCAYAAACrpQYOAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAKRSURBVHgB7d3tSgMxEIXhMeqPQoWC9f4vte5Z3dLvTbKZzCQ5LyxqKwg+tNjdUxQR+RJmXpiOvRDDvPD/kRjGhYvPgbETZlK4+fogxDApPLiNGAaFJ7cD40NYtcKL+76FGNUKK/cRo1Ih4n5gvAtTbQ1i+R5iKBcDgYBADMViIRAxFEuBQMRQKhUCEUOhHAi0YLwJK1IuBCJGwbZAoE8hRpG2QqAFg22oBAQCxkFYdqUgEE6dEyOzkhCIGJmVhkDA4PXvxDQgEMcIiWlBIGIkpAmBiBGZNgQiRkQ1IBAxVqoFgThge1FNCMTN1JNqQyBiPMgCAnHAdpMVBOJm6iJLCA7YLrKEWH4+r3+LPYQIMeY8QKDhlyFeINDQGJ4g0LAY3iDQkBgeIdBwmymvEGgoDM8QaJgBm3cINMSArQUI1P2ArRUI1PVmqiUI1C1GaxCoywFbixCouzFCqxCIyxBPnU6nLjBafkQs7YExHdJyPUCg+WmqZYxeIBAwmv3TticItJseFYcWHxm9QaD5RV9rGD1CIGAcJ4xmztr2CoHms7atYPQMgc4Y3p+qeodAwPiZjnfPGCNAoPMgwSvGKBDINcZIEMgtxmgQyCXGiBDIHcaoEAgYx+n48IAxMgQ6v1nGGmN0COQCgxB/4feAF307KwxCXDe/9dgCgxD3mWAQ4nHAqHrplRDPq3odnBCvq4ZBiPWqYBAiLnUMQsSnikGItObtlAYGIdJTmesQIq/iGITIr+h2ihDbKradIsT2imynCFGmzdspQpRr03VwQpQtG4MQ5cvCIIROyRiE0CsJgxC6RW+nCKFf1FyHEHVaxSBEvV5upwhRv4dzHULYdIdBCLuutlOEsO18HZz/u8E+YMgvrbKfmp8y7IEAAAAASUVORK5CYII=');
|
||||
background-position: top $euiSizeS left $euiSizeS;
|
||||
background-size: $euiSizeL * 2;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,10 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
.homSynopsis__cardTitle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
// SASSTODO: Fix in EUI
|
||||
.euiCard__content {
|
||||
padding-top: 0 !important;
|
||||
|
|
|
@ -1,320 +0,0 @@
|
|||
/*
|
||||
* 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 PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
|
||||
import { getServices } from '../kibana_services';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiLink,
|
||||
EuiPanel,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiText,
|
||||
EuiCard,
|
||||
EuiIcon,
|
||||
EuiHorizontalRule,
|
||||
EuiFlexGrid,
|
||||
} from '@elastic/eui';
|
||||
|
||||
const AddDataUi = ({ apmUiEnabled, isNewKibanaInstance, intl, mlEnabled }) => {
|
||||
const basePath = getServices().getBasePath();
|
||||
|
||||
const renderCards = () => {
|
||||
const apmData = {
|
||||
title: intl.formatMessage({
|
||||
id: 'home.addData.apm.nameTitle',
|
||||
defaultMessage: 'APM',
|
||||
}),
|
||||
description: intl.formatMessage({
|
||||
id: 'home.addData.apm.nameDescription',
|
||||
defaultMessage:
|
||||
'APM automatically collects in-depth performance metrics and errors from inside your applications.',
|
||||
}),
|
||||
ariaDescribedby: 'aria-describedby.addAmpButtonLabel',
|
||||
};
|
||||
const loggingData = {
|
||||
title: intl.formatMessage({
|
||||
id: 'home.addData.logging.nameTitle',
|
||||
defaultMessage: 'Logs',
|
||||
}),
|
||||
description: intl.formatMessage({
|
||||
id: 'home.addData.logging.nameDescription',
|
||||
defaultMessage:
|
||||
'Ingest logs from popular data sources and easily visualize in preconfigured dashboards.',
|
||||
}),
|
||||
ariaDescribedby: 'aria-describedby.addLogDataButtonLabel',
|
||||
};
|
||||
const metricsData = {
|
||||
title: intl.formatMessage({
|
||||
id: 'home.addData.metrics.nameTitle',
|
||||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
description: intl.formatMessage({
|
||||
id: 'home.addData.metrics.nameDescription',
|
||||
defaultMessage:
|
||||
'Collect metrics from the operating system and services running on your servers.',
|
||||
}),
|
||||
ariaDescribedby: 'aria-describedby.addMetricsButtonLabel',
|
||||
};
|
||||
const siemData = {
|
||||
title: intl.formatMessage({
|
||||
id: 'home.addData.securitySolution.nameTitle',
|
||||
defaultMessage: 'SIEM + Endpoint Security',
|
||||
}),
|
||||
description: intl.formatMessage({
|
||||
id: 'home.addData.securitySolution.nameDescription',
|
||||
defaultMessage:
|
||||
'Protect hosts, analyze security information and events, hunt threats, automate detections, and create cases.',
|
||||
}),
|
||||
ariaDescribedby: 'aria-describedby.addSiemButtonLabel',
|
||||
};
|
||||
|
||||
const getApmCard = () => (
|
||||
<EuiFlexItem grow={1} className="homAddData__flexItem">
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
className="homAddData__card"
|
||||
titleSize="xs"
|
||||
title={apmData.title}
|
||||
description={<span id={apmData.ariaDescribedby}>{apmData.description}</span>}
|
||||
footer={
|
||||
<EuiButton
|
||||
className="homAddData__button"
|
||||
href="#/tutorial/apm"
|
||||
aria-describedby={apmData.ariaDescribedby}
|
||||
>
|
||||
<FormattedMessage id="home.addData.apm.addApmButtonLabel" defaultMessage="Add APM" />
|
||||
</EuiButton>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
className="homeAddData__flexGroup homAddData__flexTablet"
|
||||
wrap={apmUiEnabled}
|
||||
gutterSize="l"
|
||||
justifyContent="spaceAround"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiFlexItem className="homAddData__cardDivider homAddData__flexItem" grow={3}>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon size="xl" type="logoObservability" className="homAddData__logo" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="home.addData.title.observability"
|
||||
defaultMessage="Observability"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup
|
||||
className="homeAddData__flexGroup"
|
||||
wrap={apmUiEnabled}
|
||||
gutterSize="l"
|
||||
justifyContent="spaceAround"
|
||||
responsive={false}
|
||||
>
|
||||
{apmUiEnabled !== false && getApmCard()}
|
||||
|
||||
<EuiFlexItem grow={1} className="homAddData__flexItem">
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
className="homAddData__card"
|
||||
title={loggingData.title}
|
||||
titleSize="xs"
|
||||
description={
|
||||
<span id={loggingData.ariaDescribedby}>{loggingData.description}</span>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
className="homAddData__button"
|
||||
data-test-subj="logsData"
|
||||
href="#/tutorial_directory/logging"
|
||||
aria-describedby={loggingData.ariaDescribedby}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.logging.addLogDataButtonLabel"
|
||||
defaultMessage="Add log data"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={1} className="homAddData__flexItem">
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
className="homAddData__card"
|
||||
title={metricsData.title}
|
||||
titleSize="xs"
|
||||
description={
|
||||
<span id={metricsData.ariaDescribedby}>{metricsData.description}</span>
|
||||
}
|
||||
footer={
|
||||
<EuiButton
|
||||
className="homAddData__button"
|
||||
href="#/tutorial_directory/metrics"
|
||||
aria-describedby={metricsData.ariaDescribedby}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.metrics.addMetricsDataButtonLabel"
|
||||
defaultMessage="Add metric data"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={1} className="homAddData__flexItem">
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon size="xl" type="logoSecurity" className="homAddData__logo" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
<FormattedMessage id="home.addData.title.security" defaultMessage="Security" />
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
titleSize="xs"
|
||||
className="homAddData__card"
|
||||
title={siemData.title}
|
||||
description={<span id={siemData.ariaDescribedby}>{siemData.description}</span>}
|
||||
footer={
|
||||
<EuiButton
|
||||
className="homAddData__button"
|
||||
href="#/tutorial_directory/security"
|
||||
aria-describedby={siemData.ariaDescribedby}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.securitySolution.addSecurityEventsButtonLabel"
|
||||
defaultMessage="Add events"
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const footerItemClasses = classNames('homAddData__footerItem', {
|
||||
'homAddData__footerItem--highlight': isNewKibanaInstance,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiPanel paddingSize="l">
|
||||
{renderCards()}
|
||||
|
||||
<EuiHorizontalRule />
|
||||
|
||||
<EuiFlexGrid columns={mlEnabled !== false ? 3 : 2}>
|
||||
<EuiFlexItem className={footerItemClasses}>
|
||||
<EuiText size="s">
|
||||
<strong style={{ height: 38 }}>
|
||||
<FormattedMessage
|
||||
id="home.addData.sampleDataTitle"
|
||||
defaultMessage="Add sample data"
|
||||
/>
|
||||
</strong>
|
||||
<EuiLink
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
href="#/tutorial_directory/sampleData"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.sampleDataLink"
|
||||
defaultMessage="Load a data set and a Kibana dashboard"
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
{mlEnabled !== false ? (
|
||||
<EuiFlexItem className={footerItemClasses}>
|
||||
<EuiText size="s">
|
||||
<strong style={{ height: 38 }}>
|
||||
<FormattedMessage
|
||||
id="home.addData.uploadFileTitle"
|
||||
defaultMessage="Upload data from log file"
|
||||
/>
|
||||
</strong>
|
||||
<EuiLink
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
href={`${basePath}/app/ml#/filedatavisualizer`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.uploadFileLink"
|
||||
defaultMessage="Import a CSV, NDJSON, or log file"
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
<EuiFlexItem className={footerItemClasses}>
|
||||
<EuiText size="s">
|
||||
<strong style={{ height: 38 }}>
|
||||
<FormattedMessage
|
||||
id="home.addData.yourDataTitle"
|
||||
defaultMessage="Use Elasticsearch data"
|
||||
/>
|
||||
</strong>
|
||||
<EuiLink
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
href={`${basePath}/app/management/kibana/indexPatterns`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.yourDataLink"
|
||||
defaultMessage="Connect to your Elasticsearch index"
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
||||
AddDataUi.propTypes = {
|
||||
apmUiEnabled: PropTypes.bool.isRequired,
|
||||
mlEnabled: PropTypes.bool.isRequired,
|
||||
isNewKibanaInstance: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export const AddData = injectI18n(AddDataUi);
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* 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 { AddData } from './add_data';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { getServices } from '../kibana_services';
|
||||
|
||||
jest.mock('../kibana_services', () => {
|
||||
const mock = {
|
||||
getBasePath: jest.fn(() => 'path'),
|
||||
};
|
||||
return {
|
||||
getServices: () => mock,
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('render', () => {
|
||||
const component = shallowWithIntl(
|
||||
<AddData.WrappedComponent apmUiEnabled={false} mlEnabled={false} isNewKibanaInstance={false} />
|
||||
);
|
||||
expect(component).toMatchSnapshot(); // eslint-disable-line
|
||||
expect(getServices().getBasePath).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('mlEnabled', () => {
|
||||
const component = shallowWithIntl(
|
||||
<AddData.WrappedComponent apmUiEnabled={true} mlEnabled={true} isNewKibanaInstance={false} />
|
||||
);
|
||||
expect(component).toMatchSnapshot(); // eslint-disable-line
|
||||
expect(getServices().getBasePath).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('apmUiEnabled', () => {
|
||||
const component = shallowWithIntl(
|
||||
<AddData.WrappedComponent apmUiEnabled={true} mlEnabled={false} isNewKibanaInstance={false} />
|
||||
);
|
||||
expect(component).toMatchSnapshot(); // eslint-disable-line
|
||||
expect(getServices().getBasePath).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('isNewKibanaInstance', () => {
|
||||
const component = shallowWithIntl(
|
||||
<AddData.WrappedComponent apmUiEnabled={false} mlEnabled={false} isNewKibanaInstance={true} />
|
||||
);
|
||||
expect(component).toMatchSnapshot(); // eslint-disable-line
|
||||
expect(getServices().getBasePath).toHaveBeenCalledTimes(1);
|
||||
});
|
96
src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap
generated
Normal file
96
src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AddData render 1`] = `
|
||||
<section
|
||||
aria-labelledby="homDataAdd__title"
|
||||
className="homDataAdd"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={1}
|
||||
>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h2
|
||||
id="homDataAdd__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Ingest your data"
|
||||
id="home.addData.sectionTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
flush="right"
|
||||
href="#/tutorial_directory/sampleData"
|
||||
iconType="visTable"
|
||||
size="xs"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Try our sample data"
|
||||
id="home.addData.sampleDataButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
className="homDataAdd__content"
|
||||
>
|
||||
<EuiFlexItem
|
||||
key="home_tutorial_directory"
|
||||
>
|
||||
<Synopsis
|
||||
description="Ingest data from popular apps and services."
|
||||
iconType="indexOpen"
|
||||
id="home_tutorial_directory"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Ingest data"
|
||||
url="/app/home#/tutorial_directory"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
key="ingestManager"
|
||||
>
|
||||
<Synopsis
|
||||
description="Add and manage your fleet of Elastic Agents and integrations."
|
||||
iconType="indexManagementApp"
|
||||
id="ingestManager"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Add Elastic Agent"
|
||||
url="/app/ingestManager"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
key="ml_file_data_visualizer"
|
||||
>
|
||||
<Synopsis
|
||||
description="Import your own CSV, NDJSON, or log file"
|
||||
iconType="document"
|
||||
id="ml_file_data_visualizer"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Upload a file"
|
||||
url="/app/ml#/filedatavisualizer"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
`;
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 { AddData } from './add_data';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
|
||||
jest.mock('../app_navigation_handler', () => {
|
||||
return {
|
||||
createAppNavigationHandler: jest.fn(() => () => {}),
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const addBasePathMock = jest.fn((path: string) => (path ? path : 'path'));
|
||||
|
||||
const mockFeatures = [
|
||||
{
|
||||
category: 'data',
|
||||
description: 'Ingest data from popular apps and services.',
|
||||
homePageSection: 'add_data',
|
||||
icon: 'indexOpen',
|
||||
id: 'home_tutorial_directory',
|
||||
order: 500,
|
||||
path: '/app/home#/tutorial_directory',
|
||||
title: 'Ingest data',
|
||||
},
|
||||
{
|
||||
category: 'admin',
|
||||
description: 'Add and manage your fleet of Elastic Agents and integrations.',
|
||||
homePageSection: 'add_data',
|
||||
icon: 'indexManagementApp',
|
||||
id: 'ingestManager',
|
||||
order: 510,
|
||||
path: '/app/ingestManager',
|
||||
title: 'Add Elastic Agent',
|
||||
},
|
||||
{
|
||||
category: 'data',
|
||||
description: 'Import your own CSV, NDJSON, or log file',
|
||||
homePageSection: 'add_data',
|
||||
icon: 'document',
|
||||
id: 'ml_file_data_visualizer',
|
||||
order: 520,
|
||||
path: '/app/ml#/filedatavisualizer',
|
||||
title: 'Upload a file',
|
||||
},
|
||||
];
|
||||
|
||||
describe('AddData', () => {
|
||||
test('render', () => {
|
||||
const component = shallowWithIntl(
|
||||
<AddData addBasePath={addBasePathMock} features={mockFeatures} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
// @ts-expect-error untyped service
|
||||
import { FeatureCatalogueEntry } from '../../services';
|
||||
import { createAppNavigationHandler } from '../app_navigation_handler';
|
||||
// @ts-expect-error untyped component
|
||||
import { Synopsis } from '../synopsis';
|
||||
|
||||
interface Props {
|
||||
addBasePath: (path: string) => string;
|
||||
features: FeatureCatalogueEntry[];
|
||||
}
|
||||
|
||||
export const AddData: FC<Props> = ({ addBasePath, features }) => (
|
||||
<section className="homDataAdd" aria-labelledby="homDataAdd__title">
|
||||
<EuiFlexGroup alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={1}>
|
||||
<EuiTitle size="s">
|
||||
<h2 id="homDataAdd__title">
|
||||
<FormattedMessage id="home.addData.sectionTitle" defaultMessage="Ingest your data" />
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
iconType="visTable"
|
||||
href="#/tutorial_directory/sampleData"
|
||||
size="xs"
|
||||
flush="right"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.addData.sampleDataButtonLabel"
|
||||
defaultMessage="Try our sample data"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup className="homDataAdd__content">
|
||||
{features.map((feature) => (
|
||||
<EuiFlexItem key={feature.id}>
|
||||
<Synopsis
|
||||
id={feature.id}
|
||||
onClick={createAppNavigationHandler(feature.path)}
|
||||
description={feature.description}
|
||||
iconType={feature.icon}
|
||||
title={feature.title}
|
||||
url={addBasePath(feature.path)}
|
||||
wrapInPanel
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
);
|
||||
|
||||
AddData.propTypes = {
|
||||
addBasePath: PropTypes.func.isRequired,
|
||||
features: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
showOnHomePage: PropTypes.bool.isRequired,
|
||||
category: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
};
|
|
@ -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 './add_data';
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { MouseEvent } from 'react';
|
||||
import { getServices } from '../kibana_services';
|
||||
|
||||
export const createAppNavigationHandler = (targetUrl: string) => (event: MouseEvent) => {
|
||||
|
|
|
@ -115,6 +115,7 @@ export class FeatureDirectory extends React.Component {
|
|||
return (
|
||||
<EuiFlexItem key={directory.id}>
|
||||
<Synopsis
|
||||
id={directory.id}
|
||||
onClick={createAppNavigationHandler(directory.path)}
|
||||
description={directory.description}
|
||||
iconType={directory.icon}
|
||||
|
@ -157,6 +158,7 @@ FeatureDirectory.propTypes = {
|
|||
path: PropTypes.string.isRequired,
|
||||
showOnHomePage: PropTypes.bool.isRequired,
|
||||
category: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
};
|
||||
|
|
|
@ -19,29 +19,22 @@
|
|||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Synopsis } from './synopsis';
|
||||
import { AddData } from './add_data';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiPage,
|
||||
EuiPanel,
|
||||
EuiButtonEmpty,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlexGrid,
|
||||
EuiText,
|
||||
EuiPageBody,
|
||||
EuiScreenReaderOnly,
|
||||
EuiHorizontalRule,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { Welcome } from './welcome';
|
||||
import { getServices } from '../kibana_services';
|
||||
import { FeatureCatalogueCategory } from '../../services';
|
||||
import { getServices } from '../kibana_services';
|
||||
import { AddData } from './add_data';
|
||||
import { createAppNavigationHandler } from './app_navigation_handler';
|
||||
import { ManageData } from './manage_data';
|
||||
import { SolutionsSection } from './solutions_section';
|
||||
import { Welcome } from './welcome';
|
||||
|
||||
const KEY_ENABLE_WELCOME = 'home:welcome:show';
|
||||
|
||||
|
@ -53,6 +46,10 @@ export class Home extends Component {
|
|||
getServices().homeConfig.disableWelcomeScreen ||
|
||||
props.localStorage.getItem(KEY_ENABLE_WELCOME) === 'false'
|
||||
);
|
||||
|
||||
const body = document.querySelector('body');
|
||||
body.classList.add('isHomPage');
|
||||
|
||||
this.state = {
|
||||
// If welcome is enabled, we wait for loading to complete
|
||||
// before rendering. This prevents an annoying flickering
|
||||
|
@ -116,105 +113,144 @@ export class Home extends Component {
|
|||
this._isMounted && this.setState({ isWelcomeEnabled: false });
|
||||
};
|
||||
|
||||
renderDirectories = (category) => {
|
||||
const { addBasePath, directories } = this.props;
|
||||
return directories
|
||||
.filter((directory) => {
|
||||
return directory.showOnHomePage && directory.category === category;
|
||||
})
|
||||
.map((directory) => {
|
||||
return (
|
||||
<EuiFlexItem className="homHome__synopsisItem" key={directory.id}>
|
||||
<Synopsis
|
||||
onClick={createAppNavigationHandler(directory.path)}
|
||||
description={directory.description}
|
||||
iconType={directory.icon}
|
||||
title={directory.title}
|
||||
url={addBasePath(directory.path)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
findDirectoryById = (id) => this.props.directories.find((directory) => directory.id === id);
|
||||
|
||||
getFeaturesByCategory = (category) =>
|
||||
this.props.directories
|
||||
.filter((directory) => directory.showOnHomePage && directory.category === category)
|
||||
.sort((directoryA, directoryB) => directoryA.order - directoryB.order);
|
||||
|
||||
renderNormal() {
|
||||
const { apmUiEnabled, mlEnabled } = this.props;
|
||||
const { addBasePath, solutions } = this.props;
|
||||
|
||||
const devTools = this.findDirectoryById('console');
|
||||
const stackManagement = this.findDirectoryById('stack-management');
|
||||
const advancedSettings = this.findDirectoryById('advanced_settings');
|
||||
|
||||
const addDataFeatures = this.getFeaturesByCategory(FeatureCatalogueCategory.DATA);
|
||||
const manageDataFeatures = this.getFeaturesByCategory(FeatureCatalogueCategory.ADMIN);
|
||||
|
||||
// Show card for console if none of the manage data plugins are available, most likely in OSS
|
||||
if (manageDataFeatures.length < 1 && devTools) {
|
||||
manageDataFeatures.push(devTools);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPage restrictWidth={1200} data-test-subj="homeApp">
|
||||
<EuiPageBody className="eui-displayBlock">
|
||||
<EuiScreenReaderOnly>
|
||||
<h1>
|
||||
<FormattedMessage id="home.welcomeHomePageHeader" defaultMessage="Kibana home" />
|
||||
</h1>
|
||||
</EuiScreenReaderOnly>
|
||||
|
||||
<AddData
|
||||
apmUiEnabled={apmUiEnabled}
|
||||
mlEnabled={mlEnabled}
|
||||
isNewKibanaInstance={this.state.isNewKibanaInstance}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel paddingSize="l">
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="home.directories.visualize.nameTitle"
|
||||
defaultMessage="Visualize and Explore Data"
|
||||
/>
|
||||
</h2>
|
||||
<main aria-labelledby="homHeader__title" className="homWrapper" data-test-subj="homeApp">
|
||||
<header
|
||||
className={`homHeader ${
|
||||
solutions.length ? 'homHeader--hasSolutions' : 'homHeader--noSolutions'
|
||||
}`}
|
||||
>
|
||||
<div className="homHeader__inner">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="m">
|
||||
<h1 id="homHeader__title">
|
||||
<FormattedMessage id="home.pageHeader.title" defaultMessage="Home" />
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGrid columns={2} gutterSize="s">
|
||||
{this.renderDirectories(FeatureCatalogueCategory.DATA)}
|
||||
</EuiFlexGrid>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup className="homHeader__actions">
|
||||
<EuiFlexItem className="homHeader__actionItem">
|
||||
<EuiButtonEmpty href="#/tutorial_directory" iconType="indexOpen">
|
||||
{i18n.translate('home.pageHeader.addDataButtonLabel', {
|
||||
defaultMessage: 'Add data',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
|
||||
{stackManagement ? (
|
||||
<EuiFlexItem className="homHeader__actionItem">
|
||||
<EuiButtonEmpty
|
||||
onClick={createAppNavigationHandler(stackManagement.path)}
|
||||
iconType="gear"
|
||||
>
|
||||
{i18n.translate('home.pageHeader.stackManagementButtonLabel', {
|
||||
defaultMessage: 'Manage',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
||||
{devTools ? (
|
||||
<EuiFlexItem className="homHeader__actionItem">
|
||||
<EuiButtonEmpty
|
||||
onClick={createAppNavigationHandler(devTools.path)}
|
||||
iconType="wrench"
|
||||
>
|
||||
{i18n.translate('home.pageHeader.devToolsButtonLabel', {
|
||||
defaultMessage: 'Dev tools',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="homContent">
|
||||
{solutions.length && <SolutionsSection addBasePath={addBasePath} solutions={solutions} />}
|
||||
|
||||
<EuiFlexGroup
|
||||
className={`homData ${
|
||||
addDataFeatures.length === 1 && manageDataFeatures.length === 1
|
||||
? 'homData--compressed'
|
||||
: 'homData--expanded'
|
||||
}`}
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<AddData addBasePath={addBasePath} features={addDataFeatures} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<EuiPanel paddingSize="l">
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="home.directories.manage.nameTitle"
|
||||
defaultMessage="Manage and Administer the Elastic Stack"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGrid columns={2}>
|
||||
{this.renderDirectories(FeatureCatalogueCategory.ADMIN)}
|
||||
</EuiFlexGrid>
|
||||
</EuiPanel>
|
||||
<ManageData addBasePath={addBasePath} features={manageDataFeatures} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
<EuiHorizontalRule margin="xl" aria-hidden="true" />
|
||||
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false} className="eui-textCenter">
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>
|
||||
<footer className="homFooter">
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
{advancedSettings && (
|
||||
<EuiButtonEmpty
|
||||
iconType="home"
|
||||
onClick={createAppNavigationHandler(
|
||||
'/app/management/kibana/settings#defaultRoute'
|
||||
)}
|
||||
size="xs"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.changeHomeRouteLink"
|
||||
defaultMessage="Display a different page on log in"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="allPlugins"
|
||||
href="#/feature_directory"
|
||||
size="xs"
|
||||
flush="right"
|
||||
iconType="apps"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="home.directories.notFound.description"
|
||||
defaultMessage="Didn’t find what you were looking for?"
|
||||
id="home.appDirectory.appDirectoryButtonLabel"
|
||||
defaultMessage="View app directory"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiButton data-test-subj="allPlugins" href="#/feature_directory">
|
||||
<FormattedMessage
|
||||
id="home.directories.notFound.viewFullButtonLabel"
|
||||
defaultMessage="View full directory of Kibana plugins"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -260,13 +296,23 @@ Home.propTypes = {
|
|||
path: PropTypes.string.isRequired,
|
||||
showOnHomePage: PropTypes.bool.isRequired,
|
||||
category: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
solutions: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
descriptions: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
apmUiEnabled: PropTypes.bool.isRequired,
|
||||
find: PropTypes.func.isRequired,
|
||||
localStorage: PropTypes.object.isRequired,
|
||||
urlBasePath: PropTypes.string.isRequired,
|
||||
mlEnabled: PropTypes.bool.isRequired,
|
||||
telemetry: PropTypes.shape({
|
||||
telemetryService: PropTypes.any,
|
||||
telemetryNotifications: PropTypes.any,
|
||||
|
|
|
@ -41,6 +41,7 @@ describe('home', () => {
|
|||
beforeEach(() => {
|
||||
defaultProps = {
|
||||
directories: [],
|
||||
solutions: [],
|
||||
apmUiEnabled: true,
|
||||
mlEnabled: true,
|
||||
kibanaVersion: '99.2.1',
|
||||
|
@ -92,8 +93,96 @@ describe('home', () => {
|
|||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('header', () => {
|
||||
test('render', async () => {
|
||||
const component = await renderHome();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show "Manage" link if stack management is available', async () => {
|
||||
const directoryEntry = {
|
||||
id: 'stack-management',
|
||||
title: 'Management',
|
||||
description: 'Your center console for managing the Elastic Stack.',
|
||||
icon: 'managementApp',
|
||||
path: 'management_landing_page',
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
showOnHomePage: false,
|
||||
};
|
||||
|
||||
const component = await renderHome({
|
||||
directories: [directoryEntry],
|
||||
});
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show "Dev tools" link if console is available', async () => {
|
||||
const directoryEntry = {
|
||||
id: 'console',
|
||||
title: 'Console',
|
||||
description: 'Skip cURL and use a JSON interface to work with your data in Console.',
|
||||
icon: 'consoleApp',
|
||||
path: 'path-to-dev-tools',
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
showOnHomePage: false,
|
||||
};
|
||||
|
||||
const component = await renderHome({
|
||||
directories: [directoryEntry],
|
||||
});
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('directories', () => {
|
||||
test('should render DATA directory entry in "Explore Data" panel', async () => {
|
||||
test('should render solutions in the "solution section"', async () => {
|
||||
const solutionEntry1 = {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
subtitle: 'Visualize & analyze',
|
||||
descriptions: ['Analyze data in dashboards'],
|
||||
icon: 'logoKibana',
|
||||
path: 'kibana_landing_page',
|
||||
order: 1,
|
||||
};
|
||||
const solutionEntry2 = {
|
||||
id: 'solution-2',
|
||||
title: 'Solution two',
|
||||
subtitle: 'Subtitle for solution two',
|
||||
descriptions: ['Example use case'],
|
||||
icon: 'empty',
|
||||
path: 'path-to-solution-two',
|
||||
order: 2,
|
||||
};
|
||||
const solutionEntry3 = {
|
||||
id: 'solution-3',
|
||||
title: 'Solution three',
|
||||
subtitle: 'Subtitle for solution three',
|
||||
descriptions: ['Example use case'],
|
||||
icon: 'empty',
|
||||
path: 'path-to-solution-three',
|
||||
order: 3,
|
||||
};
|
||||
const solutionEntry4 = {
|
||||
id: 'solution-4',
|
||||
title: 'Solution four',
|
||||
subtitle: 'Subtitle for solution four',
|
||||
descriptions: ['Example use case'],
|
||||
icon: 'empty',
|
||||
path: 'path-to-solution-four',
|
||||
order: 4,
|
||||
};
|
||||
|
||||
const component = await renderHome({
|
||||
solutions: [solutionEntry1, solutionEntry2, solutionEntry3, solutionEntry4],
|
||||
});
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render DATA directory entry in "Ingest your data" panel', async () => {
|
||||
const directoryEntry = {
|
||||
id: 'dashboard',
|
||||
title: 'Dashboard',
|
||||
|
@ -111,7 +200,7 @@ describe('home', () => {
|
|||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render ADMIN directory entry in "Manage" panel', async () => {
|
||||
test('should render ADMIN directory entry in "Manage your data" panel', async () => {
|
||||
const directoryEntry = {
|
||||
id: 'index_patterns',
|
||||
title: 'Index Patterns',
|
||||
|
@ -148,6 +237,26 @@ describe('home', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('change home route', () => {
|
||||
test('should render a link to change the default route in advanced settings if advanced settings is enabled', async () => {
|
||||
const component = await renderHome({
|
||||
directories: [
|
||||
{
|
||||
description: 'Change your settings',
|
||||
icon: 'gear',
|
||||
id: 'advanced_settings',
|
||||
path: 'path-to-advanced_settings',
|
||||
showOnHomePage: false,
|
||||
title: 'Advanced settings',
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('welcome', () => {
|
||||
test('should show the welcome screen if enabled, and there are no index patterns defined', async () => {
|
||||
defaultProps.localStorage.getItem = sinon.spy(() => 'true');
|
||||
|
|
|
@ -38,7 +38,7 @@ const RedirectToDefaultApp = () => {
|
|||
return null;
|
||||
};
|
||||
|
||||
export function HomeApp({ directories }) {
|
||||
export function HomeApp({ directories, solutions }) {
|
||||
const {
|
||||
savedObjectsClient,
|
||||
getBasePath,
|
||||
|
@ -48,8 +48,6 @@ export function HomeApp({ directories }) {
|
|||
} = getServices();
|
||||
const environment = environmentService.getEnvironment();
|
||||
const isCloudEnabled = environment.cloud;
|
||||
const mlEnabled = environment.ml;
|
||||
const apmUiEnabled = environment.apmUi;
|
||||
|
||||
const renderTutorialDirectory = (props) => {
|
||||
return (
|
||||
|
@ -87,8 +85,7 @@ export function HomeApp({ directories }) {
|
|||
<Home
|
||||
addBasePath={addBasePath}
|
||||
directories={directories}
|
||||
apmUiEnabled={apmUiEnabled}
|
||||
mlEnabled={mlEnabled}
|
||||
solutions={solutions}
|
||||
find={savedObjectsClient.find}
|
||||
localStorage={localStorage}
|
||||
urlBasePath={getBasePath()}
|
||||
|
@ -112,6 +109,18 @@ HomeApp.propTypes = {
|
|||
path: PropTypes.string.isRequired,
|
||||
showOnHomePage: PropTypes.bool.isRequired,
|
||||
category: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
solutions: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
descriptions: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
};
|
||||
|
|
91
src/plugins/home/public/application/components/manage_data/__snapshots__/manage_data.test.tsx.snap
generated
Normal file
91
src/plugins/home/public/application/components/manage_data/__snapshots__/manage_data.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ManageData render 1`] = `
|
||||
<Fragment>
|
||||
<EuiHorizontalRule
|
||||
aria-hidden="true"
|
||||
margin="xl"
|
||||
/>
|
||||
<section
|
||||
aria-labelledby="homDataManage__title"
|
||||
className="homDataManage"
|
||||
>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h2
|
||||
id="homDataManage__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Manage your data"
|
||||
id="home.manageData.sectionTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
className="homDataManage__content"
|
||||
>
|
||||
<EuiFlexItem
|
||||
key="security"
|
||||
>
|
||||
<Synopsis
|
||||
description="Control who has access and what tasks they can perform."
|
||||
iconType="securityApp"
|
||||
id="security"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Protect your data"
|
||||
url="path-to-security-roles"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
key="monitoring"
|
||||
>
|
||||
<Synopsis
|
||||
description="Track the real-time health and performance of your deployment."
|
||||
iconType="monitoringApp"
|
||||
id="monitoring"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Monitor the stack"
|
||||
url="path-to-monitoring"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
key="snapshot_restore"
|
||||
>
|
||||
<Synopsis
|
||||
description="Save snapshots to a backup repository, and restore to recover index and cluster state."
|
||||
iconType="storage"
|
||||
id="snapshot_restore"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Store & recover backups"
|
||||
url="path-to-snapshot-restore"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
key="index_lifecycle_management"
|
||||
>
|
||||
<Synopsis
|
||||
description="Define lifecycle policies to automatically perform operations as an index ages."
|
||||
iconType="indexSettings"
|
||||
id="index_lifecycle_management"
|
||||
isBeta={false}
|
||||
onClick={[Function]}
|
||||
title="Manage index lifecycles"
|
||||
url="path-to-index-lifecycle-management"
|
||||
wrapInPanel={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
</Fragment>
|
||||
`;
|
|
@ -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 './manage_data';
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 { ManageData } from './manage_data';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
|
||||
jest.mock('../app_navigation_handler', () => {
|
||||
return {
|
||||
createAppNavigationHandler: jest.fn(() => () => {}),
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const addBasePathMock = jest.fn((path: string) => (path ? path : 'path'));
|
||||
|
||||
const mockFeatures = [
|
||||
{
|
||||
category: 'admin',
|
||||
description: 'Control who has access and what tasks they can perform.',
|
||||
homePageSection: 'manage_data',
|
||||
icon: 'securityApp',
|
||||
id: 'security',
|
||||
order: 600,
|
||||
path: 'path-to-security-roles',
|
||||
title: 'Protect your data',
|
||||
showOnHomePage: true,
|
||||
},
|
||||
{
|
||||
category: 'admin',
|
||||
description: 'Track the real-time health and performance of your deployment.',
|
||||
homePageSection: 'manage_data',
|
||||
icon: 'monitoringApp',
|
||||
id: 'monitoring',
|
||||
order: 610,
|
||||
path: 'path-to-monitoring',
|
||||
title: 'Monitor the stack',
|
||||
showOnHomePage: true,
|
||||
},
|
||||
{
|
||||
category: 'admin',
|
||||
description:
|
||||
'Save snapshots to a backup repository, and restore to recover index and cluster state.',
|
||||
homePageSection: 'manage_data',
|
||||
icon: 'storage',
|
||||
id: 'snapshot_restore',
|
||||
order: 630,
|
||||
path: 'path-to-snapshot-restore',
|
||||
title: 'Store & recover backups',
|
||||
showOnHomePage: true,
|
||||
},
|
||||
{
|
||||
category: 'admin',
|
||||
description: 'Define lifecycle policies to automatically perform operations as an index ages.',
|
||||
homePageSection: 'manage_data',
|
||||
icon: 'indexSettings',
|
||||
id: 'index_lifecycle_management',
|
||||
order: 640,
|
||||
path: 'path-to-index-lifecycle-management',
|
||||
title: 'Manage index lifecycles',
|
||||
showOnHomePage: true,
|
||||
},
|
||||
];
|
||||
|
||||
describe('ManageData', () => {
|
||||
test('render', () => {
|
||||
const component = shallowWithIntl(
|
||||
<ManageData addBasePath={addBasePathMock} features={mockFeatures} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFlexGroup, EuiHorizontalRule, EuiSpacer, EuiTitle, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
// @ts-expect-error untyped service
|
||||
import { FeatureCatalogueEntry } from '../../services';
|
||||
import { createAppNavigationHandler } from '../app_navigation_handler';
|
||||
// @ts-expect-error untyped component
|
||||
import { Synopsis } from '../synopsis';
|
||||
|
||||
interface Props {
|
||||
addBasePath: (path: string) => string;
|
||||
features: FeatureCatalogueEntry[];
|
||||
}
|
||||
|
||||
export const ManageData: FC<Props> = ({ addBasePath, features }) => (
|
||||
<>
|
||||
{features.length > 1 && <EuiHorizontalRule margin="xl" aria-hidden="true" />}
|
||||
|
||||
<section className="homDataManage" aria-labelledby="homDataManage__title">
|
||||
<EuiTitle size="s">
|
||||
<h2 id="homDataManage__title">
|
||||
<FormattedMessage id="home.manageData.sectionTitle" defaultMessage="Manage your data" />
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup className="homDataManage__content">
|
||||
{features.map((feature) => (
|
||||
<EuiFlexItem key={feature.id}>
|
||||
<Synopsis
|
||||
id={feature.id}
|
||||
onClick={createAppNavigationHandler(feature.path)}
|
||||
description={feature.description}
|
||||
iconType={feature.icon}
|
||||
title={feature.title}
|
||||
url={addBasePath(feature.path)}
|
||||
wrapInPanel
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
|
||||
ManageData.propTypes = {
|
||||
addBasePath: PropTypes.func.isRequired,
|
||||
features: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
showOnHomePage: PropTypes.bool.isRequired,
|
||||
category: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SolutionPanel renders the solution panel for the given solution 1`] = `
|
||||
<EuiFlexItem
|
||||
className="homSolutions__group homSolutions__group--single homSolutions__item"
|
||||
grow={1}
|
||||
key="kibana"
|
||||
>
|
||||
<a
|
||||
className="homSolutionPanel homSolutionPanel--kibana"
|
||||
href="kibana_landing_page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<EuiPanel
|
||||
paddingSize="none"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="homSolutionPanel__header"
|
||||
grow={1}
|
||||
>
|
||||
<SolutionTitle
|
||||
iconType="logoKibana"
|
||||
subtitle="Visualize & analyze"
|
||||
title="Kibana"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
className="homSolutionPanel__content"
|
||||
grow={1}
|
||||
>
|
||||
<EuiText
|
||||
key="Analyze data in dashboards"
|
||||
size="s"
|
||||
>
|
||||
<p>
|
||||
Analyze data in dashboards
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</a>
|
||||
</EuiFlexItem>
|
||||
`;
|
|
@ -0,0 +1,41 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SolutionTitle renders the title section of the solution panel 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
gutterSize="none"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="eui-textCenter"
|
||||
>
|
||||
<EuiToken
|
||||
className="homSolutionPanel__icon"
|
||||
fill="light"
|
||||
iconType="logoKibana"
|
||||
shape="circle"
|
||||
size="l"
|
||||
/>
|
||||
<EuiTitle
|
||||
className="eui-textInheritColor"
|
||||
size="s"
|
||||
>
|
||||
<h3>
|
||||
Kibana
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiText
|
||||
size="s"
|
||||
>
|
||||
<p
|
||||
className="homSolutionPanel__subtitle"
|
||||
>
|
||||
Visualize & analyze
|
||||
|
||||
<EuiIcon
|
||||
type="sortRight"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
`;
|
|
@ -0,0 +1,288 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SolutionsSection only renders a spacer if no solutions are available 1`] = `
|
||||
<Fragment>
|
||||
<section
|
||||
aria-labelledby="homSolutions__title"
|
||||
className="homSolutions"
|
||||
>
|
||||
<EuiScreenReaderOnly>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h2
|
||||
id="homSolutions__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick your solution"
|
||||
id="home.solutionsSection.sectionTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiFlexGroup
|
||||
className="homSolutions__content"
|
||||
justifyContent="spaceAround"
|
||||
/>
|
||||
</section>
|
||||
<EuiHorizontalRule
|
||||
aria-hidden="true"
|
||||
margin="xl"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`SolutionsSection renders a single solution 1`] = `
|
||||
<Fragment>
|
||||
<section
|
||||
aria-labelledby="homSolutions__title"
|
||||
className="homSolutions"
|
||||
>
|
||||
<EuiScreenReaderOnly>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h2
|
||||
id="homSolutions__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick your solution"
|
||||
id="home.solutionsSection.sectionTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiFlexGroup
|
||||
className="homSolutions__content"
|
||||
justifyContent="spaceAround"
|
||||
>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Analyze data in dashboards",
|
||||
],
|
||||
"icon": "logoKibana",
|
||||
"id": "kibana",
|
||||
"order": 1,
|
||||
"path": "kibana_landing_page",
|
||||
"subtitle": "Visualize & analyze",
|
||||
"title": "Kibana",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
<EuiHorizontalRule
|
||||
aria-hidden="true"
|
||||
margin="xl"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`SolutionsSection renders multiple solutions in a single column when Kibana apps are not enabled 1`] = `
|
||||
<Fragment>
|
||||
<section
|
||||
aria-labelledby="homSolutions__title"
|
||||
className="homSolutions"
|
||||
>
|
||||
<EuiScreenReaderOnly>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h2
|
||||
id="homSolutions__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick your solution"
|
||||
id="home.solutionsSection.sectionTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiFlexGroup
|
||||
className="homSolutions__content"
|
||||
justifyContent="spaceAround"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="homSolutions__group homSolutions__group--multiple"
|
||||
grow={1}
|
||||
>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
key="solution-2"
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Example use case",
|
||||
],
|
||||
"icon": "empty",
|
||||
"id": "solution-2",
|
||||
"order": 2,
|
||||
"path": "path-to-solution-two",
|
||||
"subtitle": "Subtitle for solution two",
|
||||
"title": "Solution two",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
key="solution-3"
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Example use case",
|
||||
],
|
||||
"icon": "empty",
|
||||
"id": "solution-3",
|
||||
"order": 3,
|
||||
"path": "path-to-solution-three",
|
||||
"subtitle": "Subtitle for solution three",
|
||||
"title": "Solution three",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
key="solution-4"
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Example use case",
|
||||
],
|
||||
"icon": "empty",
|
||||
"id": "solution-4",
|
||||
"order": 4,
|
||||
"path": "path-to-solution-four",
|
||||
"subtitle": "Subtitle for solution four",
|
||||
"title": "Solution four",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
<EuiHorizontalRule
|
||||
aria-hidden="true"
|
||||
margin="xl"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`SolutionsSection renders multiple solutions in two columns with Kibana in its own column 1`] = `
|
||||
<Fragment>
|
||||
<section
|
||||
aria-labelledby="homSolutions__title"
|
||||
className="homSolutions"
|
||||
>
|
||||
<EuiScreenReaderOnly>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h2
|
||||
id="homSolutions__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick your solution"
|
||||
id="home.solutionsSection.sectionTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiFlexGroup
|
||||
className="homSolutions__content"
|
||||
justifyContent="spaceAround"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="homSolutions__group homSolutions__group--multiple"
|
||||
grow={1}
|
||||
>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
key="solution-2"
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Example use case",
|
||||
],
|
||||
"icon": "empty",
|
||||
"id": "solution-2",
|
||||
"order": 2,
|
||||
"path": "path-to-solution-two",
|
||||
"subtitle": "Subtitle for solution two",
|
||||
"title": "Solution two",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
key="solution-3"
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Example use case",
|
||||
],
|
||||
"icon": "empty",
|
||||
"id": "solution-3",
|
||||
"order": 3,
|
||||
"path": "path-to-solution-three",
|
||||
"subtitle": "Subtitle for solution three",
|
||||
"title": "Solution three",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
key="solution-4"
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Example use case",
|
||||
],
|
||||
"icon": "empty",
|
||||
"id": "solution-4",
|
||||
"order": 4,
|
||||
"path": "path-to-solution-four",
|
||||
"subtitle": "Subtitle for solution four",
|
||||
"title": "Solution four",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<SolutionPanel
|
||||
addBasePath={[Function]}
|
||||
solution={
|
||||
Object {
|
||||
"descriptions": Array [
|
||||
"Analyze data in dashboards",
|
||||
],
|
||||
"icon": "logoKibana",
|
||||
"id": "kibana",
|
||||
"order": 1,
|
||||
"path": "kibana_landing_page",
|
||||
"subtitle": "Visualize & analyze",
|
||||
"title": "Kibana",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
<EuiHorizontalRule
|
||||
aria-hidden="true"
|
||||
margin="xl"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
|
@ -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 './solutions_section';
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { SolutionPanel } from './solution_panel';
|
||||
|
||||
const solutionEntry = {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
subtitle: 'Visualize & analyze',
|
||||
descriptions: ['Analyze data in dashboards'],
|
||||
icon: 'logoKibana',
|
||||
path: 'kibana_landing_page',
|
||||
order: 1,
|
||||
};
|
||||
|
||||
const addBasePathMock = (path: string) => (path ? path : 'path');
|
||||
|
||||
describe('SolutionPanel', () => {
|
||||
test('renders the solution panel for the given solution', () => {
|
||||
const component = shallow(
|
||||
<SolutionPanel addBasePath={addBasePathMock} solution={solutionEntry} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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, { FC } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { FeatureCatalogueSolution } from '../../../';
|
||||
import { createAppNavigationHandler } from '../app_navigation_handler';
|
||||
import { SolutionTitle } from './solution_title';
|
||||
|
||||
const getDescriptionText = (description: string): JSX.Element => (
|
||||
<EuiText size="s" key={`${description}`}>
|
||||
<p>{description}</p>
|
||||
</EuiText>
|
||||
);
|
||||
|
||||
const addSpacersBetweenElementsReducer = (
|
||||
acc: JSX.Element[],
|
||||
element: JSX.Element,
|
||||
index: number,
|
||||
elements: JSX.Element[]
|
||||
) => {
|
||||
acc.push(element);
|
||||
if (index < elements.length - 1) {
|
||||
acc.push(<EuiSpacer key={`homeSolutionsPanel__UseCaseSpacer${index}`} size="m" />);
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
|
||||
const getDescriptions = (descriptions: string[]) =>
|
||||
descriptions.map(getDescriptionText).reduce<JSX.Element[]>(addSpacersBetweenElementsReducer, []);
|
||||
|
||||
interface Props {
|
||||
addBasePath: (path: string) => string;
|
||||
solution: FeatureCatalogueSolution;
|
||||
}
|
||||
|
||||
export const SolutionPanel: FC<Props> = ({ addBasePath, solution }) => (
|
||||
<EuiFlexItem
|
||||
key={solution.id}
|
||||
className={`${
|
||||
solution.id === 'kibana' ? 'homSolutions__group homSolutions__group--single' : ''
|
||||
} homSolutions__item`}
|
||||
grow={1}
|
||||
>
|
||||
<a
|
||||
className={`homSolutionPanel homSolutionPanel--${solution.id}`}
|
||||
href={addBasePath(solution.path)}
|
||||
onClick={createAppNavigationHandler(solution.path)}
|
||||
>
|
||||
<EuiPanel paddingSize="none">
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem grow={1} className={`homSolutionPanel__header`}>
|
||||
<SolutionTitle
|
||||
iconType={solution.icon}
|
||||
title={solution.title}
|
||||
subtitle={solution.subtitle}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={1} className="homSolutionPanel__content">
|
||||
{getDescriptions(solution.descriptions)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</a>
|
||||
</EuiFlexItem>
|
||||
);
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { SolutionTitle } from './solution_title';
|
||||
|
||||
const solutionEntry = {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
subtitle: 'Visualize & analyze',
|
||||
descriptions: ['Analyze data in dashboards'],
|
||||
icon: 'logoKibana',
|
||||
path: 'kibana_landing_page',
|
||||
order: 1,
|
||||
};
|
||||
|
||||
describe('SolutionTitle', () => {
|
||||
test('renders the title section of the solution panel', () => {
|
||||
const component = shallow(
|
||||
<SolutionTitle
|
||||
title={solutionEntry.title}
|
||||
subtitle={solutionEntry.subtitle}
|
||||
iconType={solutionEntry.icon}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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, { FC } from 'react';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiToken,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiIcon,
|
||||
IconType,
|
||||
} from '@elastic/eui';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
iconType: IconType;
|
||||
}
|
||||
|
||||
export const SolutionTitle: FC<Props> = ({ title, subtitle, iconType }) => (
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center">
|
||||
<EuiFlexItem className="eui-textCenter">
|
||||
<EuiToken
|
||||
iconType={iconType}
|
||||
shape="circle"
|
||||
fill="light"
|
||||
size="l"
|
||||
className="homSolutionPanel__icon"
|
||||
/>
|
||||
|
||||
<EuiTitle className="eui-textInheritColor" size="s">
|
||||
<h3>{title}</h3>
|
||||
</EuiTitle>
|
||||
|
||||
<EuiText size="s">
|
||||
<p className="homSolutionPanel__subtitle">
|
||||
{subtitle} <EuiIcon type="sortRight" />
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { SolutionsSection } from './solutions_section';
|
||||
|
||||
const solutionEntry1 = {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
subtitle: 'Visualize & analyze',
|
||||
descriptions: ['Analyze data in dashboards'],
|
||||
icon: 'logoKibana',
|
||||
path: 'kibana_landing_page',
|
||||
order: 1,
|
||||
};
|
||||
const solutionEntry2 = {
|
||||
id: 'solution-2',
|
||||
title: 'Solution two',
|
||||
subtitle: 'Subtitle for solution two',
|
||||
descriptions: ['Example use case'],
|
||||
icon: 'empty',
|
||||
path: 'path-to-solution-two',
|
||||
order: 2,
|
||||
};
|
||||
const solutionEntry3 = {
|
||||
id: 'solution-3',
|
||||
title: 'Solution three',
|
||||
subtitle: 'Subtitle for solution three',
|
||||
descriptions: ['Example use case'],
|
||||
icon: 'empty',
|
||||
path: 'path-to-solution-three',
|
||||
order: 3,
|
||||
};
|
||||
const solutionEntry4 = {
|
||||
id: 'solution-4',
|
||||
title: 'Solution four',
|
||||
subtitle: 'Subtitle for solution four',
|
||||
descriptions: ['Example use case'],
|
||||
icon: 'empty',
|
||||
path: 'path-to-solution-four',
|
||||
order: 4,
|
||||
};
|
||||
|
||||
const addBasePathMock = (path: string) => (path ? path : 'path');
|
||||
|
||||
describe('SolutionsSection', () => {
|
||||
test('only renders a spacer if no solutions are available', () => {
|
||||
const component = shallow(<SolutionsSection addBasePath={addBasePathMock} solutions={[]} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders a single solution', () => {
|
||||
const component = shallow(
|
||||
<SolutionsSection addBasePath={addBasePathMock} solutions={[solutionEntry1]} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders multiple solutions in two columns with Kibana in its own column', () => {
|
||||
const component = shallow(
|
||||
<SolutionsSection
|
||||
addBasePath={addBasePathMock}
|
||||
solutions={[solutionEntry1, solutionEntry2, solutionEntry3, solutionEntry4]}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
test('renders multiple solutions in a single column when Kibana apps are not enabled', () => {
|
||||
const component = shallow(
|
||||
<SolutionsSection
|
||||
addBasePath={addBasePathMock}
|
||||
solutions={[solutionEntry2, solutionEntry3, solutionEntry4]}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiScreenReaderOnly,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { SolutionPanel } from './solution_panel';
|
||||
import { FeatureCatalogueSolution } from '../../../';
|
||||
|
||||
const sortByOrder = (
|
||||
{ order: orderA = 0 }: FeatureCatalogueSolution,
|
||||
{ order: orderB = 0 }: FeatureCatalogueSolution
|
||||
) => orderA - orderB;
|
||||
|
||||
interface Props {
|
||||
addBasePath: (path: string) => string;
|
||||
solutions: FeatureCatalogueSolution[];
|
||||
}
|
||||
|
||||
export const SolutionsSection: FC<Props> = ({ addBasePath, solutions }) => {
|
||||
// Separate Kibana from other solutions
|
||||
const kibana = solutions.find(({ id }) => id === 'kibana');
|
||||
solutions = solutions.sort(sortByOrder).filter(({ id }) => id !== 'kibana');
|
||||
|
||||
return (
|
||||
<>
|
||||
<section aria-labelledby="homSolutions__title" className="homSolutions">
|
||||
<EuiScreenReaderOnly>
|
||||
<EuiTitle size="s">
|
||||
<h2 id="homSolutions__title">
|
||||
<FormattedMessage
|
||||
id="home.solutionsSection.sectionTitle"
|
||||
defaultMessage="Pick your solution"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiScreenReaderOnly>
|
||||
|
||||
<EuiFlexGroup className="homSolutions__content" justifyContent="spaceAround">
|
||||
{solutions.length ? (
|
||||
<EuiFlexItem grow={1} className="homSolutions__group homSolutions__group--multiple">
|
||||
<EuiFlexGroup direction="column">
|
||||
{solutions.map((solution) => (
|
||||
<SolutionPanel key={solution.id} solution={solution} addBasePath={addBasePath} />
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{kibana ? <SolutionPanel solution={kibana} addBasePath={addBasePath} /> : null}
|
||||
</EuiFlexGroup>
|
||||
</section>
|
||||
|
||||
<EuiHorizontalRule margin="xl" aria-hidden="true" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
SolutionsSection.propTypes = {
|
||||
solutions: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
descriptions: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
order: PropTypes.number,
|
||||
})
|
||||
),
|
||||
};
|
|
@ -24,6 +24,7 @@ import classNames from 'classnames';
|
|||
import { EuiCard, EuiIcon } from '@elastic/eui';
|
||||
|
||||
export function Synopsis({
|
||||
id,
|
||||
description,
|
||||
iconUrl,
|
||||
iconType,
|
||||
|
@ -54,8 +55,9 @@ export function Synopsis({
|
|||
description={description}
|
||||
onClick={onClick}
|
||||
href={url}
|
||||
data-test-subj={`homeSynopsisLink${title.toLowerCase()}`}
|
||||
data-test-subj={`homeSynopsisLink${id.toLowerCase()}`}
|
||||
betaBadgeLabel={isBeta ? 'Beta' : null}
|
||||
titleElement="h3"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { Synopsis } from './synopsis';
|
|||
test('render', () => {
|
||||
const component = shallow(
|
||||
<Synopsis
|
||||
id={'tutorial'}
|
||||
description="this is a great tutorial about..."
|
||||
title="Great tutorial"
|
||||
url="link_to_item"
|
||||
|
@ -37,6 +38,7 @@ describe('props', () => {
|
|||
test('iconType', () => {
|
||||
const component = shallow(
|
||||
<Synopsis
|
||||
id={'tutorial'}
|
||||
description="this is a great tutorial about..."
|
||||
title="Great tutorial"
|
||||
url="link_to_item"
|
||||
|
@ -49,6 +51,7 @@ describe('props', () => {
|
|||
test('iconUrl', () => {
|
||||
const component = shallow(
|
||||
<Synopsis
|
||||
id={'tutorial'}
|
||||
description="this is a great tutorial about..."
|
||||
title="Great tutorial"
|
||||
url="link_to_item"
|
||||
|
@ -61,6 +64,7 @@ describe('props', () => {
|
|||
test('isBeta', () => {
|
||||
const component = shallow(
|
||||
<Synopsis
|
||||
id={'tutorial'}
|
||||
description="this is a great tutorial about..."
|
||||
title="Great tutorial"
|
||||
url="link_to_item"
|
||||
|
|
|
@ -136,6 +136,7 @@ class TutorialDirectoryUi extends React.Component {
|
|||
}
|
||||
|
||||
return {
|
||||
id: tutorialConfig.id,
|
||||
category: tutorialConfig.category,
|
||||
icon: icon,
|
||||
name: tutorialConfig.name,
|
||||
|
@ -149,6 +150,7 @@ class TutorialDirectoryUi extends React.Component {
|
|||
|
||||
// Add card for sample data that only gets show in "all" tab
|
||||
tutorialCards.push({
|
||||
id: 'sample_data',
|
||||
name: this.props.intl.formatMessage({
|
||||
id: 'home.tutorial.card.sampleDataTitle',
|
||||
defaultMessage: 'Sample Data',
|
||||
|
@ -214,6 +216,7 @@ class TutorialDirectoryUi extends React.Component {
|
|||
return (
|
||||
<EuiFlexItem key={tutorial.name}>
|
||||
<Synopsis
|
||||
id={tutorial.id}
|
||||
iconType={tutorial.icon}
|
||||
description={tutorial.description}
|
||||
title={tutorial.name}
|
||||
|
|
|
@ -27,6 +27,7 @@ export {
|
|||
} from './plugin';
|
||||
export {
|
||||
FeatureCatalogueEntry,
|
||||
FeatureCatalogueSolution,
|
||||
FeatureCatalogueCategory,
|
||||
Environment,
|
||||
TutorialVariables,
|
||||
|
|
|
@ -33,6 +33,42 @@ describe('HomePublicPlugin', () => {
|
|||
});
|
||||
|
||||
describe('setup', () => {
|
||||
test('registers tutorial directory to feature catalogue', async () => {
|
||||
const setup = await new HomePublicPlugin(mockInitializerContext).setup(
|
||||
coreMock.createSetup() as any,
|
||||
{
|
||||
kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(),
|
||||
}
|
||||
);
|
||||
expect(setup).toHaveProperty('featureCatalogue');
|
||||
expect(setup.featureCatalogue.register).toHaveBeenCalledTimes(1);
|
||||
expect(setup.featureCatalogue.register).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
category: 'data',
|
||||
icon: 'indexOpen',
|
||||
id: 'home_tutorial_directory',
|
||||
showOnHomePage: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('registers kibana solution to feature catalogue', async () => {
|
||||
const setup = await new HomePublicPlugin(mockInitializerContext).setup(
|
||||
coreMock.createSetup() as any,
|
||||
{
|
||||
kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(),
|
||||
}
|
||||
);
|
||||
expect(setup).toHaveProperty('featureCatalogue');
|
||||
expect(setup.featureCatalogue.registerSolution).toHaveBeenCalledTimes(1);
|
||||
expect(setup.featureCatalogue.registerSolution).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
icon: 'logoKibana',
|
||||
id: 'kibana',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('wires up and returns registry', async () => {
|
||||
const setup = await new HomePublicPlugin(mockInitializerContext).setup(
|
||||
coreMock.createSetup() as any,
|
||||
|
|
|
@ -30,6 +30,7 @@ import { first } from 'rxjs/operators';
|
|||
import {
|
||||
EnvironmentService,
|
||||
EnvironmentServiceSetup,
|
||||
FeatureCatalogueCategory,
|
||||
FeatureCatalogueRegistry,
|
||||
FeatureCatalogueRegistrySetup,
|
||||
TutorialService,
|
||||
|
@ -42,6 +43,7 @@ import { TelemetryPluginStart } from '../../telemetry/public';
|
|||
import { UsageCollectionSetup } from '../../usage_collection/public';
|
||||
import { KibanaLegacySetup, KibanaLegacyStart } from '../../kibana_legacy/public';
|
||||
import { AppNavLinkStatus } from '../../../core/public';
|
||||
import { PLUGIN_ID, HOME_APP_BASE_PATH } from '../common/constants';
|
||||
|
||||
export interface HomePluginStartDependencies {
|
||||
data: DataPublicPluginStart;
|
||||
|
@ -68,7 +70,7 @@ export class HomePublicPlugin
|
|||
{ kibanaLegacy, usageCollection }: HomePluginSetupDependencies
|
||||
): HomePublicPluginSetup {
|
||||
core.application.register({
|
||||
id: 'home',
|
||||
id: PLUGIN_ID,
|
||||
title: 'Home',
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
|
@ -109,8 +111,58 @@ export class HomePublicPlugin
|
|||
});
|
||||
kibanaLegacy.forwardApp('home', 'home');
|
||||
|
||||
const featureCatalogue = { ...this.featuresCatalogueRegistry.setup() };
|
||||
|
||||
featureCatalogue.register({
|
||||
id: 'home_tutorial_directory',
|
||||
title: i18n.translate('home.tutorialDirectory.featureCatalogueTitle', {
|
||||
defaultMessage: 'Add data',
|
||||
}),
|
||||
description: i18n.translate('home.tutorialDirectory.featureCatalogueDescription', {
|
||||
defaultMessage: 'Ingest data from popular apps and services.',
|
||||
}),
|
||||
icon: 'indexOpen',
|
||||
showOnHomePage: true,
|
||||
path: `${HOME_APP_BASE_PATH}#/tutorial_directory`,
|
||||
category: 'data' as FeatureCatalogueCategory.DATA,
|
||||
order: 500,
|
||||
});
|
||||
|
||||
featureCatalogue.registerSolution({
|
||||
id: 'kibana',
|
||||
title: i18n.translate('home.kibana.featureCatalogue.title', {
|
||||
defaultMessage: 'Kibana',
|
||||
}),
|
||||
subtitle: i18n.translate('home.kibana.featureCatalogue.subtitle', {
|
||||
defaultMessage: 'Visualize & analyze',
|
||||
}),
|
||||
descriptions: [
|
||||
i18n.translate('home.kibana.featureCatalogueDescription1', {
|
||||
defaultMessage: 'Analyze data in dashboards.',
|
||||
}),
|
||||
i18n.translate('home.kibana.featureCatalogueDescription2', {
|
||||
defaultMessage: 'Search and find insights.',
|
||||
}),
|
||||
i18n.translate('home.kibana.featureCatalogueDescription3', {
|
||||
defaultMessage: 'Design pixel-perfect reports.',
|
||||
}),
|
||||
i18n.translate('home.kibana.featureCatalogueDescription4', {
|
||||
defaultMessage: 'Plot geographic data.',
|
||||
}),
|
||||
i18n.translate('home.kibana.featureCatalogueDescription5', {
|
||||
defaultMessage: 'Model, predict, and detect.',
|
||||
}),
|
||||
i18n.translate('home.kibana.featureCatalogueDescription6', {
|
||||
defaultMessage: 'Reveal patterns and relationships.',
|
||||
}),
|
||||
],
|
||||
icon: 'logoKibana',
|
||||
path: '/app/dashboards',
|
||||
order: 400,
|
||||
});
|
||||
|
||||
return {
|
||||
featureCatalogue: { ...this.featuresCatalogueRegistry.setup() },
|
||||
featureCatalogue,
|
||||
environment: { ...this.environmentService.setup() },
|
||||
tutorials: { ...this.tutorialService.setup() },
|
||||
};
|
||||
|
@ -124,7 +176,7 @@ export class HomePublicPlugin
|
|||
|
||||
// If the home app is the initial location when loading Kibana...
|
||||
if (
|
||||
window.location.pathname === http.basePath.prepend(`/app/home`) &&
|
||||
window.location.pathname === http.basePath.prepend(HOME_APP_BASE_PATH) &&
|
||||
window.location.hash === ''
|
||||
) {
|
||||
// ...wait for the app to mount initially and then...
|
||||
|
@ -157,5 +209,6 @@ export interface HomePublicPluginSetup {
|
|||
* be replaced by display specific extension points.
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
environment: EnvironmentSetup;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
const createSetupMock = (): jest.Mocked<FeatureCatalogueRegistrySetup> => {
|
||||
const setup = {
|
||||
register: jest.fn(),
|
||||
registerSolution: jest.fn(),
|
||||
};
|
||||
return setup;
|
||||
};
|
||||
|
@ -34,6 +35,7 @@ const createMock = (): jest.Mocked<PublicMethodsOf<FeatureCatalogueRegistry>> =>
|
|||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
get: jest.fn(() => []),
|
||||
getSolutions: jest.fn(() => []),
|
||||
};
|
||||
service.setup.mockImplementation(createSetupMock);
|
||||
return service;
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
FeatureCatalogueRegistry,
|
||||
FeatureCatalogueCategory,
|
||||
FeatureCatalogueEntry,
|
||||
FeatureCatalogueSolution,
|
||||
} from './feature_catalogue_registry';
|
||||
|
||||
const DASHBOARD_FEATURE: FeatureCatalogueEntry = {
|
||||
|
@ -33,15 +34,32 @@ const DASHBOARD_FEATURE: FeatureCatalogueEntry = {
|
|||
category: FeatureCatalogueCategory.DATA,
|
||||
};
|
||||
|
||||
const KIBANA_SOLUTION: FeatureCatalogueSolution = {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
subtitle: 'Visualize & analyze',
|
||||
descriptions: ['Analyze data in dashboards.', 'Search and find insights.'],
|
||||
icon: 'kibanaApp',
|
||||
path: `/app/home`,
|
||||
};
|
||||
|
||||
describe('FeatureCatalogueRegistry', () => {
|
||||
describe('setup', () => {
|
||||
test('throws when registering duplicate id', () => {
|
||||
test('throws when registering a feature with a duplicate id', () => {
|
||||
const setup = new FeatureCatalogueRegistry().setup();
|
||||
setup.register(DASHBOARD_FEATURE);
|
||||
expect(() => setup.register(DASHBOARD_FEATURE)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Feature with id [dashboard] has already been registered. Use a unique id."`
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when registering a solution with a duplicate id', () => {
|
||||
const setup = new FeatureCatalogueRegistry().setup();
|
||||
setup.registerSolution(KIBANA_SOLUTION);
|
||||
expect(() => setup.registerSolution(KIBANA_SOLUTION)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Solution with id [kibana] has already been registered. Use a unique id."`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
|
|
|
@ -43,11 +43,32 @@ export interface FeatureCatalogueEntry {
|
|||
readonly path: string;
|
||||
/** Whether or not this link should be shown on the front page of Kibana. */
|
||||
readonly showOnHomePage: boolean;
|
||||
/** An ordinal used to sort features relative to one another for display on the home page */
|
||||
readonly order?: number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface FeatureCatalogueSolution {
|
||||
/** Unique string identifier for this solution. */
|
||||
readonly id: string;
|
||||
/** Title of solution displayed to the user. */
|
||||
readonly title: string;
|
||||
/** The tagline of the solution displayed to the user. */
|
||||
readonly subtitle: string;
|
||||
/** A list of use cases for this solution displayed to the user. */
|
||||
readonly descriptions: string[];
|
||||
/** EUI `IconType` for icon to be displayed to the user. EUI supports any known EUI icon, SVG URL, or ReactElement. */
|
||||
readonly icon: IconType;
|
||||
/** URL path to link to this future. Should not include the basePath. */
|
||||
readonly path: string;
|
||||
/** An ordinal used to sort solutions relative to one another for display on the home page */
|
||||
readonly order?: number;
|
||||
}
|
||||
|
||||
export class FeatureCatalogueRegistry {
|
||||
private capabilities: Capabilities | null = null;
|
||||
private readonly features = new Map<string, FeatureCatalogueEntry>();
|
||||
private readonly solutions = new Map<string, FeatureCatalogueSolution>();
|
||||
|
||||
public setup() {
|
||||
return {
|
||||
|
@ -60,6 +81,15 @@ export class FeatureCatalogueRegistry {
|
|||
|
||||
this.features.set(feature.id, feature);
|
||||
},
|
||||
registerSolution: (solution: FeatureCatalogueSolution) => {
|
||||
if (this.solutions.has(solution.id)) {
|
||||
throw new Error(
|
||||
`Solution with id [${solution.id}] has already been registered. Use a unique id.`
|
||||
);
|
||||
}
|
||||
|
||||
this.solutions.set(solution.id, solution);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -76,6 +106,16 @@ export class FeatureCatalogueRegistry {
|
|||
.filter((entry) => capabilities.catalogue[entry.id] !== false)
|
||||
.sort(compareByKey('title'));
|
||||
}
|
||||
|
||||
public getSolutions(): readonly FeatureCatalogueSolution[] {
|
||||
if (this.capabilities === null) {
|
||||
throw new Error('Catalogue entries are only available after start phase');
|
||||
}
|
||||
const capabilities = this.capabilities;
|
||||
return [...this.solutions.values()]
|
||||
.filter((solution) => capabilities.catalogue[solution.id] !== false)
|
||||
.sort(compareByKey('title'));
|
||||
}
|
||||
}
|
||||
|
||||
export type FeatureCatalogueRegistrySetup = ReturnType<FeatureCatalogueRegistry['setup']>;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
export {
|
||||
FeatureCatalogueCategory,
|
||||
FeatureCatalogueEntry,
|
||||
FeatureCatalogueSolution,
|
||||
FeatureCatalogueRegistry,
|
||||
FeatureCatalogueRegistrySetup,
|
||||
} from './feature_catalogue_registry';
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["kibanaLegacy", "home"],
|
||||
"requiredBundles": ["kibanaReact", "kibanaUtils"]
|
||||
"requiredPlugins": ["kibanaLegacy"],
|
||||
"optionalPlugins": ["home"],
|
||||
"requiredBundles": ["kibanaReact", "kibanaUtils", "home"]
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import {
|
|||
} from './management_sections_service';
|
||||
|
||||
interface ManagementSetupDependencies {
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
}
|
||||
|
||||
export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart> {
|
||||
|
@ -46,19 +46,21 @@ export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart
|
|||
public setup(core: CoreSetup, { home }: ManagementSetupDependencies) {
|
||||
const kibanaVersion = this.initializerContext.env.packageInfo.version;
|
||||
|
||||
home.featureCatalogue.register({
|
||||
id: 'stack-management',
|
||||
title: i18n.translate('management.stackManagement.managementLabel', {
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
description: i18n.translate('management.stackManagement.managementDescription', {
|
||||
defaultMessage: 'Your center console for managing the Elastic Stack.',
|
||||
}),
|
||||
icon: 'managementApp',
|
||||
path: '/app/management',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
if (home) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'stack-management',
|
||||
title: i18n.translate('management.stackManagement.managementLabel', {
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
description: i18n.translate('management.stackManagement.managementDescription', {
|
||||
defaultMessage: 'Your center console for managing the Elastic Stack.',
|
||||
}),
|
||||
icon: 'managementApp',
|
||||
path: '/app/management',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
}
|
||||
|
||||
core.application.register({
|
||||
id: 'management',
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"version": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["home", "management", "data"],
|
||||
"optionalPlugins": ["dashboard", "visualizations", "discover"],
|
||||
"requiredPlugins": ["management", "data"],
|
||||
"optionalPlugins": ["dashboard", "visualizations", "discover", "home"],
|
||||
"extraPublicDirs": ["public/lib"],
|
||||
"requiredBundles": ["kibanaReact"]
|
||||
"requiredBundles": ["kibanaReact", "home"]
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ export interface SavedObjectsManagementPluginStart {
|
|||
|
||||
export interface SetupDependencies {
|
||||
management: ManagementSetup;
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
}
|
||||
|
||||
export interface StartDependencies {
|
||||
|
@ -72,20 +72,22 @@ export class SavedObjectsManagementPlugin
|
|||
): SavedObjectsManagementPluginSetup {
|
||||
const actionSetup = this.actionService.setup();
|
||||
|
||||
home.featureCatalogue.register({
|
||||
id: 'saved_objects',
|
||||
title: i18n.translate('savedObjectsManagement.objects.savedObjectsTitle', {
|
||||
defaultMessage: 'Saved Objects',
|
||||
}),
|
||||
description: i18n.translate('savedObjectsManagement.objects.savedObjectsDescription', {
|
||||
defaultMessage:
|
||||
'Import, export, and manage your saved searches, visualizations, and dashboards.',
|
||||
}),
|
||||
icon: 'savedObjectsApp',
|
||||
path: '/app/management/kibana/objects',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
if (home) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'saved_objects',
|
||||
title: i18n.translate('savedObjectsManagement.objects.savedObjectsTitle', {
|
||||
defaultMessage: 'Saved Objects',
|
||||
}),
|
||||
description: i18n.translate('savedObjectsManagement.objects.savedObjectsDescription', {
|
||||
defaultMessage:
|
||||
'Import, export, and manage your saved searches, visualizations, and dashboards.',
|
||||
}),
|
||||
icon: 'savedObjectsApp',
|
||||
path: '/app/management/kibana/objects',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
}
|
||||
|
||||
const kibanaSection = management.sections.section.kibana;
|
||||
kibanaSection.registerApp({
|
||||
|
|
|
@ -221,7 +221,7 @@ export class VisualizePlugin
|
|||
}),
|
||||
icon: 'visualizeApp',
|
||||
path: `/app/visualize#${VisualizeConstants.LANDING_PAGE_PATH}`,
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -98,19 +98,19 @@ export function HomePageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
}
|
||||
|
||||
async clickOnConsole() {
|
||||
await testSubjects.click('homeSynopsisLinkconsole');
|
||||
await this.clickSynopsis('console');
|
||||
}
|
||||
async clickOnLogo() {
|
||||
await testSubjects.click('logo');
|
||||
}
|
||||
|
||||
async ClickOnLogsData() {
|
||||
await testSubjects.click('logsData');
|
||||
async clickOnAddData() {
|
||||
await this.clickSynopsis('home_tutorial_directory');
|
||||
}
|
||||
|
||||
// clicks on Active MQ logs
|
||||
async clickOnLogsTutorial() {
|
||||
await testSubjects.click('homeSynopsisLinkactivemq logs');
|
||||
await this.clickSynopsis('activemqlogs');
|
||||
}
|
||||
|
||||
// clicks on cloud tutorial link
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
"features",
|
||||
"apmOss",
|
||||
"data",
|
||||
"home",
|
||||
"licensing",
|
||||
"triggers_actions_ui"
|
||||
],
|
||||
|
@ -18,7 +17,8 @@
|
|||
"alerts",
|
||||
"observability",
|
||||
"security",
|
||||
"ml"
|
||||
"ml",
|
||||
"home"
|
||||
],
|
||||
"server": true,
|
||||
"ui": true,
|
||||
|
@ -32,6 +32,7 @@
|
|||
"requiredBundles": [
|
||||
"kibanaReact",
|
||||
"kibanaUtils",
|
||||
"observability"
|
||||
"observability",
|
||||
"home"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@ export const featureCatalogueEntry = {
|
|||
}),
|
||||
icon: 'apmApp',
|
||||
path: '/app/apm',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
};
|
||||
|
|
|
@ -45,7 +45,7 @@ export interface ApmPluginSetupDeps {
|
|||
alerts?: AlertingPluginPublicSetup;
|
||||
data: DataPublicPluginSetup;
|
||||
features: FeaturesPluginSetup;
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
licensing: LicensingPluginSetup;
|
||||
triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
|
||||
observability?: ObservabilityPluginSetup;
|
||||
|
@ -69,8 +69,10 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
|
|||
const config = this.initializerContext.config.get();
|
||||
const pluginSetupDeps = plugins;
|
||||
|
||||
pluginSetupDeps.home.environment.update({ apmUi: true });
|
||||
pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry);
|
||||
if (pluginSetupDeps.home) {
|
||||
pluginSetupDeps.home.environment.update({ apmUi: true });
|
||||
pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry);
|
||||
}
|
||||
|
||||
if (plugins.observability) {
|
||||
const getApmDataHelper = async () => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"configPath": ["xpack", "canvas"],
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["bfetch", "data", "embeddable", "expressions", "features", "home", "inspector", "uiActions"],
|
||||
"optionalPlugins": ["usageCollection"],
|
||||
"requiredBundles": ["kibanaReact", "maps", "lens", "visualizations", "kibanaUtils", "kibanaLegacy", "discover", "savedObjects", "reporting"]
|
||||
"requiredPlugins": ["bfetch", "data", "embeddable", "expressions", "features", "inspector", "uiActions"],
|
||||
"optionalPlugins": ["usageCollection", "home"],
|
||||
"requiredBundles": ["kibanaReact", "maps", "lens", "visualizations", "kibanaUtils", "kibanaLegacy", "discover", "savedObjects", "reporting", "home"]
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@ export const featureCatalogueEntry = {
|
|||
}),
|
||||
icon: 'canvasApp',
|
||||
path: '/app/canvas',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ export { CoreStart, CoreSetup };
|
|||
export interface CanvasSetupDeps {
|
||||
data: DataPublicPluginSetup;
|
||||
expressions: ExpressionsSetup;
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
bfetch: BfetchPublicSetup;
|
||||
}
|
||||
|
@ -116,7 +116,9 @@ export class CanvasPlugin
|
|||
},
|
||||
});
|
||||
|
||||
plugins.home.featureCatalogue.register(featureCatalogueEntry);
|
||||
if (plugins.home) {
|
||||
plugins.home.featureCatalogue.register(featureCatalogueEntry);
|
||||
}
|
||||
|
||||
canvasApi.addArgumentUIs(argTypeSpecs);
|
||||
canvasApi.addTransitions(transitions);
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
"id": "enterpriseSearch",
|
||||
"version": "kibana",
|
||||
"kibanaVersion": "kibana",
|
||||
"requiredPlugins": ["home", "features", "licensing"],
|
||||
"requiredPlugins": ["features", "licensing"],
|
||||
"configPath": ["enterpriseSearch"],
|
||||
"optionalPlugins": ["usageCollection", "security"],
|
||||
"optionalPlugins": ["usageCollection", "security", "home"],
|
||||
"server": true,
|
||||
"ui": true
|
||||
"ui": true,
|
||||
"requiredBundles": ["home"]
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
AppMountParameters,
|
||||
HttpSetup,
|
||||
} from 'src/core/public';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
FeatureCatalogueCategory,
|
||||
HomePublicPluginSetup,
|
||||
|
@ -21,7 +21,11 @@ import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
|
|||
import { LicensingPluginSetup } from '../../licensing/public';
|
||||
|
||||
import { IInitialAppData } from '../common/types';
|
||||
import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../common/constants';
|
||||
import {
|
||||
ENTERPRISE_SEARCH_PLUGIN,
|
||||
APP_SEARCH_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
} from '../common/constants';
|
||||
import { ExternalUrl, IExternalUrl } from './applications/shared/enterprise_search_url';
|
||||
import AppSearchLogo from './applications/app_search/assets/logo.svg';
|
||||
import WorkplaceSearchLogo from './applications/workplace_search/assets/logo.svg';
|
||||
|
@ -35,7 +39,7 @@ export interface ClientData extends IInitialAppData {
|
|||
}
|
||||
|
||||
export interface PluginsSetup {
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
licensing: LicensingPluginSetup;
|
||||
}
|
||||
|
||||
|
@ -88,25 +92,48 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
},
|
||||
});
|
||||
|
||||
plugins.home.featureCatalogue.register({
|
||||
id: APP_SEARCH_PLUGIN.ID,
|
||||
title: APP_SEARCH_PLUGIN.NAME,
|
||||
icon: AppSearchLogo,
|
||||
description: APP_SEARCH_PLUGIN.DESCRIPTION,
|
||||
path: APP_SEARCH_PLUGIN.URL,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
showOnHomePage: true,
|
||||
});
|
||||
if (plugins.home) {
|
||||
plugins.home.featureCatalogue.registerSolution({
|
||||
id: ENTERPRISE_SEARCH_PLUGIN.ID,
|
||||
title: ENTERPRISE_SEARCH_PLUGIN.NAME,
|
||||
subtitle: i18n.translate('xpack.enterpriseSearch.featureCatalogue.subtitle', {
|
||||
defaultMessage: 'Search everything',
|
||||
}),
|
||||
icon: 'logoEnterpriseSearch',
|
||||
descriptions: [
|
||||
i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription1', {
|
||||
defaultMessage: 'Build a powerful search experience.',
|
||||
}),
|
||||
i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription2', {
|
||||
defaultMessage: 'Connect your users to relevant data.',
|
||||
}),
|
||||
i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription3', {
|
||||
defaultMessage: 'Unify your team content.',
|
||||
}),
|
||||
],
|
||||
path: APP_SEARCH_PLUGIN.URL, // TODO: Change this to enterprise search overview page once available
|
||||
});
|
||||
|
||||
plugins.home.featureCatalogue.register({
|
||||
id: WORKPLACE_SEARCH_PLUGIN.ID,
|
||||
title: WORKPLACE_SEARCH_PLUGIN.NAME,
|
||||
icon: WorkplaceSearchLogo,
|
||||
description: WORKPLACE_SEARCH_PLUGIN.DESCRIPTION,
|
||||
path: WORKPLACE_SEARCH_PLUGIN.URL,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
showOnHomePage: true,
|
||||
});
|
||||
plugins.home.featureCatalogue.register({
|
||||
id: APP_SEARCH_PLUGIN.ID,
|
||||
title: APP_SEARCH_PLUGIN.NAME,
|
||||
icon: AppSearchLogo,
|
||||
description: APP_SEARCH_PLUGIN.DESCRIPTION,
|
||||
path: APP_SEARCH_PLUGIN.URL,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
showOnHomePage: false,
|
||||
});
|
||||
|
||||
plugins.home.featureCatalogue.register({
|
||||
id: WORKPLACE_SEARCH_PLUGIN.ID,
|
||||
title: WORKPLACE_SEARCH_PLUGIN.NAME,
|
||||
icon: WorkplaceSearchLogo,
|
||||
description: WORKPLACE_SEARCH_PLUGIN.DESCRIPTION,
|
||||
path: WORKPLACE_SEARCH_PLUGIN.URL,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
showOnHomePage: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {}
|
||||
|
|
|
@ -75,7 +75,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
icon: 'logoEnterpriseSearch',
|
||||
navLinkId: APP_SEARCH_PLUGIN.ID, // TODO - remove this once functional tests no longer rely on navLinkId
|
||||
app: ['kibana', APP_SEARCH_PLUGIN.ID, WORKPLACE_SEARCH_PLUGIN.ID],
|
||||
catalogue: [APP_SEARCH_PLUGIN.ID, WORKPLACE_SEARCH_PLUGIN.ID],
|
||||
catalogue: [ENTERPRISE_SEARCH_PLUGIN.ID, APP_SEARCH_PLUGIN.ID, WORKPLACE_SEARCH_PLUGIN.ID],
|
||||
privileges: null,
|
||||
});
|
||||
|
||||
|
@ -93,6 +93,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
workplaceSearch: hasWorkplaceSearchAccess,
|
||||
},
|
||||
catalogue: {
|
||||
enterpriseSearch: hasAppSearchAccess || hasWorkplaceSearchAccess,
|
||||
appSearch: hasAppSearchAccess,
|
||||
workplaceSearch: hasWorkplaceSearchAccess,
|
||||
},
|
||||
|
|
|
@ -61,7 +61,7 @@ export class GraphPlugin
|
|||
}),
|
||||
icon: 'graphApp',
|
||||
path: '/app/graph',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
}
|
||||
|
@ -113,7 +113,8 @@ export class GraphPlugin
|
|||
throw new Error('Start called before setup');
|
||||
}
|
||||
this.licensing.license$.subscribe((license) => {
|
||||
toggleNavLink(checkLicense(license), core.chrome.navLinks);
|
||||
const licenseInformation = checkLicense(license);
|
||||
toggleNavLink(licenseInformation, core.chrome.navLinks);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,18 +4,19 @@
|
|||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": [
|
||||
"home",
|
||||
"licensing",
|
||||
"management"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"usageCollection",
|
||||
"indexManagement"
|
||||
"indexManagement",
|
||||
"home"
|
||||
],
|
||||
"configPath": ["xpack", "ilm"],
|
||||
"requiredBundles": [
|
||||
"indexManagement",
|
||||
"kibanaReact",
|
||||
"esUiShared"
|
||||
"esUiShared",
|
||||
"home"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CoreSetup, PluginInitializerContext } from 'src/core/public';
|
||||
|
||||
import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public';
|
||||
import { PLUGIN } from '../common/constants';
|
||||
import { init as initHttp } from './application/services/http';
|
||||
import { init as initDocumentation } from './application/services/documentation';
|
||||
|
@ -30,7 +31,7 @@ export class IndexLifecycleManagementPlugin {
|
|||
getStartServices,
|
||||
} = coreSetup;
|
||||
|
||||
const { usageCollection, management, indexManagement } = plugins;
|
||||
const { usageCollection, management, indexManagement, home } = plugins;
|
||||
|
||||
// Initialize services even if the app isn't mounted, because they're used by index management extensions.
|
||||
initHttp(http);
|
||||
|
@ -74,6 +75,24 @@ export class IndexLifecycleManagementPlugin {
|
|||
},
|
||||
});
|
||||
|
||||
if (home) {
|
||||
home.featureCatalogue.register({
|
||||
id: PLUGIN.ID,
|
||||
title: i18n.translate('xpack.indexLifecycleMgmt.featureCatalogueTitle', {
|
||||
defaultMessage: 'Manage index lifecycles',
|
||||
}),
|
||||
description: i18n.translate('xpack.indexLifecycleMgmt.featureCatalogueDescription', {
|
||||
defaultMessage:
|
||||
'Define lifecycle policies to automatically perform operations as an index ages.',
|
||||
}),
|
||||
icon: 'indexSettings',
|
||||
path: '/app/management/data/index_lifecycle_management',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
order: 640,
|
||||
});
|
||||
}
|
||||
|
||||
if (indexManagement) {
|
||||
addAllExtensions(indexManagement.extensionsService);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
|
||||
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
|
||||
import { ManagementSetup } from '../../../../src/plugins/management/public';
|
||||
import { IndexManagementPluginSetup } from '../../index_management/public';
|
||||
|
@ -12,6 +13,7 @@ export interface PluginsDependencies {
|
|||
usageCollection?: UsageCollectionSetup;
|
||||
management: ManagementSetup;
|
||||
indexManagement?: IndexManagementPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
}
|
||||
|
||||
export interface ClientConfigType {
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
"features",
|
||||
"usageCollection",
|
||||
"spaces",
|
||||
"home",
|
||||
|
||||
"data",
|
||||
"dataEnhanced",
|
||||
"visTypeTimeseries",
|
||||
"alerts",
|
||||
"triggers_actions_ui"
|
||||
],
|
||||
"optionalPlugins": ["ml", "observability"],
|
||||
"optionalPlugins": ["ml", "observability", "home"],
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"configPath": ["xpack", "infra"],
|
||||
|
@ -22,6 +22,7 @@
|
|||
"licenseManagement",
|
||||
"kibanaUtils",
|
||||
"kibanaReact",
|
||||
"apm"
|
||||
"apm",
|
||||
"home"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ export class Plugin implements InfraClientPluginClass {
|
|||
constructor(_context: PluginInitializerContext) {}
|
||||
|
||||
setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) {
|
||||
registerFeatures(pluginsSetup.home);
|
||||
if (pluginsSetup.home) {
|
||||
registerFeatures(pluginsSetup.home);
|
||||
}
|
||||
|
||||
pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createInventoryMetricAlertType());
|
||||
pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getLogsAlertType());
|
||||
|
|
|
@ -22,7 +22,7 @@ export const registerFeatures = (homePlugin: HomePublicPluginSetup) => {
|
|||
}),
|
||||
icon: 'metricsApp',
|
||||
path: `/app/metrics`,
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
|
||||
|
@ -37,7 +37,7 @@ export const registerFeatures = (homePlugin: HomePublicPluginSetup) => {
|
|||
}),
|
||||
icon: 'logsApp',
|
||||
path: `/app/logs`,
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ export type InfraClientStartExports = void;
|
|||
|
||||
export interface InfraClientSetupDeps {
|
||||
dataEnhanced: DataEnhancedSetup;
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
observability: ObservabilityPluginSetup;
|
||||
triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
|
|
|
@ -18,7 +18,7 @@ export const METRICS_FEATURE = {
|
|||
icon: 'metricsApp',
|
||||
navLinkId: 'metrics',
|
||||
app: ['infra', 'metrics', 'kibana'],
|
||||
catalogue: ['infraops'],
|
||||
catalogue: ['infraops', 'metrics'],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ export const METRICS_FEATURE = {
|
|||
privileges: {
|
||||
all: {
|
||||
app: ['infra', 'metrics', 'kibana'],
|
||||
catalogue: ['infraops'],
|
||||
catalogue: ['infraops', 'metrics'],
|
||||
api: ['infra'],
|
||||
savedObject: {
|
||||
all: ['infrastructure-ui-source'],
|
||||
|
@ -42,7 +42,7 @@ export const METRICS_FEATURE = {
|
|||
},
|
||||
read: {
|
||||
app: ['infra', 'metrics', 'kibana'],
|
||||
catalogue: ['infraops'],
|
||||
catalogue: ['infraops', 'metrics'],
|
||||
api: ['infra'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
|
@ -68,12 +68,12 @@ export const LOGS_FEATURE = {
|
|||
icon: 'logsApp',
|
||||
navLinkId: 'logs',
|
||||
app: ['infra', 'logs', 'kibana'],
|
||||
catalogue: ['infralogging'],
|
||||
catalogue: ['infralogging', 'logs'],
|
||||
alerting: [LOG_DOCUMENT_COUNT_ALERT_TYPE_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
app: ['infra', 'logs', 'kibana'],
|
||||
catalogue: ['infralogging'],
|
||||
catalogue: ['infralogging', 'logs'],
|
||||
api: ['infra'],
|
||||
savedObject: {
|
||||
all: ['infrastructure-ui-source'],
|
||||
|
@ -86,7 +86,7 @@ export const LOGS_FEATURE = {
|
|||
},
|
||||
read: {
|
||||
app: ['infra', 'logs', 'kibana'],
|
||||
catalogue: ['infralogging'],
|
||||
catalogue: ['infralogging', 'logs'],
|
||||
api: ['infra'],
|
||||
alerting: {
|
||||
all: [LOG_DOCUMENT_COUNT_ALERT_TYPE_ID],
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects"],
|
||||
"optionalPlugins": ["security", "features", "cloud", "usageCollection", "home"],
|
||||
"extraPublicDirs": ["common"],
|
||||
"requiredBundles": ["kibanaReact", "esUiShared"]
|
||||
"requiredBundles": ["kibanaReact", "esUiShared", "home"]
|
||||
}
|
||||
|
|
|
@ -13,9 +13,13 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
|
||||
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
|
||||
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
|
||||
import {
|
||||
HomePublicPluginSetup,
|
||||
FeatureCatalogueCategory,
|
||||
} from '../../../../src/plugins/home/public';
|
||||
import { LicensingPluginSetup } from '../../licensing/public';
|
||||
import { PLUGIN_ID, CheckPermissionsResponse, PostIngestSetupResponse } from '../common';
|
||||
import { BASE_PATH } from './applications/ingest_manager/constants';
|
||||
|
||||
import { IngestManagerConfigType } from '../common/types';
|
||||
import { setupRouteService, appRoutesService } from '../common';
|
||||
|
@ -95,6 +99,21 @@ export class IngestManagerPlugin
|
|||
deps.home.tutorials.registerDirectoryNotice(PLUGIN_ID, TutorialDirectoryNotice);
|
||||
deps.home.tutorials.registerDirectoryHeaderLink(PLUGIN_ID, TutorialDirectoryHeaderLink);
|
||||
deps.home.tutorials.registerModuleNotice(PLUGIN_ID, TutorialModuleNotice);
|
||||
|
||||
deps.home.featureCatalogue.register({
|
||||
id: 'ingestManager',
|
||||
title: i18n.translate('xpack.ingestManager.featureCatalogueTitle', {
|
||||
defaultMessage: 'Add Elastic Agent',
|
||||
}),
|
||||
description: i18n.translate('xpack.ingestManager.featureCatalogueDescription', {
|
||||
defaultMessage: 'Add and manage your fleet of Elastic Agents and integrations.',
|
||||
}),
|
||||
icon: 'indexManagementApp',
|
||||
showOnHomePage: true,
|
||||
path: BASE_PATH,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
order: 510,
|
||||
});
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -179,10 +179,12 @@ export class IngestManagerPlugin
|
|||
icon: 'savedObjectsApp',
|
||||
navLinkId: PLUGIN_ID,
|
||||
app: [PLUGIN_ID, 'kibana'],
|
||||
catalogue: ['ingestManager'],
|
||||
privileges: {
|
||||
all: {
|
||||
api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`],
|
||||
app: [PLUGIN_ID, 'kibana'],
|
||||
catalogue: ['ingestManager'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: [],
|
||||
|
@ -192,6 +194,7 @@ export class IngestManagerPlugin
|
|||
read: {
|
||||
api: [`${PLUGIN_ID}-read`],
|
||||
app: [PLUGIN_ID, 'kibana'],
|
||||
catalogue: ['ingestManager'], // TODO: check if this is actually available to read user
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
|
|
|
@ -70,7 +70,7 @@ export class LogstashPlugin implements Plugin<void, void, SetupDeps> {
|
|||
}),
|
||||
icon: 'pipelineApp',
|
||||
path: '/app/management/ingest/pipelines',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
"licensing",
|
||||
"features",
|
||||
"inspector",
|
||||
"home",
|
||||
"data",
|
||||
"fileUpload",
|
||||
"uiActions",
|
||||
|
@ -18,12 +17,14 @@
|
|||
"usageCollection",
|
||||
"share"
|
||||
],
|
||||
"optionalPlugins": ["home"],
|
||||
"ui": true,
|
||||
"server": true,
|
||||
"extraPublicDirs": ["common/constants"],
|
||||
"requiredBundles": [
|
||||
"kibanaReact",
|
||||
"kibanaUtils",
|
||||
"savedObjects"
|
||||
"savedObjects",
|
||||
"home"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ export const featureCatalogueEntry = {
|
|||
id: APP_ID,
|
||||
title: getAppTitle(),
|
||||
description: i18n.translate('xpack.maps.feature.appDescription', {
|
||||
defaultMessage: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service',
|
||||
defaultMessage: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service.',
|
||||
}),
|
||||
icon: APP_ICON,
|
||||
path: '/app/maps',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
};
|
||||
|
|
|
@ -51,7 +51,7 @@ import { StartContract as FileUploadStartContract } from '../../file_upload/publ
|
|||
|
||||
export interface MapsPluginSetupDependencies {
|
||||
inspector: InspectorSetupContract;
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
visualizations: VisualizationsSetup;
|
||||
embeddable: EmbeddableSetup;
|
||||
mapsLegacy: { config: MapsLegacyConfigType };
|
||||
|
@ -108,7 +108,9 @@ export class MapsPlugin
|
|||
);
|
||||
|
||||
plugins.inspector.registerView(MapView);
|
||||
plugins.home.featureCatalogue.register(featureCatalogueEntry);
|
||||
if (plugins.home) {
|
||||
plugins.home.featureCatalogue.register(featureCatalogueEntry);
|
||||
}
|
||||
plugins.visualizations.registerAlias(
|
||||
getMapsVisTypeAlias(plugins.visualizations, config.showMapVisualizationTypes)
|
||||
);
|
||||
|
|
|
@ -91,6 +91,7 @@ export function getPluginPrivileges() {
|
|||
admin: {
|
||||
...privilege,
|
||||
api: allMlCapabilitiesKeys.map((k) => `ml:${k}`),
|
||||
catalogue: [PLUGIN_ID, `${PLUGIN_ID}_file_data_visualizer`],
|
||||
ui: allMlCapabilitiesKeys,
|
||||
savedObject: {
|
||||
all: savedObjects,
|
||||
|
@ -100,6 +101,7 @@ export function getPluginPrivileges() {
|
|||
user: {
|
||||
...privilege,
|
||||
api: userMlCapabilitiesKeys.map((k) => `ml:${k}`),
|
||||
catalogue: [PLUGIN_ID],
|
||||
ui: userMlCapabilitiesKeys,
|
||||
savedObject: {
|
||||
all: [],
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"data",
|
||||
"cloud",
|
||||
"features",
|
||||
"home",
|
||||
"licensing",
|
||||
"usageCollection",
|
||||
"share",
|
||||
|
@ -20,6 +19,7 @@
|
|||
"indexPatternManagement"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"home",
|
||||
"security",
|
||||
"spaces",
|
||||
"management",
|
||||
|
@ -32,6 +32,7 @@
|
|||
"kibanaUtils",
|
||||
"kibanaReact",
|
||||
"dashboard",
|
||||
"savedObjects"
|
||||
"savedObjects",
|
||||
"home"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ export interface MlSetupDependencies {
|
|||
management?: ManagementSetup;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
licenseManagement?: LicenseManagementUIPluginSetup;
|
||||
home: HomePublicPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
embeddable: EmbeddableSetup;
|
||||
uiActions: UiActionsSetup;
|
||||
kibanaVersion: string;
|
||||
|
@ -111,7 +111,9 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
|
|||
const [coreStart] = await core.getStartServices();
|
||||
if (isMlEnabled(license)) {
|
||||
// add ML to home page
|
||||
registerFeature(pluginsSetup.home);
|
||||
if (pluginsSetup.home) {
|
||||
registerFeature(pluginsSetup.home);
|
||||
}
|
||||
|
||||
// register ML for the index pattern management no data screen.
|
||||
pluginsSetup.indexPatternManagement.environment.update({
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
HomePublicPluginSetup,
|
||||
FeatureCatalogueCategory,
|
||||
HomePublicPluginSetup,
|
||||
} from '../../../../src/plugins/home/public';
|
||||
import { PLUGIN_ID } from '../common/constants/app';
|
||||
|
||||
|
@ -28,7 +28,22 @@ export const registerFeature = (home: HomePublicPluginSetup) => {
|
|||
}),
|
||||
icon: 'machineLearningApp',
|
||||
path: '/app/ml',
|
||||
showOnHomePage: true,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
});
|
||||
|
||||
home.featureCatalogue.register({
|
||||
id: `${PLUGIN_ID}_file_data_visualizer`,
|
||||
title: i18n.translate('xpack.ml.fileDataVisualizerTitle', {
|
||||
defaultMessage: 'Upload a file',
|
||||
}),
|
||||
description: i18n.translate('xpack.ml.fileDataVisualizerDescription', {
|
||||
defaultMessage: 'Import your own CSV, NDJSON, or log file.',
|
||||
}),
|
||||
icon: 'document',
|
||||
path: '/app/ml#/filedatavisualizer',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.DATA,
|
||||
order: 520,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -87,7 +87,7 @@ export class MlServerPlugin implements Plugin<MlPluginSetup, MlPluginStart, Plug
|
|||
order: 500,
|
||||
navLinkId: PLUGIN_ID,
|
||||
app: [PLUGIN_ID, 'kibana'],
|
||||
catalogue: [PLUGIN_ID],
|
||||
catalogue: [PLUGIN_ID, `${PLUGIN_ID}_file_data_visualizer`],
|
||||
management: {
|
||||
insightsAndAlerting: ['jobsListLink'],
|
||||
},
|
||||
|
|
|
@ -54,14 +54,17 @@ export class MonitoringPlugin
|
|||
if (home) {
|
||||
home.featureCatalogue.register({
|
||||
id,
|
||||
title,
|
||||
title: i18n.translate('xpack.monitoring.featureCatalogueTitle', {
|
||||
defaultMessage: 'Monitor the stack',
|
||||
}),
|
||||
icon,
|
||||
path: '/app/monitoring',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
description: i18n.translate('xpack.monitoring.monitoringDescription', {
|
||||
defaultMessage: 'Track the real-time health and performance of your Elastic Stack.',
|
||||
description: i18n.translate('xpack.monitoring.featureCatalogueDescription', {
|
||||
defaultMessage: 'Track the real-time health and performance of your deployment.',
|
||||
}),
|
||||
order: 610,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
"observability"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"licensing"
|
||||
"licensing",
|
||||
"home"
|
||||
],
|
||||
"ui": true,
|
||||
"server": true,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue