diff --git a/.changeset/small-queens-vanish.md b/.changeset/small-queens-vanish.md new file mode 100644 index 00000000000..b76ff7db82a --- /dev/null +++ b/.changeset/small-queens-vanish.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +SelectPanel: Add `title` prop diff --git a/generated/components.json b/generated/components.json index 7da51136a31..038c99aca56 100644 --- a/generated/components.json +++ b/generated/components.json @@ -3638,10 +3638,16 @@ "stories": [ { "id": "components-selectpanel--default", - "code": "() => {\n const [selected, setSelected] = React.useState([\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

Multi Select Panel

\n
Please select labels that describe your issue:
\n (\n \n {children ?? 'Select Labels'}\n \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([\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

Multi Select Panel

\n
Please select labels that describe your issue:
\n (\n \n {children ?? 'Select Labels'}\n \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", diff --git a/src/SelectPanel/SelectPanel.docs.json b/src/SelectPanel/SelectPanel.docs.json index c901d755df4..7e34e9c294a 100644 --- a/src/SelectPanel/SelectPanel.docs.json +++ b/src/SelectPanel/SelectPanel.docs.json @@ -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", diff --git a/src/SelectPanel/SelectPanel.stories.tsx b/src/SelectPanel/SelectPanel.stories.tsx index 6b08629ce38..1ab7d29b47b 100644 --- a/src/SelectPanel/SelectPanel.stories.tsx +++ b/src/SelectPanel/SelectPanel.stories.tsx @@ -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', @@ -52,12 +52,13 @@ export const Default = () => {

Multi Select Panel

Please select labels that describe your issue:
( )} - placeholderText="Filter Labels" + placeholderText="Filter labels" open={open} onOpenChange={setOpen} items={filteredItems} diff --git a/src/SelectPanel/SelectPanel.tsx b/src/SelectPanel/SelectPanel.tsx index e7c27fd5992..e89c351280a 100644 --- a/src/SelectPanel/SelectPanel.tsx +++ b/src/SelectPanel/SelectPanel.tsx @@ -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 { @@ -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', @@ -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, @@ -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) => { @@ -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} > - + + + + {title} + + + + ) } diff --git a/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap b/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap index 16c8e7f01b1..223426357d3 100644 --- a/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap +++ b/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap @@ -87,7 +87,7 @@ exports[`SelectPanel renders consistently 1`] = `