diff --git a/src-docs/src/views/datagrid/schema_columns/datagrid_columns_example.js b/src-docs/src/views/datagrid/schema_columns/datagrid_columns_example.js index e553d1dccaf..cc5f073c1a9 100644 --- a/src-docs/src/views/datagrid/schema_columns/datagrid_columns_example.js +++ b/src-docs/src/views/datagrid/schema_columns/datagrid_columns_example.js @@ -298,6 +298,11 @@ schemaDetectors={[ EuiDataGridCellValueElementProps and returning a React node.

+

+ When rendering footer cell values, we encourage turning off cell + expansion on cells without content with{' '} + setCellProps({'{ isExpandable: false }'}). +

), props: { diff --git a/src-docs/src/views/datagrid/schema_columns/footer_row.js b/src-docs/src/views/datagrid/schema_columns/footer_row.js index 4f5cdc4ebc8..4222e2fa4a5 100644 --- a/src-docs/src/views/datagrid/schema_columns/footer_row.js +++ b/src-docs/src/views/datagrid/schema_columns/footer_row.js @@ -66,17 +66,24 @@ const columns = [ ]; const footerCellValues = { - amount: `Total: $${raw_data.reduce( - (acc, { amount }) => acc + Number(amount.split('$')[1]), - 0 - )}`, + amount: `Total: ${raw_data + .reduce((acc, { amount }) => acc + Number(amount.split('$')[1]), 0) + .toLocaleString('en-US', { style: 'currency', currency: 'USD' })}`, version: `Latest: ${ raw_data.map(({ version }) => version).sort()[raw_data.length - 1] }`, }; -const renderFooterCellValue = ({ columnId }) => - footerCellValues[columnId] || null; +const RenderFooterCellValue = ({ columnId, setCellProps }) => { + const value = footerCellValues[columnId]; + + useEffect(() => { + // Turn off the cell expansion button if the footer cell is empty + if (!value) setCellProps({ isExpandable: false }); + }, [value, setCellProps]); + + return value || null; +}; export default () => { // Pagination @@ -121,7 +128,7 @@ export default () => { rowCount={raw_data.length} renderCellValue={RenderCellValue} renderFooterCellValue={ - showFooterRow ? renderFooterCellValue : undefined + showFooterRow ? RenderFooterCellValue : undefined } pagination={{ ...pagination, diff --git a/src/components/datagrid/body/data_grid_footer_row.spec.tsx b/src/components/datagrid/body/data_grid_footer_row.spec.tsx new file mode 100644 index 00000000000..bae4600c7ae --- /dev/null +++ b/src/components/datagrid/body/data_grid_footer_row.spec.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/// + +import React, { useState, useEffect } from 'react'; +import { EuiDataGrid } from '../'; + +describe('EuiDataGridFooterRow', () => { + const mockData = [10, 15, 20]; + + const RenderCellValue = ({ rowIndex, columnId }) => + columnId === 'b' ? mockData[rowIndex] : null; + + const RenderFooterCellValue = ({ columnId, setCellProps }) => { + const value = columnId === 'b' ? mockData.reduce((a, b) => a + b, 0) : null; + + useEffect(() => { + if (!value) setCellProps({ isExpandable: false }); + }, [value, setCellProps]); + + return value; + }; + + // We need to set up a test component here for column ordering to work + const GridTest = () => { + const [visibleColumns, setVisibleColumns] = useState(['a', 'b']); + + return ( + + ); + }; + GridTest.displayName = 'GridTest'; + + it('renders a footer of total values', () => { + cy.mount(); + + cy.get( + '[data-gridcell-column-index="1"][data-gridcell-row-index="3"]' + ).contains('45'); + }); + + it('does not render an expansion button for the empty footer cell', () => { + cy.mount(); + + cy.get( + '[data-gridcell-column-index="0"][data-gridcell-row-index="3"]' + ).click(); + cy.get('[data-test-subj="euiDataGridCellExpandButton"]').should( + 'not.exist' + ); + }); + + // Regression test for #5720 + it('does not bug focus when moving a column and then clicking its footer cell', () => { + cy.mount(); + + cy.get( + '[data-gridcell-column-index="1"][data-gridcell-row-index="-1"]' + ).click(); + cy.contains('Move left').click(); + + cy.get('[data-gridcell-column-index="0"][data-gridcell-row-index="3"]') + .click() + .contains('45') + .click(); + + // Note that the wait/timeout and multiple focused assertions are required for + // for this specific bug which bounces focus rapidly between cells, as otherwise + // Cypress re-tries until it happens to be on the cell with correct attributes + cy.wait(50); + const checkFocusedCell = () => { + cy.wait(1); + cy.focused({ timeout: 0 }) + .should('have.attr', 'data-gridcell-column-index', '0') + .should('not.have.attr', 'data-gridcell-column-index', '1'); + }; + checkFocusedCell(); + checkFocusedCell(); + checkFocusedCell(); + }); +}); diff --git a/src/components/datagrid/body/data_grid_footer_row.test.tsx b/src/components/datagrid/body/data_grid_footer_row.test.tsx index 33af4964473..eca2c81be9c 100644 --- a/src/components/datagrid/body/data_grid_footer_row.test.tsx +++ b/src/components/datagrid/body/data_grid_footer_row.test.tsx @@ -39,7 +39,7 @@ describe('EuiDataGridFooterRow', () => { columnType="string" interactiveCellId="someId" isExpandable={true} - key="someColumn-10" + key="0,10" popoverContext={ Object { "cellLocation": Object { @@ -65,7 +65,7 @@ describe('EuiDataGridFooterRow', () => { columnType={null} interactiveCellId="someId" isExpandable={true} - key="someColumnWithoutSchema-10" + key="1,10" popoverContext={ Object { "cellLocation": Object { diff --git a/src/components/datagrid/body/data_grid_footer_row.tsx b/src/components/datagrid/body/data_grid_footer_row.tsx index fea8bdc8ea7..ca78dacc3cd 100644 --- a/src/components/datagrid/body/data_grid_footer_row.tsx +++ b/src/components/datagrid/body/data_grid_footer_row.tsx @@ -81,7 +81,7 @@ const EuiDataGridFooterRow = memo( return (