Exemplars: Change CTA style (#30880)

* Exemplars: Change CTA style

* Address review feedbacks

* Fix table column aligning

* Minor alignments + uncontrolled component warning fix
This commit is contained in:
Zoltán Bedi 2021-02-10 18:02:48 +01:00 committed by GitHub
parent e0448513eb
commit cc463f30a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 28 deletions

View File

@ -33,7 +33,7 @@ export interface DataLink<T extends DataQuery = any> {
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<T>;

View File

@ -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<Field>;
buttonProps?: ButtonProps;
};
export function FieldLink({ link, buttonProps }: FieldLinkProps) {
/**
* @internal
*/
export function DataLinkButton({ link, buttonProps }: DataLinkButtonProps) {
return (
<a
href={link.href}
target="_blank"
target={link.target}
rel="noreferrer"
onClick={
link.onClick
@ -24,7 +27,7 @@ export function FieldLink({ link, buttonProps }: FieldLinkProps) {
: undefined
}
>
<Button icon="external-link-alt" {...buttonProps}>
<Button icon="external-link-alt" variant="primary" size="sm" {...buttonProps}>
{link.title}
</Button>
</a>

View File

@ -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<LinkModel<Field>>;
};
/**
* @internal
*/
export function FieldLinkList({ links }: Props) {
const styles = useStyles(getStyles);
if (links.length === 1) {
return <DataLinkButton link={links[0]} />;
}
const externalLinks = links.filter((link) => link.target === '_blank');
const internalLinks = links.filter((link) => link.target === '_self');
return (
<>
{internalLinks.map((link, i) => {
return <DataLinkButton key={i} link={link} />;
})}
<div className={styles.wrapper}>
<p className={styles.externalLinksHeading}>External links</p>
{externalLinks.map((link, i) => (
<a key={i} href={link.href} target={link.target} className={styles.externalLink}>
<Icon name="external-link-alt" />
{link.title}
</a>
))}
</div>
</>
);
}
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};
}
`,
});

View File

@ -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<Props, State> {
return (
<>
&nbsp;
<FieldLink link={link} />
<DataLinkButton link={link} />
</>
);
})}

View File

@ -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';

View File

@ -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) {
<LabeledList className={ubTxRightAlign} dividerClassName={styles.divider} items={overviewItems} />
</div>
{link ? (
<FieldLink link={{ ...link, title: 'Logs for this span' } as any} buttonProps={{ icon: 'gf-logs' }} />
<DataLinkButton link={{ ...link, title: 'Logs for this span' } as any} buttonProps={{ icon: 'gf-logs' }} />
) : null}
<UIDivider className={cx(styles.divider, styles.dividerVertical, ubMy1)} />
<div>

View File

@ -15,7 +15,7 @@ const onExemplarsChange = ({ query, onChange }: Props) => (e: React.ChangeEvent<
export function PromExemplarField(props: Props) {
return (
<InlineField label="Exemplars" labelWidth="auto">
<InlineSwitch label="Exemplars" value={props.query.exemplar} onChange={onExemplarsChange(props)} />
<InlineSwitch label="Exemplars" value={!!props.query.exemplar} onChange={onExemplarsChange(props)} />
</InlineField>
);
}

View File

@ -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;

View File

@ -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<ExemplarMarkerProps> = ({ timeZone, dataFr
const links = field.config.links?.length ? getFieldLinks(field, index) : undefined;
return (
<tr key={i}>
<td>{field.name}</td>
<td className={styles.valueWrapper}>
{field.type === FieldType.time ? timeFormatter(value) : value}{' '}
{links &&
links.map((link, i) => {
return (
<div key={i} className={styles.link}>
<FieldLink link={link} />
</div>
);
})}
<td valign="top">{field.name}</td>
<td>
<div className={styles.valueWrapper}>
<span>{field.type === FieldType.time ? timeFormatter(value) : value}</span>
{links && <FieldLinkList links={links} />}
</div>
</td>
</tr>
);
@ -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,
};