From c03ab19139112410944eca5c889af0b3aaa11db1 Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Fri, 18 Sep 2020 12:24:30 -0700 Subject: [PATCH] [DATA GRID] Add Column header menu (#3087) Co-authored-by: Chandler Prall Co-authored-by: Matthias Wilhelm Co-authored-by: Andrea Del Rio --- CHANGELOG.md | 1 + src-docs/src/views/datagrid/column_actions.js | 107 +++ src-docs/src/views/datagrid/datagrid.js | 31 +- .../src/views/datagrid/datagrid_example.js | 12 +- .../views/datagrid/datagrid_schema_example.js | 2 + .../datagrid/datagrid_styling_example.js | 55 +- src-docs/src/views/datagrid/focus.js | 6 + .../__snapshots__/data_grid.test.tsx.snap | 847 +++++++++++++++++- .../datagrid/_data_grid_data_row.scss | 5 +- .../datagrid/_data_grid_header_row.scss | 32 +- src/components/datagrid/_mixins.scss | 7 + src/components/datagrid/column_actions.tsx | 218 +++++ src/components/datagrid/column_selector.tsx | 44 +- src/components/datagrid/data_grid.test.tsx | 91 +- src/components/datagrid/data_grid.tsx | 17 +- .../datagrid/data_grid_header_cell.tsx | 67 +- .../datagrid/data_grid_header_row.tsx | 7 + src/components/datagrid/data_grid_types.ts | 32 + 18 files changed, 1500 insertions(+), 81 deletions(-) create mode 100644 src-docs/src/views/datagrid/column_actions.js create mode 100644 src/components/datagrid/column_actions.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index f535749b294..4eb568a2e00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## [`master`](https://github.com/elastic/eui/tree/master) - Added footer row to `EuiDataGrid` via the `renderFooterCellValue` prop ([#3770](https://github.com/elastic/eui/pull/3770)) +- Added column header menu to `EuiDataGrid` ([#3087](https://github.com/elastic/eui/pull/3087)) ## [`29.0.0`](https://github.com/elastic/eui/tree/v29.0.0) diff --git a/src-docs/src/views/datagrid/column_actions.js b/src-docs/src/views/datagrid/column_actions.js new file mode 100644 index 00000000000..a5143dbf38d --- /dev/null +++ b/src-docs/src/views/datagrid/column_actions.js @@ -0,0 +1,107 @@ +import React, { useState, useCallback } from 'react'; +import { fake } from 'faker'; + +import { EuiDataGrid, EuiAvatar } from '../../../../src/components/'; + +const columns = [ + { + id: 'avatar', + initialWidth: 40, + isResizable: false, + actions: false, + }, + { + id: 'name', + initialWidth: 100, + isSortable: true, + actions: { + showHide: false, + }, + }, + { + id: 'email', + isSortable: true, + actions: { + additional: [ + { + iconType: 'heart', + label: 'Send Email', + size: 'xs', + onClick: () => + alert('🎵Return to sender, address unknown, no such number,...'), + }, + ], + }, + }, + { + id: 'city', + isSortable: true, + actions: { + showHide: { + iconType: 'cross', + label: 'Remove column', + }, + }, + }, + { + id: 'country', + }, + { + id: 'account', + }, +]; + +const data = []; + +for (let i = 1; i < 5; i++) { + data.push({ + avatar: ( + + ), + name: fake('{{name.lastName}}, {{name.firstName}} {{name.suffix}}'), + email: fake('{{internet.email}}'), + city: fake('{{address.city}}'), + country: fake('{{address.country}}'), + account: fake('{{finance.account}}'), + }); +} + +export default () => { + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + + const [visibleColumns, setVisibleColumns] = useState(() => + columns.map(({ id }) => id) + ); + + const setPageIndex = useCallback( + pageIndex => setPagination({ ...pagination, pageIndex }), + [pagination, setPagination] + ); + const setPageSize = useCallback( + pageSize => setPagination({ ...pagination, pageSize, pageIndex: 0 }), + [pagination, setPagination] + ); + + return ( + data[rowIndex][columnId]} + pagination={{ + ...pagination, + pageSizeOptions: [5, 10, 25], + onChangeItemsPerPage: setPageSize, + onChangePage: setPageIndex, + }} + /> + ); +}; diff --git a/src-docs/src/views/datagrid/datagrid.js b/src-docs/src/views/datagrid/datagrid.js index 7064f0fa65e..1322ccd7f11 100644 --- a/src-docs/src/views/datagrid/datagrid.js +++ b/src-docs/src/views/datagrid/datagrid.js @@ -26,28 +26,26 @@ const columns = [ }, { id: 'email', - display: ( - // This is an example of an icon next to a title that still respects text truncate - - -
email
-
- - alert('Email Icon Clicked!')} - /> - -
- ), }, { id: 'location', }, { id: 'account', + actions: { + showHide: { label: 'Custom hide label' }, + showMoveLeft: false, + showMoveRight: false, + additional: [ + { + label: 'Custom action', + onClick: () => alert('🎉'), + iconType: 'cheer', + size: 'xs', + color: 'text', + }, + ], + }, }, { id: 'date', @@ -65,6 +63,7 @@ const columns = [ defaultSortDirection: 'desc', initialWidth: 65, isResizable: false, + actions: false, }, ]; diff --git a/src-docs/src/views/datagrid/datagrid_example.js b/src-docs/src/views/datagrid/datagrid_example.js index b477cd1f922..51afb6ee3d5 100644 --- a/src-docs/src/views/datagrid/datagrid_example.js +++ b/src-docs/src/views/datagrid/datagrid_example.js @@ -26,6 +26,7 @@ import { EuiDataGridStyle, EuiDataGridToolBarVisibilityOptions, EuiDataGridColumnVisibility, + EuiDataGridColumnActions, EuiDataGridPopoverContentProps, EuiDataGridControlColumn, EuiDataGridToolBarVisibilityColumnSelectorOptions, @@ -41,9 +42,13 @@ const gridSnippet = ` // Required. There are 200 total records. rowCount={200} // Required. Sets up three columns, the last of which has a custom schema we later define down below. - // The second column B won't allow clicking in to see the content in a popup. - // The first column defines a starting width of 150px and prevents the user from resizing it - columns={[{ id: 'A', initialWidth: 150, isResizable: false }, { id: 'B', isExpandable: false }, {id: 'C', schema: 'franchise'}]} + // The second column B won't allow clicking in to see the content in a popup and doesn't show move actions in column header cell + // The first column defines a starting width of 150px, prevents the user from resizing it and no actions are displayed + columns={[ + { id: 'A', initialWidth: 150, isResizable: false, actions: false }, + { id: 'B', isExpandable: false, actions: { showMoveLeft: false, showMoveRight: false } }, + { id: 'C', schema: 'franchise'} + ]} // Optional. This allows you to initially hide columns. Users can still turn them on. columnVisibility={{ visibleColumns: ['A', 'C'], @@ -354,6 +359,7 @@ export const DataGridExample = { EuiDataGrid, EuiDataGridColumn, EuiDataGridColumnVisibility, + EuiDataGridColumnActions, EuiDataGridControlColumn, EuiDataGridInMemory, EuiDataGridPaginationProps, diff --git a/src-docs/src/views/datagrid/datagrid_schema_example.js b/src-docs/src/views/datagrid/datagrid_schema_example.js index 250c816c006..a0f7328beaa 100644 --- a/src-docs/src/views/datagrid/datagrid_schema_example.js +++ b/src-docs/src/views/datagrid/datagrid_schema_example.js @@ -11,6 +11,7 @@ const dataGridSchemaHtml = renderToHtml(DataGridSchema); import { EuiDataGridColumn, + EuiDataGridColumnActions, EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridInMemory, @@ -89,6 +90,7 @@ export const DataGridSchemaExample = { EuiDataGrid, EuiDataGridInMemory, EuiDataGridColumn, + EuiDataGridColumnActions, EuiDataGridColumnVisibility, EuiDataGridPaginationProps, EuiDataGridSorting, diff --git a/src-docs/src/views/datagrid/datagrid_styling_example.js b/src-docs/src/views/datagrid/datagrid_styling_example.js index 905bf1290b4..3388a84a236 100644 --- a/src-docs/src/views/datagrid/datagrid_styling_example.js +++ b/src-docs/src/views/datagrid/datagrid_styling_example.js @@ -3,7 +3,12 @@ import React, { Fragment } from 'react'; import { renderToHtml } from '../../services'; import { GuideSectionTypes } from '../../components'; -import { EuiDataGrid, EuiCode, EuiCodeBlock } from '../../../../src/components'; +import { + EuiDataGrid, + EuiCode, + EuiCodeBlock, + EuiListGroupItem, +} from '../../../../src/components'; import DataGridContainer from './container'; const dataGridContainerSource = require('!!raw-loader!./container'); @@ -18,11 +23,15 @@ const dataGridControlsSource = require('!!raw-loader!./additional_controls'); const dataGridControlsHtml = renderToHtml(DataGridControls); import DataGridColumnWidths from './column_widths'; +import DataGridColumnActions from './column_actions'; const dataGridColumnWidthsSource = require('!!raw-loader!./column_widths'); const dataGridColumnWidthsHtml = renderToHtml(DataGridColumnWidths); +const dataGridColumnActionsSource = require('!!raw-loader!./column_actions'); +const dataGridColumnActionsHtml = renderToHtml(DataGridColumnActions); import { EuiDataGridColumn, + EuiDataGridColumnActions, EuiDataGridStyle, EuiDataGridToolBarVisibilityOptions, } from '!!prop-loader!../../../../src/components/datagrid/data_grid_types'; @@ -123,7 +132,7 @@ const widthsSnippet = `, }, + { + source: [ + { + type: GuideSectionTypes.JS, + code: dataGridColumnActionsSource, + }, + { + type: GuideSectionTypes.HTML, + code: dataGridColumnActionsHtml, + }, + ], + title: 'Column actions', + text: ( + +

+ By default, columns provide actions for sorting, moving and hiding. + These can be extended with custom actions. You can customize the + actions by setting the actions value of{' '} + EuiDataGridColumn. Setting it to{' '} + false removes the action menu displayed. You can + configure it by passing an object of type{' '} + EuiDataGridColumnAction. This allows you a hide, + configure the existing actions and add new ones. +

+

+ Below, the first column provides no action, the second doesn't + provide the ability to hide the columns, the 3rd provides an + additional action, the 4th overwrites the hide action with a custom + label and icon. +

+
+ ), + components: { DataGridColumnActions }, + snippet: widthsSnippet, + props: { + EuiDataGrid, + EuiDataGridColumn, + EuiDataGridColumnActions, + EuiListGroupItem, + }, + demo: , + }, ], }; diff --git a/src-docs/src/views/datagrid/focus.js b/src-docs/src/views/datagrid/focus.js index 2d81953a405..79932b66ee7 100644 --- a/src-docs/src/views/datagrid/focus.js +++ b/src-docs/src/views/datagrid/focus.js @@ -97,6 +97,7 @@ export default () => { ), isExpandable: false, + actions: false, }, { id: 'no-interactives is expandable', @@ -115,6 +116,7 @@ export default () => { ), + actions: false, }, { id: 'one-interactive not expandable', @@ -134,6 +136,7 @@ export default () => { ), isExpandable: false, + actions: false, }, { id: 'one-interactives is expandable', @@ -153,6 +156,7 @@ export default () => { ), + actions: false, }, { id: 'two-interactives not expandable', @@ -173,6 +177,7 @@ export default () => { ), isExpandable: false, + actions: false, }, { id: 'two-interactives is expandable', @@ -192,6 +197,7 @@ export default () => { ), + actions: false, }, ], [areHeadersInteractive] diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index 8cc73e1cd98..081603dffd0 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -164,6 +164,643 @@ exports[`EuiDataGrid pagination renders 1`] = ` `; +exports[`EuiDataGrid render column actions renders various column actions configurations 1`] = ` +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    +
+`; + +exports[`EuiDataGrid render column actions renders various column actions configurations 2`] = ` +
    + +
  • + +
  • +
    +
+`; + +exports[`EuiDataGrid render column actions renders various column actions configurations 3`] = ` +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    + + } + onClick={[Function]} + showToolTip={false} + size="xs" + wrapText={false} + > +
  • + +
  • +
    + +
  • + +
  • +
    +
+`; + +exports[`EuiDataGrid render column actions renders various column actions configurations 4`] = ` +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    +
+`; + exports[`EuiDataGrid rendering renders additional toolbar controls 1`] = ` Array [
-
- A -
+
+ A +
+
+
+
+
+
+
-
- B -
+
+ B +
+
+
+
+
+
+
-
- A -
+
+ A +
+
+
+
+
+
+
-
- B -
+
+ B +
+
+
+
+
+
+
-
- Column A -
+
+ Column A +
+
+
+
+
+
+
-
-
- More Elements +
+
+ More Elements +
-
+
+
+
+
+
+
-
- A -
+
+ A +
+
+
+
+
+
+
-
- B -
+
+ B +
+
+
+
+
+
+
void, + setIsPopoverOpen: (value: boolean) => void, + sorting: EuiDataGridSorting | undefined, + switchColumnPos: (colFromId: string, colToId: string) => void +) { + if (column.actions === false) { + return []; + } + const colIdx = columns.findIndex(col => col.id === column.id); + + const sortingIdx = sorting + ? sorting.columns.findIndex(col => col.id === column.id) + : -1; + const sortBy = (direction: 'asc' | 'desc' = 'asc') => { + if (!sorting) { + return; + } + + if ( + sortingIdx >= 0 && + sorting.columns[sortingIdx]?.direction === direction + ) { + // unsort if the same current and new direction are same + const newColumns = sorting.columns.filter( + (val, idx) => idx !== sortingIdx + ); + sorting.onSort(newColumns); + } else if (sortingIdx >= 0) { + // replace existing sort + const newColumns = Object.values({ + ...sorting.columns, + [sortingIdx]: { + id: column.id, + direction: direction, + }, + }); + sorting.onSort(newColumns as EuiDataGridSorting['columns']); + } else { + // add new sort + const newColumns = [ + ...sorting.columns, + { + id: column.id, + direction: direction, + }, + ]; + sorting.onSort(newColumns as EuiDataGridSorting['columns']); + } + }; + const onClickHideColumn = () => + setVisibleColumns( + columns.filter(col => col.id !== column.id).map(col => col.id) + ); + + const onClickSortAsc = () => { + sortBy('asc'); + }; + + const onClickSortDesc = () => { + sortBy('desc'); + }; + + const onClickMoveLeft = () => { + const targetCol = columns[colIdx - 1]; + if (targetCol) { + switchColumnPos(column.id, targetCol.id); + } + }; + + const onClickMoveRight = () => { + const targetCol = columns[colIdx + 1]; + if (targetCol) { + switchColumnPos(column.id, targetCol.id); + } + }; + + const result: EuiListGroupItemProps[] = []; + if (column.actions?.showHide !== false) { + const option = { + label: ( + + ), + onClick: onClickHideColumn, + iconType: 'eyeClosed', + size: 'xs', + color: 'text', + } as EuiListGroupItemProps; + if (typeof column.actions?.showHide === 'object') { + result.push({ ...option, ...column.actions.showHide }); + } else { + result.push(option); + } + } + + if (column.actions?.showSortAsc !== false && sorting) { + const option = { + label: ( + + ), + onClick: onClickSortAsc, + isDisabled: column.isSortable === false, + className: + sortingIdx >= 0 && sorting.columns[sortingIdx].direction === 'asc' + ? 'euiDataGridHeader__action--selected' + : '', + iconType: 'sortUp', + size: 'xs', + color: 'text', + } as EuiListGroupItemProps; + if (typeof column.actions?.showSortAsc === 'object') { + result.push({ ...option, ...column.actions.showSortAsc }); + } else { + result.push(option); + } + } + + if (column.actions?.showSortDesc !== false && sorting) { + const option = { + label: ( + + ), + onClick: onClickSortDesc, + isDisabled: column.isSortable === false, + className: + sortingIdx >= 0 && sorting.columns[sortingIdx].direction === 'desc' + ? 'euiDataGridHeader__action--selected' + : '', + iconType: 'sortDown', + size: 'xs', + color: 'text', + } as EuiListGroupItemProps; + if (typeof column.actions?.showSortDesc === 'object') { + result.push({ ...option, ...column.actions.showSortDesc }); + } else { + result.push(option); + } + } + + if (column.actions?.showMoveLeft !== false) { + const option = { + label: , + iconType: 'sortLeft', + size: 'xs', + color: 'text', + onClick: onClickMoveLeft, + isDisabled: colIdx === 0, + } as EuiListGroupItemProps; + if (typeof column.actions?.showMoveLeft === 'object') { + result.push({ ...option, ...column.actions.showMoveLeft }); + } else { + result.push(option); + } + } + + if (column.actions?.showMoveRight !== false) { + const option = { + label: ( + + ), + iconType: 'sortRight', + size: 'xs', + color: 'text', + onClick: onClickMoveRight, + isDisabled: colIdx === columns.length - 1, + } as EuiListGroupItemProps; + if (typeof column.actions?.showMoveRight === 'object') { + result.push({ ...option, ...column.actions.showMoveRight }); + } else { + result.push(option); + } + } + const allActions = column.actions?.additional + ? [...result, ...column.actions?.additional] + : result; + + //wrap EuiListGroupItem onClick function to close the popover and prevet bubbling up + + return allActions.map(action => { + return { + ...action, + ...{ + onClick: (ev: React.MouseEvent) => { + ev.stopPropagation(); + setIsPopoverOpen(false); + if (action && action.onClick) { + action.onClick(ev); + } + }, + }, + }; + }) as EuiListGroupItemProps[]; +} diff --git a/src/components/datagrid/column_selector.tsx b/src/components/datagrid/column_selector.tsx index 311b346a1ce..b282e31169c 100644 --- a/src/components/datagrid/column_selector.tsx +++ b/src/components/datagrid/column_selector.tsx @@ -61,7 +61,12 @@ export const useColumnSelector = ( columnVisibility: EuiDataGridColumnVisibility, showColumnSelector: EuiDataGridToolBarVisibilityOptions['showColumnSelector'], displayValues: { [key: string]: string } -): [ReactElement, EuiDataGridColumn[]] => { +): [ + ReactElement, + EuiDataGridColumn[], + (columns: string[]) => void, + (colFrom: string, colTo: string) => void +] => { const allowColumnHiding = getShowColumnSelectorValue( showColumnSelector, 'allowHide' @@ -81,6 +86,15 @@ export const useColumnSelector = ( const [isOpen, setIsOpen] = useState(false); + function setColumns(nextColumns: string[]) { + setSortedColumns(nextColumns); + + const nextVisibleColumns = nextColumns.filter(id => + visibleColumnIds.has(id) + ); + setVisibleColumns(nextVisibleColumns); + } + function onDragEnd({ source: { index: sourceIndex }, destination, @@ -91,12 +105,7 @@ export const useColumnSelector = ( sourceIndex, destinationIndex ); - setSortedColumns(nextSortedColumns); - - const nextVisibleColumns = nextSortedColumns.filter(id => - visibleColumnIds.has(id) - ); - setVisibleColumns(nextVisibleColumns); + setColumns(nextSortedColumns); } const numberOfHiddenFields = availableColumns.length - visibleColumns.length; @@ -279,6 +288,25 @@ export const useColumnSelector = ( .filter(column => column != null), [availableColumns, visibleColumns] ); + /** + * Used for moving columns left/right, available in the headers actions menu + */ + const switchColumnPos = (fromColId: string, toColId: string) => { + const moveFromIdx = sortedColumns.indexOf(fromColId); + const moveToIdx = sortedColumns.indexOf(toColId); + if (moveFromIdx === -1 || moveToIdx === -1) { + return; + } + const nextSortedColumns = [...sortedColumns]; + nextSortedColumns.splice(moveFromIdx, 1); + nextSortedColumns.splice(moveToIdx, 0, fromColId); + setColumns(nextSortedColumns); + }; - return [columnSelector, orderedVisibleColumns]; + return [ + columnSelector, + orderedVisibleColumns, + setVisibleColumns, + switchColumnPos, + ]; }; diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 0c20f7ddbd8..779a2af3fba 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -1813,6 +1813,86 @@ Array [ }); }); + describe('render column actions', () => { + it('renders various column actions configurations', () => { + const component = mount( + {}, + }} + columns={[ + { id: 'A', actions: false }, + { id: 'B', isSortable: true }, + { + id: 'C', + isSortable: true, + actions: { + showHide: false, + showMoveRight: false, + showMoveLeft: false, + showSortAsc: false, + showSortDesc: false, + additional: [{ label: 'test' }], + }, + }, + { + id: 'D', + isSortable: true, + actions: { + showHide: false, + showMoveRight: false, + showMoveLeft: false, + additional: [{ label: 'test' }], + }, + }, + { + id: 'E', + isSortable: true, + actions: { + showHide: { label: '1' }, + showSortAsc: { label: '2' }, + showSortDesc: { label: '3' }, + showMoveLeft: { label: '4' }, + showMoveRight: { label: '5' }, + additional: [{ label: 'test' }], + }, + }, + ]} + columnVisibility={{ + visibleColumns: ['A', 'B', 'C', 'D', 'E'], + setVisibleColumns: () => {}, + }} + rowCount={2} + renderCellValue={({ rowIndex, columnId }) => + `${rowIndex}-${columnId}` + } + /> + ); + + const buttonA = findTestSubject( + component, + 'dataGridHeaderCellActionButton-A' + ); + expect(buttonA.length).toBe(0); + + for (const col of ['B', 'C', 'D', 'E']) { + const button = findTestSubject( + component, + `dataGridHeaderCellActionButton-${col}` + ); + button.simulate('click'); + component.update(); + const actionGroup = findTestSubject( + component, + `dataGridHeaderCellActionGroup-${col}` + ); + expect(actionGroup).toMatchSnapshot(); + } + }); + }); + describe('keyboard controls', () => { it('supports simple arrow navigation', () => { let pagination = { @@ -1832,7 +1912,11 @@ Array [ const component = mount( {}, @@ -2028,7 +2112,10 @@ Array [ const component = mount( {}, diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index dabed7ee193..d51b72c6fba 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -65,7 +65,10 @@ import { useColumnSelector } from './column_selector'; import { useStyleSelector, startingStyles } from './style_selector'; import { EuiTablePagination } from '../table/table_pagination'; import { EuiFocusTrap } from '../focus_trap'; -import { EuiResizeObserver } from '../observer/resize_observer'; +import { + EuiResizeObserver, + useResizeObserver, +} from '../observer/resize_observer'; import { EuiDataGridInMemoryRenderer } from './data_grid_inmemory_renderer'; import { useMergedSchema, @@ -76,7 +79,6 @@ import { import { useColumnSorting } from './column_sorting'; import { EuiMutationObserver } from '../observer/mutation_observer'; import { DataGridContext } from './data_grid_context'; -import { useResizeObserver } from '../observer/resize_observer/resize_observer'; // Used to short-circuit some async browser behaviour that is difficult to account for in tests const IS_JEST_ENVIRONMENT = global.hasOwnProperty('_isJest'); @@ -762,7 +764,12 @@ export const EuiDataGrid: FunctionComponent = props => { {} ); - const [columnSelector, orderedVisibleColumns] = useColumnSelector( + const [ + columnSelector, + orderedVisibleColumns, + setVisibleColumns, + switchColumnPos, + ] = useColumnSelector( columns, columnVisibility, checkOrDefaultToolBarDiplayOptions(toolbarVisibility, 'showColumnSelector'), @@ -1056,6 +1063,10 @@ export const EuiDataGrid: FunctionComponent = props => { ref={ref} {...commonGridProps} setColumnWidth={setColumnWidth} + setVisibleColumns={setVisibleColumns} + switchColumnPos={switchColumnPos} + schema={mergedSchema} + sorting={sorting} headerIsInteractive={ headerIsInteractive } diff --git a/src/components/datagrid/data_grid_header_cell.tsx b/src/components/datagrid/data_grid_header_cell.tsx index 5780eff7f3a..5087c666df9 100644 --- a/src/components/datagrid/data_grid_header_cell.tsx +++ b/src/components/datagrid/data_grid_header_cell.tsx @@ -31,14 +31,19 @@ import classnames from 'classnames'; import { EuiDataGridHeaderRowPropsSpecificProps } from './data_grid_header_row'; import { keys } from '../../services'; import { EuiDataGridColumnResizer } from './data_grid_column_resizer'; +import { EuiPopover } from '../popover'; +import { EuiListGroup } from '../list_group'; import { EuiScreenReaderOnly } from '../accessibility'; import tabbable from 'tabbable'; import { EuiDataGridColumn } from './data_grid_types'; +import { getColumnActions } from './column_actions'; +import { useEuiI18n } from '../i18n'; +import { EuiIcon } from '../icon'; export interface EuiDataGridHeaderCellProps extends Omit< EuiDataGridHeaderRowPropsSpecificProps, - 'columns' | 'leadingControlColumns' + 'leadingControlColumns' > { column: EuiDataGridColumn; index: number; @@ -49,10 +54,13 @@ export const EuiDataGridHeaderCell: FunctionComponent id)); @@ -89,7 +101,6 @@ export const EuiDataGridHeaderCell: FunctionComponent `Sorted by ${col.id} ${col.direction}`) .join(' then '); - screenReaderId = htmlIdGenerator()(); ariaProps['aria-describedby'] = screenReaderId; } } @@ -108,6 +119,7 @@ export const EuiDataGridHeaderCell: FunctionComponent { if (headerRef.current) { @@ -167,7 +179,7 @@ export const EuiDataGridHeaderCell: FunctionComponent headerRef.current!.blur()); e.preventDefault(); @@ -199,7 +211,7 @@ export const EuiDataGridHeaderCell: FunctionComponent { if (headerRef.current) { - if (headerRef.current.contains(document.activeElement) === false) { + if (!headerRef.current.contains(document.activeElement)) { setIsCellEntered(false); } } @@ -257,6 +269,17 @@ export const EuiDataGridHeaderCell: FunctionComponent 0; + return (
) : null} -
{display || id}
{sorting && sorting.columns.length >= 2 && (
{sortString}
)} + {!showColumnActions ? ( +
{display || id}
+ ) : ( + + )}
); }; diff --git a/src/components/datagrid/data_grid_header_row.tsx b/src/components/datagrid/data_grid_header_row.tsx index 81048f32a27..1c51dc99bb1 100644 --- a/src/components/datagrid/data_grid_header_row.tsx +++ b/src/components/datagrid/data_grid_header_row.tsx @@ -40,6 +40,8 @@ export interface EuiDataGridHeaderRowPropsSpecificProps { schema: EuiDataGridSchema; defaultColumnWidth?: number | null; setColumnWidth: (columnId: string, width: number) => void; + setVisibleColumns: (columnId: string[]) => void; + switchColumnPos: (colFromId: string, colToId: string) => void; sorting?: EuiDataGridSorting; focusedCell?: EuiDataGridFocusedCell; onCellFocus: EuiDataGridDataRowProps['onCellFocus']; @@ -63,6 +65,8 @@ const EuiDataGridHeaderRow = forwardRef< defaultColumnWidth, className, setColumnWidth, + setVisibleColumns, + switchColumnPos, sorting, focusedCell, onCellFocus: setFocusedCell, @@ -96,12 +100,15 @@ const EuiDataGridHeaderRow = forwardRef<