Alerting: Add empty state to triage page WIP (#113390)

* add empty state to triage page WIP

* tidy up and refactor

* generate translations

* resolve PR comments

* generate translations

* resolve PR comment part 2
This commit is contained in:
Lauren
2025-11-05 08:46:41 +00:00
committed by GitHub
parent 891ed6c4b7
commit 1d38cf7f0d
3 changed files with 69 additions and 31 deletions
@@ -4,8 +4,9 @@ import { useState } from 'react';
import { useMeasure } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
import { Trans } from '@grafana/i18n';
import { SceneQueryRunner } from '@grafana/scenes';
import { ScrollContainer, useSplitter, useStyles2 } from '@grafana/ui';
import { Box, EmptyState, ScrollContainer, useSplitter, useStyles2 } from '@grafana/ui';
import { DEFAULT_PER_PAGE_PAGINATION } from 'app/core/constants';
import LoadMoreHelper from '../rule-list/LoadMoreHelper';
@@ -26,6 +27,7 @@ type WorkbenchProps = {
groupBy?: string[];
filterBy?: Filter[];
queryRunner: SceneQueryRunner;
hasActiveFilters?: boolean;
};
const initialSize = 1 / 3;
@@ -116,7 +118,7 @@ function renderWorkbenchRow(
│ │ │ │
└─────────────────────────┘ └───────────────────────────────────┘
*/
export function Workbench({ domain, data, queryRunner, groupBy }: WorkbenchProps) {
export function Workbench({ domain, data, queryRunner, groupBy, hasActiveFilters = false }: WorkbenchProps) {
const styles = useStyles2(getStyles);
const isLoading = !queryRunner.isDataReadyToDisplay();
@@ -124,6 +126,10 @@ export function Workbench({ domain, data, queryRunner, groupBy }: WorkbenchProps
// Calculate once: show folder metadata only if not grouping by grafana_folder
const enableFolderMeta = !groupBy?.includes('grafana_folder');
// Determine UI state
const showEmptyState = !isLoading && data.length === 0;
const showData = !isLoading && data.length > 0;
// splitter for template and payload editor
const splitter = useSplitter({
direction: 'row',
@@ -149,42 +155,62 @@ export function Workbench({ domain, data, queryRunner, groupBy }: WorkbenchProps
<div {...splitter.primaryProps}>
<div ref={leftColumnRef} className={cx(styles.flexFull, styles.minColumnWidth)} />
</div>
<div {...splitter.splitterProps} />
{!showEmptyState && <div {...splitter.splitterProps} />}
<div {...splitter.secondaryProps}>
<div ref={rightColumnRef} className={cx(styles.flexFull, styles.minColumnWidth)} />
</div>
</div>
{/* content goes here */}
<div data-testid="groups-container" className={cx(splitter.containerProps.className, styles.groupsContainer)}>
<div className={cx(styles.groupItemWrapper(leftColumnWidth), styles.summaryContainer)}>
<SummaryStatsReact />
<SummaryChartReact />
</div>
{/* Render actual data */}
<div className={styles.virtualizedContainer}>
<WorkbenchProvider
leftColumnWidth={leftColumnWidth}
rightColumnWidth={rightColumnWidth}
domain={domain}
queryRunner={queryRunner}
>
<ScrollContainer height="100%" width="100%" scrollbarWidth="none" showScrollIndicators>
{isLoading ? (
<>
<GenericRowSkeleton key="skeleton-1" width={leftColumnWidth} depth={0} />
<GenericRowSkeleton key="skeleton-2" width={leftColumnWidth} depth={0} />
<GenericRowSkeleton key="skeleton-3" width={leftColumnWidth} depth={0} />
</>
{showEmptyState ? (
<Box display="flex" alignItems="center" justifyContent="center" width="100%" height="100%" minHeight="400px">
<EmptyState
variant="not-found"
message={hasActiveFilters ? 'No matching instances found' : 'No firing or pending instances'}
>
{hasActiveFilters ? (
<Trans i18nKey="alerting.triage.no-matching-instances-with-filters">
No alert instances match your current set of filters for the selected time range.
</Trans>
) : (
dataSlice.map((row, index) => {
const rowKey = generateRowKey(row, index);
return renderWorkbenchRow(row, leftColumnWidth, domain, rowKey, enableFolderMeta);
})
<Trans i18nKey="alerting.triage.no-firing-or-pending-instances">
You have no alert instances in a firing or pending state for the selected time range.
</Trans>
)}
{hasMore && <LoadMoreHelper handleLoad={() => setPageIndex((prevIndex) => prevIndex + 1)} />}
</ScrollContainer>
</WorkbenchProvider>
</div>
</EmptyState>
</Box>
) : (
<>
<div className={cx(styles.groupItemWrapper(leftColumnWidth), styles.summaryContainer)}>
<SummaryStatsReact />
<SummaryChartReact />
</div>
<div className={styles.virtualizedContainer}>
<WorkbenchProvider
leftColumnWidth={leftColumnWidth}
rightColumnWidth={rightColumnWidth}
domain={domain}
queryRunner={queryRunner}
>
<ScrollContainer height="100%" width="100%" scrollbarWidth="none" showScrollIndicators={showData}>
{isLoading && (
<>
<GenericRowSkeleton key="skeleton-1" width={leftColumnWidth} depth={0} />
<GenericRowSkeleton key="skeleton-2" width={leftColumnWidth} depth={0} />
<GenericRowSkeleton key="skeleton-3" width={leftColumnWidth} depth={0} />
</>
)}
{showData &&
dataSlice.map((row, index) => {
const rowKey = generateRowKey(row, index);
return renderWorkbenchRow(row, leftColumnWidth, domain, rowKey, enableFolderMeta);
})}
{hasMore && <LoadMoreHelper handleLoad={() => setPageIndex((prevIndex) => prevIndex + 1)} />}
</ScrollContainer>
</WorkbenchProvider>
</div>
</>
)}
</div>
</div>
);
@@ -34,7 +34,17 @@ export function WorkbenchRenderer() {
const { data } = runner.useState();
const rows = data ? convertToWorkbenchRows(data, groupByKeys) : [];
return <Workbench data={rows} domain={domain} queryRunner={runner} groupBy={groupByKeys} />;
const hasFiltersApplied = queryFilter.length > 0;
return (
<Workbench
data={rows}
domain={domain}
queryRunner={runner}
groupBy={groupByKeys}
hasActiveFilters={hasFiltersApplied}
/>
);
}
type DataPoint = Record<ArrayValues<typeof DEFAULT_FIELDS>, string> & Record<string, string | undefined>;
+2
View File
@@ -2948,8 +2948,10 @@
"instance-details-drawer": {
"instance-details": "Instance details"
},
"no-firing-or-pending-instances": "You have no alert instances in a firing or pending state for the selected time range.",
"no-instances-found": "No alert instances found for rule: {{ruleUID}}",
"no-labels": "No labels",
"no-matching-instances-with-filters": "No alert instances match your current set of filters for the selected time range.",
"open-in-sidebar": "Open in sidebar",
"open-rule-details": "Open rule details",
"rule-details": {