1
- import React , { useState , useMemo } from 'react'
1
+ import React , { useState , useMemo , useRef , useEffect } from 'react'
2
2
import type { Meta } from '@storybook/react-vite'
3
3
import { Button } from '../Button'
4
4
import type { ItemInput } from '../deprecated/ActionList/List'
@@ -10,6 +10,8 @@ import FormControl from '../FormControl'
10
10
import { Stack } from '../Stack'
11
11
import { Dialog } from '../experimental'
12
12
import styles from './SelectPanel.examples.stories.module.css'
13
+ import Checkbox from '../Checkbox'
14
+ import Label from '../Label'
13
15
14
16
const meta : Meta < typeof SelectPanel > = {
15
17
title : 'Components/SelectPanel/Examples' ,
@@ -474,3 +476,115 @@ export const WithDefaultMessage = () => {
474
476
</ FormControl >
475
477
)
476
478
}
479
+
480
+ const NUMBER_OF_ITEMS = 500
481
+ const lotsOfItems = Array . from ( { length : NUMBER_OF_ITEMS } , ( _ , index ) => {
482
+ return {
483
+ id : index ,
484
+ text : `Item ${ index } ` ,
485
+ description : `Description ${ index } ` ,
486
+ leadingVisual : getColorCircle ( '#a2eeef' ) ,
487
+ }
488
+ } )
489
+
490
+ export const RenderMoreOnScroll = ( ) => {
491
+ const [ selected , setSelected ] = useState < ItemInput [ ] > ( [ ] )
492
+ const [ open , setOpen ] = useState ( false )
493
+ const [ renderSubset , setRenderSubset ] = React . useState ( true )
494
+
495
+ const [ filter , setFilter ] = useState ( '' )
496
+ const filteredItems = lotsOfItems . filter ( item => item . text . toLowerCase ( ) . startsWith ( filter . toLowerCase ( ) ) )
497
+
498
+ const [ numberOfItemsInSubset , setNumberOfItemsInSubset ] = useState ( 50 )
499
+ const subsetOfFiltereredItemsToRender = filteredItems . slice ( 0 , renderSubset ? numberOfItemsInSubset : NUMBER_OF_ITEMS )
500
+
501
+ useEffect ( function loadMoreItemsOnScrollEnd ( ) {
502
+ const scrollContainer = document . querySelector ( '#select-labels-panel-dialog [role="listbox"]' ) ?. parentElement
503
+
504
+ const handler = ( event : Event ) => {
505
+ const container = event . target as HTMLElement
506
+ if ( container . scrollTop === container . scrollHeight - container . offsetHeight ) {
507
+ // has scrolled to bottom
508
+ setNumberOfItemsInSubset ( numberOfItemsInSubset + 50 )
509
+ }
510
+ }
511
+
512
+ // eslint-disable-next-line github/prefer-observers
513
+ if ( scrollContainer ) scrollContainer . addEventListener ( 'scroll' , handler )
514
+ return ( ) => scrollContainer ?. removeEventListener ( 'scroll' , handler )
515
+ } )
516
+
517
+ /* perf measurement logic start */
518
+ const timeBeforeOpen = useRef < number > ( )
519
+ const timeAfterOpen = useRef < number > ( )
520
+ const [ timeTakenToOpen , setTimeTakenToOpen ] = useState < number > ( )
521
+
522
+ const onOpenChange = ( ) => {
523
+ if ( ! open ) timeBeforeOpen . current = performance . now ( )
524
+ setOpen ( ! open )
525
+ }
526
+
527
+ useEffect (
528
+ function measureTimeAfterOpen ( ) {
529
+ if ( open ) {
530
+ timeAfterOpen . current = performance . now ( )
531
+ if ( timeBeforeOpen . current ) setTimeTakenToOpen ( timeAfterOpen . current - timeBeforeOpen . current )
532
+ }
533
+ } ,
534
+ [ open ] ,
535
+ )
536
+ /* end */
537
+
538
+ return (
539
+ < form >
540
+ < FormControl >
541
+ < FormControl . Label > Render subset of items on initial open</ FormControl . Label >
542
+ < FormControl . Caption >
543
+ { renderSubset ? 'Loads more items on scroll' : `Loads all ${ NUMBER_OF_ITEMS } items at once` }
544
+ </ FormControl . Caption >
545
+ < Checkbox
546
+ checked = { renderSubset }
547
+ onChange = { ( ) => {
548
+ setRenderSubset ( ! renderSubset )
549
+ setTimeTakenToOpen ( undefined )
550
+ setNumberOfItemsInSubset ( 50 )
551
+ } }
552
+ />
553
+ </ FormControl >
554
+ < p >
555
+ Time taken (ms) to render initial { renderSubset ? 50 : NUMBER_OF_ITEMS } items:{ ' ' }
556
+ { timeTakenToOpen ? < Label > { timeTakenToOpen . toFixed ( 2 ) } ms</ Label > : '(click "Select Labels" to open)' }
557
+ </ p >
558
+ < p >
559
+ Known bug: Scroll resets to top when the items change. Works well with feature flag{ ' ' }
560
+ < Label > primer_react_select_panel_remove_active_descendant</ Label >
561
+ </ p >
562
+
563
+ < FormControl >
564
+ < FormControl . Label > Labels</ FormControl . Label >
565
+ < SelectPanel
566
+ title = "Select labels"
567
+ placeholder = "Select labels"
568
+ subtitle = "Use labels to organize issues and pull requests"
569
+ renderAnchor = { ( { children, ...anchorProps } ) => (
570
+ < Button trailingAction = { TriangleDownIcon } { ...anchorProps } aria-haspopup = "dialog" >
571
+ { children }
572
+ </ Button >
573
+ ) }
574
+ open = { open }
575
+ onOpenChange = { onOpenChange }
576
+ items = { subsetOfFiltereredItemsToRender }
577
+ selected = { selected }
578
+ onSelectedChange = { setSelected }
579
+ onFilterChange = { setFilter }
580
+ width = "medium"
581
+ height = "large"
582
+ message = { filteredItems . length === 0 ? NoResultsMessage ( filter ) : undefined }
583
+ overlayProps = { {
584
+ id : 'select-labels-panel-dialog' ,
585
+ } }
586
+ />
587
+ </ FormControl >
588
+ </ form >
589
+ )
590
+ }
0 commit comments