Alerting: List V2 - Add a group link to the rule list item (#108960)
* Add group link to the aler rule breadcrumbs in the new list view * Remove redundant comments
This commit is contained in:
@@ -5,6 +5,7 @@ import { PromRuleType, RulerRuleDTO, RulesSourceApplication } from 'app/types/un
|
||||
|
||||
import { createReturnTo } from '../hooks/useReturnTo';
|
||||
import { Annotation } from '../utils/constants';
|
||||
import { groups } from '../utils/navigation';
|
||||
import { fromRule, fromRulerRule, stringifyIdentifier } from '../utils/rule-id';
|
||||
import { getRuleName, getRulePluginOrigin, rulerRuleType } from '../utils/rules';
|
||||
import { createRelativeUrl } from '../utils/url';
|
||||
@@ -46,11 +47,14 @@ export function DataSourceRuleListItem({
|
||||
const ruleName = rulerRule ? getRuleName(rulerRule) : rule.name;
|
||||
const labels = rulerRule ? rulerRule.labels : rule.labels;
|
||||
|
||||
const groupUrl = groups.detailsPageLink(rulesSource.uid, namespace.name, groupName);
|
||||
|
||||
const commonProps: RuleListItemCommonProps = {
|
||||
name: ruleName,
|
||||
rulesSource: rulesSource,
|
||||
application: application,
|
||||
group: groupName,
|
||||
groupUrl,
|
||||
namespace: namespace.name,
|
||||
href,
|
||||
health: rule.health,
|
||||
|
||||
@@ -107,6 +107,30 @@ describe('RuleList - FilterView', () => {
|
||||
|
||||
expect(await screen.findByText(/No matching rules found/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render group names as clickable links', async () => {
|
||||
render(
|
||||
<FilterView
|
||||
filterState={getFilter({
|
||||
dataSourceNames: ['Mimir'],
|
||||
groupName: 'test-group-4501',
|
||||
ruleName: 'mimir-test-rule-1',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
await loadMoreResults();
|
||||
|
||||
const groupLink = await screen.findByRole('link', {
|
||||
name: 'test-group-4501',
|
||||
});
|
||||
|
||||
expect(groupLink).toBeInTheDocument();
|
||||
expect(groupLink).toHaveAttribute(
|
||||
'href',
|
||||
'/alerting/mimir/namespaces/test-mimir-namespace/groups/test-group-4501/view'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
async function loadMoreResults() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { GrafanaRuleGroupIdentifier } from 'app/types/unified-alerting';
|
||||
import { GrafanaPromRuleDTO, PromRuleType } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { GrafanaRulesSource } from '../utils/datasource';
|
||||
import { GRAFANA_RULES_SOURCE_NAME, GrafanaRulesSource } from '../utils/datasource';
|
||||
import { groups } from '../utils/navigation';
|
||||
import { totalFromStats } from '../utils/ruleStats';
|
||||
import { prometheusRuleType } from '../utils/rules';
|
||||
import { createRelativeUrl } from '../utils/url';
|
||||
@@ -32,10 +33,17 @@ export function GrafanaRuleListItem({
|
||||
}: GrafanaRuleListItemProps) {
|
||||
const { name, uid, labels, provenance } = rule;
|
||||
|
||||
const groupUrl = groups.detailsPageLink(
|
||||
GRAFANA_RULES_SOURCE_NAME,
|
||||
groupIdentifier.namespace.uid,
|
||||
groupIdentifier.groupName
|
||||
);
|
||||
|
||||
const commonProps: RuleListItemCommonProps = {
|
||||
name,
|
||||
rulesSource: GrafanaRulesSource,
|
||||
group: groupIdentifier.groupName,
|
||||
groupUrl,
|
||||
namespace: namespaceName,
|
||||
href: createRelativeUrl(`/alerting/grafana/${uid}/view`),
|
||||
health: rule?.health,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ReactNode, useEffect, useId } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Alert, Icon, Stack, Text, TextLink, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { Alert, Stack, Text, TextLink, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { Rule, RuleGroupIdentifierV2, RuleHealth, RulesSourceIdentifier } from 'app/types/unified-alerting';
|
||||
import { Labels, PromAlertingRuleState, RulerRuleDTO, RulesSourceApplication } from 'app/types/unified-alerting-dto';
|
||||
|
||||
@@ -20,8 +20,8 @@ import { createContactPointSearchLink } from '../../utils/misc';
|
||||
import { RulePluginOrigin } from '../../utils/rules';
|
||||
|
||||
import { ListItem } from './ListItem';
|
||||
import { DataSourceIcon } from './Namespace';
|
||||
import { RuleListIcon, RuleOperation } from './RuleListIcon';
|
||||
import { RuleLocation } from './RuleLocation';
|
||||
import { calculateNextEvaluationEstimate } from './util';
|
||||
|
||||
export interface AlertRuleListItemProps {
|
||||
@@ -39,6 +39,7 @@ export interface AlertRuleListItemProps {
|
||||
instancesCount?: number;
|
||||
namespace?: string;
|
||||
group?: string;
|
||||
groupUrl?: string;
|
||||
rulesSource?: RulesSourceIdentifier;
|
||||
application?: RulesSourceApplication;
|
||||
// used for alert rules that use simplified routing
|
||||
@@ -65,6 +66,7 @@ export const AlertRuleListItem = (props: AlertRuleListItemProps) => {
|
||||
instancesCount = 0,
|
||||
namespace,
|
||||
group,
|
||||
groupUrl,
|
||||
rulesSource,
|
||||
application,
|
||||
contactPoint,
|
||||
@@ -81,7 +83,13 @@ export const AlertRuleListItem = (props: AlertRuleListItemProps) => {
|
||||
if (namespace && group && showLocation) {
|
||||
metadata.push(
|
||||
<Text color="secondary" variant="bodySmall">
|
||||
<RuleLocation namespace={namespace} group={group} rulesSource={rulesSource} application={application} />
|
||||
<RuleLocation
|
||||
namespace={namespace}
|
||||
group={group}
|
||||
groupUrl={groupUrl}
|
||||
rulesSource={rulesSource}
|
||||
application={application}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@@ -160,6 +168,7 @@ export function RecordingRuleListItem({
|
||||
name,
|
||||
namespace,
|
||||
group,
|
||||
groupUrl,
|
||||
rulesSource,
|
||||
application,
|
||||
href,
|
||||
@@ -175,7 +184,13 @@ export function RecordingRuleListItem({
|
||||
if (namespace && group && showLocation) {
|
||||
metadata.push(
|
||||
<Text color="secondary" variant="bodySmall">
|
||||
<RuleLocation namespace={namespace} group={group} rulesSource={rulesSource} application={application} />
|
||||
<RuleLocation
|
||||
namespace={namespace}
|
||||
group={group}
|
||||
groupUrl={groupUrl}
|
||||
rulesSource={rulesSource}
|
||||
application={application}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@@ -206,6 +221,7 @@ interface RuleOperationListItemProps {
|
||||
name: string;
|
||||
namespace: string;
|
||||
group: string;
|
||||
groupUrl?: string;
|
||||
rulesSource?: RulesSourceIdentifier;
|
||||
application?: RulesSourceApplication;
|
||||
operation: RuleOperation;
|
||||
@@ -216,6 +232,7 @@ export function RuleOperationListItem({
|
||||
name,
|
||||
namespace,
|
||||
group,
|
||||
groupUrl,
|
||||
rulesSource,
|
||||
application,
|
||||
operation,
|
||||
@@ -227,7 +244,13 @@ export function RuleOperationListItem({
|
||||
if (namespace && group && showLocation) {
|
||||
metadata.push(
|
||||
<Text color="secondary" variant="bodySmall">
|
||||
<RuleLocation namespace={namespace} group={group} rulesSource={rulesSource} application={application} />
|
||||
<RuleLocation
|
||||
namespace={namespace}
|
||||
group={group}
|
||||
groupUrl={groupUrl}
|
||||
rulesSource={rulesSource}
|
||||
application={application}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@@ -368,38 +391,6 @@ export const UnknownRuleListItem = ({ ruleName, groupIdentifier, ruleDefinition
|
||||
);
|
||||
};
|
||||
|
||||
interface RuleLocationProps {
|
||||
namespace: string;
|
||||
group: string;
|
||||
rulesSource?: RulesSourceIdentifier;
|
||||
application?: RulesSourceApplication;
|
||||
}
|
||||
|
||||
// @TODO make the datasource / namespace / group click-able to allow further filtering of the list
|
||||
export const RuleLocation = ({ namespace, group, rulesSource, application }: RuleLocationProps) => {
|
||||
const isGrafanaApp = application === 'grafana';
|
||||
const isDataSourceApp = !!rulesSource && !!application && !isGrafanaApp;
|
||||
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" gap={0.5}>
|
||||
{isGrafanaApp && <Icon size="xs" name="folder" />}
|
||||
{isDataSourceApp && (
|
||||
<Tooltip content={rulesSource.name}>
|
||||
<span>
|
||||
<DataSourceIcon application={application} size={14} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Stack direction="row" alignItems="center" gap={0}>
|
||||
{namespace}
|
||||
<Icon size="sm" name="angle-right" />
|
||||
{group}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
alertListItemContainer: css({
|
||||
position: 'relative',
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Icon, Stack, TextLink, Tooltip } from '@grafana/ui';
|
||||
import { RulesSourceIdentifier } from 'app/types/unified-alerting';
|
||||
import { RulesSourceApplication } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { DataSourceIcon } from './Namespace';
|
||||
|
||||
interface RuleLocationProps {
|
||||
namespace: string;
|
||||
group: string;
|
||||
groupUrl?: string;
|
||||
rulesSource?: RulesSourceIdentifier;
|
||||
application?: RulesSourceApplication;
|
||||
}
|
||||
|
||||
export function RuleLocation({ namespace, group, groupUrl, rulesSource, application }: RuleLocationProps) {
|
||||
const isGrafanaApp = application === 'grafana';
|
||||
const isDataSourceApp = !!rulesSource && !!application && !isGrafanaApp;
|
||||
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" gap={0.5}>
|
||||
{isGrafanaApp && <Icon size="xs" name="folder" />}
|
||||
{isDataSourceApp && (
|
||||
<Tooltip content={rulesSource.name}>
|
||||
<span>
|
||||
<DataSourceIcon application={application} size={14} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Stack direction="row" alignItems="center" gap={0}>
|
||||
{namespace}
|
||||
<Icon size="sm" name="angle-right" />
|
||||
{groupUrl ? (
|
||||
<TextLink href={groupUrl} color="secondary" variant="bodySmall" inline={false}>
|
||||
{group}
|
||||
</TextLink>
|
||||
) : (
|
||||
group
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user