Dynamic Dashboards: Add repeat responsive items (#102440)

This commit is contained in:
Bogdan Matei 2025-03-20 11:37:27 +02:00 committed by GitHub
parent 06545aa770
commit 3ddfa3229a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 160 additions and 106 deletions

View File

@ -71,19 +71,12 @@ export class VizPanelEditableElement implements EditableDashboardElement, BulkAc
);
}, [panel]);
const layoutCategory = useMemo(() => {
if (isDashboardLayoutItem(layoutElement) && layoutElement.getOptions) {
return layoutElement.getOptions();
}
return undefined;
}, [layoutElement]);
const layoutCategories = useMemo(
() => (isDashboardLayoutItem(layoutElement) && layoutElement.getOptions ? layoutElement.getOptions() : []),
[layoutElement]
);
const categories = [panelOptions];
if (layoutCategory) {
categories.push(layoutCategory);
}
return categories;
return [panelOptions, ...layoutCategories];
}
public onDelete() {

View File

@ -84,8 +84,8 @@ export function getPanelFrameOptions(panel: VizPanel): OptionsPaneCategoryDescri
)
);
if (isDashboardLayoutItem(layoutElement) && layoutElement.getOptions) {
descriptor.addCategory(layoutElement.getOptions());
if (isDashboardLayoutItem(layoutElement)) {
layoutElement.getOptions?.().forEach((category) => descriptor.addCategory(category));
}
return descriptor;

View File

@ -86,7 +86,7 @@ export class DashboardGridItem
return this.state.variableName ? 'panel-repeater-grid-item' : '';
}
public getOptions(): OptionsPaneCategoryDescriptor {
public getOptions(): OptionsPaneCategoryDescriptor[] {
return getDashboardGridItemOptions(this);
}

View File

@ -8,47 +8,44 @@ import { RepeatRowSelect2 } from 'app/features/dashboard/components/RepeatRowSel
import { DashboardGridItem } from './DashboardGridItem';
export function getDashboardGridItemOptions(gridItem: DashboardGridItem): OptionsPaneCategoryDescriptor {
const category = new OptionsPaneCategoryDescriptor({
export function getDashboardGridItemOptions(gridItem: DashboardGridItem): OptionsPaneCategoryDescriptor[] {
const repeatCategory = new OptionsPaneCategoryDescriptor({
title: t('dashboard.default-layout.item-options.repeat.title', 'Repeat options'),
id: 'Repeat options',
isOpenDefault: false,
});
})
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.default-layout.item-options.repeat.variable.title', 'Repeat by variable'),
description: t(
'dashboard.default-layout.item-options.repeat.variable.description',
'Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.'
),
render: () => <RepeatByOption gridItem={gridItem} />,
})
)
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.default-layout.item-options.repeat.direction.title', 'Repeat direction'),
useShowIf: () => {
const { variableName } = gridItem.useState();
return Boolean(variableName);
},
render: () => <RepeatDirectionOption gridItem={gridItem} />,
})
)
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.default-layout.item-options.repeat.max', 'Max per row'),
useShowIf: () => {
const { variableName, repeatDirection } = gridItem.useState();
return Boolean(variableName) && repeatDirection === 'h';
},
render: () => <MaxPerRowOption gridItem={gridItem} />,
})
);
category.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.default-layout.item-options.repeat.variable.title', 'Repeat by variable'),
description: t(
'dashboard.default-layout.item-options.repeat.variable.description',
'Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.'
),
render: () => <RepeatByOption gridItem={gridItem} />,
})
);
category.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.default-layout.item-options.repeat.direction.title', 'Repeat direction'),
useShowIf: () => {
const { variableName } = gridItem.useState();
return Boolean(variableName);
},
render: () => <RepeatDirectionOption gridItem={gridItem} />,
})
);
category.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.default-layout.item-options.repeat.max', 'Max per row'),
useShowIf: () => {
const { variableName, repeatDirection } = gridItem.useState();
return Boolean(variableName) && repeatDirection === 'h';
},
render: () => <MaxPerRowOption gridItem={gridItem} />,
})
);
return category;
return [repeatCategory];
}
interface OptionComponentProps {

View File

@ -65,7 +65,7 @@ export class ResponsiveGridItem extends SceneObjectBase<ResponsiveGridItemState>
};
}
public getOptions(): OptionsPaneCategoryDescriptor {
public getOptions(): OptionsPaneCategoryDescriptor[] {
return getOptions(this);
}

View File

@ -1,9 +1,42 @@
import { t } from 'app/core/internationalization';
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';
import { useConditionalRenderingEditor } from '../../conditional-rendering/ConditionalRenderingEditor';
import { ResponsiveGridItem } from './ResponsiveGridItem';
export function getOptions(model: ResponsiveGridItem): OptionsPaneCategoryDescriptor {
return useConditionalRenderingEditor(model.state.conditionalRendering)!;
export function getOptions(model: ResponsiveGridItem): OptionsPaneCategoryDescriptor[] {
const repeatCategory = new OptionsPaneCategoryDescriptor({
title: t('dashboard.responsive-layout.item-options.repeat.title', 'Repeat options'),
id: 'repeat-options',
isOpenDefault: false,
}).addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.responsive-layout.item-options.repeat.variable.title', 'Repeat by variable'),
description: t(
'dashboard.responsive-layout.item-options.repeat.variable.description',
'Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.'
),
render: () => <RepeatByOption item={model} />,
})
);
const conditionalRenderingCategory = useConditionalRenderingEditor(model.state.conditionalRendering)!;
return [repeatCategory, conditionalRenderingCategory];
}
function RepeatByOption({ item }: { item: ResponsiveGridItem }) {
const { variableName } = item.useState();
return (
<RepeatRowSelect2
id="repeat-by-variable-select"
sceneContext={item}
repeat={variableName}
onChange={(value?: string) => item.setRepeatByVariable(value)}
/>
);
}

