From 0eca9950a013b44e9677ea1dabfee369be1a15aa Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Fri, 21 Apr 2023 11:49:58 +0100 Subject: [PATCH] NestedFolders: Show Tags in Browse Dashboards (#67029) * NestedFolders: Show tags in Browse Dashboards view * tests --- .../components/DashboardsTree.test.tsx | 2 + .../components/DashboardsTree.tsx | 37 ++++++++++------ .../browse-dashboards/components/TagsCell.tsx | 27 ++++++++++++ .../components/customFlexTableLayout.ts | 44 +++++++++++++++++++ 4 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 public/app/features/browse-dashboards/components/TagsCell.tsx create mode 100644 public/app/features/browse-dashboards/components/customFlexTableLayout.ts diff --git a/public/app/features/browse-dashboards/components/DashboardsTree.test.tsx b/public/app/features/browse-dashboards/components/DashboardsTree.test.tsx index 1ac147c290a..42847fc3c2a 100644 --- a/public/app/features/browse-dashboards/components/DashboardsTree.test.tsx +++ b/public/app/features/browse-dashboards/components/DashboardsTree.test.tsx @@ -2,6 +2,7 @@ import { render as rtlRender, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { TestProvider } from 'test/helpers/TestProvider'; +import { assertIsDefined } from 'test/helpers/asserts'; 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')).toBeInTheDocument(); + expect(screen.queryByText(assertIsDefined(dashboard.item.tags)[0])).toBeInTheDocument(); }); it('renders a folder item', () => { diff --git a/public/app/features/browse-dashboards/components/DashboardsTree.tsx b/public/app/features/browse-dashboards/components/DashboardsTree.tsx index 7f5f58cda59..11cf3529c80 100644 --- a/public/app/features/browse-dashboards/components/DashboardsTree.tsx +++ b/public/app/features/browse-dashboards/components/DashboardsTree.tsx @@ -11,7 +11,9 @@ import { DashboardViewItem, DashboardViewItemKind } from 'app/features/search/ty import { DashboardsTreeItem, DashboardTreeSelection, INDENT_AMOUNT_CSS_VAR } from '../types'; import { NameCell } from './NameCell'; +import { TagsCell } from './TagsCell'; import { TypeCell } from './TypeCell'; +import { useCustomFlexLayout } from './customFlexTableLayout'; interface DashboardsTreeProps { items: DashboardsTreeItem[]; @@ -45,6 +47,7 @@ export function DashboardsTree({ const tableColumns = useMemo(() => { const checkboxColumn: DashboardsTreeColumn = { id: 'checkbox', + width: 0, Header: () => , Cell: ({ row: { original: row }, selectedItems }: DashboardsTreeCellProps) => { const item = row.item; @@ -65,20 +68,29 @@ export function DashboardsTree({ const nameColumn: DashboardsTreeColumn = { id: 'name', + width: 3, Header: Name, Cell: (props: DashboardsTreeCellProps) => , }; const typeColumn: DashboardsTreeColumn = { id: 'type', + width: 1, Header: 'Type', Cell: TypeCell, }; - return [checkboxColumn, nameColumn, typeColumn]; + const tagsColumns: DashboardsTreeColumn = { + id: 'tags', + width: 2, + Header: 'Tags', + Cell: TagsCell, + }; + + return [checkboxColumn, nameColumn, typeColumn, tagsColumns]; }, [onItemSelectionChange, onFolderClick]); - const table = useTable({ columns: tableColumns, data: items }); + const table = useTable({ columns: tableColumns, data: items }, useCustomFlexLayout); const { getTableProps, getTableBodyProps, headerGroups } = table; const virtualData = useMemo(() => { @@ -112,7 +124,6 @@ export function DashboardsTree({
{ - const columnSizing = 'auto 2fr 1fr'; - return { tableRoot: css({ // Responsively @@ -175,17 +184,10 @@ const getStyles = (theme: GrafanaTheme2) => { }, }), - cell: css({ - padding: theme.spacing(1), - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - }), + // Column flex properties (cell sizing) are set by customFlexTableLayout.ts row: css({ - display: 'grid', - gridTemplateColumns: columnSizing, - alignItems: 'center', + gap: theme.spacing(1), }), headerRow: css({ @@ -201,6 +203,13 @@ const getStyles = (theme: GrafanaTheme2) => { }, }), + cell: css({ + padding: theme.spacing(1), + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }), + link: css({ '&:hover': { textDecoration: 'underline', diff --git a/public/app/features/browse-dashboards/components/TagsCell.tsx b/public/app/features/browse-dashboards/components/TagsCell.tsx new file mode 100644 index 00000000000..52c279dce36 --- /dev/null +++ b/public/app/features/browse-dashboards/components/TagsCell.tsx @@ -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) { + const styles = useStyles2(getStyles); + const item = data.item; + if (item.kind === 'ui-empty-folder') { + return <>; + } + + return ; +} + +function getStyles(theme: GrafanaTheme2) { + return { + // TagList is annoying and has weird default alignment + tagList: css({ + justifyContent: 'flex-start', + }), + }; +} diff --git a/public/app/features/browse-dashboards/components/customFlexTableLayout.ts b/public/app/features/browse-dashboards/components/customFlexTableLayout.ts new file mode 100644 index 00000000000..0f4f0af1211 --- /dev/null +++ b/public/app/features/browse-dashboards/components/customFlexTableLayout.ts @@ -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(hooks: Hooks) { + 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(column: UseTableColumnProps) { + 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', + }, + }; +}