mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: update search placement on flags overview screen (#9854)
- search has a new place, closer to filters - filters are adjusted to wrap properly on small screens
This commit is contained in:
		
							parent
							
								
									58a01d0c47
								
							
						
					
					
						commit
						6c5fa4c8a7
					
				| @ -1,5 +1,5 @@ | ||||
| import { Box, Chip, styled } from '@mui/material'; | ||||
| import type { FC } from 'react'; | ||||
| import type { FC, ReactNode } from 'react'; | ||||
| import type { FilterItemParamHolder } from '../../../filter/Filters/Filters'; | ||||
| import type { LifecycleStage } from '../../FeatureView/FeatureOverview/FeatureLifecycle/LifecycleStage'; | ||||
| import { useLifecycleCount } from 'hooks/api/getters/useLifecycleCount/useLifecycleCount'; | ||||
| @ -30,13 +30,22 @@ interface ILifecycleFiltersProps { | ||||
|     state: FilterItemParamHolder; | ||||
|     onChange: (value: FilterItemParamHolder) => void; | ||||
|     total?: number; | ||||
|     children?: ReactNode; | ||||
| } | ||||
| 
 | ||||
| const Wrapper = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     justifyContent: 'space-between', | ||||
|     padding: theme.spacing(1.5, 3, 0, 3), | ||||
|     minHeight: theme.spacing(7), | ||||
|     gap: theme.spacing(2), | ||||
| })); | ||||
| 
 | ||||
| const StyledContainer = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
|     flexWrap: 'wrap', | ||||
|     gap: theme.spacing(1), | ||||
|     padding: theme.spacing(2, 3, 0, 3), | ||||
| })); | ||||
| 
 | ||||
