diff --git a/changelogs/upcoming/7656.md b/changelogs/upcoming/7656.md new file mode 100644 index 00000000000..2beedf5ab7c --- /dev/null +++ b/changelogs/upcoming/7656.md @@ -0,0 +1 @@ +- Updated `EuiTableHeaderCell` to show a subdued `sortable` icon for columns that are not currently sorted but can be 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 4ed70fc8a71..3fa583c6f2a 100644 --- a/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap +++ b/src/components/basic_table/__snapshots__/basic_table.test.tsx.snap @@ -161,7 +161,6 @@ exports[`EuiBasicTable renders (kitchen sink) with pagination, selection, sortin `; -exports[`EuiTableHeaderCell sorting is rendered with isSortAscending 1`] = ` +exports[`EuiTableHeaderCell sorting renders a button with onSort 1`] = ` + + + + + + +
+ +
+`; + +exports[`EuiTableHeaderCell sorting renders a sort arrow upwards with isSortAscending 1`] = `
`; -exports[`EuiTableHeaderCell sorting is rendered with isSorted 1`] = ` +exports[`EuiTableHeaderCell sorting renders a sort arrow with isSorted 1`] = `
`; -exports[`EuiTableHeaderCell sorting renders a button with onSort 1`] = ` +exports[`EuiTableHeaderCell sorting renders with a sortable icon if \`onSort\` is passed 1`] = `
diff --git a/src/components/table/table_cells_shared.styles.ts b/src/components/table/table_cells_shared.styles.ts index 6c30695d682..a7a74514b0a 100644 --- a/src/components/table/table_cells_shared.styles.ts +++ b/src/components/table/table_cells_shared.styles.ts @@ -8,7 +8,11 @@ import { css } from '@emotion/react'; -import { UseEuiTheme } from '../../services'; +import { + UseEuiTheme, + makeHighContrastColor, + tintOrShade, +} from '../../services'; import { euiFontSize, logicalCSS, @@ -20,7 +24,7 @@ import { euiTableVariables } from './table.styles'; export const euiTableHeaderFooterCellStyles = ( euiThemeContext: UseEuiTheme ) => { - const { euiTheme } = euiThemeContext; + const { euiTheme, colorMode } = euiThemeContext; // euiFontSize returns an object, so we keep object notation here to merge into css`` const sharedStyles = { @@ -41,11 +45,25 @@ export const euiTableHeaderFooterCellStyles = ( euiTableHeaderCell__button: css` ${logicalCSS('width', '100%')} font-weight: inherit; + line-height: inherit; + + /* Tint the sortable icon a bit further */ + .euiTableSortIcon--sortable { + color: ${makeHighContrastColor( + // Tint it arbitrarily high, the contrast util will take care of lowering back down to WCAG + tintOrShade(euiTheme.colors.subduedText, 0.9, colorMode), + 3 // 3:1 ratio from https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html + )(euiTheme.colors.emptyShade)}; + } &:hover, &:focus { color: ${euiTheme.colors.primaryText}; text-decoration: underline; + + .euiTableSortIcon--sortable { + color: ${euiTheme.colors.primaryText}; + } } `, euiTableFooterCell: css` diff --git a/src/components/table/table_header_cell.test.tsx b/src/components/table/table_header_cell.test.tsx index c2ba36e5333..2d2ffbe78aa 100644 --- a/src/components/table/table_header_cell.test.tsx +++ b/src/components/table/table_header_cell.test.tsx @@ -92,7 +92,15 @@ describe('EuiTableHeaderCell', () => { }); describe('sorting', () => { - it('is rendered with isSorted', () => { + it('renders with a sortable icon if `onSort` is passed', () => { + const { container } = renderInTableHeader( + {}}>Test + ); + + expect(container.firstChild).toMatchSnapshot(); + }); + + it('renders a sort arrow with isSorted', () => { const { container } = renderInTableHeader( Test ); @@ -100,7 +108,7 @@ describe('EuiTableHeaderCell', () => { expect(container.firstChild).toMatchSnapshot(); }); - it('is rendered with isSortAscending', () => { + it('renders a sort arrow upwards with isSortAscending', () => { const { container } = renderInTableHeader( Test diff --git a/src/components/table/table_header_cell.tsx b/src/components/table/table_header_cell.tsx index f426b2b5833..fb216ef9c5f 100644 --- a/src/components/table/table_header_cell.tsx +++ b/src/components/table/table_header_cell.tsx @@ -53,17 +53,17 @@ const CellContents = ({ align, description, children, + canSort, isSorted, isSortAscending, - showSortMsg, }: { className?: string; align: HorizontalAlignment; description: EuiTableHeaderCellProps['description']; children: EuiTableHeaderCellProps['children']; + canSort?: boolean; isSorted: EuiTableHeaderCellProps['isSorted']; isSortAscending?: EuiTableHeaderCellProps['isSortAscending']; - showSortMsg: boolean; }) => { return ( {description} )} - {showSortMsg && isSorted && ( + {isSorted ? ( - )} + ) : canSort ? ( + + ) : null} ); }; @@ -135,67 +142,49 @@ export const EuiTableHeaderCell: FunctionComponent = ({ 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, - }); - - let ariaSortValue: HTMLAttributes['aria-sort'] = 'none'; - if (isSorted) { - ariaSortValue = isSortAscending ? 'ascending' : 'descending'; - } - - return ( - - {onSort && !readOnly ? ( - - ) : ( - cellContents - )} - - ); + const canSort = !!(onSort && !readOnly); + let ariaSortValue: HTMLAttributes['aria-sort']; + if (isSorted) { + ariaSortValue = isSortAscending ? 'ascending' : 'descending'; + } else if (canSort) { + ariaSortValue = 'none'; } + const cellContentsProps = { + css: styles.euiTableHeaderCell__content, + align, + description, + canSort, + isSorted, + isSortAscending, + children, + }; + return ( - {cellContents} + {canSort ? ( + + ) : ( + + )} ); };