Skip to content

Commit 578a33d

Browse files
authored
ActionBar: Ensure overflow menu only exists if there is no space (#5476)
* Ensure items in menu leave menu if there is space * Fixes initial check for if menu items should have overflow menu or not; adds new story * Add changeset * Fix stories file structure * Fix ID * Remove redundant check
1 parent 87cf3b2 commit 578a33d

File tree

5 files changed

+303
-273
lines changed

5 files changed

+303
-273
lines changed

.changeset/thirty-apples-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": patch
3+
---
4+
5+
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.

e2e/components/drafts/ActionBar.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ test.describe('ActionBar', () => {
99
test.describe(theme, () => {
1010
test('default @vrt', async ({page}) => {
1111
await visit(page, {
12-
id: 'experimental-components-actionbar--comment-box',
12+
id: 'experimental-components-actionbar-examples--comment-box',
1313
globals: {
1414
colorScheme: theme,
1515
},
@@ -19,7 +19,7 @@ test.describe('ActionBar', () => {
1919

2020
test('axe @aat', async ({page}) => {
2121
await visit(page, {
22-
id: 'experimental-components-actionbar--comment-box',
22+
id: 'experimental-components-actionbar-examples--comment-box',
2323
globals: {
2424
colorScheme: theme,
2525
},
@@ -35,7 +35,7 @@ test.describe('ActionBar', () => {
3535
test.describe(theme, () => {
3636
test('Overflow interaction @vrt', async ({page}) => {
3737
await visit(page, {
38-
id: 'experimental-components-actionbar--comment-box',
38+
id: 'experimental-components-actionbar-examples--comment-box',
3939
globals: {
4040
colorScheme: theme,
4141
},
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import React from 'react'
2+
import type {Meta} from '@storybook/react'
3+
import ActionBar from '.'
4+
import Text from '../Text'
5+
import {
6+
PencilIcon,
7+
BoldIcon,
8+
CodeIcon,
9+
ItalicIcon,
10+
SearchIcon,
11+
LinkIcon,
12+
FileAddedIcon,
13+
HeadingIcon,
14+
QuoteIcon,
15+
ListUnorderedIcon,
16+
ListOrderedIcon,
17+
TasklistIcon,
18+
ReplyIcon,
19+
ThreeBarsIcon,
20+
} from '@primer/octicons-react'
21+
import {Box, Button, Avatar, ActionMenu, IconButton, ActionList, Textarea} from '..'
22+
import {Dialog} from '../DialogV1'
23+
import {Divider} from '../deprecated/ActionList/Divider'
24+
import mockData from '../experimental/SelectPanel2/mock-story-data'
25+
26+
export default {
27+
title: 'Experimental/Components/ActionBar/Examples',
28+
} as Meta<typeof ActionBar>
29+
30+
export const TextLabels = () => (
31+
<ActionBar aria-label="Toolbar">
32+
<Button>Edit</Button>
33+
<Button>Duplicate</Button>
34+
<Button>Export to CSV</Button>
35+
</ActionBar>
36+
)
37+
38+
export const SmallActionBar = () => (
39+
<ActionBar size="small" aria-label="Toolbar">
40+
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
41+
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
42+
<ActionBar.IconButton icon={CodeIcon} aria-label="Code"></ActionBar.IconButton>
43+
<ActionBar.IconButton icon={LinkIcon} aria-label="Link"></ActionBar.IconButton>
44+
<ActionBar.Divider />
45+
<ActionBar.IconButton icon={FileAddedIcon} aria-label="File Added"></ActionBar.IconButton>
46+
<ActionBar.IconButton icon={SearchIcon} aria-label="Search"></ActionBar.IconButton>
47+
</ActionBar>
48+
)
49+
50+
type CommentBoxProps = {'aria-label': string}
51+
52+
export const CommentBox = (props: CommentBoxProps) => {
53+
const {'aria-label': ariaLabel} = props
54+
const [value, setValue] = React.useState('')
55+
const [isOpen, setIsOpen] = React.useState(false)
56+
const buttonRef = React.useRef(null)
57+
const toolBarLabel = `${ariaLabel ? ariaLabel : 'Comment box'} toolbar`
58+
return (
59+
<Box
60+
sx={{
61+
maxWidth: 800,
62+
display: 'flex',
63+
flexDirection: 'column',
64+
width: '100%',
65+
borderColor: 'border.default',
66+
borderWidth: 1,
67+
borderStyle: 'solid',
68+
borderRadius: 2,
69+
minInlineSize: 'auto',
70+
bg: 'canvas.default',
71+
color: 'fg.default',
72+
}}
73+
>
74+
<Box
75+
sx={{
76+
display: 'flex',
77+
flexDirection: 'row',
78+
backgroundColor: 'canvas.subtle',
79+
borderTopLeftRadius: 2,
80+
borderTopRightRadius: 2,
81+
justifyContent: 'space-between',
82+
}}
83+
as="header"
84+
>
85+
<Box sx={{width: '50%'}}>
86+
<ActionBar aria-label={toolBarLabel}>
87+
<ActionBar.IconButton icon={HeadingIcon} aria-label="Heading"></ActionBar.IconButton>
88+
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
89+
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
90+
<ActionBar.IconButton icon={CodeIcon} aria-label="Insert Code"></ActionBar.IconButton>
91+
<ActionBar.IconButton icon={LinkIcon} aria-label="Insert Link"></ActionBar.IconButton>
92+
<ActionBar.Divider />
93+
<ActionBar.IconButton icon={QuoteIcon} aria-label="Insert Quote"></ActionBar.IconButton>
94+
<ActionBar.IconButton icon={ListUnorderedIcon} aria-label="Unordered List"></ActionBar.IconButton>
95+
<ActionBar.IconButton icon={ListOrderedIcon} aria-label="Ordered List"></ActionBar.IconButton>
96+
<ActionBar.IconButton icon={TasklistIcon} aria-label="Task List"></ActionBar.IconButton>
97+
<ActionBar.IconButton
98+
ref={buttonRef}
99+
onClick={() => setIsOpen(true)}
100+
icon={ReplyIcon}
101+
aria-label="Saved Replies"
102+
></ActionBar.IconButton>
103+
</ActionBar>
104+
</Box>
105+
</Box>
106+
<Textarea value={value} onChange={e => setValue(e.target.value)} id="markdowninput" aria-label="Markdown value" />
107+
<Dialog aria-labelledby="header" returnFocusRef={buttonRef} isOpen={isOpen} onDismiss={() => setIsOpen(false)}>
108+
<Dialog.Header id="header">Select a reply</Dialog.Header>
109+
<Box p={3}>Show saved replies</Box>
110+
<Divider />
111+
<Button variant="invisible">Create your own saved reply</Button>
112+
</Dialog>
113+
</Box>
114+
)
115+
}
116+
117+
export const ActionBarWithMenuTrigger = () => {
118+
const [isOpen, setIsOpen] = React.useState(false)
119+
const buttonRef = React.useRef(null)
120+
121+
return (
122+
<Box>
123+
<ActionBar aria-label="Toolbar">
124+
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
125+
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
126+
<ActionBar.IconButton icon={CodeIcon} aria-label="Code"></ActionBar.IconButton>
127+
<ActionBar.IconButton
128+
ref={buttonRef}
129+
onClick={() => setIsOpen(true)}
130+
icon={ReplyIcon}
131+
aria-label="Saved Replies"
132+
></ActionBar.IconButton>
133+
</ActionBar>
134+
135+
<Dialog aria-labelledby="header" returnFocusRef={buttonRef} isOpen={isOpen} onDismiss={() => setIsOpen(false)}>
136+
<Dialog.Header id="header">Select a reply</Dialog.Header>
137+
<Box p={3}>Show saved replies</Box>
138+
<Divider />
139+
<Button variant="invisible">Create your own saved reply</Button>
140+
</Dialog>
141+
</Box>
142+
)
143+
}
144+
145+
export const ActionbarToggle = () => {
146+
const descriptionStyles = {
147+
borderWidth: 1,
148+
borderStyle: 'solid',
149+
borderColor: 'border.default',
150+
p: 3,
151+
}
152+
const topSectionStyles = {
153+
bg: 'canvas.subtle',
154+
display: 'flex',
155+
flexDirection: 'row',
156+
justifyContent: 'space-between',
157+
borderBottomWidth: 1,
158+
borderStyle: 'solid',
159+
borderColor: 'border.default',
160+
p: 3,
161+
}
162+
const bottomSectionStyles = {
163+
p: 3,
164+
}
165+
const loginName = mockData.collaborators[1].login
166+
const [showEditView, setEditView] = React.useState(false)
167+
const [description /*, setDescription*/] = React.useState('')
168+
const anchorRef = React.useRef(null)
169+
return (
170+
<Box sx={descriptionStyles}>
171+
<Box sx={topSectionStyles}>
172+
<Box>
173+
<Avatar src={`https://github.com/${loginName}.png`} size={30} />
174+
<Text as="strong" sx={{marginLeft: 2, marginRight: 2}}>
175+
{loginName}
176+
</Text>
177+
<Text>opened this issue 2 hours ago</Text>
178+
</Box>
179+
<Box>
180+
<ActionMenu>
181+
<ActionMenu.Anchor ref={anchorRef}>
182+
<IconButton icon={ThreeBarsIcon} aria-label="Open Menu" />
183+
</ActionMenu.Anchor>
184+
<ActionMenu.Overlay>
185+
<ActionList>
186+
<ActionList.Item>
187+
<ActionList.LeadingVisual>
188+
<LinkIcon />
189+
</ActionList.LeadingVisual>
190+
Copy Link
191+
</ActionList.Item>
192+
<ActionList.Item>
193+
<ActionList.LeadingVisual>
194+
<QuoteIcon />
195+
</ActionList.LeadingVisual>
196+
Quote reply
197+
</ActionList.Item>
198+
<ActionList.Divider />
199+
<ActionList.Item onClick={() => setEditView(true)}>
200+
<ActionList.LeadingVisual>
201+
<PencilIcon />
202+
</ActionList.LeadingVisual>
203+
Edit
204+
</ActionList.Item>
205+
</ActionList>
206+
</ActionMenu.Overlay>
207+
</ActionMenu>
208+
</Box>
209+
</Box>
210+
<Box sx={bottomSectionStyles}>
211+
{showEditView ? (
212+
<Box>
213+
<CommentBox aria-label="Comment box" />
214+
<Box sx={{display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', p: 2, gap: 2}}>
215+
<Button
216+
variant="primary"
217+
onClick={() => {
218+
setEditView(false)
219+
}}
220+
>
221+
Save
222+
</Button>
223+
<Button variant="danger" onClick={() => setEditView(false)}>
224+
Cancel
225+
</Button>
226+
</Box>
227+
</Box>
228+
) : (
229+
<Box>{description ? description : 'No description Provided'}</Box>
230+
)}
231+
</Box>
232+
</Box>
233+
)
234+
}
235+
236+
export const MultipleActionBars = () => {
237+
const [showFirstCommentBox, setShowFirstCommentBox] = React.useState(false)
238+
const [showSecondCommentBox, setShowSecondCommentBox] = React.useState(false)
239+
return (
240+
<Box>
241+
<Box sx={{p: 3}}>
242+
{showFirstCommentBox ? (
243+
<Box>
244+
<CommentBox aria-label="First Comment Box" />
245+
<Box sx={{display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', p: 2, gap: 2}}>
246+
<Button
247+
variant="primary"
248+
onClick={() => {
249+
setShowFirstCommentBox(false)
250+
}}
251+
>
252+
Save
253+
</Button>
254+
<Button variant="danger" onClick={() => setShowFirstCommentBox(false)}>
255+
Cancel
256+
</Button>
257+
</Box>
258+
</Box>
259+
) : (
260+
<Button onClick={() => setShowFirstCommentBox(true)}>Show first commentBox</Button>
261+
)}
262+
</Box>
263+
<Box sx={{p: 3}}>
264+
{showSecondCommentBox ? (
265+
<Box>
266+
<CommentBox aria-label="Second Comment Box" />
267+
<Box sx={{display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', p: 2, gap: 2}}>
268+
<Button
269+
variant="primary"
270+
onClick={() => {
271+
setShowSecondCommentBox(false)
272+
}}
273+
>
274+
Save
275+
</Button>
276+
<Button variant="danger" onClick={() => setShowSecondCommentBox(false)}>
277+
Cancel
278+
</Button>
279+
</Box>
280+
</Box>
281+
) : (
282+
<Button onClick={() => setShowSecondCommentBox(true)}>Show second commentBox</Button>
283+
)}
284+
</Box>
285+
</Box>
286+
)
287+
}

0 commit comments

Comments
 (0)