Skip to content

Commit

Permalink
Bring back #5255 "SelectPanel: Prepare for non-anchored variants" (#5262
Browse files Browse the repository at this point in the history
)

* Revert "Revert "SelectPanel: Prepare for non-anchored variants (#5230)" (#5255)"

This reverts commit 64802f1.

* Revert "tiny formatting to make it easier to review"

This reverts commit f0be480.

* different way of writing the same thing
  • Loading branch information
siddharthkp authored Nov 12, 2024
1 parent 3f508c7 commit fca4ec9
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-days-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

SelectPanel: (Implementation detail, should have no changes for users) Replace AnchoredOverlay with Overlay and useAnchoredPosition
105 changes: 79 additions & 26 deletions packages/react/src/SelectPanel/SelectPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {SearchIcon, TriangleDownIcon} from '@primer/octicons-react'
import React, {useCallback, useMemo} from 'react'
import type {AnchoredOverlayProps} from '../AnchoredOverlay'
import {AnchoredOverlay} from '../AnchoredOverlay'
import Overlay from '../Overlay'
import type {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay'
import Box from '../Box'
import type {FilteredActionListProps} from '../FilteredActionList'
Expand All @@ -12,13 +12,14 @@ import type {TextInputProps} from '../TextInput'
import type {ItemProps, ItemInput} from './types'

import {Button} from '../Button'
import {useProvidedRefOrCreate} from '../hooks'
import type {FocusZoneHookSettings} from '../hooks/useFocusZone'
import {useAnchoredPosition, useProvidedRefOrCreate} from '../hooks'
import {useId} from '../hooks/useId'
import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
import {LiveRegion, LiveRegionOutlet, Message} from '../internal/components/LiveRegion'
import {useFeatureFlag} from '../FeatureFlags'

import {useFocusTrap} from '../hooks/useFocusTrap'

interface SelectPanelSingleSelection {
selected: ItemInput | undefined
onSelectedChange: (selected: ItemInput | undefined) => void
Expand Down Expand Up @@ -56,11 +57,6 @@ function isMultiSelectVariant(
return Array.isArray(selected)
}

const focusZoneSettings: Partial<FocusZoneHookSettings> = {
// Let FilteredActionList handle focus zone
disabled: true,
}

const areItemsEqual = (itemA: ItemInput, itemB: ItemInput) => {
// prefer checking equivality by item.id
if (typeof itemA.id !== 'undefined') return itemA.id === itemB.id
Expand Down Expand Up @@ -137,6 +133,57 @@ export function SelectPanel({
}
}, [placeholder, renderAnchor, selected])

/* Anchoring logic */
const overlayRef = React.useRef<HTMLDivElement>(null)
const inputRef = React.useRef<HTMLInputElement>(null)

const {position} = useAnchoredPosition(
{
anchorElementRef: anchorRef,
floatingElementRef: overlayRef,
side: 'outside-bottom',
align: 'start',
},
[open, anchorRef.current, overlayRef.current],
)

const onAnchorClick = useCallback(
(event: React.MouseEvent<HTMLElement>) => {
if (event.defaultPrevented || event.button !== 0) {
return
}

if (!open) {
onOpen('anchor-click')
} else {
onClose('anchor-click')
}
},
[open, onOpen, onClose],
)

const onAnchorKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLElement>) => {
if (!event.defaultPrevented) {
if (!open && ['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(event.key)) {
onOpen('anchor-key-press', event)
event.preventDefault()
}
}
},
[open, onOpen],
)

const anchorProps = {
ref: anchorRef,
'aria-haspopup': true,
'aria-expanded': open,
onClick: onAnchorClick,
onKeyDown: onAnchorKeyDown,
}
// TODO: anchor should be called button because it's not an anchor anymore
const anchor = renderMenuAnchor ? renderMenuAnchor(anchorProps) : null

const itemsToRender = useMemo(() => {
return items.map(item => {
const isItemSelected = isMultiSelectVariant(selected) ? doesItemsIncludeItem(selected, item) : selected === item
Expand Down Expand Up @@ -172,10 +219,13 @@ export function SelectPanel({
})
}, [onClose, onSelectedChange, items, selected])

const inputRef = React.useRef<HTMLInputElement>(null)
const focusTrapSettings = {
/** Focus trap */
useFocusTrap({
containerRef: overlayRef,
disabled: !open || !position,
initialFocusRef: inputRef,
}
returnFocusRef: anchorRef,
})

const extendedTextInputProps: Partial<TextInputProps> = useMemo(() => {
return {
Expand All @@ -189,22 +239,25 @@ export function SelectPanel({

const usingModernActionList = useFeatureFlag('primer_react_select_panel_with_modern_action_list')

if (!open) return <LiveRegion>{anchor}</LiveRegion>

return (
<LiveRegion>
<AnchoredOverlay
renderAnchor={renderMenuAnchor}
anchorRef={anchorRef}
open={open}
onOpen={onOpen}
onClose={onClose}
overlayProps={{
role: 'dialog',
'aria-labelledby': titleId,
'aria-describedby': subtitle ? subtitleId : undefined,
...overlayProps,
}}
focusTrapSettings={focusTrapSettings}
focusZoneSettings={focusZoneSettings}
{anchor}
<Overlay
role="dialog"
aria-labelledby={titleId}
aria-describedby={subtitle ? subtitleId : undefined}
ref={overlayRef}
returnFocusRef={anchorRef}
onEscape={() => onClose('escape')}
onClickOutside={() => onClose('click-outside')}
ignoreClickRefs={
/* this is required so that clicking the button while the panel is open does not re-open the panel */
[anchorRef]
}
{...position}
{...overlayProps}
>
<LiveRegionOutlet />
{usingModernActionList ? null : (
Expand Down Expand Up @@ -260,7 +313,7 @@ export function SelectPanel({
</Box>
)}
</Box>
</AnchoredOverlay>
</Overlay>
</LiveRegion>
)
}
Expand Down

0 comments on commit fca4ec9

Please sign in to comment.