diff --git a/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx b/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx index 06d2e65d0b8..679bca8cb59 100644 --- a/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx +++ b/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { useEffect } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { VizPanel, sceneGraph } from '@grafana/scenes'; +import { SceneGridRow, VizPanel, sceneGraph } from '@grafana/scenes'; import { useStyles2 } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; @@ -34,26 +34,33 @@ export function PanelSearchLayout({ dashboard, panelSearch = '', panelsPerRow }: for (const gridItem of bodyGrid.state.children) { if (gridItem instanceof DashboardGridItem) { - const panels = gridItem.state.repeatedPanels ?? [gridItem.state.body]; - for (const panel of panels) { - const interpolatedTitle = panel.interpolate(panel.state.title, undefined, 'text').toLowerCase(); - const interpolatedSearchString = sceneGraph.interpolate(dashboard, panelSearch).toLowerCase(); - if (interpolatedTitle.includes(interpolatedSearchString)) { - filteredPanels.push(panel); + filterPanels(gridItem, dashboard, panelSearch, filteredPanels); + } else if (gridItem instanceof SceneGridRow) { + for (const rowItem of gridItem.state.children) { + if (rowItem instanceof DashboardGridItem) { + filterPanels(rowItem, dashboard, panelSearch, filteredPanels); } } } } + if (filteredPanels.length > 0) { + return ( +
} + > + {filteredPanels.map((panel) => ( + + ))} +
+ ); + } + return ( -
} - > - {filteredPanels.map((panel) => ( - - ))} -
+

+ No matches found +

); } @@ -74,5 +81,37 @@ function getStyles(theme: GrafanaTheme2) { perRow: css({ gridTemplateColumns: `repeat(var(${panelsPerRowCSSVar}, 3), 1fr)`, }), + noHits: css({ + display: 'grid', + placeItems: 'center', + }), }; } + +function filterPanels( + gridItem: DashboardGridItem, + dashboard: DashboardScene, + searchString: string, + filteredPanels: VizPanel[] +) { + const interpolatedSearchString = sceneGraph.interpolate(dashboard, searchString).toLowerCase(); + + // activate inactive repeat panel if one of its children will be matched + if (gridItem.state.variableName && !gridItem.isActive) { + const panel = gridItem.state.body; + const interpolatedTitle = panel.interpolate(panel.state.title, undefined, 'text').toLowerCase(); + if (interpolatedTitle.includes(interpolatedSearchString)) { + gridItem.activate(); + } + } + + const panels = gridItem.state.repeatedPanels ?? [gridItem.state.body]; + for (const panel of panels) { + const interpolatedTitle = panel.interpolate(panel.state.title, undefined, 'text').toLowerCase(); + if (interpolatedTitle.includes(interpolatedSearchString)) { + filteredPanels.push(panel); + } + } + + return filteredPanels; +} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index b03939b26b6..63d9bb626dc 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1924,6 +1924,7 @@ } }, "panel-search": { + "no-matches": "No matches found", "unsupported-layout": "Unsupported layout" }, "playlist-edit": { diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 820267b08dc..fabb719f921 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -1924,6 +1924,7 @@ } }, "panel-search": { + "no-matches": "Ńő mäŧčĥęş ƒőūʼnđ", "unsupported-layout": "Ůʼnşūppőřŧęđ ľäyőūŧ" }, "playlist-edit": {