DynamicDashboards: prevent nested repeats based on the same variable (#114953)

This commit is contained in:
Marc M.
2025-12-16 15:11:23 +01:00
committed by GitHub
parent 5ecfc79e14
commit e03f7fe878
5 changed files with 24 additions and 19 deletions
@@ -107,7 +107,7 @@ function RowRepeatSelect({ row, dashboard, id }: { row: SceneGridRow; dashboard:
<>
<RepeatRowSelect2
id={id}
sceneContext={dashboard}
sceneContext={row}
repeat={repeatBehavior?.state.variableName}
onChange={(repeat) => {
if (repeat) {
@@ -8,7 +8,7 @@ import { Icon, useStyles2 } from '@grafana/ui';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/constants';
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
import { getQueryRunnerFor, useDashboard, useDashboardState } from '../../../utils/utils';
import { getQueryRunnerFor, useDashboardState } from '../../../utils/utils';
import { DashboardGridItem } from '../DashboardGridItem';
import { RowRepeaterBehavior } from '../RowRepeaterBehavior';
@@ -18,7 +18,6 @@ import { RowOptionsButton } from './RowOptionsButton';
export function RowActionsRenderer({ model }: SceneComponentProps<RowActions>) {
const row = model.getParent();
const { title, children } = row.useState();
const dashboard = useDashboard(model);
const { meta, isEditing } = useDashboardState(model);
const styles = useStyles2(getStyles);
@@ -53,7 +52,7 @@ export function RowActionsRenderer({ model }: SceneComponentProps<RowActions>) {
<RowOptionsButton
title={title}
repeat={behaviour instanceof RowRepeaterBehavior ? behaviour.state.variableName : undefined}
parent={dashboard}
parent={row}
onUpdate={(title, repeat) => model.onUpdate(title, repeat)}
isUsingDashboardDS={isUsingDashboardDS}
/>
@@ -2,7 +2,7 @@ import { useId, useMemo, useRef } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { Alert, Input, Switch, TextLink, Field } from '@grafana/ui';
import { Alert, Field, Input, Switch, TextLink } from '@grafana/ui';
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
import { RepeatRowSelect2 } from 'app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect';
@@ -11,7 +11,7 @@ import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSou
import { useConditionalRenderingEditor } from '../../conditional-rendering/hooks/useConditionalRenderingEditor';
import { dashboardEditActions } from '../../edit-pane/shared';
import { getQueryRunnerFor, useDashboard } from '../../utils/utils';
import { getQueryRunnerFor } from '../../utils/utils';
import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector';
import { generateUniqueTitle, useEditPaneInputAutoFocus } from '../layouts-shared/utils';
@@ -128,7 +128,6 @@ function FillScreenSwitch({ row, id }: { row: RowItem; id?: string }) {
function RowRepeatSelect({ row, id }: { row: RowItem; id?: string }) {
const { layout } = row.useState();
const dashboard = useDashboard(row);
const isAnyPanelUsingDashboardDS = layout.getVizPanels().some((vizPanel) => {
const runner = getQueryRunnerFor(vizPanel);
@@ -143,7 +142,7 @@ function RowRepeatSelect({ row, id }: { row: RowItem; id?: string }) {
<>
<RepeatRowSelect2
id={id}
sceneContext={dashboard}
sceneContext={row}
repeat={row.state.repeatByVariable}
onChange={(repeat) => row.onChangeRepeat(repeat)}
/>
@@ -2,7 +2,7 @@ import { useMemo, useRef } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { Alert, Input, Field, TextLink } from '@grafana/ui';
import { Alert, Field, Input, TextLink } from '@grafana/ui';
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
import { RepeatRowSelect2 } from 'app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect';
@@ -11,7 +11,7 @@ import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSou
import { useConditionalRenderingEditor } from '../../conditional-rendering/hooks/useConditionalRenderingEditor';
import { dashboardEditActions } from '../../edit-pane/shared';
import { getQueryRunnerFor, useDashboard } from '../../utils/utils';
import { getQueryRunnerFor } from '../../utils/utils';
import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector';
import { generateUniqueTitle, useEditPaneInputAutoFocus } from '../layouts-shared/utils';
@@ -99,7 +99,6 @@ function TabTitleInput({ tab, isNewElement, id }: { tab: TabItem; isNewElement:
function TabRepeatSelect({ tab, id }: { tab: TabItem; id?: string }) {
const { layout } = tab.useState();
const dashboard = useDashboard(tab);
const isAnyPanelUsingDashboardDS = layout.getVizPanels().some((vizPanel) => {
const runner = getQueryRunnerFor(vizPanel);
@@ -114,7 +113,7 @@ function TabRepeatSelect({ tab, id }: { tab: TabItem; id?: string }) {
<>
<RepeatRowSelect2
id={id}
sceneContext={dashboard}
sceneContext={tab}
repeat={tab.state.repeatByVariable}
onChange={(repeat) => tab.onChangeRepeat(repeat)}
/>
@@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { t } from '@grafana/i18n';
import { SceneObject, sceneGraph } from '@grafana/scenes';
import { LocalValueVariable, SceneObject, sceneGraph } from '@grafana/scenes';
import { Combobox, ComboboxOption, Select } from '@grafana/ui';
import { useSelector } from 'app/types/store';
@@ -59,10 +59,18 @@ export const RepeatRowSelect2 = ({ sceneContext, repeat, id, onChange }: Props2)
const variables = sceneVars.useState().variables;
const variableOptions = useMemo(() => {
const options: ComboboxOption[] = variables.map((item) => ({
label: item.state.name,
value: item.state.name,
}));
const options: ComboboxOption[] = variables
.filter((item) => {
if (sceneContext.parent) {
// filter out local value variables (which are only set on repeated items)
return !(sceneGraph.lookupVariable(item.state.name, sceneContext.parent) instanceof LocalValueVariable);
}
return true;
})
.map((item) => ({
label: item.state.name,
value: item.state.name,
}));
options.unshift({
label: t('dashboard.repeat-row-select2.variable-options.label.disable-repeating', 'Disable repeating'),
@@ -70,7 +78,7 @@ export const RepeatRowSelect2 = ({ sceneContext, repeat, id, onChange }: Props2)
});
return options;
}, [variables]);
}, [sceneContext, variables]);
const onSelectChange = useCallback((value: ComboboxOption | null) => value && onChange(value.value), [onChange]);
@@ -79,7 +87,7 @@ export const RepeatRowSelect2 = ({ sceneContext, repeat, id, onChange }: Props2)
return (
<Combobox
id={id}
value={repeat}
value={repeat || ''}
onChange={onSelectChange}
options={variableOptions}
disabled={isDisabled}