diff --git a/CHANGELOG.md b/CHANGELOG.md index e3c6f232418..6969e83e9fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ **Bug fixes** - Fixed multiple bugs with `EuiDataGrid` keyboard focus restoration ([#5530](https://github.com/elastic/eui/pull/5530)) +- Fixed `EuiDataGrid`'s display toolbar control to update initial UI state when developer `gridStyle` or `rowHeightsOptions` props are updated ([#5525](https://github.com/elastic/eui/pull/5525)) **Breaking changes** diff --git a/src/components/datagrid/controls/display_selector.test.tsx b/src/components/datagrid/controls/display_selector.test.tsx index a4d3f5d66ac..0990a39bd9e 100644 --- a/src/components/datagrid/controls/display_selector.test.tsx +++ b/src/components/datagrid/controls/display_selector.test.tsx @@ -121,7 +121,7 @@ describe('useDataGridDisplaySelector', () => { ).toHaveLength(0); }); - describe('convertGridStylesToSelection (loading initial state from passed gridStyles', () => { + describe('convertGridStylesToSelection', () => { it('should set compact state if both fontSize and cellPadding are s', () => { const component = mount( @@ -155,6 +155,18 @@ describe('useDataGridDisplaySelector', () => { }); }); + it('updates grid density whenever new developer styles are passed in', () => { + const component = mount( + + ); + openPopover(component); + expect(getSelection(component)).toEqual('expanded'); + + component.setProps({ gridStyles: { fontSize: 's', cellPadding: 's' } }); + component.update(); + expect(getSelection(component)).toEqual('compact'); + }); + it('correctly resets density to initial developer-passed state', () => { const component = mount( @@ -216,7 +228,7 @@ describe('useDataGridDisplaySelector', () => { ).toHaveLength(0); }); - describe('convertRowHeightsOptionsToSelection (loading initial state from passed rowHeightsOptions)', () => { + describe('convertRowHeightsOptionsToSelection', () => { test('auto', () => { const component = mount( @@ -260,6 +272,20 @@ describe('useDataGridDisplaySelector', () => { }); }); + it('updates row height whenever new developer settings are passed in', () => { + const component = mount( + + ); + openPopover(component); + expect(getSelection(component)).toEqual('auto'); + + component.setProps({ + rowHeightsOptions: { defaultHeight: { lineCount: 3 } }, + }); + component.update(); + expect(getSelection(component)).toEqual('lineCount'); + }); + it('correctly resets row height to initial developer-passed state', () => { const component = mount( @@ -380,9 +406,10 @@ describe('useDataGridDisplaySelector', () => { component.find('[data-test-subj="resetDisplaySelector"]').exists() ).toBe(true); - // Should hide the reset button again when changing back to the initial configuration - component.find('[data-test-subj="normal"]').simulate('change'); - component.find('[data-test-subj="undefined"]').simulate('change'); + // Should hide the reset button again after it's been clicked + component + .find('button[data-test-subj="resetDisplaySelector"]') + .simulate('click'); expect( component.find('[data-test-subj="resetDisplaySelector"]').exists() ).toBe(false); @@ -447,11 +474,6 @@ describe('useDataGridDisplaySelector', () => { .find('[data-test-subj="rowHeightButtonGroup"]') .simulate('change', selection); }; - const setLineCount = (component: ShallowWrapper, lineCount = 1) => { - diveIntoEuiI18n(component) - .find('[data-test-subj="lineCountNumber"]') - .simulate('change', { target: { value: lineCount } }); - }; const getOutput = (component: ShallowWrapper) => { return JSON.parse(component.find('[data-test-subj="output"]').text()); }; @@ -481,11 +503,10 @@ describe('useDataGridDisplaySelector', () => { ); setRowHeight(component, 'lineCount'); - setLineCount(component, 5); expect(getOutput(component)).toEqual({ lineHeight: '2em', - defaultHeight: { lineCount: 5 }, + defaultHeight: { lineCount: 2 }, rowHeights: {}, }); }); diff --git a/src/components/datagrid/controls/display_selector.tsx b/src/components/datagrid/controls/display_selector.tsx index d7be1d2d0af..62fc651280a 100644 --- a/src/components/datagrid/controls/display_selector.tsx +++ b/src/components/datagrid/controls/display_selector.tsx @@ -6,7 +6,13 @@ * Side Public License, v 1. */ -import React, { ReactNode, useState, useMemo, useCallback } from 'react'; +import React, { + ReactNode, + useState, + useMemo, + useCallback, + useEffect, +} from 'react'; import { useUpdateEffect } from '../../../services'; import { EuiI18n, useEuiI18n } from '../../i18n'; @@ -51,11 +57,11 @@ const densityStyles: { [key: string]: Partial } = { }, }; const convertGridStylesToSelection = (gridStyles: EuiDataGridStyle) => { - if (gridStyles?.fontSize === 's' && gridStyles?.cellPadding === 's') + if (gridStyles.fontSize === 's' && gridStyles.cellPadding === 's') return 'compact'; - if (gridStyles?.fontSize === 'm' && gridStyles?.cellPadding === 'm') + if (gridStyles.fontSize === 'm' && gridStyles.cellPadding === 'm') return 'normal'; - if (gridStyles?.fontSize === 'l' && gridStyles?.cellPadding === 'l') + if (gridStyles.fontSize === 'l' && gridStyles.cellPadding === 'l') return 'expanded'; return ''; }; @@ -66,23 +72,21 @@ const capitalizeDensityString = (s: string) => s[0].toUpperCase() + s.slice(1); // Row height options and utilities const rowHeightButtonOptions: string[] = ['undefined', 'auto', 'lineCount']; const convertRowHeightsOptionsToSelection = ( - rowHeightsOptions?: EuiDataGridRowHeightsOptions + rowHeightsOptions: EuiDataGridRowHeightsOptions ) => { - if (rowHeightsOptions) { - const { defaultHeight } = rowHeightsOptions; + const { defaultHeight } = rowHeightsOptions; - if (defaultHeight === 'auto') { - return rowHeightButtonOptions[1]; - } - if (typeof defaultHeight === 'object' && defaultHeight?.lineCount) { - return rowHeightButtonOptions[2]; - } - if ( - typeof defaultHeight === 'number' || - (typeof defaultHeight === 'object' && defaultHeight.height) - ) { - return ''; - } + if (defaultHeight === 'auto') { + return rowHeightButtonOptions[1]; + } + if (typeof defaultHeight === 'object' && defaultHeight?.lineCount) { + return rowHeightButtonOptions[2]; + } + if ( + typeof defaultHeight === 'number' || + (typeof defaultHeight === 'object' && defaultHeight.height) + ) { + return ''; } return rowHeightButtonOptions[0]; }; @@ -104,37 +108,17 @@ export const useDataGridDisplaySelector = ( 'allowRowHeight' ); - // Get initial state (also used when resetting) - const initialDensity = useMemo( - () => convertGridStylesToSelection(initialStyles), - [initialStyles] - ); - const initialRowHeight = useMemo( - () => convertRowHeightsOptionsToSelection(initialRowHeightsOptions), - [initialRowHeightsOptions] - ); - const initialLineCount = useMemo( - // @ts-ignore - optional chaining operator handles types & cases that aren't lineCount - () => initialRowHeightsOptions?.defaultHeight?.lineCount || 2, - [initialRowHeightsOptions?.defaultHeight] - ); - // Track styles specified by the user at run time const [userGridStyles, setUserGridStyles] = useState({}); const [userRowHeightsOptions, setUserRowHeightsOptions] = useState({}); - // Density state - const [gridDensity, _setGridDensity] = useState(initialDensity); - const setGridDensity = (density: string) => { - _setGridDensity(density); + // Density logic + const setGridStyles = useCallback((density: string) => { setUserGridStyles(densityStyles[density]); - }; + }, []); - // Row height state - const [lineCount, setLineCount] = useState(initialLineCount); - const [rowHeightSelection, setRowHeightSelection] = useState( - initialRowHeight - ); + // Row height logic + const [lineCount, setLineCount] = useState(2); const setRowHeight = useCallback( (option: string) => { const rowHeightsOptions: EuiDataGridRowHeightsOptions = { @@ -149,7 +133,6 @@ export const useDataGridDisplaySelector = ( rowHeightsOptions.defaultHeight = undefined; } - setRowHeightSelection(option); setUserRowHeightsOptions(rowHeightsOptions); }, [lineCount] @@ -180,39 +163,47 @@ export const useDataGridDisplaySelector = ( }; }, [initialRowHeightsOptions, userRowHeightsOptions]); - // Invoke onChange callbacks on user input (removing the callback value itself, so that only configuration values are returned) + // Set UI controls based on current configurations, on init & when either developer or user settings change + const gridDensity = useMemo(() => { + return convertGridStylesToSelection(gridStyles); + }, [gridStyles]); + + const rowHeightSelection = useMemo(() => { + return convertRowHeightsOptionsToSelection(rowHeightsOptions); + }, [rowHeightsOptions]); + + useEffect(() => { + // @ts-ignore - optional chaining operator handles types & cases that aren't lineCount + setLineCount(rowHeightsOptions?.defaultHeight?.lineCount || 2); + // @ts-ignore - same as above + }, [rowHeightsOptions?.defaultHeight?.lineCount]); + + // Show a reset button whenever users manually change settings, and + // invoke onChange callbacks (removing the callback value itself, so that only configuration values are returned) + const [showResetButton, setShowResetButton] = useState(false); + useUpdateEffect(() => { + const hasUserChanges = Object.keys(userGridStyles).length > 0; + if (hasUserChanges) setShowResetButton(true); + const { onChange, ...currentGridStyles } = gridStyles; initialStyles?.onChange?.(currentGridStyles); }, [userGridStyles]); useUpdateEffect(() => { + const hasUserChanges = Object.keys(userRowHeightsOptions).length > 0; + if (hasUserChanges) setShowResetButton(true); + const { onChange, ...currentRowHeightsOptions } = rowHeightsOptions; initialRowHeightsOptions?.onChange?.(currentRowHeightsOptions); }, [userRowHeightsOptions]); // Allow resetting to initial developer-specified configurations const resetToInitialState = useCallback(() => { - setGridDensity(initialDensity); setUserGridStyles({}); - setRowHeightSelection(initialRowHeight); setUserRowHeightsOptions({}); - setLineCount(initialLineCount); - }, [initialDensity, initialRowHeight, initialLineCount]); - - const showResetButton = useMemo(() => { - if (initialDensity !== gridDensity) return true; - if (initialRowHeight !== rowHeightSelection) return true; - if (initialLineCount !== lineCount) return true; - return false; - }, [ - initialDensity, - gridDensity, - initialRowHeight, - rowHeightSelection, - initialLineCount, - lineCount, - ]); + setShowResetButton(false); + }, []); const buttonLabel = useEuiI18n( 'euiDisplaySelector.buttonText', @@ -285,7 +276,7 @@ export const useDataGridDisplaySelector = ( label: labelExpanded, }, ]} - onChange={setGridDensity} + onChange={setGridStyles} idSelected={gridDensity} data-test-subj="densityButtonGroup" /> diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 09b7c82fef6..15f49ec113d 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -130,7 +130,10 @@ export const EuiDataGrid: FunctionComponent = (props) => { /** * Merge consumer settings with defaults */ - const gridStyleWithDefaults = { ...startingStyles, ...gridStyle }; + const gridStyleWithDefaults = useMemo( + () => ({ ...startingStyles, ...gridStyle }), + [gridStyle] + ); const mergedPopoverContents = useMemo( () => ({