DashboardScenes: Row repeater panels cannot be dragged outside and adding new panels to a cloned row updates all rows (#91062)

* Disallow dragging panels in repeated rows + dropping panels into cloned rows will update all repeated rows

* test
This commit is contained in:
Victor Marin
2024-08-30 16:50:22 +03:00
committed by GitHub
parent 172662af58
commit 962fcf1fa8
2 changed files with 85 additions and 2 deletions

View File

@@ -67,6 +67,42 @@ describe('RowRepeaterBehavior', () => {
expect(gridItem.state.body?.state.key).toBe('canvas-1-clone-B1');
});
it('Should update all rows when a panel is added to a clone', async () => {
const originalRow = grid.state.children[1] as SceneGridRow;
const clone1 = grid.state.children[2] as SceneGridRow;
const clone2 = grid.state.children[3] as SceneGridRow;
expect(originalRow.state.children.length).toBe(1);
expect(clone1.state.children.length).toBe(1);
expect(clone2.state.children.length).toBe(1);
clone1.setState({
children: [
...clone1.state.children,
new SceneGridItem({
x: 0,
y: 16,
width: 24,
height: 5,
key: 'griditem-4',
body: new SceneCanvasText({
text: 'new panel',
}),
}),
],
});
grid.forceRender();
// repeater has run so there are new clone row objects
const newClone1 = grid.state.children[2] as SceneGridRow;
const newClone2 = grid.state.children[3] as SceneGridRow;
expect(originalRow.state.children.length).toBe(2);
expect(newClone1.state.children.length).toBe(2);
expect(newClone2.state.children.length).toBe(2);
});
it('Should push row at the bottom down', () => {
// Should push row at the bottom down
const rowAtTheBottom = grid.state.children[6] as SceneGridRow;

View File

@@ -46,6 +46,48 @@ export class RowRepeaterBehavior extends SceneObjectBase<RowRepeaterBehaviorStat
private _activationHandler() {
this.performRepeat();
const layout = this._getLayout();
const originalRow = this._getRow();
const filterKey = originalRow.state.key + '-clone-';
const sub = layout.subscribeToState(() => {
const repeatedRows = layout.state.children.filter(
(child) => child instanceof SceneGridRow && child.state.key?.includes(filterKey)
);
// go through cloned rows, search for panels that are not clones
for (const row of repeatedRows) {
if (!(row instanceof SceneGridRow)) {
continue;
}
// if no differences in row children compared to original, then no new panel added to clone
if (row.state.children.length === originalRow.state.children.length) {
continue;
}
//if there are differences, find the new panel, move it to the original and perform re peat
const gridItem = row.state.children.find((gridItem) => !gridItem.state.key?.includes('clone'));
if (gridItem) {
const newGridItem = gridItem.clone();
row.setState({ children: row.state.children.filter((item) => item !== gridItem) });
// if we are moving a panel from the origin row to a clone row, we just return
// this means we are modifying the origin row, retriggering the repeat and losing that panel
if (originalRow.state.children.find((item) => item.state.key === newGridItem.state.key)) {
return;
}
originalRow.setState({ children: [...originalRow.state.children, newGridItem] });
this.performRepeat(true);
}
}
});
return () => {
sub.unsubscribe();
};
}
private _getRow(): SceneGridRow {
@@ -66,7 +108,7 @@ export class RowRepeaterBehavior extends SceneObjectBase<RowRepeaterBehaviorStat
return layout;
}
public performRepeat() {
public performRepeat(force = false) {
this.isWaitingForVariables = this._variableDependency.hasDependencyInLoadingState();
if (this.isWaitingForVariables) {
@@ -95,7 +137,7 @@ export class RowRepeaterBehavior extends SceneObjectBase<RowRepeaterBehaviorStat
const { values, texts } = getMultiVariableValues(variable);
// Do nothing if values are the same
if (isEqual(this._prevRepeatValues, values)) {
if (isEqual(this._prevRepeatValues, values) && !force) {
return;
}
@@ -134,7 +176,12 @@ export class RowRepeaterBehavior extends SceneObjectBase<RowRepeaterBehaviorStat
if (index > 0) {
ensureUniqueKeys(itemClone, localValue);
//disallow clones to be dragged around or out of the row
if (itemClone instanceof DashboardGridItem) {
itemClone.setState({
isDraggable: false,
});
itemClone.state.body.setState({
menu: new VizPanelMenu({
$behaviors: [repeatPanelMenuBehavior],