-
Notifications
You must be signed in to change notification settings - Fork 841
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 Feature] Final docs examples and patterns #3117
Merged
cchaos
merged 26 commits into
elastic:feature/collapsible_nav
from
cchaos:into-feature/all_together
Mar 25, 2020
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
e8c7f8d
Fixed the passing of `size` from EuiListGroup to items
d52b61c
Fix padding of `EuiCollapsibleNavGroup` if extra action is passed
5a88b29
Reset line-height of heading in button
cd2ef91
Adding doc example for groups with conetnt
a33047f
Adding doc example for groups with conetnt
892400a
Fix `title` type for EuiCollapsibleNavGroup
2dd17b5
Starting full pattern example
108337b
Adjusted EuiFlyout position based on fixed EuiHeader
d7fd664
Utility CSS helper for simple overflow scroll without shadows
806dc67
Adding GuideFullScreen component
7f6a708
Adding content and storing states
7b7ecd0
Fixing incompatible type with `href`
f41afb4
Fix EuiHorizontalSizing when in flex groups
b16effb
Cleanup
0692b9d
Quick fix to nav heading
3974d84
Using subdued text color
7c03d22
Ghost button in dark section for now
1fb6303
Some browser fixes
3c28b86
Fixes for mobile
1940b4d
render prop pattern
thompsongl ab4bad5
clean up
thompsongl fc331d5
Merge pull request #32 from thompsongl/into-feature/all_together
cchaos ecfc0a1
Adding accessibility (?)
d2403be
One more a11y piece
f7c7afe
Addressing some a11y concerns
1f27aa7
More a11y fixes
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React, { | ||
useState, | ||
Fragment, | ||
FunctionComponent, | ||
ReactElement, | ||
ReactNode, | ||
} from 'react'; | ||
|
||
import { EuiFocusTrap } from '../../../../src/components/focus_trap'; | ||
import { EuiButton } from '../../../../src/components/button'; | ||
|
||
export const GuideFullScreen: FunctionComponent<{ | ||
children: (setFullScreen: (isFullScreen: boolean) => void) => ReactElement; | ||
buttonText?: ReactNode; | ||
isFullScreen?: boolean; | ||
}> = ({ | ||
children, | ||
isFullScreen = false, | ||
buttonText = 'Show fullscreen demo', | ||
}) => { | ||
const [fullScreen, setFullScreen] = useState(isFullScreen); | ||
|
||
return ( | ||
<Fragment> | ||
<EuiButton onClick={() => setFullScreen(true)} iconType="fullScreen"> | ||
{buttonText} | ||
</EuiButton> | ||
|
||
{fullScreen && <EuiFocusTrap>{children(setFullScreen)}</EuiFocusTrap>} | ||
</Fragment> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
280 changes: 280 additions & 0 deletions
280
src-docs/src/views/collapsible_nav/collapsible_nav_all.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
import React, { useState } from 'react'; | ||
import _ from 'lodash'; | ||
|
||
import { | ||
EuiCollapsibleNav, | ||
EuiCollapsibleNavToggle, | ||
EuiCollapsibleNavGroup, | ||
} from '../../../../src/components/collapsible_nav'; | ||
import { | ||
EuiHeaderSectionItemButton, | ||
EuiHeaderLogo, | ||
EuiHeader, | ||
} from '../../../../src/components/header'; | ||
import { EuiIcon } from '../../../../src/components/icon'; | ||
import { EuiButtonEmpty } from '../../../../src/components/button'; | ||
import { EuiPage } from '../../../../src/components/page'; | ||
import { | ||
EuiPinnableListGroup, | ||
EuiListGroupItem, | ||
EuiPinnableListGroupItemProps, | ||
} from '../../../../src/components/list_group'; | ||
import { EuiFlexItem } from '../../../../src/components/flex'; | ||
import { EuiHorizontalRule } from '../../../../src/components/horizontal_rule'; | ||
import { GuideFullScreen } from '../../services/full_screen/full_screen'; | ||
|
||
import { | ||
DeploymentsGroup, | ||
KibanaNavLinks, | ||
SecurityGroup, | ||
} from './collapsible_nav_list'; | ||
import { EuiShowFor } from '../../../../src/components/responsive'; | ||
|
||
const TopLinks = [ | ||
{ label: 'Home', iconType: 'home', isActive: true, 'aria-current': true }, | ||
]; | ||
const KibanaLinks: EuiPinnableListGroupItemProps[] = KibanaNavLinks.map( | ||
link => { | ||
return { | ||
...link, | ||
href: '#/navigation/collapsible-nav', | ||
}; | ||
} | ||
); | ||
const LearnLinks: EuiPinnableListGroupItemProps[] = [ | ||
{ label: 'Docs', href: '#/navigation/collapsible-nav' }, | ||
{ label: 'Blogs', href: '#/navigation/collapsible-nav' }, | ||
{ label: 'Webinars', href: '#/navigation/collapsible-nav' }, | ||
{ label: 'Elastic.co', href: 'https://elastic.co' }, | ||
]; | ||
|
||
export default () => { | ||
const [navIsOpen, setNavIsOpen] = useState( | ||
JSON.parse(String(localStorage.getItem('navIsDocked'))) || false | ||
); | ||
const [navIsDocked, setNavIsDocked] = useState( | ||
JSON.parse(String(localStorage.getItem('navIsDocked'))) || false | ||
); | ||
|
||
/** | ||
* Accordion toggling | ||
*/ | ||
const [openGroups, setOpenGroups] = useState( | ||
JSON.parse(String(localStorage.getItem('openNavGroups'))) || [ | ||
'Kibana', | ||
'Learn', | ||
] | ||
); | ||
|
||
// Save which groups are open and which are not with state and local store | ||
const toggleAccordion = (isOpen: boolean, title?: string) => { | ||
if (!title) return; | ||
const itExists = openGroups.includes(title); | ||
if (isOpen) { | ||
if (itExists) return; | ||
openGroups.push(title); | ||
} else { | ||
const index = openGroups.indexOf(title); | ||
if (index > -1) { | ||
openGroups.splice(index, 1); | ||
} | ||
} | ||
setOpenGroups([...openGroups]); | ||
localStorage.setItem('openNavGroups', JSON.stringify(openGroups)); | ||
}; | ||
|
||
/** | ||
* Pinning | ||
*/ | ||
const [pinnedItems, setPinnedItems] = useState< | ||
EuiPinnableListGroupItemProps[] | ||
>(JSON.parse(String(localStorage.getItem('pinnedItems'))) || []); | ||
|
||
const addPin = (item: any) => { | ||
if (!item || _.find(pinnedItems, { label: item.label })) { | ||
return; | ||
} | ||
item.pinned = true; | ||
const newPinnedItems = pinnedItems ? pinnedItems.concat(item) : [item]; | ||
setPinnedItems(newPinnedItems); | ||
localStorage.setItem('pinnedItems', JSON.stringify(newPinnedItems)); | ||
}; | ||
|
||
const removePin = (item: any) => { | ||
const pinIndex = _.findIndex(pinnedItems, { label: item.label }); | ||
if (pinIndex > -1) { | ||
item.pinned = false; | ||
const newPinnedItems = pinnedItems; | ||
newPinnedItems.splice(pinIndex, 1); | ||
setPinnedItems([...newPinnedItems]); | ||
localStorage.setItem('pinnedItems', JSON.stringify(newPinnedItems)); | ||
} | ||
}; | ||
|
||
function alterLinksWithCurrentState( | ||
links: EuiPinnableListGroupItemProps[], | ||
showPinned = false | ||
): EuiPinnableListGroupItemProps[] { | ||
return links.map(link => { | ||
const { pinned, ...rest } = link; | ||
return { | ||
pinned: showPinned ? pinned : false, | ||
...rest, | ||
}; | ||
}); | ||
} | ||
|
||
function addLinkNameToPinTitle(listItem: EuiPinnableListGroupItemProps) { | ||
return `Pin ${listItem.label} to top`; | ||
} | ||
|
||
function addLinkNameToUnpinTitle(listItem: EuiPinnableListGroupItemProps) { | ||
return `Unpin ${listItem.label}`; | ||
} | ||
|
||
const leftSectionItems = [ | ||
<EuiCollapsibleNavToggle navIsDocked={navIsDocked}> | ||
<EuiHeaderSectionItemButton | ||
aria-label="Toggle main navigation" | ||
aria-controls="guideCollapsibleNavAllExampleNav" | ||
aria-expanded={navIsOpen} | ||
aria-pressed={navIsOpen} | ||
onClick={() => setNavIsOpen(!navIsOpen)}> | ||
<EuiIcon type={'menu'} size="m" aria-hidden="true" /> | ||
</EuiHeaderSectionItemButton> | ||
</EuiCollapsibleNavToggle>, | ||
<EuiHeaderLogo iconType="logoElastic">Elastic</EuiHeaderLogo>, | ||
]; | ||
|
||
return ( | ||
<GuideFullScreen> | ||
{setIsFullScreen => ( | ||
<React.Fragment> | ||
<EuiHeader | ||
position="fixed" | ||
sections={[ | ||
{ | ||
items: leftSectionItems, | ||
borders: 'right', | ||
}, | ||
{ | ||
items: [ | ||
<EuiButtonEmpty | ||
iconType="minimize" | ||
onClick={() => setIsFullScreen(false)}> | ||
Exit full screen | ||
</EuiButtonEmpty>, | ||
], | ||
}, | ||
]} | ||
/> | ||
|
||
{navIsOpen && ( | ||
<EuiCollapsibleNav | ||
id="guideCollapsibleNavAllExampleNav" | ||
aria-label="Main navigation" | ||
docked={navIsDocked} | ||
onClose={() => setNavIsOpen(false)}> | ||
{/* Dark deployments section */} | ||
<EuiFlexItem grow={false} style={{ flexShrink: 0 }}> | ||
{DeploymentsGroup} | ||
</EuiFlexItem> | ||
|
||
{/* Shaded pinned section always with a home item */} | ||
<EuiFlexItem grow={false} style={{ flexShrink: 0 }}> | ||
<EuiCollapsibleNavGroup | ||
background="light" | ||
className="eui-yScroll" | ||
style={{ maxHeight: '40vh' }}> | ||
<EuiPinnableListGroup | ||
aria-label="Pinned links" // A11y : Since this group doesn't have a visible `title` it should be provided an accessible description | ||
listItems={alterLinksWithCurrentState(TopLinks).concat( | ||
alterLinksWithCurrentState(pinnedItems, true) | ||
)} | ||
unpinTitle={addLinkNameToUnpinTitle} | ||
onPinClick={removePin} | ||
maxWidth="none" | ||
color="subdued" | ||
gutterSize="none" | ||
size="s" | ||
/> | ||
</EuiCollapsibleNavGroup> | ||
</EuiFlexItem> | ||
|
||
<EuiHorizontalRule margin="none" /> | ||
|
||
{/* BOTTOM */} | ||
<EuiFlexItem className="eui-yScroll"> | ||
{/* Kibana section */} | ||
<EuiCollapsibleNavGroup | ||
title="Kibana" | ||
iconType="logoKibana" | ||
isCollapsible={true} | ||
initialIsOpen={openGroups.includes('Kibana')} | ||
onToggle={(isOpen: boolean) => | ||
toggleAccordion(isOpen, 'Kibana') | ||
}> | ||
<EuiPinnableListGroup | ||
aria-label="Kibana" // A11y : EuiCollapsibleNavGroup can't correctly pass the `title` as the `aria-label` to the right HTML element, so it must be added manually | ||
listItems={alterLinksWithCurrentState(KibanaLinks)} | ||
pinTitle={addLinkNameToPinTitle} | ||
onPinClick={addPin} | ||
maxWidth="none" | ||
color="subdued" | ||
gutterSize="none" | ||
size="s" | ||
/> | ||
</EuiCollapsibleNavGroup> | ||
|
||
{/* Security callout */} | ||
{SecurityGroup} | ||
|
||
{/* Learn section */} | ||
<EuiCollapsibleNavGroup | ||
title="Learn" | ||
iconType="training" | ||
isCollapsible={true} | ||
initialIsOpen={openGroups.includes('Learn')} | ||
onToggle={(isOpen: boolean) => | ||
toggleAccordion(isOpen, 'Learn') | ||
}> | ||
<EuiPinnableListGroup | ||
aria-label="Learn" // A11y : EuiCollapsibleNavGroup can't correctly pass the `title` as the `aria-label` to the right HTML element, so it must be added manually | ||
listItems={alterLinksWithCurrentState(LearnLinks)} | ||
pinTitle={addLinkNameToPinTitle} | ||
onPinClick={addPin} | ||
maxWidth="none" | ||
color="subdued" | ||
gutterSize="none" | ||
size="s" | ||
/> | ||
</EuiCollapsibleNavGroup> | ||
|
||
{/* Docking button only for larger screens that can support it*/} | ||
<EuiShowFor sizes={['l', 'xl']}> | ||
<EuiCollapsibleNavGroup> | ||
<EuiListGroupItem | ||
size="xs" | ||
color="subdued" | ||
label={`${navIsDocked ? 'Undock' : 'Dock'} navigation`} | ||
onClick={() => { | ||
setNavIsDocked(!navIsDocked); | ||
localStorage.setItem( | ||
'navIsDocked', | ||
JSON.stringify(!navIsDocked) | ||
); | ||
}} | ||
iconType={navIsDocked ? 'lock' : 'lockOpen'} | ||
/> | ||
</EuiCollapsibleNavGroup> | ||
</EuiShowFor> | ||
</EuiFlexItem> | ||
</EuiCollapsibleNav> | ||
)} | ||
|
||
<EuiPage className="guideFullScreenOverlay" /> | ||
</React.Fragment> | ||
)} | ||
</GuideFullScreen> | ||
); | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a bunch of style tags in here. I know it's just docs, but likely this stuff will just be copy pasted. Should we make some selectors for these things since they'll need to make it anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's actually good....? Just because they're necessary for the scrolling to work properly...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I guess I just meant, none of these looked dynamic. Should we make some actual css selectors rather than style tags, since they seem necessary for the component to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so here's what's going on with the style tags. Really the only ones are:
flex-shrink: 0
on EuiFlexItemsmaxHeight
on the collapsible nav groupRemove both of those and the nav still works it's just all one big scroll. This implementation is a custom one where we (Kibana) wants the pinned section to scroll independently of the rest. They're not necessary for the Collapsible nav to work.
If we add a class for no. 1, It would be some arbitrary
.eui-flexShrinkNone
which I really don't think we want to go down this utility class path. Or it would be a specific.euiCollapsibleNavGroup__wrapper
that simply adds the that one property in which case most consumers won't know why they need it or what it's doing.We can't add a class for no. 2, because the value is completely dependent on the consumer and honestly just a best guess by me right now.
So really then it's back to, do we really want to add some random class for
flex-shrink: 0
when ,honestly,shrink
should just be a prop on EuiFlexItem likegrow
. But I tried adding this and it got a bit complicated so I abandoned.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking it was more something like
euiCollapsableNav__something
that just applied that stuff. Either way, your call. Just felt weird copy/pasting style tags.