Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/thirty-apples-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

ActionBar: Fixes issue where `ActionBar` overflow menu would persist even if it was no longer needed; allows overflow menu to appear on initial render if there is no space for all items.
6 changes: 3 additions & 3 deletions e2e/components/drafts/ActionBar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ test.describe('ActionBar', () => {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-actionbar--comment-box',
id: 'experimental-components-actionbar-examples--comment-box',
globals: {
colorScheme: theme,
},
Expand All @@ -19,7 +19,7 @@ test.describe('ActionBar', () => {

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-actionbar--comment-box',
id: 'experimental-components-actionbar-examples--comment-box',
globals: {
colorScheme: theme,
},
Expand All @@ -35,7 +35,7 @@ test.describe('ActionBar', () => {
test.describe(theme, () => {
test('Overflow interaction @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-actionbar--comment-box',
id: 'experimental-components-actionbar-examples--comment-box',
globals: {
colorScheme: theme,
},
Expand Down
287 changes: 287 additions & 0 deletions packages/react/src/ActionBar/ActionBar.examples.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
import React from 'react'
import type {Meta} from '@storybook/react'
import ActionBar from '.'
import Text from '../Text'
import {
PencilIcon,
BoldIcon,
CodeIcon,
ItalicIcon,
SearchIcon,
LinkIcon,
FileAddedIcon,
HeadingIcon,
QuoteIcon,
ListUnorderedIcon,
ListOrderedIcon,
TasklistIcon,
ReplyIcon,
ThreeBarsIcon,
} from '@primer/octicons-react'
import {Box, Button, Avatar, ActionMenu, IconButton, ActionList, Textarea} from '..'
import {Dialog} from '../DialogV1'
import {Divider} from '../deprecated/ActionList/Divider'
import mockData from '../experimental/SelectPanel2/mock-story-data'

export default {
title: 'Experimental/Components/ActionBar/Examples',
} as Meta<typeof ActionBar>

export const TextLabels = () => (
<ActionBar aria-label="Toolbar">
<Button>Edit</Button>
<Button>Duplicate</Button>
<Button>Export to CSV</Button>
</ActionBar>
)

export const SmallActionBar = () => (
<ActionBar size="small" aria-label="Toolbar">
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Code"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Link"></ActionBar.IconButton>
<ActionBar.Divider />
<ActionBar.IconButton icon={FileAddedIcon} aria-label="File Added"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Search"></ActionBar.IconButton>
</ActionBar>
)

type CommentBoxProps = {'aria-label': string}

export const CommentBox = (props: CommentBoxProps) => {
const {'aria-label': ariaLabel} = props
const [value, setValue] = React.useState('')
const [isOpen, setIsOpen] = React.useState(false)
const buttonRef = React.useRef(null)
const toolBarLabel = `${ariaLabel ? ariaLabel : 'Comment box'} toolbar`
return (
<Box
sx={{
maxWidth: 800,
display: 'flex',
flexDirection: 'column',
width: '100%',
borderColor: 'border.default',
borderWidth: 1,
borderStyle: 'solid',
borderRadius: 2,
minInlineSize: 'auto',
bg: 'canvas.default',
color: 'fg.default',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
backgroundColor: 'canvas.subtle',
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
justifyContent: 'space-between',
}}
as="header"
>
<Box sx={{width: '50%'}}>
<ActionBar aria-label={toolBarLabel}>
<ActionBar.IconButton icon={HeadingIcon} aria-label="Heading"></ActionBar.IconButton>
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Insert Code"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Insert Link"></ActionBar.IconButton>
<ActionBar.Divider />
<ActionBar.IconButton icon={QuoteIcon} aria-label="Insert Quote"></ActionBar.IconButton>
<ActionBar.IconButton icon={ListUnorderedIcon} aria-label="Unordered List"></ActionBar.IconButton>
<ActionBar.IconButton icon={ListOrderedIcon} aria-label="Ordered List"></ActionBar.IconButton>
<ActionBar.IconButton icon={TasklistIcon} aria-label="Task List"></ActionBar.IconButton>
<ActionBar.IconButton
ref={buttonRef}
onClick={() => setIsOpen(true)}
icon={ReplyIcon}
aria-label="Saved Replies"
></ActionBar.IconButton>
</ActionBar>
</Box>
</Box>
<Textarea value={value} onChange={e => setValue(e.target.value)} id="markdowninput" aria-label="Markdown value" />
<Dialog aria-labelledby="header" returnFocusRef={buttonRef} isOpen={isOpen} onDismiss={() => setIsOpen(false)}>
<Dialog.Header id="header">Select a reply</Dialog.Header>
<Box p={3}>Show saved replies</Box>
<Divider />
<Button variant="invisible">Create your own saved reply</Button>
</Dialog>
</Box>
)
}

export const ActionBarWithMenuTrigger = () => {
const [isOpen, setIsOpen] = React.useState(false)
const buttonRef = React.useRef(null)

return (
<Box>
<ActionBar aria-label="Toolbar">
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Code"></ActionBar.IconButton>
<ActionBar.IconButton
ref={buttonRef}
onClick={() => setIsOpen(true)}
icon={ReplyIcon}
aria-label="Saved Replies"
></ActionBar.IconButton>
</ActionBar>

<Dialog aria-labelledby="header" returnFocusRef={buttonRef} isOpen={isOpen} onDismiss={() => setIsOpen(false)}>
<Dialog.Header id="header">Select a reply</Dialog.Header>
<Box p={3}>Show saved replies</Box>
<Divider />
<Button variant="invisible">Create your own saved reply</Button>
</Dialog>
</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 aria-label="Comment box" />
<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 aria-label="First Comment Box" />
<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 aria-label="Second Comment Box" />
<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>
)
}
Loading
Loading