diff --git a/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row.tsx b/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row.tsx
index db34ec516a4c0..70c93f36664e2 100644
--- a/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row.tsx
+++ b/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row.tsx
@@ -9,7 +9,7 @@
import classNames from 'classnames';
import { cloneDeep } from 'lodash';
-import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
import { combineLatest, map, pairwise, skip } from 'rxjs';
import { css } from '@emotion/react';
@@ -155,6 +155,12 @@ export const GridRow = ({
);
}, [panelIds, gridLayoutStateManager, renderPanelContents, rowIndex]);
+ const toggleIsCollapsed = useCallback(() => {
+ const newLayout = cloneDeep(gridLayoutStateManager.gridLayout$.value);
+ newLayout[rowIndex].isCollapsed = !newLayout[rowIndex].isCollapsed;
+ gridLayoutStateManager.gridLayout$.next(newLayout);
+ }, [rowIndex, gridLayoutStateManager.gridLayout$]);
+
return (
{
- const newLayout = cloneDeep(gridLayoutStateManager.gridLayout$.value);
- newLayout[rowIndex].isCollapsed = !newLayout[rowIndex].isCollapsed;
- gridLayoutStateManager.gridLayout$.next(newLayout);
- }}
+ toggleIsCollapsed={toggleIsCollapsed}
/>
)}
{!isCollapsed && (
diff --git a/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_header.tsx b/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_header.tsx
index b61acc0cda228..19a29ff880c01 100644
--- a/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_header.tsx
+++ b/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_header.tsx
@@ -19,125 +19,128 @@ import { DeleteGridRowModal } from './delete_grid_row_modal';
import { GridRowTitle } from './grid_row_title';
import { useGridRowHeaderStyles } from './use_grid_row_header_styles';
-export const GridRowHeader = ({
- rowIndex,
- gridLayoutStateManager,
- toggleIsCollapsed,
-}: {
- rowIndex: number;
- gridLayoutStateManager: GridLayoutStateManager;
- toggleIsCollapsed: () => void;
-}) => {
- const headerStyles = useGridRowHeaderStyles();
+export const GridRowHeader = React.memo(
+ ({
+ rowIndex,
+ gridLayoutStateManager,
+ toggleIsCollapsed,
+ }: {
+ rowIndex: number;
+ gridLayoutStateManager: GridLayoutStateManager;
+ toggleIsCollapsed: () => void;
+ }) => {
+ const headerStyles = useGridRowHeaderStyles();
- const [editTitleOpen, setEditTitleOpen] = useState(false);
- const [deleteModalVisible, setDeleteModalVisible] = useState(false);
- const [readOnly, setReadOnly] = useState(
- gridLayoutStateManager.accessMode$.getValue() === 'VIEW'
- );
+ const [editTitleOpen, setEditTitleOpen] = useState(false);
+ const [deleteModalVisible, setDeleteModalVisible] = useState(false);
+ const [readOnly, setReadOnly] = useState(
+ gridLayoutStateManager.accessMode$.getValue() === 'VIEW'
+ );
- useEffect(() => {
- const accessModeSubscription = gridLayoutStateManager.accessMode$
- .pipe(distinctUntilChanged())
- .subscribe((accessMode) => {
- setReadOnly(accessMode === 'VIEW');
- });
+ useEffect(() => {
+ const accessModeSubscription = gridLayoutStateManager.accessMode$
+ .pipe(distinctUntilChanged())
+ .subscribe((accessMode) => {
+ setReadOnly(accessMode === 'VIEW');
+ });
- return () => {
- accessModeSubscription.unsubscribe();
- };
- }, [gridLayoutStateManager]);
+ return () => {
+ accessModeSubscription.unsubscribe();
+ };
+ }, [gridLayoutStateManager]);
- return (
- <>
-
-
-
+
+
+
+
+
-
-
- {
- /**
- * Add actions at the end of the header section when the layout is editable + the section title
- * is not in edit mode
- */
- !editTitleOpen && (
- <>
-
- {`(${
- /**
- * we can get away with grabbing the panel count without React state because this count
- * is only rendered when the section is collapsed, and the count can only be updated when
- * the section isn't collapsed
- */
- Object.keys(gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels).length
- } panels)`}
-
- {!readOnly && (
- <>
-
- {
- const panelCount = Object.keys(
- gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels
- ).length;
- if (!Boolean(panelCount)) {
- const newLayout = deleteRow(
- gridLayoutStateManager.gridLayout$.getValue(),
- rowIndex
- );
- gridLayoutStateManager.gridLayout$.next(newLayout);
- } else {
- setDeleteModalVisible(true);
- }
- }}
- />
-
-
-
-
- >
- )}
- >
- )
- }
-
- {deleteModalVisible && (
-
- )}
- >
- );
-};
+ {
+ /**
+ * Add actions at the end of the header section when the layout is editable + the section title
+ * is not in edit mode
+ */
+ !editTitleOpen && (
+ <>
+
+ {`(${
+ /**
+ * we can get away with grabbing the panel count without React state because this count
+ * is only rendered when the section is collapsed, and the count can only be updated when
+ * the section isn't collapsed
+ */
+ Object.keys(gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels)
+ .length
+ } panels)`}
+
+ {!readOnly && (
+ <>
+
+ {
+ const panelCount = Object.keys(
+ gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels
+ ).length;
+ if (!Boolean(panelCount)) {
+ const newLayout = deleteRow(
+ gridLayoutStateManager.gridLayout$.getValue(),
+ rowIndex
+ );
+ gridLayoutStateManager.gridLayout$.next(newLayout);
+ } else {
+ setDeleteModalVisible(true);
+ }
+ }}
+ />
+
+
+
+
+ >
+ )}
+ >
+ )
+ }
+
+ {deleteModalVisible && (
+
+ )}
+ >
+ );
+ }
+);
const styles = {
accordianArrow: css({
diff --git a/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_title.tsx b/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_title.tsx
index 31b94366dbc89..afe818010c15d 100644
--- a/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_title.tsx
+++ b/src/platform/packages/private/kbn-grid-layout/grid/grid_row/grid_row_title.tsx
@@ -14,92 +14,94 @@ import { EuiButtonIcon, EuiFlexItem, EuiInlineEditTitle, EuiLink, EuiTitle } fro
import { GridLayoutStateManager } from '../types';
-export const GridRowTitle = ({
- readOnly,
- rowIndex,
- editTitleOpen,
- setEditTitleOpen,
- toggleIsCollapsed,
- gridLayoutStateManager,
-}: {
- readOnly: boolean;
- rowIndex: number;
- editTitleOpen: boolean;
- setEditTitleOpen: (value: boolean) => void;
- toggleIsCollapsed: () => void;
- gridLayoutStateManager: GridLayoutStateManager;
-}) => {
- const currentRow = gridLayoutStateManager.gridLayout$.getValue()[rowIndex];
- const [rowTitle, setRowTitle] = useState(currentRow.title);
+export const GridRowTitle = React.memo(
+ ({
+ readOnly,
+ rowIndex,
+ editTitleOpen,
+ setEditTitleOpen,
+ toggleIsCollapsed,
+ gridLayoutStateManager,
+ }: {
+ readOnly: boolean;
+ rowIndex: number;
+ editTitleOpen: boolean;
+ setEditTitleOpen: (value: boolean) => void;
+ toggleIsCollapsed: () => void;
+ gridLayoutStateManager: GridLayoutStateManager;
+ }) => {
+ const currentRow = gridLayoutStateManager.gridLayout$.getValue()[rowIndex];
+ const [rowTitle, setRowTitle] = useState(currentRow.title);
- useEffect(() => {
- const titleSubscription = gridLayoutStateManager.gridLayout$
- .pipe(
- map((gridLayout) => gridLayout[rowIndex]?.title ?? ''),
- distinctUntilChanged()
- )
- .subscribe((title) => {
- setRowTitle(title);
- });
+ useEffect(() => {
+ const titleSubscription = gridLayoutStateManager.gridLayout$
+ .pipe(
+ map((gridLayout) => gridLayout[rowIndex]?.title ?? ''),
+ distinctUntilChanged()
+ )
+ .subscribe((title) => {
+ setRowTitle(title);
+ });
- return () => {
- titleSubscription.unsubscribe();
- };
- }, [rowIndex, gridLayoutStateManager]);
+ return () => {
+ titleSubscription.unsubscribe();
+ };
+ }, [rowIndex, gridLayoutStateManager]);
- const updateTitle = useCallback(
- (title: string) => {
- const newLayout = cloneDeep(gridLayoutStateManager.gridLayout$.getValue());
- newLayout[rowIndex].title = title;
- gridLayoutStateManager.gridLayout$.next(newLayout);
- setEditTitleOpen(false);
- },
- [rowIndex, setEditTitleOpen, gridLayoutStateManager.gridLayout$]
- );
+ const updateTitle = useCallback(
+ (title: string) => {
+ const newLayout = cloneDeep(gridLayoutStateManager.gridLayout$.getValue());
+ newLayout[rowIndex].title = title;
+ gridLayoutStateManager.gridLayout$.next(newLayout);
+ setEditTitleOpen(false);
+ },
+ [rowIndex, setEditTitleOpen, gridLayoutStateManager.gridLayout$]
+ );
- useEffect(() => {
- if (!editTitleOpen) return;
- }, [editTitleOpen]);
+ useEffect(() => {
+ if (!editTitleOpen) return;
+ }, [editTitleOpen]);
- return (
- <>
- {!readOnly && editTitleOpen ? (
-
- {/* @ts-ignore - */}
- setEditTitleOpen(false)}
- onSave={updateTitle}
- editModeProps={{
- cancelButtonProps: { onClick: () => setEditTitleOpen(false) },
- formRowProps: { className: 'editModeFormRow ' },
- }}
- startWithEditOpen
- inputAriaLabel="Edit title inline"
- />
-
- ) : (
- <>
-
-
-
- {rowTitle}
-
-
+ return (
+ <>
+ {!readOnly && editTitleOpen ? (
+
+ {/* @ts-ignore - EUI typing issue that will be resolved with https://github.com/elastic/eui/pull/8307 */}
+ setEditTitleOpen(false)}
+ onSave={updateTitle}
+ editModeProps={{
+ cancelButtonProps: { onClick: () => setEditTitleOpen(false) },
+ formRowProps: { className: 'editModeFormRow ' },
+ }}
+ startWithEditOpen
+ inputAriaLabel="Edit title inline"
+ />
- {!readOnly && (
+ ) : (
+ <>
- setEditTitleOpen(true)}
- color="text"
- />
+
+
+ {rowTitle}
+
+
- )}
- >
- )}
- >
- );
-};
+ {!readOnly && (
+
+ setEditTitleOpen(true)}
+ color="text"
+ />
+
+ )}
+ >
+ )}
+ >
+ );
+ }
+);