Skip to content

Commit

Permalink
Prevent Select listbox to grow out of viewport
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya committed Oct 22, 2024
1 parent c8ee777 commit 20afc2f
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 12 deletions.
31 changes: 20 additions & 11 deletions src/components/input/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,22 +190,19 @@ function SelectOption<T>({
>
<div
className={classnames(
// Make items stretch so that all have the same height. This is
// important for multi-selects, where the checkbox actionable surface
// should span to the very edges of the option containing it.
'flex justify-between items-stretch',
'flex justify-between items-center',
'w-full rounded',
{
'hover:bg-grey-1 group-focus-visible:ring': !disabled,
'bg-grey-1 hover:bg-grey-2': selected,
},
)}
>
<div className="flex items-center py-2 pl-3">
<div className="py-2 pl-3 truncate">
{optionChildren(children, { selected, disabled })}
</div>
{!multiple && (
<div className="flex items-center py-2 px-3">
<div className="px-3">
<CheckIcon
className={classnames('text-grey-6 scale-125', {
// Make the icon visible/invisible, instead of conditionally
Expand All @@ -219,7 +216,9 @@ function SelectOption<T>({
{multiple && (
<Checkbox
containerClasses={classnames(
'flex items-center py-2 px-3',
// Make the checkbox stretch, so that its actionable surface spans
// to the very edges of the option containing it.
'self-stretch px-3',
// The checkbox is sized based on the container's font size. Make
// it a bit larger.
'text-lg',
Expand Down Expand Up @@ -255,6 +254,7 @@ type ListboxCSSProps =
| 'top'
| 'left'
| 'minWidth'
| 'maxWidth'
| 'marginBottom'
| 'bottom'
| 'marginTop';
Expand Down Expand Up @@ -312,17 +312,26 @@ function useListboxPositioning(
buttonDistanceToTop > buttonDistanceToBottom;

if (asPopover) {
const { top: bodyTop } = document.body.getBoundingClientRect();
const { top: bodyTop, width: bodyWidth } =
document.body.getBoundingClientRect();
const absBodyTop = Math.abs(bodyTop);

// Make sure the listbox never increases the body size, which could cause
// horizontal scrollbars to appear
const maxWidth = bodyWidth - (buttonLeft + (right ? buttonWidth : 0)) - 8;
// To calculate left when the listbox is aligned to the right, take the
// smallest size the listbox could have at render-time
const smallestListboxWidth = Math.min(maxWidth, listboxWidth);

return setListboxCSSProps({
minWidth: `${buttonWidth}px`,
maxWidth: `${maxWidth}px`,
top: shouldListboxDropUp
? `calc(${absBodyTop + buttonDistanceToTop - listboxHeight}px - ${LISTBOX_TOGGLE_GAP})`
: `calc(${absBodyTop + buttonDistanceToTop + buttonHeight}px + ${LISTBOX_TOGGLE_GAP})`,
left:
right && listboxWidth > buttonWidth
? `${buttonLeft - (listboxWidth - buttonWidth)}px`
right && smallestListboxWidth > buttonWidth
? `${buttonLeft - (smallestListboxWidth - buttonWidth)}px`
: `${buttonLeft}px`,
});
}
Expand Down Expand Up @@ -554,7 +563,7 @@ function SelectMain<T>({
>
<ul
className={classnames(
'absolute z-5 max-h-80 overflow-y-auto',
'absolute z-5 max-h-80 overflow-y-auto overflow-x-hidden',
'rounded border bg-white shadow hover:shadow-md focus-within:shadow-md',
!listboxAsPopover && {
// Hiding instead of unmounting to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,14 @@ export default function SelectPage() {

<Library.Demo title="Plain text">
<div className="mx-auto">
<SelectExample textOnly buttonClasses="!w-36" />
<SelectExample
textOnly
buttonClasses="!w-36"
items={defaultItems.map(({ name, ...rest }) => ({
...rest,
name: `${name} (this item has a very long text)`,
}))}
/>
</div>
</Library.Demo>

Expand Down

0 comments on commit 20afc2f

Please sign in to comment.