diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Actions.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Actions.png index ca50c61e67d..3d2d9ddbd79 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Actions.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Actions.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png index 2edc9b07ff9..26a5532c33f 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Column_Actions.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Column_Actions.png new file mode 100644 index 00000000000..f46ffa432a8 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Column_Actions.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Custom_Header_Content.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Custom_Header_Content.png index e36b846da53..5d32f262af1 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Custom_Header_Content.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Custom_Header_Content.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png index e37ce718880..9094880a22f 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png index 35af7967f7a..72604cc7467 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png index f6ec8696489..bf559e38f18 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Virtualization.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Virtualization.png index f5da952784a..b66ac1bbf0a 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Virtualization.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_Virtualization.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png index b8f595ce4d5..22b153c35da 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png index a12ed9f8ff6..0288dc080df 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png index 7f0f6167c70..8027b468a2f 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png index 57cebef1779..4351c1241b7 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png index eaca7f7d40e..d4ee1037805 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto.png index 519a711cdb0..253b80b9dc6 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Auto.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Line_Height.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Line_Height.png index acfa299bcaf..f864e1833c9 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Line_Height.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Line_Height.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Row_Heights.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Row_Heights.png index 709c3af0c56..b66d31ed352 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Row_Heights.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Custom_Row_Heights.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Line_Count.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Line_Count.png index 8dbb2989928..4d4934226d4 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Line_Count.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Line_Count.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Static_Height.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Static_Height.png index d50be883a62..e68818083ae 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Static_Height.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_rowHeightsOptions_prop_Static_Height.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png index c5b56b2b0c5..a623e5639bd 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png index 2217b20d215..7d079031bde 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png index 5fda52e91cc..e0be3cc91d2 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png index 9e95900d542..1cc843240ce 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png index 6ef1cabff08..dfa13a507e0 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png index 8a644dcca6d..c3579f050b9 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png index e38564eaaea..161691678a4 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png index 74ac8382e85..1c21d47b978 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Actions.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Actions.png index 1203d043f97..6093d2348ea 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Actions.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Actions.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png index 8e4260f510a..d4254b541dc 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Cell_Expansion_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Column_Actions.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Column_Actions.png new file mode 100644 index 00000000000..05f3381944d Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Column_Actions.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Custom_Header_Content.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Custom_Header_Content.png index 7048ecc417a..518df5ae9b0 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Custom_Header_Content.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Custom_Header_Content.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png index 5e60f5820aa..d4fcb79d797 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Flyout.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png index 595f3e634cb..ed571c7c894 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_FullscreenVRT_Full_Screen_With_Header.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png index 300e2cdd650..3e4d7ad724a 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png index abf53f03899..24fb3fc5edf 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Compact.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png index fec1775e3c1..60f8d691952 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Expanded.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png index 6f895cd7908..9548cf694e3 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Horizontal_Lines.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png index a75c43c328f..fb999cb9cc2 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Minimal.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png index 9514e96ba46..5058b0eefb6 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png index 2684b934912..f43b5e6d5c9 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Selector.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png index fe992226be5..708d99e621f 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Column_Sorting.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png index 31721b52a4b..dc96de28009 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Display_Selector.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png index 6425b7e09f7..8b4559e7216 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Full_Screen_Toggle.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png index 24a4dd7ad71..97436fab22f 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Keyboard_Shortcuts.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png index d726f673d2c..f9ee3ca682a 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_No_Toolbar.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png index 3ba4b007c46..654bcea2691 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Render_Custom_Toolbar.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png index bd3e3f70dfb..f7a5a17659b 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_toolbarVisibility_prop_Toolbar_Visibility_Options.png differ diff --git a/packages/eui/.storybook/loki.ts b/packages/eui/.storybook/loki.ts index 9c8879cb44b..1d8d8834b02 100644 --- a/packages/eui/.storybook/loki.ts +++ b/packages/eui/.storybook/loki.ts @@ -25,7 +25,7 @@ export const LOKI_SELECTORS = { /** * Portal element content selector */ - portal: '#storybook-root', + portal: '#storybook-root > *', } as const; /** diff --git a/packages/eui/.storybook/preview.tsx b/packages/eui/.storybook/preview.tsx index 385ee472dff..b103c083dd0 100644 --- a/packages/eui/.storybook/preview.tsx +++ b/packages/eui/.storybook/preview.tsx @@ -35,11 +35,6 @@ appendIconComponentCache(iconCache); import { EuiProvider } from '../src/components/provider'; import { writingModeStyles } from './writing_mode.styles'; -// Import light theme for components still using Sass styling -// TODO: Remove this import and the `yarn compile-scss &&` command -// once all EUI components are converted to Emotion -import '../dist/eui_theme_light.css'; - /** * Ensure that any provider errors throw & warn us early */ diff --git a/packages/eui/changelogs/upcoming/8013.md b/packages/eui/changelogs/upcoming/8013.md new file mode 100644 index 00000000000..0d2b3962de5 --- /dev/null +++ b/packages/eui/changelogs/upcoming/8013.md @@ -0,0 +1,7 @@ +**CSS-in-JS conversions** + +- Converted `EuiDataGrid`'s row, header, and footer cells to Emotion; Removed the following Sass variables and mixins: + - `$euiDataGridColumnResizerWidth` + - `@euiDataGridRowCell` + - `@euiDataGridHeaderCell` + - `@euiDataGridFooterCell` diff --git a/packages/eui/cypress/support/component.tsx b/packages/eui/cypress/support/component.tsx index 397bdbffa6c..9ddd5aef01b 100644 --- a/packages/eui/cypress/support/component.tsx +++ b/packages/eui/cypress/support/component.tsx @@ -30,5 +30,3 @@ Cypress.on('uncaught:exception', (err) => { return false; } }); - -require(THEME_IMPORT); // defined by DefinePlugin in the cypress webpack config diff --git a/packages/eui/cypress/webpack.config.js b/packages/eui/cypress/webpack.config.js index c7704a93e3f..46c175e0b35 100644 --- a/packages/eui/cypress/webpack.config.js +++ b/packages/eui/cypress/webpack.config.js @@ -10,8 +10,6 @@ const { ProvidePlugin, DefinePlugin } = require('webpack'); -const THEME_IMPORT = `'../../dist/eui_theme_${process.env.THEME}.css'`; - const alias = {}; const reactVersion = process.env.REACT_VERSION || '18'; @@ -72,7 +70,6 @@ module.exports = { }), new DefinePlugin({ - THEME_IMPORT, // allow cypress/support/component.tsx to require the correct css file 'process.env.REACT_VERSION': JSON.stringify(reactVersion), }), ], diff --git a/packages/eui/generator-eui/changelog/index.js b/packages/eui/generator-eui/changelog/index.js index c456a47e085..6aa42a260d0 100644 --- a/packages/eui/generator-eui/changelog/index.js +++ b/packages/eui/generator-eui/changelog/index.js @@ -53,12 +53,6 @@ module.exports = class extends Generator { type: 'confirm', default: false, }, - { - message: 'Does your PR contain Emotion conversions?', - name: 'emotionConversions', - type: 'confirm', - default: false, - }, { message: 'Does your PR contain dependency updates?', name: 'dependencyUpdates', diff --git a/packages/eui/generator-eui/changelog/templates/changelog.md b/packages/eui/generator-eui/changelog/templates/changelog.md index 42b0f7992cb..1472557891f 100644 --- a/packages/eui/generator-eui/changelog/templates/changelog.md +++ b/packages/eui/generator-eui/changelog/templates/changelog.md @@ -25,11 +25,6 @@ - Removed ... -<%_ } -%> -<%_ if (emotionConversions) { -%> -**CSS-in-JS conversions** - -- Converted `EuiComponent` to Emotion; Removed `$euiComponentSassVariable` <%_ } -%> <%_ if (dependencyUpdates) { -%> **Dependency updates** diff --git a/packages/eui/package.json b/packages/eui/package.json index d8a248d9fed..7278d0edb40 100644 --- a/packages/eui/package.json +++ b/packages/eui/package.json @@ -42,8 +42,8 @@ "release": "node ./scripts/release.js", "release-backport": "node ./scripts/release.js --type=backport", "release-rc": "node ./scripts/release.js --type=prerelease", - "storybook": "yarn compile-scss && storybook dev -p 6006", - "build-storybook": "yarn compile-scss && storybook build", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", "pre-push": "yarn test-staged" }, "repository": { diff --git a/packages/eui/scripts/test-cypress.js b/packages/eui/scripts/test-cypress.js index e0cbde1c7f0..7fe30e1276a 100644 --- a/packages/eui/scripts/test-cypress.js +++ b/packages/eui/scripts/test-cypress.js @@ -18,7 +18,6 @@ const argv = yargs(hideBin(process.argv)) }) .options({ 'node-options': { type: 'string', default: '' }, - 'skip-css': { type: 'boolean' }, dev: { type: 'boolean' }, theme: { type: 'string', default: 'light', choices: ['light', 'dark'] }, a11y: { type: 'boolean' }, @@ -32,23 +31,12 @@ const argv = yargs(hideBin(process.argv)) const nodeOptions = argv['node-options']; const isDev = argv.hasOwnProperty('dev'); const isA11y = argv.hasOwnProperty('a11y'); -const skipScss = argv.hasOwnProperty('skip-css'); const theme = argv.theme; const reactVersion = argv['react-version']; const info = chalk.white; const log = chalk.grey; -// compile scss -> css so tests can render correctly -if (!skipScss) { - console.log(info('Compiling SCSS')); - execSync(`TARGET_THEME=${theme} yarn compile-scss`, { - stdio: 'inherit', - }); -} else { - console.log(info('Not compiling SCSS, disabled by --skip-css')); -} - // compile dev and a11y options for how to run tests (headless, local UI) // and whether to run component tests or axe checks. const testParams = isDev diff --git a/packages/eui/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/packages/eui/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index b8e59d3edb8..84fd9d9b42c 100644 --- a/packages/eui/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/packages/eui/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -607,34 +607,32 @@ exports[`EuiDataGrid rendering renders additional toolbar controls 1`] = ` tabindex="-1" >
A
@@ -651,34 +649,32 @@ exports[`EuiDataGrid rendering renders additional toolbar controls 1`] = ` tabindex="-1" >
B
@@ -696,18 +692,11 @@ exports[`EuiDataGrid rendering renders additional toolbar controls 1`] = ` tabindex="-1" >
0, A
-
0, B
-
1, A
-
1, B
-
2, A
-
2, B
-
@@ -1014,13 +968,9 @@ exports[`EuiDataGrid rendering renders control columns 1`] = ` style="width: 50px;" tabindex="-1" > -
- - leading heading - -
+ + leading heading +
A
@@ -1079,34 +1027,32 @@ exports[`EuiDataGrid rendering renders control columns 1`] = ` tabindex="-1" >
B
@@ -1121,13 +1067,9 @@ exports[`EuiDataGrid rendering renders control columns 1`] = ` style="width: 50px;" tabindex="-1" > -
- - trailing heading - -
+ + trailing heading +
0
-
0, A
-
0, B
-
0
-
1
-
1, A
-
1, B
-
1
-
2
-
2, A
-
2, B
-
2
-
@@ -1616,34 +1474,32 @@ exports[`EuiDataGrid rendering renders custom column headers 1`] = ` tabindex="-1" >
Column A
@@ -1660,12 +1516,11 @@ exports[`EuiDataGrid rendering renders custom column headers 1`] = ` tabindex="-1" >
@@ -1673,23 +1528,22 @@ exports[`EuiDataGrid rendering renders custom column headers 1`] = `
@@ -1707,18 +1561,11 @@ exports[`EuiDataGrid rendering renders custom column headers 1`] = ` tabindex="-1" >
0, A
-
0, B
-
1, A
-
1, B
-
2, A
-
2, B
-
@@ -2024,34 +1836,32 @@ exports[`EuiDataGrid rendering renders with common and div attributes 1`] = ` tabindex="-1" >
A
@@ -2068,34 +1878,32 @@ exports[`EuiDataGrid rendering renders with common and div attributes 1`] = ` tabindex="-1" >
B
@@ -2113,18 +1921,11 @@ exports[`EuiDataGrid rendering renders with common and div attributes 1`] = ` tabindex="-1" >
0, A
-
0, B
-
1, A
-
1, B
-
2, A
-
2, B
-
diff --git a/packages/eui/src/components/datagrid/_data_grid_data_row.scss b/packages/eui/src/components/datagrid/_data_grid_data_row.scss deleted file mode 100644 index 9821e7a21af..00000000000 --- a/packages/eui/src/components/datagrid/_data_grid_data_row.scss +++ /dev/null @@ -1,46 +0,0 @@ -@include euiDataGridRowCell { - .euiDataGridRowCell__content { - height: 100%; - overflow: hidden; - - &--autoHeight { - height: auto; - } - } - - // Hack to allow focus trap to still stretch to full row height on defined heights - > [data-focus-lock-disabled] { - height: 100%; - } - - &.euiDataGridRowCell--numeric { - text-align: right; - } - - &.euiDataGridRowCell--currency { - text-align: right; - } - - &.euiDataGridRowCell--uppercase { - text-transform: uppercase; - } - - &.euiDataGridRowCell--lowercase { - text-transform: lowercase; - } - - &.euiDataGridRowCell--capitalize { - text-transform: capitalize; - } -} - -.euiDataGridRowCell--controlColumn .euiDataGridRowCell__content { - max-height: 100%; - height: auto; - display: flex; - align-items: center; - - &.euiDataGridRowCell__content--defaultHeight { - height: 100%; - } -} diff --git a/packages/eui/src/components/datagrid/_index.scss b/packages/eui/src/components/datagrid/_index.scss deleted file mode 100644 index 5c435cae2fc..00000000000 --- a/packages/eui/src/components/datagrid/_index.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import 'variables'; -@import 'mixins'; -@import 'body/header/data_grid_header_row'; -@import 'body/footer/data_grid_footer_row'; -@import 'body/header/data_grid_column_resizer'; -@import 'data_grid_data_row'; diff --git a/packages/eui/src/components/datagrid/_mixins.scss b/packages/eui/src/components/datagrid/_mixins.scss deleted file mode 100644 index aedd5a32cd5..00000000000 --- a/packages/eui/src/components/datagrid/_mixins.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin euiDataGridHeaderCell { - .euiDataGridHeaderCell { - @content; - } -} - -@mixin euiDataGridRowCell { - .euiDataGridRowCell { - @content; - } -} - -@mixin euiDataGridFooterCell { - .euiDataGridRowCell.euiDataGridFooterCell { - @content; - } -} diff --git a/packages/eui/src/components/datagrid/_variables.scss b/packages/eui/src/components/datagrid/_variables.scss deleted file mode 100644 index 7eab3561d0d..00000000000 --- a/packages/eui/src/components/datagrid/_variables.scss +++ /dev/null @@ -1 +0,0 @@ -$euiDataGridColumnResizerWidth: 3px; // Odd number because it straddles a border diff --git a/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_custom.test.tsx.snap b/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_custom.test.tsx.snap index 86412d4b8e7..79b0a64b860 100644 --- a/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_custom.test.tsx.snap +++ b/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_custom.test.tsx.snap @@ -22,34 +22,32 @@ exports[`EuiDataGridBodyCustomRender treats \`renderCustomGridBody\` as a render tabindex="-1" >
columnA
@@ -66,34 +64,32 @@ exports[`EuiDataGridBodyCustomRender treats \`renderCustomGridBody\` as a render tabindex="-1" >
columnB
@@ -114,18 +110,11 @@ exports[`EuiDataGridBodyCustomRender treats \`renderCustomGridBody\` as a render tabindex="-1" >
hello
-
world
-
lorem
-
ipsum
-
diff --git a/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_virtualized.test.tsx.snap b/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_virtualized.test.tsx.snap index 3f7b371e037..e1a09ff8136 100644 --- a/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_virtualized.test.tsx.snap +++ b/packages/eui/src/components/datagrid/body/__snapshots__/data_grid_body_virtualized.test.tsx.snap @@ -26,34 +26,32 @@ exports[`EuiDataGridBodyVirtualized renders 1`] = ` tabindex="-1" >
columnA
@@ -70,34 +68,32 @@ exports[`EuiDataGridBodyVirtualized renders 1`] = ` tabindex="-1" >
columnB
@@ -115,20 +111,13 @@ exports[`EuiDataGridBodyVirtualized renders 1`] = ` tabindex="-1" >
cell content
-
cell content
-
diff --git a/packages/eui/src/components/datagrid/body/cell/__snapshots__/data_grid_cell.test.tsx.snap b/packages/eui/src/components/datagrid/body/cell/__snapshots__/data_grid_cell.test.tsx.snap index 6b396ed0691..3020ffe3438 100644 --- a/packages/eui/src/components/datagrid/body/cell/__snapshots__/data_grid_cell.test.tsx.snap +++ b/packages/eui/src/components/datagrid/body/cell/__snapshots__/data_grid_cell.test.tsx.snap @@ -49,7 +49,7 @@ exports[`EuiDataGridCell renders 1`] = ` tabindex="-1" >
@@ -65,12 +65,5 @@ exports[`EuiDataGridCell renders 1`] = `
- `; diff --git a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.spec.tsx b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.spec.tsx index 1a7e43109a5..08a87922e20 100644 --- a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.spec.tsx +++ b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.spec.tsx @@ -273,5 +273,43 @@ describe('Cell outline styles', () => { .realMouseMove(80, 0, { position: 'right' }) // ~50% of cell width .should('not.exist'); }); + + it('shows column actions on cell header hover/focus', () => { + const getHeaderCell = () => + cy.get('.euiDataGridHeaderCell[data-gridcell-column-id="expandable"]'); + const getHeaderActions = () => + cy.get('[data-test-subj="dataGridHeaderCellActionButton-expandable"]'); + const getActionsWidth = () => + getHeaderActions().then(($el) => { + const { width } = $el[0].getBoundingClientRect(); + return width; + }); + + cy.realMount(); + getActionsWidth().then((width) => expect(width).to.eq(0)); + + // Hovering over the header cell should slide in the actions from the right + getHeaderCell().realHover(); + cy.wait(ANIMATION.DURATION + ANIMATION.BUFFER); + getActionsWidth().then((width) => expect(width).to.eq(24)); + + // Toggling the actions popover should render the focus outline + getHeaderCell().then(($el) => { + expect(getOutlineColor($el[0])).not.to.eq(EXPECTED_FOCUS_COLOR); + }); + getHeaderActions().realClick(); + getHeaderCell().then(($el) => { + expect(getOutlineColor($el[0])).to.eq(EXPECTED_FOCUS_COLOR); + }); + getHeaderActions().realClick(); + + // Mousing off the cell should still show outline+actions since the button is still focused + getHeaderActions().realMouseMove(100, 0, { position: 'right' }); + cy.wait(ANIMATION.DURATION + ANIMATION.BUFFER); + getActionsWidth().then((width) => expect(width).to.eq(24)); + getHeaderCell().then(($el) => { + expect(getOutlineColor($el[0])).to.eq(EXPECTED_FOCUS_COLOR); + }); + }); }); }); diff --git a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.ts b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.ts index 5150ad88215..36c45a74da3 100644 --- a/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.ts +++ b/packages/eui/src/components/datagrid/body/cell/data_grid_cell.styles.ts @@ -9,7 +9,11 @@ import { css } from '@emotion/react'; import { UseEuiTheme } from '../../../../services'; -import { mathWithUnits } from '../../../../global_styling'; +import { + logicalCSS, + logicalTextAlignCSS, + mathWithUnits, +} from '../../../../global_styling'; export const euiDataGridCellOutlineStyles = ({ euiTheme }: UseEuiTheme) => { const focusColor = euiTheme.colors.primary; @@ -69,6 +73,7 @@ export const euiDataGridCellOutlineSelectors = (parentSelector = '&') => { // Utils const selectors = (...args: string[]) => [...args].join(', '); const is = (selectors: string) => `${parentSelector}:is(${selectors})`; + const not = (selectors: string) => `${parentSelector}:not(${selectors})`; const hoverNot = (selectors: string) => `${parentSelector}:hover:not(${selectors})`; const _ = (selectors: string) => `${parentSelector}${selectors}`; @@ -90,6 +95,7 @@ export const euiDataGridCellOutlineSelectors = (parentSelector = '&') => { header: { focus: is(selectors(focus, focusWithin, headerActionsOpen)), // :focus-within here is primarily intended for when the column actions button has been clicked twice focusTrapped: _(isEntered), + hideActions: not(selectors(hover, focusWithin, headerActionsOpen)), }, }; }; @@ -113,6 +119,44 @@ export const euiDataGridRowCellStyles = (euiThemeContext: UseEuiTheme) => { ${outlineSelectors.focusTrapped} { ${cellOutline.hoverStyles} } + + /* Hack to allow focus trap to still stretch to full row height on defined heights */ + & > [data-focus-lock-disabled] { + ${logicalCSS('height', '100%')} + } + + &:where(.euiDataGridRowCell--numeric, .euiDataGridRowCell--currency) { + ${logicalTextAlignCSS('right')} + } + + &:where(.euiDataGridRowCell--uppercase) { + text-transform: uppercase; + } + + &:where(.euiDataGridRowCell--lowercase) { + text-transform: lowercase; + } + + &:where(.euiDataGridRowCell--capitalize) { + text-transform: capitalize; + } `, + + content: { + euiDataGridRowCell__content: css` + overflow: hidden; + `, + controlColumn: css` + ${logicalCSS('max-height', '100%')} + display: flex; + align-items: center; + `, + autoHeight: css` + ${logicalCSS('height', 'auto')} + `, + defaultHeight: css` + ${logicalCSS('height', '100%')} + `, + }, }; }; 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 4f0d3e6793a..b8fcfe6d774 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 @@ -13,6 +13,7 @@ import { render } from '../../../../test/rtl'; import { RowHeightUtils } from '../../utils/__mocks__/row_heights'; import { mockFocusContext } from '../../utils/__mocks__/focus_context'; import { DataGridFocusContext } from '../../utils/focus'; +import type { EuiDataGridProps } from '../../data_grid_types'; import { EuiDataGridCell } from './data_grid_cell'; @@ -104,6 +105,61 @@ describe('EuiDataGridCell', () => { expect(mockPopoverContext.closeCellPopover).toHaveBeenCalledTimes(1); }); + describe('setCellProps', () => { + it('correctly merges props that also have EUI values', () => { + const RenderCellValue: EuiDataGridProps['renderCellValue'] = ({ + setCellProps, + }) => { + useEffect(() => { + setCellProps({ + style: { backgroundColor: 'black' }, + css: { color: 'white' }, + 'data-test-subj': 'test', + className: 'helloWorld', + }); + }, [setCellProps]); + return 'cell render'; + }; + + const { getByTestSubject } = render( + + ); + + const cell = getByTestSubject('dataGridRowCell test'); // should have merged `data-test-subj` correctly + expect(cell).toHaveClass('euiDataGridRowCell helloWorld'); // should have merged `className` correctly + expect(cell).toHaveStyle('background-color: rgb(0, 0, 0)'); // should have merged `style` correctly + expect(cell).toHaveStyle('color: rgb(255, 255, 255)'); // should have applied consumer `css` + expect(cell.className).toMatch(/css-[\w\d]+-euiDataGridRowCell/); // should not have overridden EUI `css` + }); + + it('does not allow overriding certain EUI props/values', () => { + const RenderCellValue: EuiDataGridProps['renderCellValue'] = ({ + setCellProps, + }) => { + useEffect(() => { + setCellProps({ + // @ts-expect-error - deliberately passing omitted props + role: 'ignored', + tabIndex: 2, + 'aria-rowindex': 99, + 'data-gridcell-visible-row-index': -200, + }); + }, [setCellProps]); + return 'cell render'; + }; + + const { container } = render( + + ); + + const cell = container.firstElementChild; + expect(cell).toHaveAttribute('role', 'gridcell'); + expect(cell).toHaveAttribute('tabIndex', '-1'); + expect(cell).toHaveAttribute('aria-rowindex', '1'); + expect(cell).toHaveAttribute('data-gridcell-visible-row-index', '0'); + }); + }); + describe('shouldComponentUpdate', () => { let shouldComponentUpdate: jest.SpyInstance; let component: ReactWrapper; @@ -489,7 +545,9 @@ describe('EuiDataGridCell', () => { }); it('allows overriding column.isExpandable with setCellProps({ isExpandable })', () => { - const RenderCellValue = ({ setCellProps }: any) => { + const RenderCellValue: EuiDataGridProps['renderCellValue'] = ({ + setCellProps, + }) => { useEffect(() => { setCellProps({ isExpandable: false }); }, [setCellProps]); 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 8e8c29ee203..7aee6c986a7 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 @@ -16,15 +16,17 @@ import React, { KeyboardEvent, memo, useMemo, + forwardRef, MutableRefObject, ReactElement, + HTMLAttributes, } from 'react'; import { createPortal } from 'react-dom'; import { IS_JEST_ENVIRONMENT } from '../../../../utils'; -import { keys, RenderWithEuiStylesMemoizer } from '../../../../services'; +import { keys, useEuiMemoizedStyles } from '../../../../services'; import { EuiScreenReaderOnly } from '../../../accessibility'; -import { EuiI18n } from '../../../i18n'; +import { useEuiI18n } from '../../../i18n'; import { EuiTextBlockTruncate } from '../../../text_truncate'; import { hasResizeObserver } from '../../../observer/resize_observer/resize_observer'; @@ -51,13 +53,8 @@ const EuiDataGridCellContent: FunctionComponent< EuiDataGridCellValueProps & { setCellProps: EuiDataGridCellValueElementProps['setCellProps']; setCellContentsRef: (ref: HTMLDivElement | null) => void; - showCellActions: boolean; isExpanded: boolean; - onExpandClick: () => void; - popoverAnchorRef: MutableRefObject; isControlColumn: boolean; - isFocused: boolean; - ariaRowIndex: number; rowHeight?: EuiDataGridRowHeightOption; } > = memo( @@ -68,14 +65,9 @@ const EuiDataGridCellContent: FunctionComponent< setCellContentsRef, rowIndex, colIndex, - ariaRowIndex, rowHeight, rowHeightUtils, isControlColumn, - isFocused, - showCellActions, - onExpandClick, - popoverAnchorRef, ...rest }) => { // React is more permissive than the TS types indicate @@ -100,65 +92,51 @@ const EuiDataGridCellContent: FunctionComponent< [cellHeightType, isControlColumn] ); + const styles = useEuiMemoizedStyles(euiDataGridRowCellStyles); + const cssStyles = [ + styles.content.euiDataGridRowCell__content, + ...(isControlColumn + ? [ + // Control column cells should not be vertically centered (defaultHeight) except + // on single rows. They should be top-aligned for auto and lineCount heights + styles.content.controlColumn, + cellHeightType === 'default' + ? styles.content.defaultHeight + : styles.content.autoHeight, + ] + : [ + // Regular data cells should always inherit height from the row wrapper, + // except for auto height + cellHeightType === 'auto' + ? styles.content.autoHeight + : styles.content.defaultHeight, + ]), + ]; + return ( - <> - +
-
- -
- - - - - - - {showCellActions && ( - - )} - +
+
); } ); @@ -610,58 +588,61 @@ export class EuiDataGridCell extends Component< return ( - - {(stylesMemoizer) => { - const styles = stylesMemoizer(euiDataGridRowCellStyles); - const cssStyles = [styles.euiDataGridRowCell, cellProps?.css]; - return ( -
- - - -
- ); - }} -
+ + + + + + {this.state.isFocused && ( + + )} + + {showCellActions && ( + + )} +
); } @@ -699,3 +680,59 @@ const RenderTruncatedCellContent: FunctionComponent<{ ); }); RenderTruncatedCellContent.displayName = 'RenderTruncatedCellContent'; + +/** + * Function component utilities for easier hook usage + */ + +const GridCellDiv = memo( + forwardRef< + HTMLDivElement, + HTMLAttributes & { + columnId: string; + columnIndex: number; + rowIndex: number; + visibleRowIndex: number; + } + >(({ columnId, columnIndex, rowIndex, visibleRowIndex, ...props }, ref) => { + const styles = useEuiMemoizedStyles(euiDataGridRowCellStyles); + return ( +
+ ); + }) +); +GridCellDiv.displayName = 'GridCellDiv'; + +const CellScreenReaderDescription: FunctionComponent<{ + columnName: string; + columnIndex: number; + rowIndex: number; + canExpandCell: boolean; +}> = memo(({ columnName, columnIndex, rowIndex, canExpandCell }) => { + const cellPosition = useEuiI18n( + 'euiDataGridCell.position', + '{columnName}, column {columnIndex}, row {rowIndex}', + { columnName, columnIndex, rowIndex } + ); + const enterKeyPrompt = useEuiI18n( + 'euiDataGridCell.expansionEnterPrompt', + 'Press the Enter key to expand this cell.' + ); + + return ( + +

{` - ${cellPosition}${canExpandCell ? `. ${enterKeyPrompt}` : ''}`}

+
+ ); +}); +CellScreenReaderDescription.displayName = 'CellScreenReaderDescription'; diff --git a/packages/eui/src/components/datagrid/body/cell/focus_utils.test.tsx b/packages/eui/src/components/datagrid/body/cell/focus_utils.test.tsx index 0373c6429d5..609efff316d 100644 --- a/packages/eui/src/components/datagrid/body/cell/focus_utils.test.tsx +++ b/packages/eui/src/components/datagrid/body/cell/focus_utils.test.tsx @@ -143,14 +143,8 @@ describe('FocusTrappedChildren', () => { data-focus-lock-disabled="disabled" > diff --git a/packages/eui/src/components/datagrid/body/cell/focus_utils.tsx b/packages/eui/src/components/datagrid/body/cell/focus_utils.tsx index 32e46ba9c82..9b5600cbbe5 100644 --- a/packages/eui/src/components/datagrid/body/cell/focus_utils.tsx +++ b/packages/eui/src/components/datagrid/body/cell/focus_utils.tsx @@ -14,12 +14,11 @@ import React, { useMemo, } from 'react'; import { FocusableElement, tabbable } from 'tabbable'; +import classNames from 'classnames'; -import { keys } from '../../../../services'; -import { useGeneratedHtmlId } from '../../../../services/accessibility'; +import { keys, useGeneratedHtmlId } from '../../../../services'; import { isDOMNode } from '../../../../utils'; import { EuiFocusTrap } from '../../../focus_trap'; -import { EuiScreenReaderOnly } from '../../../accessibility'; import { EuiI18n } from '../../../i18n'; /** @@ -91,14 +90,8 @@ export const FocusTrappedChildren: FunctionComponent< const [isCellEntered, setIsCellEntered] = useState(false); const [isExited, setExited] = useState(false); - const keyboardHintAriaId = useGeneratedHtmlId({ - prefix: 'euiDataGridCellHeader', - suffix: 'keyboardHint', - }); - - const exitedHintAriaId = useGeneratedHtmlId({ - prefix: 'euiDataGridCellHeader', - suffix: 'exited', + const ariaDescribedById = useGeneratedHtmlId({ + suffix: 'focusTrapHint', }); // direct DOM manipulation as workaround to attach required hints @@ -107,9 +100,17 @@ export const FocusTrappedChildren: FunctionComponent< cellEl.setAttribute( 'aria-describedby', - `${currentAriaDescribedbyId} ${exitedHintAriaId} ${keyboardHintAriaId} ` + classNames(currentAriaDescribedbyId, ariaDescribedById) ); - }, [cellEl, keyboardHintAriaId, exitedHintAriaId]); + + return () => { + if (currentAriaDescribedbyId) { + cellEl.setAttribute('aria-descibedby', currentAriaDescribedbyId); + } else { + cellEl.removeAttribute('aria-describedby'); + } + }; + }, [cellEl, ariaDescribedById]); useEffect(() => { if (isCellEntered) { @@ -172,37 +173,31 @@ export const FocusTrappedChildren: FunctionComponent< > {children} - - {/** - * Hints use aria-hidden to prevent them from being read as regular content. - * They are still read in JAWS and NVDA via the linking with aria-describedby. - * VoiceOver does generally not read the column on re-focus after exiting a cell, - * which mean the exited hint is not read. - * VoiceOver does react to aria-live (without aria-hidden) but that would causes - * duplicate output in JAWS/NVDA (reading content & live announcement). - * Optimizing for Windows screen readers as they have a larger usages. - */} - - - - - + {/** + * Hints use `hidden` to prevent them from being read by screen readers as regular content. + * They are still read in JAWS and NVDA via the linking with aria-describedby. + * VoiceOver does generally not read the column on re-focus after exiting a cell, + * which mean the exited hint is not read. + * VoiceOver does react to aria-live (without aria-hidden) but that would causes + * duplicate output in JAWS/NVDA (reading content & live announcement). + * Optimizing for Windows screen readers as they have a larger usages. + */} + ); }; diff --git a/packages/eui/src/components/datagrid/body/footer/_data_grid_footer_row.scss b/packages/eui/src/components/datagrid/body/footer/_data_grid_footer_row.scss deleted file mode 100644 index 568b157ab2f..00000000000 --- a/packages/eui/src/components/datagrid/body/footer/_data_grid_footer_row.scss +++ /dev/null @@ -1,5 +0,0 @@ -@include euiDataGridFooterCell { - flex: 0 0 auto; - position: relative; - font-weight: $euiFontWeightBold; -} diff --git a/packages/eui/src/components/datagrid/body/footer/data_grid_footer.styles.ts b/packages/eui/src/components/datagrid/body/footer/data_grid_footer.styles.ts index ac38024056c..41e964baaa8 100644 --- a/packages/eui/src/components/datagrid/body/footer/data_grid_footer.styles.ts +++ b/packages/eui/src/components/datagrid/body/footer/data_grid_footer.styles.ts @@ -42,5 +42,10 @@ export const euiDataGridFooterStyles = (euiThemeContext: UseEuiTheme) => { background-color: ${euiTheme.colors.lightestShade}; } `, + + euiDataGridFooterCell: css` + flex: 0 0 auto; + font-weight: ${euiTheme.font.weight.bold}; + `, }; }; diff --git a/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.test.tsx b/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.test.tsx index 19052cee4d1..d84d83c640c 100644 --- a/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.test.tsx +++ b/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.test.tsx @@ -35,7 +35,7 @@ describe('EuiDataGridFooterRow', () => { >
{ tabindex="-1" >
-
{ tabindex="-1" >
-
`); diff --git a/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.tsx b/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.tsx index 99f76a3bd2f..30b01cfcf02 100644 --- a/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.tsx +++ b/packages/eui/src/components/datagrid/body/footer/data_grid_footer_row.tsx @@ -56,6 +56,7 @@ const EuiDataGridFooterRow = memo( const popoverContext = useContext(DataGridCellPopoverContext); const sharedCellProps = { + css: styles.euiDataGridFooterCell, rowIndex, visibleRowIndex, interactiveCellId, diff --git a/packages/eui/src/components/datagrid/body/header/__snapshots__/data_grid_header_cell.test.tsx.snap b/packages/eui/src/components/datagrid/body/header/__snapshots__/data_grid_header_cell.test.tsx.snap index 62ff1f9070b..6fc8ed0541a 100644 --- a/packages/eui/src/components/datagrid/body/header/__snapshots__/data_grid_header_cell.test.tsx.snap +++ b/packages/eui/src/components/datagrid/body/header/__snapshots__/data_grid_header_cell.test.tsx.snap @@ -14,34 +14,32 @@ exports[`EuiDataGridHeaderCell renders 1`] = ` tabindex="-1" >
someColumn
diff --git a/packages/eui/src/components/datagrid/body/header/_data_grid_column_resizer.scss b/packages/eui/src/components/datagrid/body/header/_data_grid_column_resizer.scss deleted file mode 100644 index 28c836106f0..00000000000 --- a/packages/eui/src/components/datagrid/body/header/_data_grid_column_resizer.scss +++ /dev/null @@ -1,48 +0,0 @@ -// Resizer straddles the column border and is an invisible hitzone for dragging -.euiDataGridColumnResizer { - position: absolute; - top: 0; - right: -$euiSizeS; - height: 100%; - width: $euiSize; - cursor: ew-resize; - opacity: 0; - z-index: 2; // Needs to be a level above the cells themselves in case of overlaps - - // Center a vertical line within the button above - &::after { - content: ''; - position: absolute; - left: $euiSizeS - 1px; - top: 0; - bottom: 0; - width: $euiDataGridColumnResizerWidth; - background-color: $euiColorPrimary; - } - - &:hover, - &:active { - opacity: 1; - - ~ .euiDataGridHeaderCell__content { - user-select: none; - } - } -} - -// This is important. Because the resizer sits in the negative space to the right of the column -// it can cause the full grid to be a few pixels longer than it actually is. So for the last one -// we don't use negative positioning and the borders from the cell will match the container. -@include euiDataGridHeaderCell { - &:last-child { - .euiDataGridColumnResizer { - right: 0; - width: $euiSize / 2; - - &::after { - left: auto; - right: 0; - } - } - } -} diff --git a/packages/eui/src/components/datagrid/body/header/_data_grid_header_row.scss b/packages/eui/src/components/datagrid/body/header/_data_grid_header_row.scss deleted file mode 100644 index 513fadd5ee7..00000000000 --- a/packages/eui/src/components/datagrid/body/header/_data_grid_header_row.scss +++ /dev/null @@ -1,94 +0,0 @@ -@include euiDataGridHeaderCell { - position: relative; - display: flex; - flex: 0 0 auto; - align-items: center; - font-weight: $euiFontWeightBold; - - // Workaround for focus trap - & > [data-focus-lock-disabled] { - display: flex; - align-items: center; - gap: $euiSizeXS; - width: 100%; - } - - .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 { - position: relative; - display: flex; - align-items: center; - gap: $euiSizeXS; - width: 100%; - border-radius: $euiBorderRadiusSmall; - font-weight: $euiFontWeightBold; - outline: none; - - &:focus-visible { - outline: none; - } - } - - [data-focus-lock-disabled='false'] .euiDataGridHeaderCell__button { - @include euiFocusRing; - color: $euiFocusRingColor; - } - - .euiDataGridHeaderCell__content { - @include euiTextTruncate; - - text-align: left; // overwrites inherited 'center' styles from button - } - - .euiDataGridHeaderCell__sortingArrow { - flex: 0 0 auto; // Ensure icon doesn't shrink - } - - .euiDataGridHeaderCell__icon { - flex: 0 0 auto; // Ensure icon doesn't shrink - margin-left: auto; // Aligns the icon to the right - // Center the icon - display: flex; - align-items: center; - justify-content: center; - width: 0; - height: $euiSize; - overflow: hidden; - opacity: 0; - transition: width $euiAnimSpeedFast ease-in, opacity $euiAnimSpeedSlow ease-in; - } - - &:focus-within, - &:hover, - .euiPopover-isOpen { - .euiDataGridHeaderCell__button { - padding: $euiSizeXS; - // balance out additional button target size in header height, prevents increased header cell height - margin-block: -$euiSizeXS; - } - - .euiDataGridHeaderCell__icon { - width: $euiSize; - opacity: 1; - } - } - } - - // Align numeric and currency schemas to the right - &.euiDataGridHeaderCell--numeric, - &.euiDataGridHeaderCell--currency { - .euiDataGridHeaderCell__content { - text-align: right; - } - } -} - -.euiDataGridHeader__action--selected { - // stylelint-disable-next-line declaration-no-important - font-weight: $euiFontWeightBold !important; -} diff --git a/packages/eui/src/components/datagrid/body/header/column_actions.test.tsx b/packages/eui/src/components/datagrid/body/header/column_actions.test.tsx index eb6585ad060..854b87bab59 100644 --- a/packages/eui/src/components/datagrid/body/header/column_actions.test.tsx +++ b/packages/eui/src/components/datagrid/body/header/column_actions.test.tsx @@ -314,7 +314,6 @@ describe('getColumnActions', () => { it('renders a "Sort A-Z" item', () => { expect(sortAsc).toMatchInlineSnapshot(` { - "className": "", "color": "text", "iconType": "sortUp", "isDisabled": false, @@ -339,7 +338,6 @@ describe('getColumnActions', () => { it('renders a "Sort Z-A" item', () => { expect(sortDesc).toMatchInlineSnapshot(` { - "className": "", "color": "text", "iconType": "sortDown", "isDisabled": false, @@ -378,10 +376,21 @@ describe('getColumnActions', () => { }); const sortAsc = items[1]; - it('renders sortAsc as selected', () => { - expect(sortAsc.className).toEqual( - 'euiDataGridHeader__action--selected' - ); + it('changes label to unsort', () => { + expect(sortAsc.label).toMatchInlineSnapshot(` + , + } + } + /> + `); }); it('unsets the current sort if sortAsc is clicked again', () => { @@ -398,10 +407,21 @@ describe('getColumnActions', () => { const sortAsc = items[1]; const sortDesc = items[2]; - it('renders sortDesc as selected', () => { - expect(sortDesc.className).toEqual( - 'euiDataGridHeader__action--selected' - ); + it('changes label to unsort', () => { + expect(sortDesc.label).toMatchInlineSnapshot(` + , + } + } + /> + `); }); it('sets a new sort if the direction is changed', () => { diff --git a/packages/eui/src/components/datagrid/body/header/column_actions.tsx b/packages/eui/src/components/datagrid/body/header/column_actions.tsx index cd702168fe5..400a94c80ef 100644 --- a/packages/eui/src/components/datagrid/body/header/column_actions.tsx +++ b/packages/eui/src/components/datagrid/body/header/column_actions.tsx @@ -275,8 +275,17 @@ export const getSortColumnActions = ({ sortBy('asc'); }; + const isSorted = + sortingIdx >= 0 && sorting.columns[sortingIdx].direction === 'asc'; + const action = { - label: ( + label: isSorted ? ( + + ) : ( = 0 && sorting.columns[sortingIdx].direction === 'asc' - ? 'euiDataGridHeader__action--selected' - : '', iconType: 'sortUp', size: 'xs', color: 'text', @@ -306,8 +311,17 @@ export const getSortColumnActions = ({ sortBy('desc'); }; + const isSorted = + sortingIdx >= 0 && sorting.columns[sortingIdx].direction === 'desc'; + const action = { - label: ( + label: isSorted ? ( + + ) : ( = 0 && sorting.columns[sortingIdx].direction === 'desc' - ? 'euiDataGridHeader__action--selected' - : '', iconType: 'sortDown', size: 'xs', color: 'text', diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.styles.ts b/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.styles.ts new file mode 100644 index 00000000000..840c371787f --- /dev/null +++ b/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.styles.ts @@ -0,0 +1,72 @@ +/* + * 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 { css } from '@emotion/react'; + +import { UseEuiTheme } from '../../../../services'; +import { logicalCSS, mathWithUnits } from '../../../../global_styling'; + +// Resizer straddles the column border and is an invisible hitzone for dragging +export const euiDataGridColumnResizerStyles = ( + euiThemeContext: UseEuiTheme +) => { + const { euiTheme } = euiThemeContext; + + const clickableWidth = euiTheme.size.base; + const positionOffset = mathWithUnits(clickableWidth, (x) => x / -2); + + const indicatorWidth = mathWithUnits( + [euiTheme.border.width.thin, euiTheme.border.width.thick], + (x, y) => x + y + ); // Odd number because it straddles a border + const indicatorOffset = `-${euiTheme.border.width.thin}`; + + return { + euiDataGridColumnResizer: css` + z-index: 2; /* Needs to be a level above the cells themselves in case of overlaps */ + position: absolute; + ${logicalCSS('vertical', 0)} + ${logicalCSS('right', positionOffset)} + cursor: ew-resize; + opacity: 0; + + &:hover, + &:active { + opacity: 1; + } + + /* Center a vertical line within the button above */ + &::after { + content: ''; + position: absolute; + ${logicalCSS('vertical', 0)} + ${logicalCSS('left', positionOffset)} + ${logicalCSS('margin-left', indicatorOffset)} + ${logicalCSS('width', indicatorWidth)} + background-color: ${euiTheme.colors.primary}; + } + + /* Because the resizer sits in the negative space to the right of the column, + * it can cause the full grid to be a few pixels longer than it actually is. + * So for the last cell, we don't use negative positioning and the borders from + * the cell will match the container. */ + .euiDataGridHeaderCell:last-child & { + ${logicalCSS('right', 0)} + ${logicalCSS('width', euiTheme.size.s)} + + &::after { + ${logicalCSS('left', 'auto')} + ${logicalCSS('right', 0)} + } + } + `, + isDragging: css` + opacity: 1; + `, + }; +}; diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.test.tsx b/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.test.tsx index 494db0ad8c7..a9c77fbaa1c 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.test.tsx +++ b/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.test.tsx @@ -7,7 +7,8 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { act } from '@testing-library/react'; +import { render } from '../../../../test/rtl'; import { EuiDataGridColumnResizer } from './data_grid_column_resizer'; @@ -18,57 +19,94 @@ describe('EuiDataGridHeaderResizer', () => { setColumnWidth: jest.fn(), }; - const component = shallow(); - const componentMethods = component.instance() as EuiDataGridColumnResizer; - it('renders', () => { - expect(component).toMatchInlineSnapshot(` + const { container } = render(); + + expect(container.firstChild).toMatchInlineSnapshot(`
`); }); - describe('on mouse down', () => { - it('saves the current mouse horizontal position and adds mouse move & up listeners', () => { - const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); - component.simulate('mouseDown', { - pageX: 100, - preventDefault: jest.fn(), - }); + describe('mouse events', () => { + const mouseEvent = { + preventDefault: () => {}, + } as React.MouseEvent; - expect(component.state('initialX')).toEqual(100); - expect(addEventListenerSpy).toHaveBeenCalledTimes(3); - }); - }); + // Using a ref to reach into class methods/state directly - + // mocking mouse events in jsdom is too much of a headache + let classRef: EuiDataGridColumnResizer; + const setRef = (ref: EuiDataGridColumnResizer) => { + classRef = ref; + }; - describe('on mouse move', () => { - it('does not allow an offset that would go under the mininum column width', () => { - componentMethods.onMouseMove({ pageX: 0 }); - expect(component.state('offset')).toEqual(-10); + describe('on mouse down', () => { + it('adds mouse move & up listeners', () => { + render(); + const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); + + act(() => classRef.onMouseDown({ ...mouseEvent, pageX: 100 })); + expect(classRef.state.initialX).toEqual(100); + + const anyFn = expect.any(Function); + expect(addEventListenerSpy).toHaveBeenCalledWith('mouseup', anyFn); + expect(addEventListenerSpy).toHaveBeenCalledWith('mousemove', anyFn); + expect(addEventListenerSpy).toHaveBeenCalledWith('blur', anyFn); + }); }); - it('sets offset state to the difference of the moved pageX', () => { - componentMethods.onMouseMove({ pageX: 200 }); - expect(component.state('offset')).toEqual(100); + describe('on mouse move', () => { + it('does not allow an offset that would go under the mininum column width', () => { + const { getByTestSubject } = render( + + ); + + act(() => classRef.onMouseDown({ ...mouseEvent, pageX: 100 })); + act(() => classRef.onMouseMove({ pageX: 0 })); + + expect(classRef.state.offset).toEqual(-10); + expect(getByTestSubject('dataGridColumnResizer')).toHaveStyle( + 'margin-inline-end: 10px' + ); + }); + + it('sets offset state to the difference of the moved pageX', () => { + const { getByTestSubject } = render( + + ); + + act(() => classRef.onMouseDown({ ...mouseEvent, pageX: 100 })); + act(() => classRef.onMouseMove({ pageX: 200 })); + + expect(classRef.state.offset).toEqual(100); + expect(getByTestSubject('dataGridColumnResizer')).toHaveStyle( + 'margin-inline-end: -100px' + ); + }); }); - }); - describe('on mouse up', () => { - it('calls setColumnWidth, reset offset, and removes event listeners', () => { - const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener'); - componentMethods.onMouseUp(); + describe('on mouse up', () => { + it('calls setColumnWidth, reset offset, and removes event listeners', () => { + render(); + const removeEventListenerSpy = jest.spyOn( + window, + 'removeEventListener' + ); + + act(() => classRef.onMouseDown({ ...mouseEvent, pageX: 100 })); + act(() => classRef.onMouseMove({ pageX: 200 })); + act(() => classRef.onMouseUp()); - expect(props.setColumnWidth).toHaveBeenCalledWith('someColumn', 150); - expect(component.state('offset')).toEqual(0); - expect(removeEventListenerSpy).toHaveBeenCalledTimes(3); + expect(props.setColumnWidth).toHaveBeenCalledWith('someColumn', 150); + expect(classRef.state.offset).toEqual(0); + + const anyFn = expect.any(Function); + expect(removeEventListenerSpy).toHaveBeenCalledWith('mouseup', anyFn); + expect(removeEventListenerSpy).toHaveBeenCalledWith('mousemove', anyFn); + expect(removeEventListenerSpy).toHaveBeenCalledWith('blur', anyFn); + }); }); }); }); diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.tsx b/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.tsx index d8577e35e39..158e5ea49a4 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.tsx +++ b/packages/eui/src/components/datagrid/body/header/data_grid_column_resizer.tsx @@ -7,10 +7,13 @@ */ import React, { Component } from 'react'; +import { RenderWithEuiStylesMemoizer } from '../../../../services'; +import { logicalStyle } from '../../../../global_styling'; import { EuiDataGridColumnResizerProps, EuiDataGridColumnResizerState, } from '../../data_grid_types'; +import { euiDataGridColumnResizerStyles } from './data_grid_column_resizer.styles'; const MINIMUM_COLUMN_WIDTH = 40; @@ -65,12 +68,28 @@ export class EuiDataGridColumnResizer extends Component< const { offset } = this.state; return ( -
+ + {(stylesMemoizer) => { + const styles = stylesMemoizer(euiDataGridColumnResizerStyles); + const cssStyles = [ + styles.euiDataGridColumnResizer, + offset && styles.isDragging, + ]; + return ( +
+ ); + }} + ); } } diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.test.tsx b/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.test.tsx index 2d5accb778b..087e56a4274 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.test.tsx +++ b/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { render } from '../../../../test/rtl'; import { EuiDataGridControlHeaderCell } from './data_grid_control_header_cell'; @@ -23,20 +23,45 @@ describe('EuiDataGridControlHeaderCell', () => { }; it('renders', () => { - const component = shallow(); - expect(component).toMatchInlineSnapshot(` - ); + expect(container.firstChild).toMatchInlineSnapshot(` +
+
- +
- +
+
`); }); }); diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.tsx b/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.tsx index f6bc746cbe9..6971947c0b9 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.tsx +++ b/packages/eui/src/components/datagrid/body/header/data_grid_control_header_cell.tsx @@ -32,9 +32,7 @@ export const EuiDataGridControlHeaderCell: FunctionComponent -
- -
+ ); }); diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_header_cell.styles.ts b/packages/eui/src/components/datagrid/body/header/data_grid_header_cell.styles.ts new file mode 100644 index 00000000000..b3efb1a7614 --- /dev/null +++ b/packages/eui/src/components/datagrid/body/header/data_grid_header_cell.styles.ts @@ -0,0 +1,65 @@ +/* + * 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 { css } from '@emotion/react'; + +import { UseEuiTheme } from '../../../../services'; +import { + euiCanAnimate, + euiTextTruncate, + logicalCSS, + logicalTextAlignCSS, +} from '../../../../global_styling'; +import { euiDataGridCellOutlineSelectors } from '../cell/data_grid_cell.styles'; + +/** + * Styles only applied to data header cell content, not control header cells + */ +export const euiDataGridHeaderCellStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme } = euiThemeContext; + const { header } = euiDataGridCellOutlineSelectors('.euiDataGridHeaderCell'); + + return { + euiDataGridHeaderCell__content: css` + flex-grow: 1; /* ensures content stretches and allows for manual layout styles to apply */ + ${euiTextTruncate()} + `, + // Overwrite inherited 'center' styles from + /> } isOpen={isPopoverOpen} closePopover={closePopover} diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.styles.ts b/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.styles.ts index 5ea2bfc551a..61cbac4c334 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.styles.ts +++ b/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.styles.ts @@ -9,6 +9,7 @@ import { css } from '@emotion/react'; import { UseEuiTheme } from '../../../../services'; +import { logicalCSS } from '../../../../global_styling'; import { euiDataGridCellOutlineStyles, @@ -21,12 +22,24 @@ import { export const euiDataGridHeaderCellWrapperStyles = ( euiThemeContext: UseEuiTheme ) => { + const { euiTheme } = euiThemeContext; const { focusStyles, hoverStyles } = euiDataGridCellOutlineStyles(euiThemeContext); const { header: outlineSelectors } = euiDataGridCellOutlineSelectors(); + const _sharedFlexCss = css` + display: flex; + align-items: center; + gap: ${euiTheme.size.xxs}; + `; + return { euiDataGridHeaderCell: css` + position: relative; /* Needed for cell outline */ + ${_sharedFlexCss} + flex: 0 0 auto; + font-weight: ${euiTheme.font.weight.bold}; + ${outlineSelectors.focus} { ${focusStyles} } @@ -34,6 +47,12 @@ export const euiDataGridHeaderCellWrapperStyles = ( ${outlineSelectors.focusTrapped} { ${hoverStyles} } + + /* Workaround for focus trap */ + & > [data-focus-lock-disabled] { + ${_sharedFlexCss} + ${logicalCSS('width', '100%')} + } `, }; }; diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.test.tsx b/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.test.tsx index 268781eb110..12cfcc79d5d 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.test.tsx +++ b/packages/eui/src/components/datagrid/body/header/data_grid_header_cell_wrapper.test.tsx @@ -121,14 +121,8 @@ describe('EuiDataGridHeaderCellWrapper', () => { Mock column actions diff --git a/packages/eui/src/components/datagrid/body/header/data_grid_header_row.test.tsx b/packages/eui/src/components/datagrid/body/header/data_grid_header_row.test.tsx index ebd3998d2b9..1b1048abdf6 100644 --- a/packages/eui/src/components/datagrid/body/header/data_grid_header_row.test.tsx +++ b/packages/eui/src/components/datagrid/body/header/data_grid_header_row.test.tsx @@ -64,34 +64,32 @@ describe('EuiDataGridHeaderRow', () => { tabindex="-1" >
someColumn
diff --git a/packages/eui/src/components/datagrid/controls/full_screen_selector.stories.tsx b/packages/eui/src/components/datagrid/controls/full_screen_selector.stories.tsx index 788669f6074..72008d593af 100644 --- a/packages/eui/src/components/datagrid/controls/full_screen_selector.stories.tsx +++ b/packages/eui/src/components/datagrid/controls/full_screen_selector.stories.tsx @@ -7,7 +7,7 @@ */ import React, { useState } from 'react'; -import { fireEvent, within } from '@storybook/test'; +import { fireEvent, waitFor, within } from '@storybook/test'; import type { Meta, StoryObj, ReactRenderer } from '@storybook/react'; import type { PlayFunctionContext } from '@storybook/csf'; import { LOKI_SELECTORS } from '../../../../.storybook/loki'; @@ -47,7 +47,7 @@ const dataGridProps: EuiDataGridProps = { export const FullScreenWithHeader: Story = { tags: ['vrt-only'], parameters: { - loki: { chromeSelector: LOKI_SELECTORS.body }, + loki: { chromeSelector: LOKI_SELECTORS.portal }, }, render: () => ( <> @@ -59,6 +59,7 @@ export const FullScreenWithHeader: Story = { ), play: async ({ canvasElement }: PlayFunctionContext) => { const canvas = within(canvasElement); + await waitFor(() => canvas.getByLabelText('Enter fullscreen')); await fireEvent.click(canvas.getByLabelText('Enter fullscreen')); }, }; diff --git a/packages/eui/src/components/datagrid/data_grid.a11y.tsx b/packages/eui/src/components/datagrid/data_grid.a11y.tsx index 925faff7ef8..5580f3cef82 100644 --- a/packages/eui/src/components/datagrid/data_grid.a11y.tsx +++ b/packages/eui/src/components/datagrid/data_grid.a11y.tsx @@ -255,6 +255,7 @@ describe('EuiDataGrid', () => { it('has zero violations on sort and when the columns sorting menu is open', () => { cy.get('.euiDataGridHeaderCell').last().realHover(); + cy.wait(200); // Wait for transition cy.get('button.euiDataGridHeaderCell__button').last().realClick(); cy.get('button.euiListGroupItem__button') .contains('Sort Alma to Debian') diff --git a/packages/eui/src/components/datagrid/data_grid.stories.tsx b/packages/eui/src/components/datagrid/data_grid.stories.tsx index 1c0ee36939d..b8bdd9bce64 100644 --- a/packages/eui/src/components/datagrid/data_grid.stories.tsx +++ b/packages/eui/src/components/datagrid/data_grid.stories.tsx @@ -7,9 +7,11 @@ */ import React, { useRef, useEffect } from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; - +import type { Meta, StoryObj, ReactRenderer } from '@storybook/react'; +import type { PlayFunctionContext } from '@storybook/csf'; +import { within } from '../../../.storybook/test'; import { enableFunctionToggleControls } from '../../../.storybook/utils'; + import { EuiButtonIcon } from '../button'; import { EuiToolTip } from '../tool_tip'; @@ -144,7 +146,7 @@ export const CellActions: Story = { dataGridRef.current?.setFocusedCell({ rowIndex: 2, colIndex: 2 }); }, []); - return ; + return ; }, }; @@ -158,6 +160,23 @@ export const CellExpansionPopover: Story = { dataGridRef.current?.openCellPopover({ rowIndex: 1, colIndex: 1 }); }, []); - return ; + return ; + }, +}; + +export const ColumnActions: Story = { + tags: ['vrt-only'], + args: defaultStorybookArgs, + render: () => ( + + ), + play: async ({ canvasElement }: PlayFunctionContext) => { + const canvas = within(canvasElement); + await canvas.waitForAndClick('dataGridHeaderCellActionButton-account'); + await canvas.waitForEuiPopoverVisible(); }, }; diff --git a/packages/eui/src/components/datagrid/data_grid.stories.utils.tsx b/packages/eui/src/components/datagrid/data_grid.stories.utils.tsx index 135043bee98..f4c2d5b4da6 100644 --- a/packages/eui/src/components/datagrid/data_grid.stories.utils.tsx +++ b/packages/eui/src/components/datagrid/data_grid.stories.utils.tsx @@ -12,7 +12,6 @@ import React, { useCallback, useEffect, useState, - forwardRef, FunctionComponent, } from 'react'; import { faker } from '@faker-js/faker'; @@ -27,7 +26,6 @@ import type { EuiDataGridColumnCellActionProps, EuiDataGridColumnSortingConfig, EuiDataGridProps, - EuiDataGridRefProps, EuiDataGridStyle, EuiDataGridRowHeightsOptions, EuiDataGridToolBarVisibilityOptions, @@ -293,10 +291,7 @@ export const defaultStorybookArgs = { } as const, }; -export const StatefulDataGrid = forwardRef< - EuiDataGridRefProps, - EuiDataGridProps ->((props, ref) => { +export const StatefulDataGrid = (props: EuiDataGridProps) => { const { pagination, sorting, columnVisibility, ...rest } = props; // Pagination @@ -360,7 +355,6 @@ export const StatefulDataGrid = forwardRef< return ( ); -}); -StatefulDataGrid.displayName = 'StatefulDataGrid'; +}; /* * Components that exist purely for allowing Storybook to parse certain nested diff --git a/packages/eui/src/components/datagrid/data_grid.styles.ts b/packages/eui/src/components/datagrid/data_grid.styles.ts index 2a4b8b3166c..557b76289ae 100644 --- a/packages/eui/src/components/datagrid/data_grid.styles.ts +++ b/packages/eui/src/components/datagrid/data_grid.styles.ts @@ -87,6 +87,13 @@ export const euiDataGridStyles = (euiThemeContext: UseEuiTheme) => { `${cellPadding[size]} solid transparent` )} } + + /* Ensure the column actions button maintains its size for accessible click/tap targeting + * (see https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html) + * while not increasing the height of the header row at compact sizes. */ + .euiDataGridHeaderCell__button { + margin-block: -${cellPadding[size]}; + } `, get s() { return css(this.cellPadding('s')); diff --git a/packages/eui/src/components/datagrid/data_grid.test.tsx b/packages/eui/src/components/datagrid/data_grid.test.tsx index 5287ff4683c..f233e57f2fa 100644 --- a/packages/eui/src/components/datagrid/data_grid.test.tsx +++ b/packages/eui/src/components/datagrid/data_grid.test.tsx @@ -51,9 +51,7 @@ function extractGridData(datagrid: ReactWrapper) { const headerCells = findTestSubject(datagrid, 'dataGridHeaderCell', '|='); const headerRow: string[] = []; headerCells.forEach((cell: any) => - headerRow.push( - cell.find('[className~="euiDataGridHeaderCell__content"]').text() - ) + headerRow.push(cell.find('div.euiDataGridHeaderCell__content').text()) ); rows.push(headerRow); diff --git a/packages/eui/src/components/datagrid/data_grid_types.ts b/packages/eui/src/components/datagrid/data_grid_types.ts index eec39af4683..64419e06d29 100644 --- a/packages/eui/src/components/datagrid/data_grid_types.ts +++ b/packages/eui/src/components/datagrid/data_grid_types.ts @@ -546,7 +546,10 @@ interface SharedRenderCellElementProps { } export type EuiDataGridSetCellProps = CommonProps & - HTMLAttributes & { + Omit< + HTMLAttributes, + 'role' | 'tabIndex' | 'aria-rowindex' + > & { isExpandable?: boolean; }; diff --git a/packages/eui/src/components/datagrid/utils/row_heights.ts b/packages/eui/src/components/datagrid/utils/row_heights.ts index 8e0acacf48b..6d898a0b98d 100644 --- a/packages/eui/src/components/datagrid/utils/row_heights.ts +++ b/packages/eui/src/components/datagrid/utils/row_heights.ts @@ -32,8 +32,8 @@ import { import { euiDataGridVariables } from '../data_grid.styles'; import { DataGridSortedContext } from './sorting'; -export const AUTO_HEIGHT = 'auto'; -export const DEFAULT_ROW_HEIGHT = 34; +const AUTO_HEIGHT = 'auto'; +const DEFAULT_ROW_HEIGHT = 34; export type RowHeightUtilsType = RowHeightUtils | RowHeightVirtualizationUtils; diff --git a/packages/eui/src/components/index.scss b/packages/eui/src/components/index.scss deleted file mode 100644 index 276f366acfd..00000000000 --- a/packages/eui/src/components/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Components - -@import 'datagrid/index'; diff --git a/packages/eui/src/themes/amsterdam/theme_dark.scss b/packages/eui/src/themes/amsterdam/theme_dark.scss index b18ac759e36..a578e60d010 100644 --- a/packages/eui/src/themes/amsterdam/theme_dark.scss +++ b/packages/eui/src/themes/amsterdam/theme_dark.scss @@ -3,6 +3,3 @@ // Global styling @import './global_styling/index'; - -// Components -@import '../../components/index'; diff --git a/packages/eui/src/themes/amsterdam/theme_light.scss b/packages/eui/src/themes/amsterdam/theme_light.scss index 504e8bc600a..0b499f9e37d 100644 --- a/packages/eui/src/themes/amsterdam/theme_light.scss +++ b/packages/eui/src/themes/amsterdam/theme_light.scss @@ -3,6 +3,3 @@ // Global styling @import './global_styling/index'; - -// Components -@import '../../components/index';