mirror of https://github.com/grafana/grafana.git
				
				
				
			Combobox: Squish the menu height in small viewports (#95568)
* Combobox: Squish the menu height in small viewports * rename constants
This commit is contained in:
		
							parent
							
								
									261b4a5564
								
							
						
					
					
						commit
						952957248f
					
				|  | @ -330,6 +330,52 @@ export const Async: StoryObj<PropsAndCustomArgs> = { | |||
|   render: AsyncStory, | ||||
| }; | ||||
| 
 | ||||
| const noop = () => {}; | ||||
| const PositioningTestStory: StoryFn<PropsAndCustomArgs> = (args) => { | ||||
|   if (typeof args.options === 'function') { | ||||
|     throw new Error('This story does not support async options'); | ||||
|   } | ||||
| 
 | ||||
|   function renderColumnOfComboboxes(pos: string) { | ||||
|     return ( | ||||
|       <div | ||||
|         style={{ | ||||
|           display: 'flex', | ||||
|           flexDirection: 'column', | ||||
|           justifyContent: 'space-between', | ||||
|           flex: 1, | ||||
|         }} | ||||
|       > | ||||
|         <Combobox placeholder={`${pos} top`} options={args.options} value={null} onChange={noop} /> | ||||
|         <Combobox placeholder={`${pos} middle`} options={args.options} value={null} onChange={noop} /> | ||||
|         <Combobox placeholder={`${pos} bottom`} options={args.options} value={null} onChange={noop} /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       style={{ | ||||
|         display: 'flex', | ||||
|         flexDirection: 'row', | ||||
| 
 | ||||
|         // approx the height of the dev alert, and three margins. exact doesn't matter
 | ||||
|         minHeight: 'calc(100vh - (105px + 16px + 16px + 16px))', | ||||
|         justifyContent: 'space-between', | ||||
|         gap: 32, | ||||
|       }} | ||||
|     > | ||||
|       {renderColumnOfComboboxes('Left')} | ||||
|       {renderColumnOfComboboxes('Middle')} | ||||
|       {renderColumnOfComboboxes('Right')} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export const PositioningTest: StoryObj<PropsAndCustomArgs> = { | ||||
|   render: PositioningTestStory, | ||||
| }; | ||||
| 
 | ||||
| export const ComparisonToSelect: StoryObj<PropsAndCustomArgs> = { | ||||
|   args: { | ||||
|     numberOfOptions: 100, | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import { AutoSizeInput } from '../Input/AutoSizeInput'; | |||
| import { Input, Props as InputProps } from '../Input/Input'; | ||||
| 
 | ||||
| import { getComboboxStyles } from './getComboboxStyles'; | ||||
| import { estimateSize, useComboboxFloat } from './useComboboxFloat'; | ||||
| import { useComboboxFloat, OPTION_HEIGHT } from './useComboboxFloat'; | ||||
| import { StaleResultError, useLatestAsyncCall } from './useLatestAsyncCall'; | ||||
| 
 | ||||
| export type ComboboxOption<T extends string | number = string> = { | ||||
|  | @ -129,7 +129,7 @@ export const Combobox = <T extends string | number>({ | |||
|   const virtualizerOptions = { | ||||
|     count: items.length, | ||||
|     getScrollElement: () => floatingRef.current, | ||||
|     estimateSize, | ||||
|     estimateSize: () => OPTION_HEIGHT, | ||||
|     overscan: 4, | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,8 +2,6 @@ import { css } from '@emotion/css'; | |||
| 
 | ||||
| import { GrafanaTheme2 } from '@grafana/data'; | ||||
| 
 | ||||
| const MAX_HEIGHT = 400; | ||||
| 
 | ||||
| // We need a px font size to accurately measure the width of items.
 | ||||
| // This should be in sync with the body font size in the theme.
 | ||||
| export const MENU_ITEM_FONT_SIZE = 14; | ||||
|  | @ -20,7 +18,6 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => { | |||
|       background: theme.components.dropdown.background, | ||||
|       boxShadow: theme.shadows.z3, | ||||
|       zIndex: theme.zIndex.dropdown, | ||||
|       maxHeight: MAX_HEIGHT, | ||||
|       overflowY: 'auto', | ||||
|       position: 'relative', | ||||
|     }), | ||||
|  |  | |||
|  | @ -9,12 +9,12 @@ import { MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT, MENU_ITEM_PADDING_X } from | |||
| // Only consider the first n items when calculating the width of the popover.
 | ||||
| const WIDTH_CALCULATION_LIMIT_ITEMS = 100_000; | ||||
| 
 | ||||
| /** | ||||
|  * Used with Downshift to get the height of each item | ||||
|  */ | ||||
| export function estimateSize() { | ||||
|   return 45; | ||||
| } | ||||
| // Used with Downshift to get the height of each item
 | ||||
| export const OPTION_HEIGHT = 45; | ||||
| const POPOVER_MAX_HEIGHT = OPTION_HEIGHT * 8.5; | ||||
| 
 | ||||
| // Clearance around the popover to prevent it from being too close to the edge of the viewport
 | ||||
| const POPOVER_PADDING = 16; | ||||
| 
 | ||||
| export const useComboboxFloat = ( | ||||
|   items: Array<ComboboxOption<string | number>>, | ||||
|  | @ -23,7 +23,7 @@ export const useComboboxFloat = ( | |||
| ) => { | ||||
|   const inputRef = useRef<HTMLInputElement>(null); | ||||
|   const floatingRef = useRef<HTMLDivElement>(null); | ||||
|   const [popoverMaxWidth, setPopoverMaxWidth] = useState<number | undefined>(undefined); | ||||
|   const [popoverMaxSize, setPopoverMaxSize] = useState<{ width: number; height: number } | undefined>(undefined); | ||||
| 
 | ||||
|   const scrollbarWidth = useMemo(() => getScrollbarWidth(), []); | ||||
| 
 | ||||
|  | @ -35,8 +35,14 @@ export const useComboboxFloat = ( | |||
|       boundary: document.body, | ||||
|     }), | ||||
|     size({ | ||||
|       apply({ availableWidth }) { | ||||
|         setPopoverMaxWidth(availableWidth); | ||||
|       apply({ availableWidth, availableHeight }) { | ||||
|         const preferredMaxWidth = availableWidth - POPOVER_PADDING; | ||||
|         const preferredMaxHeight = availableHeight - POPOVER_PADDING; | ||||
| 
 | ||||
|         const width = Math.max(preferredMaxWidth, 0); | ||||
|         const height = Math.min(Math.max(preferredMaxHeight, OPTION_HEIGHT), POPOVER_MAX_HEIGHT); | ||||
| 
 | ||||
|         setPopoverMaxSize({ width, height }); | ||||
|       }, | ||||
|     }), | ||||
|   ]; | ||||
|  | @ -67,8 +73,10 @@ export const useComboboxFloat = ( | |||
|   const floatStyles = { | ||||
|     ...floatingStyles, | ||||
|     width: longestItemWidth, | ||||
|     maxWidth: popoverMaxWidth, | ||||
|     maxWidth: popoverMaxSize?.width, | ||||
|     minWidth: inputRef.current?.offsetWidth, | ||||
| 
 | ||||
|     maxHeight: popoverMaxSize?.height, | ||||
|   }; | ||||
| 
 | ||||
|   return { inputRef, floatingRef, floatStyles }; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue