mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[kbn-grid-layout] Store rows in object instead of array (#212965)
Closes https://github.com/elastic/kibana/issues/211930 ## Summary This PR makes it so that `kbn-grid-layout` stores its rows as an object / dictionary (`{ [key: string]: GridRowData }`) rather than an array (`Array<GridRowData>`). This is a prerequisite for https://github.com/elastic/kibana/issues/190381 , since it allows us to re-order rows without re-rendering their contents. It also means that deleting a row will no longer cause the rows below it to re-render, since re-rendering is now dependant on the row's **ID** rather than the row's order. **Before** https://github.com/user-attachments/assets/83651b24-a32c-4953-8ad5-c0eced163eb5 **After** https://github.com/user-attachments/assets/9cef6dbc-3d62-46aa-bc40-ab24fc4e5556 ### Checklist - [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: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ef0c364f11
commit
b32f0fe1e8
29 changed files with 325 additions and 286 deletions
|
@ -8,9 +8,11 @@
|
|||
*/
|
||||
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Subject, combineLatest, debounceTime, map, skip, take } from 'rxjs';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
EuiBadge,
|
||||
|
@ -88,8 +90,8 @@ export const GridExample = ({
|
|||
const currentPanel = panels[panelId];
|
||||
const savedPanel = savedState.current.panels[panelId];
|
||||
panelsAreEqual = deepEqual(
|
||||
{ row: 0, ...currentPanel.gridData },
|
||||
{ row: 0, ...savedPanel.gridData }
|
||||
{ row: 'first', ...currentPanel.gridData },
|
||||
{ row: 'first', ...savedPanel.gridData }
|
||||
);
|
||||
}
|
||||
const hasChanges = !(panelsAreEqual && deepEqual(rows, savedState.current.rows));
|
||||
|
@ -147,15 +149,17 @@ export const GridExample = ({
|
|||
);
|
||||
|
||||
const addNewSection = useCallback(() => {
|
||||
mockDashboardApi.rows$.next([
|
||||
...mockDashboardApi.rows$.getValue(),
|
||||
{
|
||||
title: i18n.translate('examples.gridExample.defaultSectionTitle', {
|
||||
defaultMessage: 'New collapsible section',
|
||||
}),
|
||||
collapsed: false,
|
||||
},
|
||||
]);
|
||||
const rows = cloneDeep(mockDashboardApi.rows$.getValue());
|
||||
const id = uuidv4();
|
||||
rows[id] = {
|
||||
id,
|
||||
order: Object.keys(rows).length,
|
||||
title: i18n.translate('examples.gridExample.defaultSectionTitle', {
|
||||
defaultMessage: 'New collapsible section',
|
||||
}),
|
||||
collapsed: false,
|
||||
};
|
||||
mockDashboardApi.rows$.next(rows);
|
||||
|
||||
// scroll to bottom after row is added
|
||||
layoutUpdated$.pipe(skip(1), take(1)).subscribe(() => {
|
||||
|
|
|
@ -1004,7 +1004,7 @@
|
|||
"w": 48,
|
||||
"h": 17,
|
||||
"i": "4",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "4",
|
||||
|
@ -1035,7 +1035,7 @@
|
|||
"w": 18,
|
||||
"h": 8,
|
||||
"i": "05da0d2b-0145-4068-b21c-00be3184d465",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "05da0d2b-0145-4068-b21c-00be3184d465",
|
||||
|
@ -1073,7 +1073,7 @@
|
|||
"w": 18,
|
||||
"h": 16,
|
||||
"i": "b7da9075-4742-47e3-b4f8-fc9ba82de74c",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "b7da9075-4742-47e3-b4f8-fc9ba82de74c",
|
||||
|
@ -1111,7 +1111,7 @@
|
|||
"w": 12,
|
||||
"h": 16,
|
||||
"i": "5c409557-644d-4c05-a284-ffe54bb28db0",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "5c409557-644d-4c05-a284-ffe54bb28db0",
|
||||
|
@ -1234,7 +1234,7 @@
|
|||
"w": 6,
|
||||
"h": 8,
|
||||
"i": "af4b5c07-506e-44c2-b2bb-2113d0c5b274",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "af4b5c07-506e-44c2-b2bb-2113d0c5b274",
|
||||
|
@ -1400,7 +1400,7 @@
|
|||
"w": 6,
|
||||
"h": 8,
|
||||
"i": "d42c4870-c028-4d8a-abd0-0effbc190ce3",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "d42c4870-c028-4d8a-abd0-0effbc190ce3",
|
||||
|
@ -1520,7 +1520,7 @@
|
|||
"w": 6,
|
||||
"h": 8,
|
||||
"i": "4092d42c-f93b-4c71-a6db-8f12abf12791",
|
||||
"row": 1
|
||||
"row": "second"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "4092d42c-f93b-4c71-a6db-8f12abf12791",
|
||||
|
@ -1641,7 +1641,7 @@
|
|||
"w": 30,
|
||||
"h": 15,
|
||||
"i": "15",
|
||||
"row": 2
|
||||
"row": "third"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "15",
|
||||
|
@ -1887,7 +1887,7 @@
|
|||
"w": 18,
|
||||
"h": 8,
|
||||
"i": "4e64d6d7-4f92-4d5e-abbb-13796604db30",
|
||||
"row": 2
|
||||
"row": "third"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "4e64d6d7-4f92-4d5e-abbb-13796604db30v",
|
||||
|
@ -1925,7 +1925,7 @@
|
|||
"w": 6,
|
||||
"h": 7,
|
||||
"i": "ddce4ad8-6a82-44f0-9995-57f46f153f50",
|
||||
"row": 2
|
||||
"row": "third"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "ddce4ad8-6a82-44f0-9995-57f46f153f50",
|
||||
|
@ -2120,7 +2120,7 @@
|
|||
"w": 6,
|
||||
"h": 7,
|
||||
"i": "a2884704-db3b-4b92-a19a-cdfe668dec39",
|
||||
"row": 2
|
||||
"row": "third"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "a2884704-db3b-4b92-a19a-cdfe668dec39",
|
||||
|
@ -2315,7 +2315,7 @@
|
|||
"w": 6,
|
||||
"h": 7,
|
||||
"i": "529eec49-10e2-4a40-9c77-5c81f4eb3943",
|
||||
"row": 2
|
||||
"row": "third"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "529eec49-10e2-4a40-9c77-5c81f4eb3943",
|
||||
|
@ -2510,7 +2510,7 @@
|
|||
"w": 48,
|
||||
"h": 12,
|
||||
"i": "1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b",
|
||||
"row": 2
|
||||
"row": "third"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b",
|
||||
|
@ -2905,7 +2905,7 @@
|
|||
"w": 48,
|
||||
"h": 15,
|
||||
"i": "9f79ecca-123f-4098-a658-6b0e998da003",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "9f79ecca-123f-4098-a658-6b0e998da003",
|
||||
|
@ -2922,7 +2922,7 @@
|
|||
"w": 24,
|
||||
"h": 9,
|
||||
"i": "7",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "7",
|
||||
|
@ -3161,7 +3161,7 @@
|
|||
"w": 24,
|
||||
"h": 11,
|
||||
"i": "10",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "10",
|
||||
|
@ -3346,7 +3346,7 @@
|
|||
"w": 24,
|
||||
"h": 22,
|
||||
"i": "23",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "23",
|
||||
|
@ -3371,7 +3371,7 @@
|
|||
"w": 24,
|
||||
"h": 22,
|
||||
"i": "31",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "31",
|
||||
|
@ -3388,7 +3388,7 @@
|
|||
"w": 24,
|
||||
"h": 8,
|
||||
"i": "6afc61f7-e2d5-45a3-9e7a-281160ad3eb9",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "6afc61f7-e2d5-45a3-9e7a-281160ad3eb9",
|
||||
|
@ -3420,7 +3420,7 @@
|
|||
"w": 8,
|
||||
"h": 8,
|
||||
"i": "392b4936-f753-47bc-a98d-a4e41a0a4cd4",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "392b4936-f753-47bc-a98d-a4e41a0a4cd4",
|
||||
|
@ -3485,7 +3485,7 @@
|
|||
"w": 8,
|
||||
"h": 4,
|
||||
"i": "9271deff-5a61-4665-83fc-f9fdc6bf0c0b",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "9271deff-5a61-4665-83fc-f9fdc6bf0c0b",
|
||||
|
@ -3613,7 +3613,7 @@
|
|||
"w": 8,
|
||||
"h": 4,
|
||||
"i": "aa591c29-1a31-4ee1-a71d-b829c06fd162",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "aa591c29-1a31-4ee1-a71d-b829c06fd162",
|
||||
|
@ -3777,7 +3777,7 @@
|
|||
"w": 8,
|
||||
"h": 4,
|
||||
"i": "b766e3b8-4544-46ed-99e6-9ecc4847e2a2",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "b766e3b8-4544-46ed-99e6-9ecc4847e2a2",
|
||||
|
@ -3905,7 +3905,7 @@
|
|||
"w": 8,
|
||||
"h": 4,
|
||||
"i": "2e33ade5-96e5-40b4-b460-493e5d4fa834",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "2e33ade5-96e5-40b4-b460-493e5d4fa834",
|
||||
|
@ -4069,7 +4069,7 @@
|
|||
"w": 24,
|
||||
"h": 8,
|
||||
"i": "086ac2e9-dd16-4b45-92b8-1e43ff7e3f65",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "086ac2e9-dd16-4b45-92b8-1e43ff7e3f65",
|
||||
|
@ -4190,7 +4190,7 @@
|
|||
"w": 24,
|
||||
"h": 28,
|
||||
"i": "fb86b32f-fb7a-45cf-9511-f366fef51bbd",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "fb86b32f-fb7a-45cf-9511-f366fef51bbd",
|
||||
|
@ -4500,7 +4500,7 @@
|
|||
"w": 24,
|
||||
"h": 11,
|
||||
"i": "0cc42484-16f7-42ec-b38c-9bf8be69cde7",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "0cc42484-16f7-42ec-b38c-9bf8be69cde7",
|
||||
|
@ -4643,7 +4643,7 @@
|
|||
"w": 12,
|
||||
"h": 11,
|
||||
"i": "5d53db36-2d5a-4adc-af7b-cec4c1a294e0",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "5d53db36-2d5a-4adc-af7b-cec4c1a294e0",
|
||||
|
@ -4773,7 +4773,7 @@
|
|||
"w": 12,
|
||||
"h": 11,
|
||||
"i": "ecd89a7c-9124-4472-bdc6-9bdbd70d45d5",
|
||||
"row": 3
|
||||
"row": "fourth"
|
||||
},
|
||||
"explicitInput": {
|
||||
"id": "ecd89a7c-9124-4472-bdc6-9bdbd70d45d5",
|
||||
|
|
|
@ -28,10 +28,10 @@ export function setSerializedGridLayout(state: MockSerializedDashboardState) {
|
|||
|
||||
const initialState: MockSerializedDashboardState = {
|
||||
panels: logsPanels,
|
||||
rows: [
|
||||
{ title: 'Request Sizes', collapsed: false },
|
||||
{ title: 'Visitors', collapsed: false },
|
||||
{ title: 'Response Codes', collapsed: false },
|
||||
{ title: 'Entire Flights Dashboard', collapsed: true },
|
||||
],
|
||||
rows: {
|
||||
first: { id: 'first', order: 0, title: 'Request Sizes', collapsed: false },
|
||||
second: { id: 'second', order: 1, title: 'Visitors', collapsed: false },
|
||||
third: { id: 'third', order: 2, title: 'Response Codes', collapsed: false },
|
||||
fourth: { id: 'fourth', order: 3, title: 'Entire Flights Dashboard', collapsed: true },
|
||||
},
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ export interface DashboardGridData {
|
|||
|
||||
interface DashboardPanelState {
|
||||
type: string;
|
||||
gridData: DashboardGridData & { row?: number };
|
||||
gridData: DashboardGridData & { row?: string };
|
||||
explicitInput: Partial<any> & { id: string };
|
||||
version?: string;
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ export interface MockedDashboardPanelMap {
|
|||
[key: string]: DashboardPanelState;
|
||||
}
|
||||
|
||||
export type MockedDashboardRowMap = Array<{ title: string; collapsed: boolean }>;
|
||||
export interface MockedDashboardRowMap {
|
||||
[id: string]: { id: string; order: number; title: string; collapsed: boolean };
|
||||
}
|
||||
|
||||
export interface MockSerializedDashboardState {
|
||||
panels: MockedDashboardPanelMap;
|
||||
|
|
|
@ -99,7 +99,7 @@ export const useMockDashboardApi = ({
|
|||
[newId]: {
|
||||
type: panelPackage.panelType,
|
||||
gridData: {
|
||||
row: 0,
|
||||
row: 'first',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: DEFAULT_PANEL_WIDTH,
|
||||
|
|
|
@ -15,10 +15,11 @@ export const gridLayoutToDashboardPanelMap = (
|
|||
layout: GridLayoutData
|
||||
): { panels: MockedDashboardPanelMap; rows: MockedDashboardRowMap } => {
|
||||
const panels: MockedDashboardPanelMap = {};
|
||||
const rows: MockedDashboardRowMap = [];
|
||||
layout.forEach((row, rowIndex) => {
|
||||
rows.push({ title: row.title, collapsed: row.isCollapsed });
|
||||
Object.values(row.panels).forEach((panelGridData) => {
|
||||
const rows: MockedDashboardRowMap = {};
|
||||
Object.entries(layout).forEach(([rowId, row]) => {
|
||||
const { panels: rowPanels, isCollapsed, ...rest } = row; // drop panels
|
||||
rows[rowId] = { ...rest, collapsed: isCollapsed };
|
||||
Object.values(rowPanels).forEach((panelGridData) => {
|
||||
panels[panelGridData.id] = {
|
||||
...panelState[panelGridData.id],
|
||||
gridData: {
|
||||
|
@ -27,7 +28,7 @@ export const gridLayoutToDashboardPanelMap = (
|
|||
x: panelGridData.column,
|
||||
w: panelGridData.width,
|
||||
h: panelGridData.height,
|
||||
row: rowIndex,
|
||||
row: rowId,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -42,15 +43,19 @@ export const dashboardInputToGridLayout = ({
|
|||
panels: MockedDashboardPanelMap;
|
||||
rows: MockedDashboardRowMap;
|
||||
}): GridLayoutData => {
|
||||
const layout: GridLayoutData = [];
|
||||
|
||||
rows.forEach((row) => {
|
||||
layout.push({ title: row.title, isCollapsed: row.collapsed, panels: {} });
|
||||
const layout: GridLayoutData = {};
|
||||
Object.values(rows).forEach((row) => {
|
||||
const { collapsed, ...rest } = row;
|
||||
layout[row.id] = {
|
||||
...rest,
|
||||
panels: {},
|
||||
isCollapsed: collapsed,
|
||||
};
|
||||
});
|
||||
|
||||
Object.keys(panels).forEach((panelId) => {
|
||||
const gridData = panels[panelId].gridData;
|
||||
layout[gridData.row ?? 0].panels[panelId] = {
|
||||
layout[gridData.row ?? 'first'].panels[panelId] = {
|
||||
id: panelId,
|
||||
row: gridData.y,
|
||||
column: gridData.x,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue