Skip to content

Commit f07d18c

Browse files
authored
[KibanaPageLayout] Solution Nav specific styles & props (#100089) (#100576)
* Fixing sticky nav * Adding some side bar styles * Added a built-in solution nav title with avatar icon * Adding tutorial docs * Added KibanaPageTemplateSolutionNavAvatar * Added KibanaPageTemplateSolutionNav * Increased limit to `core` / `kibanaReact` plugin because of additional CSS # Conflicts: # packages/kbn-optimizer/limits.yml
1 parent 5102aa7 commit f07d18c

18 files changed

+755
-5
lines changed
140 KB
Loading
160 KB
Loading

dev_docs/tutorials/kibana_page_template.mdx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ tags: ['kibana', 'dev', 'ui', 'tutorials']
99

1010
`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.
1111

12-
Refer to EUI's documentation on [EuiPageTemplate](https://elastic.github.io/eui/#/layout/page) for constructing page layouts.
12+
Refer to EUI's documentation on [**EuiPageTemplate**](https://elastic.github.io/eui/#/layout/page) for constructing page layouts.
1313

1414
## `isEmptyState`
1515

1616
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.
1717

18-
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.
18+
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.
1919

2020
```tsx
2121
<KibanaPageTemplate
@@ -84,3 +84,36 @@ When passing both a `pageHeader` configuration and `isEmptyState`, the component
8484
```
8585

8686
![Screenshot of demo custom empty state code with a page header. Shows the Kibana navigation bars, a level 1 heading "Dashboards", and a centered empty state with the a level 2 heading "No data", body text "You have no data. Would you like some of ours?", and a button that says "Get sample data".](../assets/kibana_header_and_empty_state.png)
87+
88+
## `solutionNav`
89+
90+
To add left side navigation for your solution, we recommend passing [**EuiSideNav**](https://elastic.github.io/eui/#/navigation/side-nav) props to the `solutionNav` prop. The template component will then handle the mobile views and add the solution nav embellishments. On top of the EUI props, you'll need to pass your solution `name` and an optional `icon`.
91+
92+
If you need to custom side bar content, you will need to pass you own navigation component to `pageSideBar`. We still recommend using [**EuiSideNav**](https://elastic.github.io/eui/#/navigation/side-nav).
93+
94+
When using `EuiSideNav`, root level items should not be linked but provide section labelling only.
95+
96+
```tsx
97+
<KibanaPageTemplate
98+
solutionNav={{
99+
name: 'Management',
100+
icon: 'managementApp',
101+
items: [
102+
{
103+
name: 'Root item',
104+
items: [
105+
{ name: 'Navigation item', href: '#' },
106+
{ name: 'Navigation item', href: '#' },
107+
]
108+
}
109+
]
110+
}}
111+
>
112+
{...}
113+
</KibanaPageTemplate>
114+
```
115+
116+
117+
![Screenshot of Stack Management empty state with a provided solution navigation shown on the left, outlined in pink.](../assets/kibana_template_solution_nav.png)
118+
119+
![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)

packages/kbn-optimizer/limits.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pageLoadAssetSize:
99
charts: 195358
1010
cloud: 21076
1111
console: 46235
12-
core: 414000
12+
core: 432925
1313
crossClusterReplication: 65408
1414
dashboard: 374267
1515
dashboardEnhanced: 65646

src/core/public/rendering/_base.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@
4949
top: $headerHeight;
5050
height: calc(100% - #{$headerHeight});
5151
}
52+
53+
@include euiBreakpoint('m', 'l', 'xl') {
54+
.euiPageSideBar--sticky {
55+
max-height: calc(100vh - #{$headerHeight});
56+
top: #{$headerHeight};
57+
}
58+
}
5259
}
5360

5461
.kbnBody {

src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap

Lines changed: 93 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
$euiSideNavEmphasizedBackgroundColor: transparentize($euiColorLightShade, .7);
2+
3+
.kbnPageTemplate__pageSideBar {
4+
padding: $euiSizeL;
5+
background:
6+
linear-gradient(160deg, $euiSideNavEmphasizedBackgroundColor 0, $euiSideNavEmphasizedBackgroundColor $euiSizeXL, rgba(#FFF, 0) 0),
7+
linear-gradient(175deg, $euiSideNavEmphasizedBackgroundColor 0, $euiSideNavEmphasizedBackgroundColor $euiSize, rgba(#FFF, 0) 0);
8+
}
9+
10+
@include euiBreakpoint('xs','s') {
11+
.kbnPageTemplate__pageSideBar {
12+
width: auto;
13+
padding: 0;
14+
}
15+
}

src/plugins/kibana_react/public/page_template/page_template.test.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,46 @@ import React from 'react';
1010
import { shallow } from 'enzyme';
1111
import { KibanaPageTemplate } from './page_template';
1212
import { EuiEmptyPrompt } from '@elastic/eui';
13+
import { KibanaPageTemplateSolutionNavProps } from './solution_nav';
14+
15+
const navItems: KibanaPageTemplateSolutionNavProps['items'] = [
16+
{
17+
name: 'Ingest',
18+
id: '1',
19+
items: [
20+
{
21+
name: 'Ingest Node Pipelines',
22+
id: '1.1',
23+
},
24+
{
25+
name: 'Logstash Pipelines',
26+
id: '1.2',
27+
},
28+
{
29+
name: 'Beats Central Management',
30+
id: '1.3',
31+
},
32+
],
33+
},
34+
{
35+
name: 'Data',
36+
id: '2',
37+
items: [
38+
{
39+
name: 'Index Management',
40+
id: '2.1',
41+
},
42+
{
43+
name: 'Index Lifecycle Policies',
44+
id: '2.2',
45+
},
46+
{
47+
name: 'Snapshot and Restore',
48+
id: '2.3',
49+
},
50+
],
51+
},
52+
];
1353

1454
describe('KibanaPageTemplate', () => {
1555
test('render default empty prompt', () => {
@@ -66,4 +106,23 @@ describe('KibanaPageTemplate', () => {
66106
);
67107
expect(component).toMatchSnapshot();
68108
});
109+
110+
test('render solutionNav', () => {
111+
const component = shallow(
112+
<KibanaPageTemplate
113+
pageHeader={{
114+
iconType: 'test',
115+
title: 'test',
116+
description: 'test',
117+
rightSideItems: ['test'],
118+
}}
119+
solutionNav={{
120+
name: 'Solution',
121+
icon: 'solution',
122+
items: navItems,
123+
}}
124+
/>
125+
);
126+
expect(component).toMatchSnapshot();
127+
});
69128
});

src/plugins/kibana_react/public/page_template/page_template.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,21 @@
55
* in compliance with, at your election, the Elastic License 2.0 or the Server
66
* Side Public License, v 1.
77
*/
8+
import './page_template.scss';
89

9-
import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui';
1010
import React, { FunctionComponent } from 'react';
11+
import classNames from 'classnames';
12+
13+
import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui';
14+
15+
import {
16+
KibanaPageTemplateSolutionNav,
17+
KibanaPageTemplateSolutionNavProps,
18+
} from './solution_nav/solution_nav';
1119

20+
/**
21+
* A thin wrapper around EuiPageTemplate with a few Kibana specific additions
22+
*/
1223
export type KibanaPageTemplateProps = EuiPageTemplateProps & {
1324
/**
1425
* Changes the template type depending on other props provided.
@@ -17,6 +28,10 @@ export type KibanaPageTemplateProps = EuiPageTemplateProps & {
1728
* With `pageHeader` and `children`: Uses `centeredContent`
1829
*/
1930
isEmptyState?: boolean;
31+
/**
32+
* Quick creation of EuiSideNav. Hooks up mobile instance too
33+
*/
34+
solutionNav?: KibanaPageTemplateSolutionNavProps;
2035
};
2136

2237
export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
@@ -27,6 +42,8 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
2742
restrictWidth = true,
2843
bottomBar,
2944
bottomBarProps,
45+
pageSideBar,
46+
solutionNav,
3047
...rest
3148
}) => {
3249
// Needed for differentiating between union types
@@ -38,6 +55,13 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
3855
};
3956
}
4057

58+
/**
59+
* Create the solution nav component
60+
*/
61+
if (solutionNav) {
62+
pageSideBar = <KibanaPageTemplateSolutionNav {...solutionNav} />;
63+
}
64+
4165
/**
4266
* An easy way to create the right content for empty pages
4367
*/
@@ -48,6 +72,7 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
4872
children = (
4973
<EuiEmptyPrompt
5074
iconType={iconType}
75+
iconColor={''} // This is likely a solution or app logo, so keep it multi-color
5176
title={pageTitle ? <h1>{pageTitle}</h1> : undefined}
5277
body={description ? <p>{description}</p> : undefined}
5378
actions={rightSideItems}
@@ -62,8 +87,14 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
6287
return (
6388
<EuiPageTemplate
6489
template={template}
65-
pageHeader={pageHeader}
6690
restrictWidth={restrictWidth}
91+
paddingSize={template === 'centeredBody' ? 'none' : 'l'}
92+
pageHeader={pageHeader}
93+
pageSideBar={pageSideBar}
94+
pageSideBarProps={{
95+
...rest.pageSideBarProps,
96+
className: classNames('kbnPageTemplate__pageSideBar', rest.pageSideBarProps?.className),
97+
}}
6798
{...localBottomBarProps}
6899
{...rest}
69100
>

0 commit comments

Comments
 (0)