Skip to content

Commit

Permalink
Refactor FilteredActionList to address a11y violations and use new Ac…
Browse files Browse the repository at this point in the history
…tionList. (#3247)

* Update FilteredActionList to use non-deprecated ActionList.

* Use non-deprecated ActionList in FilteredActionList.

* Fix a11y issues in FilteredActionList story.

* Add prop to hide selection component if needed.

* Remove unused hook import.

* Get SavedReplies to look as it did with deprecated ActionList.

* Fix failing test.

* Create weak-jokes-chew.md

* Update generated/components.json

* Fix themePreval snapshot.

* Linting fixes.

* Fix type-check errors.

* Update themePreval snapshot again.

* Fix themePreval snapshot to match origin. Unsure why it's not generating the same way.

* Hide selections in MarkdownEditor saved replies.

* Remove hideSelection prop and add defaultRenderFn to FilteredActionList so these don't have to be defined manually everywhere.

* Fix selection rendering (needed explicit selected boolean) and fix SelectPanel docs.

* Pass selectionVariant illegally to SelectPanel in src/MarkdownEditor/_SavedReplies.tsx so Selection components don't render.

* Remove remaining references of hideSelection prop.

* Update changeset to reflect that changes impact SelectPanel.

* Remove renderFn prop from SelectPanel and use default for FilteredActionList, which seems to cover most cases.

* Fix truncation in SavedReplies descriptions.

* Update generated/components.json

* Fix linting error.

* Don't make renderFn a prop (if we need to make this configurable, we can expose it later. Use maxWidth 100% for SavedReplies truncation.

* Use showDividers prop in SelectPanel story.

* Formatting.

* Add temporary support for showItemDividers prop to SelectPanel to keep backwards compatibility.

* Support passing deprecated showItemDividers prop in ActionList.

---------

Co-authored-by: radglob <radglob@users.noreply.github.com>
radglob and radglob authored May 26, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 1fd6d32 commit e865e3e
Showing 12 changed files with 165 additions and 127 deletions.
5 changes: 5 additions & 0 deletions .changeset/weak-jokes-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

FilteredActionList now uses new ActionList as a base, and SelectPanel reflects those changes.
26 changes: 12 additions & 14 deletions docs/content/SelectPanel.mdx
Original file line number Diff line number Diff line change
@@ -12,20 +12,18 @@ A `SelectPanel` provides an anchor that will open an overlay with a list of sele

```javascript live noinline
function getColorCircle(color) {
return function () {
return (
<Box
borderWidth="1px"
borderStyle="solid"
bg={color}
borderColor={color}
width={14}
height={14}
borderRadius={10}
margin="auto"
/>
)
}
return (
<Box
borderWidth="1px"
borderStyle="solid"
bg={color}
borderColor={color}
width={14}
height={14}
borderRadius={10}
margin="auto"
/>
)
}

const items = [
2 changes: 1 addition & 1 deletion generated/components.json
Original file line number Diff line number Diff line change
@@ -3626,7 +3626,7 @@
"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 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}"
"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 overlayProps={{\n width: 'small',\n height: 'xsmall',\n }}\n />\n </>\n )\n}"
}
],
"props": [
3 changes: 2 additions & 1 deletion src/ActionList/List.tsx
Original file line number Diff line number Diff line change
@@ -69,7 +69,8 @@ export const List = React.forwardRef<HTMLUListElement, ActionListProps>(
value={{
variant,
selectionVariant: selectionVariant || containerSelectionVariant,
showDividers,
// @ts-ignore showItemDividers may be passed by some components until next major.
showDividers: showDividers || !!props.showItemDividers,
role: role || listRole,
headingId,
}}
46 changes: 22 additions & 24 deletions src/FilteredActionList/FilteredActionList.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Meta} from '@storybook/react'
import React from 'react'
import {ThemeProvider} from '..'
import {FilteredActionList} from '../FilteredActionList'
import {FilteredActionList, ItemInput} from '../FilteredActionList'
import BaseStyles from '../BaseStyles'
import Box from '../Box'

@@ -26,35 +26,33 @@ const meta: Meta = {
export default meta

function getColorCircle(color: string) {
return function () {
return (
<Box
bg={color}
borderColor={color}
width={14}
height={14}
borderRadius={10}
margin="auto"
borderWidth="1px"
borderStyle="solid"
/>
)
}
return (
<Box
bg={color}
borderColor={color}
width={14}
height={14}
borderRadius={10}
margin="auto"
borderWidth="1px"
borderStyle="solid"
/>
)
}

const items = [
{leadingVisual: getColorCircle('#a2eeef'), text: 'enhancement', id: 1},
{leadingVisual: getColorCircle('#d73a4a'), text: 'bug', id: 2},
{leadingVisual: getColorCircle('#0cf478'), text: 'good first issue', id: 3},
{leadingVisual: getColorCircle('#ffd78e'), text: 'design', id: 4},
{leadingVisual: getColorCircle('#ff0000'), text: 'blocker', id: 5},
{leadingVisual: getColorCircle('#a4f287'), text: 'backend', id: 6},
{leadingVisual: getColorCircle('#8dc6fc'), text: 'frontend', id: 7},
]
{leadingVisual: getColorCircle('#a2eeef'), text: 'enhancement', id: '1'},
{leadingVisual: getColorCircle('#d73a4a'), text: 'bug', id: '2'},
{leadingVisual: getColorCircle('#0cf478'), text: 'good first issue', id: '3'},
{leadingVisual: getColorCircle('#ffd78e'), text: 'design', id: '4'},
{leadingVisual: getColorCircle('#ff0000'), text: 'blocker', id: '5'},
{leadingVisual: getColorCircle('#a4f287'), text: 'backend', id: '6'},
{leadingVisual: getColorCircle('#8dc6fc'), text: 'frontend', id: '7'},
] as ItemInput[]

export function Default(): JSX.Element {
const [filter, setFilter] = React.useState('')
const filteredItems = items.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
const filteredItems = items.filter(item => item.text?.toLowerCase().startsWith(filter.toLowerCase()))

return (
<>
70 changes: 54 additions & 16 deletions src/FilteredActionList/FilteredActionList.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,70 @@
import type {ScrollIntoViewOptions} from '@primer/behaviors'
import {scrollIntoView} from '@primer/behaviors'
import React, {KeyboardEventHandler, useCallback, useEffect, useRef} from 'react'
import styled from 'styled-components'
import TextInput, {TextInputProps} from '../TextInput'
import Box from '../Box'
import {ActionList, ActionListProps, ActionListItemProps} from '../ActionList'
import Spinner from '../Spinner'
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 styled from 'styled-components'
import {get} from '../constants'
import {useProvidedRefOrCreate} from '../hooks/useProvidedRefOrCreate'
import useScrollFlash from '../hooks/useScrollFlash'
import {scrollIntoView} from '@primer/behaviors'
import type {ScrollIntoViewOptions} from '@primer/behaviors'
import {useId} from '../hooks/useId'
import {VisuallyHidden} from '../internal/components/VisuallyHidden'
import {SxProp} from '../sx'

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

export interface FilteredActionListProps
extends Partial<Omit<GroupedListProps, keyof ListPropsBase>>,
ListPropsBase,
SxProp {
export type ItemInput = Partial<
ActionListItemProps & {
description?: string | React.ReactElement
descriptionVariant?: 'inline' | 'block'
leadingVisual?: JSX.Element
onAction?: (itemFromAction: ItemInput, event: React.MouseEvent) => void
selected?: boolean
text?: string
trailingVisual?: string
}
>

export interface FilteredActionListProps extends ActionListProps, SxProp {
loading?: boolean
placeholderText?: string
filterValue?: string
onFilterChange: (value: string, e: React.ChangeEvent<HTMLInputElement>) => void
textInputProps?: Partial<Omit<TextInputProps, 'onChange'>>
inputRef?: React.RefObject<HTMLInputElement>
items: ItemInput[]
}

const StyledHeader = styled.div`
box-shadow: 0 1px 0 ${get('colors.border.default')};
z-index: 1;
`

const renderFn = ({
description,
descriptionVariant,
id,
sx,
text,
trailingVisual,
leadingVisual,
onSelect,
selected,
}: ItemInput): React.ReactElement => {
return (
<ActionList.Item key={id} sx={sx} role="option" onSelect={onSelect} selected={selected}>
{!!leadingVisual && <ActionList.LeadingVisual>{leadingVisual}</ActionList.LeadingVisual>}
<Box>{text ? text : null}</Box>
{description ? <ActionList.Description variant={descriptionVariant}>{description}</ActionList.Description> : null}
{!!trailingVisual && <ActionList.TrailingVisual>{trailingVisual}</ActionList.TrailingVisual>}
</ActionList.Item>
)
}

export function FilteredActionList({
loading = false,
placeholderText,
@@ -57,7 +87,7 @@ export function FilteredActionList({
)

const scrollContainerRef = useRef<HTMLDivElement>(null)
const listContainerRef = useRef<HTMLDivElement>(null)
const listContainerRef = useRef<HTMLUListElement>(null)
const inputRef = useProvidedRefOrCreate<HTMLInputElement>(providedInputRef)
const activeDescendantRef = useRef<HTMLElement>()
const listId = useId()
@@ -84,7 +114,7 @@ export function FilteredActionList({
return !(element instanceof HTMLInputElement)
},
activeDescendantFocus: inputRef,
onActiveDescendantChanged: (current, previous, directlyActivated) => {
onActiveDescendantChanged: (current, _previous, directlyActivated) => {
activeDescendantRef.current = current

if (current && scrollContainerRef.current && directlyActivated) {
@@ -132,7 +162,15 @@ export function FilteredActionList({
<Spinner />
</Box>
) : (
<ActionList ref={listContainerRef} items={items} {...listProps} role="listbox" id={listId} />
<ActionList
ref={listContainerRef}
{...listProps}
role="listbox"
id={listId}
aria-label={`${placeholderText} options`}
>
{items.map(i => renderFn(i))}
</ActionList>
)}
</Box>
</Box>
2 changes: 1 addition & 1 deletion src/FilteredActionList/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {FilteredActionList} from './FilteredActionList'
export type {FilteredActionListProps} from './FilteredActionList'
export type {FilteredActionListProps, ItemInput} from './FilteredActionList'
50 changes: 21 additions & 29 deletions src/SelectPanel/SelectPanel.features.stories.tsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import {ComponentMeta} from '@storybook/react'

import Box from '../Box'
import {Button} from '../Button'
import {ItemInput} from '../deprecated/ActionList/List'
import {ItemInput} from '../FilteredActionList'
import {SelectPanel} from './SelectPanel'
import {TriangleDownIcon} from '@primer/octicons-react'
import type {OverlayProps} from '../Overlay'
@@ -14,30 +14,28 @@ export default {
} as ComponentMeta<typeof SelectPanel>

function getColorCircle(color: string) {
return function () {
return (
<Box
bg={color}
borderColor={color}
width={14}
height={14}
borderRadius={10}
margin="auto"
borderWidth="1px"
borderStyle="solid"
/>
)
}
return (
<Box
bg={color}
borderColor={color}
width={14}
height={14}
borderRadius={10}
margin="auto"
borderWidth="1px"
borderStyle="solid"
/>
)
}

const items = [
{leadingVisual: getColorCircle('#a2eeef'), text: 'enhancement', id: 1},
{leadingVisual: getColorCircle('#d73a4a'), text: 'bug', id: 2},
{leadingVisual: getColorCircle('#0cf478'), text: 'good first issue', id: 3},
{leadingVisual: getColorCircle('#ffd78e'), text: 'design', id: 4},
{leadingVisual: getColorCircle('#ff0000'), text: 'blocker', id: 5},
{leadingVisual: getColorCircle('#a4f287'), text: 'backend', id: 6},
{leadingVisual: getColorCircle('#8dc6fc'), text: 'frontend', id: 7},
{leadingVisual: getColorCircle('#a2eeef'), text: 'enhancement', id: '1'},
{leadingVisual: getColorCircle('#d73a4a'), text: 'bug', id: '2'},
{leadingVisual: getColorCircle('#0cf478'), text: 'good first issue', id: '3'},
{leadingVisual: getColorCircle('#ffd78e'), text: 'design', id: '4'},
{leadingVisual: getColorCircle('#ff0000'), text: 'blocker', id: '5'},
{leadingVisual: getColorCircle('#a4f287'), text: 'backend', id: '6'},
{leadingVisual: getColorCircle('#8dc6fc'), text: 'frontend', id: '7'},
]

export const SingleSelectStory = () => {
@@ -63,6 +61,7 @@ export const SingleSelectStory = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showDividers={true}
showItemDividers={true}
overlayProps={{width: 'small', height: 'xsmall'}}
/>
@@ -94,7 +93,6 @@ export const ExternalAnchorStory = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'small', height: 'xsmall'}}
/>
</>
@@ -125,7 +123,6 @@ export const SelectPanelHeightInitialWithOverflowingItemsStory = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'small', height: 'initial', maxHeight: 'xsmall'}}
/>
</>
@@ -157,7 +154,6 @@ export const SelectPanelHeightInitialWithUnderflowingItemsStory = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'small', height: 'initial', maxHeight: 'xsmall'}}
/>
</>
@@ -202,7 +198,6 @@ export const SelectPanelHeightInitialWithUnderflowingItemsAfterFetch = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'small', height, maxHeight: 'xsmall'}}
/>
</>
@@ -234,7 +229,6 @@ export const SelectPanelAboveTallBody = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'small', height: 'xsmall'}}
/>
<div
@@ -276,7 +270,6 @@ export const SelectPanelHeightAndScroll = () => {
selected={selectedA}
onSelectedChange={setSelectedA}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{height: 'medium'}}
/>
<h2>With height:auto, maxheight:medium</h2>
@@ -293,7 +286,6 @@ export const SelectPanelHeightAndScroll = () => {
selected={selectedB}
onSelectedChange={setSelectedB}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{
height: 'auto',
maxHeight: 'medium',
49 changes: 23 additions & 26 deletions src/SelectPanel/SelectPanel.stories.tsx
Original file line number Diff line number Diff line change
@@ -2,43 +2,41 @@ 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 {SelectPanel} from '../SelectPanel'
import {ItemInput} from '../deprecated/ActionList/List'
import Box from '../Box'
import {ItemInput} from '../FilteredActionList'

export default {
title: 'Components/SelectPanel',
component: SelectPanel,
} as ComponentMeta<typeof SelectPanel>

function getColorCircle(color: string) {
return function () {
return (
<Box
sx={{
backgroundColor: color,
borderColor: color,
width: 14,
height: 14,
borderRadius: 10,
margin: 'auto',
borderWidth: '1px',
borderStyle: 'solid',
}}
/>
)
}
return (
<Box
sx={{
backgroundColor: color,
borderColor: color,
width: 14,
height: 14,
borderRadius: 10,
margin: 'auto',
borderWidth: '1px',
borderStyle: 'solid',
}}
/>
)
}

const items = [
{leadingVisual: getColorCircle('#a2eeef'), text: 'enhancement', id: 1},
{leadingVisual: getColorCircle('#d73a4a'), text: 'bug', id: 2},
{leadingVisual: getColorCircle('#0cf478'), text: 'good first issue', id: 3},
{leadingVisual: getColorCircle('#ffd78e'), text: 'design', id: 4},
{leadingVisual: getColorCircle('#ff0000'), text: 'blocker', id: 5},
{leadingVisual: getColorCircle('#a4f287'), text: 'backend', id: 6},
{leadingVisual: getColorCircle('#8dc6fc'), text: 'frontend', id: 7},
{leadingVisual: getColorCircle('#a2eeef'), text: 'enhancement', id: '1'},
{leadingVisual: getColorCircle('#d73a4a'), text: 'bug', id: '2'},
{leadingVisual: getColorCircle('#0cf478'), text: 'good first issue', id: '3'},
{leadingVisual: getColorCircle('#ffd78e'), text: 'design', id: '4'},
{leadingVisual: getColorCircle('#ff0000'), text: 'blocker', id: '5'},
{leadingVisual: getColorCircle('#a4f287'), text: 'backend', id: '6'},
{leadingVisual: getColorCircle('#8dc6fc'), text: 'frontend', id: '7'},
]

export const Default = () => {
@@ -65,7 +63,6 @@ export const Default = () => {
selected={selected}
onSelectedChange={setSelected}
onFilterChange={setFilter}
showItemDividers={true}
overlayProps={{width: 'small', height: 'xsmall'}}
/>
</>
2 changes: 1 addition & 1 deletion src/SelectPanel/SelectPanel.test.tsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import theme from '../theme'
import {SelectPanel} from '../SelectPanel'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {BaseStyles, SSRProvider, ThemeProvider} from '..'
import {ItemInput} from '../deprecated/ActionList/List'
import {ItemInput} from '../FilteredActionList'

expect.extend(toHaveNoViolations)

23 changes: 10 additions & 13 deletions src/SelectPanel/SelectPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import {SearchIcon} from '@primer/octicons-react'
import React, {useCallback, useMemo} from 'react'
import {FilteredActionList, FilteredActionListProps, ItemInput} from '../FilteredActionList'
import {OverlayProps} from '../Overlay'
import {FocusZoneHookSettings} from '../hooks/useFocusZone'
import {DropdownButton} from '../deprecated/DropdownMenu'
import {ActionListItemProps} from '../ActionList'
import {AnchoredOverlay, AnchoredOverlayProps} from '../AnchoredOverlay'
import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay'
import Box from '../Box'
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 {DropdownButton} from '../deprecated/DropdownMenu'
import {useProvidedRefOrCreate} from '../hooks'
import {FocusZoneHookSettings} from '../hooks/useFocusZone'
import {useId} from '../hooks/useId'
import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
import {LiveRegion, LiveRegionOutlet, Message} from '../internal/components/LiveRegion'
@@ -43,7 +42,8 @@ export type SelectPanelProps = SelectPanelBaseProps &
Omit<FilteredActionListProps, 'selectionVariant'> &
Pick<AnchoredOverlayProps, 'open'> &
AnchoredOverlayWrapperAnchorProps &
(SelectPanelSingleSelection | SelectPanelMultiSelection)
// TODO: 23-05-23 - Remove showItemDividers after next-major release
(SelectPanelSingleSelection | SelectPanelMultiSelection) & {showItemDividers?: boolean}

function isMultiSelectVariant(
selected: SelectPanelSingleSelection['selected'] | SelectPanelMultiSelection['selected'],
@@ -120,9 +120,7 @@ export function SelectPanel({
...item,
role: 'option',
selected: 'selected' in item && item.selected === undefined ? undefined : isItemSelected,
onAction: (itemFromAction, event) => {
item.onAction?.(itemFromAction, event)

onSelect: (event: React.MouseEvent | React.KeyboardEvent) => {
if (event.defaultPrevented) {
return
}
@@ -141,7 +139,7 @@ export function SelectPanel({
singleSelectOnChange(item === selected ? undefined : item)
onClose('selection')
},
} as ItemProps
} as ActionListItemProps
})
}, [onClose, onSelectedChange, items, selected])

@@ -191,11 +189,10 @@ export function SelectPanel({
<FilteredActionList
filterValue={filterValue}
onFilterChange={onFilterChange}
placeholderText={placeholderText}
{...listProps}
role="listbox"
aria-multiselectable={isMultiSelectVariant(selected) ? 'true' : 'false'}
selectionVariant={isMultiSelectVariant(selected) ? 'multiple' : 'single'}
{...listProps}
items={itemsToRender}
textInputProps={extendedTextInputProps}
inputRef={inputRef}
14 changes: 13 additions & 1 deletion src/drafts/MarkdownEditor/_SavedReplies.tsx
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import React, {
} from 'react'
import {SelectPanel, SelectPanelProps} from '../../SelectPanel'
import {ToolbarButton} from './Toolbar'
import Truncate from '../../Truncate'

export type SavedReply = {
name: string
@@ -53,7 +54,11 @@ export const SavedRepliesButton = () => {
.map(
(reply, i): Item => ({
text: reply.name,
description: reply.content,
description: (
<Truncate maxWidth="100%" title={reply.content}>
{reply.content}
</Truncate>
),
descriptionVariant: 'block',
trailingVisual: i < 9 ? `Ctrl + ${i + 1}` : undefined,
sx: {
@@ -66,6 +71,7 @@ export const SavedRepliesButton = () => {
maxWidth: '100%',
},
},
id: i.toString(),
}),
)

@@ -105,6 +111,12 @@ export const SavedRepliesButton = () => {
onSelectItem(Array.isArray(selection) ? selection[0] : selection)
}}
overlayProps={{width: 'small', maxHeight: 'small', anchorSide: 'outside-right', onKeyDown}}
// @ts-ignore this is bad because SelectPanel does not accept selectionVariant in the public API
// but it does pass it down to FilteredActionList underneath.
// SavedReplies should not use SelectPanel and override it's semantics, it should instead
// use the building blocks of SelectPanel to build a new component
selectionVariant={undefined}
aria-multiselectable={undefined}
/>
) : (
<></>

0 comments on commit e865e3e

Please sign in to comment.