Skip to content

Commit

Permalink
ActionBar: Add stories for couple of edge cases (#4424)
Browse files Browse the repository at this point in the history
* Add stories for couple of edge cases

* Create an Actionmenu in the more button instead of bootlegged menu

* Clean up

* Create sweet-icons-stare.md
  • Loading branch information
pksjce authored Apr 2, 2024
1 parent 4e281b2 commit 392a3ca
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 83 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-icons-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

ActionBar: The overflow menu was earlier bootlegged with heavily customised ActionList. This is being replaced with ActionMenu which is cleaner and more robust.
150 changes: 149 additions & 1 deletion packages/react/src/drafts/ActionBar/ActionBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react'
import type {Meta} from '@storybook/react'
import ActionBar from '.'
import Text from '../../Text'
import {
PencilIcon,
BoldIcon,
CodeIcon,
ItalicIcon,
Expand All @@ -14,12 +16,14 @@ import {
ListOrderedIcon,
TasklistIcon,
ReplyIcon,
ThreeBarsIcon,
} from '@primer/octicons-react'
import {MarkdownInput} from '../MarkdownEditor/_MarkdownInput'
import {ViewSwitch} from '../MarkdownEditor/_ViewSwitch'
import type {MarkdownViewMode} from '../MarkdownEditor/_ViewSwitch'
import {Box, Dialog, Button} from '../..'
import {Box, Dialog, Button, Avatar, ActionMenu, IconButton, ActionList} from '../..'
import {Divider} from '../../deprecated/ActionList/Divider'
import mockData from '../SelectPanel2/mock-story-data'

export default {
title: 'Drafts/Components/ActionBar',
Expand Down Expand Up @@ -169,3 +173,147 @@ export const ActionBarWithMenuTrigger = () => {
</Box>
)
}

export const ActionbarToggle = () => {
const descriptionStyles = {
borderWidth: 1,
borderStyle: 'solid',
borderColor: 'border.default',
p: 3,
}
const topSectionStyles = {
bg: 'canvas.subtle',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
borderBottomWidth: 1,
borderStyle: 'solid',
borderColor: 'border.default',
p: 3,
}
const bottomSectionStyles = {
p: 3,
}
const loginName = mockData.collaborators[1].login
const [showEditView, setEditView] = React.useState(false)
const [description /*, setDescription*/] = React.useState('')
const anchorRef = React.useRef(null)
return (
<Box sx={descriptionStyles}>
<Box sx={topSectionStyles}>
<Box>
<Avatar src={`https://github.com/${loginName}.png`} size={30} />
<Text as="strong" sx={{marginLeft: 2, marginRight: 2}}>
{loginName}
</Text>
<Text>opened this issue 2 hours ago</Text>
</Box>
<Box>
<ActionMenu>
<ActionMenu.Anchor ref={anchorRef}>
<IconButton icon={ThreeBarsIcon} aria-label="Open Menu" />
</ActionMenu.Anchor>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item>
<ActionList.LeadingVisual>
<LinkIcon />
</ActionList.LeadingVisual>
Copy Link
</ActionList.Item>
<ActionList.Item>
<ActionList.LeadingVisual>
<QuoteIcon />
</ActionList.LeadingVisual>
Quote reply
</ActionList.Item>
<ActionList.Divider />
<ActionList.Item onClick={() => setEditView(true)}>
<ActionList.LeadingVisual>
<PencilIcon />
</ActionList.LeadingVisual>
Edit
</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</Box>
</Box>
<Box sx={bottomSectionStyles}>
{showEditView ? (
<Box>
<CommentBox />
<Box sx={{display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', p: 2, gap: 2}}>
<Button
variant="primary"
onClick={() => {
setEditView(false)
}}
>
Save
</Button>
<Button variant="danger" onClick={() => setEditView(false)}>
Cancel
</Button>
</Box>
</Box>
) : (
<Box>{description ? description : 'No description Provided'}</Box>
)}
</Box>
</Box>
)
}

export const MultipleActionBars = () => {
const [showFirstCommentBox, setShowFirstCommentBox] = React.useState(false)
const [showSecondCommentBox, setShowSecondCommentBox] = React.useState(false)
return (
<Box>
<Box sx={{p: 3}}>
{showFirstCommentBox ? (
<Box>
<CommentBox key={1} />
<Box sx={{display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', p: 2, gap: 2}}>
<Button
variant="primary"
onClick={() => {
setShowFirstCommentBox(false)
}}
>
Save
</Button>
<Button variant="danger" onClick={() => setShowFirstCommentBox(false)}>
Cancel
</Button>
</Box>
</Box>
) : (
<Button onClick={() => setShowFirstCommentBox(true)}>Show first commentBox</Button>
)}
</Box>
<Box sx={{p: 3}}>
{showSecondCommentBox ? (
<Box>
<CommentBox key={2} />
<Box sx={{display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', p: 2, gap: 2}}>
<Button
variant="primary"
onClick={() => {
setShowSecondCommentBox(false)
}}
>
Save
</Button>
<Button variant="danger" onClick={() => setShowSecondCommentBox(false)}>
Cancel
</Button>
</Box>
</Box>
) : (
<Button onClick={() => setShowSecondCommentBox(true)}>Show second commentBox</Button>
)}
</Box>
</Box>
)
}
128 changes: 46 additions & 82 deletions packages/react/src/drafts/ActionBar/ActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {useOnOutsideClick} from '../../hooks/useOnOutsideClick'
import type {IconButtonProps} from '../../Button'
import {IconButton} from '../../Button'
import Box from '../../Box'
import {ActionMenu} from '../..'

type ChildSize = {
text: string
Expand Down Expand Up @@ -46,13 +47,7 @@ const NavigationList = styled.div`
${sx};
`

const MORE_BTN_HEIGHT = 45
const GAP = 8
const MoreMenuListItem = styled.li`
display: flex;
align-items: center;
height: ${MORE_BTN_HEIGHT}px;
`

const listStyles = {
display: 'flex',
Expand All @@ -67,29 +62,15 @@ const listStyles = {
position: 'relative',
}

const menuStyles = {
position: 'absolute',
zIndex: 1,
top: '90%',
right: '0',
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)',
borderRadius: '12px',
backgroundColor: 'canvas.overlay',
listStyle: 'none',
// Values are from ActionMenu
minWidth: '192px',
maxWidth: '640px',
}

const MORE_BTN_WIDTH = 86
const getNavStyles = () => ({
const navStyles = {
display: 'flex',
paddingX: 3,
justifyContent: 'flex-end',
align: 'row',
alignItems: 'center',
maxHeight: '32px',
})
}

const menuItemStyles = {
textDecoration: 'none',
Expand Down Expand Up @@ -192,7 +173,6 @@ export const ActionBar: React.FC<React.PropsWithChildren<ActionBarProps>> = prop
const moreMenuRef = useRef<HTMLLIElement>(null)
const moreMenuBtnRef = useRef<HTMLButtonElement>(null)
const containerRef = React.useRef<HTMLUListElement>(null)
const disclosureWidgetId = React.useId()

const validChildren = getValidChildren(children)
// Responsive props object manages which items are in the list and which items are in the menu.
Expand Down Expand Up @@ -231,13 +211,6 @@ export const ActionBar: React.FC<React.PropsWithChildren<ActionBarProps>> = prop
moreMenuBtnRef.current?.focus()
}, [])

const onAnchorClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
if (event.defaultPrevented || event.button !== 0) {
return
}
setIsWidgetOpen(isWidgetOpen => !isWidgetOpen)
}, [])

useOnEscapePress(
(event: KeyboardEvent) => {
if (isWidgetOpen) {
Expand All @@ -253,61 +226,52 @@ export const ActionBar: React.FC<React.PropsWithChildren<ActionBarProps>> = prop

return (
<ActionBarContext.Provider value={{size, setChildrenWidth}}>
<Box ref={navRef} sx={getNavStyles()}>
<Box ref={navRef} sx={navStyles}>
<NavigationList sx={listStyles} ref={listRef} role="toolbar">
{listItems}
{menuItems.length > 0 && (
<MoreMenuListItem ref={moreMenuRef}>
<IconButton
ref={moreMenuBtnRef}
sx={moreBtnStyles}
aria-controls={disclosureWidgetId}
aria-expanded={isWidgetOpen}
onClick={onAnchorClick}
aria-label={`More ${ariaLabel} items`}
icon={KebabHorizontalIcon}
/>
<ActionList
ref={containerRef}
id={disclosureWidgetId}
sx={menuStyles}
style={{display: isWidgetOpen ? 'block' : 'none'}}
>
{menuItems.map((menuItem, index) => {
if (menuItem.type === ActionList.Divider) {
return <ActionList.Divider key={index} />
} else {
const {
children: menuItemChildren,
//'aria-current': ariaCurrent,
onClick,
icon: Icon,
'aria-label': ariaLabel,
} = menuItem.props
return (
<ActionList.LinkItem
key={menuItemChildren}
sx={menuItemStyles}
onClick={(
event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>,
) => {
closeOverlay()
focusOnMoreMenuBtn()
typeof onClick === 'function' && onClick(event)
}}
>
{Icon ? (
<ActionList.LeadingVisual>
<Icon />
</ActionList.LeadingVisual>
) : null}
{ariaLabel}
</ActionList.LinkItem>
)
}
})}
</ActionList>
</MoreMenuListItem>
<ActionMenu>
<ActionMenu.Anchor>
<IconButton sx={moreBtnStyles} aria-label={`More ${ariaLabel} items`} icon={KebabHorizontalIcon} />
</ActionMenu.Anchor>
<ActionMenu.Overlay>
<ActionList>
{menuItems.map((menuItem, index) => {
if (menuItem.type === ActionList.Divider) {
return <ActionList.Divider key={index} />
} else {
const {
children: menuItemChildren,
//'aria-current': ariaCurrent,
onClick,
icon: Icon,
'aria-label': ariaLabel,
} = menuItem.props
return (
<ActionList.LinkItem
key={menuItemChildren}
sx={menuItemStyles}
onClick={(
event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>,
) => {
closeOverlay()
focusOnMoreMenuBtn()
typeof onClick === 'function' && onClick(event)
}}
>
{Icon ? (
<ActionList.LeadingVisual>
<Icon />
</ActionList.LeadingVisual>
) : null}
{ariaLabel}
</ActionList.LinkItem>
)
}
})}
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
)}
</NavigationList>
</Box>
Expand Down

0 comments on commit 392a3ca

Please sign in to comment.