From 3e65a4647002ef98d35b60e7d42b1ab28950a161 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 24 Mar 2026 20:49:08 -0400 Subject: [PATCH 1/4] feat: add displayInViewport option to SelectPanel for improved visibility --- packages/react/src/SelectPanel/SelectPanel.docs.json | 6 ++++++ packages/react/src/SelectPanel/SelectPanel.tsx | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/react/src/SelectPanel/SelectPanel.docs.json b/packages/react/src/SelectPanel/SelectPanel.docs.json index f55961d2614..d7f91b944e1 100644 --- a/packages/react/src/SelectPanel/SelectPanel.docs.json +++ b/packages/react/src/SelectPanel/SelectPanel.docs.json @@ -237,6 +237,12 @@ "type": "boolean", "defaultValue": "false", "description": "If true, enables client-side list virtualization. Only the visible items plus a small overscan buffer are rendered in the DOM, dramatically improving performance for large lists. Recommended for lists with more than 100 items. Has no effect when `groupMetadata` is provided." + }, + { + "name": "displayInViewport", + "type": "boolean", + "defaultValue": "false", + "description": "If true, the panel will attempt to fit entirely into the visible viewport, without having to scroll." } ], "subcomponents": [] diff --git a/packages/react/src/SelectPanel/SelectPanel.tsx b/packages/react/src/SelectPanel/SelectPanel.tsx index 82cd0812d1b..04667503618 100644 --- a/packages/react/src/SelectPanel/SelectPanel.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.tsx @@ -131,7 +131,7 @@ type SelectPanelVariantProps = {variant?: 'anchored'; onCancel?: () => void} | { export type SelectPanelProps = SelectPanelBaseProps & Omit & - Pick & + Pick & AnchoredOverlayWrapperAnchorProps & (SelectPanelSingleSelection | SelectPanelMultiSelection) & SelectPanelVariantProps @@ -203,6 +203,7 @@ function Panel({ showSelectAll = false, focusPrependedElements, virtualized, + displayInViewport, ...listProps }: SelectPanelProps): JSX.Element { const titleId = useId() @@ -876,6 +877,7 @@ function Panel({ className={classes.Overlay} displayCloseButton={showXCloseIcon} closeButtonProps={closeButtonProps} + displayInViewport={displayInViewport} >
From fd4a28d757fc1d52c85b5abcc6793d0b41ebc1f8 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 24 Mar 2026 20:50:54 -0400 Subject: [PATCH 2/4] Add changeset --- .changeset/select-panel-display-in-viewport.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/select-panel-display-in-viewport.md diff --git a/.changeset/select-panel-display-in-viewport.md b/.changeset/select-panel-display-in-viewport.md new file mode 100644 index 00000000000..42be1dbbff0 --- /dev/null +++ b/.changeset/select-panel-display-in-viewport.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +SelectPanel: Add `displayInViewport` prop to allow the panel to fit entirely within the visible viewport without requiring scrolling. From bfcf0df5b3ee5e861dac7ee5c9919508a47cf74e Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 24 Mar 2026 20:51:14 -0400 Subject: [PATCH 3/4] add changeset --- .changeset/select-panel-display-in-viewport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/select-panel-display-in-viewport.md b/.changeset/select-panel-display-in-viewport.md index 42be1dbbff0..05357d80c04 100644 --- a/.changeset/select-panel-display-in-viewport.md +++ b/.changeset/select-panel-display-in-viewport.md @@ -2,4 +2,4 @@ '@primer/react': minor --- -SelectPanel: Add `displayInViewport` prop to allow the panel to fit entirely within the visible viewport without requiring scrolling. +SelectPanel: Add `displayInViewport` prop From 1f95f3ed8875801d4f7fa61347b67a32b7be2be5 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:02:59 -0400 Subject: [PATCH 4/4] [WIP] [WIP] Address feedback on displayInViewport option for SelectPanel (#7702) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: francinelucca <40550942+francinelucca@users.noreply.github.com> --- .../src/SelectPanel/SelectPanel.test.tsx | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/react/src/SelectPanel/SelectPanel.test.tsx b/packages/react/src/SelectPanel/SelectPanel.test.tsx index fae621080c7..de52939b1ac 100644 --- a/packages/react/src/SelectPanel/SelectPanel.test.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.test.tsx @@ -10,6 +10,29 @@ import {IconButton} from '../Button' import {ArrowLeftIcon} from '@primer/octicons-react' import classes from './SelectPanel.test.module.css' import {implementsClassName} from '../utils/testing' +import {getAnchoredPosition} from '@primer/behaviors' +import type {AnchorPosition} from '@primer/behaviors' + +// Mock getAnchoredPosition to verify displayInViewport is forwarded +vi.mock('@primer/behaviors', async () => { + const actual = await vi.importActual('@primer/behaviors') + return { + ...actual, + getAnchoredPosition: vi.fn( + ( + _floatingElement: Element, + _anchorElement: Element | DOMRect, + _settings?: Partial<{displayInViewport?: boolean}>, + ) => + ({ + top: 100, + left: 100, + anchorSide: 'outside-bottom', + anchorAlign: 'start', + }) as AnchorPosition, + ), + } +}) // Instead of importing from live-region/__tests__/test-helpers.ts, we define our own getLiveRegion function export function getLiveRegion(): LiveRegionElement { @@ -1734,3 +1757,49 @@ for (const usingRemoveActiveDescendant of [false, true]) { }) }) } + +describe('SelectPanel displayInViewport prop', () => { + const mockGetAnchoredPosition = vi.mocked(getAnchoredPosition) + + beforeEach(() => { + mockGetAnchoredPosition.mockClear() + }) + + it('should forward displayInViewport={true} to getAnchoredPosition', async () => { + const user = userEvent.setup() + render() + + await user.click(screen.getByRole('button', {name: 'Select items'})) + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument() + }) + + await waitFor(() => { + expect(mockGetAnchoredPosition).toHaveBeenCalled() + }) + + const calls = mockGetAnchoredPosition.mock.calls + const lastCall = calls[calls.length - 1] + expect(lastCall[2]?.displayInViewport).toBe(true) + }) + + it('should not set displayInViewport when prop is not provided', async () => { + const user = userEvent.setup() + render() + + await user.click(screen.getByRole('button', {name: 'Select items'})) + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument() + }) + + await waitFor(() => { + expect(mockGetAnchoredPosition).toHaveBeenCalled() + }) + + const calls = mockGetAnchoredPosition.mock.calls + const lastCall = calls[calls.length - 1] + expect(lastCall[2]?.displayInViewport).not.toBe(true) + }) +})