@primer/components 31.2.0-rc.5ccefd7d → 31.2.0-rc.6afeaa37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/browser.esm.js +622 -620
- package/dist/browser.esm.js.map +1 -1
- package/dist/browser.umd.js +163 -161
- package/dist/browser.umd.js.map +1 -1
- package/docs/content/ActionList2.mdx +354 -0
- package/docs/content/getting-started.md +1 -1
- package/docs/src/@primer/gatsby-theme-doctocat/components/hero.js +1 -3
- package/docs/src/@primer/gatsby-theme-doctocat/components/live-preview-wrapper.js +1 -1
- package/docs/src/@primer/gatsby-theme-doctocat/live-code-scope.js +17 -0
- package/lib/ActionList2/Description.d.ts +12 -0
- package/lib/ActionList2/Description.js +53 -0
- package/lib/ActionList2/Divider.d.ts +5 -0
- package/lib/ActionList2/Divider.js +35 -0
- package/lib/ActionList2/Group.d.ts +11 -0
- package/lib/ActionList2/Group.js +57 -0
- package/lib/ActionList2/Header.d.ts +26 -0
- package/lib/ActionList2/Header.js +55 -0
- package/lib/ActionList2/Item.d.ts +63 -0
- package/lib/ActionList2/Item.js +244 -0
- package/lib/ActionList2/LinkItem.d.ts +17 -0
- package/lib/ActionList2/LinkItem.js +57 -0
- package/lib/ActionList2/List.d.ts +26 -0
- package/lib/ActionList2/List.js +59 -0
- package/lib/ActionList2/Selection.d.ts +5 -0
- package/lib/ActionList2/Selection.js +70 -0
- package/lib/ActionList2/Visuals.d.ts +9 -0
- package/lib/ActionList2/Visuals.js +90 -0
- package/lib/ActionList2/index.d.ts +36 -0
- package/lib/ActionList2/index.js +47 -0
- package/lib/Overlay.js +3 -1
- package/lib/Portal/Portal.js +3 -2
- package/lib/_TextInputWrapper.js +2 -2
- package/lib/__tests__/ActionList2.test.d.ts +1 -0
- package/lib/__tests__/ActionList2.test.js +53 -0
- package/lib/__tests__/AnchoredOverlay.test.js +4 -2
- package/lib/__tests__/TextInputWithTokens.test.js +1 -10
- package/lib/__tests__/utils/createSlots.test.d.ts +1 -0
- package/lib/__tests__/utils/createSlots.test.js +75 -0
- package/lib/hooks/useAnchoredPosition.js +3 -2
- package/lib/hooks/useCombinedRefs.d.ts +2 -2
- package/lib/hooks/useCombinedRefs.js +4 -6
- package/lib/hooks/useResizeObserver.js +2 -2
- package/lib/stories/ActionList2.stories.js +907 -0
- package/lib/stories/TextInput.stories.js +144 -0
- package/lib/stories/Token.stories.js +19 -2
- package/lib/sx.d.ts +2 -0
- package/lib/sx.js +8 -0
- package/lib/theme-preval.js +81 -2
- package/lib/unreleased.d.ts +7 -0
- package/lib/unreleased.js +18 -0
- package/lib/utils/create-slots.d.ts +17 -0
- package/lib/utils/create-slots.js +105 -0
- package/lib/utils/testing.d.ts +14 -1
- package/lib/utils/use-force-update.d.ts +1 -0
- package/lib/utils/use-force-update.js +19 -0
- package/lib/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/lib/utils/useIsomorphicLayoutEffect.js +12 -0
- package/lib-esm/ActionList2/Description.d.ts +12 -0
- package/lib-esm/ActionList2/Description.js +37 -0
- package/lib-esm/ActionList2/Divider.d.ts +5 -0
- package/lib-esm/ActionList2/Divider.js +23 -0
- package/lib-esm/ActionList2/Group.d.ts +11 -0
- package/lib-esm/ActionList2/Group.js +40 -0
- package/lib-esm/ActionList2/Header.d.ts +26 -0
- package/lib-esm/ActionList2/Header.js +44 -0
- package/lib-esm/ActionList2/Item.d.ts +63 -0
- package/lib-esm/ActionList2/Item.js +210 -0
- package/lib-esm/ActionList2/LinkItem.d.ts +17 -0
- package/lib-esm/ActionList2/LinkItem.js +43 -0
- package/lib-esm/ActionList2/List.d.ts +26 -0
- package/lib-esm/ActionList2/List.js +37 -0
- package/lib-esm/ActionList2/Selection.d.ts +5 -0
- package/lib-esm/ActionList2/Selection.js +52 -0
- package/lib-esm/ActionList2/Visuals.d.ts +9 -0
- package/lib-esm/ActionList2/Visuals.js +68 -0
- package/lib-esm/ActionList2/index.d.ts +36 -0
- package/lib-esm/ActionList2/index.js +33 -0
- package/lib-esm/Overlay.js +2 -1
- package/lib-esm/Portal/Portal.js +2 -1
- package/lib-esm/_TextInputWrapper.js +2 -2
- package/lib-esm/__tests__/ActionList2.test.d.ts +1 -0
- package/lib-esm/__tests__/ActionList2.test.js +41 -0
- package/lib-esm/__tests__/AnchoredOverlay.test.js +4 -2
- package/lib-esm/__tests__/TextInputWithTokens.test.js +1 -10
- package/lib-esm/__tests__/utils/createSlots.test.d.ts +1 -0
- package/lib-esm/__tests__/utils/createSlots.test.js +67 -0
- package/lib-esm/hooks/useAnchoredPosition.js +2 -1
- package/lib-esm/hooks/useCombinedRefs.d.ts +2 -2
- package/lib-esm/hooks/useCombinedRefs.js +3 -2
- package/lib-esm/hooks/useResizeObserver.js +2 -2
- package/lib-esm/stories/ActionList2.stories.js +796 -0
- package/lib-esm/stories/TextInput.stories.js +117 -0
- package/lib-esm/stories/Token.stories.js +14 -1
- package/lib-esm/sx.d.ts +2 -0
- package/lib-esm/sx.js +3 -1
- package/lib-esm/theme-preval.js +81 -2
- package/lib-esm/unreleased.d.ts +7 -0
- package/lib-esm/unreleased.js +8 -0
- package/lib-esm/utils/create-slots.d.ts +17 -0
- package/lib-esm/utils/create-slots.js +84 -0
- package/lib-esm/utils/testing.d.ts +14 -1
- package/lib-esm/utils/use-force-update.d.ts +1 -0
- package/lib-esm/utils/use-force-update.js +6 -0
- package/lib-esm/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/lib-esm/utils/useIsomorphicLayoutEffect.js +3 -0
- package/migrating.md +1 -1
- package/package-lock.json +146 -7
- package/package.json +5 -3
- package/script/build +2 -0
- package/src/ActionList2/Description.tsx +49 -0
- package/src/ActionList2/Divider.tsx +24 -0
- package/src/ActionList2/Group.tsx +34 -0
- package/src/ActionList2/Header.tsx +58 -0
- package/src/ActionList2/Item.tsx +245 -0
- package/src/ActionList2/LinkItem.tsx +49 -0
- package/src/ActionList2/List.tsx +55 -0
- package/src/ActionList2/Selection.tsx +40 -0
- package/src/ActionList2/Visuals.tsx +76 -0
- package/src/ActionList2/index.ts +39 -0
- package/src/Overlay.tsx +2 -1
- package/src/Portal/Portal.tsx +2 -1
- package/src/_TextInputWrapper.tsx +7 -0
- package/src/__tests__/ActionList2.test.tsx +47 -0
- package/src/__tests__/AnchoredOverlay.test.tsx +2 -2
- package/src/__tests__/TextInputWithTokens.test.tsx +0 -10
- package/src/__tests__/__snapshots__/ActionList2.test.tsx.snap +14 -0
- package/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +35 -135
- package/src/__tests__/utils/__snapshots__/createSlots.test.tsx.snap +55 -0
- package/src/__tests__/utils/createSlots.test.tsx +74 -0
- package/src/hooks/useAnchoredPosition.ts +2 -1
- package/src/hooks/useCombinedRefs.ts +3 -3
- package/src/hooks/useResizeObserver.ts +2 -2
- package/src/stories/ActionList2.stories.tsx +1288 -0
- package/src/stories/TextInput.stories.tsx +113 -0
- package/src/stories/Token.stories.tsx +12 -1
- package/src/sx.ts +3 -0
- package/src/theme-preval.js +1 -0
- package/src/unreleased.ts +9 -0
- package/src/utils/create-slots.tsx +96 -0
- package/src/utils/use-force-update.ts +7 -0
- package/src/utils/useIsomorphicLayoutEffect.ts +10 -0
- package/stats.html +1 -1
@@ -0,0 +1,1288 @@
|
|
1
|
+
import {
|
2
|
+
ServerIcon,
|
3
|
+
PlusCircleIcon,
|
4
|
+
TypographyIcon,
|
5
|
+
VersionsIcon,
|
6
|
+
SearchIcon,
|
7
|
+
NoteIcon,
|
8
|
+
ProjectIcon,
|
9
|
+
FilterIcon,
|
10
|
+
GearIcon,
|
11
|
+
ArrowRightIcon,
|
12
|
+
ArrowLeftIcon,
|
13
|
+
LinkIcon,
|
14
|
+
LawIcon,
|
15
|
+
StarIcon,
|
16
|
+
GitForkIcon,
|
17
|
+
AlertIcon,
|
18
|
+
TableIcon,
|
19
|
+
PeopleIcon,
|
20
|
+
CalendarIcon,
|
21
|
+
IssueOpenedIcon,
|
22
|
+
NumberIcon,
|
23
|
+
XIcon,
|
24
|
+
RepoIcon
|
25
|
+
} from '@primer/octicons-react'
|
26
|
+
import {Meta} from '@storybook/react'
|
27
|
+
import React, {forwardRef} from 'react'
|
28
|
+
import styled from 'styled-components'
|
29
|
+
import {DndProvider, useDrag, useDrop} from 'react-dnd'
|
30
|
+
import {HTML5Backend} from 'react-dnd-html5-backend'
|
31
|
+
import {Label, ThemeProvider} from '..'
|
32
|
+
import {ActionList as _ActionList, ItemProps} from '../ActionList2'
|
33
|
+
import {Header} from '../ActionList/Header'
|
34
|
+
import BaseStyles from '../BaseStyles'
|
35
|
+
import Avatar from '../Avatar'
|
36
|
+
import {ButtonInvisible} from '../Button'
|
37
|
+
import TextInput from '../TextInput'
|
38
|
+
import Spinner from '../Spinner'
|
39
|
+
import Box from '../Box'
|
40
|
+
import {AnchoredOverlay} from '../AnchoredOverlay'
|
41
|
+
|
42
|
+
const ActionList = Object.assign(_ActionList, {
|
43
|
+
Header
|
44
|
+
})
|
45
|
+
|
46
|
+
const meta: Meta = {
|
47
|
+
title: 'Composite components/ActionList2',
|
48
|
+
component: ActionList,
|
49
|
+
decorators: [
|
50
|
+
(Story: React.ComponentType): JSX.Element => (
|
51
|
+
<ThemeProvider>
|
52
|
+
<BaseStyles>
|
53
|
+
<Story />
|
54
|
+
</BaseStyles>
|
55
|
+
</ThemeProvider>
|
56
|
+
)
|
57
|
+
],
|
58
|
+
parameters: {
|
59
|
+
controls: {
|
60
|
+
disable: true
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
export default meta
|
65
|
+
|
66
|
+
const ErsatzOverlay = styled.div<{maxWidth?: string}>`
|
67
|
+
border-radius: 12px;
|
68
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 8px 24px rgba(149, 157, 165, 0.2);
|
69
|
+
overflow: hidden;
|
70
|
+
max-width: ${({maxWidth}) => maxWidth || 'none'};
|
71
|
+
`
|
72
|
+
export function SimpleListStory(): JSX.Element {
|
73
|
+
return (
|
74
|
+
<>
|
75
|
+
<h1>Simple List</h1>
|
76
|
+
|
77
|
+
<ActionList>
|
78
|
+
<ActionList.Item>Copy link</ActionList.Item>
|
79
|
+
<ActionList.Item>Quote reply</ActionList.Item>
|
80
|
+
<ActionList.Item>Edit comment</ActionList.Item>
|
81
|
+
<ActionList.Divider />
|
82
|
+
<ActionList.Item variant="danger">Delete file</ActionList.Item>
|
83
|
+
</ActionList>
|
84
|
+
</>
|
85
|
+
)
|
86
|
+
}
|
87
|
+
SimpleListStory.storyName = 'Simple List'
|
88
|
+
|
89
|
+
export function WithIcon(): JSX.Element {
|
90
|
+
return (
|
91
|
+
<>
|
92
|
+
<h1>With Icon</h1>
|
93
|
+
|
94
|
+
<ActionList>
|
95
|
+
<ActionList.Item>
|
96
|
+
<ActionList.LeadingVisual>
|
97
|
+
<LinkIcon />
|
98
|
+
</ActionList.LeadingVisual>
|
99
|
+
github.com/primer
|
100
|
+
</ActionList.Item>
|
101
|
+
<ActionList.Item>
|
102
|
+
<ActionList.LeadingVisual>
|
103
|
+
<LawIcon />
|
104
|
+
</ActionList.LeadingVisual>
|
105
|
+
MIT License
|
106
|
+
</ActionList.Item>
|
107
|
+
<ActionList.Item>
|
108
|
+
<ActionList.LeadingVisual>
|
109
|
+
<StarIcon />
|
110
|
+
</ActionList.LeadingVisual>
|
111
|
+
256 stars
|
112
|
+
</ActionList.Item>
|
113
|
+
<ActionList.Item>
|
114
|
+
<ActionList.LeadingVisual>
|
115
|
+
<GitForkIcon />
|
116
|
+
</ActionList.LeadingVisual>
|
117
|
+
3 forks
|
118
|
+
</ActionList.Item>
|
119
|
+
<ActionList.Item variant="danger">
|
120
|
+
<ActionList.LeadingVisual>
|
121
|
+
<AlertIcon />
|
122
|
+
</ActionList.LeadingVisual>
|
123
|
+
4 vulnerabilities
|
124
|
+
</ActionList.Item>
|
125
|
+
</ActionList>
|
126
|
+
</>
|
127
|
+
)
|
128
|
+
}
|
129
|
+
WithIcon.storyName = 'With Icon'
|
130
|
+
|
131
|
+
const users = [
|
132
|
+
{login: 'pksjce', name: 'Pavithra Kodmad'},
|
133
|
+
{login: 'jfuchs', name: 'Jonathan Fuchs'},
|
134
|
+
{login: 'colebemis', name: 'Cole Bemis'},
|
135
|
+
{login: 'mperrotti', name: 'Mike Perrotti'},
|
136
|
+
{login: 'dgreif', name: 'Dusty Greif'},
|
137
|
+
{login: 'smockle', name: 'Clay Miller'},
|
138
|
+
{login: 'siddharthkp', name: 'Siddharth Kshetrapal'}
|
139
|
+
]
|
140
|
+
|
141
|
+
export function WithAvatar(): JSX.Element {
|
142
|
+
return (
|
143
|
+
<>
|
144
|
+
<h1>With Avatar</h1>
|
145
|
+
<ErsatzOverlay>
|
146
|
+
<ActionList>
|
147
|
+
{users.map(user => (
|
148
|
+
<ActionList.Item key={user.login}>
|
149
|
+
<ActionList.LeadingVisual>
|
150
|
+
<Avatar src={`https://github.com/${user.login}.png`} />
|
151
|
+
</ActionList.LeadingVisual>
|
152
|
+
{user.login}
|
153
|
+
</ActionList.Item>
|
154
|
+
))}
|
155
|
+
</ActionList>
|
156
|
+
</ErsatzOverlay>
|
157
|
+
</>
|
158
|
+
)
|
159
|
+
}
|
160
|
+
WithAvatar.storyName = 'With Avatar'
|
161
|
+
|
162
|
+
const labels = [
|
163
|
+
{name: 'blocked', color: '#86181d', description: 'Someone or something is preventing this from moving forward'},
|
164
|
+
{name: 'dependencies', color: '#0366d6', description: 'Pull requests that update a dependency file'},
|
165
|
+
{name: 'duplicate', color: '#cfd3d7', description: 'This issue or pull request already exists'},
|
166
|
+
{name: 'good first issue', color: '#7057ff', description: 'Good for newcomers'}
|
167
|
+
]
|
168
|
+
|
169
|
+
const LabelColor: React.FC<{color: string}> = ({color}) => (
|
170
|
+
<Box sx={{backgroundColor: color, width: '14px', height: '14px', borderRadius: 3}} />
|
171
|
+
)
|
172
|
+
|
173
|
+
export function WithDescription(): JSX.Element {
|
174
|
+
return (
|
175
|
+
<>
|
176
|
+
<h1>With Description & Dividers</h1>
|
177
|
+
<ErsatzOverlay>
|
178
|
+
<ActionList showDividers>
|
179
|
+
{users.map(user => (
|
180
|
+
<ActionList.Item key={user.login}>
|
181
|
+
<ActionList.LeadingVisual>
|
182
|
+
<Avatar src={`https://github.com/${user.login}.png`} />
|
183
|
+
</ActionList.LeadingVisual>
|
184
|
+
{user.login}
|
185
|
+
<ActionList.Description>{user.name}</ActionList.Description>
|
186
|
+
</ActionList.Item>
|
187
|
+
))}
|
188
|
+
<ActionList.Divider />
|
189
|
+
{labels.map((label, index) => (
|
190
|
+
<ActionList.Item key={index}>
|
191
|
+
<ActionList.LeadingVisual>
|
192
|
+
<LabelColor color={label.color} />
|
193
|
+
</ActionList.LeadingVisual>
|
194
|
+
{label.name}
|
195
|
+
<ActionList.Description variant="block">{label.description}</ActionList.Description>
|
196
|
+
</ActionList.Item>
|
197
|
+
))}
|
198
|
+
</ActionList>
|
199
|
+
</ErsatzOverlay>
|
200
|
+
</>
|
201
|
+
)
|
202
|
+
}
|
203
|
+
WithDescription.storyName = 'With Description & Dividers'
|
204
|
+
|
205
|
+
const projects = [
|
206
|
+
{name: 'Primer Backlog', scope: 'GitHub'},
|
207
|
+
{name: 'Accessibility', scope: 'GitHub'},
|
208
|
+
{name: 'Octicons', scope: 'github/primer'},
|
209
|
+
{name: 'Primer React', scope: 'github/primer'}
|
210
|
+
]
|
211
|
+
|
212
|
+
export function SingleSelectListStory(): JSX.Element {
|
213
|
+
const [selectedIndex, setSelectedIndex] = React.useState(1)
|
214
|
+
|
215
|
+
return (
|
216
|
+
<>
|
217
|
+
<h1>Single Select List</h1>
|
218
|
+
<ErsatzOverlay>
|
219
|
+
<ActionList selectionVariant="single" showDividers role="listbox" aria-label="Select a project">
|
220
|
+
{projects.map((project, index) => (
|
221
|
+
<ActionList.Item
|
222
|
+
key={index}
|
223
|
+
role="option"
|
224
|
+
selected={index === selectedIndex}
|
225
|
+
onSelect={() => setSelectedIndex(index)}
|
226
|
+
>
|
227
|
+
<ActionList.LeadingVisual>
|
228
|
+
<TableIcon />
|
229
|
+
</ActionList.LeadingVisual>
|
230
|
+
{project.name}
|
231
|
+
<ActionList.Description variant="block">{project.scope}</ActionList.Description>
|
232
|
+
</ActionList.Item>
|
233
|
+
))}
|
234
|
+
</ActionList>
|
235
|
+
</ErsatzOverlay>
|
236
|
+
</>
|
237
|
+
)
|
238
|
+
}
|
239
|
+
SingleSelectListStory.storyName = 'Single Select'
|
240
|
+
|
241
|
+
export function MultiSelectListStory(): JSX.Element {
|
242
|
+
const [assignees, setAssignees] = React.useState(users.slice(0, 2))
|
243
|
+
|
244
|
+
const toggleAssignee = assignee => {
|
245
|
+
const assigneeIndex = assignees.findIndex(a => a.login === assignee.login)
|
246
|
+
|
247
|
+
if (assigneeIndex === -1) setAssignees([...assignees, assignee])
|
248
|
+
else setAssignees(assignees.filter((_, index) => index !== assigneeIndex))
|
249
|
+
}
|
250
|
+
|
251
|
+
return (
|
252
|
+
<>
|
253
|
+
<h1>Multi Select List</h1>
|
254
|
+
<ErsatzOverlay>
|
255
|
+
<ActionList selectionVariant="multiple" showDividers role="listbox" aria-label="Select assignees">
|
256
|
+
{users.map(user => (
|
257
|
+
<ActionList.Item
|
258
|
+
role="option"
|
259
|
+
key={user.login}
|
260
|
+
selected={Boolean(assignees.find(assignee => assignee.login === user.login))}
|
261
|
+
onSelect={() => toggleAssignee(user)}
|
262
|
+
>
|
263
|
+
<ActionList.LeadingVisual>
|
264
|
+
<Avatar src={`https://github.com/${user.login}.png`} />
|
265
|
+
</ActionList.LeadingVisual>
|
266
|
+
{user.login}
|
267
|
+
<ActionList.Description>{user.name}</ActionList.Description>
|
268
|
+
</ActionList.Item>
|
269
|
+
))}
|
270
|
+
</ActionList>
|
271
|
+
</ErsatzOverlay>
|
272
|
+
</>
|
273
|
+
)
|
274
|
+
}
|
275
|
+
MultiSelectListStory.storyName = 'Multi Select'
|
276
|
+
|
277
|
+
export function DisabledStory(): JSX.Element {
|
278
|
+
const [selectedIndex, setSelectedIndex] = React.useState(0)
|
279
|
+
|
280
|
+
return (
|
281
|
+
<>
|
282
|
+
<h1>Disabled Items</h1>
|
283
|
+
<ErsatzOverlay>
|
284
|
+
<ActionList selectionVariant="single" showDividers role="listbox" aria-label="Select a project">
|
285
|
+
{projects.map((project, index) => (
|
286
|
+
<ActionList.Item
|
287
|
+
key={index}
|
288
|
+
role="option"
|
289
|
+
selected={index === selectedIndex}
|
290
|
+
onSelect={() => setSelectedIndex(index)}
|
291
|
+
disabled={index === 1}
|
292
|
+
>
|
293
|
+
<ActionList.LeadingVisual>
|
294
|
+
<TableIcon />
|
295
|
+
</ActionList.LeadingVisual>
|
296
|
+
{project.name}
|
297
|
+
<ActionList.Description variant="block">{project.scope}</ActionList.Description>
|
298
|
+
</ActionList.Item>
|
299
|
+
))}
|
300
|
+
</ActionList>
|
301
|
+
</ErsatzOverlay>
|
302
|
+
</>
|
303
|
+
)
|
304
|
+
}
|
305
|
+
DisabledStory.storyName = 'Disabled Items'
|
306
|
+
|
307
|
+
export function GroupsStory(): JSX.Element {
|
308
|
+
const [assignees, setAssignees] = React.useState(users.slice(0, 1))
|
309
|
+
|
310
|
+
const toggleAssignee = assignee => {
|
311
|
+
const assigneeIndex = assignees.findIndex(a => a.login === assignee.login)
|
312
|
+
|
313
|
+
if (assigneeIndex === -1) setAssignees([...assignees, assignee])
|
314
|
+
else setAssignees(assignees.filter((_, index) => index !== assigneeIndex))
|
315
|
+
}
|
316
|
+
|
317
|
+
return (
|
318
|
+
<>
|
319
|
+
<h1>Groups</h1>
|
320
|
+
<ErsatzOverlay>
|
321
|
+
<ActionList selectionVariant="multiple" showDividers role="listbox" aria-label="Select reviewers">
|
322
|
+
<ActionList.Group title="Suggestions" variant="filled">
|
323
|
+
{users.slice(0, 2).map(user => (
|
324
|
+
<ActionList.Item
|
325
|
+
key={user.login}
|
326
|
+
role="option"
|
327
|
+
selected={Boolean(assignees.find(assignee => assignee.login === user.login))}
|
328
|
+
onSelect={() => toggleAssignee(user)}
|
329
|
+
>
|
330
|
+
<ActionList.LeadingVisual>
|
331
|
+
<Avatar src={`https://github.com/${user.login}.png`} />
|
332
|
+
</ActionList.LeadingVisual>
|
333
|
+
{user.login}
|
334
|
+
<ActionList.Description>{user.name}</ActionList.Description>
|
335
|
+
<ActionList.Description variant="block">Recently edited these files</ActionList.Description>
|
336
|
+
</ActionList.Item>
|
337
|
+
))}
|
338
|
+
</ActionList.Group>
|
339
|
+
<ActionList.Group title="Everyone" variant="filled">
|
340
|
+
{users.slice(2).map(user => (
|
341
|
+
<ActionList.Item
|
342
|
+
role="option"
|
343
|
+
key={user.login}
|
344
|
+
selected={Boolean(assignees.find(assignee => assignee.login === user.login))}
|
345
|
+
onSelect={() => toggleAssignee(user)}
|
346
|
+
>
|
347
|
+
<ActionList.LeadingVisual>
|
348
|
+
<Avatar src={`https://github.com/${user.login}.png`} />
|
349
|
+
</ActionList.LeadingVisual>
|
350
|
+
{user.login}
|
351
|
+
<ActionList.Description>{user.name}</ActionList.Description>
|
352
|
+
</ActionList.Item>
|
353
|
+
))}
|
354
|
+
</ActionList.Group>
|
355
|
+
</ActionList>
|
356
|
+
</ErsatzOverlay>
|
357
|
+
</>
|
358
|
+
)
|
359
|
+
}
|
360
|
+
GroupsStory.storyName = 'Groups'
|
361
|
+
|
362
|
+
export function ActionsStory(): JSX.Element {
|
363
|
+
return (
|
364
|
+
<>
|
365
|
+
<h1>Actions</h1>
|
366
|
+
<ErsatzOverlay>
|
367
|
+
<ActionList showDividers>
|
368
|
+
<ActionList.Item>
|
369
|
+
<ActionList.LeadingVisual>
|
370
|
+
<ServerIcon />
|
371
|
+
</ActionList.LeadingVisual>
|
372
|
+
Open current Codespace
|
373
|
+
<ActionList.Description variant="block">
|
374
|
+
Your existing Codespace will be opened to its previous state, and you'll be asked to manually switch
|
375
|
+
to new-branch.
|
376
|
+
</ActionList.Description>
|
377
|
+
</ActionList.Item>
|
378
|
+
<ActionList.Item>
|
379
|
+
<ActionList.LeadingVisual>
|
380
|
+
<PlusCircleIcon />
|
381
|
+
</ActionList.LeadingVisual>
|
382
|
+
Create new Codespace
|
383
|
+
<ActionList.Description variant="block">
|
384
|
+
Create a brand new Codespace with a fresh image and checkout this branch.
|
385
|
+
</ActionList.Description>
|
386
|
+
</ActionList.Item>
|
387
|
+
</ActionList>
|
388
|
+
</ErsatzOverlay>
|
389
|
+
</>
|
390
|
+
)
|
391
|
+
}
|
392
|
+
ActionsStory.storyName = 'Actions'
|
393
|
+
|
394
|
+
export function ComplexListInsetVariantStory(): JSX.Element {
|
395
|
+
return (
|
396
|
+
<>
|
397
|
+
<h1>Complex List</h1>
|
398
|
+
<h2>Inset Variant</h2>
|
399
|
+
<ErsatzOverlay>
|
400
|
+
<ActionList showDividers>
|
401
|
+
<ActionList.Item>
|
402
|
+
<ActionList.LeadingVisual>
|
403
|
+
<TypographyIcon />
|
404
|
+
</ActionList.LeadingVisual>
|
405
|
+
Rename
|
406
|
+
</ActionList.Item>
|
407
|
+
<ActionList.Item>
|
408
|
+
<ActionList.LeadingVisual>
|
409
|
+
<VersionsIcon />
|
410
|
+
</ActionList.LeadingVisual>
|
411
|
+
Duplicate
|
412
|
+
<ActionList.Description>Create a copy</ActionList.Description>
|
413
|
+
</ActionList.Item>
|
414
|
+
|
415
|
+
<ActionList.Group title="Live query" variant="filled">
|
416
|
+
<ActionList.Item style={{color: 'rebeccapurple'}}>
|
417
|
+
<ActionList.LeadingVisual>
|
418
|
+
<SearchIcon />
|
419
|
+
</ActionList.LeadingVisual>
|
420
|
+
repo:github/memex,github/github
|
421
|
+
</ActionList.Item>
|
422
|
+
</ActionList.Group>
|
423
|
+
<ActionList.Divider />
|
424
|
+
<ActionList.Group title="Layout" variant="subtle">
|
425
|
+
<ActionList.Item>
|
426
|
+
<ActionList.LeadingVisual>
|
427
|
+
<NoteIcon />
|
428
|
+
</ActionList.LeadingVisual>
|
429
|
+
Table
|
430
|
+
<ActionList.Description variant="block">
|
431
|
+
Information-dense table optimized for operations across teams
|
432
|
+
</ActionList.Description>
|
433
|
+
</ActionList.Item>
|
434
|
+
<ActionList.Item role="listitem">
|
435
|
+
<ActionList.LeadingVisual>
|
436
|
+
<ProjectIcon />
|
437
|
+
</ActionList.LeadingVisual>
|
438
|
+
Board
|
439
|
+
<ActionList.Description variant="block">
|
440
|
+
Kanban-style board focused on visual states
|
441
|
+
</ActionList.Description>
|
442
|
+
</ActionList.Item>
|
443
|
+
</ActionList.Group>
|
444
|
+
<ActionList.Divider />
|
445
|
+
<ActionList.Group>
|
446
|
+
<ActionList.Item style={{fontWeight: 'bold'}}>
|
447
|
+
<ActionList.LeadingVisual>
|
448
|
+
<FilterIcon />
|
449
|
+
</ActionList.LeadingVisual>
|
450
|
+
Save sort and filters to current view
|
451
|
+
</ActionList.Item>
|
452
|
+
<ActionList.Item style={{fontWeight: 'bold'}}>
|
453
|
+
<ActionList.LeadingVisual>
|
454
|
+
<FilterIcon />
|
455
|
+
</ActionList.LeadingVisual>
|
456
|
+
Save sort and filters to new view
|
457
|
+
</ActionList.Item>
|
458
|
+
</ActionList.Group>
|
459
|
+
<ActionList.Divider />
|
460
|
+
<ActionList.Group sx={{backgroundColor: 'accent.emphasis'}}>
|
461
|
+
<ActionList.Item sx={{color: 'white'}}>
|
462
|
+
<ActionList.LeadingVisual sx={{color: 'white'}}>
|
463
|
+
<GearIcon />
|
464
|
+
</ActionList.LeadingVisual>
|
465
|
+
View settings
|
466
|
+
</ActionList.Item>
|
467
|
+
</ActionList.Group>
|
468
|
+
</ActionList>
|
469
|
+
</ErsatzOverlay>
|
470
|
+
</>
|
471
|
+
)
|
472
|
+
}
|
473
|
+
ComplexListInsetVariantStory.storyName = 'Complex List — Inset Variant'
|
474
|
+
|
475
|
+
export function ComplexListFullVariantStory(): JSX.Element {
|
476
|
+
return (
|
477
|
+
<>
|
478
|
+
<h1>Complex List</h1>
|
479
|
+
<h2>Full Variant</h2>
|
480
|
+
<ErsatzOverlay>
|
481
|
+
<ActionList variant="full">
|
482
|
+
<ActionList.Item>
|
483
|
+
<ActionList.LeadingVisual>
|
484
|
+
<TypographyIcon />
|
485
|
+
</ActionList.LeadingVisual>
|
486
|
+
Rename
|
487
|
+
</ActionList.Item>
|
488
|
+
<ActionList.Item>
|
489
|
+
<ActionList.LeadingVisual>
|
490
|
+
<VersionsIcon />
|
491
|
+
</ActionList.LeadingVisual>
|
492
|
+
Duplicate
|
493
|
+
<ActionList.Description>Create a copy</ActionList.Description>
|
494
|
+
</ActionList.Item>
|
495
|
+
|
496
|
+
<ActionList.Group title="Live query" variant="filled">
|
497
|
+
<ActionList.Item style={{color: 'rebeccapurple'}}>
|
498
|
+
<ActionList.LeadingVisual>
|
499
|
+
<SearchIcon />
|
500
|
+
</ActionList.LeadingVisual>
|
501
|
+
repo:github/memex,github/github
|
502
|
+
</ActionList.Item>
|
503
|
+
</ActionList.Group>
|
504
|
+
<ActionList.Divider />
|
505
|
+
<ActionList.Group title="Layout" variant="subtle">
|
506
|
+
<ActionList.Item>
|
507
|
+
<ActionList.LeadingVisual>
|
508
|
+
<NoteIcon />
|
509
|
+
</ActionList.LeadingVisual>
|
510
|
+
Table
|
511
|
+
<ActionList.Description variant="block">
|
512
|
+
Information-dense table optimized for operations across teams
|
513
|
+
</ActionList.Description>
|
514
|
+
</ActionList.Item>
|
515
|
+
<ActionList.Item>
|
516
|
+
<ActionList.LeadingVisual>
|
517
|
+
<ProjectIcon />
|
518
|
+
</ActionList.LeadingVisual>
|
519
|
+
Board
|
520
|
+
<ActionList.Description variant="block">
|
521
|
+
Kanban-style board focused on visual states
|
522
|
+
</ActionList.Description>
|
523
|
+
</ActionList.Item>
|
524
|
+
</ActionList.Group>
|
525
|
+
<ActionList.Divider />
|
526
|
+
<ActionList.Group>
|
527
|
+
<ActionList.Item style={{fontWeight: 'bold'}}>
|
528
|
+
<ActionList.LeadingVisual>
|
529
|
+
<FilterIcon />
|
530
|
+
</ActionList.LeadingVisual>
|
531
|
+
Save sort and filters to current view
|
532
|
+
</ActionList.Item>
|
533
|
+
<ActionList.Item style={{fontWeight: 'bold'}}>
|
534
|
+
<ActionList.LeadingVisual>
|
535
|
+
<FilterIcon />
|
536
|
+
</ActionList.LeadingVisual>
|
537
|
+
Save sort and filters to new view
|
538
|
+
</ActionList.Item>
|
539
|
+
</ActionList.Group>
|
540
|
+
<ActionList.Divider />
|
541
|
+
<ActionList.Group sx={{backgroundColor: 'accent.emphasis'}}>
|
542
|
+
<ActionList.Item sx={{color: 'white'}}>
|
543
|
+
<ActionList.LeadingVisual sx={{color: 'white'}}>
|
544
|
+
<GearIcon />
|
545
|
+
</ActionList.LeadingVisual>
|
546
|
+
View settings
|
547
|
+
</ActionList.Item>
|
548
|
+
</ActionList.Group>
|
549
|
+
</ActionList>
|
550
|
+
</ErsatzOverlay>
|
551
|
+
</>
|
552
|
+
)
|
553
|
+
}
|
554
|
+
ComplexListFullVariantStory.storyName = 'Complex List — Full Variant'
|
555
|
+
|
556
|
+
type ReactRouterLikeLinkProps = {to: string; children: React.ReactNode}
|
557
|
+
const ReactRouterLikeLink = forwardRef<HTMLAnchorElement, ReactRouterLikeLinkProps>(
|
558
|
+
({to, ...props}: {to: string; children: React.ReactNode}, ref) => {
|
559
|
+
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
560
|
+
return <a ref={ref} href={to} {...props} />
|
561
|
+
}
|
562
|
+
)
|
563
|
+
|
564
|
+
const NextJSLikeLink = forwardRef(
|
565
|
+
({href, children}: {href: string; children: React.ReactNode}, ref): React.ReactElement => {
|
566
|
+
const child = React.Children.only(children)
|
567
|
+
const childProps = {
|
568
|
+
ref,
|
569
|
+
href
|
570
|
+
}
|
571
|
+
return <>{React.isValidElement(child) ? React.cloneElement(child, childProps) : null}</>
|
572
|
+
}
|
573
|
+
)
|
574
|
+
|
575
|
+
export function LinkItemStory(): JSX.Element {
|
576
|
+
return (
|
577
|
+
<>
|
578
|
+
<h1>List with LinkItem</h1>
|
579
|
+
<ErsatzOverlay>
|
580
|
+
<ActionList showDividers>
|
581
|
+
<ActionList.Item>
|
582
|
+
<ActionList.LeadingVisual>
|
583
|
+
<XIcon />
|
584
|
+
</ActionList.LeadingVisual>
|
585
|
+
not a link, just an Item for comparison
|
586
|
+
</ActionList.Item>
|
587
|
+
<ActionList.LinkItem href="https://github.com/primer">
|
588
|
+
<ActionList.LeadingVisual>
|
589
|
+
<LinkIcon />
|
590
|
+
</ActionList.LeadingVisual>
|
591
|
+
ActionList.LinkItem
|
592
|
+
</ActionList.LinkItem>
|
593
|
+
<ActionList.LinkItem href="https://github.com/primer" target="_blank" rel="noopener noreferrer">
|
594
|
+
<ActionList.LeadingVisual>
|
595
|
+
<LinkIcon />
|
596
|
+
</ActionList.LeadingVisual>
|
597
|
+
ActionList.LinkItem with anchor attributes
|
598
|
+
</ActionList.LinkItem>
|
599
|
+
<ActionList.LinkItem
|
600
|
+
as={ReactRouterLikeLink}
|
601
|
+
to="?path=/story/composite-components-actionlist2--simple-list-story"
|
602
|
+
>
|
603
|
+
<ActionList.LeadingVisual>
|
604
|
+
<LinkIcon />
|
605
|
+
</ActionList.LeadingVisual>
|
606
|
+
as ReactRouterLink
|
607
|
+
</ActionList.LinkItem>
|
608
|
+
<NextJSLikeLink href="?path=/story/composite-components-actionlist2--simple-list-story">
|
609
|
+
<ActionList.LinkItem>
|
610
|
+
<ActionList.LeadingVisual>
|
611
|
+
<LinkIcon />
|
612
|
+
</ActionList.LeadingVisual>
|
613
|
+
NextJS style Link
|
614
|
+
</ActionList.LinkItem>
|
615
|
+
</NextJSLikeLink>
|
616
|
+
<ActionList.LinkItem href="?path=/story/composite-components-actionlist2--simple-list-story">
|
617
|
+
<ActionList.LeadingVisual>
|
618
|
+
<LinkIcon />
|
619
|
+
</ActionList.LeadingVisual>
|
620
|
+
ActionList.LinkItem with everything
|
621
|
+
<ActionList.Description variant="inline">inline description</ActionList.Description>
|
622
|
+
<ActionList.Description variant="block">Block description</ActionList.Description>
|
623
|
+
<ActionList.TrailingVisual>⌘ + L</ActionList.TrailingVisual>
|
624
|
+
</ActionList.LinkItem>
|
625
|
+
</ActionList>
|
626
|
+
</ErsatzOverlay>
|
627
|
+
</>
|
628
|
+
)
|
629
|
+
}
|
630
|
+
LinkItemStory.storyName = 'List with LinkItem'
|
631
|
+
|
632
|
+
export function DOMPropsStory(): JSX.Element {
|
633
|
+
return (
|
634
|
+
<>
|
635
|
+
<h1>Simple List</h1>
|
636
|
+
<ErsatzOverlay>
|
637
|
+
<ActionList>
|
638
|
+
<ActionList.Item id="something" onClick={event => alert(`Id is '${event.target.id}'`)}>
|
639
|
+
Has an id
|
640
|
+
</ActionList.Item>
|
641
|
+
</ActionList>
|
642
|
+
</ErsatzOverlay>
|
643
|
+
</>
|
644
|
+
)
|
645
|
+
}
|
646
|
+
DOMPropsStory.storyName = 'List an item input including DOM props'
|
647
|
+
|
648
|
+
export function CustomItemChildren(): JSX.Element {
|
649
|
+
return (
|
650
|
+
<>
|
651
|
+
<h1>Custom Item Children</h1>
|
652
|
+
<ErsatzOverlay>
|
653
|
+
<ActionList>
|
654
|
+
<ActionList.Item>
|
655
|
+
<ActionList.LeadingVisual>
|
656
|
+
<ArrowRightIcon />
|
657
|
+
</ActionList.LeadingVisual>
|
658
|
+
<Label outline borderColor="success.emphasis">
|
659
|
+
Choose this one
|
660
|
+
</Label>
|
661
|
+
<ActionList.TrailingVisual>
|
662
|
+
<ArrowLeftIcon />
|
663
|
+
</ActionList.TrailingVisual>
|
664
|
+
</ActionList.Item>
|
665
|
+
</ActionList>
|
666
|
+
</ErsatzOverlay>
|
667
|
+
</>
|
668
|
+
)
|
669
|
+
}
|
670
|
+
CustomItemChildren.storyName = 'Custom Item Children'
|
671
|
+
|
672
|
+
export function SizeStressTestingStory(): JSX.Element {
|
673
|
+
return (
|
674
|
+
<>
|
675
|
+
<h1>Size Stress Testing</h1>
|
676
|
+
<ErsatzOverlay maxWidth="300px">
|
677
|
+
<ActionList showDividers>
|
678
|
+
<ActionList.Item>
|
679
|
+
<ActionList.LeadingVisual>
|
680
|
+
<ArrowRightIcon />
|
681
|
+
</ActionList.LeadingVisual>
|
682
|
+
Block Description. Long text should wrap
|
683
|
+
<ActionList.Description variant="block">
|
684
|
+
This description is long, but it is block so it wraps
|
685
|
+
</ActionList.Description>
|
686
|
+
<ActionList.TrailingVisual>
|
687
|
+
<ArrowLeftIcon />
|
688
|
+
</ActionList.TrailingVisual>
|
689
|
+
</ActionList.Item>
|
690
|
+
<ActionList.Item>
|
691
|
+
<ActionList.LeadingVisual>
|
692
|
+
<ArrowRightIcon />
|
693
|
+
</ActionList.LeadingVisual>
|
694
|
+
Inline Description
|
695
|
+
<ActionList.Description>This description gets truncated because it is inline</ActionList.Description>
|
696
|
+
<ActionList.TrailingVisual>
|
697
|
+
<ArrowLeftIcon />
|
698
|
+
</ActionList.TrailingVisual>
|
699
|
+
</ActionList.Item>
|
700
|
+
<ActionList.Item>
|
701
|
+
<ActionList.LeadingVisual>
|
702
|
+
<ArrowRightIcon />
|
703
|
+
</ActionList.LeadingVisual>
|
704
|
+
Really long text without a description should wrap so it wraps
|
705
|
+
<ActionList.TrailingVisual>
|
706
|
+
<ArrowLeftIcon />
|
707
|
+
</ActionList.TrailingVisual>
|
708
|
+
</ActionList.Item>
|
709
|
+
</ActionList>
|
710
|
+
</ErsatzOverlay>
|
711
|
+
</>
|
712
|
+
)
|
713
|
+
}
|
714
|
+
SizeStressTestingStory.storyName = 'Size Stress Testing'
|
715
|
+
|
716
|
+
export function AllCombinations(): JSX.Element {
|
717
|
+
return (
|
718
|
+
<>
|
719
|
+
<h1>All Possible Combinations</h1>
|
720
|
+
<code>
|
721
|
+
dynamic features: L = Leading Visual, I = Inline Description, B = Block Description, T = Trailing Visual
|
722
|
+
</code>
|
723
|
+
<br />
|
724
|
+
<code>16 possible combinations</code>
|
725
|
+
<br />
|
726
|
+
<br />
|
727
|
+
<ErsatzOverlay maxWidth="300px">
|
728
|
+
<ActionList showDividers>
|
729
|
+
<ActionList.Item>
|
730
|
+
<ActionList.LeadingVisual>
|
731
|
+
<StarIcon />
|
732
|
+
</ActionList.LeadingVisual>
|
733
|
+
The everything bagel
|
734
|
+
<ActionList.Description variant="inline">inline description</ActionList.Description>
|
735
|
+
<ActionList.Description variant="block">Block description</ActionList.Description>
|
736
|
+
<ActionList.TrailingVisual>
|
737
|
+
<StarIcon />
|
738
|
+
</ActionList.TrailingVisual>
|
739
|
+
</ActionList.Item>
|
740
|
+
<ActionList.Item>none of them, only text</ActionList.Item>
|
741
|
+
<ActionList.Item>
|
742
|
+
<ActionList.LeadingVisual>
|
743
|
+
<StarIcon />
|
744
|
+
</ActionList.LeadingVisual>
|
745
|
+
only L
|
746
|
+
</ActionList.Item>
|
747
|
+
<ActionList.Item>
|
748
|
+
only I<ActionList.Description variant="inline">inline description</ActionList.Description>
|
749
|
+
</ActionList.Item>
|
750
|
+
<ActionList.Item>
|
751
|
+
only B<ActionList.Description variant="block">Block description</ActionList.Description>
|
752
|
+
</ActionList.Item>
|
753
|
+
<ActionList.Item>
|
754
|
+
only T
|
755
|
+
<ActionList.TrailingVisual>
|
756
|
+
<StarIcon />
|
757
|
+
</ActionList.TrailingVisual>
|
758
|
+
</ActionList.Item>
|
759
|
+
<ActionList.Item>
|
760
|
+
<ActionList.LeadingVisual>
|
761
|
+
<StarIcon />
|
762
|
+
</ActionList.LeadingVisual>
|
763
|
+
L + I<ActionList.Description variant="inline">inline description</ActionList.Description>
|
764
|
+
</ActionList.Item>
|
765
|
+
<ActionList.Item>
|
766
|
+
<ActionList.LeadingVisual>
|
767
|
+
<StarIcon />
|
768
|
+
</ActionList.LeadingVisual>
|
769
|
+
L + B<ActionList.Description variant="block">Block description</ActionList.Description>
|
770
|
+
</ActionList.Item>
|
771
|
+
<ActionList.Item>
|
772
|
+
<ActionList.LeadingVisual>
|
773
|
+
<StarIcon />
|
774
|
+
</ActionList.LeadingVisual>
|
775
|
+
L + T
|
776
|
+
<ActionList.TrailingVisual>
|
777
|
+
<StarIcon />
|
778
|
+
</ActionList.TrailingVisual>
|
779
|
+
</ActionList.Item>
|
780
|
+
<ActionList.Item>
|
781
|
+
I + B<ActionList.Description variant="inline">inline description</ActionList.Description>
|
782
|
+
<ActionList.Description variant="block">Block description</ActionList.Description>
|
783
|
+
</ActionList.Item>
|
784
|
+
<ActionList.Item>
|
785
|
+
I + T<ActionList.Description variant="inline">inline description</ActionList.Description>
|
786
|
+
<ActionList.TrailingVisual>
|
787
|
+
<StarIcon />
|
788
|
+
</ActionList.TrailingVisual>
|
789
|
+
</ActionList.Item>
|
790
|
+
<ActionList.Item>
|
791
|
+
B + T<ActionList.Description variant="block">Block description</ActionList.Description>
|
792
|
+
<ActionList.TrailingVisual>
|
793
|
+
<StarIcon />
|
794
|
+
</ActionList.TrailingVisual>
|
795
|
+
</ActionList.Item>
|
796
|
+
<ActionList.Item>
|
797
|
+
<ActionList.LeadingVisual>
|
798
|
+
<StarIcon />
|
799
|
+
</ActionList.LeadingVisual>
|
800
|
+
L + I + B<ActionList.Description variant="inline">inline description</ActionList.Description>
|
801
|
+
<ActionList.Description variant="block">Block description</ActionList.Description>
|
802
|
+
</ActionList.Item>
|
803
|
+
<ActionList.Item>
|
804
|
+
<ActionList.LeadingVisual>
|
805
|
+
<StarIcon />
|
806
|
+
</ActionList.LeadingVisual>
|
807
|
+
L + I + T<ActionList.Description variant="inline">inline description</ActionList.Description>
|
808
|
+
<ActionList.TrailingVisual>
|
809
|
+
<StarIcon />
|
810
|
+
</ActionList.TrailingVisual>
|
811
|
+
</ActionList.Item>
|
812
|
+
<ActionList.Item>
|
813
|
+
<ActionList.LeadingVisual>
|
814
|
+
<StarIcon />
|
815
|
+
</ActionList.LeadingVisual>
|
816
|
+
L + B + T<ActionList.Description variant="block">Block description</ActionList.Description>
|
817
|
+
<ActionList.TrailingVisual>
|
818
|
+
<StarIcon />
|
819
|
+
</ActionList.TrailingVisual>
|
820
|
+
</ActionList.Item>
|
821
|
+
<ActionList.Item>
|
822
|
+
I + B + T<ActionList.Description variant="inline">inline description</ActionList.Description>
|
823
|
+
<ActionList.Description variant="block">Block description</ActionList.Description>
|
824
|
+
<ActionList.TrailingVisual>
|
825
|
+
<StarIcon />
|
826
|
+
</ActionList.TrailingVisual>
|
827
|
+
</ActionList.Item>
|
828
|
+
</ActionList>
|
829
|
+
</ErsatzOverlay>
|
830
|
+
</>
|
831
|
+
)
|
832
|
+
}
|
833
|
+
AllCombinations.storyName = 'All Combinations'
|
834
|
+
|
835
|
+
const teams = [
|
836
|
+
{id: '5025661', type: 'team', slug: 'github/primer-reviewers', name: 'Primer Reviewers', members: 20},
|
837
|
+
{id: '1929972', type: 'team', slug: 'github/design-infrastructure', name: 'Design Infrastructure', members: 20}
|
838
|
+
]
|
839
|
+
|
840
|
+
export function ConditionalChildren(): JSX.Element {
|
841
|
+
type reviewerType = {name: string; id?: string; type?: string; login?: string; slug?: string; members?: number}
|
842
|
+
const potentialReviewers: reviewerType[] = [...teams, ...users]
|
843
|
+
return (
|
844
|
+
<>
|
845
|
+
<h1>Conditional Children</h1>
|
846
|
+
<ErsatzOverlay>
|
847
|
+
<ActionList showDividers>
|
848
|
+
{potentialReviewers.map((reviewer, index) => (
|
849
|
+
<ActionList.Item key={index}>
|
850
|
+
<ActionList.LeadingVisual>
|
851
|
+
{reviewer.type === 'team' ? (
|
852
|
+
<Avatar src={`https://avatars.githubusercontent.com/t/${reviewer.id}`} />
|
853
|
+
) : (
|
854
|
+
<Avatar src={`https://avatars.githubusercontent.com/${reviewer.login}`} />
|
855
|
+
)}
|
856
|
+
</ActionList.LeadingVisual>
|
857
|
+
{reviewer.login || reviewer.slug}
|
858
|
+
{reviewer.type === 'team' ? (
|
859
|
+
<ActionList.Description variant="block">{reviewer.name}</ActionList.Description>
|
860
|
+
) : (
|
861
|
+
<ActionList.Description>{reviewer.name}</ActionList.Description>
|
862
|
+
)}
|
863
|
+
{reviewer.type === 'team' && (
|
864
|
+
<ActionList.TrailingVisual>
|
865
|
+
<PeopleIcon />
|
866
|
+
{reviewer.members}
|
867
|
+
</ActionList.TrailingVisual>
|
868
|
+
)}
|
869
|
+
</ActionList.Item>
|
870
|
+
))}
|
871
|
+
</ActionList>
|
872
|
+
</ErsatzOverlay>
|
873
|
+
</>
|
874
|
+
)
|
875
|
+
}
|
876
|
+
ConditionalChildren.storyName = 'Conditional Children'
|
877
|
+
|
878
|
+
export function NestedChildren(): JSX.Element {
|
879
|
+
return (
|
880
|
+
<>
|
881
|
+
<h1>Nested Children</h1>
|
882
|
+
<ErsatzOverlay>
|
883
|
+
<ActionList showDividers>
|
884
|
+
<li>
|
885
|
+
<ul id="i like extra lists" style={{paddingInlineStart: 0}}>
|
886
|
+
{users.map(user => (
|
887
|
+
<ActionList.Item key={user.login}>
|
888
|
+
<ActionList.LeadingVisual>
|
889
|
+
<Avatar src={`https://avatars.githubusercontent.com/${user.login}`} />
|
890
|
+
</ActionList.LeadingVisual>
|
891
|
+
{user.login}
|
892
|
+
<ReviewerDescription user={user} />
|
893
|
+
</ActionList.Item>
|
894
|
+
))}
|
895
|
+
</ul>
|
896
|
+
</li>
|
897
|
+
</ActionList>
|
898
|
+
</ErsatzOverlay>
|
899
|
+
</>
|
900
|
+
)
|
901
|
+
}
|
902
|
+
NestedChildren.storyName = 'Nested Children'
|
903
|
+
|
904
|
+
const ReviewerDescription = ({user}) => {
|
905
|
+
const usersRecentlyEditedFile = users.slice(0, 2)
|
906
|
+
|
907
|
+
if (usersRecentlyEditedFile.find(u => u.login === user.login)) {
|
908
|
+
return (
|
909
|
+
<span>
|
910
|
+
<ActionList.Description>{user.name}</ActionList.Description>
|
911
|
+
<ActionList.Description variant="block">Recently edited this file</ActionList.Description>
|
912
|
+
</span>
|
913
|
+
)
|
914
|
+
} else {
|
915
|
+
return <ActionList.Description>{user.name}</ActionList.Description>
|
916
|
+
}
|
917
|
+
}
|
918
|
+
|
919
|
+
export function ChildWithInternalState(): JSX.Element {
|
920
|
+
return (
|
921
|
+
<>
|
922
|
+
<h1>Child with internal state - broken</h1>
|
923
|
+
<ErsatzOverlay>
|
924
|
+
<ActionList showDividers>
|
925
|
+
{users.map(user => (
|
926
|
+
<ActionList.Item key={user.login}>
|
927
|
+
<ActionList.LeadingVisual>
|
928
|
+
<Avatar src={`https://avatars.githubusercontent.com/${user.login}`} />
|
929
|
+
</ActionList.LeadingVisual>
|
930
|
+
{user.login}
|
931
|
+
<StatefulChild>{user.name}</StatefulChild>
|
932
|
+
</ActionList.Item>
|
933
|
+
))}
|
934
|
+
</ActionList>
|
935
|
+
</ErsatzOverlay>
|
936
|
+
</>
|
937
|
+
)
|
938
|
+
}
|
939
|
+
ChildWithInternalState.storyName = 'Child with internal state'
|
940
|
+
|
941
|
+
const StatefulChild = props => {
|
942
|
+
const [nameVisible, setNameVisibility] = React.useState(false)
|
943
|
+
const toggle = () => {
|
944
|
+
setNameVisibility(!nameVisible)
|
945
|
+
}
|
946
|
+
|
947
|
+
/** once description is registered, it cannot be unregistered, only updated. */
|
948
|
+
|
949
|
+
return (
|
950
|
+
<>
|
951
|
+
<ButtonInvisible onClick={toggle} sx={{fontSize: 0, paddingY: 0}}>
|
952
|
+
{nameVisible ? 'Hide name' : 'Show name'}
|
953
|
+
</ButtonInvisible>
|
954
|
+
{nameVisible && <ActionList.Description>{props.children}</ActionList.Description>}
|
955
|
+
</>
|
956
|
+
)
|
957
|
+
}
|
958
|
+
|
959
|
+
export function ChildWithSideEffects(): JSX.Element {
|
960
|
+
const user = users[0]
|
961
|
+
const [selected, setSelected] = React.useState(true)
|
962
|
+
|
963
|
+
return (
|
964
|
+
<>
|
965
|
+
<h1>Child with side effects</h1>
|
966
|
+
<ErsatzOverlay>
|
967
|
+
<ActionList selectionVariant="multiple" role="listbox" aria-label="Select assignees">
|
968
|
+
<ActionList.Item selected={selected} onSelect={() => setSelected(!selected)} role="option">
|
969
|
+
<ActionList.LeadingVisual>
|
970
|
+
<Avatar src={`https://avatars.githubusercontent.com/${user.login}`} />
|
971
|
+
</ActionList.LeadingVisual>
|
972
|
+
{user.login}
|
973
|
+
<SideEffectDescription />
|
974
|
+
</ActionList.Item>
|
975
|
+
</ActionList>
|
976
|
+
</ErsatzOverlay>
|
977
|
+
</>
|
978
|
+
)
|
979
|
+
}
|
980
|
+
ChildWithSideEffects.storyName = 'Child with side effects'
|
981
|
+
|
982
|
+
const SideEffectDescription = () => {
|
983
|
+
const [seconds, setSeconds] = React.useState(0)
|
984
|
+
|
985
|
+
React.useEffect(() => {
|
986
|
+
const fn = () => setSeconds(s => s + 1)
|
987
|
+
const interval = window.setInterval(fn, 1000)
|
988
|
+
return () => window.clearInterval(interval)
|
989
|
+
}, [])
|
990
|
+
|
991
|
+
return <ActionList.Description>{seconds} seconds passed</ActionList.Description>
|
992
|
+
}
|
993
|
+
|
994
|
+
export function WithSx(): JSX.Element {
|
995
|
+
return (
|
996
|
+
<>
|
997
|
+
<h1>With sx prop</h1>
|
998
|
+
<ErsatzOverlay>
|
999
|
+
<ActionList sx={{paddingTop: 4}}>
|
1000
|
+
<ActionList.Item sx={{backgroundColor: 'accent.subtle'}}>
|
1001
|
+
<ActionList.LeadingVisual sx={{color: 'accent.emphasis'}}>
|
1002
|
+
<LinkIcon />
|
1003
|
+
</ActionList.LeadingVisual>
|
1004
|
+
github.com/primer
|
1005
|
+
<ActionList.Description sx={{paddingLeft: 4, color: 'accent.emphasis'}}>
|
1006
|
+
all items support sx prop
|
1007
|
+
</ActionList.Description>
|
1008
|
+
</ActionList.Item>
|
1009
|
+
<ActionList.Item>
|
1010
|
+
<ActionList.LeadingVisual>
|
1011
|
+
<LawIcon />
|
1012
|
+
</ActionList.LeadingVisual>
|
1013
|
+
MIT License
|
1014
|
+
</ActionList.Item>
|
1015
|
+
<ActionList.Item
|
1016
|
+
variant="danger"
|
1017
|
+
sx={{
|
1018
|
+
borderLeft: '2px solid',
|
1019
|
+
borderColor: 'danger.emphasis',
|
1020
|
+
borderTopLeftRadius: 0,
|
1021
|
+
borderBottomLeftRadius: 0
|
1022
|
+
}}
|
1023
|
+
>
|
1024
|
+
<ActionList.LeadingVisual>
|
1025
|
+
<AlertIcon />
|
1026
|
+
</ActionList.LeadingVisual>
|
1027
|
+
4 vulnerabilities
|
1028
|
+
</ActionList.Item>
|
1029
|
+
</ActionList>
|
1030
|
+
</ErsatzOverlay>
|
1031
|
+
</>
|
1032
|
+
)
|
1033
|
+
}
|
1034
|
+
WithSx.storyName = 'With sx'
|
1035
|
+
|
1036
|
+
export function MemexGroupBy(): JSX.Element {
|
1037
|
+
const [selectedIndex, setSelectedIndex] = React.useState<number | null>(1)
|
1038
|
+
|
1039
|
+
const options = [
|
1040
|
+
{text: 'Status', icon: <IssueOpenedIcon />},
|
1041
|
+
{text: 'Stage', icon: <TableIcon />},
|
1042
|
+
{text: 'Assignee', icon: <PeopleIcon />},
|
1043
|
+
{text: 'Team', icon: <TypographyIcon />},
|
1044
|
+
{text: 'Estimate', icon: <NumberIcon />},
|
1045
|
+
{text: 'Due Date', icon: <CalendarIcon />}
|
1046
|
+
]
|
1047
|
+
|
1048
|
+
return (
|
1049
|
+
<>
|
1050
|
+
<h1>Memex GroupBy List</h1>
|
1051
|
+
<ErsatzOverlay>
|
1052
|
+
<ActionList>
|
1053
|
+
<ActionList.Group title="Group by" selectionVariant="single">
|
1054
|
+
{options.map((option, index) => (
|
1055
|
+
<ActionList.Item key={index} selected={index === selectedIndex} onSelect={() => setSelectedIndex(index)}>
|
1056
|
+
<ActionList.LeadingVisual>{option.icon}</ActionList.LeadingVisual>
|
1057
|
+
{option.text}
|
1058
|
+
</ActionList.Item>
|
1059
|
+
))}
|
1060
|
+
</ActionList.Group>
|
1061
|
+
{typeof selectedIndex === 'number' && (
|
1062
|
+
<>
|
1063
|
+
<ActionList.Divider />
|
1064
|
+
<ActionList.Item onSelect={() => setSelectedIndex(null)}>
|
1065
|
+
<ActionList.LeadingVisual>
|
1066
|
+
<XIcon />
|
1067
|
+
</ActionList.LeadingVisual>
|
1068
|
+
Clear Group by
|
1069
|
+
</ActionList.Item>
|
1070
|
+
</>
|
1071
|
+
)}
|
1072
|
+
</ActionList>
|
1073
|
+
</ErsatzOverlay>
|
1074
|
+
</>
|
1075
|
+
)
|
1076
|
+
}
|
1077
|
+
MemexGroupBy.storyName = 'Memex GroupBy List'
|
1078
|
+
|
1079
|
+
type Option = {text: string; icon: React.ReactNode; selected: boolean}
|
1080
|
+
export function MemexSortable(): JSX.Element {
|
1081
|
+
const [options, setOptions] = React.useState<Option[]>([
|
1082
|
+
{text: 'Status', icon: <IssueOpenedIcon />, selected: true},
|
1083
|
+
{text: 'Stage', icon: <TableIcon />, selected: true},
|
1084
|
+
{text: 'Assignee', icon: <PeopleIcon />, selected: true},
|
1085
|
+
{text: 'Team', icon: <TypographyIcon />, selected: true},
|
1086
|
+
{text: 'Estimate', icon: <NumberIcon />, selected: false},
|
1087
|
+
{text: 'Due Date', icon: <CalendarIcon />, selected: false}
|
1088
|
+
])
|
1089
|
+
|
1090
|
+
const toggle = (text: string) => {
|
1091
|
+
setOptions(
|
1092
|
+
options.map(option => {
|
1093
|
+
if (option.text === text) option.selected = !option.selected
|
1094
|
+
return option
|
1095
|
+
})
|
1096
|
+
)
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
const reorder = ({optionToMove, moveAfterOption}: {optionToMove: Option; moveAfterOption: Option}) => {
|
1100
|
+
setOptions(currentOptions => {
|
1101
|
+
const newOptions = [...currentOptions]
|
1102
|
+
// remove option to move
|
1103
|
+
const currentPosition = newOptions.findIndex(o => o.text === optionToMove.text)
|
1104
|
+
newOptions.splice(currentPosition, 1)
|
1105
|
+
// add it after the provided element
|
1106
|
+
const newPosition = newOptions.findIndex(o => o.text === moveAfterOption.text) + 1
|
1107
|
+
newOptions.splice(newPosition, 0, optionToMove)
|
1108
|
+
return newOptions
|
1109
|
+
})
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
const visibleOptions = options.filter(option => option.selected)
|
1113
|
+
const hiddenOptions = options.filter(option => !option.selected)
|
1114
|
+
|
1115
|
+
return (
|
1116
|
+
<>
|
1117
|
+
<h1>Memex Sortable List</h1>
|
1118
|
+
<ErsatzOverlay>
|
1119
|
+
<DndProvider backend={HTML5Backend}>
|
1120
|
+
<ActionList selectionVariant="multiple">
|
1121
|
+
<ActionList.Group title="Visible fields (can be reordered)">
|
1122
|
+
{visibleOptions.map(option => (
|
1123
|
+
<SortableItem
|
1124
|
+
key={option.text}
|
1125
|
+
option={option}
|
1126
|
+
onSelect={() => toggle(option.text)}
|
1127
|
+
reorder={reorder}
|
1128
|
+
/>
|
1129
|
+
))}
|
1130
|
+
</ActionList.Group>
|
1131
|
+
<ActionList.Group
|
1132
|
+
title="Hidden fields"
|
1133
|
+
selectionVariant={
|
1134
|
+
/** selectionVariant override on Group: disable selection if there are no options */
|
1135
|
+
hiddenOptions.length ? 'multiple' : false
|
1136
|
+
}
|
1137
|
+
>
|
1138
|
+
{hiddenOptions.map((option, index) => (
|
1139
|
+
<ActionList.Item key={index} selected={option.selected} onSelect={() => toggle(option.text)}>
|
1140
|
+
<ActionList.LeadingVisual>{option.icon}</ActionList.LeadingVisual>
|
1141
|
+
{option.text}
|
1142
|
+
</ActionList.Item>
|
1143
|
+
))}
|
1144
|
+
{hiddenOptions.length === 0 && <ActionList.Item disabled>No hidden fields</ActionList.Item>}
|
1145
|
+
</ActionList.Group>
|
1146
|
+
</ActionList>
|
1147
|
+
</DndProvider>
|
1148
|
+
</ErsatzOverlay>
|
1149
|
+
</>
|
1150
|
+
)
|
1151
|
+
}
|
1152
|
+
MemexSortable.storyName = 'Memex Sortable List'
|
1153
|
+
|
1154
|
+
type SortableItemProps = {
|
1155
|
+
option: Option
|
1156
|
+
onSelect: ItemProps['onSelect']
|
1157
|
+
reorder: ({optionToMove, moveAfterOption}: {optionToMove: Option; moveAfterOption: Option}) => void
|
1158
|
+
}
|
1159
|
+
const SortableItem: React.FC<SortableItemProps> = ({option, onSelect, reorder}) => {
|
1160
|
+
const [{isDragging}, dragRef] = useDrag(() => ({
|
1161
|
+
type: 'ITEM',
|
1162
|
+
item: option,
|
1163
|
+
collect: monitor => {
|
1164
|
+
return {isDragging: monitor.isDragging()}
|
1165
|
+
}
|
1166
|
+
}))
|
1167
|
+
|
1168
|
+
const [{isOver}, dropRef] = useDrop(() => ({
|
1169
|
+
accept: 'ITEM',
|
1170
|
+
collect: monitor => {
|
1171
|
+
return {isOver: monitor.isOver()}
|
1172
|
+
},
|
1173
|
+
drop: (optionDropped: Option) => {
|
1174
|
+
reorder({optionToMove: optionDropped, moveAfterOption: option})
|
1175
|
+
}
|
1176
|
+
}))
|
1177
|
+
|
1178
|
+
return (
|
1179
|
+
<ActionList.Item
|
1180
|
+
ref={element => dragRef(element) && dropRef(element)} // merge refs
|
1181
|
+
selected={option.selected}
|
1182
|
+
onSelect={onSelect}
|
1183
|
+
sx={{
|
1184
|
+
opacity: isDragging ? 0.5 : 1,
|
1185
|
+
boxShadow: isOver ? theme => `0px 2px 0 0px ${theme.colors.accent.emphasis}` : undefined,
|
1186
|
+
borderRadius: isOver ? 0 : 2
|
1187
|
+
}}
|
1188
|
+
>
|
1189
|
+
<ActionList.LeadingVisual>{option.icon}</ActionList.LeadingVisual>
|
1190
|
+
{option.text}
|
1191
|
+
</ActionList.Item>
|
1192
|
+
)
|
1193
|
+
}
|
1194
|
+
|
1195
|
+
const repos = [
|
1196
|
+
'primer/primer-markdown',
|
1197
|
+
'primer/octicons',
|
1198
|
+
'primer/css',
|
1199
|
+
'primer/primer-layout',
|
1200
|
+
'primer/primer-alerts',
|
1201
|
+
'primer/primer-avatars',
|
1202
|
+
'primer/react',
|
1203
|
+
'primer/primitives'
|
1204
|
+
]
|
1205
|
+
|
1206
|
+
export function AsyncListStory(): JSX.Element {
|
1207
|
+
const [results, setResults] = React.useState(repos.slice(0, 6))
|
1208
|
+
const [loading, setLoading] = React.useState(false)
|
1209
|
+
const filter = async event => {
|
1210
|
+
setLoading(true)
|
1211
|
+
const filteredResults = await filterSlowly(event.target.value)
|
1212
|
+
setResults(filteredResults)
|
1213
|
+
setLoading(false)
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
return (
|
1217
|
+
<>
|
1218
|
+
<h1>Async List Items</h1>
|
1219
|
+
<ErsatzOverlay>
|
1220
|
+
<TextInput
|
1221
|
+
onChange={filter}
|
1222
|
+
placeholder="Search repositories, showing 6 by default"
|
1223
|
+
sx={{m: 2, mb: 0, width: 'calc(100% - 16px)'}}
|
1224
|
+
/>
|
1225
|
+
<ActionList sx={{height: 208, overflow: 'auto'}}>
|
1226
|
+
{loading ? (
|
1227
|
+
<Box sx={{display: 'flex', justifyContent: 'center', pt: 2}}>
|
1228
|
+
<Spinner />
|
1229
|
+
</Box>
|
1230
|
+
) : (
|
1231
|
+
results.map(name => (
|
1232
|
+
<ActionList.Item key={name}>
|
1233
|
+
<ActionList.LeadingVisual>
|
1234
|
+
<RepoIcon />
|
1235
|
+
</ActionList.LeadingVisual>
|
1236
|
+
{name}
|
1237
|
+
</ActionList.Item>
|
1238
|
+
))
|
1239
|
+
)}
|
1240
|
+
</ActionList>
|
1241
|
+
</ErsatzOverlay>
|
1242
|
+
</>
|
1243
|
+
)
|
1244
|
+
}
|
1245
|
+
AsyncListStory.storyName = 'Async List Options'
|
1246
|
+
|
1247
|
+
const filterSlowly = async query => {
|
1248
|
+
// sleep for 1s before returning results
|
1249
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
1250
|
+
return await repos.filter(name => name.includes(query))
|
1251
|
+
}
|
1252
|
+
|
1253
|
+
export function InsideOverlay(): JSX.Element {
|
1254
|
+
const [open, setOpen] = React.useState(false)
|
1255
|
+
const toggle = () => setOpen(!open)
|
1256
|
+
return (
|
1257
|
+
<>
|
1258
|
+
<h1>Inside Overlay</h1>
|
1259
|
+
<AnchoredOverlay
|
1260
|
+
open={open}
|
1261
|
+
onOpen={toggle}
|
1262
|
+
onClose={toggle}
|
1263
|
+
renderAnchor={props => <button {...props}>toggle overlay</button>}
|
1264
|
+
>
|
1265
|
+
<ActionList>
|
1266
|
+
<ActionList.Item>
|
1267
|
+
Use your arrow keys
|
1268
|
+
<ActionList.TrailingVisual>↓</ActionList.TrailingVisual>
|
1269
|
+
</ActionList.Item>
|
1270
|
+
<ActionList.Item>
|
1271
|
+
keep going
|
1272
|
+
<ActionList.TrailingVisual>↓</ActionList.TrailingVisual>
|
1273
|
+
</ActionList.Item>
|
1274
|
+
<ActionList.Item>
|
1275
|
+
more more
|
1276
|
+
<ActionList.TrailingVisual>↓</ActionList.TrailingVisual>
|
1277
|
+
</ActionList.Item>
|
1278
|
+
<ActionList.Divider />
|
1279
|
+
<ActionList.Item variant="danger">
|
1280
|
+
now go up!
|
1281
|
+
<ActionList.TrailingVisual>↑</ActionList.TrailingVisual>
|
1282
|
+
</ActionList.Item>
|
1283
|
+
</ActionList>
|
1284
|
+
</AnchoredOverlay>
|
1285
|
+
</>
|
1286
|
+
)
|
1287
|
+
}
|
1288
|
+
InsideOverlay.storyName = 'Inside Overlay'
|