mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 07:32:13 +08:00
Alerting: Several list view fixes (#106902)
This commit is contained in:
@ -93,6 +93,7 @@ const RuleViewer = () => {
|
||||
// we want to be able to show a modal if the rule has been provisioned explain the limitations
|
||||
// of duplicating provisioned alert rules
|
||||
const [duplicateRuleIdentifier, setDuplicateRuleIdentifier] = useState<RuleIdentifier>();
|
||||
const { returnTo } = useReturnTo('/alerting/list');
|
||||
const { annotations, promRule, rulerRule } = rule;
|
||||
|
||||
const hasError = isErrorHealth(promRule?.health);
|
||||
@ -119,7 +120,7 @@ const RuleViewer = () => {
|
||||
health={promRule?.health}
|
||||
ruleType={promRule?.type}
|
||||
ruleOrigin={ruleOrigin}
|
||||
returnToHref="/alerting/list"
|
||||
returnToHref={returnTo}
|
||||
/>
|
||||
)}
|
||||
actions={<RuleActionsButtons rule={rule} rulesSource={rule.namespace.rulesSource} />}
|
||||
|
@ -57,7 +57,7 @@ describe('DataSourceGroupLoader', () => {
|
||||
|
||||
promGroup.rules.forEach((rule, index) => {
|
||||
const ruleLink = within(ruleListItems[index]).getByRole('link', { name: `prom-only-rule-${index + 1}` });
|
||||
expect(ruleLink).toHaveAttribute('href', createViewLinkV2(groupIdentifier, rule));
|
||||
expect(ruleLink).toHaveAttribute('href', expect.stringContaining(createViewLinkV2(groupIdentifier, rule)));
|
||||
});
|
||||
});
|
||||
|
||||
@ -103,7 +103,7 @@ describe('DataSourceGroupLoader', () => {
|
||||
expect(ruleListItems).toHaveLength(1);
|
||||
|
||||
const ruleLink = within(ruleListItems[0]).getByRole('link', { name: 'mimir-rule-1' });
|
||||
expect(ruleLink).toHaveAttribute('href', getRuleLink(groupIdentifier, rulerRule));
|
||||
expect(ruleLink).toHaveAttribute('href', expect.stringContaining(getRuleLink(groupIdentifier, rulerRule)));
|
||||
});
|
||||
|
||||
it('should render Edit and More buttons for rules that are present in ruler and prometheus', async () => {
|
||||
|
@ -141,6 +141,7 @@ export function DataSourceGroupLoader({ groupIdentifier, expectedRulesCount = 3
|
||||
rule={rule}
|
||||
groupIdentifier={groupIdentifier}
|
||||
application={dsFeatures?.application}
|
||||
showLocation={false}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
@ -194,6 +195,7 @@ export function RulerBasedGroupRules({
|
||||
actions={
|
||||
<RuleActionsButtons rule={rulerRule} promRule={promRule} groupIdentifier={groupIdentifier} compact />
|
||||
}
|
||||
showLocation={false}
|
||||
/>
|
||||
) : (
|
||||
<RuleOperationListItem
|
||||
@ -204,6 +206,7 @@ export function RulerBasedGroupRules({
|
||||
rulesSource={groupIdentifier.rulesSource}
|
||||
application={application}
|
||||
operation={RuleOperation.Creating}
|
||||
showLocation={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -216,6 +219,7 @@ export function RulerBasedGroupRules({
|
||||
rulesSource={groupIdentifier.rulesSource}
|
||||
application={application}
|
||||
operation={RuleOperation.Deleting}
|
||||
showLocation={false}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
||||
import { DataSourceRuleGroupIdentifier, Rule, RuleIdentifier } from 'app/types/unified-alerting';
|
||||
import { PromRuleType, RulerRuleDTO, RulesSourceApplication } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { createReturnTo } from '../hooks/useReturnTo';
|
||||
import { Annotation } from '../utils/constants';
|
||||
import { fromRule, fromRulerRule, stringifyIdentifier } from '../utils/rule-id';
|
||||
import { getRuleName, getRulePluginOrigin, rulerRuleType } from '../utils/rules';
|
||||
@ -21,6 +22,7 @@ export interface DataSourceRuleListItemProps {
|
||||
groupIdentifier: DataSourceRuleGroupIdentifier;
|
||||
application?: RulesSourceApplication;
|
||||
actions?: React.ReactNode;
|
||||
showLocation?: boolean;
|
||||
}
|
||||
|
||||
export function DataSourceRuleListItem({
|
||||
@ -29,13 +31,15 @@ export function DataSourceRuleListItem({
|
||||
groupIdentifier,
|
||||
application,
|
||||
actions,
|
||||
showLocation = true,
|
||||
}: DataSourceRuleListItemProps) {
|
||||
const returnTo = createReturnTo();
|
||||
const { rulesSource, namespace, groupName } = groupIdentifier;
|
||||
|
||||
const ruleIdentifier = rulerRule
|
||||
? fromRulerRule(rulesSource.name, namespace.name, groupName, rulerRule)
|
||||
: fromRule(rulesSource.name, namespace.name, groupName, rule);
|
||||
const href = createViewLinkFromIdentifier(ruleIdentifier);
|
||||
const href = createViewLinkFromIdentifier(ruleIdentifier, returnTo);
|
||||
const originMeta = getRulePluginOrigin(rule);
|
||||
|
||||
// If ruler rule is available, we should use it as it contains fresh data
|
||||
@ -54,6 +58,7 @@ export function DataSourceRuleListItem({
|
||||
labels,
|
||||
actions,
|
||||
origin: originMeta,
|
||||
showLocation,
|
||||
};
|
||||
|
||||
switch (rule.type) {
|
||||
|
@ -49,7 +49,10 @@ describe('GrafanaGroupLoader', () => {
|
||||
expect(ruleStatus).toBeInTheDocument();
|
||||
|
||||
const ruleLink = ui.ruleLink(rule1.grafana_alert.title).get(ruleListItem);
|
||||
expect(ruleLink).toHaveAttribute('href', `/alerting/grafana/${rule1.grafana_alert.uid}/view`);
|
||||
expect(ruleLink).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/alerting/grafana/${rule1.grafana_alert.uid}/view`)
|
||||
);
|
||||
});
|
||||
|
||||
it('should render rule with url and creating state when only ruler rule exists', async () => {
|
||||
@ -66,7 +69,10 @@ describe('GrafanaGroupLoader', () => {
|
||||
expect(creatingIcon).toBeInTheDocument();
|
||||
|
||||
const ruleLink = ui.ruleLink(rule1.grafana_alert.title).get(ruleListItem);
|
||||
expect(ruleLink).toHaveAttribute('href', `/alerting/grafana/${rule1.grafana_alert.uid}/view`);
|
||||
expect(ruleLink).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/alerting/grafana/${rule1.grafana_alert.uid}/view`)
|
||||
);
|
||||
});
|
||||
|
||||
it('should render delete rule operation list item when only prom rule exists', async () => {
|
||||
|
@ -98,6 +98,7 @@ export function GrafanaGroupLoader({
|
||||
groupIdentifier={groupIdentifier}
|
||||
namespaceName={namespaceName}
|
||||
operation={RuleOperation.Creating}
|
||||
showLocation={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -109,6 +110,8 @@ export function GrafanaGroupLoader({
|
||||
rulerRule={rulerRule}
|
||||
groupIdentifier={groupIdentifier}
|
||||
namespaceName={namespaceName}
|
||||
// we don't show the location again for rules, it's redundant because they are shown in a folder > group hierarchy
|
||||
showLocation={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -121,6 +124,7 @@ export function GrafanaGroupLoader({
|
||||
rulesSource={GrafanaRulesSource}
|
||||
application="grafana"
|
||||
operation={RuleOperation.Deleting}
|
||||
showLocation={false}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
@ -5,6 +5,7 @@ import { GrafanaPromRuleDTO, PromRuleType, RulerGrafanaRuleDTO } from 'app/types
|
||||
|
||||
import { alertRuleApi } from '../api/alertRuleApi';
|
||||
import { prometheusApi } from '../api/prometheusApi';
|
||||
import { createReturnTo } from '../hooks/useReturnTo';
|
||||
import { GrafanaRulesSource } from '../utils/datasource';
|
||||
import { totalFromStats } from '../utils/ruleStats';
|
||||
import { rulerRuleType } from '../utils/rules';
|
||||
@ -91,6 +92,7 @@ interface GrafanaRuleListItemProps {
|
||||
groupIdentifier: GrafanaRuleGroupIdentifier;
|
||||
namespaceName: string;
|
||||
operation?: RuleOperation;
|
||||
showLocation?: boolean;
|
||||
}
|
||||
|
||||
export function GrafanaRuleListItem({
|
||||
@ -99,7 +101,10 @@ export function GrafanaRuleListItem({
|
||||
groupIdentifier,
|
||||
namespaceName,
|
||||
operation,
|
||||
showLocation = true,
|
||||
}: GrafanaRuleListItemProps) {
|
||||
const returnTo = createReturnTo();
|
||||
|
||||
const {
|
||||
grafana_alert: { uid, title, provenance, is_paused },
|
||||
annotations = {},
|
||||
@ -111,7 +116,7 @@ export function GrafanaRuleListItem({
|
||||
rulesSource: GrafanaRulesSource,
|
||||
group: groupIdentifier.groupName,
|
||||
namespace: namespaceName,
|
||||
href: createRelativeUrl(`/alerting/grafana/${uid}/view`),
|
||||
href: createRelativeUrl(`/alerting/grafana/${uid}/view`, { returnTo }),
|
||||
health: rule?.health,
|
||||
error: rule?.lastError,
|
||||
labels: labels,
|
||||
@ -119,6 +124,7 @@ export function GrafanaRuleListItem({
|
||||
isPaused: rule?.isPaused ?? is_paused,
|
||||
application: 'grafana' as const,
|
||||
actions: <RuleActionsButtons rule={rulerRule} promRule={rule} groupIdentifier={groupIdentifier} compact />,
|
||||
showLocation,
|
||||
};
|
||||
|
||||
if (rulerRuleType.grafana.alertingRule(rulerRule)) {
|
||||
|
@ -45,6 +45,8 @@ export interface AlertRuleListItemProps {
|
||||
actions?: ReactNode;
|
||||
origin?: RulePluginOrigin;
|
||||
operation?: RuleOperation;
|
||||
// the grouped view doesn't need to show the location again – it's redundant
|
||||
showLocation?: boolean;
|
||||
}
|
||||
|
||||
export const AlertRuleListItem = (props: AlertRuleListItemProps) => {
|
||||
@ -69,12 +71,13 @@ export const AlertRuleListItem = (props: AlertRuleListItemProps) => {
|
||||
origin,
|
||||
actions = null,
|
||||
operation,
|
||||
showLocation = true,
|
||||
} = props;
|
||||
|
||||
const listItemAriaId = useId();
|
||||
|
||||
const metadata: ReactNode[] = [];
|
||||
if (namespace && group) {
|
||||
if (namespace && group && showLocation) {
|
||||
metadata.push(
|
||||
<Text color="secondary" variant="bodySmall">
|
||||
<RuleLocation namespace={namespace} group={group} rulesSource={rulesSource} application={application} />
|
||||
@ -167,9 +170,10 @@ export function RecordingRuleListItem({
|
||||
isPaused,
|
||||
origin,
|
||||
actions,
|
||||
showLocation = true,
|
||||
}: RecordingRuleListItemProps) {
|
||||
const metadata: ReactNode[] = [];
|
||||
if (namespace && group) {
|
||||
if (namespace && group && showLocation) {
|
||||
metadata.push(
|
||||
<Text color="secondary" variant="bodySmall">
|
||||
<RuleLocation namespace={namespace} group={group} rulesSource={rulesSource} application={application} />
|
||||
@ -206,6 +210,7 @@ interface RuleOperationListItemProps {
|
||||
rulesSource?: RulesSourceIdentifier;
|
||||
application?: RulesSourceApplication;
|
||||
operation: RuleOperation;
|
||||
showLocation?: boolean;
|
||||
}
|
||||
|
||||
export function RuleOperationListItem({
|
||||
@ -215,11 +220,12 @@ export function RuleOperationListItem({
|
||||
rulesSource,
|
||||
application,
|
||||
operation,
|
||||
showLocation = true,
|
||||
}: RuleOperationListItemProps) {
|
||||
const listItemAriaId = useId();
|
||||
|
||||
const metadata: ReactNode[] = [];
|
||||
if (namespace && group) {
|
||||
if (namespace && group && showLocation) {
|
||||
metadata.push(
|
||||
<Text color="secondary" variant="bodySmall">
|
||||
<RuleLocation namespace={namespace} group={group} rulesSource={rulesSource} application={application} />
|
||||
@ -256,7 +262,7 @@ function Summary({ content, error }: SummaryProps) {
|
||||
}
|
||||
if (content) {
|
||||
return (
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
<Text variant="bodySmall" color="secondary" truncate>
|
||||
{content}
|
||||
</Text>
|
||||
);
|
||||
|
Reference in New Issue
Block a user