diff --git a/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png b/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png
new file mode 100644
index 000000000000..bf3ea3645462
Binary files /dev/null and b/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png differ
diff --git a/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png b/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png
new file mode 100644
index 000000000000..fb260487de84
Binary files /dev/null and b/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png differ
diff --git a/changelogs/upcoming/7720.md b/changelogs/upcoming/7720.md
new file mode 100644
index 000000000000..faf548b583b1
--- /dev/null
+++ b/changelogs/upcoming/7720.md
@@ -0,0 +1,4 @@
+**Bug fixes**
+
+- Fixed missing styles on header cells of `EuiDataGrid` that prevented content text alignment styles to apply
+
diff --git a/src/components/datagrid/body/header/_data_grid_header_row.scss b/src/components/datagrid/body/header/_data_grid_header_row.scss
index 92169af28832..a32a8a8889e7 100644
--- a/src/components/datagrid/body/header/_data_grid_header_row.scss
+++ b/src/components/datagrid/body/header/_data_grid_header_row.scss
@@ -28,6 +28,10 @@
@include euiDataGridCellFocus;
}
+ .euiDataGridHeaderCell__content {
+ flex-grow: 1; // ensures content stretches and allows for manual layout styles to apply
+ }
+
// We only truncate if the cell is not a control column.
&:not(.euiDataGridHeaderCell--controlColumn) {
.euiDataGridHeaderCell__button {
@@ -42,6 +46,8 @@
.euiDataGridHeaderCell__content {
@include euiTextTruncate;
+
+ text-align: left; // overwrites inherited 'center' styles from button
}
.euiDataGridHeaderCell__sortingArrow {
@@ -76,7 +82,6 @@
&.euiDataGridHeaderCell--numeric,
&.euiDataGridHeaderCell--currency {
.euiDataGridHeaderCell__content {
- flex-grow: 1;
text-align: right;
}
}
diff --git a/src/components/datagrid/controls/data_grid_toolbar.tsx b/src/components/datagrid/controls/data_grid_toolbar.tsx
index f00722d10142..2cd96ed3ecb1 100644
--- a/src/components/datagrid/controls/data_grid_toolbar.tsx
+++ b/src/components/datagrid/controls/data_grid_toolbar.tsx
@@ -18,7 +18,7 @@ import { EuiScreenReaderOnly } from '../../accessibility';
import { IS_JEST_ENVIRONMENT } from '../../../utils';
// When below this number the grid only shows the right control icon buttons
-const MINIMUM_WIDTH_FOR_GRID_CONTROLS = 479;
+export const MINIMUM_WIDTH_FOR_GRID_CONTROLS = 479;
export const EuiDataGridToolbar = ({
gridWidth,
diff --git a/src/components/datagrid/data_grid.stories.tsx b/src/components/datagrid/data_grid.stories.tsx
new file mode 100644
index 000000000000..aa2e4f04cd61
--- /dev/null
+++ b/src/components/datagrid/data_grid.stories.tsx
@@ -0,0 +1,353 @@
+/*
+ * 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, { useCallback, useEffect, useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { faker } from '@faker-js/faker';
+
+import { enableFunctionToggleControls } from '../../../.storybook/utils';
+import { EuiLink } from '../link';
+import { EuiScreenReaderOnly } from '../accessibility';
+import { EuiButtonIcon } from '../button';
+
+import { DEFAULT_ROW_HEIGHT } from './utils/row_heights';
+import { MINIMUM_WIDTH_FOR_GRID_CONTROLS } from './controls/data_grid_toolbar';
+import type {
+ EuiDataGridCellValueElementProps,
+ EuiDataGridColumnCellActionProps,
+ EuiDataGridColumnSortingConfig,
+ EuiDataGridProps,
+} from './data_grid_types';
+import { EuiDataGrid } from './data_grid';
+
+const dataKeys = [
+ 'name',
+ 'email',
+ 'account',
+ 'location',
+ 'date',
+ 'version',
+] as const;
+const raw_data = Array.from({ length: 10 }).map(() => {
+ const email = faker.internet.email();
+ const name = `${faker.person.lastName()}, ${faker.person.firstName()}`;
+ const suffix = faker.person.suffix();
+ return {
+ name: {
+ formatted: `${name} ${suffix}`,
+ raw: name,
+ },
+ email: {
+ formatted: {faker.internet.email()},
+ raw: email,
+ },
+ location: (
+ <>
+ {`${faker.location.city()}, `}
+ {faker.location.country()}
+ >
+ ),
+ date: `${faker.date.past()}`,
+ account: faker.finance.accountNumber(),
+ version: faker.system.semver(),
+ };
+});
+
+const columns = [
+ {
+ id: 'name',
+ displayAsText: 'Name',
+ defaultSortDirection: 'asc' as const,
+ cellActions: [
+ ({ rowIndex, Component }: EuiDataGridColumnCellActionProps) => {
+ const data = raw_data;
+ const value = data[rowIndex].name.raw;
+ return (
+ alert(`Hi ${value}`)}
+ iconType="heart"
+ aria-label={`Say hi to ${value}!`}
+ >
+ Say hi
+
+ );
+ },
+ ],
+ },
+ {
+ id: 'email',
+ displayAsText: 'Email address',
+ initialWidth: 130,
+ cellActions: [
+ ({ rowIndex, Component }: EuiDataGridColumnCellActionProps) => {
+ const data = raw_data;
+ const value = data[rowIndex].email.raw;
+ return (
+ alert(value)}
+ iconType="email"
+ aria-label={`Send email to ${value}`}
+ >
+ Send email
+
+ );
+ },
+ ],
+ },
+ {
+ id: 'location',
+ displayAsText: 'Location',
+ },
+ {
+ id: 'account',
+ displayAsText: 'Account',
+ actions: {
+ showHide: { label: 'Custom hide label' },
+ showMoveLeft: false,
+ showMoveRight: false,
+ additional: [
+ {
+ label: 'Custom action',
+ onClick: () => {},
+ iconType: 'cheer',
+ size: 'xs' as const,
+ color: 'text' as const,
+ },
+ ],
+ },
+ cellActions: [
+ ({
+ rowIndex,
+ Component,
+ isExpanded,
+ }: EuiDataGridColumnCellActionProps) => {
+ const data = raw_data;
+ const value = data[rowIndex].account;
+ const onClick = isExpanded
+ ? () => alert(`Sent money to ${value} when expanded`)
+ : () => alert(`Sent money to ${value} when not expanded`);
+ return (
+
+ Send money
+
+ );
+ },
+ ],
+ },
+ {
+ id: 'date',
+ displayAsText: 'Date',
+ defaultSortDirection: 'desc' as const,
+ },
+ {
+ id: 'version',
+ displayAsText: 'Version',
+ defaultSortDirection: 'desc' as const,
+ initialWidth: 70,
+ isResizable: false,
+ actions: false as const,
+ },
+];
+
+const RenderCellValue = ({
+ rowIndex,
+ columnId,
+}: EuiDataGridCellValueElementProps) => {
+ const data = raw_data;
+ const row = data[rowIndex];
+ const columnName = columnId as (typeof dataKeys)[number];
+ const column = row[columnName];
+
+ const getFormatted = () => {
+ if (typeof column === 'object') {
+ const hasFormatted = 'formatted' in column;
+
+ return hasFormatted ? column.formatted : column;
+ }
+
+ return typeof column === 'string' ? column : null;
+ };
+
+ return data.hasOwnProperty(rowIndex) ? getFormatted() : null;
+};
+
+const meta: Meta = {
+ title: 'Tabular Content/EuiDataGrid',
+ component: EuiDataGrid,
+ argTypes: {
+ width: { control: 'text' },
+ height: { control: 'text' },
+ },
+ args: {
+ minSizeForControls: MINIMUM_WIDTH_FOR_GRID_CONTROLS,
+ },
+};
+enableFunctionToggleControls(meta, ['onColumnResize']);
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {
+ args: {
+ columns,
+ rowCount: 10,
+ renderCellValue: RenderCellValue,
+ trailingControlColumns: [
+ {
+ id: 'trailing-actions',
+ width: 40,
+ headerCellRender: () => (
+
+ Trailing actions
+
+ ),
+ rowCellRender: () => ,
+ },
+ ],
+ leadingControlColumns: [
+ {
+ id: 'leading-actions',
+ width: 40,
+ headerCellRender: () => (
+
+ Leading actions
+
+ ),
+ rowCellRender: () => ,
+ },
+ ],
+ // setup for easier testing/QA
+ columnVisibility: {
+ visibleColumns: [
+ 'name',
+ 'email',
+ 'account',
+ 'location',
+ 'date',
+ 'amount',
+ 'phone',
+ 'version',
+ ],
+ setVisibleColumns: () => {},
+ },
+ inMemory: { level: 'sorting' },
+ pagination: {
+ pageIndex: 0,
+ pageSize: 10,
+ pageSizeOptions: [10, 20, 50],
+ onChangeItemsPerPage: () => {},
+ onChangePage: () => {},
+ },
+ gridStyle: {
+ fontSize: 'm',
+ cellPadding: 'm',
+ border: 'all',
+ stripes: false,
+ header: 'shade',
+ footer: 'overline',
+ stickyFooter: true,
+ rowHover: 'highlight',
+ rowClasses: {},
+ },
+ width: '',
+ height: '',
+ toolbarVisibility: {
+ showColumnSelector: true,
+ showDisplaySelector: true,
+ showSortSelector: true,
+ showKeyboardShortcuts: true,
+ showFullScreenSelector: true,
+ additionalControls: null,
+ },
+ rowHeightsOptions: {
+ defaultHeight: DEFAULT_ROW_HEIGHT,
+ rowHeights: {},
+ lineHeight: undefined,
+ scrollAnchorRow: undefined,
+ },
+ },
+ render: (args: EuiDataGridProps) => ,
+};
+
+const StatefulDataGrid = (props: EuiDataGridProps) => {
+ const { pagination, sorting, columnVisibility, ...rest } = props;
+
+ // Pagination
+ const [_pagination, setPagination] = useState({
+ pageIndex: pagination?.pageIndex ?? 0,
+ ...pagination,
+ });
+ const onChangeItemsPerPage = useCallback(
+ (pageSize: number) =>
+ setPagination((pagination) => ({
+ ...pagination,
+ pageSize,
+ pageIndex: 0,
+ })),
+ [setPagination]
+ );
+ const onChangePage = useCallback(
+ (pageIndex: number) =>
+ setPagination((pagination) => ({ ...pagination, pageIndex })),
+ [setPagination]
+ );
+
+ useEffect(() => {
+ if (pagination) {
+ setPagination((curentPagination) => ({
+ ...curentPagination,
+ ...pagination,
+ }));
+ }
+ }, [pagination]);
+
+ // Sorting
+ const [sortingColumns, setSortingColumns] = useState<
+ EuiDataGridColumnSortingConfig[]
+ >(sorting?.columns ?? []);
+ const onSort = useCallback(
+ (sortingColumns: EuiDataGridColumnSortingConfig[]) => {
+ setSortingColumns(sortingColumns);
+ },
+ [setSortingColumns]
+ );
+
+ useEffect(() => {
+ if (sorting && Array.isArray(sorting.columns)) {
+ setSortingColumns(sorting.columns);
+ }
+ }, [sorting]);
+
+ // Column visibility
+ const [visibleColumns, setVisibleColumns] = useState(
+ columnVisibility?.visibleColumns ?? columns.map(({ id }) => id) // initialize to the full set of columns
+ );
+
+ useEffect(() => {
+ if (columnVisibility?.visibleColumns != null) {
+ setVisibleColumns(columnVisibility?.visibleColumns);
+ }
+ }, [columnVisibility]);
+
+ return (
+
+ );
+};