Skip to content

Commit

Permalink
SelectPanel: Add title prop (#3311)
Browse files Browse the repository at this point in the history
* Add a title prop to SelectPanel

* Update generated/components.json

* Create small-queens-vanish.md

* Allow title to be ReactElement

* Update SelectPanel stories

* Update generated/components.json

* Update snapshots

---------

Co-authored-by: colebemis <colebemis@users.noreply.github.com>
  • Loading branch information
colebemis and colebemis authored May 24, 2023
1 parent 56c9dd2 commit 00cf2e3
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 29 deletions.
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
8 changes: 7 additions & 1 deletion generated/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -3638,10 +3638,16 @@
"stories": [
{
"id": "components-selectpanel--default",
"code": "() => {\n const [selected, setSelected] = React.useState<ItemInput[]>([\n items[0],\n items[1],\n ])\n const [filter, setFilter] = React.useState('')\n const filteredItems = items.filter((item) =>\n item.text.toLowerCase().startsWith(filter.toLowerCase()),\n )\n const [open, setOpen] = useState(false)\n return (\n <>\n <h1>Multi Select Panel</h1>\n <div>Please select labels that describe your issue:</div>\n <SelectPanel\n renderAnchor={({\n children,\n 'aria-labelledby': ariaLabelledBy,\n ...anchorProps\n }) => (\n <Button\n trailingAction={TriangleDownIcon}\n aria-labelledby={` ${ariaLabelledBy}`}\n {...anchorProps}\n >\n {children ?? 'Select Labels'}\n </Button>\n )}\n placeholderText=\"Filter Labels\"\n open={open}\n onOpenChange={setOpen}\n items={filteredItems}\n selected={selected}\n onSelectedChange={setSelected}\n onFilterChange={setFilter}\n showItemDividers={true}\n overlayProps={{\n width: 'small',\n height: 'xsmall',\n }}\n />\n </>\n )\n}"
"code": "() => {\n const [selected, setSelected] = React.useState<ItemInput[]>([\n items[0],\n items[1],\n ])\n const [filter, setFilter] = React.useState('')\n const filteredItems = items.filter((item) =>\n item.text.toLowerCase().startsWith(filter.toLowerCase()),\n )\n const [open, setOpen] = useState(false)\n return (\n <>\n <h1>Multi Select Panel</h1>\n <div>Please select labels that describe your issue:</div>\n <SelectPanel\n title=\"Select labels\"\n renderAnchor={({\n children,\n 'aria-labelledby': ariaLabelledBy,\n ...anchorProps\n }) => (\n <Button\n trailingAction={TriangleDownIcon}\n aria-labelledby={` ${ariaLabelledBy}`}\n {...anchorProps}\n >\n {children ?? 'Select Labels'}\n </Button>\n )}\n placeholderText=\"Filter labels\"\n open={open}\n onOpenChange={setOpen}\n items={filteredItems}\n selected={selected}\n onSelectedChange={setSelected}\n onFilterChange={setFilter}\n showItemDividers={true}\n overlayProps={{\n width: 'small',\n height: 'xsmall',\n }}\n />\n </>\n )\n}"
}
],
"props": [
{
"name": "title",
"type": "string | React.ReactElement",
"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
6 changes: 6 additions & 0 deletions 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 | React.ReactElement",
"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
11 changes: 6 additions & 5 deletions src/SelectPanel/SelectPanel.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, {useState} from 'react'
import {ComponentMeta} from '@storybook/react'
import {TriangleDownIcon} from '@primer/octicons-react'
import {ComponentMeta} from '@storybook/react'
import React, {useState} from 'react'

import Box from '../Box'
import {Button} from '../Button'
import {ItemInput} from '../deprecated/ActionList/List'
import {SelectPanel} from '../SelectPanel'
import Box from '../Box'
import {ItemInput} from '../deprecated/ActionList/List'

export default {
title: 'Components/SelectPanel',
Expand Down Expand Up @@ -52,12 +52,13 @@ export const Default = () => {
<h1>Multi Select Panel</h1>
<div>Please select labels that describe your issue:</div>
<SelectPanel
title="Select labels"
renderAnchor={({children, 'aria-labelledby': ariaLabelledBy, ...anchorProps}) => (
<Button trailingAction={TriangleDownIcon} aria-labelledby={` ${ariaLabelledBy}`} {...anchorProps}>
{children ?? 'Select Labels'}
</Button>
)}
placeholderText="Filter Labels"
placeholderText="Filter labels"
open={open}
onOpenChange={setOpen}
items={filteredItems}
Expand Down
58 changes: 36 additions & 22 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'
import {SearchIcon} from '@primer/octicons-react'

interface SelectPanelSingleSelection {
Expand All @@ -23,6 +26,8 @@ interface SelectPanelMultiSelection {
}

interface SelectPanelBaseProps {
// TODO: Make `title` required in the next major version
title?: string | React.ReactElement
onOpenChange: (
open: boolean,
gesture: 'anchor-click' | 'anchor-key-press' | 'click-outside' | 'escape' | 'selection',
Expand Down Expand Up @@ -59,6 +64,7 @@ export function SelectPanel({
placeholderText = 'Filter items',
inputLabel = placeholderText,
selected,
title = isMultiSelectVariant(selected) ? 'Select items' : 'Select an item',
onSelectedChange,
filterValue: externalFilterValue,
onFilterChange: externalOnFilterChange,
Expand All @@ -68,6 +74,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 @@ -159,25 +166,32 @@ 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}
placeholderText={placeholderText}
{...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}
placeholderText={placeholderText}
{...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
2 changes: 1 addition & 1 deletion src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ exports[`SelectPanel renders consistently 1`] = `
<button
aria-haspopup="true"
className="c1"
id="react-aria-1"
id="react-aria-2"
onClick={[Function]}
onKeyDown={[Function]}
tabIndex={0}
Expand Down

0 comments on commit 00cf2e3

Please sign in to comment.