@sproutsocial/seeds-react-menu 1.10.8 → 1.11.0
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/.turbo/turbo-build.log +21 -15
- package/CHANGELOG.md +22 -0
- package/dist/esm/index.js +45 -24
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/v2/index.js +1 -1
- package/dist/esm/v2/index.js.map +1 -1
- package/dist/esm/v3/index.js +800 -0
- package/dist/esm/v3/index.js.map +1 -0
- package/dist/index.d.mts +22 -22
- package/dist/index.d.ts +22 -22
- package/dist/index.js +35 -14
- package/dist/index.js.map +1 -1
- package/dist/v2/index.js +1 -1
- package/dist/v2/index.js.map +1 -1
- package/dist/v3/index.d.mts +295 -0
- package/dist/v3/index.d.ts +295 -0
- package/dist/v3/index.js +868 -0
- package/dist/v3/index.js.map +1 -0
- package/package.json +7 -1
- package/src/MenuGroup/MenuGroup.tsx +16 -3
- package/src/MenuGroup/__tests__/MenuGroup.test.tsx +19 -0
- package/src/MenuItem/MenuItem.tsx +21 -10
- package/src/MenuItem/__tests__/MenuItem.test.tsx +19 -0
- package/src/v2/Common-V2/ComboboxContent.tsx +1 -1
- package/src/v3/Common/ComboboxItem.tsx +82 -0
- package/src/v3/Common/ComboboxStyles.tsx +475 -0
- package/src/v3/Common/SelectGroup.tsx +28 -0
- package/src/v3/Common/SelectIcons.tsx +26 -0
- package/src/v3/Common/SelectItem.tsx +108 -0
- package/src/v3/Common/SelectStyles.tsx +126 -0
- package/src/v3/Common/VirtualizedComboboxList.tsx +200 -0
- package/src/v3/Common/groupItems.ts +12 -0
- package/src/v3/Common/types.ts +3 -0
- package/src/v3/ModalStories.stories.tsx +181 -0
- package/src/v3/MultiCombobox.stories.tsx +322 -0
- package/src/v3/MultiCombobox.tsx +562 -0
- package/src/v3/MultiSearchableSelect.stories.tsx +389 -0
- package/src/v3/MultiSearchableSelect.tsx +545 -0
- package/src/v3/MultiSelect.stories.tsx +300 -0
- package/src/v3/MultiSelect.tsx +498 -0
- package/src/v3/SeedsPortal.tsx +35 -0
- package/src/v3/SingleCombobox.stories.tsx +169 -0
- package/src/v3/SingleCombobox.tsx +304 -0
- package/src/v3/SingleSearchableSelect.stories.tsx +239 -0
- package/src/v3/SingleSearchableSelect.tsx +319 -0
- package/src/v3/SingleSelect.stories.tsx +227 -0
- package/src/v3/SingleSelect.tsx +245 -0
- package/src/v3/index.ts +7 -0
- package/src/v3/storyData.tsx +117 -0
- package/tsup.config.ts +1 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { FormField } from "@sproutsocial/seeds-react-form-field";
|
|
4
|
+
import MultiSearchableSelect from "./MultiSearchableSelect";
|
|
5
|
+
import { books, largeBookSet, itemToString, renderItem } from "./storyData";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof MultiSearchableSelect> = {
|
|
8
|
+
title: "Really Under Development/V3/Multi Searchable Select",
|
|
9
|
+
component: MultiSearchableSelect,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof MultiSearchableSelect>;
|
|
14
|
+
|
|
15
|
+
export const Basic: Story = {
|
|
16
|
+
name: "Basic",
|
|
17
|
+
render: () => (
|
|
18
|
+
<div style={{ width: "300px" }}>
|
|
19
|
+
<FormField label="Pick books">
|
|
20
|
+
{({ id }) => (
|
|
21
|
+
<MultiSearchableSelect
|
|
22
|
+
id={id}
|
|
23
|
+
data={books}
|
|
24
|
+
itemToString={itemToString}
|
|
25
|
+
placeholder="Select books"
|
|
26
|
+
searchPlaceholder="Search books..."
|
|
27
|
+
/>
|
|
28
|
+
)}
|
|
29
|
+
</FormField>
|
|
30
|
+
</div>
|
|
31
|
+
),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const CustomRenderItem: Story = {
|
|
35
|
+
name: "Custom render item",
|
|
36
|
+
render: () => (
|
|
37
|
+
<div style={{ width: "300px" }}>
|
|
38
|
+
<FormField label="Pick books">
|
|
39
|
+
{({ id }) => (
|
|
40
|
+
<MultiSearchableSelect
|
|
41
|
+
id={id}
|
|
42
|
+
data={books}
|
|
43
|
+
itemToString={itemToString}
|
|
44
|
+
renderItem={renderItem}
|
|
45
|
+
placeholder="Select books"
|
|
46
|
+
searchPlaceholder="Search books..."
|
|
47
|
+
/>
|
|
48
|
+
)}
|
|
49
|
+
</FormField>
|
|
50
|
+
</div>
|
|
51
|
+
),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Grouped: Story = {
|
|
55
|
+
name: "Grouped",
|
|
56
|
+
render: () => (
|
|
57
|
+
<div style={{ width: "300px" }}>
|
|
58
|
+
<FormField label="Pick books">
|
|
59
|
+
{({ id }) => (
|
|
60
|
+
<MultiSearchableSelect
|
|
61
|
+
id={id}
|
|
62
|
+
data={books}
|
|
63
|
+
itemToString={itemToString}
|
|
64
|
+
renderItem={renderItem}
|
|
65
|
+
placeholder="Select books"
|
|
66
|
+
searchPlaceholder="Search books..."
|
|
67
|
+
groupBy={(book) => book.author}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
</FormField>
|
|
71
|
+
</div>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const DefaultSelected: Story = {
|
|
76
|
+
name: "Default selected",
|
|
77
|
+
render: () => (
|
|
78
|
+
<div style={{ width: "300px" }}>
|
|
79
|
+
<FormField label="Pick books">
|
|
80
|
+
{({ id }) => (
|
|
81
|
+
<MultiSearchableSelect
|
|
82
|
+
id={id}
|
|
83
|
+
data={books}
|
|
84
|
+
itemToString={itemToString}
|
|
85
|
+
renderItem={renderItem}
|
|
86
|
+
placeholder="Select books"
|
|
87
|
+
searchPlaceholder="Search books..."
|
|
88
|
+
defaultSelectedItemIds={["book-1", "book-3"]}
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
</FormField>
|
|
92
|
+
</div>
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const Controlled: Story = {
|
|
97
|
+
name: "Controlled",
|
|
98
|
+
render: () => {
|
|
99
|
+
const [selectedIds, setSelectedIds] = React.useState<string[]>(["book-2"]);
|
|
100
|
+
const selected = books.filter((b) => selectedIds.includes(b.id));
|
|
101
|
+
return (
|
|
102
|
+
<div
|
|
103
|
+
style={{
|
|
104
|
+
width: "300px",
|
|
105
|
+
display: "flex",
|
|
106
|
+
flexDirection: "column",
|
|
107
|
+
gap: 16,
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
<FormField label="Pick books">
|
|
111
|
+
{({ id }) => (
|
|
112
|
+
<MultiSearchableSelect
|
|
113
|
+
id={id}
|
|
114
|
+
data={books}
|
|
115
|
+
itemToString={itemToString}
|
|
116
|
+
renderItem={renderItem}
|
|
117
|
+
placeholder="Select books"
|
|
118
|
+
searchPlaceholder="Search books..."
|
|
119
|
+
selectedItemIds={selectedIds}
|
|
120
|
+
onSelectedItemIdsChange={(ids: string[]) => setSelectedIds(ids)}
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
</FormField>
|
|
124
|
+
<div style={{ fontSize: 13 }}>
|
|
125
|
+
Selected:{" "}
|
|
126
|
+
<strong>
|
|
127
|
+
{selected.length > 0
|
|
128
|
+
? selected.map((b) => b.title).join(", ")
|
|
129
|
+
: "none"}
|
|
130
|
+
</strong>
|
|
131
|
+
</div>
|
|
132
|
+
<div style={{ display: "flex", gap: 8 }}>
|
|
133
|
+
<button onClick={() => setSelectedIds([])}>Clear all</button>
|
|
134
|
+
<button onClick={() => setSelectedIds(["book-5", "book-7"])}>
|
|
135
|
+
Select 1984 & Meditations
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const CustomSelection: Story = {
|
|
144
|
+
name: "Custom renderSelection",
|
|
145
|
+
render: () => (
|
|
146
|
+
<div style={{ width: "300px" }}>
|
|
147
|
+
<FormField label="Pick books">
|
|
148
|
+
{({ id }) => (
|
|
149
|
+
<MultiSearchableSelect
|
|
150
|
+
id={id}
|
|
151
|
+
data={books}
|
|
152
|
+
itemToString={itemToString}
|
|
153
|
+
renderItem={renderItem}
|
|
154
|
+
placeholder="Select books"
|
|
155
|
+
searchPlaceholder="Search books..."
|
|
156
|
+
renderSelection={(selected) => {
|
|
157
|
+
if (selected.length === 0) return "Select books";
|
|
158
|
+
const first = itemToString(selected[0]);
|
|
159
|
+
const extra =
|
|
160
|
+
selected.length > 1 ? ` (+${selected.length - 1})` : "";
|
|
161
|
+
return (
|
|
162
|
+
<span style={{ fontSize: 13 }}>
|
|
163
|
+
{first}
|
|
164
|
+
{extra}
|
|
165
|
+
</span>
|
|
166
|
+
);
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
)}
|
|
170
|
+
</FormField>
|
|
171
|
+
</div>
|
|
172
|
+
),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const CustomRenderSelections: Story = {
|
|
176
|
+
name: "Custom customRenderSelections",
|
|
177
|
+
render: () => (
|
|
178
|
+
<div style={{ width: "300px" }}>
|
|
179
|
+
<FormField label="Pick books">
|
|
180
|
+
{({ id }) => (
|
|
181
|
+
<MultiSearchableSelect
|
|
182
|
+
id={id}
|
|
183
|
+
data={books}
|
|
184
|
+
itemToString={itemToString}
|
|
185
|
+
renderItem={renderItem}
|
|
186
|
+
placeholder="Select books"
|
|
187
|
+
searchPlaceholder="Search books..."
|
|
188
|
+
customRenderSelections={(item) => `${item.title} (${item.author})`}
|
|
189
|
+
/>
|
|
190
|
+
)}
|
|
191
|
+
</FormField>
|
|
192
|
+
</div>
|
|
193
|
+
),
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export const SelectableHeadings: Story = {
|
|
197
|
+
name: "Selectable headings",
|
|
198
|
+
render: () => (
|
|
199
|
+
<div style={{ width: "300px" }}>
|
|
200
|
+
<FormField label="Pick books">
|
|
201
|
+
{({ id }) => (
|
|
202
|
+
<MultiSearchableSelect
|
|
203
|
+
id={id}
|
|
204
|
+
data={books}
|
|
205
|
+
itemToString={itemToString}
|
|
206
|
+
renderItem={renderItem}
|
|
207
|
+
placeholder="Select books"
|
|
208
|
+
searchPlaceholder="Search books..."
|
|
209
|
+
groupBy={(book) => book.author}
|
|
210
|
+
selectHeadings
|
|
211
|
+
/>
|
|
212
|
+
)}
|
|
213
|
+
</FormField>
|
|
214
|
+
</div>
|
|
215
|
+
),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const SelectAll: Story = {
|
|
219
|
+
name: "Select all",
|
|
220
|
+
render: () => (
|
|
221
|
+
<div style={{ width: "300px" }}>
|
|
222
|
+
<FormField label="Pick books">
|
|
223
|
+
{({ id }) => (
|
|
224
|
+
<MultiSearchableSelect
|
|
225
|
+
id={id}
|
|
226
|
+
data={books}
|
|
227
|
+
itemToString={itemToString}
|
|
228
|
+
renderItem={renderItem}
|
|
229
|
+
placeholder="Select books"
|
|
230
|
+
searchPlaceholder="Search books..."
|
|
231
|
+
groupBy={(book) => book.author}
|
|
232
|
+
selectHeadings
|
|
233
|
+
selectAll
|
|
234
|
+
/>
|
|
235
|
+
)}
|
|
236
|
+
</FormField>
|
|
237
|
+
</div>
|
|
238
|
+
),
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export const Virtualized: Story = {
|
|
242
|
+
name: "Virtualized",
|
|
243
|
+
render: () => (
|
|
244
|
+
<div style={{ width: "300px" }}>
|
|
245
|
+
<FormField label="Pick books">
|
|
246
|
+
{({ id }) => (
|
|
247
|
+
<MultiSearchableSelect
|
|
248
|
+
id={id}
|
|
249
|
+
data={largeBookSet}
|
|
250
|
+
itemToString={itemToString}
|
|
251
|
+
renderItem={renderItem}
|
|
252
|
+
placeholder="Select books"
|
|
253
|
+
searchPlaceholder="Search books..."
|
|
254
|
+
groupBy={(book) => book.author}
|
|
255
|
+
selectHeadings
|
|
256
|
+
isVirtualized
|
|
257
|
+
/>
|
|
258
|
+
)}
|
|
259
|
+
</FormField>
|
|
260
|
+
</div>
|
|
261
|
+
),
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export const MaxSelections: Story = {
|
|
265
|
+
name: "Max selections",
|
|
266
|
+
render: () => (
|
|
267
|
+
<div style={{ width: "300px" }}>
|
|
268
|
+
<FormField label="Pick books">
|
|
269
|
+
{({ id }) => (
|
|
270
|
+
<MultiSearchableSelect
|
|
271
|
+
id={id}
|
|
272
|
+
data={books}
|
|
273
|
+
itemToString={itemToString}
|
|
274
|
+
renderItem={renderItem}
|
|
275
|
+
placeholder="Select books"
|
|
276
|
+
searchPlaceholder="Search books..."
|
|
277
|
+
defaultSelectedItemIds={[
|
|
278
|
+
"book-1",
|
|
279
|
+
"book-2",
|
|
280
|
+
"book-3",
|
|
281
|
+
"book-4",
|
|
282
|
+
"book-5",
|
|
283
|
+
]}
|
|
284
|
+
maxSelections={2}
|
|
285
|
+
/>
|
|
286
|
+
)}
|
|
287
|
+
</FormField>
|
|
288
|
+
</div>
|
|
289
|
+
),
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
export const RemoveSelectedItems: Story = {
|
|
293
|
+
name: "Remove selected items",
|
|
294
|
+
render: () => (
|
|
295
|
+
<div style={{ width: "300px" }}>
|
|
296
|
+
<FormField label="Pick books">
|
|
297
|
+
{({ id }) => (
|
|
298
|
+
<MultiSearchableSelect
|
|
299
|
+
id={id}
|
|
300
|
+
data={books}
|
|
301
|
+
itemToString={itemToString}
|
|
302
|
+
renderItem={renderItem}
|
|
303
|
+
placeholder="Select books"
|
|
304
|
+
searchPlaceholder="Search books..."
|
|
305
|
+
removeSelectedItems
|
|
306
|
+
/>
|
|
307
|
+
)}
|
|
308
|
+
</FormField>
|
|
309
|
+
</div>
|
|
310
|
+
),
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
export const VirtualizedSelectAll: Story = {
|
|
314
|
+
name: "Virtualized + select all",
|
|
315
|
+
render: () => (
|
|
316
|
+
<div style={{ width: "300px" }}>
|
|
317
|
+
<FormField label="Pick books">
|
|
318
|
+
{({ id }) => (
|
|
319
|
+
<MultiSearchableSelect
|
|
320
|
+
id={id}
|
|
321
|
+
data={largeBookSet}
|
|
322
|
+
itemToString={itemToString}
|
|
323
|
+
renderItem={renderItem}
|
|
324
|
+
placeholder="Select books"
|
|
325
|
+
searchPlaceholder="Search books..."
|
|
326
|
+
groupBy={(book) => book.author}
|
|
327
|
+
selectHeadings
|
|
328
|
+
selectAll
|
|
329
|
+
isVirtualized
|
|
330
|
+
/>
|
|
331
|
+
)}
|
|
332
|
+
</FormField>
|
|
333
|
+
</div>
|
|
334
|
+
),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export const Loading: Story = {
|
|
338
|
+
name: "Loading",
|
|
339
|
+
render: () => (
|
|
340
|
+
<div style={{ width: "300px" }}>
|
|
341
|
+
<FormField label="Pick books">
|
|
342
|
+
{({ id }) => (
|
|
343
|
+
<MultiSearchableSelect
|
|
344
|
+
id={id}
|
|
345
|
+
data={[]}
|
|
346
|
+
itemToString={itemToString}
|
|
347
|
+
placeholder="Select books"
|
|
348
|
+
searchPlaceholder="Search books..."
|
|
349
|
+
isLoading
|
|
350
|
+
/>
|
|
351
|
+
)}
|
|
352
|
+
</FormField>
|
|
353
|
+
</div>
|
|
354
|
+
),
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
export const LoadingWithToggle: Story = {
|
|
358
|
+
name: "Loading (toggle)",
|
|
359
|
+
render: () => {
|
|
360
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
361
|
+
const data = isLoading ? [] : books;
|
|
362
|
+
return (
|
|
363
|
+
<div
|
|
364
|
+
style={{
|
|
365
|
+
width: "300px",
|
|
366
|
+
display: "flex",
|
|
367
|
+
flexDirection: "column",
|
|
368
|
+
gap: 16,
|
|
369
|
+
}}
|
|
370
|
+
>
|
|
371
|
+
<FormField label="Pick books">
|
|
372
|
+
{({ id }) => (
|
|
373
|
+
<MultiSearchableSelect
|
|
374
|
+
id={id}
|
|
375
|
+
data={data}
|
|
376
|
+
itemToString={itemToString}
|
|
377
|
+
placeholder="Select books"
|
|
378
|
+
searchPlaceholder="Search books..."
|
|
379
|
+
isLoading={isLoading}
|
|
380
|
+
/>
|
|
381
|
+
)}
|
|
382
|
+
</FormField>
|
|
383
|
+
<button onClick={() => setIsLoading((v) => !v)}>
|
|
384
|
+
{isLoading ? "Finish loading" : "Set loading"}
|
|
385
|
+
</button>
|
|
386
|
+
</div>
|
|
387
|
+
);
|
|
388
|
+
},
|
|
389
|
+
};
|