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

New nav #64018

Merged
merged 54 commits into from
May 6, 2020
Merged

New nav #64018

Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3aaeefe
Fix height calc to use correct EUI variable
MichaelMarcialis Apr 21, 2020
b315034
collapsible nav
Apr 23, 2020
23eeebc
fixing PR feedback
Apr 23, 2020
90e537b
updated category public api
Apr 23, 2020
ebfa324
fixing first few tests
Apr 23, 2020
84b3f2b
undoing visualize->visualization library naming
Apr 24, 2020
91fcde6
move recently viewed to the top
Apr 24, 2020
b35eacc
hotfix apps sometimes displaying a scrollbar
Apr 24, 2020
811cde3
fix euiBottomBar width calculation
Apr 24, 2020
7b8a016
translation file fix
Apr 24, 2020
ea92cd7
displaying links without a category
Apr 24, 2020
2875a35
more test fixed
Apr 24, 2020
affb4d9
Merge remote-tracking branch 'upstream/master' into new-nav
Apr 24, 2020
d1259c5
Revert "fix euiBottomBar width calculation"
Apr 27, 2020
2f3a1cf
better todo naming
Apr 27, 2020
aec8305
safely migrate bottomBar width to new nav widths
Apr 27, 2020
27531e6
addressing feedback from @pgayvallet
Apr 27, 2020
86fb664
fix visualization full screen issues
Apr 27, 2020
bbb0a33
fixing more tests
Apr 27, 2020
04fea87
fixing painless lab bottombar popover
Apr 28, 2020
9591a6f
pass onclick to nav items without category
Apr 28, 2020
9568d18
documenting category id and getNavType
Apr 28, 2020
31eec3e
Merge remote-tracking branch 'upstream/master' into new-nav
Apr 28, 2020
7c16e3a
fixing more tests
Apr 28, 2020
1f84027
fixing width overflow with legacy nav
MichaelMarcialis Apr 28, 2020
21e6255
additional test fixes
Apr 29, 2020
4891ee9
updating EUI to 22.3.1
Apr 29, 2020
421e687
fixing more tests...
Apr 29, 2020
1cc0fb5
fixing jest + a11y tests
Apr 30, 2020
283d9d0
fixing more jest + a11y tests
Apr 30, 2020
216cac8
Merge remote-tracking branch 'upstream/master' into new-nav
Apr 30, 2020
975b08c
Merge branch 'master' into new-nav
elasticmachine Apr 30, 2020
4bc97a7
Merge branch 'master' into new-nav
elasticmachine Apr 30, 2020
86f7830
Merge branch 'master' into new-nav
elasticmachine Apr 30, 2020
797dfa9
jest + a11y tests fixes
Apr 30, 2020
757df00
fixing plugin categories
Apr 30, 2020
dbb49a0
Merge remote-tracking branch 'upstream/master' into new-nav
Apr 30, 2020
b612739
fixing the last remaining tests, I hope
Apr 30, 2020
bf1b6ee
fixing discvoer test failure
May 1, 2020
1fe8bb9
implementing PR feedback and adding basic test coverage
May 5, 2020
08d83d5
Merge remote-tracking branch 'upstream/master' into new-nav
May 5, 2020
db25a51
fixing schema failure
May 5, 2020
7c61b6c
fixing canvas category type
May 5, 2020
d7f1ba1
Merge remote-tracking branch 'upstream/master' into new-nav
May 5, 2020
ca23f13
Merge remote-tracking branch 'upstream/master' into new-nav
May 5, 2020
2d57e30
Merge remote-tracking branch 'upstream/master' into new-nav
May 5, 2020
96bf7c0
attempting to fix a merge conflict
May 5, 2020
5dbc9c8
fixing react types because yarn is having a hard time
May 5, 2020
153cf49
Merge remote-tracking branch 'upstream/master' into new-nav
May 5, 2020
a947add
Merge remote-tracking branch 'upstream/master' into new-nav
May 5, 2020
485a4ee
fix DEFAULT_APP_CATEGORIES import
May 5, 2020
02dac24
update public api
May 6, 2020
4690a2f
Merge remote-tracking branch 'upstream/master' into new-nav
May 6, 2020
9c98447
Merge remote-tracking branch 'upstream/master' into new-nav
May 6, 2020
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
3 changes: 3 additions & 0 deletions docs/management/advanced-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ into the document when displaying it.
`metrics:max_buckets`:: The maximum numbers of buckets that a single
data source can return. This might arise when the user selects a
short interval (for example, 1s) for a long time period (1 year).
`pageNavigation`:: The style of navigation menu for Kibana.
Choices are Legacy, the legacy style where every plugin is represented in the nav,
and Modern, a new format that bundles related plugins together in flyaway nested navigation.
`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character
in a query clause. Only applies when experimental query features are
enabled in the query bar. To disallow leading wildcards in Lucene queries,
Expand Down
5 changes: 5 additions & 0 deletions src/core/public/_core.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@
overflow-x: hidden;
min-width: 100%;
min-height: 100%;

// TODO remove when EUI is updated
&.euiBody--headerIsFixed {
padding-top: 49px;
}
myasonik marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions src/core/public/chrome/chrome_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export class ChromeService {
navControlsRight$={navControls.getRight$()}
onIsLockedUpdate={setIsNavDrawerLocked}
isLocked$={getIsNavDrawerLocked$}
navSetting$={uiSettings.get$('pageNavigation')}
/>
</React.Fragment>
),
Expand Down
16 changes: 12 additions & 4 deletions src/core/public/chrome/ui/header/_index.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
@import '@elastic/eui/src/components/header/variables';
@import '@elastic/eui/src/components/nav_drawer/variables';

.chrHeaderWrapper {
// TODO when legacy nav removed
// Delete this block
.chrHeaderWrapper:not(.headerWrapper) {
width: 100%;
position: fixed;
top: 0;
z-index: 10;
}

.chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) {
// TODO when legacy nav removed
// Delete this block
.chrHeaderWrapper:not(.headerWrapper) ~ .app-wrapper:not(.hidden-chrome) {
top: $euiHeaderChildSize;
left: $euiHeaderChildSize;

Expand All @@ -30,14 +34,18 @@
}

// Mobile header is smaller
// TODO when legacy nav removed
// Delete this block
@include euiBreakpoint('xs', 's') {
.chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) {
.chrHeaderWrapper:not(.headerWrapper) ~ .app-wrapper:not(.hidden-chrome) {
left: 0;
}
}

// TODO when legacy nav removed
// Delete this block
@include euiBreakpoint('xl') {
.chrHeaderWrapper--navIsLocked {
.chrHeaderWrapper--navIsLocked:not(.headerWrapper) {
~ .app-wrapper:not(.hidden-chrome) {
// Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS)
left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important
Expand Down
262 changes: 262 additions & 0 deletions src/core/public/chrome/ui/header/collapsible_nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/*
myasonik marked this conversation as resolved.
Show resolved Hide resolved
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiCollapsibleNav,
EuiCollapsibleNavGroup,
EuiListGroup,
EuiListGroupItem,
EuiFlexItem,
EuiHorizontalRule,
EuiShowFor,
EuiText,
} from '@elastic/eui';
import { groupBy, sortBy } from 'lodash';
import { OnIsLockedUpdate } from './';
import { AppCategory } from '../../../../types';
import { NavLink, RecentNavLink } from './nav_link';

function getAllCategories(allCategorizedLinks: Record<string, NavLink[]>) {
const allCategories = {} as Record<string, AppCategory | undefined>;

for (const [key, value] of Object.entries(allCategorizedLinks)) {
allCategories[key] = value[0].category;
}

return allCategories;
}

function getOrderedCategories(
mainCategories: Record<string, NavLink[]>,
categoryDictionary: ReturnType<typeof getAllCategories>
myasonik marked this conversation as resolved.
Show resolved Hide resolved
) {
return sortBy(
Object.keys(mainCategories),
categoryName => categoryDictionary[categoryName]?.order
);
}

function getCategoryLocalStorageKey(label: string) {
return `core.navGroup.${label}`;
}

function getIsCategoryOpen(label: string) {
const value = localStorage.getItem(getCategoryLocalStorageKey(label)) ?? 'true';

return value === 'true';
}
myasonik marked this conversation as resolved.
Show resolved Hide resolved

function setIsCategoryOpen(label: string, isOpen: boolean) {
localStorage.setItem(getCategoryLocalStorageKey(label), `${isOpen}`);
}

interface Props {
isLocked: boolean;
isOpen: boolean;
navLinks: NavLink[];
recentNavLinks: RecentNavLink[];
homeHref: string;
id: string;
onIsLockedUpdate: OnIsLockedUpdate;
onIsOpenUpdate: (isOpen?: boolean) => void;
}

export function CollapsibleNav({
isLocked,
isOpen,
navLinks,
recentNavLinks,
onIsLockedUpdate,
onIsOpenUpdate,
homeHref,
id,
}: Props) {
const groupedNavLinks = groupBy(navLinks, link => link?.category?.label);
const { undefined: unknowns = [], ...allCategorizedLinks } = groupedNavLinks;
const categoryDictionary = getAllCategories(allCategorizedLinks);
const orderedCategories = getOrderedCategories(allCategorizedLinks, categoryDictionary);

return (
<EuiCollapsibleNav
data-test-subj="collapsibleNav"
id={id}
aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', {
defaultMessage: 'Primary',
})}
isOpen={isOpen}
isDocked={isLocked}
onClose={onIsOpenUpdate}
>
{/* Pinned items */}
<EuiFlexItem grow={false} style={{ flexShrink: 0 }}>
<EuiCollapsibleNavGroup
background="light"
className="eui-yScroll"
style={{ maxHeight: '40vh' }}
>
<EuiListGroup
aria-label={i18n.translate('core.ui.primaryNav.pinnedLinksAriaLabel', {
defaultMessage: 'Pinned links',
})}
listItems={[
{
label: 'Home',
iconType: 'home',
href: homeHref,
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
onIsOpenUpdate(false);
},
myasonik marked this conversation as resolved.
Show resolved Hide resolved
},
]}
maxWidth="none"
color="text"
gutterSize="none"
size="s"
/>
</EuiCollapsibleNavGroup>
</EuiFlexItem>

<EuiHorizontalRule margin="none" />

<EuiFlexItem className="eui-yScroll">
{/* Recently viewed */}
<EuiCollapsibleNavGroup
key="recentlyViewed"
title={i18n.translate('core.ui.recentlyViewed', { defaultMessage: 'Recently viewed' })}
isCollapsible={true}
initialIsOpen={getIsCategoryOpen('recentlyViewed')}
onToggle={isCategoryOpen => setIsCategoryOpen('recentlyViewed', isCategoryOpen)}
>
{recentNavLinks.length > 0 ? (
<EuiListGroup
aria-label={i18n.translate('core.ui.recentlyViewedAriaLabel', {
defaultMessage: 'Recently viewed links',
})}
listItems={recentNavLinks}
maxWidth="none"
color="subdued"
gutterSize="none"
size="s"
/>
) : (
<EuiText size="s" color="subdued" style={{ padding: '0 8px 8px' }}>
<p>
{i18n.translate('core.ui.EmptyRecentlyViewed', {
defaultMessage: 'No recently viewed items',
})}
</p>
</EuiText>
)}
</EuiCollapsibleNavGroup>

{/* Kibana, Observability, Security, and Management sections */}
{orderedCategories.map(categoryName => {
const category = categoryDictionary[categoryName]!;
const links = allCategorizedLinks[categoryName].map(
({ label, href, onClick }: NavLink) => ({
label,
href,
'data-test-subj': 'collapsibleNavAppLink',
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
onIsOpenUpdate(false);
onClick(e);
},
})
);

return (
<EuiCollapsibleNavGroup
key={category.label}
iconType={category.euiIconType}
title={category.label}
isCollapsible={true}
initialIsOpen={getIsCategoryOpen(category.label)}
myasonik marked this conversation as resolved.
Show resolved Hide resolved
onToggle={isCategoryOpen => setIsCategoryOpen(category.label, isCategoryOpen)}
>
<EuiListGroup
aria-label={i18n.translate('core.ui.primaryNavSection.screenReaderLabel', {
defaultMessage: 'Primary navigation links, {category}',
values: { category: category.label },
})}
listItems={links}
maxWidth="none"
color="subdued"
gutterSize="none"
size="s"
/>
</EuiCollapsibleNavGroup>
);
})}

{/* Things with no category (largely for custom plugins) */}
{unknowns.map(linkWithoutCategory => (
<EuiCollapsibleNavGroup>
<EuiListGroup flush>
<EuiListGroupItem
color="text"
size="s"
label={linkWithoutCategory.label}
href={linkWithoutCategory.href}
icon={linkWithoutCategory.icon}
/>
</EuiListGroup>
</EuiCollapsibleNavGroup>
))}

{/* Docking button only for larger screens that can support it*/}
<EuiShowFor sizes={['l', 'xl']}>
<EuiCollapsibleNavGroup>
<EuiListGroup flush>
<EuiListGroupItem
size="xs"
color="subdued"
id="foo"
label={
isLocked
? i18n.translate('core.ui.primaryNavSection.undockLabel', {
defaultMessage: 'Undock navigation',
})
: i18n.translate('core.ui.primaryNavSection.dockLabel', {
defaultMessage: 'Dock navigation',
})
}
aria-label={
isLocked
? i18n.translate('core.ui.primaryNavSection.undockAriaLabel', {
defaultMessage: 'Undock primary navigation',
})
: i18n.translate('core.ui.primaryNavSection.dockAriaLabel', {
defaultMessage: 'Dock primary navigation',
})
}
onClick={() => {
onIsLockedUpdate(!isLocked);
// TODO should keep focus here
}}
iconType={isLocked ? 'lock' : 'lockOpen'}
/>
</EuiListGroup>
</EuiCollapsibleNavGroup>
</EuiShowFor>
</EuiFlexItem>
</EuiCollapsibleNav>
);
}
Loading