mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DataLinks: Bring back single click links for Stat, Gauge and BarGauge panel (#31692)
* Bring back clickable Stat, Gauge and BarGauge panels * Demo dashboard * Add DataLinksContextMenu tests * Only use new UI for data links, revert panel links logic
This commit is contained in:
parent
bbee7da3e0
commit
fdc6f2cc6f
1194
devenv/dev-dashboards/panel-common/linked-viz.json
Normal file
1194
devenv/dev-dashboards/panel-common/linked-viz.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -181,4 +181,7 @@ export const Components = {
|
|||||||
CallToActionCard: {
|
CallToActionCard: {
|
||||||
button: (name: string) => `Call to action button ${name}`,
|
button: (name: string) => `Call to action button ${name}`,
|
||||||
},
|
},
|
||||||
|
DataLinksContextMenu: {
|
||||||
|
singleLink: 'Data link',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { DataLinksContextMenu } from './DataLinksContextMenu';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
const fakeAriaLabel = 'fake aria label';
|
||||||
|
describe('DataLinksContextMenu', () => {
|
||||||
|
it('renders context menu when there are more than one data links', () => {
|
||||||
|
render(
|
||||||
|
<DataLinksContextMenu
|
||||||
|
links={() => [
|
||||||
|
{
|
||||||
|
href: '/link1',
|
||||||
|
title: 'Link1',
|
||||||
|
target: '_blank',
|
||||||
|
origin: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/link2',
|
||||||
|
title: 'Link2',
|
||||||
|
target: '_blank',
|
||||||
|
origin: {},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
config={{
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
title: 'Link1',
|
||||||
|
url: '/link1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Link2',
|
||||||
|
url: '/link2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
return <div aria-label="fake aria label" />;
|
||||||
|
}}
|
||||||
|
</DataLinksContextMenu>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText(fakeAriaLabel)).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByLabelText(selectors.components.DataLinksContextMenu.singleLink)).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders link when there is a single data link', () => {
|
||||||
|
render(
|
||||||
|
<DataLinksContextMenu
|
||||||
|
links={() => [
|
||||||
|
{
|
||||||
|
href: '/link1',
|
||||||
|
title: 'Link1',
|
||||||
|
target: '_blank',
|
||||||
|
origin: {},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
config={{
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
title: 'Link1',
|
||||||
|
url: '/link1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
return <div aria-label="fake aria label" />;
|
||||||
|
}}
|
||||||
|
</DataLinksContextMenu>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText(fakeAriaLabel)).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText(selectors.components.DataLinksContextMenu.singleLink)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -1,12 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { WithContextMenu } from '../ContextMenu/WithContextMenu';
|
import { FieldConfig, LinkModel } from '@grafana/data';
|
||||||
import { LinkModel } from '@grafana/data';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { linkModelToContextMenuItems } from '../../utils/dataLinks';
|
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
|
import { WithContextMenu } from '../ContextMenu/WithContextMenu';
|
||||||
|
import { linkModelToContextMenuItems } from '../../utils/dataLinks';
|
||||||
|
|
||||||
interface DataLinksContextMenuProps {
|
interface DataLinksContextMenuProps {
|
||||||
children: (props: DataLinksContextMenuApi) => JSX.Element;
|
children: (props: DataLinksContextMenuApi) => JSX.Element;
|
||||||
links: () => LinkModel[];
|
links: () => LinkModel[];
|
||||||
|
config: FieldConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataLinksContextMenuApi {
|
export interface DataLinksContextMenuApi {
|
||||||
@ -14,7 +16,8 @@ export interface DataLinksContextMenuApi {
|
|||||||
targetClassName?: string;
|
targetClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links }) => {
|
export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links, config }) => {
|
||||||
|
const linksCounter = config.links!.length;
|
||||||
const getDataLinksContextMenuItems = () => {
|
const getDataLinksContextMenuItems = () => {
|
||||||
return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }];
|
return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }];
|
||||||
};
|
};
|
||||||
@ -24,11 +27,27 @@ export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ chil
|
|||||||
cursor: context-menu;
|
cursor: context-menu;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return (
|
if (linksCounter > 1) {
|
||||||
<WithContextMenu getContextMenuItems={getDataLinksContextMenuItems}>
|
return (
|
||||||
{({ openMenu }) => {
|
<WithContextMenu getContextMenuItems={getDataLinksContextMenuItems}>
|
||||||
return children({ openMenu, targetClassName });
|
{({ openMenu }) => {
|
||||||
}}
|
return children({ openMenu, targetClassName });
|
||||||
</WithContextMenu>
|
}}
|
||||||
);
|
</WithContextMenu>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const linkModel = links()[0];
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={linkModel.href}
|
||||||
|
onClick={linkModel.onClick}
|
||||||
|
target={linkModel.target}
|
||||||
|
title={linkModel.title}
|
||||||
|
style={{ display: 'flex' }}
|
||||||
|
aria-label={selectors.components.DataLinksContextMenu.singleLink}
|
||||||
|
>
|
||||||
|
{children({})}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -57,14 +57,13 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
|||||||
|
|
||||||
if (hasLinks && getLinks) {
|
if (hasLinks && getLinks) {
|
||||||
return (
|
return (
|
||||||
<DataLinksContextMenu links={getLinks}>
|
<DataLinksContextMenu links={getLinks} config={value.field}>
|
||||||
{(api) => {
|
{(api) => {
|
||||||
return this.renderComponent(valueProps, api);
|
return this.renderComponent(valueProps, api);
|
||||||
}}
|
}}
|
||||||
</DataLinksContextMenu>
|
</DataLinksContextMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.renderComponent(valueProps, {});
|
return this.renderComponent(valueProps, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
|||||||
|
|
||||||
if (hasLinks && getLinks) {
|
if (hasLinks && getLinks) {
|
||||||
return (
|
return (
|
||||||
<DataLinksContextMenu links={getLinks}>
|
<DataLinksContextMenu links={getLinks} config={value.field}>
|
||||||
{(api) => {
|
{(api) => {
|
||||||
return this.renderComponent(valueProps, api);
|
return this.renderComponent(valueProps, api);
|
||||||
}}
|
}}
|
||||||
|
@ -69,7 +69,7 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
|
|||||||
|
|
||||||
if (hasLinks && getLinks) {
|
if (hasLinks && getLinks) {
|
||||||
return (
|
return (
|
||||||
<DataLinksContextMenu links={getLinks}>
|
<DataLinksContextMenu links={getLinks} config={value.field}>
|
||||||
{(api) => {
|
{(api) => {
|
||||||
return this.renderComponent(valueProps, api);
|
return this.renderComponent(valueProps, api);
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user