diff --git a/.changeset/afraid-beds-lick.md b/.changeset/afraid-beds-lick.md new file mode 100644 index 00000000000..72fcfd7cd6c --- /dev/null +++ b/.changeset/afraid-beds-lick.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +experimental/SelectPanel + FormControl: Automatically wires SelectPanel v2 to the accessibility and validation provided by the FormControl component it's nested within diff --git a/packages/react/src/drafts/SelectPanel2/SelectPanel.examples.stories.tsx b/packages/react/src/drafts/SelectPanel2/SelectPanel.examples.stories.tsx index fff5fa47d0e..d1c314870ac 100644 --- a/packages/react/src/drafts/SelectPanel2/SelectPanel.examples.stories.tsx +++ b/packages/react/src/drafts/SelectPanel2/SelectPanel.examples.stories.tsx @@ -1,6 +1,6 @@ import React from 'react' import {SelectPanel} from './SelectPanel' -import {ActionList, ActionMenu, Avatar, Box, Button, Text, Octicon, Flash} from '../../index' +import {ActionList, ActionMenu, Avatar, Box, Button, Text, Octicon, Flash, FormControl} from '../../index' import {Dialog} from '../../drafts' import { ArrowRightIcon, @@ -846,6 +846,38 @@ export const NestedSelection = () => { ) } +export const WithinForm = () => { + const [selectedTag, setSelectedTag] = React.useState() + + const onSubmit = () => { + if (!selectedTag) return + data.ref = selectedTag // pretending to persist changes + } + + const itemsToShow = data.tags + + return ( + <> +

Within Form

+ + + SelectPanel within FormControl + + {selectedTag || 'Choose a tag'} + + + {itemsToShow.map(tag => ( + setSelectedTag(tag.id)} selected={selectedTag === tag.id}> + {tag.name} + + ))} + + + + + ) +} + // ----- Suspense implementation details ---- const cache = new Map() diff --git a/packages/react/src/drafts/SelectPanel2/SelectPanel.test.tsx b/packages/react/src/drafts/SelectPanel2/SelectPanel.test.tsx index f9c920df832..11b186d374a 100644 --- a/packages/react/src/drafts/SelectPanel2/SelectPanel.test.tsx +++ b/packages/react/src/drafts/SelectPanel2/SelectPanel.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {ThemeProvider, ActionList} from '../../' +import {ThemeProvider, ActionList, FormControl} from '../../' import type {RenderResult} from '@testing-library/react' import {render} from '@testing-library/react' import type {UserEvent} from '@testing-library/user-event' @@ -43,6 +43,15 @@ const Fixture = ({onSubmit, onCancel}: Pick + Select Panel Label + + + ) +} + describe('SelectPanel', () => { it('renders Button by default', async () => { const container = render() @@ -132,4 +141,12 @@ describe('SelectPanel', () => { expect(mockOnCancel).toHaveBeenCalledTimes(1) expect(mockOnSubmit).toHaveBeenCalledTimes(0) }) + + it('SelectPanel within FormControl should be labelled by FormControl.Label', async () => { + const component = render() + const button = component.getByLabelText('Select Panel Label') + expect(button).toBeVisible() + const buttonByRole = component.getByRole('button') + expect(button.id).toBe(buttonByRole.id) + }) }) diff --git a/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx b/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx index 20b1fc67002..4e896b8fdbf 100644 --- a/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx @@ -3,7 +3,20 @@ import {SearchIcon, XCircleFillIcon, XIcon, FilterRemoveIcon, AlertIcon, ArrowLe import {FocusKeys} from '@primer/behaviors' import type {ButtonProps, TextInputProps, ActionListProps, LinkProps, CheckboxProps} from '../../index' -import {Button, IconButton, Heading, Box, Tooltip, TextInput, Spinner, Text, Octicon, Link, Checkbox} from '../../index' +import { + Button, + IconButton, + Heading, + Box, + Tooltip, + TextInput, + Spinner, + Text, + Octicon, + Link, + Checkbox, + useFormControlForwardedProps, +} from '../../index' import {ActionListContainerContext} from '../../ActionList/ActionListContainerContext' import {useSlots} from '../../hooks/useSlots' import {useProvidedRefOrCreate, useId, useAnchoredPosition} from '../../hooks' @@ -315,7 +328,8 @@ const Panel: React.FC = ({ } const SelectPanelButton = React.forwardRef((props, anchorRef) => { - return