diff --git a/public/app/core/components/NestedFolderPicker/NestedFolderList.tsx b/public/app/core/components/NestedFolderPicker/NestedFolderList.tsx index 53d0d17599f..78fad030400 100644 --- a/public/app/core/components/NestedFolderPicker/NestedFolderList.tsx +++ b/public/app/core/components/NestedFolderPicker/NestedFolderList.tsx @@ -1,4 +1,4 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import React, { useCallback, useId, useMemo, useRef } from 'react'; import Skeleton from 'react-loading-skeleton'; import { FixedSizeList as List } from 'react-window'; @@ -10,31 +10,38 @@ import { getSvgSize } from '@grafana/ui/src/components/Icon/utils'; import { Text } from '@grafana/ui/src/components/Text/Text'; import { Trans } from 'app/core/internationalization'; import { Indent } from 'app/features/browse-dashboards/components/Indent'; +import { childrenByParentUIDSelector, rootItemsSelector } from 'app/features/browse-dashboards/state'; import { DashboardsTreeItem } from 'app/features/browse-dashboards/types'; import { DashboardViewItem } from 'app/features/search/types'; +import { useSelector } from 'app/types'; import { FolderUID } from './types'; const ROW_HEIGHT = 40; const CHEVRON_SIZE = 'md'; +export const getDOMId = (idPrefix: string, id: string) => `${idPrefix}-${id || 'root'}`; + interface NestedFolderListProps { items: DashboardsTreeItem[]; + focusedItemIndex: number; foldersAreOpenable: boolean; + idPrefix: string; selectedFolder: FolderUID | undefined; - onFolderClick: (uid: string, newOpenState: boolean) => void; - onSelectionChange: (event: React.FormEvent, item: DashboardViewItem) => void; - + onFolderExpand: (uid: string, newOpenState: boolean) => void; + onFolderSelect: (item: DashboardViewItem) => void; isItemLoaded: (itemIndex: number) => boolean; requestLoadMore: (folderUid: string | undefined) => void; } export function NestedFolderList({ items, + focusedItemIndex, foldersAreOpenable, + idPrefix, selectedFolder, - onFolderClick, - onSelectionChange, + onFolderExpand, + onFolderSelect, isItemLoaded, requestLoadMore, }: NestedFolderListProps) { @@ -42,8 +49,16 @@ export function NestedFolderList({ const styles = useStyles2(getStyles); const virtualData = useMemo( - (): VirtualData => ({ items, foldersAreOpenable, selectedFolder, onFolderClick, onSelectionChange }), - [items, foldersAreOpenable, selectedFolder, onFolderClick, onSelectionChange] + (): VirtualData => ({ + items, + focusedItemIndex, + foldersAreOpenable, + selectedFolder, + onFolderExpand, + onFolderSelect, + idPrefix, + }), + [items, focusedItemIndex, foldersAreOpenable, selectedFolder, onFolderExpand, onFolderSelect, idPrefix] ); const handleIsItemLoaded = useCallback( @@ -62,8 +77,8 @@ export function NestedFolderList({ ); return ( -
- {items.length ? ( +
+ {items.length > 0 ? ( (null); + const labelId = useId(); + const rootCollection = useSelector(rootItemsSelector); + const childrenCollections = useSelector(childrenByParentUIDSelector); + const children = (item.uid ? childrenCollections[item.uid] : rootCollection)?.items ?? []; + let siblings: DashboardViewItem[] = []; + // only look for siblings if we're not at the root + if (item.uid) { + siblings = (parentUID ? childrenCollections[parentUID] : rootCollection)?.items ?? []; + } - const id = useId() + `-uid-${item.uid}`; const styles = useStyles2(getStyles); - const handleClick = useCallback( + const handleExpand = useCallback( (ev: React.MouseEvent) => { ev.preventDefault(); - onFolderClick(item.uid, !isOpen); - }, - [item.uid, isOpen, onFolderClick] - ); - - const handleRadioChange = useCallback( - (ev: React.FormEvent) => { - if (item.kind === 'folder') { - onSelectionChange(ev, item); + ev.stopPropagation(); + if (item.uid) { + onFolderExpand(item.uid, !isOpen); } }, - [item, onSelectionChange] + [item.uid, isOpen, onFolderExpand] ); - const handleKeyDown = useCallback( - (ev: React.KeyboardEvent) => { - // Expand/collapse folder on arrow keys - if (foldersAreOpenable && (ev.key === 'ArrowRight' || ev.key === 'ArrowLeft')) { - ev.preventDefault(); - onFolderClick(item.uid, ev.key === 'ArrowRight'); - } - }, - [item.uid, foldersAreOpenable, onFolderClick] - ); + const handleSelect = useCallback(() => { + if (item.kind === 'folder') { + onFolderSelect(item); + } + }, [item, onFolderSelect]); if (item.kind === 'ui' && item.uiKind === 'pagination-placeholder') { return ( @@ -156,25 +170,36 @@ function Row({ index, style: virtualStyles, data }: RowProps) { } return ( -
- - + // don't need a key handler here, it's handled at the input level in NestedFolderPicker + // eslint-disable-next-line jsx-a11y/click-events-have-key-events +
0 ? children.map((child) => getDOMId(idPrefix, child.uid)).join(' ') : undefined} + aria-setsize={children.length} + aria-posinset={siblings.findIndex((i) => i.uid === item.uid) + 1} + id={getDOMId(idPrefix, item.uid)} + >
{foldersAreOpenable ? ( )} -