@sproutsocial/seeds-react-menu 1.17.0 → 1.18.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.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +14 -22
  2. package/CHANGELOG.md +30 -0
  3. package/dist/esm/index.js +542 -229
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/v3/index.js +793 -1038
  6. package/dist/esm/v3/index.js.map +1 -1
  7. package/dist/v3/index.d.mts +64 -38
  8. package/dist/v3/index.d.ts +64 -38
  9. package/dist/v3/index.js +813 -1058
  10. package/dist/v3/index.js.map +1 -1
  11. package/dist/v3/menu.css +947 -0
  12. package/package.json +20 -21
  13. package/src/v3/ActionMenuStyles.tsx +169 -149
  14. package/src/v3/Common/ComboboxStyles.tsx +396 -511
  15. package/src/v3/Common/SelectIcons.tsx +17 -8
  16. package/src/v3/Common/SelectItem.tsx +25 -38
  17. package/src/v3/Common/SelectStyles.tsx +146 -144
  18. package/src/v3/Common/cn.ts +38 -0
  19. package/src/v3/MenuButtonStyles.tsx +55 -34
  20. package/src/v3/MultiSearchableSelect.tsx +8 -8
  21. package/src/v3/MultiSelect.tsx +11 -16
  22. package/src/v3/SingleSelect.tsx +13 -13
  23. package/src/v3/menu.css +947 -0
  24. package/tsup.config.ts +0 -1
  25. package/dist/esm/chunk-ROQATIB7.js +0 -357
  26. package/dist/esm/chunk-ROQATIB7.js.map +0 -1
  27. package/dist/esm/v2/index.js +0 -2089
  28. package/dist/esm/v2/index.js.map +0 -1
  29. package/dist/v2/index.d.mts +0 -108
  30. package/dist/v2/index.d.ts +0 -108
  31. package/dist/v2/index.js +0 -2280
  32. package/dist/v2/index.js.map +0 -1
  33. package/src/v2/Autocomplete/Autocomplete.stories.tsx +0 -645
  34. package/src/v2/Autocomplete/MultiAutocomplete.tsx +0 -487
  35. package/src/v2/Autocomplete/SingleAutocomplete.tsx +0 -366
  36. package/src/v2/Autocomplete/index.ts +0 -2
  37. package/src/v2/Common-V2/ComboboxContent.tsx +0 -457
  38. package/src/v2/Common-V2/MenuGroup.tsx +0 -60
  39. package/src/v2/Common-V2/MenuGroupTypes.ts +0 -30
  40. package/src/v2/Common-V2/MenuHeading.tsx +0 -83
  41. package/src/v2/Common-V2/MenuItem.tsx +0 -138
  42. package/src/v2/Common-V2/Popout.tsx +0 -124
  43. package/src/v2/Common-V2/PopoutTypes.tsx +0 -109
  44. package/src/v2/Common-V2/SelectToggleButton.tsx +0 -99
  45. package/src/v2/Common-V2/index.ts +0 -11
  46. package/src/v2/Common-V2/props.ts +0 -84
  47. package/src/v2/Common-V2/styles.tsx +0 -41
  48. package/src/v2/Common-V2/types.ts +0 -16
  49. package/src/v2/Common-V2/useHighlightedIndex.ts +0 -62
  50. package/src/v2/Common-V2/utils.ts +0 -238
  51. package/src/v2/SearchableSelect/AutocompleteContent.tsx +0 -325
  52. package/src/v2/SearchableSelect/SearchableMultiSelect.tsx +0 -527
  53. package/src/v2/SearchableSelect/SearchableSelect.tsx +0 -395
  54. package/src/v2/SearchableSelect/index.ts +0 -12
  55. package/src/v2/Select/MultiSelect.tsx +0 -417
  56. package/src/v2/Select/Select.stories.tsx +0 -593
  57. package/src/v2/Select/SingleSelect.tsx +0 -285
  58. package/src/v2/Select/index.ts +0 -2
  59. package/src/v2/index.ts +0 -2
