Skip to content

Commit 9da91b7

Browse files
committed
add components dir, forgot with last commit
1 parent b2d1a92 commit 9da91b7

6 files changed

Lines changed: 333 additions & 0 deletions

File tree

src/plugins/management/public/components/__snapshots__/sidebar_nav.test.ts.snap

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import './sidebar_nav';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.mgtSidebarNav {
2+
width: 192px;
3+
}
4+
5+
@include euiBreakpoint('xs','s') {
6+
.mgtSideBarNav {
7+
width: auto;
8+
margin-bottom: $euiSize;
9+
}
10+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
export { SidebarNav } from './sidebar_nav';
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { IndexedArray } from '../../../../legacy/ui/public/indexed_array';
21+
import { sideNavItems } from '../components/sidebar_nav';
22+
23+
const toIndexedArray = (initialSet: any[]) =>
24+
new IndexedArray({
25+
index: ['id'],
26+
order: ['order'],
27+
initialSet,
28+
});
29+
30+
const visibleItem = { title: 'item', id: 'item' };
31+
const registerApp = () => {};
32+
33+
const notVisibleSection = {
34+
title: 'Not visible',
35+
id: 'not-visible',
36+
apps: toIndexedArray([visibleItem]),
37+
registerApp,
38+
};
39+
const disabledSection = {
40+
title: 'Disabled',
41+
id: 'disabled',
42+
apps: toIndexedArray([visibleItem]),
43+
registerApp,
44+
};
45+
const noItemsSection = {
46+
title: 'No items',
47+
id: 'no-items',
48+
apps: toIndexedArray([]),
49+
registerApp,
50+
};
51+
const noActiveItemsSection = {
52+
title: 'No active items',
53+
id: 'no-active-items',
54+
apps: toIndexedArray([
55+
{ display: 'disabled', id: 'disabled' },
56+
{ display: 'notVisible', id: 'notVisible' },
57+
]),
58+
registerApp,
59+
};
60+
const activeSection = {
61+
title: 'activeSection',
62+
id: 'activeSection',
63+
apps: toIndexedArray([visibleItem]),
64+
registerApp,
65+
};
66+
67+
const managementSections = [
68+
notVisibleSection,
69+
disabledSection,
70+
noItemsSection,
71+
noActiveItemsSection,
72+
activeSection,
73+
];
74+
75+
describe('Management', () => {
76+
it('filters and filters and maps section objects into SidebarNav items', () => {
77+
expect(sideNavItems(managementSections, 'active-item-id')).toMatchSnapshot();
78+
});
79+
});
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import {
21+
EuiIcon,
22+
// @ts-ignore
23+
EuiSideNav,
24+
IconType,
25+
EuiScreenReaderOnly,
26+
} from '@elastic/eui';
27+
import { FormattedMessage } from '@kbn/i18n/react';
28+
import { i18n } from '@kbn/i18n';
29+
import React from 'react';
30+
import {
31+
ManagementApp,
32+
ISection as ManagementSection,
33+
} from '../../../../plugins/management/public';
34+
35+
interface LegacySection extends LegacyApp {
36+
items: LegacyApp[];
37+
}
38+
39+
interface LegacyApp {
40+
disabled: boolean;
41+
visible: boolean;
42+
id: string;
43+
display: string;
44+
url?: string;
45+
euiIconType?: IconType;
46+
icon?: string;
47+
}
48+
49+
interface NavApp {
50+
[key: string]: unknown;
51+
}
52+
53+
interface NavSection extends NavApp {
54+
items: NavApp[];
55+
}
56+
57+
interface SidebarNavProps {
58+
sections: ManagementSection[];
59+
legacySections: LegacySection[];
60+
selectedId: string;
61+
}
62+
63+
interface SidebarNavState {
64+
isSideNavOpenOnMobile: boolean;
65+
}
66+
67+
const managementSectionOrAppToNav = (appOrSection: ManagementApp | ManagementSection) => ({
68+
id: appOrSection.id,
69+
name: appOrSection.title,
70+
'data-test-subj': appOrSection.id,
71+
});
72+
73+
const managementSectionToNavSection = (section: ManagementSection) => ({
74+
// todo support icon as url path
75+
icon: section.euiIconType ? <EuiIcon type={section.euiIconType} /> : null,
76+
...managementSectionOrAppToNav(section),
77+
});
78+
79+
const managementAppToNavItem = (selectedId?: string, parentId?: string) => (
80+
app: ManagementApp
81+
) => ({
82+
isSelected: selectedId === app.id,
83+
href: `#/management/${parentId}/${app.id}`,
84+
...managementSectionOrAppToNav(app),
85+
});
86+
87+
const legacySectionToNavSection = (section: LegacySection) => ({
88+
name: section.display,
89+
id: section.id,
90+
icon: section.icon ? <EuiIcon type={section.icon} /> : null,
91+
items: [],
92+
});
93+
94+
const legacyAppToNavItem = (app: LegacyApp, selectedId: string) => ({
95+
isSelected: selectedId === app.id,
96+
name: app.display,
97+
id: app.id,
98+
href: app.url,
99+
'data-test-subj': app.id,
100+
});
101+
102+
const sectionVisible = (section: LegacySection | LegacyApp) => !section.disabled && section.visible;
103+
104+
export const sideNavItems = (sections: ManagementSection[], selectedId: string) =>
105+
sections
106+
// .filter(sectionVisible)
107+
// .filter(section => section.items.filter(sectionVisible).length)
108+
.map(section => ({
109+
// items: section.items.filter(sectionVisible).map(sectionToNav(selectedId)),
110+
items: section.apps.map(managementAppToNavItem(selectedId, section.id)),
111+
...managementSectionToNavSection(section),
112+
}));
113+
114+
const findOrAddSection = (navItems: NavSection[], legacySection: LegacySection): NavSection => {
115+
const foundSection = navItems.find(sec => sec.id === legacySection.id);
116+
117+
if (foundSection) {
118+
return foundSection;
119+
} else {
120+
const newSection = legacySectionToNavSection(legacySection);
121+
navItems.push(newSection);
122+
return newSection;
123+
}
124+
};
125+
126+
const mergeLegacyItems = (
127+
navItems: NavSection[],
128+
legacySections: LegacySection[],
129+
selectedId: string
130+
) => {
131+
// todo make sure filtering disabled apps
132+
const filteredLegacySections = legacySections
133+
.filter(sectionVisible)
134+
.filter(section => section.items.filter(sectionVisible).length);
135+
136+
filteredLegacySections.forEach(legacySection => {
137+
const section = findOrAddSection(navItems, legacySection);
138+
legacySection.items.forEach(app => section.items.push(legacyAppToNavItem(app, selectedId)));
139+
});
140+
141+
return navItems;
142+
};
143+
144+
const sectionsToItems = (
145+
sections: ManagementSection[],
146+
legacySections: LegacySection[],
147+
selectedId: string
148+
) => {
149+
const navItems = sideNavItems(sections, selectedId);
150+
return mergeLegacyItems(navItems, legacySections, selectedId);
151+
};
152+
153+
export class SidebarNav extends React.Component<SidebarNavProps, SidebarNavState> {
154+
constructor(props: SidebarNavProps) {
155+
super(props);
156+
this.state = {
157+
isSideNavOpenOnMobile: false,
158+
};
159+
}
160+
161+
public render() {
162+
const HEADER_ID = 'management-nav-header';
163+
164+
return (
165+
<>
166+
<EuiScreenReaderOnly>
167+
<h2 id={HEADER_ID}>
168+
{i18n.translate('common.ui.management.nav.label', {
169+
defaultMessage: 'Management',
170+
})}
171+
</h2>
172+
</EuiScreenReaderOnly>
173+
<EuiSideNav
174+
aria-labelledby={HEADER_ID}
175+
mobileTitle={this.renderMobileTitle()}
176+
isOpenOnMobile={this.state.isSideNavOpenOnMobile}
177+
toggleOpenOnMobile={this.toggleOpenOnMobile}
178+
// @ts-ignore
179+
items={sectionsToItems(
180+
this.props.sections,
181+
this.props.legacySections,
182+
this.props.selectedId
183+
)}
184+
className="mgtSideBarNav"
185+
/>
186+
</>
187+
);
188+
}
189+
190+
private renderMobileTitle() {
191+
return <FormattedMessage id="common.ui.management.nav.menu" defaultMessage="Management menu" />;
192+
}
193+
194+
private toggleOpenOnMobile = () => {
195+
this.setState({
196+
isSideNavOpenOnMobile: !this.state.isSideNavOpenOnMobile,
197+
});
198+
};
199+
}

0 commit comments

Comments
 (0)