2025-04-01 10:38:02 +09:00

99 lines
2.9 KiB
TypeScript

import { css } from '@emotion/css';
import { take, takeRight, uniqueId } from 'lodash';
import { FC } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack, getTagColorsFromName, useStyles2 } from '@grafana/ui';
import { ObjectMatcher } from 'app/plugins/datasource/alertmanager/types';
import { MatcherFormatter, matcherFormatter } from '../../utils/matchers';
import { PopupCard } from '../HoverCard';
type MatchersProps = { matchers: ObjectMatcher[]; formatter?: MatcherFormatter };
// renders the first N number of matchers
const Matchers: FC<MatchersProps> = ({ matchers, formatter = 'default' }) => {
const styles = useStyles2(getStyles);
const NUM_MATCHERS = 5;
const firstFew = take(matchers, NUM_MATCHERS);
const rest = takeRight(matchers, matchers.length - NUM_MATCHERS);
const hasMoreMatchers = rest.length > 0;
return (
<span data-testid="label-matchers">
<Stack direction="row" gap={1} alignItems="center" wrap={'wrap'}>
{firstFew.map((matcher) => (
<MatcherBadge key={uniqueId()} matcher={matcher} formatter={formatter} />
))}
{/* TODO hover state to show all matchers we're not showing */}
{hasMoreMatchers && (
<PopupCard
arrow
placement="top"
content={
<>
{rest.map((matcher) => (
<MatcherBadge key={uniqueId()} matcher={matcher} />
))}
</>
}
>
<span>
<div className={styles.metadata}>{`and ${rest.length} more`}</div>
</span>
</PopupCard>
)}
</Stack>
</span>
);
};
interface MatcherBadgeProps {
matcher: ObjectMatcher;
formatter?: MatcherFormatter;
}
const MatcherBadge: FC<MatcherBadgeProps> = ({ matcher, formatter = 'default' }) => {
const styles = useStyles2(getStyles);
return (
<div className={styles.matcher(matcher[0]).wrapper}>
<Stack direction="row" gap={0} alignItems="baseline">
{matcherFormatter[formatter](matcher)}
</Stack>
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
matcher: (label: string) => {
const { color, borderColor } = getTagColorsFromName(label);
return {
wrapper: css({
color: '#fff',
background: color,
padding: `${theme.spacing(0.33)} ${theme.spacing(0.66)}`,
fontSize: theme.typography.bodySmall.fontSize,
border: `solid 1px ${borderColor}`,
borderRadius: theme.shape.borderRadius(2),
// Ensure we preserve whitespace, as otherwise it's not noticeable _at all_
// when rendering the matcher, and is only noticeable when editing
whiteSpace: 'pre',
}),
};
},
metadata: css({
color: theme.colors.text.secondary,
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.bodySmall.fontWeight,
}),
});
export { Matchers };