Alerting: Several list view fixes (#106902)

bogdan/dashboard-banners
Gilles De Mey 1 month ago committed by GitHub
parent a7c65b4e0a
commit 3ab54a5562
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      public/app/features/alerting/unified/components/rule-viewer/RuleViewer.tsx
  2. 4
      public/app/features/alerting/unified/rule-list/DataSourceGroupLoader.test.tsx
  3. 4
      public/app/features/alerting/unified/rule-list/DataSourceGroupLoader.tsx
  4. 7
      public/app/features/alerting/unified/rule-list/DataSourceRuleListItem.tsx
  5. 10
      public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.test.tsx
  6. 4
      public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx
  7. 8
      public/app/features/alerting/unified/rule-list/GrafanaRuleLoader.tsx
  8. 14
      public/app/features/alerting/unified/rule-list/components/AlertRuleListItem.tsx

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

Loading…
Cancel
Save