Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions semcore/select/src/Select.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ const Select = createComponent(
},
],
Group: Dropdown.Group,
VirtualList: DropdownMenu.VirtualList,
Divider,
InputSearch: [InputSearchWrapper, InputSearch._______childrenComponents],
Input: [InputSearchWrapper, InputSearch._______childrenComponents],
Expand Down
1 change: 1 addition & 0 deletions semcore/select/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ declare const Select: IntergalacticSelectComponent & {
Popper: typeof DropdownMenu.Popper;
List: typeof DropdownMenu.List;
Menu: typeof DropdownMenu.Menu;
VirtualList: typeof DropdownMenu.VirtualList;
Group: typeof Dropdown.Group;
Option: Intergalactic.Component<
'option',
Expand Down
5 changes: 5 additions & 0 deletions stories/components/select/tests/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ProgrammaticallyFocusExample from './examples/programmatically_focus';
import type { defaultProps as SelectWithEllipsisProps } from './examples/select-with-ellipsis';
import SelectWithEllipsisExample from './examples/select-with-ellipsis';
import SubcomponentsExample, { defaultProps as SubcomponentsProps } from './examples/subcomponents_trigger_popper_list_search';
import VirtualizationExample from './examples/virtualization';

const meta: Meta<typeof Select> = {
title: 'Components/Select/Test',
Expand Down Expand Up @@ -217,3 +218,7 @@ export const SubcomponentsTriggerPopperListSearch: StoryObj<typeof Subcomponents
},
args: SubcomponentsProps,
};

export const Virtualization: StoryObj = {
render: VirtualizationExample,
};
93 changes: 93 additions & 0 deletions stories/components/select/tests/examples/virtualization.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { ButtonTrigger } from '@semcore/ui/base-trigger';
import type { RenderRowProps, DropdownMenuProps, DropdownMenuListProps, DropdownMenuItemProps } from '@semcore/ui/dropdown-menu';
import Select, { InputSearch } from '@semcore/ui/select';
import React from 'react';

const projects = Array.from({ length: 5500 }, (_, index) => `project ${index}`);
const rowHeight = 32;

type ProjectSelectorProps = DropdownMenuProps & DropdownMenuListProps & DropdownMenuItemProps & {
disabledAll?: boolean;
disabledFirstItem?: boolean;
visibleItems?: number;
};

const Row = React.memo(({ index, data, row }: RenderRowProps<string, { selected: string | null; setProject: (project: string, index: number) => void; disabledAll?: boolean; disabledFirstItem?: boolean }>) => {
const projectName = row;

return (
<Select.Option
key={row}
disabled={data.disabledAll || (index === 0 && data.disabledFirstItem)}
index={index}
data-test-id={`item-${projectName}`}
value={projectName}
>
{projectName}
</Select.Option>
);
});

const Demo = (props: ProjectSelectorProps) => {
const [searchValue, setSearchValue] = React.useState('');
const [visible, setVisible] = React.useState(false);
const [selectedProject, setProject] = React.useState<string>('project 33');

const visibleItems = props.visibleItems ?? 10;
const listHeight = visibleItems * rowHeight;

const normalizedQuery = searchValue.trim().toLowerCase();

const filteredProjects = React.useMemo(() => {
if (!normalizedQuery) return projects;
return projects.filter((p) => p.toLowerCase().includes(normalizedQuery));
}, [normalizedQuery]);

const handleSetProject = (project: string) => {
setProject(project);
};

return (
<Select
stretch={props.stretch}
itemsCount={filteredProjects.length}
visible={visible}
onVisibleChange={setVisible}
value={selectedProject}
onChange={handleSetProject}
>
<Select.Trigger tag={ButtonTrigger} w={220}>
{selectedProject ?? 'Select project'}
</Select.Trigger>

<Select.Popper aria-label='Select project popover'>
<InputSearch value={searchValue} onChange={setSearchValue} m={1} autoFocus={false} />

<Select.VirtualList
key={filteredProjects.length === projects.length ? 'virtual-list-stable-key' : normalizedQuery}
hMax={listHeight + 41}
rowHeight={rowHeight}
renderRow={Row}
rows={filteredProjects}
customData={{
setProject: handleSetProject,
selected: selectedProject,
disabledAll: props.disabledAll,
disabledFirstItem: props.disabledFirstItem,
}}
/>
</Select.Popper>
</Select>
);
};

export const defaultProps: ProjectSelectorProps = {
disabledAll: false,
disabledFirstItem: false,
stretch: undefined,
visibleItems: 4,
};

Demo.defaultProps = defaultProps;

export default Demo;
Loading