Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/small-queens-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

SelectPanel: Add `title` prop
6 changes: 6 additions & 0 deletions generated/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -3642,6 +3642,12 @@
}
],
"props": [
{
"name": "title",
"type": "string",
"defaultValue": "\"Select an item\" or \"Select items\"",
"description": "A descriptive title for the panel"
},
{
"name": "onOpenChange",
"type": "( open: boolean, gesture: | 'anchor-click' | 'anchor-key-press' | 'click-outside' | 'escape' | 'selection' ) => void",
Expand Down
8 changes: 7 additions & 1 deletion src/SelectPanel/SelectPanel.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
"a11yReviewed": false,
"stories": [],
"props": [
{
"name": "title",
"type": "string",
"defaultValue": "\"Select an item\" or \"Select items\"",
"description": "A descriptive title for the panel"
},
{
"name": "onOpenChange",
"type": "( open: boolean, gesture: | 'anchor-click' | 'anchor-key-press' | 'click-outside' | 'escape' | 'selection' ) => void",
Expand Down Expand Up @@ -56,4 +62,4 @@
}
],
"subcomponents": []
}
}
56 changes: 35 additions & 21 deletions src/SelectPanel/SelectPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import {useId} from '../hooks/useId'
import React, {useCallback, useMemo} from 'react'
import {AnchoredOverlay, AnchoredOverlayProps} from '../AnchoredOverlay'
import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay'
import {FilteredActionList, FilteredActionListProps} from '../FilteredActionList'
import Heading from '../Heading'
import {OverlayProps} from '../Overlay'
import {TextInputProps} from '../TextInput'
import {ItemProps} from '../deprecated/ActionList'
import {ItemInput} from '../deprecated/ActionList/List'
import {FocusZoneHookSettings} from '../hooks/useFocusZone'
import {DropdownButton} from '../deprecated/DropdownMenu'
import {ItemProps} from '../deprecated/ActionList'
import {AnchoredOverlay, AnchoredOverlayProps} from '../AnchoredOverlay'
import {TextInputProps} from '../TextInput'
import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay'
import {useProvidedRefOrCreate} from '../hooks'
import {FocusZoneHookSettings} from '../hooks/useFocusZone'
import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
import Box from '../Box'

interface SelectPanelSingleSelection {
selected: ItemInput | undefined
Expand All @@ -22,6 +25,8 @@ interface SelectPanelMultiSelection {
}

interface SelectPanelBaseProps {
// TODO: Make `title` required in the next major version
title?: string
onOpenChange: (
open: boolean,
gesture: 'anchor-click' | 'anchor-key-press' | 'click-outside' | 'escape' | 'selection',
Expand Down Expand Up @@ -54,6 +59,7 @@ export function SelectPanel({
anchorRef: externalAnchorRef,
placeholder,
selected,
title = isMultiSelectVariant(selected) ? 'Select items' : 'Select an item',
onSelectedChange,
filterValue: externalFilterValue,
onFilterChange: externalOnFilterChange,
Expand All @@ -63,6 +69,7 @@ export function SelectPanel({
sx,
...listProps
}: SelectPanelProps): JSX.Element {
const titleId = useId()
const [filterValue, setInternalFilterValue] = useProvidedStateOrCreate(externalFilterValue, undefined, '')
const onFilterChange: FilteredActionListProps['onFilterChange'] = useCallback(
(value, e) => {
Expand Down Expand Up @@ -152,24 +159,31 @@ export function SelectPanel({
open={open}
onOpen={onOpen}
onClose={onClose}
overlayProps={{role: 'dialog', ...overlayProps}}
overlayProps={{role: 'dialog', 'aria-labelledby': titleId, ...overlayProps}}
focusTrapSettings={focusTrapSettings}
focusZoneSettings={focusZoneSettings}
>
<FilteredActionList
filterValue={filterValue}
onFilterChange={onFilterChange}
{...listProps}
role="listbox"
aria-multiselectable={isMultiSelectVariant(selected) ? 'true' : 'false'}
selectionVariant={isMultiSelectVariant(selected) ? 'multiple' : 'single'}
items={itemsToRender}
textInputProps={extendedTextInputProps}
inputRef={inputRef}
// inheriting height and maxHeight ensures that the FilteredActionList is never taller
// than the Overlay (which would break scrolling the items)
sx={{...sx, height: 'inherit', maxHeight: 'inherit'}}
/>
<Box sx={{display: 'flex', flexDirection: 'column', height: 'inherit', maxHeight: 'inherit'}}>
<Box sx={{pt: 2, px: 3}}>
<Heading as="h1" id={titleId} sx={{fontSize: 1}}>
{title}
</Heading>
</Box>
<FilteredActionList
filterValue={filterValue}
onFilterChange={onFilterChange}
{...listProps}
role="listbox"
aria-multiselectable={isMultiSelectVariant(selected) ? 'true' : 'false'}
selectionVariant={isMultiSelectVariant(selected) ? 'multiple' : 'single'}
items={itemsToRender}
textInputProps={extendedTextInputProps}
inputRef={inputRef}
// inheriting height and maxHeight ensures that the FilteredActionList is never taller
// than the Overlay (which would break scrolling the items)
sx={{...sx, height: 'inherit', maxHeight: 'inherit'}}
/>
</Box>
</AnchoredOverlay>
)
}
Expand Down