From 7767b10688c944e7c7d9ddc9edfbccb21ce35aa0 Mon Sep 17 00:00:00 2001 From: Julia Rechkunova Date: Mon, 25 Sep 2023 18:12:17 +0200 Subject: [PATCH] [EuiDataGrid] Introduce `renderCustomToolbar`, `additionalDisplaySettings` and `allowResetButton` customization options (#7190) Co-authored-by: Tim Schnell Co-authored-by: Matthias Wilhelm Co-authored-by: Cee Chen <549407+cee-chen@users.noreply.github.com> Co-authored-by: Cee Chen --- src-docs/src/views/datagrid/toolbar/_grid.js | 43 +++++- .../src/views/datagrid/toolbar/_props.tsx | 2 + .../toolbar/datagrid_toolbar_example.js | 53 +++++++ .../toolbar/render_custom_toolbar.tsx | 136 ++++++++++++++++++ .../src/views/datagrid/toolbar/visibility.js | 43 +++++- .../__snapshots__/data_grid.test.tsx.snap | 8 +- .../display_selector.test.tsx.snap | 2 +- .../controls/data_grid_toolbar.test.tsx | 30 ++++ .../datagrid/controls/data_grid_toolbar.tsx | 86 ++++++----- .../controls/display_selector.test.tsx | 105 ++++++++++---- .../datagrid/controls/display_selector.tsx | 40 ++++-- src/components/datagrid/data_grid.tsx | 2 + src/components/datagrid/data_grid_types.ts | 31 ++++ upcoming_changelogs/7190.md | 4 + 14 files changed, 497 insertions(+), 88 deletions(-) create mode 100644 src-docs/src/views/datagrid/toolbar/render_custom_toolbar.tsx create mode 100644 upcoming_changelogs/7190.md diff --git a/src-docs/src/views/datagrid/toolbar/_grid.js b/src-docs/src/views/datagrid/toolbar/_grid.js index b1faa80f8b2..c4edb25ddb9 100644 --- a/src-docs/src/views/datagrid/toolbar/_grid.js +++ b/src-docs/src/views/datagrid/toolbar/_grid.js @@ -1,7 +1,12 @@ import React, { useState, useCallback, useMemo } from 'react'; import { faker } from '@faker-js/faker'; -import { EuiDataGrid, EuiAvatar } from '../../../../../src/components'; +import { + EuiDataGrid, + EuiAvatar, + EuiFormRow, + EuiRange, +} from '../../../../../src/components'; const columns = [ { @@ -52,6 +57,8 @@ const DataGridStyle = ({ showFullScreenSelector, allowDensity, allowRowHeight, + allowResetButton, + additionalDisplaySettings, allowHideColumns, allowOrderingColumns, }) => { @@ -98,13 +105,41 @@ const DataGridStyle = ({ const toggleDisplaySelector = useMemo(() => { if ( showDisplaySelector === true && - (allowDensity === false || allowRowHeight === false) + (allowDensity === false || + allowRowHeight === false || + allowResetButton === false || + additionalDisplaySettings) ) { - return { allowDensity, allowRowHeight }; + const customDisplaySetting = additionalDisplaySettings && ( + + + + ); + return { + allowDensity, + allowRowHeight, + allowResetButton, + additionalDisplaySettings: customDisplaySetting, + }; } else { return showDisplaySelector; } - }, [showDisplaySelector, allowDensity, allowRowHeight]); + }, [ + showDisplaySelector, + allowDensity, + allowRowHeight, + allowResetButton, + additionalDisplaySettings, + ]); const toolbarVisibilityOptions = { showColumnSelector: toggleColumnSelector, diff --git a/src-docs/src/views/datagrid/toolbar/_props.tsx b/src-docs/src/views/datagrid/toolbar/_props.tsx index 87671f9502c..77fc48346a7 100644 --- a/src-docs/src/views/datagrid/toolbar/_props.tsx +++ b/src-docs/src/views/datagrid/toolbar/_props.tsx @@ -13,6 +13,8 @@ const gridSnippets = { showDisplaySelector: `showDisplaySelector: { allowDensity: false; allowRowHeight: false; + allowResetButton: false; + additionalDisplaySettings: ; }`, showSortSelector: 'showSortSelector: false', showFullScreenSelector: 'showFullScreenSelector: false', diff --git a/src-docs/src/views/datagrid/toolbar/datagrid_toolbar_example.js b/src-docs/src/views/datagrid/toolbar/datagrid_toolbar_example.js index 1a36b21cc05..9998f094434 100644 --- a/src-docs/src/views/datagrid/toolbar/datagrid_toolbar_example.js +++ b/src-docs/src/views/datagrid/toolbar/datagrid_toolbar_example.js @@ -9,6 +9,9 @@ const dataGridToolbarVisibilitySource = require('!!raw-loader!./_grid'); import DataGridControls from './additional_controls'; const dataGridControlsSource = require('!!raw-loader!./additional_controls'); +import DataGridCustomToolbar from './render_custom_toolbar'; +const dataGridCustomToolbarSource = require('!!raw-loader!./render_custom_toolbar'); + import ToolbarPropsTable from './_props'; import { @@ -17,6 +20,7 @@ import { EuiDataGridToolBarVisibilityColumnSelectorOptions, EuiDataGridToolBarVisibilityDisplaySelectorOptions, EuiDataGridToolBarAdditionalControlsLeftOptions, + EuiDataGridCustomToolbarProps, } from '!!prop-loader!../../../../../src/components/datagrid/data_grid_types'; /* eslint-disable local/css-logical-properties */ @@ -185,6 +189,55 @@ export const DataGridToolbarExample = { }, demo: , }, + { + title: 'Completely custom toolbar rendering', + source: [ + { + type: GuideSectionTypes.TSX, + code: dataGridCustomToolbarSource, + }, + ], + text: ( + <> +

+ If more customized control over the toolbar is required than{' '} + toolbarVisibility or{' '} + additionalControls allows, you can use the{' '} + renderCustomToolbar prop to pass a component. The + default datagrid controls are passed back as parameters for optional + usage. +

+

+ renderCustomToolbar should only be used when a + very custom layout (e.g. moving default buttons between sides, + interspering custom controls between default controls, custom + responsive behavior, etc.) is required. We would caution you to keep + consistency in mind also when customizing the toolbar: if using + multiple datagrid instances across your app, users will typically + want to reach for the same controls for each grid. Changing the + available controls inconsistently across your app may result in user + frustration. +

+ + ), + demo: , + props: { + EuiDataGridCustomToolbarProps, + }, + snippet: `
Custom toolbar content {displayControl}
} + toolbarVisibility={{ + showDisplaySelector: { + allowResetButton: false, + additionalDisplaySettings:
Custom settings content
+ } + }} +/>`, + }, { title: 'Toolbar props', text: ( diff --git a/src-docs/src/views/datagrid/toolbar/render_custom_toolbar.tsx b/src-docs/src/views/datagrid/toolbar/render_custom_toolbar.tsx new file mode 100644 index 00000000000..6f32b3a895a --- /dev/null +++ b/src-docs/src/views/datagrid/toolbar/render_custom_toolbar.tsx @@ -0,0 +1,136 @@ +import React, { useCallback, useState } from 'react'; +import { css } from '@emotion/react'; +import { faker } from '@faker-js/faker'; + +import { + EuiDataGrid, + EuiDataGridSorting, + EuiDataGridColumnSortingConfig, + EuiDataGridToolbarProps, + EuiButtonEmpty, + EuiFormRow, + EuiRange, + EuiFlexGroup, + EuiFlexItem, + euiScreenReaderOnly, +} from '../../../../../src'; + +const raw_data: Array<{ [key: string]: string }> = []; +for (let i = 0; i < 5; i++) { + raw_data.push({ + name: `${faker.person.lastName()}, ${faker.person.firstName()}`, + email: faker.internet.email(), + location: `${faker.location.city()}, ${faker.location.country()}`, + date: `${faker.date.past()}`, + amount: faker.commerce.price({ min: 1, max: 1000, dec: 2, symbol: '$' }), + }); +} +const columns = [ + { id: 'name', displayAsText: 'Name' }, + { id: 'email', displayAsText: 'Email address' }, + { id: 'location', displayAsText: 'Location' }, + { id: 'date', displayAsText: 'Date' }, + { id: 'amount', displayAsText: 'Amount' }, +]; + +// Custom toolbar renderer +const renderCustomToolbar: EuiDataGridToolbarProps['renderCustomToolbar'] = ({ + hasRoomForGridControls, + columnControl, + columnSortingControl, + displayControl, + fullScreenControl, + keyboardShortcutsControl, +}) => { + const mobileStyles = + !hasRoomForGridControls && + css` + .euiDataGrid__controlBtn .euiButtonEmpty__text { + ${euiScreenReaderOnly()} + } + `; + return ( + + + {hasRoomForGridControls && ( + + Custom left side + + )} + + + + + {columnControl} + {columnSortingControl} + {keyboardShortcutsControl} + {displayControl} + {fullScreenControl} + + + + ); +}; + +// Some additional custom settings to show in the Display popover +const AdditionalDisplaySettings = () => { + const [exampleSettingValue, setExampleSettingValue] = useState(10); + + return ( + + { + setExampleSettingValue(Number(event.currentTarget.value)); + }} + /> + + ); +}; + +export default () => { + // Column visibility + const [visibleColumns, setVisibleColumns] = useState(() => + columns.map(({ id }) => id) + ); + + // Sorting + const [sortingColumns, setSortingColumns] = useState< + EuiDataGridColumnSortingConfig[] + >([]); + const onSort = useCallback((sortingColumns) => { + setSortingColumns(sortingColumns); + }, []); + + return ( + raw_data[rowIndex][columnId]} + gridStyle={{ border: 'none', header: 'underline' }} + renderCustomToolbar={renderCustomToolbar} + toolbarVisibility={{ + showDisplaySelector: { + allowResetButton: false, + additionalDisplaySettings: , + }, + }} + /> + ); +}; diff --git a/src-docs/src/views/datagrid/toolbar/visibility.js b/src-docs/src/views/datagrid/toolbar/visibility.js index f885a69099e..ccbc141e832 100644 --- a/src-docs/src/views/datagrid/toolbar/visibility.js +++ b/src-docs/src/views/datagrid/toolbar/visibility.js @@ -46,6 +46,9 @@ const DataGrid = () => { const [showDisplaySelector, setShowDisplaySelector] = useState(true); const [allowDensity, setAllowDensity] = useState(true); const [allowRowHeight, setAllowRowHeight] = useState(true); + const [allowResetButton, setAllowResetButton] = useState(true); + const [additionalDisplaySettings, setAdditionalDisplaySettings] = + useState(false); const [showColumnSelector, setShowColumnSelector] = useState(true); const [allowHideColumns, setAllowHideColumns] = useState(true); const [allowOrderingColumns, setAllowOrderingColumns] = useState(true); @@ -76,6 +79,12 @@ const DataGrid = () => { const onAllowRowHeightChange = (optionId) => { setAllowRowHeight(optionId === 'true'); }; + const onAllowResetButtonChange = (optionId) => { + setAllowResetButton(optionId === 'true'); + }; + const onAdditionalDisplaySettingsChange = (optionId) => { + setAdditionalDisplaySettings(optionId === 'true'); + }; const onShowKeyboardShortcutsChange = (optionId) => { setShowKeyboardShortcuts(optionId === 'true'); @@ -125,13 +134,27 @@ const DataGrid = () => { const toggleDisplaySelector = useMemo(() => { if ( showDisplaySelector === true && - (allowDensity === false || allowRowHeight === false) + (allowDensity === false || + allowRowHeight === false || + allowResetButton === false || + additionalDisplaySettings) ) { - return { allowDensity, allowRowHeight }; + return { + allowDensity, + allowRowHeight, + allowResetButton, + additionalDisplaySettings, + }; } else { return showDisplaySelector; } - }, [showDisplaySelector, allowDensity, allowRowHeight]); + }, [ + showDisplaySelector, + allowDensity, + allowRowHeight, + allowResetButton, + additionalDisplaySettings, + ]); const createItem = (name, buttonProps = {}) => { return ( @@ -234,6 +257,18 @@ const DataGrid = () => { onChange: onAllowRowHeightChange, })} +
  • + {createItem('Show reset button', { + idSelected: allowResetButton.toString(), + onChange: onAllowResetButtonChange, + })} +
  • +
  • + {createItem('Additional display settings', { + idSelected: additionalDisplaySettings.toString(), + onChange: onAdditionalDisplaySettingsChange, + })} +
  • )} @@ -266,6 +301,8 @@ const DataGrid = () => { showFullScreenSelector={showFullScreenSelector} allowDensity={allowDensity} allowRowHeight={allowRowHeight} + allowResetButton={allowResetButton} + additionalDisplaySettings={additionalDisplaySettings} allowHideColumns={allowHideColumns} allowOrderingColumns={allowOrderingColumns} /> diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index b9eacc989f8..85841317fe0 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -556,7 +556,7 @@ exports[`EuiDataGrid rendering renders additional toolbar controls 1`] = ` aria-hidden="true" class="euiButtonIcon__icon" color="inherit" - data-euiicon-type="tableDensityNormal" + data-euiicon-type="controlsHorizontal" /> @@ -1037,7 +1037,7 @@ exports[`EuiDataGrid rendering renders control columns 1`] = ` aria-hidden="true" class="euiButtonIcon__icon" color="inherit" - data-euiicon-type="tableDensityNormal" + data-euiicon-type="controlsHorizontal" /> @@ -1841,7 +1841,7 @@ exports[`EuiDataGrid rendering renders custom column headers 1`] = ` aria-hidden="true" class="euiButtonIcon__icon" color="inherit" - data-euiicon-type="tableDensityNormal" + data-euiicon-type="controlsHorizontal" /> @@ -2321,7 +2321,7 @@ exports[`EuiDataGrid rendering renders with common and div attributes 1`] = ` aria-hidden="true" class="euiButtonIcon__icon" color="inherit" - data-euiicon-type="tableDensityNormal" + data-euiicon-type="controlsHorizontal" /> diff --git a/src/components/datagrid/controls/__snapshots__/display_selector.test.tsx.snap b/src/components/datagrid/controls/__snapshots__/display_selector.test.tsx.snap index feca107a869..d806098b349 100644 --- a/src/components/datagrid/controls/__snapshots__/display_selector.test.tsx.snap +++ b/src/components/datagrid/controls/__snapshots__/display_selector.test.tsx.snap @@ -16,7 +16,7 @@ exports[`useDataGridDisplaySelector displaySelector renders a toolbar button/pop className="euiDataGrid__controlBtn" color="text" data-test-subj="dataGridDisplaySelectorButton" - iconType="tableDensityNormal" + iconType="controlsHorizontal" onClick={[Function]} size="xs" /> diff --git a/src/components/datagrid/controls/data_grid_toolbar.test.tsx b/src/components/datagrid/controls/data_grid_toolbar.test.tsx index 2d7bfa9299f..6299871343e 100644 --- a/src/components/datagrid/controls/data_grid_toolbar.test.tsx +++ b/src/components/datagrid/controls/data_grid_toolbar.test.tsx @@ -135,6 +135,36 @@ describe('EuiDataGridToolbar', () => { `); }); + + it('renders custom content if renderCustomToolbar is defined', () => { + const mockRenderCustomToolbar = jest.fn(() => ( +
    Custom
    + )); + const component = shallow( + + ); + + expect(component).toMatchInlineSnapshot(` +
    + Custom +
    + `); + expect(mockRenderCustomToolbar).toHaveBeenCalledWith( + expect.objectContaining({ + hasRoomForGridControls: true, + fullScreenControl: requiredProps.fullScreenSelector, + keyboardShortcutsControl: requiredProps.keyboardShortcuts, + displayControl: requiredProps.displaySelector, + columnControl: requiredProps.columnSelector, + columnSortingControl: requiredProps.columnSorting, + }) + ); + }); }); describe('checkOrDefaultToolBarDisplayOptions', () => { diff --git a/src/components/datagrid/controls/data_grid_toolbar.tsx b/src/components/datagrid/controls/data_grid_toolbar.tsx index ec043048c61..f00722d1014 100644 --- a/src/components/datagrid/controls/data_grid_toolbar.tsx +++ b/src/components/datagrid/controls/data_grid_toolbar.tsx @@ -30,56 +30,74 @@ export const EuiDataGridToolbar = ({ displaySelector, columnSelector, columnSorting, + renderCustomToolbar, }: EuiDataGridToolbarProps) => { // Enables/disables grid controls based on available width const hasRoomForGridControls = IS_JEST_ENVIRONMENT ? true : gridWidth > minSizeForControls || isFullScreen; + const columnControl = checkOrDefaultToolBarDisplayOptions( + toolbarVisibility, + 'showColumnSelector' + ) + ? columnSelector + : null; + const columnSortingControl = checkOrDefaultToolBarDisplayOptions( + toolbarVisibility, + 'showSortSelector' + ) + ? columnSorting + : null; + const keyboardShortcutsControl = checkOrDefaultToolBarDisplayOptions( + toolbarVisibility, + 'showKeyboardShortcuts' + ) ? ( + keyboardShortcuts + ) : ( + + {keyboardShortcuts} + + ); + const displayControl = checkOrDefaultToolBarDisplayOptions( + toolbarVisibility, + 'showDisplaySelector' + ) + ? displaySelector + : null; + const fullScreenControl = checkOrDefaultToolBarDisplayOptions( + toolbarVisibility, + 'showFullScreenSelector' + ) + ? fullScreenSelector + : null; + + if (renderCustomToolbar) { + return renderCustomToolbar({ + hasRoomForGridControls, + columnControl, + columnSortingControl, + keyboardShortcutsControl, + displayControl, + fullScreenControl, + }); + } + return (
    {hasRoomForGridControls && (
    {renderAdditionalControls(toolbarVisibility, 'left.prepend')} - {checkOrDefaultToolBarDisplayOptions( - toolbarVisibility, - 'showColumnSelector' - ) - ? columnSelector - : null} - {checkOrDefaultToolBarDisplayOptions( - toolbarVisibility, - 'showSortSelector' - ) - ? columnSorting - : null} + {columnControl} + {columnSortingControl} {renderAdditionalControls(toolbarVisibility, 'left.append')}
    )}
    {renderAdditionalControls(toolbarVisibility, 'right')} - {checkOrDefaultToolBarDisplayOptions( - toolbarVisibility, - 'showKeyboardShortcuts' - ) ? ( - keyboardShortcuts - ) : ( - - {keyboardShortcuts} - - )} - {checkOrDefaultToolBarDisplayOptions( - toolbarVisibility, - 'showDisplaySelector' - ) - ? displaySelector - : null} - {checkOrDefaultToolBarDisplayOptions( - toolbarVisibility, - 'showFullScreenSelector' - ) - ? fullScreenSelector - : null} + {keyboardShortcutsControl} + {displayControl} + {fullScreenControl}
    ); diff --git a/src/components/datagrid/controls/display_selector.test.tsx b/src/components/datagrid/controls/display_selector.test.tsx index dbb3629c83e..40dd9910e5c 100644 --- a/src/components/datagrid/controls/display_selector.test.tsx +++ b/src/components/datagrid/controls/display_selector.test.tsx @@ -84,15 +84,6 @@ describe('useDataGridDisplaySelector', () => { expect(getSelection(component)).toEqual('normal'); component.find('[data-test-subj="compact"]').simulate('change'); expect(getSelection(component)).toEqual('compact'); - - // Should have changed the main toolbar icon accordingly - closePopover(component); - expect( - component - .find('[data-test-subj="dataGridDisplaySelectorButton"]') - .first() - .prop('iconType') - ).toEqual('tableDensityCompact'); }); it('calls the gridStyles.onDensityChange callback on user change', () => { @@ -409,26 +400,86 @@ describe('useDataGridDisplaySelector', () => { }); }); - it('renders a reset button only when the user changes from the current settings', () => { - const component = mount(); - openPopover(component); - expect( - component.find('[data-test-subj="resetDisplaySelector"]').exists() - ).toBe(false); + describe('reset button', () => { + it('renders a reset button only when the user changes from the current settings', () => { + const component = mount(); + openPopover(component); + expect( + component.find('[data-test-subj="resetDisplaySelector"]').exists() + ).toBe(false); - component.find('[data-test-subj="expanded"]').simulate('change'); - component.find('[data-test-subj="auto"]').simulate('change'); - expect( - component.find('[data-test-subj="resetDisplaySelector"]').exists() - ).toBe(true); + component.find('[data-test-subj="expanded"]').simulate('change'); + component.find('[data-test-subj="auto"]').simulate('change'); + expect( + component.find('[data-test-subj="resetDisplaySelector"]').exists() + ).toBe(true); - // 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); + // Should show the reset button again after the popover was reopened + closePopover(component); + openPopover(component); + expect( + component.find('[data-test-subj="resetDisplaySelector"]').exists() + ).toBe(true); + + // 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); + }); + + it('hides the reset button even after changes if allowResetButton is false', () => { + const component = mount( + + ); + openPopover(component); + expect( + component.find('[data-test-subj="resetDisplaySelector"]').exists() + ).toBe(false); + + component.find('[data-test-subj="expanded"]').simulate('change'); + component.find('[data-test-subj="auto"]').simulate('change'); + expect( + component.find('[data-test-subj="resetDisplaySelector"]').exists() + ).toBe(false); + + // Should hide the reset button again after the popover was reopened + closePopover(component); + openPopover(component); + expect( + component.find('[data-test-subj="resetDisplaySelector"]').exists() + ).toBe(false); + }); + }); + + describe('additionalDisplaySettings', () => { + it('renders custom content if additionalDisplaySettings is defined', () => { + const component = mount( + Custom content + ), + }} + /> + ); + openPopover(component); + expect(component.find('[data-test-subj="test-custom"]')) + .toMatchInlineSnapshot(` +
    + Custom content +
    + `); + }); }); }); diff --git a/src/components/datagrid/controls/display_selector.tsx b/src/components/datagrid/controls/display_selector.tsx index b89276b6e53..4504d61159f 100644 --- a/src/components/datagrid/controls/display_selector.tsx +++ b/src/components/datagrid/controls/display_selector.tsx @@ -66,9 +66,6 @@ const convertGridStylesToSelection = (gridStyles: EuiDataGridStyle) => { return ''; }; -// Used to correctly format the icon name for the grid density icon -const capitalizeDensityString = (s: string) => s[0].toUpperCase() + s.slice(1); - // Row height options and utilities const rowHeightButtonOptions: string[] = ['undefined', 'auto', 'lineCount']; const convertRowHeightsOptionsToSelection = ( @@ -108,6 +105,16 @@ export const useDataGridDisplaySelector = ( 'allowRowHeight' ); + const allowResetButton = getNestedObjectOptions( + showDisplaySelector, + 'allowResetButton' + ); + + const additionalDisplaySettings = + typeof showDisplaySelector === 'boolean' + ? null + : showDisplaySelector?.additionalDisplaySettings ?? null; + // Track styles specified by the user at run time const [userGridStyles, setUserGridStyles] = useState({}); const [userRowHeightsOptions, setUserRowHeightsOptions] = useState({}); @@ -185,20 +192,24 @@ export const useDataGridDisplaySelector = ( const [showResetButton, setShowResetButton] = useState(false); useUpdateEffect(() => { - const hasUserChanges = Object.keys(userGridStyles).length > 0; - if (hasUserChanges) setShowResetButton(true); + if (allowResetButton) { + const hasUserChanges = Object.keys(userGridStyles).length > 0; + if (hasUserChanges) setShowResetButton(true); + } const { onChange, ...currentGridStyles } = gridStyles; initialStyles?.onChange?.(currentGridStyles); - }, [userGridStyles]); + }, [userGridStyles, allowResetButton]); useUpdateEffect(() => { - const hasUserChanges = Object.keys(userRowHeightsOptions).length > 0; - if (hasUserChanges) setShowResetButton(true); + if (allowResetButton) { + const hasUserChanges = Object.keys(userRowHeightsOptions).length > 0; + if (hasUserChanges) setShowResetButton(true); + } const { onChange, ...currentRowHeightsOptions } = rowHeightsOptions; initialRowHeightsOptions?.onChange?.(currentRowHeightsOptions); - }, [userRowHeightsOptions]); + }, [userRowHeightsOptions, allowResetButton]); // Allow resetting to initial developer-specified configurations const resetToInitialState = useCallback(() => { @@ -217,7 +228,9 @@ export const useDataGridDisplaySelector = ( ); const displaySelector = - showDensityControls || showRowHeightControls ? ( + showDensityControls || + showRowHeightControls || + additionalDisplaySettings ? ( )} + {additionalDisplaySettings} {showResetButton && ( diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 92b190a7565..063daebfbf2 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -129,6 +129,7 @@ export const EuiDataGrid = forwardRef( rowHeightsOptions: _rowHeightsOptions, virtualizationOptions, renderCustomGridBody, + renderCustomToolbar, ...rest } = props; @@ -400,6 +401,7 @@ export const EuiDataGrid = forwardRef( displaySelector={displaySelector} columnSelector={columnSelector} columnSorting={columnSorting} + renderCustomToolbar={renderCustomToolbar} /> )} {inMemory ? ( diff --git a/src/components/datagrid/data_grid_types.ts b/src/components/datagrid/data_grid_types.ts index 7bdf0d5ba8b..3935a7c47b8 100644 --- a/src/components/datagrid/data_grid_types.ts +++ b/src/components/datagrid/data_grid_types.ts @@ -47,6 +47,19 @@ export interface EuiDataGridToolbarProps { displaySelector: ReactNode; columnSelector: ReactNode; columnSorting: ReactNode; + renderCustomToolbar?: (props: EuiDataGridCustomToolbarProps) => ReactElement; +} + +/** + * Props which are available for a custom toolbar rendering + */ +export interface EuiDataGridCustomToolbarProps { + hasRoomForGridControls: boolean; + fullScreenControl: ReactNode; + keyboardShortcutsControl: ReactNode; + displayControl: ReactNode; + columnControl: ReactNode; + columnSortingControl: ReactNode; } export interface EuiDataGridPaginationRendererProps @@ -280,6 +293,15 @@ export type CommonGridProps = CommonProps & * It receives #EuiDataGridCustomBodyProps as its only argument. */ renderCustomGridBody?: (args: EuiDataGridCustomBodyProps) => ReactNode; + /** + * An optional function called to customize placement of controls in EuiDataGrid's toolbar. + * This can be used to add custom buttons or reorder existing ones. + * + * Behind the scenes, this function is treated as a React component, + * allowing hooks, context, and other React concepts to be used. + * It receives #EuiDataGridCustomToolbarProps as its only argument. + */ + renderCustomToolbar?: EuiDataGridToolbarProps['renderCustomToolbar']; /** * Defines the initial style of the grid. Accepts a partial #EuiDataGridStyle object. * Settings provided may be overwritten or merged with user defined preferences if `toolbarVisibility.showDisplaySelector.allowDensity = true` (which is the default). @@ -482,6 +504,7 @@ export interface EuiDataGridCustomBodyProps { */ setCustomGridBodyProps: (props: EuiDataGridSetCustomGridBodyProps) => void; } + export type EuiDataGridSetCustomGridBodyProps = CommonProps & HTMLAttributes & { ref?: MutableRefObject | Ref; @@ -846,6 +869,14 @@ export interface EuiDataGridToolBarVisibilityDisplaySelectorOptions { * When `false`, removes the ability to change row height display through the UI */ allowRowHeight?: boolean; + /** + * When `false`, removes the ability to reset styles to default through the UI + */ + allowResetButton?: boolean; + /** + * Allows appending additional content to the bottom of the display settings popover + */ + additionalDisplaySettings?: ReactNode; } export interface EuiDataGridToolBarVisibilityOptions { diff --git a/upcoming_changelogs/7190.md b/upcoming_changelogs/7190.md new file mode 100644 index 00000000000..2df5c499765 --- /dev/null +++ b/upcoming_changelogs/7190.md @@ -0,0 +1,4 @@ +- Added a new `renderCustomToolbar` prop to `EuiDataGrid`, which allows custom rendering of the toolbar. +- Added a new `allowResetButton` prop to `toolbarVisibility.showDisplaySelector` of `EuiDataGrid`, which allows hiding the "Reset to default" button from the display settings popover. +- Added a new `additionalDisplaySettings` prop to `toolbarVisibility.showDisplaySelector` of `EuiDataGrid`, which allows rendering extra settings inside the display settings popover. +- Updated `EuiDataGrid`'s toolbar display settings button icon