Dynamic dashboards: Add missing row props to schema (#102859)

* Add missing row props to schema

* Fix test, add tests

* Fix renaming that was done by auto refactoring
This commit is contained in:
Oscar Kilhed
2025-03-26 14:49:28 +02:00
committed by GitHub
parent 0f0519eae5
commit e95cd04b25
14 changed files with 149 additions and 42 deletions
@@ -551,7 +551,9 @@ RowsLayoutRowKind: {
RowsLayoutRowSpec: {
title?: string
collapsed: bool
collapse?: bool
hideHeader?: bool
fillScreen?: bool
conditionalRendering?: ConditionalRenderingGroupKind
repeat?: RowRepeatOptions
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind | RowsLayoutKind
@@ -837,7 +837,9 @@ func NewDashboardRowsLayoutRowKind() *DashboardRowsLayoutRowKind {
// +k8s:openapi-gen=true
type DashboardRowsLayoutRowSpec struct {
Title *string `json:"title,omitempty"`
Collapsed bool `json:"collapsed"`
Collapse *bool `json:"collapse,omitempty"`
HideHeader *bool `json:"hideHeader,omitempty"`
FillScreen *bool `json:"fillScreen,omitempty"`
ConditionalRendering *DashboardConditionalRenderingGroupKind `json:"conditionalRendering,omitempty"`
Repeat *DashboardRowRepeatOptions `json:"repeat,omitempty"`
Layout DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind `json:"layout"`
@@ -3577,11 +3577,22 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutRowSpec(ref common.Re
Format: "",
},
},
"collapsed": {
"collapse": {
SchemaProps: spec.SchemaProps{
Default: false,
Type: []string{"boolean"},
Format: "",
Type: []string{"boolean"},
Format: "",
},
},
"hideHeader": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
"fillScreen": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
"conditionalRendering": {
@@ -3600,7 +3611,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutRowSpec(ref common.Re
},
},
},
Required: []string{"collapsed", "layout"},
Required: []string{"layout"},
},
},
Dependencies: []string{
@@ -551,7 +551,9 @@ RowsLayoutRowKind: {
RowsLayoutRowSpec: {
title?: string
collapsed: bool
collapse?: bool
hideHeader?: bool
fillScreen?: bool
repeat?: RowRepeatOptions
conditionalRendering?: ConditionalRenderingGroupKind
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind | RowsLayoutKind
@@ -733,14 +733,15 @@ export const defaultRowsLayoutRowKind = (): RowsLayoutRowKind => ({
export interface RowsLayoutRowSpec {
title?: string;
collapsed: boolean;
collapse?: boolean;
hideHeader?: boolean;
fillScreen?: boolean;
repeat?: RowRepeatOptions;
conditionalRendering?: ConditionalRenderingGroupKind;
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind | RowsLayoutKind;
}
export const defaultRowsLayoutRowSpec = (): RowsLayoutRowSpec => ({
collapsed: false,
layout: defaultGridLayoutKind(),
});
@@ -688,14 +688,15 @@ export const defaultRowsLayoutRowKind = (): RowsLayoutRowKind => ({
export interface RowsLayoutRowSpec {
title?: string;
collapsed: boolean;
collapse?: boolean;
hideHeader?: boolean;
fillScreen?: boolean;
conditionalRendering?: ConditionalRenderingGroupKind;
repeat?: RowRepeatOptions;
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind | RowsLayoutKind;
}
export const defaultRowsLayoutRowSpec = (): RowsLayoutRowSpec => ({
collapsed: false,
layout: defaultGridLayoutKind(),
});
@@ -21,8 +21,8 @@ import { RowsLayoutManager } from './RowsLayoutManager';
export interface RowItemState extends SceneObjectState {
layout: DashboardLayoutManager;
title?: string;
isCollapsed?: boolean;
isHeaderHidden?: boolean;
collapse?: boolean;
hideHeader?: boolean;
fillScreen?: boolean;
conditionalRendering?: ConditionalRendering;
}
@@ -137,8 +137,8 @@ export class RowItem
this.setState({ title });
}
public onHeaderHiddenToggle(isHeaderHidden = !this.state.isHeaderHidden) {
this.setState({ isHeaderHidden });
public onHeaderHiddenToggle(hideHeader = !this.state.hideHeader) {
this.setState({ hideHeader });
}
public onChangeFillScreen(fillScreen: boolean) {
@@ -163,7 +163,7 @@ export class RowItem
}
public onCollapseToggle() {
this.setState({ isCollapsed: !this.state.isCollapsed });
this.setState({ collapse: !this.state.collapse });
}
private _getParentLayout(): RowsLayoutManager {
@@ -93,7 +93,7 @@ function RowTitleInput({ row }: { row: RowItem }) {
}
function RowHeaderSwitch({ row }: { row: RowItem }) {
const { isHeaderHidden = false } = row.useState();
const { hideHeader: isHeaderHidden = false } = row.useState();
return <Switch value={isHeaderHidden} onChange={() => row.onHeaderHiddenToggle()} />;
}
@@ -19,7 +19,7 @@ import { RowItem } from './RowItem';
import { RowItemMenu } from './RowItemMenu';
export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
const { layout, isCollapsed, fillScreen, isHeaderHidden } = model.useState();
const { layout, collapse: isCollapsed, fillScreen, hideHeader: isHeaderHidden } = model.useState();
const isClone = useIsClone(model);
const { isEditing } = useDashboardState(model);
const isConditionallyHidden = useIsConditionallyHidden(model);
@@ -23,12 +23,12 @@ function RowHeaderCheckboxMulti({ model }: { model: RowItems }) {
let indeterminate = false;
for (let i = 0; i < rows.length; i++) {
const { isHeaderHidden } = rows[i].useState();
const { hideHeader } = rows[i].useState();
const prevElement = rows[i - 1];
indeterminate = indeterminate || (prevElement && !!prevElement.state.isHeaderHidden !== !!isHeaderHidden);
indeterminate = indeterminate || (prevElement && !!prevElement.state.hideHeader !== !!hideHeader);
value = value || !!isHeaderHidden;
value = value || !!hideHeader;
}
return (
@@ -261,7 +261,7 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
(rowConfig) =>
new RowItem({
title: rowConfig.title,
isCollapsed: !!rowConfig.isCollapsed,
collapse: !!rowConfig.isCollapsed,
layout: DefaultGridLayoutManager.fromGridItems(
rowConfig.children,
rowConfig.isDraggable,
@@ -20,7 +20,7 @@ describe('deserialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapsed: false,
collapse: false,
layout: { kind: 'GridLayout', spec: { items: [] } },
},
},
@@ -33,6 +33,33 @@ describe('deserialization', () => {
expect(deserialized.state.rows[0].state.layout).toBeInstanceOf(DefaultGridLayoutManager);
});
it('should deserialize rows layout with collapse and hideHeader properly set', () => {
const layout: DashboardV2Spec['layout'] = {
kind: 'RowsLayout',
spec: {
rows: [
{
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapse: true,
hideHeader: true,
fillScreen: true,
layout: { kind: 'GridLayout', spec: { items: [] } },
},
},
],
},
};
const serializer = new RowsLayoutSerializer();
const deserialized = serializer.deserialize(layout, {}, false);
expect(deserialized).toBeInstanceOf(RowsLayoutManager);
expect(deserialized.state.rows[0].state.layout).toBeInstanceOf(DefaultGridLayoutManager);
expect(deserialized.state.rows[0].state.collapse).toBe(true);
expect(deserialized.state.rows[0].state.hideHeader).toBe(true);
expect(deserialized.state.rows[0].state.fillScreen).toBe(true);
});
it('should deserialize rows layout with responsive grid child', () => {
const layout: DashboardV2Spec['layout'] = {
kind: 'RowsLayout',
@@ -42,7 +69,7 @@ describe('deserialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapsed: false,
collapse: false,
layout: {
kind: 'ResponsiveGridLayout',
spec: {
@@ -72,7 +99,9 @@ describe('deserialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapsed: false,
collapse: false,
hideHeader: undefined,
fillScreen: undefined,
layout: {
kind: 'ResponsiveGridLayout',
spec: {
@@ -88,7 +117,9 @@ describe('deserialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 2',
collapsed: true,
collapse: true,
hideHeader: undefined,
fillScreen: undefined,
layout: { kind: 'GridLayout', spec: { items: [] } },
},
},
@@ -101,8 +132,8 @@ describe('deserialization', () => {
expect(deserialized.state.rows).toHaveLength(2);
expect(deserialized.state.rows[0].state.layout).toBeInstanceOf(ResponsiveGridLayoutManager);
expect(deserialized.state.rows[1].state.layout).toBeInstanceOf(DefaultGridLayoutManager);
expect(deserialized.state.rows[0].state.isCollapsed).toBe(false);
expect(deserialized.state.rows[1].state.isCollapsed).toBe(true);
expect(deserialized.state.rows[0].state.collapse).toBe(false);
expect(deserialized.state.rows[1].state.collapse).toBe(true);
});
it('should handle 0 rows', () => {
@@ -127,7 +158,9 @@ describe('deserialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Repeated Row',
collapsed: false,
collapse: false,
hideHeader: undefined,
fillScreen: undefined,
layout: { kind: 'GridLayout', spec: { items: [] } },
repeat: { value: 'foo', mode: 'variable' },
},
@@ -157,7 +190,7 @@ describe('serialization', () => {
rows: [
new RowItem({
title: 'Row 1',
isCollapsed: false,
collapse: false,
layout: new DefaultGridLayoutManager({
grid: new SceneGridLayout({
children: [],
@@ -180,7 +213,50 @@ describe('serialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapsed: false,
collapse: false,
hideHeader: undefined,
fillScreen: undefined,
layout: { kind: 'GridLayout', spec: { items: [] } },
},
},
],
},
});
});
it('should serialize basic row layout with collapse and hideHeader', () => {
const rowsLayout = new RowsLayoutManager({
rows: [
new RowItem({
title: 'Row 1',
collapse: true,
hideHeader: true,
fillScreen: true,
layout: new DefaultGridLayoutManager({
grid: new SceneGridLayout({
children: [],
isDraggable: true,
isResizable: true,
}),
}),
}),
],
});
const serializer = new RowsLayoutSerializer();
const serialized = serializer.serialize(rowsLayout);
expect(serialized).toEqual({
kind: 'RowsLayout',
spec: {
rows: [
{
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapse: true,
hideHeader: true,
fillScreen: true,
layout: { kind: 'GridLayout', spec: { items: [] } },
},
},
@@ -194,7 +270,7 @@ describe('serialization', () => {
rows: [
new RowItem({
title: 'Repeated Row',
isCollapsed: false,
collapse: false,
layout: new DefaultGridLayoutManager({
grid: new SceneGridLayout({
children: [],
@@ -218,7 +294,9 @@ describe('serialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Repeated Row',
collapsed: false,
collapse: false,
hideHeader: undefined,
fillScreen: undefined,
layout: { kind: 'GridLayout', spec: { items: [] } },
repeat: { value: 'foo', mode: 'variable' },
},
@@ -233,7 +311,9 @@ describe('serialization', () => {
rows: [
new RowItem({
title: 'Row 1',
isCollapsed: false,
collapse: false,
hideHeader: undefined,
fillScreen: undefined,
layout: new ResponsiveGridLayoutManager({
columnWidth: 'standard',
rowHeight: 'standard',
@@ -243,7 +323,7 @@ describe('serialization', () => {
}),
new RowItem({
title: 'Row 2',
isCollapsed: true,
collapse: true,
layout: new DefaultGridLayoutManager({
grid: new SceneGridLayout({
children: [],
@@ -266,7 +346,9 @@ describe('serialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 1',
collapsed: false,
collapse: false,
hideHeader: undefined,
fillScreen: undefined,
layout: {
kind: 'ResponsiveGridLayout',
spec: {
@@ -285,7 +367,9 @@ describe('serialization', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'Row 2',
collapsed: true,
collapse: true,
hideHeader: undefined,
fillScreen: undefined,
layout: { kind: 'GridLayout', spec: { items: [] } },
},
},
@@ -20,8 +20,10 @@ export class RowsLayoutSerializer implements LayoutManagerSerializer {
kind: 'RowsLayoutRow',
spec: {
title: row.state.title,
collapsed: row.state.isCollapsed ?? false,
collapse: row.state.collapse,
layout: layout,
fillScreen: row.state.fillScreen,
hideHeader: row.state.hideHeader,
},
};
@@ -64,7 +66,9 @@ export class RowsLayoutSerializer implements LayoutManagerSerializer {
return new RowItem({
title: row.spec.title,
isCollapsed: row.spec.collapsed,
collapse: row.spec.collapse,
hideHeader: row.spec.hideHeader,
fillScreen: row.spec.fillScreen,
$behaviors: behaviors,
layout: layoutSerializerRegistry.get(layout.kind).serializer.deserialize(layout, elements, preload),
conditionalRendering: getConditionalRendering(row),
@@ -593,7 +593,7 @@ describe('transformSaveModelSchemaV2ToScene', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'row1',
collapsed: false,
collapse: false,
layout: {
kind: 'ResponsiveGridLayout',
spec: {
@@ -619,7 +619,7 @@ describe('transformSaveModelSchemaV2ToScene', () => {
kind: 'RowsLayoutRow',
spec: {
title: 'row2',
collapsed: true,
collapse: true,
layout: {
kind: 'GridLayout',
spec: {