Files
grafana/public/app/features/search/page/components/SearchResultsCards.tsx
Josh Hunt d13488a435 NestedFolders: Show nested folders in Browse folder view (#63746)
* dirty dirty code for showing nested folders in folder view

refactor to NestedFolderItem

Update dashboard grid view to new types

update tests

REBASE OUT OF THIS BRANCH - joshhunt/star-by-uid merged into this

Squashed commit of the following:

commit d0f046ccd3d820575f58d9a60cfcedf5cbdb217d
Author: joshhunt <josh@trtr.co>
Date:   Wed Feb 8 18:35:56 2023 +0000

    undo async

commit abe2777a1fdb6418802102fbb1b6aca7ae4d8e66
Author: joshhunt <josh@trtr.co>
Date:   Wed Feb 8 18:34:11 2023 +0000

    Dashboards: Star dashboards by UID

add type for dashboard search dto

clean DashboardSearchItem type

simplify DashboardSearchHit type

remove unused properties from DashboardSearchHit

make uid non-optional

rename + move NestedFolderItem type to DashboardViewItem

clean up

* wip

* fix checkbox selection of nested folders

* show folder's parent correctly

* Add dashboard result kind

* don't render folder empty view in SearchView

* call nested folders api only if feature flag enabled

* remove unused import

* un-rename variable to reduce PR diff

* fix typo in comment

* fix order of pseudoFolders

* Fix General folder not showing in browse

* rename folder view tests

---------

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
2023-03-23 13:28:45 +00:00

126 lines
3.8 KiB
TypeScript

/* eslint-disable react/jsx-no-undef */
import { css } from '@emotion/css';
import React, { useEffect, useRef, useCallback, useState, CSSProperties } from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '@grafana/ui';
import { SearchItem } from '../../components/SearchItem';
import { useSearchKeyboardNavigation } from '../../hooks/useSearchKeyboardSelection';
import { queryResultToViewItem } from '../../service/utils';
import { SearchResultsProps } from './SearchResultsTable';
export const SearchResultsCards = React.memo(
({
response,
width,
height,
selection,
selectionToggle,
onTagSelected,
keyboardEvents,
onClickItem,
}: SearchResultsProps) => {
const styles = useStyles2(getStyles);
const infiniteLoaderRef = useRef<InfiniteLoader>(null);
const [listEl, setListEl] = useState<FixedSizeList | null>(null);
const highlightIndex = useSearchKeyboardNavigation(keyboardEvents, 0, response);
// Scroll to the top and clear loader cache when the query results change
useEffect(() => {
if (infiniteLoaderRef.current) {
infiniteLoaderRef.current.resetloadMoreItemsCache();
}
if (listEl) {
listEl.scrollTo(0);
}
}, [response, listEl]);
const RenderRow = useCallback(
({ index: rowIndex, style }: { index: number; style: CSSProperties }) => {
let className = '';
if (rowIndex === highlightIndex.y) {
className += ' ' + styles.selectedRow;
}
const item = response.view.get(rowIndex);
const searchItem = queryResultToViewItem(item, response.view);
const isSelected = selectionToggle && selection?.(searchItem.kind, searchItem.uid);
return (
<div style={style} key={item.uid} className={className} role="row">
<SearchItem
item={searchItem}
onTagSelected={onTagSelected}
onToggleChecked={(item) => {
if (selectionToggle) {
selectionToggle('dashboard', item.uid!);
}
}}
editable={Boolean(selection != null)}
onClickItem={onClickItem}
isSelected={isSelected}
/>
</div>
);
},
[response.view, highlightIndex, styles, onTagSelected, selection, selectionToggle, onClickItem]
);
if (!response.totalRows) {
return (
<div className={styles.noData} style={{ width }}>
No data
</div>
);
}
return (
<div aria-label="Search results list" style={{ width }} role="list">
<InfiniteLoader
ref={infiniteLoaderRef}
isItemLoaded={response.isItemLoaded}
itemCount={response.totalRows}
loadMoreItems={response.loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<FixedSizeList
ref={(innerRef) => {
ref(innerRef);
setListEl(innerRef);
}}
onItemsRendered={onItemsRendered}
height={height}
itemCount={response.totalRows}
itemSize={72}
width="100%"
style={{ overflow: 'hidden auto' }}
>
{RenderRow}
</FixedSizeList>
)}
</InfiniteLoader>
</div>
);
}
);
SearchResultsCards.displayName = 'SearchResultsCards';
const getStyles = (theme: GrafanaTheme2) => {
return {
noData: css`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
`,
selectedRow: css`
border-left: 3px solid ${theme.colors.primary.border};
`,
};
};