kibana/examples/grid_example/public/utils.ts
Hannah Mudge 1b25685667
[kbn-grid-layout] Flatten grid layout (#218900)
Closes https://github.com/elastic/kibana/issues/216096

## Summary

This PR accomplishes two main things:
1. It flattens out how grid elements are rendered, which means that
embeddables no longer re-mount when dragged between sections and
2. It allows panels and sections to be "intermixed" on a **single**
level (i.e. you can only drop a section header between panels if they
are **not** in a section)

Since this was a **major** rewrite of the grid layout logic, I also took
some time to clean up the code - this includes removing
`proposedGridLayout$` (since this added two sources of truth, which was
causing issues with the DOM becoming out-of-sync with the layout object;
however, this also caused
https://github.com/elastic/kibana/issues/220309) and unifying on the use
of "section" rather than "row" (since it was confusing that we were
using "row" for both the grid row number and the section ID).


https://github.com/user-attachments/assets/c5d9aa97-5b14-4f4c-aacf-74055c7d9c33

> [!NOTE]
> Reminder that, since collapsible sections aren't available in
Dashboard yet, you must test this PR in the `grid` example app (by
running Kibana with `yarn start --run-examples`).


### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: mbondyra <marta.bondyra@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com>
2025-05-21 08:01:13 -06:00

90 lines
2.8 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { GridLayoutData, GridSectionData } from '@kbn/grid-layout';
import { MockedDashboardPanelMap, MockedDashboardSectionMap } from './types';
export const gridLayoutToDashboardPanelMap = (
panelState: MockedDashboardPanelMap,
layout: GridLayoutData
): { panels: MockedDashboardPanelMap; sections: MockedDashboardSectionMap } => {
const panels: MockedDashboardPanelMap = {};
const sections: MockedDashboardSectionMap = {};
Object.entries(layout).forEach(([widgetId, widget]) => {
if (widget.type === 'panel') {
const panelGridData = widget;
panels[panelGridData.id] = {
...panelState[panelGridData.id],
gridData: {
i: panelGridData.id,
y: panelGridData.row,
x: panelGridData.column,
w: panelGridData.width,
h: panelGridData.height,
},
};
} else {
const { panels: rowPanels, type, isCollapsed, row, ...rest } = widget; // drop panels and type
sections[widgetId] = { ...rest, y: row, collapsed: isCollapsed };
Object.values(rowPanels).forEach((panelGridData) => {
panels[panelGridData.id] = {
...panelState[panelGridData.id],
gridData: {
i: panelGridData.id,
y: panelGridData.row,
x: panelGridData.column,
w: panelGridData.width,
h: panelGridData.height,
section: widgetId,
},
};
});
}
});
return { panels, sections };
};
export const dashboardInputToGridLayout = ({
panels,
sections,
}: {
panels: MockedDashboardPanelMap;
sections: MockedDashboardSectionMap;
}): GridLayoutData => {
const layout: GridLayoutData = {};
Object.values(sections).forEach((row) => {
const { collapsed, y, ...rest } = row;
layout[row.id] = {
...rest,
type: 'section',
row: y,
panels: {},
isCollapsed: collapsed,
};
});
Object.keys(panels).forEach((panelId) => {
const gridData = panels[panelId].gridData;
const panelData = {
id: panelId,
row: gridData.y,
column: gridData.x,
width: gridData.w,
height: gridData.h,
};
if (gridData.section) {
(layout[gridData.section] as GridSectionData).panels[panelId] = panelData;
} else {
layout[panelId] = { type: 'panel', ...panelData };
}
});
return layout;
};