NestedFolders: Show Tags in Browse Dashboards (#67029)

* NestedFolders: Show tags in Browse Dashboards view

* tests
This commit is contained in:
Josh Hunt
2023-04-21 11:49:58 +01:00
committed by GitHub
parent 6522bb377e
commit 0eca9950a0
4 changed files with 96 additions and 14 deletions

View File

@@ -2,6 +2,7 @@ import { render as rtlRender, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import React from 'react'; import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
import { assertIsDefined } from 'test/helpers/asserts';
import { wellFormedDashboard, wellFormedEmptyFolder, wellFormedFolder } from '../fixtures/dashboardsTreeItem.fixture'; import { wellFormedDashboard, wellFormedEmptyFolder, wellFormedFolder } from '../fixtures/dashboardsTreeItem.fixture';
@@ -38,6 +39,7 @@ describe('browse-dashboards DashboardsTree', () => {
); );
expect(screen.queryByText(dashboard.item.title)).toBeInTheDocument(); expect(screen.queryByText(dashboard.item.title)).toBeInTheDocument();
expect(screen.queryByText('Dashboard')).toBeInTheDocument(); expect(screen.queryByText('Dashboard')).toBeInTheDocument();
expect(screen.queryByText(assertIsDefined(dashboard.item.tags)[0])).toBeInTheDocument();
}); });
it('renders a folder item', () => { it('renders a folder item', () => {

View File

@@ -11,7 +11,9 @@ import { DashboardViewItem, DashboardViewItemKind } from 'app/features/search/ty
import { DashboardsTreeItem, DashboardTreeSelection, INDENT_AMOUNT_CSS_VAR } from '../types'; import { DashboardsTreeItem, DashboardTreeSelection, INDENT_AMOUNT_CSS_VAR } from '../types';
import { NameCell } from './NameCell'; import { NameCell } from './NameCell';
import { TagsCell } from './TagsCell';
import { TypeCell } from './TypeCell'; import { TypeCell } from './TypeCell';
import { useCustomFlexLayout } from './customFlexTableLayout';
interface DashboardsTreeProps { interface DashboardsTreeProps {
items: DashboardsTreeItem[]; items: DashboardsTreeItem[];
@@ -45,6 +47,7 @@ export function DashboardsTree({
const tableColumns = useMemo(() => { const tableColumns = useMemo(() => {
const checkboxColumn: DashboardsTreeColumn = { const checkboxColumn: DashboardsTreeColumn = {
id: 'checkbox', id: 'checkbox',
width: 0,
Header: () => <Checkbox value={false} />, Header: () => <Checkbox value={false} />,
Cell: ({ row: { original: row }, selectedItems }: DashboardsTreeCellProps) => { Cell: ({ row: { original: row }, selectedItems }: DashboardsTreeCellProps) => {
const item = row.item; const item = row.item;
@@ -65,20 +68,29 @@ export function DashboardsTree({
const nameColumn: DashboardsTreeColumn = { const nameColumn: DashboardsTreeColumn = {
id: 'name', id: 'name',
width: 3,
Header: <span style={{ paddingLeft: 20 }}>Name</span>, Header: <span style={{ paddingLeft: 20 }}>Name</span>,
Cell: (props: DashboardsTreeCellProps) => <NameCell {...props} onFolderClick={onFolderClick} />, Cell: (props: DashboardsTreeCellProps) => <NameCell {...props} onFolderClick={onFolderClick} />,
}; };
const typeColumn: DashboardsTreeColumn = { const typeColumn: DashboardsTreeColumn = {
id: 'type', id: 'type',
width: 1,
Header: 'Type', Header: 'Type',
Cell: TypeCell, Cell: TypeCell,
}; };
return [checkboxColumn, nameColumn, typeColumn]; const tagsColumns: DashboardsTreeColumn = {
id: 'tags',
width: 2,
Header: 'Tags',
Cell: TagsCell,
};
return [checkboxColumn, nameColumn, typeColumn, tagsColumns];
}, [onItemSelectionChange, onFolderClick]); }, [onItemSelectionChange, onFolderClick]);
const table = useTable({ columns: tableColumns, data: items }); const table = useTable({ columns: tableColumns, data: items }, useCustomFlexLayout);
const { getTableProps, getTableBodyProps, headerGroups } = table; const { getTableProps, getTableBodyProps, headerGroups } = table;
const virtualData = useMemo(() => { const virtualData = useMemo(() => {
@@ -112,7 +124,6 @@ export function DashboardsTree({
<div {...getTableBodyProps()}> <div {...getTableBodyProps()}>
<List <List
className="virtual list"
height={height - HEADER_HEIGHT} height={height - HEADER_HEIGHT}
width={width} width={width}
itemCount={items.length} itemCount={items.length}
@@ -163,8 +174,6 @@ function VirtualListRow({ index, style, data }: VirtualListRowProps) {
} }
const getStyles = (theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => {
const columnSizing = 'auto 2fr 1fr';
return { return {
tableRoot: css({ tableRoot: css({
// Responsively // Responsively
@@ -175,17 +184,10 @@ const getStyles = (theme: GrafanaTheme2) => {
}, },
}), }),
cell: css({ // Column flex properties (cell sizing) are set by customFlexTableLayout.ts
padding: theme.spacing(1),
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}),
row: css({ row: css({
display: 'grid', gap: theme.spacing(1),
gridTemplateColumns: columnSizing,
alignItems: 'center',
}), }),
headerRow: css({ headerRow: css({
@@ -201,6 +203,13 @@ const getStyles = (theme: GrafanaTheme2) => {
}, },
}), }),
cell: css({
padding: theme.spacing(1),
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}),
link: css({ link: css({
'&:hover': { '&:hover': {
textDecoration: 'underline', textDecoration: 'underline',

View File

@@ -0,0 +1,27 @@
import { css } from '@emotion/css';
import React from 'react';
import { CellProps } from 'react-table';
import { GrafanaTheme2 } from '@grafana/data';
import { TagList, useStyles2 } from '@grafana/ui';
import { DashboardsTreeItem } from '../types';
export function TagsCell({ row: { original: data } }: CellProps<DashboardsTreeItem, unknown>) {
const styles = useStyles2(getStyles);
const item = data.item;
if (item.kind === 'ui-empty-folder') {
return <></>;
}
return <TagList className={styles.tagList} tags={item.tags ?? []} />;
}
function getStyles(theme: GrafanaTheme2) {
return {
// TagList is annoying and has weird default alignment
tagList: css({
justifyContent: 'flex-start',
}),
};
}

View File

@@ -0,0 +1,44 @@
import { Hooks, UseTableColumnProps } from 'react-table';
/**
* Simplified flex layout module for react-table.
* Uses the width of the column as the flex grow amount - the ratio of width between all columns
*
* Width of 0 for 'auto' width - useful for columns of fixed with that should shrink to the size
* of content
*
* Originally based on https://github.com/TanStack/table/blob/v7/src/plugin-hooks/useFlexLayout.js
*/
export function useCustomFlexLayout<D extends object>(hooks: Hooks<D>) {
hooks.getRowProps.push((props) => [props, getRowStyles()]);
hooks.getHeaderGroupProps.push((props) => [props, getRowStyles()]);
hooks.getFooterGroupProps.push((props) => [props, getRowStyles()]);
hooks.getHeaderProps.push((props, { column }) => [props, getColumnStyleProps(column)]);
hooks.getCellProps.push((props, { cell }) => [props, getColumnStyleProps(cell.column)]);
hooks.getFooterProps.push((props, { column }) => [props, getColumnStyleProps(column)]);
}
useCustomFlexLayout.pluginName = 'useCustomFlexLayout';
function getColumnStyleProps<D extends object>(column: UseTableColumnProps<D>) {
return {
style: {
flex:
column.totalWidth === 0
? // if width: 0, prevent the column from growing (or shrinking), and set basis to auto to
// fit column to the width of its content
'0 0 auto'
: // Otherwise, grow the content to a size in proportion to the other column widths
`${column.totalWidth} 0 0`,
},
};
}
function getRowStyles() {
return {
style: {
display: 'flex',
flex: '1 0 auto',
},
};
}