View File

@ -19,48 +19,61 @@ import { RowItem } from './RowItem';
export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] {
const { layout } = model.useState();
const rowOptions = useMemo(() => {
const editPaneHeaderOptions = new OptionsPaneCategoryDescriptor({ title: '', id: 'row-options' })
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.option.title', 'Title'),
render: () => <RowTitleInput row={model} />,
})
)
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.option.height', 'Height'),
render: () => <RowHeightSelect row={model} />,
})
);
editPaneHeaderOptions
.addItem(
const rowCategory = useMemo(
() =>
new OptionsPaneCategoryDescriptor({ title: '', id: 'row-options' })
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.row-options.row.title', 'Title'),
render: () => <RowTitleInput row={model} />,
})
)
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.row-options.row.height', 'Height'),
render: () => <RowHeightSelect row={model} />,
})
)
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.row-options.row.hide-header', 'Hide row header'),
render: () => <RowHeaderSwitch row={model} />,
})
),
[model]
);
const repeatCategory = useMemo(
() =>
new OptionsPaneCategoryDescriptor({
title: t('dashboard.rows-layout.row-options.repeat.title', 'Repeat options'),
id: 'repeat-options',
isOpenDefault: false,
}).addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.option.repeat', 'Repeat for'),
title: t('dashboard.rows-layout.row-options.repeat.variable.title', 'Repeat by variable'),
description: t(
'dashboard.rows-layout.row-options.repeat.variable.description',
'Repeat this row for each value in the selected variable.'
),
render: () => <RowRepeatSelect row={model} />,
})
)
.addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.rows-layout.option.hide-header', 'Hide row header'),
render: () => <RowHeaderSwitch row={model} />,
})
);
return editPaneHeaderOptions;
}, [model]);
),
[model]
);
const layoutCategory = useLayoutCategory(layout);
const conditionalRenderingOptions = useMemo(() => {
return useConditionalRenderingEditor(model.state.conditionalRendering);
}, [model]);
const editOptions = [rowCategory, layoutCategory, repeatCategory];
const editOptions = [rowOptions, layoutCategory];
const conditionalRenderingCategory = useMemo(
() => useConditionalRenderingEditor(model.state.conditionalRendering),
[model]
);
if (conditionalRenderingOptions) {
editOptions.push(conditionalRenderingOptions);
if (conditionalRenderingCategory) {
editOptions.push(conditionalRenderingCategory);
}
return editOptions;

View File

@ -11,19 +11,22 @@ import { useEditPaneInputAutoFocus } from '../layouts-shared/utils';
import { TabItem } from './TabItem';
export function getEditOptions(model: TabItem): OptionsPaneCategoryDescriptor[] {
const tabOptions = useMemo(() => {
return new OptionsPaneCategoryDescriptor({ title: '', id: 'tab-item-options' }).addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.tabs-layout.tab-options.title-option', 'Title'),
render: () => <TabTitleInput tab={model} />,
})
);
}, [model]);
const { layout } = model.useState();
const layoutOptions = useLayoutCategory(layout);
return [tabOptions, layoutOptions];
const tabCategory = useMemo(
() =>
new OptionsPaneCategoryDescriptor({ title: '', id: 'tab-item-options' }).addItem(
new OptionsPaneItemDescriptor({
title: t('dashboard.tabs-layout.tab-options.title-option', 'Title'),
render: () => <TabTitleInput tab={model} />,
})
),
[model]
);
const layoutCategory = useLayoutCategory(layout);
return [tabCategory, layoutCategory];
}
function TabTitleInput({ tab }: { tab: TabItem }) {

View File

@ -30,7 +30,7 @@ export interface DashboardLayoutItem extends SceneObject {
/**
* Return layout item options (like repeat, repeat direction, etc. for the default DashboardGridItem)
*/
getOptions?(): OptionsPaneCategoryDescriptor;
getOptions?(): OptionsPaneCategoryDescriptor[];
/**
* When going into panel edit

View File

@ -1439,6 +1439,15 @@
},
"responsive-layout": {
"description": "Panels resize to fit and form uniform grids",
"item-options": {
"repeat": {
"title": "Repeat options",
"variable": {
"description": "Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.",
"title": "Repeat by variable"
}
}
},
"name": "Auto grid",
"options": {
"columns": "Columns",
@ -1453,12 +1462,6 @@
"rows-layout": {
"description": "Collapsable panel groups with headings",
"name": "Rows",
"option": {
"height": "Height",
"hide-header": "Hide row header",
"repeat": "Repeat for",
"title": "Title"
},
"options": {
"height-expand": "Expand",
"height-min": "Min"
@ -1482,6 +1485,18 @@
}
},
"row-options": {
"repeat": {
"title": "Repeat options",
"variable": {
"description": "Repeat this row for each value in the selected variable.",
"title": "Repeat by variable"
}
},
"row": {
"height": "Height",
"hide-header": "Hide row header",
"title": "Title"
},
"title-option": "Title"
}
},