FolderPicker: Don't show expand button for empty folders and move search icon (#111872)
* Remove expand indicator if empty * Simplify the empty folder checking * Move search icon * Remove commented code
This commit is contained in:
@@ -32,6 +32,7 @@ interface NestedFolderListProps {
|
||||
onFolderSelect: (item: DashboardViewItem) => void;
|
||||
isItemLoaded: (itemIndex: number) => boolean;
|
||||
requestLoadMore: (folderUid: string | undefined) => void;
|
||||
emptyFolders: Set<string>;
|
||||
}
|
||||
|
||||
export function NestedFolderList({
|
||||
@@ -44,6 +45,7 @@ export function NestedFolderList({
|
||||
onFolderSelect,
|
||||
isItemLoaded,
|
||||
requestLoadMore,
|
||||
emptyFolders,
|
||||
}: NestedFolderListProps) {
|
||||
const infiniteLoaderRef = useRef<InfiniteLoader>(null);
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -57,8 +59,18 @@ export function NestedFolderList({
|
||||
onFolderExpand,
|
||||
onFolderSelect,
|
||||
idPrefix,
|
||||
emptyFolders,
|
||||
}),
|
||||
[items, focusedItemIndex, foldersAreOpenable, selectedFolder, onFolderExpand, onFolderSelect, idPrefix]
|
||||
[
|
||||
items,
|
||||
focusedItemIndex,
|
||||
foldersAreOpenable,
|
||||
selectedFolder,
|
||||
onFolderExpand,
|
||||
onFolderSelect,
|
||||
idPrefix,
|
||||
emptyFolders,
|
||||
]
|
||||
);
|
||||
|
||||
const handleIsItemLoaded = useCallback(
|
||||
@@ -119,8 +131,16 @@ interface RowProps {
|
||||
const SKELETON_WIDTHS = [100, 200, 130, 160, 150];
|
||||
|
||||
function Row({ index, style: virtualStyles, data }: RowProps) {
|
||||
const { items, focusedItemIndex, foldersAreOpenable, selectedFolder, onFolderExpand, onFolderSelect, idPrefix } =
|
||||
data;
|
||||
const {
|
||||
items,
|
||||
focusedItemIndex,
|
||||
foldersAreOpenable,
|
||||
selectedFolder,
|
||||
onFolderExpand,
|
||||
onFolderSelect,
|
||||
idPrefix,
|
||||
emptyFolders,
|
||||
} = data;
|
||||
const { item, isOpen, level, parentUID } = items[index];
|
||||
const rowRef = useRef<HTMLDivElement>(null);
|
||||
const labelId = useId();
|
||||
@@ -203,7 +223,7 @@ function Row({ index, style: virtualStyles, data }: RowProps) {
|
||||
<div className={styles.rowBody}>
|
||||
<Indent level={level} spacing={2} />
|
||||
|
||||
{foldersAreOpenable ? (
|
||||
{foldersAreOpenable && !emptyFolders.has(item.uid) ? (
|
||||
<IconButton
|
||||
size={CHEVRON_SIZE}
|
||||
// by using onMouseDown here instead of onClick we can stop focus moving
|
||||
@@ -257,7 +277,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
}),
|
||||
|
||||
folderButtonSpacer: css({
|
||||
paddingLeft: theme.spacing(0.5),
|
||||
paddingLeft: theme.spacing(2.5),
|
||||
}),
|
||||
|
||||
row: css({
|
||||
|
||||
@@ -111,6 +111,7 @@ export function NestedFolderPicker({
|
||||
|
||||
const isBrowsing = Boolean(overlayOpen && !(search && searchResults));
|
||||
const {
|
||||
emptyFolders,
|
||||
items: browseFlatTree,
|
||||
isLoading: isBrowseLoading,
|
||||
requestNextPage: fetchFolderPage,
|
||||
@@ -328,7 +329,7 @@ export function NestedFolderPicker({
|
||||
<Input
|
||||
ref={refs.setReference}
|
||||
autoFocus
|
||||
prefix={label ? <Icon name="folder" /> : null}
|
||||
prefix={label ? <Icon name="folder" /> : <Icon name="search" />}
|
||||
placeholder={label ?? t('browse-dashboards.folder-picker.search-placeholder', 'Search folders')}
|
||||
value={search}
|
||||
invalid={invalid}
|
||||
@@ -341,7 +342,6 @@ export function NestedFolderPicker({
|
||||
aria-owns={overlayId}
|
||||
aria-activedescendant={getDOMId(overlayId, flatTree[focusedItemIndex]?.item.uid)}
|
||||
role="combobox"
|
||||
suffix={<Icon name="search" />}
|
||||
{...getReferenceProps()}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
@@ -381,6 +381,7 @@ export function NestedFolderPicker({
|
||||
foldersAreOpenable={!(search && searchResults)}
|
||||
isItemLoaded={isItemLoaded}
|
||||
requestLoadMore={handleLoadMore}
|
||||
emptyFolders={emptyFolders}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -40,6 +40,9 @@ export function useFoldersQueryAppPlatform({
|
||||
// Keep a list of all request subscriptions so we can unsubscribe from them when the component is unmounted
|
||||
const requestsRef = useRef<GetFolderChildrenRequest[]>([]);
|
||||
|
||||
// Set of UIDs for which children were requested but were empty.
|
||||
const [emptyFolders, setEmptyFolders] = useState<Set<string>>(new Set());
|
||||
|
||||
// Keep a list of selectors for dynamic state selection
|
||||
const [selectors, setSelectors] = useState<Array<ReturnType<typeof dashboardAPIv0alpha1.endpoints.getSearch.select>>>(
|
||||
[]
|
||||
@@ -145,6 +148,14 @@ export function useFoldersQueryAppPlatform({
|
||||
|
||||
const childResponse = folderIsOpen && state.responseByParent[name];
|
||||
if (childResponse) {
|
||||
// If we finished loading and there are no children add to empty list
|
||||
if (
|
||||
childResponse.data &&
|
||||
childResponse.status !== QueryStatus.pending &&
|
||||
childResponse.data.hits.length === 0
|
||||
) {
|
||||
setEmptyFolders((prev) => new Set(prev).add(name));
|
||||
}
|
||||
const childFlatItems = createFlatList(name, childResponse, level + 1);
|
||||
return [flatItem, ...childFlatItems];
|
||||
}
|
||||
@@ -168,6 +179,7 @@ export function useFoldersQueryAppPlatform({
|
||||
}, [state, isBrowsing, openFolders, rootFolderUID, rootFolderItem]);
|
||||
|
||||
return {
|
||||
emptyFolders,
|
||||
items: treeList,
|
||||
isLoading: state.isLoading,
|
||||
requestNextPage,
|
||||
|
||||
@@ -58,6 +58,9 @@ export function useFoldersQueryLegacy({
|
||||
// Keep a list of all request subscriptions so we can unsubscribe from them when the component is unmounted
|
||||
const requestsRef = useRef<ListFoldersRequest[]>([]);
|
||||
|
||||
// Set of UIDs for which children were requested but were empty.
|
||||
const [emptyFolders, setEmptyFolders] = useState<Set<string>>(new Set());
|
||||
|
||||
// Keep a list of selectors for dynamic state selection
|
||||
const [selectors, setSelectors] = useState<
|
||||
Array<ReturnType<typeof browseDashboardsAPI.endpoints.listFolders.select>>
|
||||
@@ -165,8 +168,15 @@ export function useFoldersQueryLegacy({
|
||||
};
|
||||
|
||||
const childPages = folderIsOpen && state.pagesByParent[item.uid];
|
||||
|
||||
if (childPages) {
|
||||
const childFlatItems = createFlatList(item.uid, childPages, level + 1);
|
||||
|
||||
// If we finished loading and there are no children add to empty list
|
||||
if (childPages[0] && childPages[0].status !== PENDING_STATUS && childFlatItems.length === 0) {
|
||||
setEmptyFolders((prev) => new Set(prev).add(item.uid));
|
||||
}
|
||||
|
||||
return [flatItem, ...childFlatItems];
|
||||
}
|
||||
|
||||
@@ -191,6 +201,7 @@ export function useFoldersQueryLegacy({
|
||||
}, [state, isBrowsing, openFolders, rootFolderUID, rootFolderItem]);
|
||||
|
||||
return {
|
||||
emptyFolders,
|
||||
items: treeList,
|
||||
isLoading: state.isLoading,
|
||||
requestNextPage,
|
||||
|
||||
Reference in New Issue
Block a user