diff --git a/.betterer.results b/.betterer.results
index b8420d1ceeb..dadf1c314c2 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -639,9 +639,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
- "packages/grafana-ui/src/components/Table/Cells/DataLinksCell.tsx:5381": [
- [0, 0, 0, "There should be no empty line within import group", "0"]
- ],
"packages/grafana-ui/src/components/Table/Cells/TableCell.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
diff --git a/packages/grafana-ui/src/components/Table/Cells/DataLinksCell.tsx b/packages/grafana-ui/src/components/Table/Cells/DataLinksCell.tsx
index c4c325682f4..3e95d3946b4 100644
--- a/packages/grafana-ui/src/components/Table/Cells/DataLinksCell.tsx
+++ b/packages/grafana-ui/src/components/Table/Cells/DataLinksCell.tsx
@@ -1,5 +1,4 @@
import { getCellLinks } from '../../../utils';
-
import { TableCellProps } from '../types';
export const DataLinksCell = (props: TableCellProps) => {
diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/DataLinksCell.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/DataLinksCell.tsx
new file mode 100644
index 00000000000..15092b85a19
--- /dev/null
+++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/DataLinksCell.tsx
@@ -0,0 +1,50 @@
+import { css } from '@emotion/css';
+
+import { GrafanaTheme2 } from '@grafana/data';
+
+import { useStyles2 } from '../../../../themes';
+import { CellNGProps } from '../types';
+import { getCellLinks } from '../utils';
+
+export const DataLinksCell = (props: CellNGProps) => {
+ const { field, rowIdx } = props;
+ const styles = useStyles2(getStyles);
+
+ const links = getCellLinks(field, rowIdx!);
+
+ return (
+
+ {links &&
+ links.map((link, idx) => {
+ return (
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
+
+
+ {link.title}
+
+
+ );
+ })}
+
+ );
+};
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ linkCell: css({
+ cursor: 'pointer',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ userSelect: 'text',
+ whiteSpace: 'nowrap',
+ color: theme.colors.text.link,
+ fontWeight: theme.typography.fontWeightMedium,
+ paddingRight: theme.spacing(1.5),
+ a: {
+ color: theme.colors.text.link,
+ },
+ '&:hover': {
+ textDecoration: 'underline',
+ color: theme.colors.text.link,
+ },
+ }),
+});
diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellNG.tsx
index 69b7b9a163b..0aba7fa1ee0 100644
--- a/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellNG.tsx
+++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellNG.tsx
@@ -13,6 +13,7 @@ import { getCellColors } from '../utils';
import AutoCell from './AutoCell';
import { BarGaugeCell } from './BarGaugeCell';
+import { DataLinksCell } from './DataLinksCell';
import { ImageCell } from './ImageCell';
import { JSONCell } from './JSONCell';
import { SparklineCell } from './SparklineCell';
@@ -22,6 +23,7 @@ import { SparklineCell } from './SparklineCell';
// fieldDisplay: any?
// }
+// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
export function TableCellNG(props: any) {
const {
field,
@@ -117,6 +119,9 @@ export function TableCellNG(props: any) {
case TableCellDisplayMode.JSONView:
cell = ;
break;
+ case TableCellDisplayMode.DataLinks:
+ cell = ;
+ break;
case TableCellDisplayMode.Auto:
default:
cell = (
diff --git a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx
index 0f1ad1e8207..33f062b88bf 100644
--- a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx
+++ b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx
@@ -51,6 +51,13 @@ export type FilterType = {
};
};
+/**
+ * getIsNestedTable is a helper function that takes a DataFrame and returns a
+ * boolean value based on the presence of nested frames
+ */
+const getIsNestedTable = (dataFrame: DataFrame): boolean =>
+ dataFrame.fields.some(({ type }) => type === FieldType.nestedFrames);
+
export function TableNG(props: TableNGProps) {
const {
height,
@@ -158,6 +165,7 @@ export function TableNG(props: TableNGProps) {
// setSortColumns is still used to trigger re-render
const sortColumnsRef = useRef(sortColumns);
const [expandedRows, setExpandedRows] = useState([]);
+ const [isNestedTable, setIsNestedTable] = useState(false);
function getDefaultRowHeight(): number {
const bodyFontSize = theme.typography.fontSize;
@@ -230,12 +238,10 @@ export function TableNG(props: TableNGProps) {
const mapFrameToDataGrid = (main: DataFrame, calcsRef: React.MutableRefObject, subTable?: boolean) => {
const columns: TableColumn[] = [];
- // Check for nestedFrames
- const nestedDataField = main.fields.find((f) => f.type === FieldType.nestedFrames);
- const hasNestedData = nestedDataField !== undefined;
+ const hasNestedFrames = getIsNestedTable(main);
// If nested frames, add expansion control column
- if (hasNestedData) {
+ if (hasNestedFrames) {
const expanderField: Field = {
name: '',
type: FieldType.other,
@@ -546,9 +552,14 @@ export function TableNG(props: TableNGProps) {
const columns = useMemo(
() => mapFrameToDataGrid(props.data, calcsRef),
- [props.data, calcsRef, filter, expandedRows, footerOptions] // eslint-disable-line react-hooks/exhaustive-deps
+ [props.data, calcsRef, filter, expandedRows, expandedRows.length, footerOptions] // eslint-disable-line react-hooks/exhaustive-deps
);
+ useEffect(() => {
+ const hasNestedFrames = getIsNestedTable(props.data);
+ setIsNestedTable(hasNestedFrames);
+ }, [props.data]);
+
// This effect needed to set header cells refs before row height calculation
useLayoutEffect(() => {
setReadyForRowHeightCalc(Object.keys(headerCellRefs.current).length > 0);
@@ -603,7 +614,7 @@ export function TableNG(props: TableNGProps) {
sortable: true,
resizable: true,
}}
- rowHeight={textWrap ? calculateRowHeight : defaultRowHeight}
+ rowHeight={textWrap || isNestedTable ? calculateRowHeight : defaultRowHeight}
// TODO: This doesn't follow current table behavior
style={{ width, height: height - (enablePagination ? paginationHeight : 0) }}
renderers={{ renderRow: myRowRenderer }}
diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.ts
index c8aeb2b7567..16d1f06ca55 100644
--- a/packages/grafana-ui/src/components/Table/TableNG/utils.ts
+++ b/packages/grafana-ui/src/components/Table/TableNG/utils.ts
@@ -216,23 +216,14 @@ export function getCellColors(
return { textColor, bgColor, bgHoverColor };
}
-export const getLinks = (field: Field, rowIdx: number) => {
- if (field.getLinks) {
- return field.getLinks({ valueRowIndex: rowIdx });
- }
-
- return [];
-};
-
/**
* @internal
- * TODO: unify with the existing getCellLinks
*/
-export const getCellLinks = (field: Field, rowIndex: number) => {
+export const getCellLinks = (field: Field, rowIdx: number) => {
let links: Array> | undefined;
if (field.getLinks) {
links = field.getLinks({
- valueRowIndex: rowIndex,
+ valueRowIndex: rowIdx,
});
}
@@ -250,7 +241,7 @@ export const getCellLinks = (field: Field, rowIndex: number) => {
event.preventDefault();
origOnClick!(event, {
field,
- rowIndex: rowIndex,
+ rowIndex: rowIdx,
});
}
};