DashboardScene: Take selected object into account when adding objects (#102423)

* Dashboard: Add object to selected object

* Try not to nest rows

* Update

* Update

* rows in rows, tabs in tabs

* Update schema to allow nested rows and nested tabs

* fix lint issue

* reset v2alpha1 types from main

* reset dashboard_object_gen.go to main

---------

Co-authored-by: oscarkilhed <oscar.kilhed@grafana.com>
This commit is contained in:
Torkel Ödegaard
2025-03-21 14:28:44 +01:00
committed by GitHub
co-authored by oscarkilhed
parent ba3e8014b3
commit 8a8b1a0743
12 changed files with 1091 additions and 1115 deletions
@@ -554,7 +554,7 @@ RowsLayoutRowSpec: {
collapsed: bool
conditionalRendering?: ConditionalRenderingGroupKind
repeat?: RowRepeatOptions
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind | RowsLayoutKind
}
ResponsiveGridLayoutKind: {
@@ -595,7 +595,7 @@ TabsLayoutTabKind: {
TabsLayoutTabSpec: {
title?: string
layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind
layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind
}
PanelSpec: {
@@ -836,17 +836,17 @@ func NewDashboardRowsLayoutRowKind() *DashboardRowsLayoutRowKind {
// +k8s:openapi-gen=true
type DashboardRowsLayoutRowSpec struct {
Title *string `json:"title,omitempty"`
Collapsed bool `json:"collapsed"`
ConditionalRendering *DashboardConditionalRenderingGroupKind `json:"conditionalRendering,omitempty"`
Repeat *DashboardRowRepeatOptions `json:"repeat,omitempty"`
Layout DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind `json:"layout"`
Title *string `json:"title,omitempty"`
Collapsed bool `json:"collapsed"`
ConditionalRendering *DashboardConditionalRenderingGroupKind `json:"conditionalRendering,omitempty"`
Repeat *DashboardRowRepeatOptions `json:"repeat,omitempty"`
Layout DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind `json:"layout"`
}
// NewDashboardRowsLayoutRowSpec creates a new DashboardRowsLayoutRowSpec object.
func NewDashboardRowsLayoutRowSpec() *DashboardRowsLayoutRowSpec {
return &DashboardRowsLayoutRowSpec{
Layout: *NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(),
Layout: *NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind(),
}
}
@@ -1054,14 +1054,14 @@ func NewDashboardTabsLayoutTabKind() *DashboardTabsLayoutTabKind {
// +k8s:openapi-gen=true
type DashboardTabsLayoutTabSpec struct {
Title *string `json:"title,omitempty"`
Layout DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind `json:"layout"`
Title *string `json:"title,omitempty"`
Layout DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind `json:"layout"`
}
// NewDashboardTabsLayoutTabSpec creates a new DashboardTabsLayoutTabSpec object.
func NewDashboardTabsLayoutTabSpec() *DashboardTabsLayoutTabSpec {
return &DashboardTabsLayoutTabSpec{
Layout: *NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind(),
Layout: *NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(),
}
}
@@ -2012,19 +2012,20 @@ func (resource *DashboardGridLayoutItemKindOrGridLayoutRowKind) UnmarshalJSON(ra
}
// +k8s:openapi-gen=true
type DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind struct {
type DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind struct {
GridLayoutKind *DashboardGridLayoutKind `json:"GridLayoutKind,omitempty"`
ResponsiveGridLayoutKind *DashboardResponsiveGridLayoutKind `json:"ResponsiveGridLayoutKind,omitempty"`
TabsLayoutKind *DashboardTabsLayoutKind `json:"TabsLayoutKind,omitempty"`
RowsLayoutKind *DashboardRowsLayoutKind `json:"RowsLayoutKind,omitempty"`
}
// NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind creates a new DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind object.
func NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind() *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind {
return &DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{}
// NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind creates a new DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind object.
func NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind() *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind {
return &DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{}
}
// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` as JSON.
func (resource DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) MarshalJSON() ([]byte, error) {
// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind` as JSON.
func (resource DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind) MarshalJSON() ([]byte, error) {
if resource.GridLayoutKind != nil {
return json.Marshal(resource.GridLayoutKind)
}
@@ -2034,11 +2035,14 @@ func (resource DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind
if resource.TabsLayoutKind != nil {
return json.Marshal(resource.TabsLayoutKind)
}
if resource.RowsLayoutKind != nil {
return json.Marshal(resource.RowsLayoutKind)
}
return []byte("null"), nil
}
// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` from JSON.
func (resource *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) UnmarshalJSON(raw []byte) error {
// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind` from JSON.
func (resource *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind) UnmarshalJSON(raw []byte) error {
if raw == nil {
return nil
}
@@ -2071,6 +2075,14 @@ func (resource *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKin
resource.ResponsiveGridLayoutKind = &dashboardResponsiveGridLayoutKind
return nil
case "RowsLayout":
var dashboardRowsLayoutKind DashboardRowsLayoutKind
if err := json.Unmarshal(raw, &dashboardRowsLayoutKind); err != nil {
return err
}
resource.RowsLayoutKind = &dashboardRowsLayoutKind
return nil
case "TabsLayout":
var dashboardTabsLayoutKind DashboardTabsLayoutKind
if err := json.Unmarshal(raw, &dashboardTabsLayoutKind); err != nil {
@@ -2158,19 +2170,20 @@ func (resource *DashboardConditionalRenderingVariableKindOrConditionalRenderingD
}
// +k8s:openapi-gen=true
type DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind struct {
type DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind struct {
GridLayoutKind *DashboardGridLayoutKind `json:"GridLayoutKind,omitempty"`
RowsLayoutKind *DashboardRowsLayoutKind `json:"RowsLayoutKind,omitempty"`
ResponsiveGridLayoutKind *DashboardResponsiveGridLayoutKind `json:"ResponsiveGridLayoutKind,omitempty"`
TabsLayoutKind *DashboardTabsLayoutKind `json:"TabsLayoutKind,omitempty"`
}
// NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind creates a new DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind object.
func NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind() *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind {
return &DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind{}
// NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind creates a new DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind object.
func NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind() *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind {
return &DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{}
}
// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind` as JSON.
func (resource DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind) MarshalJSON() ([]byte, error) {
// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` as JSON.
func (resource DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) MarshalJSON() ([]byte, error) {
if resource.GridLayoutKind != nil {
return json.Marshal(resource.GridLayoutKind)
}
@@ -2180,11 +2193,14 @@ func (resource DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind
if resource.ResponsiveGridLayoutKind != nil {
return json.Marshal(resource.ResponsiveGridLayoutKind)
}
if resource.TabsLayoutKind != nil {
return json.Marshal(resource.TabsLayoutKind)
}
return []byte("null"), nil
}
// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind` from JSON.
func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind) UnmarshalJSON(raw []byte) error {
// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` from JSON.
func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) UnmarshalJSON(raw []byte) error {
if raw == nil {
return nil
}
@@ -2225,6 +2241,14 @@ func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKin
resource.RowsLayoutKind = &dashboardRowsLayoutKind
return nil
case "TabsLayout":
var dashboardTabsLayoutKind DashboardTabsLayoutKind
if err := json.Unmarshal(raw, &dashboardTabsLayoutKind); err != nil {
return err
}
resource.TabsLayoutKind = &dashboardTabsLayoutKind
return nil
}
return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator)
@@ -2472,88 +2496,3 @@ func (resource *DashboardStringOrFloat64) UnmarshalJSON(raw []byte) error {
return errors.Join(errList...)
}
// +k8s:openapi-gen=true
type DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind struct {
GridLayoutKind *DashboardGridLayoutKind `json:"GridLayoutKind,omitempty"`
RowsLayoutKind *DashboardRowsLayoutKind `json:"RowsLayoutKind,omitempty"`
ResponsiveGridLayoutKind *DashboardResponsiveGridLayoutKind `json:"ResponsiveGridLayoutKind,omitempty"`
TabsLayoutKind *DashboardTabsLayoutKind `json:"TabsLayoutKind,omitempty"`
}
// NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind creates a new DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind object.
func NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind() *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind {
return &DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{}
}
// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` as JSON.
func (resource DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) MarshalJSON() ([]byte, error) {
if resource.GridLayoutKind != nil {
return json.Marshal(resource.GridLayoutKind)
}
if resource.RowsLayoutKind != nil {
return json.Marshal(resource.RowsLayoutKind)
}
if resource.ResponsiveGridLayoutKind != nil {
return json.Marshal(resource.ResponsiveGridLayoutKind)
}
if resource.TabsLayoutKind != nil {
return json.Marshal(resource.TabsLayoutKind)
}
return []byte("null"), nil
}
// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` from JSON.
func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) UnmarshalJSON(raw []byte) error {
if raw == nil {
return nil
}
// FIXME: this is wasteful, we need to find a more efficient way to unmarshal this.
parsedAsMap := make(map[string]interface{})
if err := json.Unmarshal(raw, &parsedAsMap); err != nil {
return err
}
discriminator, found := parsedAsMap["kind"]
if !found {
return errors.New("discriminator field 'kind' not found in payload")
}
switch discriminator {
case "GridLayout":
var dashboardGridLayoutKind DashboardGridLayoutKind
if err := json.Unmarshal(raw, &dashboardGridLayoutKind); err != nil {
return err
}
resource.GridLayoutKind = &dashboardGridLayoutKind
return nil
case "ResponsiveGridLayout":
var dashboardResponsiveGridLayoutKind DashboardResponsiveGridLayoutKind
if err := json.Unmarshal(raw, &dashboardResponsiveGridLayoutKind); err != nil {
return err
}
resource.ResponsiveGridLayoutKind = &dashboardResponsiveGridLayoutKind
return nil
case "RowsLayout":
var dashboardRowsLayoutKind DashboardRowsLayoutKind
if err := json.Unmarshal(raw, &dashboardRowsLayoutKind); err != nil {
return err
}
resource.RowsLayoutKind = &dashboardRowsLayoutKind
return nil
case "TabsLayout":
var dashboardTabsLayoutKind DashboardTabsLayoutKind
if err := json.Unmarshal(raw, &dashboardTabsLayoutKind); err != nil {
return err
}
resource.TabsLayoutKind = &dashboardTabsLayoutKind
return nil
}
return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator)
}
@@ -554,7 +554,7 @@ RowsLayoutRowSpec: {
collapsed: bool
repeat?: RowRepeatOptions
conditionalRendering?: ConditionalRenderingGroupKind
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind | RowsLayoutKind
}
ResponsiveGridLayoutKind: {
@@ -595,7 +595,7 @@ TabsLayoutTabKind: {
TabsLayoutTabSpec: {
title?: string
layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind
layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind
}
PanelSpec: {
File diff suppressed because it is too large Load Diff
@@ -99,6 +99,10 @@ export class DashboardEditPane extends SceneObjectBase<DashboardEditPaneState> {
}
}
public getSelection(): SceneObject | SceneObject[] | undefined {
return this.state.selection?.getSelection();
}
public selectObject(obj: SceneObject, id: string, multi?: boolean) {
const prevItem = this.state.selection?.getFirstObject();
if (prevItem === obj && !multi) {
@@ -75,7 +75,7 @@ import { LayoutOrchestrator } from './layout-manager/LayoutOrchestrator';
import { LayoutRestorer } from './layouts-shared/LayoutRestorer';
import { addNewRowTo, addNewTabTo } from './layouts-shared/addNew';
import { DashboardLayoutManager } from './types/DashboardLayoutManager';
import { LayoutParent } from './types/LayoutParent';
import { isLayoutParent, LayoutParent } from './types/LayoutParent';
export const PERSISTED_PROPS = ['title', 'description', 'tags', 'editable', 'graphTooltip', 'links', 'meta', 'preload'];
export const PANEL_SEARCH_VAR = 'systemPanelFilterVar';
@@ -500,6 +500,13 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
this.onEnterEditMode();
}
const selectedObject = this.state.editPane.getSelection();
if (selectedObject && !Array.isArray(selectedObject) && isLayoutParent(selectedObject)) {
const layout = selectedObject.getLayout();
layout.addPanel(vizPanel);
return;
}
// Add panel to layout
this.state.body.addPanel(vizPanel);
}
@@ -609,10 +616,22 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
}
public onCreateNewRow() {
const selectedObject = this.state.editPane.getSelection();
if (selectedObject && !Array.isArray(selectedObject) && isLayoutParent(selectedObject)) {
const layout = selectedObject.getLayout();
return addNewRowTo(layout);
}
return addNewRowTo(this.state.body);
}
public onCreateNewTab() {
const selectedObject = this.state.editPane.getSelection();
if (selectedObject && !Array.isArray(selectedObject) && isLayoutParent(selectedObject)) {
const layout = selectedObject.getLayout();
return addNewTabTo(layout);
}
return addNewTabTo(this.state.body);
}
@@ -256,6 +256,7 @@ export class DefaultGridLayoutManager
sceneGridLayout.setState({ children: [row, ...sceneGridLayout.state.children] });
this.publishEvent(new NewObjectAddedToCanvasEvent(row), true);
return row;
}
@@ -89,6 +89,7 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
public addNewRow(): RowItem {
const row = new RowItem();
this.setState({ rows: [...this.state.rows, row] });
this.publishEvent(new NewObjectAddedToCanvasEvent(row), true);
return row;
}
@@ -112,7 +113,7 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
});
}
public addRowAbove(row: RowItem) {
public addRowAbove(row: RowItem): RowItem {
const index = this.state.rows.indexOf(row);
const newRow = new RowItem();
const newRows = [...this.state.rows];
@@ -121,9 +122,11 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
this.setState({ rows: newRows });
this.publishEvent(new NewObjectAddedToCanvasEvent(newRow), true);
return newRow;
}
public addRowBelow(row: RowItem) {
public addRowBelow(row: RowItem): RowItem {
const rows = this.state.rows;
let index = rows.indexOf(row);
@@ -139,6 +142,8 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
this.setState({ rows: newRows });
this.publishEvent(new NewObjectAddedToCanvasEvent(newRow), true);
return newRow;
}
public removeRow(row: RowItem) {
@@ -110,6 +110,7 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
public addNewTab() {
const newTab = new TabItem();
this.setState({ tabs: [...this.state.tabs, newTab], currentTabIndex: this.state.tabs.length });
this.publishEvent(new NewObjectAddedToCanvasEvent(newTab), true);
return newTab;
}
@@ -143,20 +144,24 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
this.publishEvent(new ObjectRemovedFromCanvasEvent(tabToRemove), true);
}
public addTabBefore(tab: TabItem) {
public addTabBefore(tab: TabItem): TabItem {
const newTab = new TabItem();
const tabs = this.state.tabs.slice();
tabs.splice(tabs.indexOf(tab), 0, newTab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex });
this.publishEvent(new NewObjectAddedToCanvasEvent(newTab), true);
return newTab;
}
public addTabAfter(tab: TabItem) {
public addTabAfter(tab: TabItem): TabItem {
const newTab = new TabItem();
const tabs = this.state.tabs.slice();
tabs.splice(tabs.indexOf(tab) + 1, 0, newTab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex + 1 });
this.publishEvent(new NewObjectAddedToCanvasEvent(newTab), true);
return newTab;
}
public moveTabLeft(tab: TabItem) {
@@ -1,5 +1,5 @@
import { config } from '@grafana/runtime';
import { SceneGridRow } from '@grafana/scenes';
import { sceneGraph, SceneGridRow } from '@grafana/scenes';
import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared';
import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager';
@@ -11,18 +11,25 @@ import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
import { isLayoutParent } from '../types/LayoutParent';
export function addNewTabTo(layout: DashboardLayoutManager): TabItem {
if (layout instanceof TabsLayoutManager) {
const tab = layout.addNewTab();
layout.publishEvent(new NewObjectAddedToCanvasEvent(tab), true);
return tab;
}
const layoutParent = layout.parent!;
if (!isLayoutParent(layoutParent)) {
throw new Error('Parent layout is not a LayoutParent');
}
const tabsLayout = TabsLayoutManager.createFromLayout(layoutParent.getLayout());
// If layout parent is tab item we add new tab after it rather than create a nested tab
if (layoutParent instanceof TabItem) {
const tabsLayout = sceneGraph.getAncestor(layoutParent, TabsLayoutManager);
return tabsLayout.addTabAfter(layoutParent);
}
if (layout instanceof TabsLayoutManager) {
return layout.addNewTab();
}
// Create new tabs layout and wrap the current layout in the first tab
const tabsLayout = TabsLayoutManager.createEmpty();
tabsLayout.state.tabs[0].setState({ layout: layout.clone() });
layoutParent.switchLayout(tabsLayout);
const tab = tabsLayout.state.tabs[0];
@@ -37,18 +44,25 @@ export function addNewRowTo(layout: DashboardLayoutManager): RowItem | SceneGrid
*/
if (!config.featureToggles.dashboardNewLayouts) {
if (layout instanceof DefaultGridLayoutManager) {
const row = layout.addNewRow();
layout.publishEvent(new NewObjectAddedToCanvasEvent(row), true);
return row;
return layout.addNewRow();
} else {
throw new Error('New dashboard layouts feature not enabled but new layout found');
}
}
const layoutParent = layout.parent!;
if (!isLayoutParent(layoutParent)) {
throw new Error('Parent layout is not a LayoutParent');
}
// If adding we are adding a row to a row we add it below the current row
if (layoutParent instanceof RowItem) {
const rowsLayout = sceneGraph.getAncestor(layoutParent, RowsLayoutManager);
return rowsLayout.addRowBelow(layoutParent);
}
if (layout instanceof RowsLayoutManager) {
const row = layout.addNewRow();
layout.publishEvent(new NewObjectAddedToCanvasEvent(row), true);
return row;
return layout.addNewRow();
}
if (layout instanceof TabsLayoutManager) {
@@ -59,11 +73,6 @@ export function addNewRowTo(layout: DashboardLayoutManager): RowItem | SceneGrid
// If we want to add a row and current layout is custom grid or auto we migrate to rows layout
// And wrap current layout in a row
const layoutParent = layout.parent!;
if (!isLayoutParent(layoutParent)) {
throw new Error('Parent layout is not a LayoutParent');
}
const rowsLayout = RowsLayoutManager.createFromLayout(layoutParent.getLayout());
layoutParent.switchLayout(rowsLayout);
@@ -16,9 +16,6 @@ export class RowsLayoutSerializer implements LayoutManagerSerializer {
spec: {
rows: layoutManager.state.rows.map((row) => {
const layout = getLayout(row.state.layout);
if (layout.kind === 'RowsLayout') {
throw new Error('Nested RowsLayout is not supported');
}
const rowKind: RowsLayoutRowKind = {
kind: 'RowsLayoutRow',
spec: {
@@ -14,9 +14,6 @@ export class TabsLayoutSerializer implements LayoutManagerSerializer {
spec: {
tabs: layoutManager.state.tabs.map((tab) => {
const layout = getLayout(tab.state.layout);
if (layout.kind === 'TabsLayout') {
throw new Error('Nested TabsLayout is not supported');
}
return {
kind: 'TabsLayoutTab',
spec: {