diff --git a/dev_docs/assets/kibana_template_solution_nav.png b/dev_docs/assets/kibana_template_solution_nav.png
new file mode 100644
index 0000000000000..4ffec1990ed0a
Binary files /dev/null and b/dev_docs/assets/kibana_template_solution_nav.png differ
diff --git a/dev_docs/assets/kibana_template_solution_nav_mobile.png b/dev_docs/assets/kibana_template_solution_nav_mobile.png
new file mode 100644
index 0000000000000..487114f7a344a
Binary files /dev/null and b/dev_docs/assets/kibana_template_solution_nav_mobile.png differ
diff --git a/dev_docs/tutorials/kibana_page_template.mdx b/dev_docs/tutorials/kibana_page_template.mdx
index aa38890a8ac9e..d9605ac5643ba 100644
--- a/dev_docs/tutorials/kibana_page_template.mdx
+++ b/dev_docs/tutorials/kibana_page_template.mdx
@@ -9,13 +9,13 @@ tags: ['kibana', 'dev', 'ui', 'tutorials']
`KibanaPageTemplate` is a thin wrapper around [EuiPageTemplate](https://elastic.github.io/eui/#/layout/page) that makes setting up common types of Kibana pages quicker and easier while also adhering to any Kibana-specific requirements and patterns.
-Refer to EUI's documentation on [EuiPageTemplate](https://elastic.github.io/eui/#/layout/page) for constructing page layouts.
+Refer to EUI's documentation on [**EuiPageTemplate**](https://elastic.github.io/eui/#/layout/page) for constructing page layouts.
## `isEmptyState`
Use the `isEmptyState` prop for when there is no page content to show. For example, before the user has created something, when no search results are found, before data is populated, or when permissions aren't met.
-The default empty state uses any `pageHeader` info provided to populate an [`EuiEmptyPrompt`](https://elastic.github.io/eui/#/display/empty-prompt) and uses the `centeredBody` template type.
+The default empty state uses any `pageHeader` info provided to populate an [**EuiEmptyPrompt**](https://elastic.github.io/eui/#/display/empty-prompt) and uses the `centeredBody` template type.
```tsx
+ {...}
+
+```
+
+
+![Screenshot of Stack Management empty state with a provided solution navigation shown on the left, outlined in pink.](../assets/kibana_template_solution_nav.png)
+
+![Screenshots of Stack Management page in mobile view. Menu closed on the left, menu open on the right.](../assets/kibana_template_solution_nav_mobile.png)
diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml
index 7f2d0768a1fb9..a585a0fc7542f 100644
--- a/packages/kbn-optimizer/limits.yml
+++ b/packages/kbn-optimizer/limits.yml
@@ -9,7 +9,7 @@ pageLoadAssetSize:
charts: 195358
cloud: 21076
console: 46091
- core: 414000
+ core: 432925
crossClusterReplication: 65408
dashboard: 374194
dashboardEnhanced: 65646
diff --git a/src/core/public/rendering/_base.scss b/src/core/public/rendering/_base.scss
index 936b41e7682bb..3a748f3ceb6fd 100644
--- a/src/core/public/rendering/_base.scss
+++ b/src/core/public/rendering/_base.scss
@@ -49,6 +49,13 @@
top: $headerHeight;
height: calc(100% - #{$headerHeight});
}
+
+ @include euiBreakpoint('m', 'l', 'xl') {
+ .euiPageSideBar--sticky {
+ max-height: calc(100vh - #{$headerHeight});
+ top: #{$headerHeight};
+ }
+ }
}
.kbnBody {
diff --git a/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap b/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap
index 89fa05615a039..a80e3a67fb2db 100644
--- a/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap
+++ b/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap
@@ -2,6 +2,7 @@
exports[`KibanaPageTemplate render basic template 1`] = `
`;
exports[`KibanaPageTemplate render custom empty prompt only 1`] = `
@@ -33,6 +45,7 @@ exports[`KibanaPageTemplate render custom empty prompt only 1`] = `
exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = `
@@ -58,6 +76,12 @@ exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = `
exports[`KibanaPageTemplate render default empty prompt 1`] = `
@@ -72,7 +96,76 @@ exports[`KibanaPageTemplate render default empty prompt 1`] = `
test
}
+ iconColor=""
iconType="test"
/>
`;
+
+exports[`KibanaPageTemplate render solutionNav 1`] = `
+
+ }
+ pageSideBarProps={
+ Object {
+ "className": "kbnPageTemplate__pageSideBar",
+ }
+ }
+ restrictWidth={true}
+/>
+`;
diff --git a/src/plugins/kibana_react/public/page_template/page_template.scss b/src/plugins/kibana_react/public/page_template/page_template.scss
new file mode 100644
index 0000000000000..4b8513311114d
--- /dev/null
+++ b/src/plugins/kibana_react/public/page_template/page_template.scss
@@ -0,0 +1,15 @@
+$euiSideNavEmphasizedBackgroundColor: transparentize($euiColorLightShade, .7);
+
+.kbnPageTemplate__pageSideBar {
+ padding: $euiSizeL;
+ background:
+ linear-gradient(160deg, $euiSideNavEmphasizedBackgroundColor 0, $euiSideNavEmphasizedBackgroundColor $euiSizeXL, rgba(#FFF, 0) 0),
+ linear-gradient(175deg, $euiSideNavEmphasizedBackgroundColor 0, $euiSideNavEmphasizedBackgroundColor $euiSize, rgba(#FFF, 0) 0);
+}
+
+@include euiBreakpoint('xs','s') {
+ .kbnPageTemplate__pageSideBar {
+ width: auto;
+ padding: 0;
+ }
+}
diff --git a/src/plugins/kibana_react/public/page_template/page_template.test.tsx b/src/plugins/kibana_react/public/page_template/page_template.test.tsx
index 2ad9a81e7916c..43f96a6c2b98c 100644
--- a/src/plugins/kibana_react/public/page_template/page_template.test.tsx
+++ b/src/plugins/kibana_react/public/page_template/page_template.test.tsx
@@ -10,6 +10,46 @@ import React from 'react';
import { shallow } from 'enzyme';
import { KibanaPageTemplate } from './page_template';
import { EuiEmptyPrompt } from '@elastic/eui';
+import { KibanaPageTemplateSolutionNavProps } from './solution_nav';
+
+const navItems: KibanaPageTemplateSolutionNavProps['items'] = [
+ {
+ name: 'Ingest',
+ id: '1',
+ items: [
+ {
+ name: 'Ingest Node Pipelines',
+ id: '1.1',
+ },
+ {
+ name: 'Logstash Pipelines',
+ id: '1.2',
+ },
+ {
+ name: 'Beats Central Management',
+ id: '1.3',
+ },
+ ],
+ },
+ {
+ name: 'Data',
+ id: '2',
+ items: [
+ {
+ name: 'Index Management',
+ id: '2.1',
+ },
+ {
+ name: 'Index Lifecycle Policies',
+ id: '2.2',
+ },
+ {
+ name: 'Snapshot and Restore',
+ id: '2.3',
+ },
+ ],
+ },
+];
describe('KibanaPageTemplate', () => {
test('render default empty prompt', () => {
@@ -66,4 +106,23 @@ describe('KibanaPageTemplate', () => {
);
expect(component).toMatchSnapshot();
});
+
+ test('render solutionNav', () => {
+ const component = shallow(
+
+ );
+ expect(component).toMatchSnapshot();
+ });
});
diff --git a/src/plugins/kibana_react/public/page_template/page_template.tsx b/src/plugins/kibana_react/public/page_template/page_template.tsx
index eb834d00402ef..0bbf97ca6ddb5 100644
--- a/src/plugins/kibana_react/public/page_template/page_template.tsx
+++ b/src/plugins/kibana_react/public/page_template/page_template.tsx
@@ -5,10 +5,21 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
+import './page_template.scss';
-import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui';
import React, { FunctionComponent } from 'react';
+import classNames from 'classnames';
+
+import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui';
+
+import {
+ KibanaPageTemplateSolutionNav,
+ KibanaPageTemplateSolutionNavProps,
+} from './solution_nav/solution_nav';
+/**
+ * A thin wrapper around EuiPageTemplate with a few Kibana specific additions
+ */
export type KibanaPageTemplateProps = EuiPageTemplateProps & {
/**
* Changes the template type depending on other props provided.
@@ -17,6 +28,10 @@ export type KibanaPageTemplateProps = EuiPageTemplateProps & {
* With `pageHeader` and `children`: Uses `centeredContent`
*/
isEmptyState?: boolean;
+ /**
+ * Quick creation of EuiSideNav. Hooks up mobile instance too
+ */
+ solutionNav?: KibanaPageTemplateSolutionNavProps;
};
export const KibanaPageTemplate: FunctionComponent = ({
@@ -27,6 +42,8 @@ export const KibanaPageTemplate: FunctionComponent = ({
restrictWidth = true,
bottomBar,
bottomBarProps,
+ pageSideBar,
+ solutionNav,
...rest
}) => {
// Needed for differentiating between union types
@@ -38,6 +55,13 @@ export const KibanaPageTemplate: FunctionComponent = ({
};
}
+ /**
+ * Create the solution nav component
+ */
+ if (solutionNav) {
+ pageSideBar = ;
+ }
+
/**
* An easy way to create the right content for empty pages
*/
@@ -48,6 +72,7 @@ export const KibanaPageTemplate: FunctionComponent = ({
children = (
{pageTitle} : undefined}
body={description ?