diff --git a/changelogs/upcoming/7641.md b/changelogs/upcoming/7641.md new file mode 100644 index 00000000000..7a007a7b5b9 --- /dev/null +++ b/changelogs/upcoming/7641.md @@ -0,0 +1,3 @@ +**DOM changes** + +- `EuiTableRowCell`s with `textOnly` set to `false` will no longer attempt to apply the `.euiTableCellContent__text` className to child elements. diff --git a/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap b/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap index 177c5f5a488..32cae740486 100644 --- a/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap +++ b/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap @@ -1,5 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`EuiBasicTable actions custom item actions 1`] = ` + + +
+
+ +
+
+ + +`; + exports[`EuiBasicTable renders (bare-bones) 1`] = `
- Name @@ -36,7 +60,7 @@ exports[`EuiBasicTable renders (bare-bones) 1`] = ` > description - +
@@ -55,7 +79,7 @@ exports[`EuiBasicTable renders (bare-bones) 1`] = ` Name
- Name @@ -182,7 +206,7 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin class="euiTableSortIcon" data-euiicon-type="sortUp" /> - +
- ID @@ -205,7 +229,7 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin > your id - +
- Age @@ -227,23 +251,23 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin > your age - +
- Actions - + @@ -285,7 +309,7 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin Name
NAME1
@@ -299,7 +323,7 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin ID
NAME2
@@ -420,7 +444,7 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin ID
NAME3
@@ -541,7 +565,7 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin ID
- Name @@ -184,7 +184,7 @@ exports[`EuiInMemoryTable empty array 1`] = ` > description - +
@@ -199,7 +199,7 @@ exports[`EuiInMemoryTable empty array 1`] = ` colspan="1" >
- Name @@ -276,7 +276,7 @@ exports[`EuiInMemoryTable with items 1`] = ` > description - +
@@ -295,7 +295,7 @@ exports[`EuiInMemoryTable with items 1`] = ` Name
{ // Numbers should be right aligned expect( - container.querySelectorAll('.euiTableCellContent--alignRight') + container.querySelectorAll('[class*="euiTableCellContent-right"]') ).toHaveLength(3); // Booleans should output as Yes or No @@ -704,7 +704,6 @@ describe('EuiBasicTable', () => { const props: EuiBasicTableProps = { items: basicItems, columns: [ - ...basicColumns, { name: 'Actions', actions: [ @@ -729,9 +728,21 @@ describe('EuiBasicTable', () => { expect(queryByTestSubject('customAction-2')).toBeInTheDocument(); expect(queryByTestSubject('customAction-3')).not.toBeInTheDocument(); + // TODO: These assertions should ideally be visual regression snapshots instead expect( container.querySelector('.euiTableRowCell--hasActions')!.className ).toContain('-customActions'); + expect( + container.querySelector( + '.euiTableRowCell--hasActions .euiTableCellContent' + )!.className + ).not.toContain('-actions-mobile'); + expect( + container.querySelector('.euiTableRow-hasActions')!.className + ).not.toContain('-hasRightColumn'); + expect( + container.querySelector('.euiTableRow-hasActions') + ).toMatchSnapshot(); }); describe('are disabled on selection', () => { diff --git a/src/components/basic_table/basic_table.tsx b/src/components/basic_table/basic_table.tsx index 2b060f15831..b486d9409db 100644 --- a/src/components/basic_table/basic_table.tsx +++ b/src/components/basic_table/basic_table.tsx @@ -987,18 +987,26 @@ export class EuiBasicTable extends Component< rowSelectionDisabled = !!isDisabled; } - let hasActions; + let hasActions: 'custom' | boolean = false; columns.forEach((column: EuiBasicTableColumn, columnIndex: number) => { - if ((column as EuiTableActionsColumnType).actions) { + const columnActions = (column as EuiTableActionsColumnType).actions; + + if (columnActions) { + const hasCustomActions = columnActions.some( + (action) => !!(action as CustomItemAction).render + ); cells.push( this.renderItemActionsCell( itemId, item, column as EuiTableActionsColumnType, - columnIndex + columnIndex, + hasCustomActions ) ); - hasActions = true; + // A table theoretically could have both custom and default action items + // If it has both, default action mobile row styles take precedence over custom + hasActions = !hasActions && hasCustomActions ? 'custom' : true; } else if ((column as EuiTableFieldDataColumnType).field) { const fieldDataColumn = column as EuiTableFieldDataColumnType; cells.push( @@ -1122,15 +1130,12 @@ export class EuiBasicTable extends Component< itemId: ItemIdResolved, item: T, column: EuiTableActionsColumnType, - columnIndex: number + columnIndex: number, + hasCustomActions: boolean ) { // Disable all actions if any row(s) are selected const allDisabled = this.state.selection.length > 0; - const hasCustomActions = column.actions.some( - (action: Action) => !!(action as CustomItemAction).render - ); - let actualActions = column.actions.filter( (action: Action) => !action.available || action.available(item) ); diff --git a/src/components/basic_table/in_memory_table.test.tsx b/src/components/basic_table/in_memory_table.test.tsx index be4af72f428..e4f8d89dc91 100644 --- a/src/components/basic_table/in_memory_table.test.tsx +++ b/src/components/basic_table/in_memory_table.test.tsx @@ -1438,7 +1438,7 @@ describe('EuiInMemoryTable', () => { const columns = [{ field: 'title', name: 'Title' }]; const query = Query.parse('baz'); - const component = mount( + const { container } = render( { /> ); - const tableContent = component.find( + const tableContent = container.querySelectorAll( '.euiTableRowCell .euiTableCellContent' ); expect(tableContent.length).toBe(1); // only 1 match - expect(tableContent.at(0).text()).toBe('baz'); + expect(tableContent[0]).toHaveTextContent('baz'); }); it('does not execute the Query and renders the items passed as is', () => { @@ -1460,7 +1460,7 @@ describe('EuiInMemoryTable', () => { const columns = [{ field: 'title', name: 'Title' }]; const query = Query.parse('baz'); - const component = mount( + const { container } = render( { /> ); - const tableContent = component.find( + const tableContent = container.querySelectorAll( '.euiTableRowCell .euiTableCellContent' ); expect(tableContent.length).toBe(3); - expect(tableContent.at(0).text()).toBe('foo'); - expect(tableContent.at(1).text()).toBe('bar'); - expect(tableContent.at(2).text()).toBe('baz'); + expect(tableContent[0]).toHaveTextContent('foo'); + expect(tableContent[1]).toHaveTextContent('bar'); + expect(tableContent[2]).toHaveTextContent('baz'); }); }); diff --git a/src/components/basic_table/table_types.ts b/src/components/basic_table/table_types.ts index 60856e1e2ee..657687ab9e6 100644 --- a/src/components/basic_table/table_types.ts +++ b/src/components/basic_table/table_types.ts @@ -121,9 +121,12 @@ export interface EuiTableComputedColumnType */ width?: string; /** - * Indicates whether this column should truncate its content when it doesn't fit + * Indicates whether this column should truncate overflowing text content. + * - Set to `true` to enable single-line truncation. + * - To enable multi-line truncation, use a configuration object with `lines` + * set to a number of lines to truncate to. */ - truncateText?: boolean; + truncateText?: EuiTableRowCellProps['truncateText']; isExpander?: boolean; align?: HorizontalAlignment; /** diff --git a/src/components/index.scss b/src/components/index.scss index dcfd6a3c324..79063613970 100644 --- a/src/components/index.scss +++ b/src/components/index.scss @@ -7,4 +7,3 @@ @import 'form/index'; @import 'markdown_editor/index'; @import 'selectable/index'; -@import 'table/index'; diff --git a/src/components/table/__snapshots__/table.test.tsx.snap b/src/components/table/__snapshots__/table.test.tsx.snap index 537abe6e504..afca98fa5d7 100644 --- a/src/components/table/__snapshots__/table.test.tsx.snap +++ b/src/components/table/__snapshots__/table.test.tsx.snap @@ -14,32 +14,32 @@ exports[`EuiTable renders 1`] = ` role="columnheader" scope="col" > - Hi Title - +
- Bye Title - +
@@ -51,7 +51,7 @@ exports[`EuiTable renders 1`] = ` class="euiTableRowCell emotion-euiTableRowCell-middle-desktop" >
- - +
@@ -29,13 +29,13 @@ exports[`align renders center when specified 1`] = ` class="euiTableHeaderCell emotion-euiTableHeaderCell" role="columnheader" > - - +
@@ -50,13 +50,13 @@ exports[`align renders right when specified 1`] = ` class="euiTableHeaderCell emotion-euiTableHeaderCell" role="columnheader" > - - +
@@ -74,16 +74,16 @@ exports[`renders EuiTableHeaderCell 1`] = ` role="columnheader" scope="col" > - children - +
@@ -100,13 +100,13 @@ exports[`renders td when children is null/undefined 1`] = ` data-test-subj="test subject string" role="columnheader" > - - +
@@ -124,11 +124,11 @@ exports[`sorting does not render a button with readOnly 1`] = ` role="columnheader" scope="col" > - Test @@ -137,7 +137,7 @@ exports[`sorting does not render a button with readOnly 1`] = ` class="euiTableSortIcon" data-euiicon-type="sortDown" /> - +
@@ -155,11 +155,11 @@ exports[`sorting is rendered with isSortAscending 1`] = ` role="columnheader" scope="col" > - Test @@ -168,7 +168,7 @@ exports[`sorting is rendered with isSortAscending 1`] = ` class="euiTableSortIcon" data-euiicon-type="sortUp" /> - +
@@ -186,11 +186,11 @@ exports[`sorting is rendered with isSorted 1`] = ` role="columnheader" scope="col" > - Test @@ -199,7 +199,7 @@ exports[`sorting is rendered with isSorted 1`] = ` class="euiTableSortIcon" data-euiicon-type="sortDown" /> - +
@@ -222,11 +222,11 @@ exports[`sorting renders a button with onSort 1`] = ` data-test-subj="tableHeaderSortButton" type="button" > - Test @@ -235,7 +235,7 @@ exports[`sorting renders a button with onSort 1`] = ` class="euiTableSortIcon" data-euiicon-type="sortDown" /> - +
@@ -253,16 +253,16 @@ exports[`width and style accepts style attribute 1`] = ` scope="col" style="width: 20%;" > - Test - +
@@ -279,16 +279,16 @@ exports[`width and style accepts width attribute 1`] = ` scope="col" style="width: 10%;" > - Test - +
@@ -305,16 +305,16 @@ exports[`width and style accepts width attribute as number 1`] = ` scope="col" style="width: 100px;" > - Test - +
@@ -331,16 +331,16 @@ exports[`width and style resolves style and width attribute 1`] = ` scope="col" style="width: 10%;" > - Test - +
diff --git a/src/components/table/__snapshots__/table_row.test.tsx.snap b/src/components/table/__snapshots__/table_row.test.tsx.snap index 85309f355bd..98b0c62380c 100644 --- a/src/components/table/__snapshots__/table_row.test.tsx.snap +++ b/src/components/table/__snapshots__/table_row.test.tsx.snap @@ -10,7 +10,7 @@ exports[`isSelected renders true when specified 1`] = ` class="euiTableRowCell emotion-euiTableRowCell-middle-desktop" >
@@ -148,7 +148,7 @@ exports[`truncateText defaults to false 1`] = ` class="euiTableRowCell emotion-euiTableRowCell-middle-desktop" >
({ + euiTableCellContent: css` + display: flex; + align-items: center; /* Vertically align all children */ + `, + + // Align + left: null, // Default, no CSS needed + right: css` + justify-content: flex-end; + ${logicalTextAlignCSS('right')} + `, + center: css` + justify-content: center; + text-align: center; + `, + + // Text wrapping + truncateText: css` + ${euiTextTruncate()} + + /* Text truncation on flex parents doesn't work - this wrapper + extra CSS is required */ + .euiTableCellContent__text { + overflow: hidden; + text-overflow: ellipsis; + } + `, + wrapText: css` + ${euiTextBreakWord()} + `, + + // Action cells + hasActions: { + actions: css` + gap: ${euiTheme.size.s}; + `, + desktop: css` + flex-wrap: wrap; + `, + mobile: css` + flex-direction: column; + padding: 0; + `, + }, +}); diff --git a/src/components/table/_table_cell_content.tsx b/src/components/table/_table_cell_content.tsx new file mode 100644 index 00000000000..2abc04b5f48 --- /dev/null +++ b/src/components/table/_table_cell_content.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FunctionComponent, HTMLAttributes, useMemo } from 'react'; +import { CommonProps } from '../common'; +import classNames from 'classnames'; + +import { LEFT_ALIGNMENT, useEuiMemoizedStyles } from '../../services'; +import { isObject } from '../../services/predicate'; +import { EuiTextBlockTruncate } from '../text_truncate'; + +import type { EuiTableRowCellProps } from './table_row_cell'; +import { useEuiTableIsResponsive } from './mobile/responsive_context'; +import { euiTableCellContentStyles } from './_table_cell_content.styles'; + +export type EuiTableCellContentProps = CommonProps & + HTMLAttributes & + Pick & { + truncateText?: EuiTableRowCellProps['truncateText'] | null; + }; + +export const EuiTableCellContent: FunctionComponent< + EuiTableCellContentProps +> = ({ + children, + className, + align = LEFT_ALIGNMENT, + textOnly, + truncateText = false, + hasActions, + ...rest +}) => { + const isResponsive = useEuiTableIsResponsive(); + + const styles = useEuiMemoizedStyles(euiTableCellContentStyles); + const cssStyles = [ + styles.euiTableCellContent, + !isResponsive && styles[align], // On mobile, always align cells to the left + truncateText === true && styles.truncateText, + truncateText === false && styles.wrapText, + ...(hasActions + ? [ + styles.hasActions.actions, + !isResponsive && styles.hasActions.desktop, + isResponsive && hasActions !== 'custom' && styles.hasActions.mobile, + ] + : []), + ]; + + const classes = classNames('euiTableCellContent', className); + + const renderedChildren = useMemo(() => { + const textClasses = 'euiTableCellContent__text'; + + if (isObject(truncateText) && truncateText.lines) { + return ( + + {children} + + ); + } + if (textOnly === true || truncateText === true) { + return {children}; + } + return children; + }, [children, textOnly, truncateText]); + + return ( +
+ {renderedChildren} +
+ ); +}; diff --git a/src/components/table/table_cells_shared.styles.ts b/src/components/table/table_cells_shared.styles.ts index 181a8342f70..e8c0bd14071 100644 --- a/src/components/table/table_cells_shared.styles.ts +++ b/src/components/table/table_cells_shared.styles.ts @@ -29,11 +29,10 @@ export const euiTableHeaderFooterCellStyles = ( return { euiTableHeaderCell: css` ${sharedStyles} - - .euiTableCellContent { - /* Spacing between text and sort icon */ - gap: ${euiTheme.size.xs}; - } + `, + euiTableHeaderCell__content: css` + /* Spacing between text and sort icon */ + gap: ${euiTheme.size.xs}; `, euiTableHeaderCell__button: css` ${logicalCSS('width', '100%')} @@ -42,10 +41,7 @@ export const euiTableHeaderFooterCellStyles = ( &:hover, &:focus { color: ${euiTheme.colors.primaryText}; - - .euiTableCellContent__text { - text-decoration: underline; - } + text-decoration: underline; } `, euiTableFooterCell: css` diff --git a/src/components/table/table_footer_cell.tsx b/src/components/table/table_footer_cell.tsx index c7e28d9e688..0273398baa9 100644 --- a/src/components/table/table_footer_cell.tsx +++ b/src/components/table/table_footer_cell.tsx @@ -13,12 +13,11 @@ import { useEuiMemoizedStyles, HorizontalAlignment, LEFT_ALIGNMENT, - RIGHT_ALIGNMENT, - CENTER_ALIGNMENT, } from '../../services'; import { CommonProps } from '../common'; import { resolveWidthAsStyle } from './utils'; +import { EuiTableCellContent } from './_table_cell_content'; import { euiTableHeaderFooterCellStyles } from './table_cells_shared.styles'; export type EuiTableFooterCellProps = CommonProps & @@ -36,10 +35,6 @@ export const EuiTableFooterCell: FunctionComponent = ({ ...rest }) => { const classes = classNames('euiTableFooterCell', className); - const contentClasses = classNames('euiTableCellContent', className, { - 'euiTableCellContent--alignRight': align === RIGHT_ALIGNMENT, - 'euiTableCellContent--alignCenter': align === CENTER_ALIGNMENT, - }); const inlineStyles = resolveWidthAsStyle(style, width); const styles = useEuiMemoizedStyles(euiTableHeaderFooterCellStyles); @@ -50,9 +45,9 @@ export const EuiTableFooterCell: FunctionComponent = ({ style={inlineStyles} {...rest} > -
- {children} -
+ + {children} + ); }; diff --git a/src/components/table/table_header_cell.tsx b/src/components/table/table_header_cell.tsx index ace4d595fe6..90d4fc4ab43 100644 --- a/src/components/table/table_header_cell.tsx +++ b/src/components/table/table_header_cell.tsx @@ -17,8 +17,6 @@ import { useEuiMemoizedStyles, HorizontalAlignment, LEFT_ALIGNMENT, - RIGHT_ALIGNMENT, - CENTER_ALIGNMENT, } from '../../services'; import { EuiI18n } from '../i18n'; import { EuiScreenReaderOnly } from '../accessibility'; @@ -27,6 +25,7 @@ import { EuiIcon } from '../icon'; import { EuiInnerText } from '../inner_text'; import { resolveWidthAsStyle } from './utils'; +import { EuiTableCellContent } from './_table_cell_content'; import { euiTableHeaderFooterCellStyles } from './table_cells_shared.styles'; export type TableHeaderCellScope = 'col' | 'row' | 'colgroup' | 'rowgroup'; @@ -62,13 +61,15 @@ export type EuiTableHeaderCellProps = CommonProps & const CellContents = ({ className, + align, description, children, isSorted, isSortAscending, showSortMsg, }: { - className: string; + className?: string; + align: HorizontalAlignment; description: EuiTableHeaderCellProps['description']; children: EuiTableHeaderCellProps['children']; isSorted: EuiTableHeaderCellProps['isSorted']; @@ -76,7 +77,12 @@ const CellContents = ({ showSortMsg: boolean; }) => { return ( - + {(ref, innerText) => ( {children} @@ -108,7 +114,7 @@ const CellContents = ({ size="m" /> )} -
+ ); }; @@ -136,16 +142,24 @@ export const EuiTableHeaderCell: FunctionComponent = ({ 'euiTableHeaderCell--hideForMobile': !mobileOptions.show, }); - const contentClasses = classNames('euiTableCellContent', className, { - 'euiTableCellContent--alignRight': align === RIGHT_ALIGNMENT, - 'euiTableCellContent--alignCenter': align === CENTER_ALIGNMENT, - }); - const inlineStyles = resolveWidthAsStyle(style, width); const CellComponent = children ? 'th' : 'td'; const cellScope = CellComponent === 'th' ? scope ?? 'col' : undefined; // `scope` is only valid on `th` elements + const cellContents = ( + + {children} + + ); + if (onSort || isSorted) { const buttonClasses = classNames('euiTableHeaderButton', { 'euiTableHeaderButton-isSorted': isSorted, @@ -156,17 +170,6 @@ export const EuiTableHeaderCell: FunctionComponent = ({ ariaSortValue = isSortAscending ? 'ascending' : 'descending'; } - const cellContents = ( - - ); - return ( = ({ style={inlineStyles} {...rest} > - + {cellContents} ); }; diff --git a/src/components/table/table_row.tsx b/src/components/table/table_row.tsx index 05830f37924..73d48abf76a 100644 --- a/src/components/table/table_row.tsx +++ b/src/components/table/table_row.tsx @@ -38,7 +38,7 @@ export interface EuiTableRowProps { * Indicates if the table has a dedicated column for actions * (used for mobile styling and desktop action hover behavior) */ - hasActions?: boolean; + hasActions?: boolean | 'custom'; /** * Indicates if the row will have an expanded row */ @@ -75,7 +75,7 @@ export const EuiTableRow: FunctionComponent = ({ styles.mobile.mobile, isSelected && styles.mobile.selected, isExpandedRow && styles.mobile.expanded, - (hasActions || isExpandable || isExpandedRow) && + (hasActions === true || isExpandable || isExpandedRow) && styles.mobile.hasRightColumn, hasSelection && styles.mobile.hasLeftColumn, ] diff --git a/src/components/table/table_row_cell.styles.ts b/src/components/table/table_row_cell.styles.ts index d68b90414ac..6035d504811 100644 --- a/src/components/table/table_row_cell.styles.ts +++ b/src/components/table/table_row_cell.styles.ts @@ -31,13 +31,6 @@ export const euiTableRowCellStyles = (euiThemeContext: UseEuiTheme) => { `, hasActions: css` ${hasIcons} - - /* TODO: Move this to EuiTableCellContent, once we're further along in the Emotion conversion */ - .euiTableCellContent { - display: flex; - align-items: center; - gap: ${euiTheme.size.s}; - } `, // valign @@ -59,11 +52,6 @@ export const euiTableRowCellStyles = (euiThemeContext: UseEuiTheme) => { ${logicalCSS('border-vertical', euiTheme.border.thin)} `, actions: css` - /* TODO: Move this to EuiTableCellContent, once we're further along in the Emotion conversion */ - .euiTableCellContent { - flex-wrap: wrap; - } - .euiBasicTableAction-showOnHover { opacity: 0; transition: opacity ${euiTheme.animation.normal} @@ -91,15 +79,6 @@ export const euiTableRowCellStyles = (euiThemeContext: UseEuiTheme) => { ${logicalCSS('right', 0)} ${logicalCSS('min-width', '0')} ${logicalCSS('width', mobileSizes.actions.width)} - - /* TODO: Move this to EuiTableCellContent, once we're further along in the Emotion conversion */ - .euiTableCellContent { - display: flex; - flex-direction: column; - align-items: center; - gap: ${euiTheme.size.s}; - padding: 0; - } `, get actions() { // Note: Visible-on-hover actions on desktop always show on mobile diff --git a/src/components/table/table_row_cell.test.tsx b/src/components/table/table_row_cell.test.tsx index 8a78c33c3d5..80c39cfcf33 100644 --- a/src/components/table/table_row_cell.test.tsx +++ b/src/components/table/table_row_cell.test.tsx @@ -97,6 +97,9 @@ describe('truncateText', () => { const { container } = renderInTableRow(); expect(container.firstChild).toMatchSnapshot(); + expect( + container.querySelector('.euiTableCellContent')!.className + ).toContain('euiTableCellContent-wrapText'); }); it('renders true', () => { @@ -104,10 +107,10 @@ describe('truncateText', () => { ); - expect( - container.querySelector('.euiTableCellContent--truncateText') - ).toBeInTheDocument(); expect(container.firstChild).toMatchSnapshot(); + expect( + container.querySelector('.euiTableCellContent')!.className + ).toContain('euiTableCellContent-truncateText'); }); test('renders lines configuration', () => { @@ -115,13 +118,10 @@ describe('truncateText', () => { ); - expect( - container.querySelector('.euiTableCellContent--truncateText') - ).not.toBeInTheDocument(); + expect(container.firstChild).toMatchSnapshot(); expect(container.querySelector('.euiTableCellContent__text')).toHaveClass( 'euiTextBlockTruncate' ); - expect(container.firstChild).toMatchSnapshot(); }); }); diff --git a/src/components/table/table_row_cell.tsx b/src/components/table/table_row_cell.tsx index a607e6c92cd..4dc739b4af3 100644 --- a/src/components/table/table_row_cell.tsx +++ b/src/components/table/table_row_cell.tsx @@ -9,10 +9,8 @@ import React, { CSSProperties, FunctionComponent, - ReactElement, ReactNode, TdHTMLAttributes, - useCallback, } from 'react'; import classNames from 'classnames'; @@ -21,14 +19,11 @@ import { useEuiMemoizedStyles, HorizontalAlignment, LEFT_ALIGNMENT, - RIGHT_ALIGNMENT, - CENTER_ALIGNMENT, } from '../../services'; -import { isObject } from '../../services/predicate'; -import { EuiTextBlockTruncate } from '../text_truncate'; import { useEuiTableIsResponsive } from './mobile/responsive_context'; import { resolveWidthAsStyle } from './utils'; +import { EuiTableCellContent } from './_table_cell_content'; import { euiTableRowCellStyles } from './table_row_cell.styles'; interface EuiTableRowCellSharedPropsShape { @@ -39,6 +34,7 @@ interface EuiTableRowCellSharedPropsShape { /** * Creates a text wrapper around cell content that helps word break or truncate * long text correctly. + * @default true */ textOnly?: boolean; /** @@ -46,6 +42,7 @@ interface EuiTableRowCellSharedPropsShape { * - Set to `true` to enable single-line truncation. * - To enable multi-line truncation, use a configuration object with `lines` * set to a number of lines to truncate to. + * @default false */ truncateText?: boolean | { lines: number }; width?: CSSProperties['width']; @@ -152,32 +149,6 @@ export const EuiTableRowCell: FunctionComponent = ({ 'euiTableRowCell--hideForDesktop': mobileOptions.only, }); - const contentClasses = classNames('euiTableCellContent', { - 'euiTableCellContent--alignRight': align === RIGHT_ALIGNMENT, - 'euiTableCellContent--alignCenter': align === CENTER_ALIGNMENT, - 'euiTableCellContent--truncateText': truncateText === true, - // We're doing this rigamarole instead of creating `euiTableCellContent--textOnly` for BWC - // purposes for the time-being. - 'euiTableCellContent--overflowingContent': textOnly !== true, - }); - - const mobileContentClasses = classNames('euiTableCellContent', { - 'euiTableCellContent--alignRight': - mobileOptions.align === RIGHT_ALIGNMENT || align === RIGHT_ALIGNMENT, - 'euiTableCellContent--alignCenter': - mobileOptions.align === CENTER_ALIGNMENT || align === CENTER_ALIGNMENT, - 'euiTableCellContent--truncateText': - mobileOptions.truncateText ?? truncateText, - // We're doing this rigamarole instead of creating `euiTableCellContent--textOnly` for BWC - // purposes for the time-being. - 'euiTableCellContent--overflowingContent': - mobileOptions.textOnly !== true || textOnly !== true, - }); - - const childClasses = classNames({ - euiTableCellContent__text: textOnly === true, - }); - const widthValue = isResponsive ? hasActions || isExpander ? undefined // On mobile, actions are shifted to a right column via CSS @@ -186,37 +157,6 @@ export const EuiTableRowCell: FunctionComponent = ({ const styleObj = resolveWidthAsStyle(style, widthValue); - const modifyChildren = useCallback( - (children: ReactNode) => { - let modifiedChildren = children; - - if (textOnly === true) { - modifiedChildren = {children}; - } else if (React.isValidElement(children)) { - modifiedChildren = React.Children.map(children, (child: ReactElement) => - React.cloneElement(child, { - className: classNames( - (child.props as CommonProps).className, - childClasses - ), - }) - ); - } - if (isObject(truncateText) && truncateText.lines) { - modifiedChildren = ( - - {modifiedChildren} - - ); - } - - return modifiedChildren; - }, - [childClasses, textOnly, truncateText] - ); - - const childrenNode = modifyChildren(children); - const hideForMobileClasses = 'euiTableRowCell--hideForMobile'; const showForMobileClasses = 'euiTableRowCell--hideForDesktop'; @@ -227,13 +167,22 @@ export const EuiTableRowCell: FunctionComponent = ({ css: cssStyles, ...rest, }; + const sharedContentProps = { + align, + textOnly, + truncateText, + hasActions: hasActions || isExpander, + }; + if (mobileOptions.show === false) { return ( -
{childrenNode}
+ + {children} +
); } else { @@ -252,15 +201,25 @@ export const EuiTableRowCell: FunctionComponent = ({ {/* Content depending on mobile render existing */} {mobileOptions.render ? ( <> -
- {modifyChildren(mobileOptions.render)} -
-
- {childrenNode} -
+ + {mobileOptions.render} + + + {children} + ) : ( -
{childrenNode}
+ + {children} + )} );