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" + /> + + )} + + )} + + ); + } +);