grafana/public/app/features/scenes/components/layout/SceneGridLayout.test.tsx
Dominik Prokop 80e80221b9
Scenes: Grid layout (#56737)
* WIP: First approach to scene grid layout

* Flex layout

* Grid layout rows

* Allow passing custom props to scene object renderers

* Allow nesting grid layouts

* Re-layout nested grid's enclosing grids

* Update public/app/features/scenes/components/layout/SceneGridLayout.tsx

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>

* Review comments

* Got rid of flex & grid child layout objects

* WIP: Recreating rows behaviour (almost working)

* Major progress on rows

* remove nested grid example (not supported)

* Remove removal damn

* Trying to use children directly

* Ts fixes

* chore: Fix TS

* Fix issue when row bboxes when not updated on layout change

* Now the tricky part

* working

* Removing some code

* needs more work

* Getting some thing working

* Getting some thing working

* fix toggle row

* Starting to work

* Fix

* Yay it's working

* Updates

* Updates

* Added some sorting of children

* Updated comment

* Simplify sorting

* removed commented code

* Updated

* Pushed a fix so we can move a panel out from a row and into the parent grid

* simplify move logic

* Minor simplification

* Removed some unnesary code

* fixed comment

* Removed unnessary condition in findGridSceneParent

* remove unnessary if

* Simplify toGridCell

* removed duplicate if

* removed unused code

* Adds grid demo with different data scenarios

* Make it green

* Demo grid with multiple time ranges

* Move child atomically

* Add tests

* Cleanup

* Fix unused import

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
2022-11-15 00:49:39 -08:00

239 lines
8.4 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import React from 'react';
import { SceneObjectBase } from '../../core/SceneObjectBase';
import { SceneComponentProps, SceneLayoutChildState } from '../../core/types';
import { Scene } from '../Scene';
import { SceneGridLayout, SceneGridRow } from './SceneGridLayout';
// Mocking AutoSizer to allow testing of the SceneGridLayout component rendering
jest.mock(
'react-virtualized-auto-sizer',
() =>
({ children }: { children: (args: { width: number; height: number }) => React.ReactNode }) =>
children({ height: 600, width: 600 })
);
class TestObject extends SceneObjectBase<SceneLayoutChildState> {
public static Component = (m: SceneComponentProps<TestObject>) => {
return <div data-testid="test-object">TestObject</div>;
};
}
describe('SceneGridLayout', () => {
describe('rendering', () => {
it('should render all grid children', async () => {
const scene = new Scene({
title: 'Grid test',
layout: new SceneGridLayout({
children: [
new TestObject({ size: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ size: { x: 0, y: 5, width: 12, height: 5 } }),
],
}),
});
render(<scene.Component model={scene} />);
expect(screen.queryAllByTestId('test-object')).toHaveLength(2);
});
it('should not render children of a collapsed row', async () => {
const scene = new Scene({
title: 'Grid test',
layout: new SceneGridLayout({
children: [
new TestObject({ key: 'a', size: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ key: 'b', size: { x: 0, y: 5, width: 12, height: 5 } }),
new SceneGridRow({
title: 'Row A',
key: 'Row A',
isCollapsed: true,
size: { y: 10 },
children: [new TestObject({ key: 'c', size: { x: 0, y: 11, width: 12, height: 5 } })],
}),
],
}),
});
render(<scene.Component model={scene} />);
expect(screen.queryAllByTestId('test-object')).toHaveLength(2);
});
it('should render children of an expanded row', async () => {
const scene = new Scene({
title: 'Grid test',
layout: new SceneGridLayout({
children: [
new TestObject({ key: 'a', size: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ key: 'b', size: { x: 0, y: 5, width: 12, height: 5 } }),
new SceneGridRow({
title: 'Row A',
key: 'Row A',
isCollapsed: false,
size: { y: 10 },
children: [new TestObject({ key: 'c', size: { x: 0, y: 11, width: 12, height: 5 } })],
}),
],
}),
});
render(<scene.Component model={scene} />);
expect(screen.queryAllByTestId('test-object')).toHaveLength(3);
});
});
describe('when moving a panel', () => {
it('shoud update layout children placement and order ', () => {
const layout = new SceneGridLayout({
children: [
new TestObject({ key: 'a', size: { x: 0, y: 0, width: 1, height: 1 } }),
new TestObject({ key: 'b', size: { x: 1, y: 0, width: 1, height: 1 } }),
new TestObject({ key: 'c', size: { x: 0, y: 1, width: 1, height: 1 } }),
],
});
layout.onDragStop(
[
{ i: 'b', x: 0, y: 0, w: 1, h: 1 },
{
i: 'a',
x: 0,
y: 1,
w: 1,
h: 1,
},
{
i: 'c',
x: 0,
y: 2,
w: 1,
h: 1,
},
],
// @ts-expect-error
{},
{ i: 'b', x: 0, y: 0, w: 1, h: 1 },
{},
{},
{}
);
expect(layout.state.children[0].state.key).toEqual('b');
expect(layout.state.children[0].state.size).toEqual({ x: 0, y: 0, width: 1, height: 1 });
expect(layout.state.children[1].state.key).toEqual('a');
expect(layout.state.children[1].state.size).toEqual({ x: 0, y: 1, width: 1, height: 1 });
expect(layout.state.children[2].state.key).toEqual('c');
expect(layout.state.children[2].state.size).toEqual({ x: 0, y: 2, width: 1, height: 1 });
});
});
describe('when using rows', () => {
it('should update objects relations when moving object out of a row', () => {
const rowAChild1 = new TestObject({ key: 'row-a-child1', size: { x: 0, y: 1, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', size: { x: 1, y: 1, width: 1, height: 1 } });
const sourceRow = new SceneGridRow({
title: 'Row A',
key: 'row-a',
children: [rowAChild1, rowAChild2],
size: { y: 0 },
});
const layout = new SceneGridLayout({
children: [sourceRow],
});
const updatedLayout = layout.moveChildTo(rowAChild1, layout);
expect(updatedLayout.length).toEqual(2);
// the source row should be cloned and with children updated
expect(updatedLayout[0].state.key).toEqual(sourceRow.state.key);
expect(updatedLayout[0]).not.toEqual(sourceRow);
expect((updatedLayout[0] as SceneGridRow).state.children.length).toEqual(1);
expect((updatedLayout[0] as SceneGridRow).state.children).not.toContain(rowAChild1);
// the moved child should be cloned in the root
expect(updatedLayout[1].state.key).toEqual(rowAChild1.state.key);
expect(updatedLayout[1]).not.toEqual(rowAChild1);
});
it('should update objects relations when moving objects between rows', () => {
const rowAChild1 = new TestObject({ key: 'row-a-child1', size: { x: 0, y: 0, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', size: { x: 1, y: 0, width: 1, height: 1 } });
const sourceRow = new SceneGridRow({
title: 'Row A',
key: 'row-a',
children: [rowAChild1, rowAChild2],
});
const targetRow = new SceneGridRow({
title: 'Row B',
key: 'row-b',
children: [],
});
const panelOutsideARow = new TestObject({ key: 'a', size: { x: 0, y: 0, width: 1, height: 1 } });
const layout = new SceneGridLayout({
children: [panelOutsideARow, sourceRow, targetRow],
});
const updatedLayout = layout.moveChildTo(rowAChild1, targetRow);
expect(updatedLayout[0]).toEqual(panelOutsideARow);
// the source row should be cloned and with children updated
expect(updatedLayout[1].state.key).toEqual(sourceRow.state.key);
expect(updatedLayout[1]).not.toEqual(sourceRow);
expect((updatedLayout[1] as SceneGridRow).state.children.length).toEqual(1);
// the target row should be cloned and with children updated
expect(updatedLayout[2].state.key).toEqual(targetRow.state.key);
expect(updatedLayout[2]).not.toEqual(targetRow);
expect((updatedLayout[2] as SceneGridRow).state.children.length).toEqual(1);
// the moved object should be cloned and added to the target row
const movedObject = (updatedLayout[2] as SceneGridRow).state.children[0];
expect(movedObject.state.key).toEqual('row-a-child1');
expect(movedObject).not.toEqual(rowAChild1);
});
it('should update position of objects when row is expanded', () => {
const rowAChild1 = new TestObject({ key: 'row-a-child1', size: { x: 0, y: 1, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', size: { x: 1, y: 1, width: 1, height: 1 } });
const rowA = new SceneGridRow({
title: 'Row A',
key: 'row-a',
children: [rowAChild1, rowAChild2],
size: { y: 0 },
isCollapsed: true,
});
const panelOutsideARow = new TestObject({ key: 'outsider', size: { x: 0, y: 1, width: 1, height: 1 } });
const rowBChild1 = new TestObject({ key: 'row-b-child1', size: { x: 0, y: 3, width: 1, height: 1 } });
const rowB = new SceneGridRow({
title: 'Row B',
key: 'row-b',
children: [rowBChild1],
size: { y: 2 },
isCollapsed: false,
});
const layout = new SceneGridLayout({
children: [rowA, panelOutsideARow, rowB],
});
layout.toggleRow(rowA);
expect(panelOutsideARow.state!.size!.y).toEqual(2);
expect(rowB.state!.size!.y).toEqual(3);
expect(rowBChild1.state!.size!.y).toEqual(4);
});
});
});