mirror of https://github.com/grafana/grafana
Dashboards: Tabs layout persistence (#100485)
* Tabs layout persistence * fix lint issue * Add tests, add tabs serializer to registry * Fix deserialize tabs * more tests * tab item title optional * change TabItemKind -> TabLayoutTabKind * add tests for tabs serializer * fix name in test * Fix test after renaming tabspull/100808/head
parent
497491846e
commit
855eadcabd
@ -0,0 +1,92 @@ |
||||
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0'; |
||||
|
||||
import { DefaultGridLayoutManager } from '../../scene/layout-default/DefaultGridLayoutManager'; |
||||
import { ResponsiveGridLayoutManager } from '../../scene/layout-responsive-grid/ResponsiveGridLayoutManager'; |
||||
import { RowsLayoutManager } from '../../scene/layout-rows/RowsLayoutManager'; |
||||
import { TabsLayoutManager } from '../../scene/layout-tabs/TabsLayoutManager'; |
||||
|
||||
import { TabsLayoutSerializer } from './TabsLayoutSerializer'; |
||||
|
||||
describe('deserialization', () => { |
||||
it('should deserialize tabs layout with row child', () => { |
||||
const layout: DashboardV2Spec['layout'] = { |
||||
kind: 'TabsLayout', |
||||
spec: { |
||||
tabs: [{ kind: 'TabsLayoutTab', spec: { title: 'Tab 1', layout: { kind: 'RowsLayout', spec: { rows: [] } } } }], |
||||
}, |
||||
}; |
||||
const serializer = new TabsLayoutSerializer(); |
||||
const deserialized = serializer.deserialize(layout, {}, false); |
||||
expect(deserialized).toBeInstanceOf(TabsLayoutManager); |
||||
expect(deserialized.state.tabs[0].state.layout).toBeInstanceOf(RowsLayoutManager); |
||||
}); |
||||
|
||||
it('should deserialize tabs layout with responsive grid child', () => { |
||||
const layout: DashboardV2Spec['layout'] = { |
||||
kind: 'TabsLayout', |
||||
spec: { |
||||
tabs: [ |
||||
{ |
||||
kind: 'TabsLayoutTab', |
||||
spec: { title: 'Tab 1', layout: { kind: 'ResponsiveGridLayout', spec: { row: '', col: '', items: [] } } }, |
||||
}, |
||||
], |
||||
}, |
||||
}; |
||||
const serializer = new TabsLayoutSerializer(); |
||||
const deserialized = serializer.deserialize(layout, {}, false); |
||||
expect(deserialized).toBeInstanceOf(TabsLayoutManager); |
||||
expect(deserialized.state.tabs[0].state.layout).toBeInstanceOf(ResponsiveGridLayoutManager); |
||||
}); |
||||
|
||||
it('should deserialize tabs layout with default grid child', () => { |
||||
const layout: DashboardV2Spec['layout'] = { |
||||
kind: 'TabsLayout', |
||||
spec: { |
||||
tabs: [ |
||||
{ |
||||
kind: 'TabsLayoutTab', |
||||
spec: { title: 'Tab 1', layout: { kind: 'GridLayout', spec: { items: [] } } }, |
||||
}, |
||||
], |
||||
}, |
||||
}; |
||||
const serializer = new TabsLayoutSerializer(); |
||||
const deserialized = serializer.deserialize(layout, {}, false); |
||||
expect(deserialized).toBeInstanceOf(TabsLayoutManager); |
||||
expect(deserialized.state.tabs[0].state.layout).toBeInstanceOf(DefaultGridLayoutManager); |
||||
}); |
||||
|
||||
it('should handle multiple tabs', () => { |
||||
const layout: DashboardV2Spec['layout'] = { |
||||
kind: 'TabsLayout', |
||||
spec: { |
||||
tabs: [ |
||||
{ |
||||
kind: 'TabsLayoutTab', |
||||
spec: { title: 'Tab 1', layout: { kind: 'ResponsiveGridLayout', spec: { row: '', col: '', items: [] } } }, |
||||
}, |
||||
{ kind: 'TabsLayoutTab', spec: { title: 'Tab 2', layout: { kind: 'GridLayout', spec: { items: [] } } } }, |
||||
], |
||||
}, |
||||
}; |
||||
const serializer = new TabsLayoutSerializer(); |
||||
const deserialized = serializer.deserialize(layout, {}, false); |
||||
expect(deserialized).toBeInstanceOf(TabsLayoutManager); |
||||
expect(deserialized.state.tabs[0].state.layout).toBeInstanceOf(ResponsiveGridLayoutManager); |
||||
expect(deserialized.state.tabs[1].state.layout).toBeInstanceOf(DefaultGridLayoutManager); |
||||
}); |
||||
|
||||
it('should handle 0 tabs', () => { |
||||
const layout: DashboardV2Spec['layout'] = { |
||||
kind: 'TabsLayout', |
||||
spec: { |
||||
tabs: [], |
||||
}, |
||||
}; |
||||
const serializer = new TabsLayoutSerializer(); |
||||
const deserialized = serializer.deserialize(layout, {}, false); |
||||
expect(deserialized).toBeInstanceOf(TabsLayoutManager); |
||||
expect(deserialized.state.tabs).toHaveLength(0); |
||||
}); |
||||
}); |
@ -0,0 +1,49 @@ |
||||
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0'; |
||||
|
||||
import { TabItem } from '../../scene/layout-tabs/TabItem'; |
||||
import { TabsLayoutManager } from '../../scene/layout-tabs/TabsLayoutManager'; |
||||
import { LayoutManagerSerializer } from '../../scene/types/DashboardLayoutManager'; |
||||
|
||||
import { layoutSerializerRegistry } from './layoutSerializerRegistry'; |
||||
import { getLayout } from './utils'; |
||||
|
||||
export class TabsLayoutSerializer implements LayoutManagerSerializer { |
||||
serialize(layoutManager: TabsLayoutManager): DashboardV2Spec['layout'] { |
||||
return { |
||||
kind: 'TabsLayout', |
||||
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: { |
||||
title: tab.state.title, |
||||
layout: layout, |
||||
}, |
||||
}; |
||||
}), |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
deserialize( |
||||
layout: DashboardV2Spec['layout'], |
||||
elements: DashboardV2Spec['elements'], |
||||
preload: boolean |
||||
): TabsLayoutManager { |
||||
if (layout.kind !== 'TabsLayout') { |
||||
throw new Error('Invalid layout kind'); |
||||
} |
||||
const tabs = layout.spec.tabs.map((tab) => { |
||||
const layout = tab.spec.layout; |
||||
return new TabItem({ |
||||
title: tab.spec.title, |
||||
layout: layoutSerializerRegistry.get(layout.kind).serializer.deserialize(layout, elements, preload), |
||||
}); |
||||
}); |
||||
return new TabsLayoutManager({ tabs, currentTab: tabs[0] }); |
||||
} |
||||
} |
Loading…
Reference in new issue