diff --git a/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx b/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx index 1f48099e13..bafab6a9bb 100644 --- a/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx +++ b/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, Checkbox, styled, TextField } from '@mui/material'; +import { Checkbox, styled, TextField } from '@mui/material'; import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import type { IUser } from 'interfaces/user'; @@ -9,7 +9,7 @@ import { UG_USERS_ID } from 'utils/testIds'; import { caseInsensitiveSearch } from 'utils/search'; import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts'; import type { IServiceAccount } from 'interfaces/service-account'; -import AutocompleteVirtual from './AutcompleteVirtual'; +import AutocompleteVirtual from 'component/common/AutocompleteVirtual/AutcompleteVirtual'; const StyledOption = styled('div')(({ theme }) => ({ display: 'flex', @@ -110,91 +110,45 @@ export const GroupFormUsersSelect: VFC = ({ return ( - {isLargeList ? ( - { - if ( - event.type === 'keydown' && - (event as React.KeyboardEvent).key === - 'Backspace' && - reason === 'removeOption' - ) { - return; - } - setUsers(newValue); - }} - options={options} - renderOption={renderOption} - filterOptions={(options, { inputValue }) => - options.filter( - ({ name, username, email }) => - caseInsensitiveSearch(inputValue, email) || - caseInsensitiveSearch(inputValue, name) || - caseInsensitiveSearch(inputValue, username), - ) + { + if ( + event.type === 'keydown' && + (event as React.KeyboardEvent).key === 'Backspace' && + reason === 'removeOption' + ) { + return; } - isOptionEqualToValue={(option, value) => - option.id === value.id - } - getOptionLabel={(option: UserOption) => - option.email || option.name || option.username || '' - } - renderInput={(params) => ( - - )} - renderTags={(value) => renderTags(value)} - /> - ) : ( - { - if ( - event.type === 'keydown' && - (event as React.KeyboardEvent).key === - 'Backspace' && - reason === 'removeOption' - ) { - return; - } - setUsers(newValue); - }} - groupBy={(option) => option.type} - options={options} - renderOption={renderOption} - filterOptions={(options, { inputValue }) => - options.filter( - ({ name, username, email }) => - caseInsensitiveSearch(inputValue, email) || - caseInsensitiveSearch(inputValue, name) || - caseInsensitiveSearch(inputValue, username), - ) - } - isOptionEqualToValue={(option, value) => - option.id === value.id - } - getOptionLabel={(option: UserOption) => - option.email || option.name || option.username || '' - } - renderInput={(params) => ( - - )} - renderTags={(value) => renderTags(value)} - noOptionsText={isLoading ? 'Loading…' : 'No options'} - /> - )} + setUsers(newValue); + }} + options={options} + groupBy={(option) => option.type} + renderOption={renderOption} + filterOptions={(options, { inputValue }) => + options.filter( + ({ name, username, email }) => + caseInsensitiveSearch(inputValue, email) || + caseInsensitiveSearch(inputValue, name) || + caseInsensitiveSearch(inputValue, username), + ) + } + isOptionEqualToValue={(option, value) => option.id === value.id} + getOptionLabel={(option: UserOption) => + option.email || option.name || option.username || '' + } + renderInput={(params) => ( + + )} + renderTags={(value) => renderTags(value)} + noOptionsText={isLoading ? 'Loading…' : 'No options'} + /> ); }; diff --git a/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/AutcompleteVirtual.tsx b/frontend/src/component/common/AutocompleteVirtual/AutcompleteVirtual.tsx similarity index 71% rename from frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/AutcompleteVirtual.tsx rename to frontend/src/component/common/AutocompleteVirtual/AutcompleteVirtual.tsx index 770aeb1b04..24de41bd86 100644 --- a/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/AutcompleteVirtual.tsx +++ b/frontend/src/component/common/AutocompleteVirtual/AutcompleteVirtual.tsx @@ -61,24 +61,38 @@ const ListboxComponent = forwardRef< ); }); -type TProps = Omit< +type AutocompleteVirtualProps = Omit< AutocompleteProps, - 'autoHighlight' | 'disableListWrap' | 'ListboxComponent' | 'groupBy' ->; + 'disableListWrap' | 'ListboxComponent' +> & { + virtualThreshold?: number; +}; +// This component has a default threshold of 250 when virtualization kicks in +// When virtualization is enabled we skip groupBy function AutocompleteVirtual( - props: TProps, + props: AutocompleteVirtualProps, ) { - const { getOptionLabel, className, ...restAutocompleteProps } = props; + const { + virtualThreshold = 250, + getOptionLabel, + className, + ...restAutocompleteProps + } = props; - return ( - - ); + const isLargeList = props.options.length > virtualThreshold; + + const autocompleteProps = { + ...restAutocompleteProps, + getOptionLabel, + disableListWrap: true, + ...(isLargeList && { + ListboxComponent: ListboxComponent, + groupBy: undefined, + }), + }; + + return ; } export default AutocompleteVirtual; diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx index fafae19412..a5ece25497 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx @@ -1,7 +1,6 @@ import type React from 'react'; import { type FormEvent, useState } from 'react'; import { - Autocomplete, Button, capitalize, Checkbox, @@ -40,6 +39,7 @@ import { MultipleRoleSelect } from 'component/common/MultipleRoleSelect/Multiple import type { IUserProjectRole } from '../../../../interfaces/userProjectRoles'; import { useCheckProjectPermissions } from 'hooks/useHasAccess'; import { ADMIN } from 'component/providers/AccessProvider/permissions'; +import AutocompleteVirtual from 'component/common/AutocompleteVirtual/AutcompleteVirtual'; const StyledForm = styled('form')(() => ({ display: 'flex', @@ -339,6 +339,7 @@ export const ProjectAccessAssign = ({ userRoles.some((userrole) => role.id === userrole.id), ); } + return ( - - options - .filter((option: IAccessOption) => { - if ( - option.type === - ENTITY_TYPE.USER || - option.type === - ENTITY_TYPE.SERVICE_ACCOUNT - ) { - const optionUser = - option.entity as IUser; - return ( - caseInsensitiveSearch( - inputValue, - optionUser.email, - ) || - caseInsensitiveSearch( - inputValue, - optionUser.name, - ) || - caseInsensitiveSearch( - inputValue, - optionUser.username, - ) - ); - } - return caseInsensitiveSearch( - inputValue, - option.entity.name, + options.filter((option: IAccessOption) => { + if ( + option.type === ENTITY_TYPE.USER || + option.type === + ENTITY_TYPE.SERVICE_ACCOUNT + ) { + const optionUser = + option.entity as IUser; + return ( + caseInsensitiveSearch( + inputValue, + optionUser.email, + ) || + caseInsensitiveSearch( + inputValue, + optionUser.name, + ) || + caseInsensitiveSearch( + inputValue, + optionUser.username, + ) ); - }) - .slice(0, 100) + } + return caseInsensitiveSearch( + inputValue, + option.entity.name, + ); + }) } isOptionEqualToValue={(option, value) => option.type === value.type &&