From 56c9dd2b022f78fb9905fad86a1b64873a0bfb1b Mon Sep 17 00:00:00 2001 From: Cole Bemis Date: Wed, 24 May 2023 09:10:04 -0700 Subject: [PATCH] SelectPanel: Add filter input label and description (#3312) * Make placeholderText prop optional * Add `inputLabel` prop * Add filter input description * Create early-beds-whisper.md * Document inputLabel prop * Update src/SelectPanel/SelectPanel.docs.json * Update generated/components.json --------- Co-authored-by: colebemis --- .changeset/early-beds-whisper.md | 5 ++++ generated/components.json | 6 +++++ src/FilteredActionList/FilteredActionList.tsx | 24 +++++++++++-------- src/SelectPanel/SelectPanel.docs.json | 8 ++++++- src/SelectPanel/SelectPanel.tsx | 10 +++++++- 5 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 .changeset/early-beds-whisper.md diff --git a/.changeset/early-beds-whisper.md b/.changeset/early-beds-whisper.md new file mode 100644 index 00000000000..b8e530c6b6c --- /dev/null +++ b/.changeset/early-beds-whisper.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +SelectPanel: Add filter input label and description diff --git a/generated/components.json b/generated/components.json index b6e0488b3fe..7da51136a31 100644 --- a/generated/components.json +++ b/generated/components.json @@ -3655,6 +3655,12 @@ "defaultValue": "", "description": "" }, + { + "name": "inputLabel", + "type": "string", + "defaultValue": "Same as placeholderText", + "description": "The aria-label for the filter input" + }, { "name": "overlayProps", "type": "Partial", diff --git a/src/FilteredActionList/FilteredActionList.tsx b/src/FilteredActionList/FilteredActionList.tsx index 7a6cf8ef007..00fc8ae8bb6 100644 --- a/src/FilteredActionList/FilteredActionList.tsx +++ b/src/FilteredActionList/FilteredActionList.tsx @@ -1,19 +1,20 @@ +import type {ScrollIntoViewOptions} from '@primer/behaviors' +import {scrollIntoView} from '@primer/behaviors' import React, {KeyboardEventHandler, useCallback, useEffect, useRef} from 'react' -import {GroupedListProps, ListPropsBase} from '../deprecated/ActionList/List' -import TextInput, {TextInputProps} from '../TextInput' +import styled from 'styled-components' import Box from '../Box' -import {ActionList} from '../deprecated/ActionList' import Spinner from '../Spinner' -import {useFocusZone} from '../hooks/useFocusZone' -import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate' -import styled from 'styled-components' +import TextInput, {TextInputProps} from '../TextInput' import {get} from '../constants' +import {ActionList} from '../deprecated/ActionList' +import {GroupedListProps, ListPropsBase} from '../deprecated/ActionList/List' +import {useFocusZone} from '../hooks/useFocusZone' +import {useId} from '../hooks/useId' import {useProvidedRefOrCreate} from '../hooks/useProvidedRefOrCreate' +import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate' import useScrollFlash from '../hooks/useScrollFlash' -import {scrollIntoView} from '@primer/behaviors' -import type {ScrollIntoViewOptions} from '@primer/behaviors' +import {VisuallyHidden} from '../internal/components/VisuallyHidden' import {SxProp} from '../sx' -import {useId} from '../hooks/useId' const menuScrollMargins: ScrollIntoViewOptions = {startMargin: 0, endMargin: 8} @@ -22,7 +23,7 @@ export interface FilteredActionListProps ListPropsBase, SxProp { loading?: boolean - placeholderText: string + placeholderText?: string filterValue?: string onFilterChange: (value: string, e: React.ChangeEvent) => void textInputProps?: Partial> @@ -60,6 +61,7 @@ export function FilteredActionList({ const inputRef = useProvidedRefOrCreate(providedInputRef) const activeDescendantRef = useRef() const listId = useId() + const inputDescriptionTextId = useId() const onInputKeyPress: KeyboardEventHandler = useCallback( event => { if (event.key === 'Enter' && activeDescendantRef.current) { @@ -119,9 +121,11 @@ export function FilteredActionList({ placeholder={placeholderText} aria-label={placeholderText} aria-controls={listId} + aria-describedby={inputDescriptionTextId} {...textInputProps} /> + Items will be filtered as you type {loading ? ( diff --git a/src/SelectPanel/SelectPanel.docs.json b/src/SelectPanel/SelectPanel.docs.json index 2eeb4498d60..c901d755df4 100644 --- a/src/SelectPanel/SelectPanel.docs.json +++ b/src/SelectPanel/SelectPanel.docs.json @@ -18,6 +18,12 @@ "defaultValue": "", "description": "" }, + { + "name": "inputLabel", + "type": "string", + "defaultValue": "Same as placeholderText", + "description": "The aria-label for the filter input" + }, { "name": "overlayProps", "type": "Partial", @@ -56,4 +62,4 @@ } ], "subcomponents": [] -} \ No newline at end of file +} diff --git a/src/SelectPanel/SelectPanel.tsx b/src/SelectPanel/SelectPanel.tsx index b8604f0a344..e7c27fd5992 100644 --- a/src/SelectPanel/SelectPanel.tsx +++ b/src/SelectPanel/SelectPanel.tsx @@ -10,6 +10,7 @@ import {TextInputProps} from '../TextInput' import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate' import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay' import {useProvidedRefOrCreate} from '../hooks' +import {SearchIcon} from '@primer/octicons-react' interface SelectPanelSingleSelection { selected: ItemInput | undefined @@ -27,6 +28,8 @@ interface SelectPanelBaseProps { gesture: 'anchor-click' | 'anchor-key-press' | 'click-outside' | 'escape' | 'selection', ) => void placeholder?: string + // TODO: Make `inputLabel` required in next major version + inputLabel?: string overlayProps?: Partial } @@ -53,6 +56,8 @@ export function SelectPanel({ renderAnchor = props => , anchorRef: externalAnchorRef, placeholder, + placeholderText = 'Filter items', + inputLabel = placeholderText, selected, onSelectedChange, filterValue: externalFilterValue, @@ -141,9 +146,11 @@ export function SelectPanel({ return { sx: {m: 2}, contrast: true, + leadingVisual: SearchIcon, + 'aria-label': inputLabel, ...textInputProps, } - }, [textInputProps]) + }, [inputLabel, textInputProps]) return (