Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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/stupid-knives-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': patch
---

Reverted SelectPanel breaking behavioral changes
13 changes: 6 additions & 7 deletions docs/content/SelectPanel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,27 @@ const items = [
]

function DemoComponent() {
const [selected, setSelected] = React.useState([items[2], items[4]])
const [selected, setSelected] = React.useState([items[0], items[1]])
const [filter, setFilter] = React.useState('')
const filteredItems = items.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
const [open, setOpen] = React.useState(false)

return (
<SelectPanel
renderAnchor={({...anchorProps}) => (
<Button trailingIcon={TriangleDownIcon} {...anchorProps}>
Select Labels
renderAnchor={({children, 'aria-labelledby': ariaLabelledBy, ...anchorProps}) => (
<Button trailingIcon={TriangleDownIcon} aria-labelledby={` ${ariaLabelledBy}`} {...anchorProps}>
{children || 'Select Labels'}
</Button>
)}
title="Select Items"
inputLabel="Select Items"
placeholderText="Filter Labels"
open={open}
onOpenChange={setOpen}
items={filteredItems}
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'medium', height: 'medium'}}
overlayProps={{width: 'small', height: 'xsmall'}}
/>
)
}
Expand Down
52 changes: 21 additions & 31 deletions docs/content/deprecated/ActionList.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,25 @@ Use [new version of ActionList](/ActionList) with composable API, design updates

```jsx
<ActionList
role="listbox"
items={[
{text: 'New file', role: 'option'},
{text: 'Copy link', role: 'option'},
{text: 'Edit file', role: 'option'},
{text: 'New file'},
{text: 'Copy link'},
{text: 'Edit file'},
ActionList.Divider,
{text: 'Delete file', variant: 'danger', role: 'option'}
{text: 'Delete file', variant: 'danger'}
]}
/>
```

**After**

```jsx
<ActionList role="listbox">
<ActionList.Item role="option">New file</ActionList.Item>
<ActionList.Item role="option">Copy link</ActionList.Item>
<ActionList.Item role="option">Edit file</ActionList.Item>
<ActionList>
<ActionList.Item>New file</ActionList.Item>
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Edit file</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger" role="option">Delete file</ActionList.Item>
<ActionList.Item variant="danger">Delete file</ActionList.Item>
</ActionList>
```

Expand All @@ -47,13 +46,12 @@ import {ActionList} from '@primer/react/deprecated'

```jsx live deprecated
<ActionList
role="listbox"
items={[
{text: 'New file', role: 'option'},
{text: 'New file'},
ActionList.Divider,
{text: 'Copy link', role: 'option'},
{text: 'Edit file', role: 'option'},
{text: 'Delete file', variant: 'danger', role: 'option'}
{text: 'Copy link'},
{text: 'Edit file'},
{text: 'Delete file', variant: 'danger'}
]}
/>
```
Expand All @@ -62,7 +60,6 @@ import {ActionList} from '@primer/react/deprecated'

```jsx live deprecated
<ActionList
role="listbox"
groupMetadata={[
{groupId: '0'},
{groupId: '1', header: {title: 'Live query', variant: 'subtle'}},
Expand All @@ -71,37 +68,34 @@ import {ActionList} from '@primer/react/deprecated'
{groupId: '4'}
]}
items={[
{key: '1', leadingVisual: TypographyIcon, text: 'Rename', groupId: '0', trailingVisual: '⌘R', role: 'option'},
{key: '2', leadingVisual: VersionsIcon, text: 'Duplicate', groupId: '0', role: 'option'},
{key: '3', leadingVisual: SearchIcon, text: 'repo:github/github', groupId: '1', role: 'option'},
{key: '1', leadingVisual: TypographyIcon, text: 'Rename', groupId: '0', trailingVisual: '⌘R'},
{key: '2', leadingVisual: VersionsIcon, text: 'Duplicate', groupId: '0'},
{key: '3', leadingVisual: SearchIcon, text: 'repo:github/github', groupId: '1'},
{
key: '4',
leadingVisual: NoteIcon,
text: 'Table',
description: 'Information-dense table optimized for operations across teams',
descriptionVariant: 'block',
groupId: '2',
role: 'option'
groupId: '2'
},
{
key: '5',
leadingVisual: ProjectIcon,
text: 'Board',
description: 'Kanban-style board focused on visual states',
descriptionVariant: 'block',
groupId: '2',
role: 'option'
groupId: '2'
},
{
key: '6',
leadingVisual: FilterIcon,
text: 'Save sort and filters to current view',
disabled: true,
groupId: '3',
role: 'option'
groupId: '3'
},
{key: '7', leadingVisual: FilterIcon, text: 'Save sort and filters to new view', groupId: '3', role: 'option'},
{key: '8', leadingVisual: GearIcon, text: 'View settings', groupId: '4', trailingVisual: ArrowRightIcon, role: 'option'}
{key: '7', leadingVisual: FilterIcon, text: 'Save sort and filters to new view', groupId: '3'},
{key: '8', leadingVisual: GearIcon, text: 'View settings', groupId: '4', trailingVisual: ArrowRightIcon}
]}
/>
```
Expand All @@ -110,21 +104,17 @@ import {ActionList} from '@primer/react/deprecated'

