diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto_Below_Line_Count.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto_Below_Line_Count.png
new file mode 100644
index 00000000000..092ccb900cb
Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto_Below_Line_Count.png differ
diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto_Below_Line_Count.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto_Below_Line_Count.png
new file mode 100644
index 00000000000..6cef9e165a2
Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto_Below_Line_Count.png differ
diff --git a/packages/eui/changelogs/upcoming/8096.md b/packages/eui/changelogs/upcoming/8096.md
new file mode 100644
index 00000000000..57f9615617b
--- /dev/null
+++ b/packages/eui/changelogs/upcoming/8096.md
@@ -0,0 +1 @@
+- Updated `EuiDataGrid` with a beta `rowHeightsOptions.autoBelowLineCount` feature flag
diff --git a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.test.tsx b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.test.tsx
index b12992b8494..841f347407b 100644
--- a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.test.tsx
+++ b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.test.tsx
@@ -757,6 +757,26 @@ describe('EuiDataGridCell', () => {
callMethod(component);
expect(setRowHeight).not.toHaveBeenCalled();
});
+
+ it('does nothing if cell height is auto or autoBelowLineCount', () => {
+ mockRowHeightUtils.isAutoBelowLineCount.mockReturnValue(true);
+
+ const component = mount(
+
+ );
+
+ callMethod(component);
+ expect(setRowHeight).not.toHaveBeenCalled();
+
+ mockRowHeightUtils.isAutoBelowLineCount.mockRestore();
+ });
});
});
@@ -816,6 +836,30 @@ describe('EuiDataGridCell', () => {
expect(component.find('.eui-textBreakWord').exists()).toBe(true);
expect(component.find('.euiTextBlockTruncate').exists()).toBe(true);
});
+
+ test('autoBelowLineCount', () => {
+ mockRowHeightUtils.isAutoBelowLineCount.mockReturnValue(true);
+
+ const component = mount(
+
+ );
+
+ expect(
+ component
+ .find('div.euiDataGridRowCell__content--autoBelowLineCountHeight')
+ .hasClass(/autoHeight/)
+ ).toBe(true);
+ expect(component.find('.eui-textBreakWord').exists()).toBe(true);
+ expect(component.find('.euiTextBlockTruncate').exists()).toBe(true);
+
+ mockRowHeightUtils.isAutoBelowLineCount.mockRestore();
+ });
});
// Note: Tests for cell interactivity (focus, tabbing, etc) are in `focus_utils.spec.tsx`
diff --git a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.tsx b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.tsx
index 57592f9c0e8..013e4ccf17b 100644
--- a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.tsx
+++ b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.tsx
@@ -69,7 +69,7 @@ const EuiDataGridCellContent: FunctionComponent<
setCellContentsRef,
rowIndex,
colIndex,
- rowHeight,
+ rowHeightsOptions,
rowHeightUtils,
isControlColumn,
...rest
@@ -78,11 +78,18 @@ const EuiDataGridCellContent: FunctionComponent<
const CellElement =
renderCellValue as JSXElementConstructor;
- const cellHeightType = useMemo(
- () => rowHeightUtils?.getHeightType(rowHeight) || 'default',
- [rowHeightUtils, rowHeight]
+ // Cell height type
+ const rowHeight = rowHeightUtils?.getRowHeightOption(
+ rowIndex,
+ rowHeightsOptions
);
+ const cellHeightType = useMemo(() => {
+ return rowHeightUtils?.isAutoBelowLineCount(rowHeightsOptions, rowHeight)
+ ? 'autoBelowLineCount'
+ : rowHeightUtils?.getHeightType(rowHeight) || 'default';
+ }, [rowHeightUtils, rowHeight, rowHeightsOptions]);
+ // Classes and styles
const classes = useMemo(
() =>
classNames(
@@ -104,7 +111,7 @@ const EuiDataGridCellContent: FunctionComponent<
: [
// Regular data cells should always inherit height from the row wrapper,
// except for auto height
- cellHeightType === 'auto'
+ cellHeightType === 'auto' || cellHeightType === 'autoBelowLineCount'
? styles.content.autoHeight
: styles.content.defaultHeight,
]),
@@ -113,7 +120,9 @@ const EuiDataGridCellContent: FunctionComponent<
return (
@@ -201,6 +210,12 @@ export class EuiDataGridCell extends Component<
rowIndex,
rowHeightsOptions
);
+ if (
+ rowHeightUtils?.isAutoBelowLineCount(rowHeightsOptions, rowHeightOption)
+ ) {
+ return; // Using auto height instead
+ }
+
const isSingleLine = rowHeightOption == null; // Undefined rowHeightsOptions default to a single line
const lineCount = isSingleLine
? 1
@@ -585,11 +600,6 @@ export class EuiDataGridCell extends Component<
...cellPropsStyle, // apply anything from setCellProps({ style })
};
- const rowHeight = rowHeightUtils?.getRowHeightOption(
- rowIndex,
- rowHeightsOptions
- );
-
const row =
rowManager && !IS_JEST_ENVIRONMENT
? rowManager.getRow({
@@ -628,7 +638,7 @@ export class EuiDataGridCell extends Component<
isExpandable={isExpandable}
isExpanded={popoverIsOpen}
setCellContentsRef={this.setCellContentsRef}
- rowHeight={rowHeight}
+ rowHeightsOptions={rowHeightsOptions}
rowHeightUtils={rowHeightUtils}
isControlColumn={isControlColumn}
rowIndex={rowIndex}
diff --git a/packages/eui/src/components/datagrid/body/data_grid_body_custom.tsx b/packages/eui/src/components/datagrid/body/data_grid_body_custom.tsx
index 3093b852ad5..3b93a598a35 100644
--- a/packages/eui/src/components/datagrid/body/data_grid_body_custom.tsx
+++ b/packages/eui/src/components/datagrid/body/data_grid_body_custom.tsx
@@ -84,11 +84,7 @@ export const EuiDataGridBodyCustomRender: FunctionComponent
gridItemsRenderedRef: gridItemsRendered,
},
rowHeightsOptions,
- gridStyles,
columns,
});
diff --git a/packages/eui/src/components/datagrid/controls/display_selector.test.tsx b/packages/eui/src/components/datagrid/controls/display_selector.test.tsx
index d6b0374ab80..964af8eb30e 100644
--- a/packages/eui/src/components/datagrid/controls/display_selector.test.tsx
+++ b/packages/eui/src/components/datagrid/controls/display_selector.test.tsx
@@ -266,9 +266,19 @@ describe('useDataGridDisplaySelector', () => {
expect(getSelection(baseElement, 'rowHeightButtonGroup')).toEqual(
'static'
);
+ expect(getByTestSubject('static')).toHaveTextContent('Static');
expect(getByTestSubject('lineCountNumber')).toHaveValue(1);
});
+ it('renders a "Max" label instead of "Static" if autoBelowLineCount is true', async () => {
+ const { container, getByTestSubject } = render(
+
+ );
+ openPopover(container);
+
+ expect(getByTestSubject('static')).toHaveTextContent('Max');
+ });
+
it('calls the rowHeightsOptions.onChange callback on user change', async () => {
const onRowHeightChange = jest.fn();
const { container, baseElement, getByTestSubject } = render(
diff --git a/packages/eui/src/components/datagrid/controls/display_selector.tsx b/packages/eui/src/components/datagrid/controls/display_selector.tsx
index 9016dd7af39..34f028950b5 100644
--- a/packages/eui/src/components/datagrid/controls/display_selector.tsx
+++ b/packages/eui/src/components/datagrid/controls/display_selector.tsx
@@ -162,6 +162,8 @@ const RowHeightControl = ({
rowHeightsOptions: EuiDataGridRowHeightsOptions;
onChange: Function;
}) => {
+ const { autoBelowLineCount } = rowHeightsOptions;
+
const [lineCountInput, setLineCountInput] = useState(1);
const setLineCountHeight = useCallback(
(event: ChangeEvent) => {
@@ -227,10 +229,11 @@ const RowHeightControl = ({
'euiDisplaySelector.rowHeightLabel',
'euiDisplaySelector.labelAuto',
'euiDisplaySelector.labelStatic',
+ 'euiDisplaySelector.labelMax',
]}
- defaults={['Lines per row', 'Auto', 'Static']}
+ defaults={['Lines per row', 'Auto', 'Static', 'Max']}
>
- {([rowHeightLabel, labelAuto, labelStatic]: string[]) => (
+ {([rowHeightLabel, labelAuto, labelStatic, labelMax]: string[]) => (
{
}
/* Workaround to trim line-clamp and padding - @see https://github.com/elastic/eui/issues/7780 */
- .euiDataGridRowCell__content--lineCountHeight {
+ .euiDataGridRowCell__content--lineCountHeight,
+ .euiDataGridRowCell__content--autoBelowLineCountHeight {
${logicalCSS('padding-bottom', 0)}
${logicalCSS(
'border-bottom',
diff --git a/packages/eui/src/components/datagrid/data_grid_row_heights.stories.tsx b/packages/eui/src/components/datagrid/data_grid_row_heights.stories.tsx
index 4b69a84b8d3..5100b100c95 100644
--- a/packages/eui/src/components/datagrid/data_grid_row_heights.stories.tsx
+++ b/packages/eui/src/components/datagrid/data_grid_row_heights.stories.tsx
@@ -80,6 +80,27 @@ export const LineCount1: Story = {
),
};
+import { faker } from '@faker-js/faker';
+faker.seed(42);
+const loremData = Array.from({ length: 5 }).map((_, i) =>
+ faker.lorem.lines(i % 2 === 0 ? 1 : 20)
+);
+
+export const AutoBelowLineCount: Story = {
+ args: {
+ autoBelowLineCount: true,
+ defaultHeight: { lineCount: 3 },
+ },
+ render: (rowHeightsOptions) => (
+ loremData[rowIndex]}
+ columns={[{ id: 'name' }, { id: 'location' }]}
+ />
+ ),
+};
+
export const StaticHeight: Story = {
args: {
defaultHeight: { height: 48 },
diff --git a/packages/eui/src/components/datagrid/data_grid_types.ts b/packages/eui/src/components/datagrid/data_grid_types.ts
index 34f7921bd54..acac707bf34 100644
--- a/packages/eui/src/components/datagrid/data_grid_types.ts
+++ b/packages/eui/src/components/datagrid/data_grid_types.ts
@@ -1108,6 +1108,14 @@ export interface EuiDataGridRowHeightsOptions {
* Defines the default size for all rows. It can be line count or just height.
*/
defaultHeight?: EuiDataGridRowHeightOption;
+ /**
+ * Feature flag for custom `lineCount` behavior, where `lineCount` acts like a
+ * *max* number of lines (instead of a set number of lines for all rows).
+ *
+ * This functionality is in beta and has performance implications;
+ * we do not yet fully recommend/support it for heavy production usage.
+ */
+ autoBelowLineCount?: boolean;
/**
* Defines the height for a specific row. It can be line count or just height.
*
diff --git a/packages/eui/src/components/datagrid/utils/__mocks__/row_heights.ts b/packages/eui/src/components/datagrid/utils/__mocks__/row_heights.ts
index 2fef7caa05b..3ff7283eca0 100644
--- a/packages/eui/src/components/datagrid/utils/__mocks__/row_heights.ts
+++ b/packages/eui/src/components/datagrid/utils/__mocks__/row_heights.ts
@@ -24,6 +24,7 @@ export const RowHeightUtils = jest.fn().mockImplementation(() => {
const rowHeightUtilsMock: RowHeightUtilsPublicAPI = {
getHeightType: jest.fn(rowHeightUtils.getHeightType),
+ isAutoBelowLineCount: jest.fn(() => false),
isAutoHeight: jest.fn(() => false),
setRowHeight: jest.fn(),
pruneHiddenColumnHeights: jest.fn(),
diff --git a/packages/eui/src/components/datagrid/utils/grid_height_width.ts b/packages/eui/src/components/datagrid/utils/grid_height_width.ts
index 7c66fe64d90..686acc38323 100644
--- a/packages/eui/src/components/datagrid/utils/grid_height_width.ts
+++ b/packages/eui/src/components/datagrid/utils/grid_height_width.ts
@@ -141,7 +141,7 @@ export const useUnconstrainedHeight = ({
rowHeightOption,
defaultRowHeight,
correctRowIndex,
- rowHeightUtils.isRowHeightOverride(correctRowIndex, rowHeightsOptions)
+ rowHeightsOptions
);
}
}
diff --git a/packages/eui/src/components/datagrid/utils/row_heights.test.ts b/packages/eui/src/components/datagrid/utils/row_heights.test.ts
index 988c71edc78..5ee8e2b71bf 100644
--- a/packages/eui/src/components/datagrid/utils/row_heights.test.ts
+++ b/packages/eui/src/components/datagrid/utils/row_heights.test.ts
@@ -9,7 +9,6 @@
import type { MutableRefObject } from 'react';
import { act } from '@testing-library/react';
import { renderHook } from '../../../test/rtl';
-import { startingStyles } from '../controls';
import type { ImperativeGridApi } from '../data_grid_types';
import {
RowHeightUtils,
@@ -108,6 +107,23 @@ describe('RowHeightUtils', () => {
});
});
+ describe('autoBelowLineCount', () => {
+ it('uses the auto height cache', () => {
+ const rowIndex = 3;
+ const autoRowHeight = 100;
+ rowHeightUtils.setRowHeight(rowIndex, 'a', autoRowHeight, 0);
+
+ expect(
+ rowHeightUtils.getCalculatedHeight(
+ { lineCount: 10 },
+ 34,
+ rowIndex,
+ { rowHeights: { [rowIndex]: autoRowHeight } }
+ )
+ ).toEqual(autoRowHeight);
+ });
+ });
+
describe('row-specific overrides', () => {
it('returns the height set in the cache', () => {
const rowIndex = 5;
@@ -119,7 +135,7 @@ describe('RowHeightUtils', () => {
{ lineCount: 10 },
34,
rowIndex,
- true
+ { rowHeights: { [rowIndex]: rowHeightOverride } }
)
).toEqual(rowHeightOverride);
});
@@ -223,6 +239,56 @@ describe('RowHeightUtils', () => {
); // 5 * 24 + 6 + 6
});
});
+
+ describe('isAutoBelowLineCount', () => {
+ it('returns true when the feature flag is enabled and a lineCount above 1 exists', () => {
+ expect(
+ rowHeightUtils.isAutoBelowLineCount(
+ { autoBelowLineCount: true },
+ { lineCount: 3 }
+ )
+ ).toEqual(true);
+ });
+
+ it('returns false if the feature flag is not enabled', () => {
+ expect(
+ rowHeightUtils.isAutoBelowLineCount(
+ { autoBelowLineCount: false },
+ { lineCount: 3 }
+ )
+ ).toEqual(false);
+ expect(
+ rowHeightUtils.isAutoBelowLineCount(undefined, { lineCount: 3 })
+ ).toEqual(false);
+ });
+
+ it('returns false if height type is not lineCount', () => {
+ expect(
+ rowHeightUtils.isAutoBelowLineCount({ autoBelowLineCount: true }, 50)
+ ).toEqual(false);
+ expect(
+ rowHeightUtils.isAutoBelowLineCount(
+ { autoBelowLineCount: true },
+ 'auto'
+ )
+ ).toEqual(false);
+ });
+
+ it('returns false if lineCount is 1 (treated as single line/undefined)', () => {
+ expect(
+ rowHeightUtils.isAutoBelowLineCount(
+ { autoBelowLineCount: true },
+ { lineCount: 1 }
+ )
+ ).toEqual(false);
+ expect(
+ rowHeightUtils.isAutoBelowLineCount(
+ { autoBelowLineCount: true },
+ undefined
+ )
+ ).toEqual(false);
+ });
+ });
});
describe('auto height utils', () => {
@@ -240,6 +306,15 @@ describe('RowHeightUtils', () => {
).toEqual(true);
});
+ it('returns true if the conditions for `.isAutoBelowLineCount` are met', () => {
+ expect(
+ rowHeightUtils.isAutoHeight(1, {
+ autoBelowLineCount: true,
+ defaultHeight: { lineCount: 2 },
+ })
+ ).toEqual(true);
+ });
+
it('returns false otherwise', () => {
expect(
rowHeightUtils.isAutoHeight(1, {
@@ -539,10 +614,8 @@ describe('RowHeightVirtualizationUtils', () => {
});
describe('useRowHeightUtils', () => {
- const mockArgs = {
- gridStyles: startingStyles,
+ const mockArgs: Parameters[0] = {
columns: [{ id: 'A' }, { id: 'B' }],
- rowHeightOptions: undefined,
};
const mockVirtualizationArgs = {
...mockArgs,
diff --git a/packages/eui/src/components/datagrid/utils/row_heights.ts b/packages/eui/src/components/datagrid/utils/row_heights.ts
index 57f415e11e4..aa02bd8a5ab 100644
--- a/packages/eui/src/components/datagrid/utils/row_heights.ts
+++ b/packages/eui/src/components/datagrid/utils/row_heights.ts
@@ -22,7 +22,6 @@ import {
EuiDataGridRowHeightOption,
EuiDataGridRowHeightsOptions,
EuiDataGridScrollAnchorRow,
- EuiDataGridStyle,
ImperativeGridApi,
} from '../data_grid_types';
import { DataGridSortedContext } from './sorting';
@@ -54,7 +53,7 @@ export class RowHeightUtils {
heightOption: EuiDataGridRowHeightOption,
defaultHeight: number,
rowIndex?: number,
- isRowHeightOverride?: boolean
+ rowHeightsOptions?: EuiDataGridRowHeightsOptions
) {
if (isObject(heightOption) && heightOption.height) {
return Math.max(heightOption.height, defaultHeight);
@@ -65,8 +64,13 @@ export class RowHeightUtils {
}
if (isObject(heightOption) && heightOption.lineCount) {
- if (isRowHeightOverride) {
- return this.getRowHeight(rowIndex!) || defaultHeight; // lineCount overrides are stored in the heights cache
+ const { autoBelowLineCount } = rowHeightsOptions || {}; // uses auto height cache
+ const isRowHeightOverride = // lineCount overrides are stored in the heights cache
+ rowIndex != null &&
+ this.isRowHeightOverride(rowIndex, rowHeightsOptions);
+
+ if (autoBelowLineCount || isRowHeightOverride) {
+ return this.getRowHeight(rowIndex!) || defaultHeight;
} else {
return defaultHeight; // default lineCount height is set in minRowHeight state in grid_row_body
}
@@ -115,6 +119,15 @@ export class RowHeightUtils {
return contentHeight + padding * 2;
}
+ isAutoBelowLineCount(
+ options?: EuiDataGridRowHeightsOptions,
+ option?: EuiDataGridRowHeightOption
+ ) {
+ if (!options?.autoBelowLineCount) return false;
+ if ((this.getLineCount(option) ?? 0) > 1) return true;
+ return false;
+ }
+
/**
* Auto height utils
*/
@@ -128,6 +141,9 @@ export class RowHeightUtils {
if (height === AUTO_HEIGHT) {
return true;
}
+ if (this.isAutoBelowLineCount(rowHeightsOptions, height)) {
+ return true;
+ }
return false;
}
@@ -310,7 +326,6 @@ export const useRowHeightUtils = ({
gridItemsRenderedRef: MutableRefObject;
};
rowHeightsOptions?: EuiDataGridRowHeightsOptions;
- gridStyles: EuiDataGridStyle;
columns: EuiDataGridColumn[];
}) => {
const forceRenderRef = useLatest(useForceRender());
@@ -393,7 +408,7 @@ export const useDefaultRowHeight = ({
rowHeightOption,
minRowHeight,
correctRowIndex,
- rowHeightUtils.isRowHeightOverride(correctRowIndex, rowHeightsOptions)
+ rowHeightsOptions
);
}