Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KibanaPageLayout] Solution Nav specific styles & props #100089

Merged
merged 18 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added dev_docs/assets/kibana_template_solution_nav.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 35 additions & 2 deletions dev_docs/tutorials/kibana_page_template.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
<KibanaPageTemplate
Expand Down Expand Up @@ -84,3 +84,36 @@ When passing both a `pageHeader` configuration and `isEmptyState`, the component
```

![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)

## `solutionNav`

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

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).

When using `EuiSideNav`, root level items should not be linked but provide section labelling only.

```tsx
<KibanaPageTemplate
solutionNav={{
name: 'Management',
icon: 'managementApp',
items: [
{
name: 'Root item',
items: [
{ name: 'Navigation item', href: '#' },
{ name: 'Navigation item', href: '#' },
]
}
]
}}
>
{...}
</KibanaPageTemplate>
```


![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)
7 changes: 7 additions & 0 deletions src/core/public/rendering/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions src/plugins/kibana_react/public/page_template/page_template.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -66,4 +106,23 @@ describe('KibanaPageTemplate', () => {
);
expect(component).toMatchSnapshot();
});

test('render solutionNav', () => {
const component = shallow(
<KibanaPageTemplate
pageHeader={{
iconType: 'test',
title: 'test',
description: 'test',
rightSideItems: ['test'],
}}
solutionNav={{
name: 'Solution',
icon: 'solution',
items: navItems,
}}
/>
);
expect(component).toMatchSnapshot();
});
});
35 changes: 33 additions & 2 deletions src/plugins/kibana_react/public/page_template/page_template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<KibanaPageTemplateProps> = ({
Expand All @@ -27,6 +42,8 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
restrictWidth = true,
bottomBar,
bottomBarProps,
pageSideBar,
solutionNav,
...rest
}) => {
// Needed for differentiating between union types
Expand All @@ -38,6 +55,13 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
};
}

/**
* Create the solution nav component
*/
if (solutionNav) {
pageSideBar = <KibanaPageTemplateSolutionNav {...solutionNav} />;
}

/**
* An easy way to create the right content for empty pages
*/
Expand All @@ -48,6 +72,7 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
children = (
<EuiEmptyPrompt
iconType={iconType}
iconColor={''} // This is likely a solution or app logo, so keep it multi-color
title={pageTitle ? <h1>{pageTitle}</h1> : undefined}
body={description ? <p>{description}</p> : undefined}
actions={rightSideItems}
Expand All @@ -62,8 +87,14 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
return (
<EuiPageTemplate
template={template}
pageHeader={pageHeader}
restrictWidth={restrictWidth}
paddingSize={template === 'centeredBody' ? 'none' : 'l'}
pageHeader={pageHeader}
pageSideBar={pageSideBar}
pageSideBarProps={{
...rest.pageSideBarProps,
className: classNames('kbnPageTemplate__pageSideBar', rest.pageSideBarProps?.className),
}}
{...localBottomBarProps}
{...rest}
>
Expand Down
Loading