```jsx deprecated
<ActionList
role="listbox"
items={[
{
text: 'Vanilla link',
role: 'option',
renderItem: props => <ActionList.Item as="a" href="/about" {...props} />
},
{
text: 'React Router link',
role: 'option',
renderItem: props => <ActionList.Item as={ReactRouterLikeLink} to="/about" {...props} />
},
{
text: 'NextJS style',
role: 'option',
renderItem: props => (
<NextJSLikeLink href="/about">
<ActionList.Item as="a" {...props} />
Expand Down
2 changes: 0 additions & 2 deletions src/Autocomplete/AutocompleteMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ function AutocompleteMenu<T extends AutocompleteItemProps>(props: AutocompleteMe
items.map(selectableItem => {
return {
...selectableItem,
_legacyEnterSupport: true, //TODO: Change behaviour, the enter key should not be used here.
role: 'option',
id: selectableItem.id,
selected: selectionVariant === 'multiple' ? selectedItemIds.includes(selectableItem.id) : undefined,
Expand Down Expand Up @@ -220,7 +219,6 @@ function AutocompleteMenu<T extends AutocompleteItemProps>(props: AutocompleteMe
? [
{
...addNewItem,
_legacyEnterSupport: true, //TODO: Change behaviour, the enter key should not be used here.
leadingVisual: () => <PlusIcon />,
onAction: (item: T) => {
// TODO: make it possible to pass a leadingVisual when using `addNewItem`
Expand Down
32 changes: 26 additions & 6 deletions src/FilteredActionList/FilteredActionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import TextInput, {TextInputProps} from '../TextInput'
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 {get} from '../constants'
Expand All @@ -13,7 +14,6 @@ import useScrollFlash from '../hooks/useScrollFlash'
import {scrollIntoView} from '@primer/behaviors'
import type {ScrollIntoViewOptions} from '@primer/behaviors'
import {SxProp} from '../sx'
import VisuallyHidden from '../_VisuallyHidden'

const menuScrollMargins: ScrollIntoViewOptions = {startMargin: 0, endMargin: 8}

Expand All @@ -22,7 +22,7 @@ export interface FilteredActionListProps
ListPropsBase,
SxProp {
loading?: boolean
placeholderText?: string
placeholderText: string
filterValue?: string
onFilterChange: (value: string, e: React.ChangeEvent<HTMLInputElement>) => void
textInputProps?: Partial<Omit<TextInputProps, 'onChange'>>
Expand Down Expand Up @@ -56,10 +56,10 @@ export function FilteredActionList({
)

const scrollContainerRef = useRef<HTMLDivElement>(null)
const listContainerRef = useRef<HTMLDivElement>(null)
const inputRef = useProvidedRefOrCreate<HTMLInputElement>(providedInputRef)
const activeDescendantRef = useRef<HTMLElement>()
const listId = useSSRSafeId()
const inputDescriptionTextId = useSSRSafeId()
const onInputKeyPress: KeyboardEventHandler = useCallback(
event => {
if (event.key === 'Enter' && activeDescendantRef.current) {
Expand All @@ -74,6 +74,28 @@ export function FilteredActionList({
[activeDescendantRef]
)

useFocusZone(
{
containerRef: listContainerRef,
focusOutBehavior: 'wrap',
focusableElementFilter: element => {
return !(element instanceof HTMLInputElement)
},
activeDescendantFocus: inputRef,
onActiveDescendantChanged: (current, previous, directlyActivated) => {
activeDescendantRef.current = current

if (current && scrollContainerRef.current && directlyActivated) {
scrollIntoView(current, scrollContainerRef.current, menuScrollMargins)
}
}
},
[
// List ref isn't set while loading. Need to re-bind focus zone when it changes
loading
]
)

useEffect(() => {
// if items changed, we want to instantly move active descendant into view
if (activeDescendantRef.current && scrollContainerRef.current) {
Expand All @@ -97,18 +119,16 @@ export function FilteredActionList({
placeholder={placeholderText}
aria-label={placeholderText}
aria-controls={listId}
aria-describedby={inputDescriptionTextId}
{...textInputProps}
/>
<VisuallyHidden id={inputDescriptionTextId}>Items will be filtered as you type</VisuallyHidden>
</StyledHeader>
<Box ref={scrollContainerRef} overflow="auto">
{loading ? (
<Box width="100%" display="flex" flexDirection="row" justifyContent="center" pt={6} pb={7}>
<Spinner />
</Box>
) : (
<ActionList items={items} {...listProps} role="listbox" id={listId} />
<ActionList ref={listContainerRef} items={items} {...listProps} role="listbox" id={listId} />
)}
</Box>
</Box>
Expand Down
Loading