diff --git a/packages/eui-theme-borealis/changelogs/upcoming/8834.md b/packages/eui-theme-borealis/changelogs/upcoming/8834.md new file mode 100644 index 00000000000..e9251fc05ea --- /dev/null +++ b/packages/eui-theme-borealis/changelogs/upcoming/8834.md @@ -0,0 +1,9 @@ +- Added component tokens: + - `dataGridRowBackgroundMarked` + - `dataGridRowBackgroundMarkedHover` + - `dataGridRowBorderActive` + - `dataGridRowBorderHover` + - `dataGridRowBorderMarked` + - `tableRowBackgroundMarked` + - `tableRowBackgroundMarkedHover` + diff --git a/packages/eui-theme-borealis/src/variables/_components.ts b/packages/eui-theme-borealis/src/variables/_components.ts index b616a1b08eb..9cf2ca7980a 100644 --- a/packages/eui-theme-borealis/src/variables/_components.ts +++ b/packages/eui-theme-borealis/src/variables/_components.ts @@ -136,6 +136,21 @@ const component_colors: _EuiThemeComponentColors = { backgroundBaseInteractiveSelectHover, ['colors.backgroundBaseInteractiveSelectHover'] ), + dataGridRowBackgroundMarked: SEMANTIC_COLORS.warning10, + dataGridRowBackgroundMarkedHover: SEMANTIC_COLORS.warning20, + + dataGridRowBorderActive: computed( + ([borderStrongPrimary]) => borderStrongPrimary, + ['colors.borderStrongPrimary'] + ), + dataGridRowBorderHover: computed( + ([borderStrongText]) => borderStrongText, + ['colors.borderStrongText'] + ), + dataGridRowBorderMarked: computed( + ([borderStrongWarning]) => borderStrongWarning, + ['colors.borderStrongWarning'] + ), dataGridRowStripesBackground: computed( ([backgroundBasePlain]) => backgroundBasePlain, @@ -332,6 +347,8 @@ const component_colors: _EuiThemeComponentColors = { ([backgroundBaseInteractiveHover]) => backgroundBaseInteractiveHover, ['colors.backgroundBaseInteractiveHover'] ), + tableRowBackgroundMarked: SEMANTIC_COLORS.warning10, + tableRowBackgroundMarkedHover: SEMANTIC_COLORS.warning20, tableCellSortableIconColor: computed( ([backgroundFilledText]) => backgroundFilledText, ['colors.backgroundFilledText'] @@ -364,6 +381,9 @@ export const components: _EuiThemeComponents = { buttonGroupFocusColor: SEMANTIC_COLORS.plainLight, + dataGridRowBackgroundMarked: SEMANTIC_COLORS.warning140, + dataGridRowBackgroundMarkedHover: SEMANTIC_COLORS.warning130, + codeInlineBackground: SEMANTIC_COLORS.shade135, codeInlineColor: SEMANTIC_COLORS.assistance60, codeStringColor: SEMANTIC_COLORS.accent60, @@ -405,5 +425,7 @@ export const components: _EuiThemeComponents = { ([backgroundBaseInteractiveHover]) => backgroundBaseInteractiveHover, ['colors.backgroundBaseInteractiveHover'] ), + tableRowBackgroundMarked: SEMANTIC_COLORS.warning140, + tableRowBackgroundMarkedHover: SEMANTIC_COLORS.warning130, }, }; diff --git a/packages/eui-theme-common/changelogs/upcoming/8834.md b/packages/eui-theme-common/changelogs/upcoming/8834.md new file mode 100644 index 00000000000..c5ed1f49786 --- /dev/null +++ b/packages/eui-theme-common/changelogs/upcoming/8834.md @@ -0,0 +1,9 @@ +- Added component token types: + - `dataGridRowBackgroundMarked` + - `dataGridRowBackgroundMarkedHover` + - `dataGridRowBorderActive` + - `dataGridRowBorderHover` + - `dataGridRowBorderMarked` + - `tableRowBackgroundMarked` + - `tableRowBackgroundMarkedHover` + diff --git a/packages/eui-theme-common/src/global_styling/variables/components.ts b/packages/eui-theme-common/src/global_styling/variables/components.ts index 855693a1e99..cf8ef16ec52 100644 --- a/packages/eui-theme-common/src/global_styling/variables/components.ts +++ b/packages/eui-theme-common/src/global_styling/variables/components.ts @@ -64,6 +64,11 @@ export type _EuiThemeComponentColors = { dataGridRowBackgroundHover: ColorModeSwitch; dataGridRowBackgroundSelect: ColorModeSwitch; dataGridRowBackgroundSelectHover: ColorModeSwitch; + dataGridRowBackgroundMarked: ColorModeSwitch; + dataGridRowBackgroundMarkedHover: ColorModeSwitch; + dataGridRowBorderActive: ColorModeSwitch; + dataGridRowBorderHover: ColorModeSwitch; + dataGridRowBorderMarked: ColorModeSwitch; dataGridRowStripesBackground: ColorModeSwitch; dataGridRowStripesBackgroundHover: ColorModeSwitch; dataGridRowStripesBackgroundStriped: ColorModeSwitch; @@ -136,6 +141,8 @@ export type _EuiThemeComponentColors = { tableRowBackgroundSelectedHover: ColorModeSwitch; tableRowInteractiveBackgroundHover: ColorModeSwitch; tableRowInteractiveBackgroundFocus: ColorModeSwitch; + tableRowBackgroundMarked: ColorModeSwitch; + tableRowBackgroundMarkedHover: ColorModeSwitch; tableCellSortableIconColor: ColorModeSwitch; tooltipBackground: ColorModeSwitch; diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Marked_Row.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Marked_Row.png new file mode 100644 index 00000000000..726375f2ce9 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiBasicTable_Marked_Row.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Marked_Row.png b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Marked_Row.png new file mode 100644 index 00000000000..764032f00be Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Tabular_Content_EuiDataGrid_gridStyle_prop_Marked_Row.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Marked_Row.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Marked_Row.png new file mode 100644 index 00000000000..eec9714cbfc Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiBasicTable_Marked_Row.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Marked_Row.png b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Marked_Row.png new file mode 100644 index 00000000000..d721361b752 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Tabular_Content_EuiDataGrid_gridStyle_prop_Marked_Row.png differ diff --git a/packages/eui/changelogs/upcoming/8834.md b/packages/eui/changelogs/upcoming/8834.md new file mode 100644 index 00000000000..cd52317d1bb --- /dev/null +++ b/packages/eui/changelogs/upcoming/8834.md @@ -0,0 +1,10 @@ +- Added marked row styling via the classes `euiDataGridRow--marked` and `euiTableRow--marked` for `EuiDataGrid` and `EuiBasicTable` +- Added component tokens: + - `dataGridRowBackgroundMarked` + - `dataGridRowBackgroundMarkedHover` + - `dataGridRowBorderActive` + - `dataGridRowBorderHover` + - `dataGridRowBorderMarked` + - `tableRowBackgroundMarked` + - `tableRowBackgroundMarkedHover` + diff --git a/packages/eui/src/components/basic_table/basic_table.stories.tsx b/packages/eui/src/components/basic_table/basic_table.stories.tsx index 1bad6a57ad5..b0ebfdae184 100644 --- a/packages/eui/src/components/basic_table/basic_table.stories.tsx +++ b/packages/eui/src/components/basic_table/basic_table.stories.tsx @@ -76,7 +76,7 @@ for (let i = 0; i < 5; i++) { id: i + 1, firstName: faker.person.firstName(), lastName: faker.person.lastName(), - online: faker.datatype.boolean(), + online: i === 0 ? true : faker.datatype.boolean(), location: { city: faker.location.city(), country: faker.location.country(), @@ -248,6 +248,29 @@ export const Playground: Story = { render: (args: EuiBasicTableProps) => , }; +export const MarkedRow: Story = { + ...Playground, + parameters: { + codeSnippet: { + snippet: ` + ({ + className: user.online ? 'euiTableRow--marked' : '', + })} />`, + }, + controls: { + include: ['rowProps', 'columns', 'items'], + }, + }, + args: { + ...Playground.args, + rowProps: (user: User) => ({ + className: user.online ? 'euiTableRow--marked' : '', + onClick: () => {}, + }), + }, + render: (args: EuiBasicTableProps) => , +}; + export const ExpandedRow: Story = { parameters: { controls: { 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 6043eb0799a..90bf5ad2e4b 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 @@ -16,8 +16,9 @@ import { } from '../../../../global_styling'; export const euiDataGridCellOutlineStyles = ({ euiTheme }: UseEuiTheme) => { - const focusColor = euiTheme.colors.borderStrongPrimary; - const hoverColor = euiTheme.colors.borderStrongText; + const focusColor = euiTheme.components.dataGridRowBorderActive; + const hoverColor = euiTheme.components.dataGridRowBorderHover; + const markedColor = euiTheme.components.dataGridRowBorderMarked; const outlineWidth = euiTheme.border.width.thick; const borderRadius = mathWithUnits( euiTheme.border.radius.medium, @@ -53,6 +54,18 @@ export const euiDataGridCellOutlineStyles = ({ euiTheme }: UseEuiTheme) => { border-color: ${hoverColor}; } `, + markedColor, + // marked styles are used in `data_grid.styles.ts` + markedStyles: ` + &::after { + border-color: ${markedColor}; + } + + .euiDataGridRowCell__actions { + background-color: ${markedColor}; + border-color: ${markedColor}; + } + `, }; }; @@ -84,6 +97,7 @@ export const euiDataGridCellOutlineSelectors = (parentSelector = '&') => { show: is(selectors(hover, focus, isOpen, isEntered)), hover: hoverNot(selectors(focus, focusWithin, isOpen)), focusTrapped: _(isEntered), + marked: is(selectors(focus, isOpen)), }, actions: { diff --git a/packages/eui/src/components/datagrid/data_grid.styles.ts b/packages/eui/src/components/datagrid/data_grid.styles.ts index 8da6f8096f4..d263e8e23f7 100644 --- a/packages/eui/src/components/datagrid/data_grid.styles.ts +++ b/packages/eui/src/components/datagrid/data_grid.styles.ts @@ -16,6 +16,10 @@ import { mathWithUnits, } from '../../global_styling'; import { highContrastModeStyles } from '../../global_styling/functions/high_contrast'; +import { + euiDataGridCellOutlineSelectors, + euiDataGridCellOutlineStyles, +} from './body/cell/data_grid_cell.styles'; export const euiDataGridVariables = (euiThemeContext: UseEuiTheme) => { const { euiTheme } = euiThemeContext; @@ -47,6 +51,7 @@ export const euiDataGridStyles = (euiThemeContext: UseEuiTheme) => { const { euiTheme, highContrastMode } = euiThemeContext; const { cellPadding, lineHeight, fontSize } = euiDataGridVariables(euiThemeContext); + const { outline: outlineSelectors } = euiDataGridCellOutlineSelectors(); const borderColors = { default: highContrastMode @@ -79,13 +84,30 @@ export const euiDataGridStyles = (euiThemeContext: UseEuiTheme) => { background-color: ${euiTheme.components.dataGridRowBackgroundHover}; } - /* The euiDataGridRow--selected class is not used internally, - * it's there for convenience, to be used by consumers */ + /* The euiDataGridRow--selected and euiDataGridRow--marked classes are not used internally, + * they're there for convenience, to be used by consumers */ *:where(& .euiDataGridRow--selected) { background-color: ${euiTheme.components.dataGridRowBackgroundSelect}; } + *:where(& .euiDataGridRow--marked) { + background-color: ${euiTheme.components.dataGridRowBackgroundMarked}; + + &:hover { + background-color: ${euiTheme.components + .dataGridRowBackgroundMarkedHover}; + } + + /* class duplication to ensure default styles are overriden based on specificity + (marked styles are defined here instead of the cell styles due to the scope of the marked row class) */ + .euiDataGridRowCell.euiDataGridRowCell { + ${outlineSelectors.marked} { + ${euiDataGridCellOutlineStyles(euiThemeContext).markedStyles} + } + } + } + *:where( &.euiDataGrid--rowHoverHighlight .euiDataGridRow--selected:hover ) { diff --git a/packages/eui/src/components/datagrid/data_grid_styles.stories.tsx b/packages/eui/src/components/datagrid/data_grid_styles.stories.tsx index cb6efe9826a..f466b43c338 100644 --- a/packages/eui/src/components/datagrid/data_grid_styles.stories.tsx +++ b/packages/eui/src/components/datagrid/data_grid_styles.stories.tsx @@ -62,6 +62,32 @@ export const Playground: Story = { }; enableFunctionToggleControls(Playground, ['onChange']); +export const MarkedRow: Story = { + parameters: { + codeSnippet: { + snippet: ` + + `, + }, + controls: { sort: 'none' }, + }, + args: { + ...Playground.args, + stripes: false, + rowClasses: { + 1: 'euiDataGridRow--marked', + }, + }, + render: (gridStyle: EuiDataGridStyle) => ( + + ), +}; +enableFunctionToggleControls(MarkedRow, ['onChange']); + /** * VRT only */ diff --git a/packages/eui/src/components/table/table_row.styles.ts b/packages/eui/src/components/table/table_row.styles.ts index c31fd346f83..a8919694ee5 100644 --- a/packages/eui/src/components/table/table_row.styles.ts +++ b/packages/eui/src/components/table/table_row.styles.ts @@ -24,14 +24,26 @@ export const euiTableRowStyles = (euiThemeContext: UseEuiTheme) => { const { cellContentPadding, mobileSizes, checkboxSize } = euiTableVariables(euiThemeContext); + const markedStyles = ` + :where(&.euiTableRow--marked):hover { + background-color: ${rowColors.marked.hover}; + } + `; + return { - euiTableRow: css``, + euiTableRow: css` + :where(&.euiTableRow--marked) { + background-color: ${rowColors.marked.background}; + } + `, desktop: { desktop: css` &:hover { background-color: ${rowColors.hover}; } + + ${markedStyles} `, expanded: css` background-color: ${rowColors.hover}; @@ -43,6 +55,8 @@ export const euiTableRowStyles = (euiThemeContext: UseEuiTheme) => { cursor: pointer; } + ${markedStyles} + &:focus { background-color: ${rowColors.clickable.focus}; } @@ -50,7 +64,7 @@ export const euiTableRowStyles = (euiThemeContext: UseEuiTheme) => { selected: css` &, & + .euiTableRow-isExpandedRow { - background-color: ${rowColors.selected.color}; + background-color: ${rowColors.selected.background}; } &:hover, @@ -96,7 +110,7 @@ export const euiTableRowStyles = (euiThemeContext: UseEuiTheme) => { selected: css` &, & + .euiTableRow-isExpandedRow { - background-color: ${rowColors.selected.color}; + background-color: ${rowColors.selected.background}; } `, /** @@ -179,11 +193,15 @@ const _expandedRowAnimation = ({ euiTheme }: UseEuiTheme) => { const _rowColorVariables = ({ euiTheme }: UseEuiTheme) => ({ hover: euiTheme.components.tableRowBackgroundHover, selected: { - color: euiTheme.components.tableRowBackgroundSelected, + background: euiTheme.components.tableRowBackgroundSelected, hover: euiTheme.components.tableRowBackgroundSelectedHover, }, clickable: { hover: euiTheme.components.tableRowInteractiveBackgroundHover, focus: euiTheme.components.tableRowInteractiveBackgroundFocus, }, + marked: { + background: euiTheme.components.tableRowBackgroundMarked, + hover: euiTheme.components.tableRowBackgroundMarkedHover, + }, }); diff --git a/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts b/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts index e65420f8b89..d4ac080a3bd 100644 --- a/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts +++ b/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts @@ -191,6 +191,26 @@ const component_colors: _EuiThemeComponentColors = { ([highlight]) => highlight, ['colors.highlight'] ), + dataGridRowBackgroundMarked: computed( + ([highlight]) => highlight, + ['colors.highlight'] + ), + dataGridRowBackgroundMarkedHover: computed( + ([highlight]) => highlight, + ['colors.highlight'] + ), + dataGridRowBorderActive: computed( + ([borderStrongPrimary]) => borderStrongPrimary, + ['colors.borderStrongPrimary'] + ), + dataGridRowBorderHover: computed( + ([borderStrongText]) => borderStrongText, + ['colors.borderStrongText'] + ), + dataGridRowBorderMarked: computed( + ([borderStrongPrimary]) => borderStrongPrimary, + ['colors.borderStrongPrimary'] + ), dataGridRowStripesBackground: computed( ([emptyShade]) => emptyShade, ['colors.emptyShade'] @@ -404,6 +424,14 @@ const component_colors: _EuiThemeComponentColors = { ([primary]) => transparentize(primary, 0.1), ['colors.primary'] ), + tableRowBackgroundMarked: computed( + ([highlight]) => highlight, + ['colors.highlight'] + ), + tableRowBackgroundMarkedHover: computed( + ([highlight]) => highlight, + ['colors.highlight'] + ), tableCellSortableIconColor: computed( ([emptyShade, subduedText]) => { const color = tint(subduedText, 0.9); diff --git a/packages/website/docs/components/data-grid/style-and-display.mdx b/packages/website/docs/components/data-grid/style-and-display.mdx index 79edf7e1dbf..f74c4309317 100644 --- a/packages/website/docs/components/data-grid/style-and-display.mdx +++ b/packages/website/docs/components/data-grid/style-and-display.mdx @@ -66,8 +66,9 @@ const data = Array.from({ length: 5 }).map(() => ({ account: faker.finance.accountNumber(), })); -const DEMO_ROW = 2; -data[DEMO_ROW].account = 'OVERDUE'; +const MARKED_ROW = 2; +const SELECTED_ROW = 3; +data[MARKED_ROW].account = 'OVERDUE'; /** * Selection @@ -179,10 +180,6 @@ export default () => { const rowStyles = useMemo(() => { return css` - .euiDataGridRow--rowClassesDemo { - background-color: ${euiTheme.colors.backgroundLightWarning}; - } - .euiDataGridRow--rowClassesDemoSelected { background-color: ${euiTheme.colors.backgroundBaseInteractiveSelect}; @@ -195,7 +192,8 @@ export default () => { const rowClasses = useMemo(() => { const rowClasses = { - [DEMO_ROW]: 'euiDataGridRow--rowClassesDemo', + [MARKED_ROW]: 'euiDataGridRow--marked', + [SELECTED_ROW]: 'euiDataGridRow--selected', }; rowSelection[0].forEach((rowIndex) => { rowClasses[rowIndex] = 'euiDataGridRow--rowClassesDemoSelected'; @@ -223,9 +221,10 @@ export default () => { }; ``` -:::tip A `.euiDataGridRow--selected` CSS class is available +:::tip There are styling utility classes available: `euiDataGridRow--marked` and `euiDataGridRow--selected` -It can be useful to style selected rows when using the `gridStyle.rowClasses` prop. It also provides styles for the hover state, when `rowHover` equals `highlight`. +These classes can be useful to highlight rows either as selected or marked (highlighted). Use the `gridStyle.rowClasses` prop to add the classes to specific rows. +Both utility classes additionally define hover styles when `rowHover` equals `highlight`. ::: diff --git a/packages/website/docs/components/tables/basic.mdx b/packages/website/docs/components/tables/basic.mdx index 2fb7a52fc97..9d77fe502c8 100644 --- a/packages/website/docs/components/tables/basic.mdx +++ b/packages/website/docs/components/tables/basic.mdx @@ -662,6 +662,154 @@ export default () => { }; ``` +## Table row classes + +import { Example } from '@site/src/components'; + +Specific rows can be highlighted or otherwise have custom styling passed to them via the `rowProps` prop. +It provides the current row data item as argument which can be used to add classes conditionally. + + + ```tsx + ({ + className: user.online ? 'euiTableRow--marked' : '', + })} /> + ``` + + +:::tip There is a styling utility class available: `euiTableRow--marked` + +This class can be used to highlight rows. Use the `rowProps.className` prop to add the class to specific rows. +The utility class additionally defines hover styles. + +::: + +```tsx interactive +import React from 'react'; +import { + formatDate, + EuiBasicTable, + EuiBasicTableColumn, + EuiTableFieldDataColumnType, + EuiLink, + EuiHealth, +} from '@elastic/eui'; +import { faker } from '@faker-js/faker'; + +type User = { + id: string; + firstName: string | null | undefined; + lastName: string; + github: string; + dateOfBirth: Date; + online: boolean; +}; + +const users: User[] = []; + +for (let i = 0; i < 5; i++) { + users.push({ + id: faker.string.uuid(), + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + github: faker.internet.userName(), + dateOfBirth: faker.date.past(), + online: i === 0 ? true : faker.datatype.boolean(), + }); +} + +export default () => { + const columns: Array> = [ + { + field: 'firstName', + name: 'First Name', + 'data-test-subj': 'firstNameCell', + mobileOptions: { + render: (user: User) => ( + <> + {user.firstName} {user.lastName} + + ), + header: false, + truncateText: false, + enlarge: true, + width: '100%', + }, + }, + { + field: 'lastName', + name: 'Last Name', + truncateText: true, + mobileOptions: { + show: false, + }, + }, + { + field: 'github', + name: 'Github', + nameTooltip: { + content: 'Their mascot is the Octokitty' + }, + render: (username: User['github']) => ( + + {username} + + ), + truncateText: true, + }, + { + field: 'dateOfBirth', + name: 'Date of Birth', + dataType: 'date', + render: (dateOfBirth: User['dateOfBirth']) => + formatDate(dateOfBirth, 'dobShort'), + }, + { + field: 'online', + name: 'Online', + dataType: 'boolean', + render: (online: User['online']) => { + const color = online ? 'success' : 'danger'; + const label = online ? 'Online' : 'Offline'; + return {label}; + }, + }, + ]; + + const getRowProps = (user: User) => { + const { id } = user; + return { + className: user.online ? 'euiTableRow--marked' : '' + }; + }; + + const getCellProps = ( + user: User, + column: EuiTableFieldDataColumnType + ) => { + const { id } = user; + const { field } = column; + return { + className: 'customCellClass', + 'data-test-subj': `cell-${id}-${String(field)}`, + textOnly: true, + }; + }; + + return ( + + ); +}; +``` + ## Adding a footer to a table The following example shows how to add a footer to your table by adding `footer` to your column definitions. If one or more of your columns contains a `footer` definition, the footer area will be visible. By default, columns with no footer specified (undefined) will render an empty cell to preserve the table layout. Check out the [custom tables](./custom.mdx) page for more examples of how you can work with table footers in EUI.