mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: grouping of project level roles in autocomplete (#9046)
This commit is contained in:
		
							parent
							
								
									18cd0e2cdb
								
							
						
					
					
						commit
						e0b4e258dc
					
				| @ -0,0 +1,53 @@ | ||||
| import { render } from 'utils/testRenderer'; | ||||
| import { MultipleRoleSelect } from './MultipleRoleSelect'; | ||||
| import { fireEvent, screen } from '@testing-library/react'; | ||||
| 
 | ||||
| test('Display grouped project roles with names and descriptions', async () => { | ||||
|     render( | ||||
|         <MultipleRoleSelect | ||||
|             roles={[ | ||||
|                 { | ||||
|                     id: 0, | ||||
|                     name: 'Owner', | ||||
|                     project: null, | ||||
|                     description: 'Owner description', | ||||
|                     type: 'project', | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 1, | ||||
|                     name: 'B Custom Role', | ||||
|                     project: null, | ||||
|                     description: 'Custom role description A', | ||||
|                     type: 'custom', | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 2, | ||||
|                     name: 'A Custom Role', | ||||
|                     project: null, | ||||
|                     description: 'Custom role description B', | ||||
|                     type: 'custom', | ||||
|                 }, | ||||
|             ]} | ||||
|             value={[]} | ||||
|             setValue={() => {}} | ||||
|         />, | ||||
|     ); | ||||
| 
 | ||||
|     const multiselect = await screen.findByLabelText('Role'); | ||||
| 
 | ||||
|     fireEvent.click(multiselect); | ||||
| 
 | ||||
|     expect(screen.getByText('Predefined project roles')).toBeInTheDocument(); | ||||
|     expect(screen.getByText('Owner')).toBeInTheDocument(); | ||||
|     expect(screen.getByText('Owner description')).toBeInTheDocument(); | ||||
|     expect(screen.getByText('Custom project roles')).toBeInTheDocument(); | ||||
|     const customRoleA = screen.getByText('A Custom Role'); | ||||
|     const customRoleB = screen.getByText('B Custom Role'); | ||||
|     expect(customRoleA).toBeInTheDocument(); | ||||
|     expect(customRoleB).toBeInTheDocument(); | ||||
|     expect(customRoleA.compareDocumentPosition(customRoleB)).toBe( | ||||
|         Node.DOCUMENT_POSITION_FOLLOWING, | ||||
|     ); | ||||
|     expect(screen.getByText('Custom role description A')).toBeInTheDocument(); | ||||
|     expect(screen.getByText('Custom role description B')).toBeInTheDocument(); | ||||
| }); | ||||
| @ -3,8 +3,8 @@ import { | ||||
|     type AutocompleteProps, | ||||
|     type AutocompleteRenderOptionState, | ||||
|     Checkbox, | ||||
|     TextField, | ||||
|     styled, | ||||
|     TextField, | ||||
| } from '@mui/material'; | ||||
| import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; | ||||
| import CheckBoxIcon from '@mui/icons-material/CheckBox'; | ||||
| @ -13,6 +13,7 @@ import { RoleDescription } from '../RoleDescription/RoleDescription'; | ||||
| import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; | ||||
| 
 | ||||
| const StyledRoleOption = styled('div')(({ theme }) => ({ | ||||
|     paddingTop: theme.spacing(0.75), | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     '& > span:last-of-type': { | ||||
| @ -29,6 +30,25 @@ interface IMultipleRoleSelectProps | ||||
|     required?: boolean; | ||||
| } | ||||
| 
 | ||||
| function sortItems<T extends { name: string; type: string }>(items: T[]): T[] { | ||||
|     return items.sort((a, b) => { | ||||
|         if (a.type !== b.type) { | ||||
|             return a.type === 'project' ? -1 : 1; | ||||
|         } | ||||
| 
 | ||||
|         if (a.type === 'custom') { | ||||
|             return a.name.localeCompare(b.name); | ||||
|         } | ||||
| 
 | ||||
|         return 0; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| const StyledListItem = styled('li')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     gap: theme.spacing(0.5), | ||||
| })); | ||||
| 
 | ||||
| export const MultipleRoleSelect = ({ | ||||
|     roles, | ||||
|     value, | ||||
| @ -41,30 +61,48 @@ export const MultipleRoleSelect = ({ | ||||
|         option: IRole, | ||||
|         state: AutocompleteRenderOptionState, | ||||
|     ) => ( | ||||
|         <li {...props}> | ||||
|         <StyledListItem {...props} key={option.id}> | ||||
|             <Checkbox | ||||
|                 icon={<CheckBoxOutlineBlankIcon fontSize='small' />} | ||||
|                 checkedIcon={<CheckBoxIcon fontSize='small' />} | ||||
|                 style={{ marginRight: 8 }} | ||||
|                 checked={state.selected} | ||||
|             /> | ||||
|             <StyledRoleOption> | ||||
|                 <span>{option.name}</span> | ||||
|                 <span>{option.description}</span> | ||||
|             </StyledRoleOption> | ||||
|         </li> | ||||
|         </StyledListItem> | ||||
|     ); | ||||
| 
 | ||||
|     const sortedRoles = sortItems(roles); | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <Autocomplete | ||||
|                 slotProps={{ | ||||
|                     paper: { | ||||
|                         sx: { | ||||
|                             '& .MuiAutocomplete-listbox': { | ||||
|                                 '& .MuiAutocomplete-option': { | ||||
|                                     paddingLeft: (theme) => theme.spacing(0.5), | ||||
|                                     alignItems: 'flex-start', | ||||
|                                 }, | ||||
|                             }, | ||||
|                         }, | ||||
|                     }, | ||||
|                 }} | ||||
|                 multiple | ||||
|                 disableCloseOnSelect | ||||
|                 openOnFocus | ||||
|                 size='small' | ||||
|                 value={value} | ||||
|                 groupBy={(option) => { | ||||
|                     return option.type === 'project' | ||||
|                         ? 'Predefined project roles' | ||||
|                         : 'Custom project roles'; | ||||
|                 }} | ||||
|                 onChange={(_, roles) => setValue(roles)} | ||||
|                 options={roles} | ||||
|                 options={sortedRoles} | ||||
|                 renderOption={renderRoleOption} | ||||
|                 getOptionLabel={(option) => option.name} | ||||
|                 renderInput={(params) => ( | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user