From f5d585a29593621332344816d76bd8fde35b67f7 Mon Sep 17 00:00:00 2001 From: koji Date: Fri, 14 Jun 2024 17:54:54 -0400 Subject: [PATCH] fix(app): Fix dropdown menu display issue for many items (#15420) * fix(app): Fix dropdown menu display issue for many items --- .../atoms/MenuList/DropdownMenu.stories.tsx | 17 ++++---- app/src/atoms/MenuList/DropdownMenu.tsx | 39 ++++++++++++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/app/src/atoms/MenuList/DropdownMenu.stories.tsx b/app/src/atoms/MenuList/DropdownMenu.stories.tsx index fd355600c7c..b73d0502c36 100644 --- a/app/src/atoms/MenuList/DropdownMenu.stories.tsx +++ b/app/src/atoms/MenuList/DropdownMenu.stories.tsx @@ -3,14 +3,15 @@ import { DropdownMenu as DropdownMenuComponent } from './DropdownMenu' import type { Meta, StoryObj } from '@storybook/react' import type { DropdownOption } from './DropdownMenu' -const mockOptions: DropdownOption[] = [ - { name: 'option 1', value: '1' }, - { name: 'option 2', value: '2' }, - { name: 'option 3', value: '3' }, - { name: 'option 4', value: '4' }, - { name: 'option 5', value: '5' }, - { name: 'option 6', value: '6' }, -] +function createMockOptions(): DropdownOption[] { + const options: DropdownOption[] = [] + for (let i = 1; i <= 100; i++) { + options.push({ name: `option ${i}`, value: `${i}` }) + } + return options +} + +const mockOptions: DropdownOption[] = createMockOptions() const meta: Meta = { title: 'App/Atoms/DropdownMenu', diff --git a/app/src/atoms/MenuList/DropdownMenu.tsx b/app/src/atoms/MenuList/DropdownMenu.tsx index c2dd1ed9c3d..7408683f986 100644 --- a/app/src/atoms/MenuList/DropdownMenu.tsx +++ b/app/src/atoms/MenuList/DropdownMenu.tsx @@ -16,10 +16,17 @@ import { useOnClickOutside, POSITION_RELATIVE, useHoverTooltip, + OVERFLOW_AUTO, } from '@opentrons/components' import { Tooltip } from '../Tooltip' import { MenuItem } from './MenuItem' +/** this is the max height to display 10 items */ +const MAX_HEIGHT = 316 + +/** this is for adjustment variable for the case that the space of the bottom and the space of the top are very close */ +const HEIGHT_ADJUSTMENT = 100 + export interface DropdownOption { name: string value: string @@ -69,6 +76,12 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { 'top' | 'bottom' >('bottom') + const dropDownMenuWrapperRef = useOnClickOutside({ + onClickOutside: () => { + setShowDropdownMenu(false) + }, + }) + React.useEffect(() => { const handlePositionCalculation = (): void => { const dropdownRect = dropDownMenuWrapperRef.current?.getBoundingClientRect() @@ -88,10 +101,18 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { scrollOffset = parentRect.top } - const dropdownBottom = - dropdownRect.bottom + (filterOptions.length + 1) * 34 - scrollOffset + const downSpace = + filterOptions.length + 1 > 10 + ? MAX_HEIGHT + : (filterOptions.length + 1) * 34 + const dropdownBottom = dropdownRect.bottom + downSpace - scrollOffset - setDropdownPosition(dropdownBottom > availableHeight ? 'top' : 'bottom') + setDropdownPosition( + dropdownBottom > availableHeight && + Math.abs(dropdownBottom - availableHeight) > HEIGHT_ADJUSTMENT + ? 'top' + : 'bottom' + ) } } @@ -103,16 +124,11 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { window.removeEventListener('resize', handlePositionCalculation) window.removeEventListener('scroll', handlePositionCalculation) } - }, [filterOptions.length, window.innerHeight]) + }, [filterOptions.length, dropDownMenuWrapperRef]) const toggleSetShowDropdownMenu = (): void => { setShowDropdownMenu(!showDropdownMenu) } - const dropDownMenuWrapperRef = useOnClickOutside({ - onClickOutside: () => { - setShowDropdownMenu(false) - }, - }) const DROPDOWN_STYLE = css` flex-direction: ${DIRECTION_ROW}; @@ -204,7 +220,7 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { {showDropdownMenu && ( {filterOptions.map((option, index) => ( { onClick(option.value)