mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[KibanaPageLayout] Solution Nav specific styles & props (#100089)
* 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
This commit is contained in:
parent
111e15a054
commit
bca1c14f9c
18 changed files with 755 additions and 5 deletions
BIN
dev_docs/assets/kibana_template_solution_nav.png
Normal file
BIN
dev_docs/assets/kibana_template_solution_nav.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 140 KiB |
BIN
dev_docs/assets/kibana_template_solution_nav_mobile.png
Normal file
BIN
dev_docs/assets/kibana_template_solution_nav_mobile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
|
@ -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
|
||||
|
@ -84,3 +84,36 @@ When passing both a `pageHeader` configuration and `isEmptyState`, the component
|
|||
```
|
||||
|
||||

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

|
||||
|
||||

|
||||
|
|
|
@ -9,7 +9,7 @@ pageLoadAssetSize:
|
|||
charts: 195358
|
||||
cloud: 21076
|
||||
console: 46091
|
||||
core: 414000
|
||||
core: 432925
|
||||
crossClusterReplication: 65408
|
||||
dashboard: 374194
|
||||
dashboardEnhanced: 65646
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
exports[`KibanaPageTemplate render basic template 1`] = `
|
||||
<EuiPageTemplate
|
||||
paddingSize="l"
|
||||
pageHeader={
|
||||
Object {
|
||||
"description": "test",
|
||||
|
@ -12,12 +13,23 @@ exports[`KibanaPageTemplate render basic template 1`] = `
|
|||
"title": "test",
|
||||
}
|
||||
}
|
||||
pageSideBarProps={
|
||||
Object {
|
||||
"className": "kbnPageTemplate__pageSideBar",
|
||||
}
|
||||
}
|
||||
restrictWidth={true}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KibanaPageTemplate render custom empty prompt only 1`] = `
|
||||
<EuiPageTemplate
|
||||
paddingSize="none"
|
||||
pageSideBarProps={
|
||||
Object {
|
||||
"className": "kbnPageTemplate__pageSideBar",
|
||||
}
|
||||
}
|
||||
restrictWidth={true}
|
||||
template="centeredBody"
|
||||
>
|
||||
|
@ -33,6 +45,7 @@ exports[`KibanaPageTemplate render custom empty prompt only 1`] = `
|
|||
|
||||
exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = `
|
||||
<EuiPageTemplate
|
||||
paddingSize="l"
|
||||
pageHeader={
|
||||
Object {
|
||||
"description": "test",
|
||||
|
@ -43,6 +56,11 @@ exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = `
|
|||
"title": "test",
|
||||
}
|
||||
}
|
||||
pageSideBarProps={
|
||||
Object {
|
||||
"className": "kbnPageTemplate__pageSideBar",
|
||||
}
|
||||
}
|
||||
restrictWidth={true}
|
||||
template="centeredContent"
|
||||
>
|
||||
|
@ -58,6 +76,12 @@ exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = `
|
|||
|
||||
exports[`KibanaPageTemplate render default empty prompt 1`] = `
|
||||
<EuiPageTemplate
|
||||
paddingSize="none"
|
||||
pageSideBarProps={
|
||||
Object {
|
||||
"className": "kbnPageTemplate__pageSideBar",
|
||||
}
|
||||
}
|
||||
restrictWidth={true}
|
||||
template="centeredBody"
|
||||
>
|
||||
|
@ -72,7 +96,76 @@ exports[`KibanaPageTemplate render default empty prompt 1`] = `
|
|||
test
|
||||
</p>
|
||||
}
|
||||
iconColor=""
|
||||
iconType="test"
|
||||
/>
|
||||
</EuiPageTemplate>
|
||||
`;
|
||||
|
||||
exports[`KibanaPageTemplate render solutionNav 1`] = `
|
||||
<EuiPageTemplate
|
||||
paddingSize="l"
|
||||
pageHeader={
|
||||
Object {
|
||||
"description": "test",
|
||||
"iconType": "test",
|
||||
"rightSideItems": Array [
|
||||
"test",
|
||||
],
|
||||
"title": "test",
|
||||
}
|
||||
}
|
||||
pageSideBar={
|
||||
<KibanaPageTemplateSolutionNav
|
||||
icon="solution"
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "1.1",
|
||||
"name": "Ingest Node Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.2",
|
||||
"name": "Logstash Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.3",
|
||||
"name": "Beats Central Management",
|
||||
},
|
||||
],
|
||||
"name": "Ingest",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "2.1",
|
||||
"name": "Index Management",
|
||||
},
|
||||
Object {
|
||||
"id": "2.2",
|
||||
"name": "Index Lifecycle Policies",
|
||||
},
|
||||
Object {
|
||||
"id": "2.3",
|
||||
"name": "Snapshot and Restore",
|
||||
},
|
||||
],
|
||||
"name": "Data",
|
||||
},
|
||||
]
|
||||
}
|
||||
name="Solution"
|
||||
/>
|
||||
}
|
||||
pageSideBarProps={
|
||||
Object {
|
||||
"className": "kbnPageTemplate__pageSideBar",
|
||||
}
|
||||
}
|
||||
restrictWidth={true}
|
||||
/>
|
||||
`;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
<KibanaPageTemplate
|
||||
pageHeader={{
|
||||
iconType: 'test',
|
||||
title: 'test',
|
||||
description: 'test',
|
||||
rightSideItems: ['test'],
|
||||
}}
|
||||
solutionNav={{
|
||||
name: 'Solution',
|
||||
icon: 'solution',
|
||||
items: navItems,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 React, { FunctionComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
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<KibanaPageTemplateProps> = ({
|
||||
|
@ -27,6 +42,8 @@ export const KibanaPageTemplate: FunctionComponent<KibanaPageTemplateProps> = ({
|
|||
restrictWidth = true,
|
||||
bottomBar,
|
||||
bottomBarProps,
|
||||
pageSideBar,
|
||||
solutionNav,
|
||||
...rest
|
||||
}) => {
|
||||
// Needed for differentiating between union types
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
>
|
||||
|
|
238
src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap
generated
Normal file
238
src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,238 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KibanaPageTemplateSolutionNav accepts EuiSideNavProps 1`] = `
|
||||
<div
|
||||
className="kbnPageTemplateSolutionNav"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnPageTemplateSolutionNav__title"
|
||||
id="kbnPageTemplateSolutionNav__title_generated-id"
|
||||
size="xs"
|
||||
>
|
||||
<h2>
|
||||
<strong>
|
||||
Solution
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSideNav
|
||||
aria-labelledby="kbnPageTemplateSolutionNav__title_generated-id"
|
||||
data-test-subj="DTS"
|
||||
isOpenOnMobile={false}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "1.1",
|
||||
"name": "Ingest Node Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.2",
|
||||
"name": "Logstash Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.3",
|
||||
"name": "Beats Central Management",
|
||||
},
|
||||
],
|
||||
"name": "Ingest",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "2.1",
|
||||
"name": "Index Management",
|
||||
},
|
||||
Object {
|
||||
"id": "2.2",
|
||||
"name": "Index Lifecycle Policies",
|
||||
},
|
||||
Object {
|
||||
"id": "2.3",
|
||||
"name": "Snapshot and Restore",
|
||||
},
|
||||
],
|
||||
"name": "Data",
|
||||
},
|
||||
]
|
||||
}
|
||||
mobileTitle={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
defaultMessage="{solutionName} Menu"
|
||||
id="kibana-react.pageTemplate.solutionNav.mobileTitleText"
|
||||
values={
|
||||
Object {
|
||||
"solutionName": "Solution",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
toggleOpenOnMobile={[Function]}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`KibanaPageTemplateSolutionNav renders 1`] = `
|
||||
<div
|
||||
className="kbnPageTemplateSolutionNav"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnPageTemplateSolutionNav__title"
|
||||
id="kbnPageTemplateSolutionNav__title_generated-id"
|
||||
size="xs"
|
||||
>
|
||||
<h2>
|
||||
<strong>
|
||||
Solution
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSideNav
|
||||
aria-labelledby="kbnPageTemplateSolutionNav__title_generated-id"
|
||||
isOpenOnMobile={false}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "1.1",
|
||||
"name": "Ingest Node Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.2",
|
||||
"name": "Logstash Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.3",
|
||||
"name": "Beats Central Management",
|
||||
},
|
||||
],
|
||||
"name": "Ingest",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "2.1",
|
||||
"name": "Index Management",
|
||||
},
|
||||
Object {
|
||||
"id": "2.2",
|
||||
"name": "Index Lifecycle Policies",
|
||||
},
|
||||
Object {
|
||||
"id": "2.3",
|
||||
"name": "Snapshot and Restore",
|
||||
},
|
||||
],
|
||||
"name": "Data",
|
||||
},
|
||||
]
|
||||
}
|
||||
mobileTitle={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
defaultMessage="{solutionName} Menu"
|
||||
id="kibana-react.pageTemplate.solutionNav.mobileTitleText"
|
||||
values={
|
||||
Object {
|
||||
"solutionName": "Solution",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
toggleOpenOnMobile={[Function]}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`KibanaPageTemplateSolutionNav renders with icon 1`] = `
|
||||
<div
|
||||
className="kbnPageTemplateSolutionNav"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnPageTemplateSolutionNav__title"
|
||||
id="kbnPageTemplateSolutionNav__title_generated-id"
|
||||
size="xs"
|
||||
>
|
||||
<h2>
|
||||
<KibanaPageTemplateSolutionNavAvatar
|
||||
iconType="logoElastic"
|
||||
name="Solution"
|
||||
/>
|
||||
<strong>
|
||||
Solution
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSideNav
|
||||
aria-labelledby="kbnPageTemplateSolutionNav__title_generated-id"
|
||||
isOpenOnMobile={false}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "1.1",
|
||||
"name": "Ingest Node Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.2",
|
||||
"name": "Logstash Pipelines",
|
||||
},
|
||||
Object {
|
||||
"id": "1.3",
|
||||
"name": "Beats Central Management",
|
||||
},
|
||||
],
|
||||
"name": "Ingest",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "2.1",
|
||||
"name": "Index Management",
|
||||
},
|
||||
Object {
|
||||
"id": "2.2",
|
||||
"name": "Index Lifecycle Policies",
|
||||
},
|
||||
Object {
|
||||
"id": "2.3",
|
||||
"name": "Snapshot and Restore",
|
||||
},
|
||||
],
|
||||
"name": "Data",
|
||||
},
|
||||
]
|
||||
}
|
||||
mobileTitle={
|
||||
<h2>
|
||||
<KibanaPageTemplateSolutionNavAvatar
|
||||
iconType="logoElastic"
|
||||
name="Solution"
|
||||
/>
|
||||
<FormattedMessage
|
||||
defaultMessage="{solutionName} Menu"
|
||||
id="kibana-react.pageTemplate.solutionNav.mobileTitleText"
|
||||
values={
|
||||
Object {
|
||||
"solutionName": "Solution",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
toggleOpenOnMobile={[Function]}
|
||||
/>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,10 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KibanaPageTemplateSolutionNavAvatar renders 1`] = `
|
||||
<EuiAvatar
|
||||
className="kbnPageTemplateSolutionNavAvatar"
|
||||
color="plain"
|
||||
iconType="logoElastic"
|
||||
name="Solution"
|
||||
/>
|
||||
`;
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav';
|
||||
export {
|
||||
KibanaPageTemplateSolutionNavAvatar,
|
||||
KibanaPageTemplateSolutionNavAvatarProps,
|
||||
} from './solution_nav_avatar';
|
|
@ -0,0 +1,22 @@
|
|||
.kbnPageTemplateSolutionNav__title {
|
||||
margin-bottom: $euiSizeL;
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xs','s') {
|
||||
.kbnPageTemplateSolutionNav {
|
||||
// TODO: Fix in EUI
|
||||
.euiSideNav__mobileToggle {
|
||||
height: auto;
|
||||
font-size: $euiFontSizeM;
|
||||
|
||||
.euiButtonEmpty__text {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rely on the `mobileToggle` of the EuiSideNav component to title the navigation list
|
||||
.kbnPageTemplateSolutionNav__title {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav';
|
||||
|
||||
const items: 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('KibanaPageTemplateSolutionNav', () => {
|
||||
test('renders', () => {
|
||||
const component = shallow(<KibanaPageTemplateSolutionNav name="Solution" items={items} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders with icon', () => {
|
||||
const component = shallow(
|
||||
<KibanaPageTemplateSolutionNav name="Solution" icon="logoElastic" items={items} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('accepts EuiSideNavProps', () => {
|
||||
const component = shallow(
|
||||
<KibanaPageTemplateSolutionNav name="Solution" data-test-subj="DTS" items={items} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import './solution_nav.scss';
|
||||
|
||||
import React, { FunctionComponent, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { EuiTitle, EuiSideNav, EuiSideNavProps, htmlIdGenerator } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
KibanaPageTemplateSolutionNavAvatar,
|
||||
KibanaPageTemplateSolutionNavAvatarProps,
|
||||
} from './solution_nav_avatar';
|
||||
|
||||
export type KibanaPageTemplateSolutionNavProps = EuiSideNavProps<{}> & {
|
||||
/**
|
||||
* Name of the solution, i.e. "Observability"
|
||||
*/
|
||||
name: KibanaPageTemplateSolutionNavAvatarProps['name'];
|
||||
/**
|
||||
* Solution logo, i.e. "logoObservability"
|
||||
*/
|
||||
icon?: KibanaPageTemplateSolutionNavAvatarProps['iconType'];
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper around EuiSideNav but also creates the appropriate title with optional solution logo
|
||||
*/
|
||||
export const KibanaPageTemplateSolutionNav: FunctionComponent<KibanaPageTemplateSolutionNavProps> = ({
|
||||
name,
|
||||
icon,
|
||||
items,
|
||||
...rest
|
||||
}) => {
|
||||
const [isSideNavOpenOnMobile, setisSideNavOpenOnMobile] = useState(false);
|
||||
const toggleOpenOnMobile = () => {
|
||||
setisSideNavOpenOnMobile(!isSideNavOpenOnMobile);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the avatar.
|
||||
*/
|
||||
let solutionAvatar;
|
||||
if (icon) {
|
||||
solutionAvatar = <KibanaPageTemplateSolutionNavAvatar iconType={icon} name={name} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the required title.
|
||||
* a11y: Since the heading can't be nested inside `<nav>`, we have to hook them up via `aria-labelledby`
|
||||
*/
|
||||
const titleID = htmlIdGenerator('kbnPageTemplateSolutionNav__title')();
|
||||
const solutionNavTitle = (
|
||||
<EuiTitle size="xs" id={titleID} className="kbnPageTemplateSolutionNav__title">
|
||||
<h2>
|
||||
{solutionAvatar}
|
||||
<strong>{name}</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
);
|
||||
|
||||
/**
|
||||
* Create the side nav component
|
||||
*/
|
||||
let sideNav;
|
||||
if (items) {
|
||||
const mobileTitleText = (
|
||||
<FormattedMessage
|
||||
id="kibana-react.pageTemplate.solutionNav.mobileTitleText"
|
||||
defaultMessage="{solutionName} Menu"
|
||||
values={{ solutionName: name || 'Navigation' }}
|
||||
/>
|
||||
);
|
||||
|
||||
sideNav = (
|
||||
<EuiSideNav
|
||||
aria-labelledby={titleID}
|
||||
mobileTitle={
|
||||
<h2>
|
||||
{solutionAvatar}
|
||||
{mobileTitleText}
|
||||
</h2>
|
||||
}
|
||||
toggleOpenOnMobile={toggleOpenOnMobile}
|
||||
isOpenOnMobile={isSideNavOpenOnMobile}
|
||||
items={items}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="kbnPageTemplateSolutionNav">
|
||||
{solutionNavTitle}
|
||||
{sideNav}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
.kbnPageTemplateSolutionNavAvatar {
|
||||
@include euiBottomShadowSmall;
|
||||
margin-right: $euiSize;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { KibanaPageTemplateSolutionNavAvatar } from './solution_nav_avatar';
|
||||
|
||||
describe('KibanaPageTemplateSolutionNavAvatar', () => {
|
||||
test('renders', () => {
|
||||
const component = shallow(
|
||||
<KibanaPageTemplateSolutionNavAvatar name="Solution" iconType="logoElastic" />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import './solution_nav_avatar.scss';
|
||||
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { EuiAvatar, EuiAvatarProps } from '@elastic/eui';
|
||||
|
||||
export type KibanaPageTemplateSolutionNavAvatarProps = EuiAvatarProps;
|
||||
|
||||
/**
|
||||
* Applies extra styling to a typical EuiAvatar
|
||||
*/
|
||||
export const KibanaPageTemplateSolutionNavAvatar: FunctionComponent<KibanaPageTemplateSolutionNavAvatarProps> = ({
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
<EuiAvatar
|
||||
className={classNames('kbnPageTemplateSolutionNavAvatar', className)}
|
||||
color="plain"
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue