diff --git a/packages/grafana-data/src/types/dataLink.ts b/packages/grafana-data/src/types/dataLink.ts index 2a7dab4ea8c..b83f18277d1 100644 --- a/packages/grafana-data/src/types/dataLink.ts +++ b/packages/grafana-data/src/types/dataLink.ts @@ -33,7 +33,7 @@ export interface DataLink { onClick?: (event: DataLinkClickEvent) => void; // If dataLink represents internal link this has to be filled. Internal link is defined as a query in a particular - // datas ource that we want to show to the user. Usually this results in a link to explore but can also lead to + // data source that we want to show to the user. Usually this results in a link to explore but can also lead to // more custom onClick behaviour if needed. // @internal and subject to change in future releases internal?: InternalDataLink; diff --git a/packages/grafana-ui/src/components/Logs/FieldLink.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinkButton.tsx similarity index 71% rename from packages/grafana-ui/src/components/Logs/FieldLink.tsx rename to packages/grafana-ui/src/components/DataLinks/DataLinkButton.tsx index 116f2de2aa4..95d8b8d4160 100644 --- a/packages/grafana-ui/src/components/Logs/FieldLink.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinkButton.tsx @@ -2,16 +2,19 @@ import { Field, LinkModel } from '@grafana/data'; import React from 'react'; import { ButtonProps, Button } from '../Button'; -type FieldLinkProps = { +type DataLinkButtonProps = { link: LinkModel; buttonProps?: ButtonProps; }; -export function FieldLink({ link, buttonProps }: FieldLinkProps) { +/** + * @internal + */ +export function DataLinkButton({ link, buttonProps }: DataLinkButtonProps) { return ( - diff --git a/packages/grafana-ui/src/components/DataLinks/FieldLinkList.tsx b/packages/grafana-ui/src/components/DataLinks/FieldLinkList.tsx new file mode 100644 index 00000000000..1822820599e --- /dev/null +++ b/packages/grafana-ui/src/components/DataLinks/FieldLinkList.tsx @@ -0,0 +1,71 @@ +import { Field, GrafanaTheme, LinkModel } from '@grafana/data'; +import { css } from 'emotion'; +import React from 'react'; +import { useStyles } from '../../themes'; +import { Icon } from '../Icon/Icon'; +import { DataLinkButton } from './DataLinkButton'; + +type Props = { + links: Array>; +}; + +/** + * @internal + */ +export function FieldLinkList({ links }: Props) { + const styles = useStyles(getStyles); + + if (links.length === 1) { + return ; + } + + const externalLinks = links.filter((link) => link.target === '_blank'); + const internalLinks = links.filter((link) => link.target === '_self'); + + return ( + <> + {internalLinks.map((link, i) => { + return ; + })} +
+

External links

+ {externalLinks.map((link, i) => ( + + + {link.title} + + ))} +
+ + ); +} + +const getStyles = (theme: GrafanaTheme) => ({ + wrapper: css` + flex-basis: 150px; + width: 100px; + margin-top: ${theme.spacing.sm}; + `, + externalLinksHeading: css` + color: ${theme.colors.textWeak}; + font-weight: ${theme.typography.weight.regular}; + font-size: ${theme.typography.size.sm}; + margin: 0; + `, + externalLink: css` + color: ${theme.colors.linkExternal}; + font-weight: ${theme.typography.weight.regular}; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &:hover { + text-decoration: underline; + } + + div { + margin-right: ${theme.spacing.sm}; + } + `, +}); diff --git a/packages/grafana-ui/src/components/Logs/LogDetailsRow.tsx b/packages/grafana-ui/src/components/Logs/LogDetailsRow.tsx index 9cd1b34b3ce..8c5fb6ea2fb 100644 --- a/packages/grafana-ui/src/components/Logs/LogDetailsRow.tsx +++ b/packages/grafana-ui/src/components/Logs/LogDetailsRow.tsx @@ -10,7 +10,7 @@ import { stylesFactory } from '../../themes/stylesFactory'; //Components import { LogLabelStats } from './LogLabelStats'; import { IconButton } from '../IconButton/IconButton'; -import { FieldLink } from './FieldLink'; +import { DataLinkButton } from '../DataLinks/DataLinkButton'; export interface Props extends Themeable { parsedValue: string; @@ -158,7 +158,7 @@ class UnThemedLogDetailsRow extends PureComponent { return ( <>   - + ); })} diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index a67294acf1b..5985039b7de 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -87,7 +87,8 @@ export { LogLabels } from './Logs/LogLabels'; export { LogMessageAnsi } from './Logs/LogMessageAnsi'; export { LogRows } from './Logs/LogRows'; export { getLogRowStyles } from './Logs/getLogRowStyles'; -export { FieldLink } from './Logs/FieldLink'; +export { DataLinkButton } from './DataLinks/DataLinkButton'; +export { FieldLinkList } from './DataLinks/FieldLinkList'; export { ToggleButtonGroup, ToggleButton } from './ToggleButtonGroup/ToggleButtonGroup'; // Panel editors export { FullWidthButtonContainer } from './Button/FullWidthButtonContainer'; diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/index.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/index.tsx index f17bc030b43..bfde2b39915 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/index.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/index.tsx @@ -30,7 +30,7 @@ import AccordianReferences from './AccordianReferences'; import { autoColor, createStyle, Theme, useTheme } from '../../Theme'; import { UIDivider } from '../../uiElementsContext'; import { ubFlex, ubFlexAuto, ubItemsCenter, ubM0, ubMb1, ubMy1, ubTxRightAlign } from '../../uberUtilityStyles'; -import { FieldLink, TextArea } from '@grafana/ui'; +import { DataLinkButton, TextArea } from '@grafana/ui'; import { CreateSpanLink } from '../types'; const getStyles = createStyle((theme: Theme) => { @@ -183,7 +183,7 @@ export default function SpanDetail(props: SpanDetailProps) { {link ? ( - + ) : null}
diff --git a/public/app/plugins/datasource/prometheus/components/PromExemplarField.tsx b/public/app/plugins/datasource/prometheus/components/PromExemplarField.tsx index 6e43b313018..0b21afcbd50 100644 --- a/public/app/plugins/datasource/prometheus/components/PromExemplarField.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromExemplarField.tsx @@ -15,7 +15,7 @@ const onExemplarsChange = ({ query, onChange }: Props) => (e: React.ChangeEvent< export function PromExemplarField(props: Props) { return ( - + ); } diff --git a/public/app/plugins/datasource/prometheus/result_transformer.ts b/public/app/plugins/datasource/prometheus/result_transformer.ts index f530cc12f43..21c2a4b5023 100644 --- a/public/app/plugins/datasource/prometheus/result_transformer.ts +++ b/public/app/plugins/datasource/prometheus/result_transformer.ts @@ -165,8 +165,9 @@ function getDataLinks(options: ExemplarTraceIdDestination): DataLink[] { if (options.url) { dataLinks.push({ - title: 'Open link', + title: `Go to ${options.url}`, url: options.url, + targetBlank: true, }); } return dataLinks; diff --git a/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx b/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx index d4acd1ee778..eedd1997a80 100644 --- a/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx +++ b/public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx @@ -8,7 +8,7 @@ import { systemDateFormats, TimeZone, } from '@grafana/data'; -import { FieldLink, Portal, TooltipContainer, useStyles } from '@grafana/ui'; +import { FieldLinkList, Portal, TooltipContainer, useStyles } from '@grafana/ui'; import { css, cx } from 'emotion'; import React, { useCallback, useRef, useState } from 'react'; @@ -78,17 +78,12 @@ export const ExemplarMarker: React.FC = ({ timeZone, dataFr const links = field.config.links?.length ? getFieldLinks(field, index) : undefined; return ( - {field.name} - - {field.type === FieldType.time ? timeFormatter(value) : value}{' '} - {links && - links.map((link, i) => { - return ( -
- -
- ); - })} + {field.name} + +
+ {field.type === FieldType.time ? timeFormatter(value) : value} + {links && } +
); @@ -179,7 +174,17 @@ const getExemplarMarkerStyles = (theme: GrafanaTheme) => { valueWrapper: css` display: flex; flex-direction: row; - align-items: center; + flex-wrap: wrap; + column-gap: ${theme.spacing.sm}; + + > span { + flex-grow: 0; + } + + > * { + flex: 1 1; + align-self: center; + } `, tooltip: css` background: none; @@ -203,9 +208,6 @@ const getExemplarMarkerStyles = (theme: GrafanaTheme) => { padding: ${theme.spacing.sm}; font-weight: ${theme.typography.weight.semibold}; `, - link: css` - margin: 0 ${theme.spacing.sm}; - `, marble, activeMarble, };