Table: Add unit tests for MaybeWrapWithLink (#109932)

This commit is contained in:
Paul Marbach
2025-08-21 16:00:18 +02:00
committed by GitHub
parent 01d48e26fe
commit 2ab5df43e0
7 changed files with 215 additions and 9 deletions
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { formattedValueToString } from '@grafana/data';
import { MaybeWrapWithLink } from '../MaybeWrapWithLink';
import { MaybeWrapWithLink } from '../components/MaybeWrapWithLink';
import { AutoCellProps, TableCellStyles } from '../types';
export function AutoCell({ value, field, rowIdx }: AutoCellProps) {
@@ -2,7 +2,7 @@ import { ThresholdsConfig, ThresholdsMode, VizOrientation, getFieldConfigWithMin
import { BarGaugeDisplayMode, BarGaugeValueMode, TableCellDisplayMode } from '@grafana/schema';
import { BarGauge } from '../../../BarGauge/BarGauge';
import { MaybeWrapWithLink } from '../MaybeWrapWithLink';
import { MaybeWrapWithLink } from '../components/MaybeWrapWithLink';
import { TABLE } from '../constants';
import { BarGaugeCellProps } from '../types';
import { getCellOptions, getAlignmentFactor } from '../utils';
@@ -1,7 +1,7 @@
import { css } from '@emotion/css';
import { TableCellDisplayMode } from '../../types';
import { MaybeWrapWithLink } from '../MaybeWrapWithLink';
import { MaybeWrapWithLink } from '../components/MaybeWrapWithLink';
import { ImageCellProps, TableCellStyles } from '../types';
export const ImageCell = ({ cellOptions, field, value, rowIdx }: ImageCellProps) => {
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { renderMarkdown } from '@grafana/data';
import { MaybeWrapWithLink } from '../MaybeWrapWithLink';
import { MaybeWrapWithLink } from '../components/MaybeWrapWithLink';
import { MarkdownCellProps, TableCellStyles } from '../types';
export function MarkdownCell({ field, rowIdx, disableSanitizeHtml }: MarkdownCellProps) {
@@ -0,0 +1,199 @@
import { render, screen } from '@testing-library/react';
import { ActionType, Field, FieldType, HttpRequestMethod } from '@grafana/data';
import { MaybeWrapWithLink } from './MaybeWrapWithLink';
describe('MaybeWrapWithLink', () => {
describe('single link', () => {
it('renders children as a link when there is a single link', () => {
const link = { title: 'My link', url: 'http://example.com' };
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => [{ title: link.title, href: link.url, target: '_blank', origin: field }]),
config: {
links: [link],
actions: [],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const linkElement = screen.getByTitle(link.title);
expect(linkElement).toBeInTheDocument();
expect(linkElement).toHaveAttribute('href', link.url);
expect(linkElement).toHaveTextContent('Test Link');
});
it('does not throw if getLinks unexpectedly returns nothing when a single link is present', () => {
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => []),
config: {
links: [{ title: 'My link', url: 'http://example.com' }],
actions: [],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const childElement = screen.getByText('Test Link');
expect(childElement).toBeInTheDocument();
});
});
describe('multi link and/or actions', () => {
it('renders a popup target link if multiple links are present', () => {
const links = [
{ title: 'My link', url: 'http://example.com' },
{ title: 'Another link', url: 'http://example.com' },
];
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => links.map((l) => ({ title: l.title, href: l.url, target: '_blank', origin: field }))),
config: {
links,
actions: [],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const linkElement = screen.getByTitle('view data links and actions');
expect(linkElement).toBeInTheDocument();
expect(linkElement.tagName).toBe('A');
expect(linkElement).toHaveAttribute('aria-haspopup', 'menu');
expect(linkElement).toHaveTextContent('Test Link');
});
it('renders a popup target link if multiple actions are present', () => {
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => []),
config: {
links: [],
actions: [
{
type: ActionType.Fetch,
title: 'My action',
[ActionType.Fetch]: { method: HttpRequestMethod.GET, url: 'http://example.com' },
},
{
type: ActionType.Fetch,
title: 'Another action',
[ActionType.Fetch]: { method: HttpRequestMethod.POST, url: 'http://example.com' },
},
],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const linkElement = screen.getByTitle('view data links and actions');
expect(linkElement).toBeInTheDocument();
expect(linkElement.tagName).toBe('A');
expect(linkElement).toHaveAttribute('aria-haspopup', 'menu');
expect(linkElement).toHaveTextContent('Test Link');
});
it('renders a popup target link if a single action is present', () => {
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => []),
config: {
links: [],
actions: [
{
type: ActionType.Fetch,
title: 'My action',
[ActionType.Fetch]: { method: HttpRequestMethod.GET, url: 'http://example.com' },
},
],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const linkElement = screen.getByTitle('view data links and actions');
expect(linkElement).toBeInTheDocument();
expect(linkElement.tagName).toBe('A');
expect(linkElement).toHaveAttribute('aria-haspopup', 'menu');
expect(linkElement).toHaveTextContent('Test Link');
});
it('renders a popup target link if a mixture of actions and links are present', () => {
const links = [{ title: 'My link', url: 'http://example.com' }];
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => links.map((l) => ({ title: l.title, href: l.url, target: '_blank', origin: field }))),
config: {
links,
actions: [
{
type: ActionType.Fetch,
title: 'My action',
[ActionType.Fetch]: { method: HttpRequestMethod.GET, url: 'http://example.com' },
},
],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const linkElement = screen.getByTitle('view data links and actions');
expect(linkElement).toBeInTheDocument();
expect(linkElement.tagName).toBe('A');
expect(linkElement).toHaveAttribute('aria-haspopup', 'menu');
expect(linkElement).toHaveTextContent('Test Link');
});
});
describe('no links or actions', () => {
it('passes the children through when no links or actions are present', () => {
const links = [
{ title: 'My link', url: 'http://example.com' },
{ title: 'Another link', url: 'http://example.com' },
];
const field: Field = {
type: FieldType.string,
name: 'Test Field',
values: [],
getLinks: jest.fn(() => []),
config: {
links,
actions: [],
},
};
const rowIdx = 0;
const children = <span>Test Link</span>;
render(<MaybeWrapWithLink field={field} rowIdx={rowIdx} children={children} />);
const childElement = screen.getByText('Test Link');
expect(childElement).toBeInTheDocument();
});
});
});
@@ -1,10 +1,10 @@
import { memo, ReactNode } from 'react';
import { Field } from '@grafana/data';
import { t } from '@grafana/i18n';
import { renderSingleLink } from '../DataLinksActionsTooltip';
import { getCellLinks } from './utils';
import { renderSingleLink } from '../../DataLinksActionsTooltip';
import { getCellLinks } from '../utils';
interface MaybeWrapWithLinkProps {
field: Field;
@@ -23,8 +23,12 @@ export const MaybeWrapWithLink = memo(({ field, rowIdx, children }: MaybeWrapWit
}
// as faux link that acts as hit-area for tooltip activation
else if (linksCount + actionsCount > 0) {
// eslint-disable-next-line jsx-a11y/anchor-is-valid
return <a aria-haspopup="menu">{children}</a>;
return (
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a title={t('table.link-wrapper.menu', 'view data links and actions')} aria-haspopup="menu">
{children}
</a>
);
}
// raw value
+3
View File
@@ -12751,6 +12751,9 @@
"label-alt-text": "Alt text",
"label-title-text": "Title text"
},
"link-wrapper": {
"menu": "view data links and actions"
},
"markdown-cell-options-editor": {
"description-dynamic-height": "We recommend enabling pagination with this option to avoid performance issues.",
"label": {