| const lifecycleOptions: { | ||||
| @ -74,43 +83,49 @@ export const LifecycleFilters: FC<ILifecycleFiltersProps> = ({ | ||||
|     state, | ||||
|     onChange, | ||||
|     total, | ||||
|     children, | ||||
| }) => { | ||||
|     const { lifecycleCount } = useLifecycleCount(); | ||||
|     const current = state.lifecycle?.values ?? []; | ||||
| 
 | ||||
|     return ( | ||||
|         <Wrapper> | ||||
|             {lifecycleOptions.map(({ label, value }) => { | ||||
|                 const isActive = | ||||
|                     value === null ? !state.lifecycle : current.includes(value); | ||||
|                 const count = getStageCount(value, lifecycleCount); | ||||
|                 const dynamicLabel = | ||||
|                     isActive && Number.isInteger(total) | ||||
|                         ? `${label} (${total === count ? total : `${total} of ${count}`})` | ||||
|                         : `${label}${count !== undefined ? ` (${count})` : ''}`; | ||||
| 
 | ||||
|                 const handleClick = () => | ||||
|                     onChange( | ||||
|             <StyledContainer> | ||||
|                 {lifecycleOptions.map(({ label, value }) => { | ||||
|                     const isActive = | ||||
|                         value === null | ||||
|                             ? { lifecycle: null } | ||||
|                             : { | ||||
|                                   lifecycle: { | ||||
|                                       operator: 'IS', | ||||
|                                       values: [value], | ||||
|                                   }, | ||||
|                               }, | ||||
|                     ); | ||||
|                             ? !state.lifecycle | ||||
|                             : current.includes(value); | ||||
|                     const count = getStageCount(value, lifecycleCount); | ||||
|                     const dynamicLabel = | ||||
|                         isActive && Number.isInteger(total) | ||||
|                             ? `${label} (${total === count ? total : `${total} of ${count}`})` | ||||
|                             : `${label}${count !== undefined ? ` (${count})` : ''}`; | ||||
| 
 | ||||
|                 return ( | ||||
|                     <StyledChip | ||||
|                         key={label} | ||||
|                         label={dynamicLabel} | ||||
|                         variant='outlined' | ||||
|                         isActive={isActive} | ||||
|                         onClick={handleClick} | ||||
|                     /> | ||||
|                 ); | ||||
|             })} | ||||
|                     const handleClick = () => | ||||
|                         onChange( | ||||
|                             value === null | ||||
|                                 ? { lifecycle: null } | ||||
|                                 : { | ||||
|                                       lifecycle: { | ||||
|                                           operator: 'IS', | ||||
|                                           values: [value], | ||||
|                                       }, | ||||
|                                   }, | ||||
|                         ); | ||||
| 
 | ||||
|                     return ( | ||||
|                         <StyledChip | ||||
|                             key={label} | ||||
|                             label={dynamicLabel} | ||||
|                             variant='outlined' | ||||
|                             isActive={isActive} | ||||
|                             onClick={handleClick} | ||||
|                         /> | ||||
|                     ); | ||||
|                 })} | ||||
|             </StyledContainer> | ||||
|             {children} | ||||
|         </Wrapper> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -155,7 +155,7 @@ export const FeatureToggleListTable: FC = () => { | ||||
|                               onTagClick, | ||||
|                               onFlagTypeClick, | ||||
|                           ), | ||||
|                           meta: { width: '50%' }, | ||||
|                           meta: { width: '40%' }, | ||||
|                       }), | ||||
|                       columnHelper.accessor('createdAt', { | ||||
|                           header: 'Created', | ||||
| @ -181,7 +181,7 @@ export const FeatureToggleListTable: FC = () => { | ||||
|                                   data-loading | ||||
|                               /> | ||||
|                           ), | ||||
|                           enableSorting: false, // FIXME: enable sorting by lifecycle
 | ||||
|                           enableSorting: false, | ||||
|                           size: 50, | ||||
|                           meta: { width: '1%' }, | ||||
|                       }), | ||||
| @ -192,6 +192,7 @@ export const FeatureToggleListTable: FC = () => { | ||||
|                               <StatusCell {...original} /> | ||||
|                           ), | ||||
|                           enableSorting: false, | ||||
|                           size: 350, | ||||
|                       }), | ||||
|                       columnHelper.accessor('project', { | ||||
|                           header: 'Project', | ||||
| @ -402,23 +403,19 @@ export const FeatureToggleListTable: FC = () => { | ||||
|                     } | ||||
|                     actions={ | ||||
|                         <> | ||||
|                             <ConditionallyRender | ||||
|                                 condition={!isSmallScreen} | ||||
|                                 show={ | ||||
|                                     <> | ||||
|                                         <Search | ||||
|                                             placeholder='Search' | ||||
|                                             expandable | ||||
|                                             initialValue={ | ||||
|                                                 tableState.query || '' | ||||
|                                             } | ||||
|                                             onChange={setSearchValue} | ||||
|                                             id='globalFeatureFlags' | ||||
|                                         /> | ||||
|                                         <PageHeader.Divider /> | ||||
|                                     </> | ||||
|                                 } | ||||
|                             /> | ||||
|                             {!flagsReleaseManagementUIEnabled && | ||||
|                             !isSmallScreen ? ( | ||||
|                                 <> | ||||
|                                     <Search | ||||
|                                         placeholder='Search' | ||||
|                                         expandable | ||||
|                                         initialValue={tableState.query || ''} | ||||
|                                         onChange={setSearchValue} | ||||
|                                         id='globalFeatureFlags' | ||||
|                                     /> | ||||
|                                     <PageHeader.Divider /> | ||||
|                                 </> | ||||
|                             ) : null} | ||||
|                             <Link | ||||
|                                 component={RouterLink} | ||||
|                                 to='/archive' | ||||
| @ -449,7 +446,9 @@ export const FeatureToggleListTable: FC = () => { | ||||
|                     } | ||||
|                 > | ||||
|                     <ConditionallyRender | ||||
|                         condition={isSmallScreen} | ||||
|                         condition={ | ||||
|                             isSmallScreen && !flagsReleaseManagementUIEnabled | ||||
|                         } | ||||
|                         show={ | ||||
|                             <Search | ||||
|                                 initialValue={tableState.query || ''} | ||||
| @ -466,17 +465,36 @@ export const FeatureToggleListTable: FC = () => { | ||||
|                     state={filterState} | ||||
|                     onChange={setTableState} | ||||
|                     total={loading ? undefined : total} | ||||
|                 /> | ||||
|                 > | ||||
|                     {!isSmallScreen ? ( | ||||
|                         <Search | ||||
|                             placeholder='Search' | ||||
|                             initialValue={tableState.query || ''} | ||||
|                             onChange={setSearchValue} | ||||
|                             id='globalFeatureFlags' | ||||
|                         /> | ||||
|                     ) : null} | ||||
|                 </LifecycleFilters> | ||||
|             ) : null} | ||||
|             <FeatureToggleFilters | ||||
|                 onChange={setTableState} | ||||
|                 state={filterState} | ||||
|             /> | ||||
|             {isSmallScreen ? ( | ||||
|                 <Box sx={(theme) => ({ padding: theme.spacing(0, 3, 3) })}> | ||||
|                     <Search | ||||
|                         initialValue={tableState.query || ''} | ||||
|                         onChange={setSearchValue} | ||||
|                         id='globalFeatureFlags' | ||||
|                     /> | ||||
|                 </Box> | ||||
|             ) : null} | ||||
|             <SearchHighlightProvider value={tableState.query || ''}> | ||||
|                 <div ref={bodyLoadingRef}> | ||||
|                     <PaginatedTable tableInstance={table} totalItems={total} /> | ||||
|                 </div> | ||||
|             </SearchHighlightProvider> | ||||
| 
 | ||||
|             <ConditionallyRender | ||||
|                 condition={rows.length === 0} | ||||
|                 show={ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user