@@ -1,645 +0,0 @@
1
- /* eslint-disable react-hooks/rules-of-hooks */
2
- import React from "react";
3
- import type { Meta, StoryObj } from "@storybook/react";
4
- import { action } from "@storybook/addon-actions";
5
-
6
- import { Box } from "@sproutsocial/seeds-react-box";
7
- import { FormField } from "@sproutsocial/seeds-react-form-field";
8
- import { Icon } from "@sproutsocial/seeds-react-icon";
9
-
10
- import { MultiAutocomplete } from "./MultiAutocomplete";
11
- import { SingleAutocomplete } from "./SingleAutocomplete";
12
- import Button from "@sproutsocial/seeds-react-button";
13
- import type { ToggleItem, GroupHeading } from "../Common-V2";
14
-
15
- type CustomAutocompleteArgs = React.ComponentProps<typeof MultiAutocomplete>;
16
-
17
- const meta: Meta<CustomAutocompleteArgs> = {
18
- title: "Components/Menus/v2/Autocomplete",
19
- component: MultiAutocomplete,
20
- parameters: {
21
- controls: {
22
- expanded: true,
23
- },
24
- },
25
- decorators: [
26
- (Story) => (
27
- <Box display="flex" flexDirection="column" gap="16px" maxWidth={350}>
28
- <Story />
29
- </Box>
30
- ),
31
- ],
32
- };
33
-
34
- export default meta;
35
-
36
- type Story = StoryObj<CustomAutocompleteArgs>;
37
-
38
- interface Book {
39
- id: string;
40
- author: string;
41
- title: string;
42
- }
43
-
44
- const books: Book[] = [
45
- { id: "book-1", author: "Harper Lee", title: "To Kill a Mockingbird" },
46
- { id: "book-2", author: "Lev Tolstoy", title: "War and Peace" },
47
- { id: "book-3", author: "Fyodor Dostoyevsy", title: "The Idiot" },
48
- { id: "book-4", author: "Oscar Wilde", title: "A Picture of Dorian Gray" },
49
- { id: "book-5", author: "George Orwell", title: "1984" },
50
- { id: "book-6", author: "Jane Austen", title: "Pride and Prejudice" },
51
- { id: "book-7", author: "Marcus Aurelius", title: "Meditations" },
52
- {
53
- id: "book-8",
54
- author: "Fyodor Dostoevsky",
55
- title: "The Brothers Karamazov",
56
- },
57
- { id: "book-9", author: "Lev Tolstoy", title: "Anna Karenina" },
58
- { id: "book-10", author: "Fyodor Dostoevsky", title: "Crime and Punishment" },
59
- ];
60
-
61
- // Generate 1000 books for virtualization testing
62
- const generateBooks = (count: number): Book[] => {
63
- const authors = [
64
- "Harper Lee",
65
- "Lev Tolstoy",
66
- "Fyodor Dostoevsky",
67
- "Oscar Wilde",
68
- "George Orwell",
69
- "Jane Austen",
70
- "Marcus Aurelius",
71
- "Ernest Hemingway",
72
- "Virginia Woolf",
73
- "James Joyce",
74
- "Charles Dickens",
75
- "Mark Twain",
76
- "J.R.R. Tolkien",
77
- "C.S. Lewis",
78
- "Agatha Christie",
79
- "Arthur Conan Doyle",
80
- "J.K. Rowling",
81
- "Stephen King",
82
- "Isaac Asimov",
83
- "Ray Bradbury",
84
- "Aldous Huxley",
85
- "Kurt Vonnegut",
86
- "J.D. Salinger",
87
- "Maya Angelou",
88
- "Toni Morrison",
89
- "Gabriel García Márquez",
90
- "Milan Kundera",
91
- "Umberto Eco",
92
- "Haruki Murakami",
93
- "Chimamanda Ngozi Adichie",
94
- ];
95
-
96
- const titleWords = [
97
- "Adventures",
98
- "Chronicles",
99
- "Memories",
100
- "Journey",
101
- "Quest",
102
- "Tales",
103
- "Stories",
104
- "Legends",
105
- "Myths",
106
- "Dreams",
107
- "Visions",
108
- "Revelations",
109
- "Secrets",
110
- "Mysteries",
111
- "Shadows",
112
- "Light",
113
- "Darkness",
114
- "Echoes",
115
- "Whispers",
116
- "Voices",
117
- "Songs",
118
- "Poems",
119
- ];
120
-
121
- const books: Book[] = [];
122
- for (let i = 0; i < count; i++) {
123
- const author = authors[i % authors.length];
124
- const titleWord1 =
125
- titleWords[Math.floor(Math.random() * titleWords.length)];
126
- const titleWord2 =
127
- titleWords[Math.floor(Math.random() * titleWords.length)];
128
- const number = Math.floor(i / authors.length) + 1;
129
- const title = `${titleWord1} of ${titleWord2} ${number}`;
130
-
131
- books.push({
132
- id: `book-${i + 1}`,
133
- author,
134
- title,
135
- });
136
- }
137
-
138
- return books;
139
- };
140
- const largeBookSet = generateBooks(100);
141
-
142
- const renderItem = (item: Book) => (
143
- <>
144
- <span style={{ fontWeight: "bold" }}>{item.title}</span>
145
- {item.author && (
146
- <span style={{ fontSize: "12px", color: "#666", marginLeft: "8px" }}>
147
- by {item.author}
148
- </span>
149
- )}
150
- </>
151
- );
152
-
153
- const itemToString = (item: Book | null): string => {
154
- if (!item) return "";
155
- return item.title;
156
- };
157
-
158
- const itemToSearchString = (item: Book) => `${item.author}|${item.title}`;
159
-
160
- const defaultEmptyState = (
161
- <div style={{ padding: "16px", textAlign: "center", color: "#666" }}>
162
- No books found
163
- </div>
164
- );
165
-
166
- // 1. Single
167
- export const Single: Story = {
168
- name: "Single",
169
- render: () => {
170
- return (
171
- <FormField label="Pick a book">
172
- {({ id }) => (
173
- <SingleAutocomplete
174
- id={id}
175
- data={books}
176
- itemToString={itemToString}
177
- renderItem={renderItem}
178
- onSelectedItemIdChange={(itemId) => console.log(itemId)}
179
- EmptyState={defaultEmptyState}
180
- placeholder="Search for a book"
181
- />
182
- )}
183
- </FormField>
184
- );
185
- },
186
- };
187
-
188
- // 3. Multi
189
- export const Multi: Story = {
190
- name: "Multi",
191
- render: () => {
192
- return (
193
- <div>
194
- <FormField label="Pick some books">
195
- {({ id }) => (
196
- <MultiAutocomplete
197
- id={id}
198
- data={books}
199
- itemToString={itemToString}
200
- itemToSearchString={itemToSearchString}
201
- renderItem={renderItem}
202
- onSelectedItemIdsChange={(itemIds) => console.log(itemIds)}
203
- EmptyState={defaultEmptyState}
204
- showSelectedItems={true}
205
- placeholder="Search for books"
206
- />
207
- )}
208
- </FormField>
209
- <Button
210
- style={{ marginTop: "16px" }}
211
- onClick={action("Button Clicked")}
212
- >
213
- Example Button
214
- </Button>
215
- </div>
216
- );
217
- },
218
- };
219
-
220
- // 4. Multi Grouped by Author
221
- const TestMultiGroupedByAuthor = () => {
222
- return (
223
- <FormField label="Pick some books">
224
- {({ id }) => (
225
- <MultiAutocomplete
226
- id={id}
227
- data={books}
228
- itemToString={itemToString}
229
- itemToSearchString={itemToSearchString}
230
- renderItem={renderItem}
231
- groupBy={(book) => book.author}
232
- EmptyState={defaultEmptyState}
233
- placeholder="Search for books"
234
- />
235
- )}
236
- </FormField>
237
- );
238
- };
239
-
240
- export const MultiGroupedByAuthor: Story = {
241
- name: "Multi Grouped by Author",
242
- render: () => {
243
- return <TestMultiGroupedByAuthor />;
244
- },
245
- };
246
-
247
- // 5. Multi Grouped by First Letter
248
- const TestMultiGroupedByFirstLetter = () => {
249
- return (
250
- <FormField label="Pick some books">
251
- {({ id }) => (
252
- <MultiAutocomplete
253
- id={id}
254
- data={books}
255
- itemToString={itemToString}
256
- itemToSearchString={itemToSearchString}
257
- renderItem={renderItem}
258
- groupBy={(book) => book.title.charAt(0).toUpperCase()}
259
- EmptyState={defaultEmptyState}
260
- placeholder="Search for books"
261
- />
262
- )}
263
- </FormField>
264
- );
265
- };
266
-
267
- export const MultiGroupedByFirstLetter: Story = {
268
- name: "Multi Grouped by First Letter",
269
- render: () => {
270
- return <TestMultiGroupedByFirstLetter />;
271
- },
272
- };
273
-
274
- // 6. Multi Grouped by Author with Custom Heading
275
- const TestMultiGroupedByAuthorWithCustomHeading = () => {
276
- return (
277
- <FormField label="Pick some books">
278
- {({ id }) => (
279
- <MultiAutocomplete
280
- id={id}
281
- data={books}
282
- itemToString={itemToString}
283
- itemToSearchString={itemToSearchString}
284
- renderItem={renderItem}
285
- groupBy={(book) => book.author}
286
- renderGroupHeading={(label) => `📚 ${label.toUpperCase()}`}
287
- EmptyState={defaultEmptyState}
288
- placeholder="Search for books"
289
- />
290
- )}
291
- </FormField>
292
- );
293
- };
294
-
295
- export const MultiGroupedByAuthorWithCustomHeading: Story = {
296
- name: "Multi Grouped by Author with Custom Heading",
297
- render: () => {
298
- return <TestMultiGroupedByAuthorWithCustomHeading />;
299
- },
300
- };
301
-
302
- // 7. Multi with Select All
303
- const selectAllItem: ToggleItem = {
304
- id: "select-all",
305
- label: "Select All",
306
- };
307
-
308
- const TestMultiWithSelectAll = () => {
309
- return (
310
- <FormField label="Pick some books">
311
- {({ id }) => (
312
- <MultiAutocomplete
313
- id={id}
314
- data={books}
315
- itemToString={itemToString}
316
- itemToSearchString={itemToSearchString}
317
- renderItem={renderItem}
318
- selectAllItem={selectAllItem}
319
- onSelectedItemIdsChange={(itemIds) => console.log(itemIds)}
320
- EmptyState={defaultEmptyState}
321
- showSelectedItems={true}
322
- placeholder="Search for books"
323
- />
324
- )}
325
- </FormField>
326
- );
327
- };
328
-
329
- export const MultiWithSelectAll: Story = {
330
- name: "Multi with Select All",
331
- render: () => {
332
- return <TestMultiWithSelectAll />;
333
- },
334
- };
335
-
336
- // 8. Virtualized Multi (with 1000 items)
337
- const TestVirtualizedMulti = () => {
338
- return (
339
- <FormField label="Pick some books">
340
- {({ id }) => (
341
- <MultiAutocomplete
342
- id={id}
343
- data={largeBookSet}
344
- itemToString={itemToString}
345
- itemToSearchString={itemToSearchString}
346
- renderItem={renderItem}
347
- isVirtualized={true}
348
- groupBy={(book) => book.author}
349
- selectHeadings={true}
350
- EmptyState={defaultEmptyState}
351
- placeholder="Search for books"
352
- menuProps={{
353
- style: {
354
- maxHeight: "400px",
355
- overflow: "auto",
356
- },
357
- }}
358
- />
359
- )}
360
- </FormField>
361
- );
362
- };
363
-
364
- export const VirtualizedMulti: Story = {
365
- name: "Virtualized Multi (with 1000 items)",
366
- render: () => {
367
- return <TestVirtualizedMulti />;
368
- },
369
- };
370
-
371
- // 9. Virtualized Multi (with 1000 items), With Select All, Grouped by Author, with Selectable Headings
372
- const TestVirtualizedMultiWithSelectAllGrouped = () => {
373
- return (
374
- <FormField label="Pick some books">
375
- {({ id }) => (
376
- <MultiAutocomplete
377
- id={id}
378
- data={largeBookSet}
379
- itemToString={itemToString}
380
- itemToSearchString={itemToSearchString}
381
- renderItem={renderItem}
382
- groupBy={(book) => book.author}
383
- selectAllItem={selectAllItem}
384
- selectHeadings={true}
385
- isVirtualized={true}
386
- renderGroupHeading={(label) => `📚 ${label.toUpperCase()}`}
387
- onSelectedItemIdsChange={(itemIds) => console.log(itemIds)}
388
- EmptyState={defaultEmptyState}
389
- showSelectedItems={true}
390
- placeholder="Search for books"
391
- menuProps={{
392
- style: {
393
- maxHeight: "400px",
394
- overflow: "auto",
395
- },
396
- }}
397
- />
398
- )}
399
- </FormField>
400
- );
401
- };
402
-
403
- export const VirtualizedMultiWithSelectAllGrouped: Story = {
404
- name: "Virtualized Multi (with 1000 items), With Select All, Grouped by Author, with Selectable Headings",
405
- render: () => {
406
- return <TestVirtualizedMultiWithSelectAllGrouped />;
407
- },
408
- };
409
-
410
- // 10. FullyControlled Single
411
- const TestFullyControlledSingle = () => {
412
- const [inputValue, setInputValue] = React.useState("");
413
- const [selectedItemId, setSelectedItemId] = React.useState<string | null>(
414
- books[0].id
415
- );
416
- const [isOpen, setIsOpen] = React.useState(false);
417
-
418
- const selectedItem = books.find((book) => book.id === selectedItemId) ?? null;
419
-
420
- return (
421
- <>
422
- <FormField label="Pick a book">
423
- {({ id }) => (
424
- <SingleAutocomplete
425
- id={id}
426
- data={books}
427
- inputValue={inputValue}
428
- itemToString={itemToString}
429
- itemToSearchString={itemToSearchString}
430
- onInputValueChange={(newInputValue) => {
431
- console.log("Input value changed:", newInputValue);
432
- setInputValue(newInputValue);
433
- }}
434
- selectedItemId={selectedItemId}
435
- onSelectedItemIdChange={(newItemId) => {
436
- console.log("Selected item changed:", newItemId);
437
- setSelectedItemId(newItemId);
438
- }}
439
- isOpen={isOpen}
440
- onIsOpenChange={(newIsOpen) => {
441
- console.log("isOpen changed:", newIsOpen);
442
- setIsOpen(newIsOpen);
443
- }}
444
- renderItem={renderItem}
445
- EmptyState={defaultEmptyState}
446
- placeholder="Search for a book"
447
- />
448
- )}
449
- </FormField>
450
- <div
451
- style={{
452
- marginTop: "16px",
453
- display: "flex",
454
- flexDirection: "column",
455
- gap: "8px",
456
- }}
457
- >
458
- <div>
459
- <Button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</Button>
460
- <Button
461
- onClick={() => setInputValue("War")}
462
- style={{ marginLeft: "8px" }}
463
- >
464
- Set Input to "War"
465
- </Button>
466
- <Button
467
- onClick={() => {
468
- const randomBook =
469
- books[Math.floor(Math.random() * books.length)];
470
- setSelectedItemId(randomBook.id);
471
- }}
472
- style={{ marginLeft: "8px" }}
473
- >
474
- Select Random Book
475
- </Button>
476
- </div>
477
- <div style={{ fontSize: "12px", color: "#666" }}>
478
- Input: "{inputValue}" | Menu: {isOpen ? "OPEN" : "CLOSED"} | Selected:{" "}
479
- {selectedItem ? selectedItem.title : "None"}
480
- </div>
481
- </div>
482
- </>
483
- );
484
- };
485
-
486
- export const FullyControlledSingle: Story = {
487
- name: "FullyControlled Single",
488
- render: () => {
489
- return <TestFullyControlledSingle />;
490
- },
491
- };
492
-
493
- // 11. FullyControlled Multi
494
- const TestFullyControlledMulti = () => {
495
- const [inputValue, setInputValue] = React.useState("");
496
- const [selectedItemIds, setSelectedItemIds] = React.useState<string[]>([
497
- books[0].id,
498
- ]);
499
- const [isOpen, setIsOpen] = React.useState(false);
500
-
501
- const selectedItems = books.filter((book) =>
502
- selectedItemIds.includes(book.id)
503
- );
504
-
505
- return (
506
- <>
507
- <FormField label="Pick some books">
508
- {({ id }) => (
509
- <MultiAutocomplete
510
- id={id}
511
- data={books}
512
- inputValue={inputValue}
513
- itemToString={itemToString}
514
- itemToSearchString={itemToSearchString}
515
- onInputValueChange={(newInputValue) => {
516
- console.log("Input value changed:", newInputValue);
517
- setInputValue(newInputValue);
518
- }}
519
- selectedItemIds={selectedItemIds}
520
- onSelectedItemIdsChange={(newItemIds) => {
521
- console.log("Selected items changed:", newItemIds);
522
- setSelectedItemIds(newItemIds);
523
- }}
524
- isOpen={isOpen}
525
- onIsOpenChange={(newIsOpen) => {
526
- console.log("isOpen changed:", newIsOpen);
527
- setIsOpen(newIsOpen);
528
- }}
529
- renderItem={renderItem}
530
- EmptyState={defaultEmptyState}
531
- placeholder="Search for books"
532
- />
533
- )}
534
- </FormField>
535
- <div
536
- style={{
537
- marginTop: "16px",
538
- display: "flex",
539
- flexDirection: "column",
540
- gap: "8px",
541
- }}
542
- >
543
- <div>
544
- <Button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</Button>
545
- <Button
546
- onClick={() => setInputValue("War")}
547
- style={{ marginLeft: "8px" }}
548
- >
549
- Set Input to "War"
550
- </Button>
551
- <Button
552
- onClick={() => {
553
- const randomBook =
554
- books[Math.floor(Math.random() * books.length)];
555
- setSelectedItemIds((prev) => {
556
- if (!prev.includes(randomBook.id)) {
557
- return [...prev, randomBook.id];
558
- }
559
- return prev;
560
- });
561
- }}
562
- style={{ marginLeft: "8px" }}
563
- >
564
- Add Random Book
565
- </Button>
566
- </div>
567
- <div style={{ fontSize: "12px", color: "#666" }}>
568
- Input: "{inputValue}" | Menu: {isOpen ? "OPEN" : "CLOSED"} | Selected:{" "}
569
- {selectedItems.map((item) => item.title).join(", ") || "None"}
570
- </div>
571
- </div>
572
- </>
573
- );
574
- };
575
-
576
- export const FullyControlledMulti: Story = {
577
- name: "FullyControlled Multi",
578
- render: () => {
579
- return <TestFullyControlledMulti />;
580
- },
581
- };
582
-
583
- // Single Custom Search string
584
- const TestCustomSearchString = () => {
585
- const customItemToSearchString = (item: Book) =>
586
- `${item.author}|${item.title}`;
587
-
588
- return (
589
- <FormField label="Pick a book">
590
- {({ id }) => (
591
- <SingleAutocomplete
592
- id={id}
593
- data={books}
594
- itemToString={itemToString}
595
- itemToSearchString={customItemToSearchString}
596
- renderItem={renderItem}
597
- onSelectedItemIdChange={(itemId) => console.log(itemId)}
598
- EmptyState={defaultEmptyState}
599
- placeholder="Search for a book"
600
- />
601
- )}
602
- </FormField>
603
- );
604
- };
605
-
606
- export const CustomSearchString: Story = {
607
- name: "Custom Search String",
608
- render: () => {
609
- return <TestCustomSearchString />;
610
- },
611
- };
612
-
613
- // Popout Placement Top
614
- // Positioned at the bottom of the viewport to demonstrate that the popout
615
- // opens upward rather than downward when popoutPlacement="top" is set.
616
- const TestPopoutPlacementTop = () => {
617
- return (
618
- <FormField label="Pick some books" width="100%">
619
- {({ id }) => (
620
- <MultiAutocomplete
621
- id={id}
622
- data={books}
623
- itemToString={itemToString}
624
- renderItem={renderItem}
625
- onSelectedItemIdsChange={(itemIds) => console.log(itemIds)}
626
- EmptyState={defaultEmptyState}
627
- placeholder="Search for books"
628
- popoutPlacement="top"
629
- />
630
- )}
631
- </FormField>
632
- );
633
- };
634
-
635
- export const PopoutPlacementTop: Story = {
636
- name: "Popout Placement Top",
637
- decorators: [
638
- (Story) => (
639
- <Box display="flex" height="100vh" alignItems="flex-end">
640
- <Story />
641
- </Box>
642
- ),
643
- ],
644
- render: () => <TestPopoutPlacementTop />,
645
- };