Skip to content

Commit

Permalink
fix: [Obs Discover][KEYBOARD]: Tooltips in the datagrid header must b…
Browse files Browse the repository at this point in the history
…e keyboard accessible (#183270)

Closes: elastic/observability-dev#3353

## Summary

## Description

The Obs Discover view has a datagrid with four (or more) tooltips that
cannot be reached by keyboard. These elements can be helpful to users to
understand what the column represents, and must be available to all
users. Screenshot attached below.

This issue encompasses both the **Discover** and **Logs Explorer** tabs.
There are 4 unique views (2 tabs, 2 sub-tabs each) that have the same
issue. I'm consolidating for speed, but please let me know if you'd
prefer they be broken out to related issues.

### Steps to recreate

1. Open the [Obs Serverless
Discover](https://keepserverless-qa-oblt-b4ba07.kb.eu-west-1.aws.qa.elastic.cloud/app/observability-logs-explorer/)
view
2. Click somewhere close to the datagrid to avoid having to tab through
the entire page
3. Press `Tab` until the datagrid has focus
4. Use the `arrow` keys to traverse the data grid header row
5. Verify the tooltips cannot be reached by`arrow` or `Tab` keypress

### What was done?: 
1. Removed `HoverPopover`. The logic might not be suitable for use
outside of the `DataGrid` column.
2. Replaced `EuiToolTip` with `EuiIconTip` for text-based popovers.
3. Refactored `TooltipButton` to properly handle keyboard navigation.

### Screen 


https://github.com/elastic/kibana/assets/20072247/9ac9bc70-075f-41f6-a5a5-4e30a385b1c9
  • Loading branch information
alexwizp committed May 15, 2024
1 parent 3e9a808 commit d982bae
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import React, { useMemo } from 'react';
import { css, CSSObject } from '@emotion/react';
import { EuiIcon, EuiToolTip } from '@elastic/eui';
import { EuiIconTip } from '@elastic/eui';
import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
import { FieldIcon, getFieldIconProps, getTextBasedColumnIconType } from '@kbn/field-utils';
import { isNestedFieldParent } from '@kbn/discover-utils';
Expand Down Expand Up @@ -129,11 +129,9 @@ export const DataTableTimeColumnHeader = ({
text-align: left;
`}
>
<EuiToolTip content={primaryTimeTooltip}>
<ColumnHeaderTruncateContainer headerRowHeight={headerRowHeight}>
{timeFieldName} <EuiIcon type="clock" />
</ColumnHeaderTruncateContainer>
</EuiToolTip>
<ColumnHeaderTruncateContainer headerRowHeight={headerRowHeight}>
{timeFieldName} <EuiIconTip type="clock" content={primaryTimeTooltip} />
</ColumnHeaderTruncateContainer>
</div>
);
};
2 changes: 1 addition & 1 deletion test/functional/services/data_grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export class DataGridService extends FtrService {

public async getControlColumnHeaderFields(): Promise<string[]> {
const result = await this.find.allByCssSelector(
'.euiDataGridHeaderCell--controlColumn > .euiDataGridHeaderCell__content'
'.euiDataGridHeaderCell--controlColumn .euiDataGridHeaderCell__content'
);

const textArr = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EuiInMemoryTable,
EuiText,
EuiToolTip,
EuiIconTip,
LEFT_ALIGNMENT,
RIGHT_ALIGNMENT,
EuiResizeObserver,
Expand Down Expand Up @@ -230,19 +231,13 @@ export const DataVisualizerTable = <T extends DataVisualizerTableItem>({
{i18n.translate('xpack.dataVisualizer.dataGrid.documentsCountColumnName', {
defaultMessage: 'Documents (%)',
})}
{
<EuiToolTip
content={i18n.translate(
'xpack.dataVisualizer.dataGrid.documentsCountColumnTooltip',
{
defaultMessage:
'Document count found is based on a smaller set of sampled records.',
}
)}
>
<EuiIcon type="questionInCircle" />
</EuiToolTip>
}
<EuiIconTip
content={i18n.translate('xpack.dataVisualizer.dataGrid.documentsCountColumnTooltip', {
defaultMessage:
'Document count found is based on a smaller set of sampled records.',
})}
type="questionInCircle"
/>
</div>
),

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
actionsLabel,
actionsLabelLowerCase,
} from '../../common/translations';
import { HoverPopover } from '../../common/hover_popover';
import { TooltipButtonComponent } from './tooltip_button';
import { TooltipButton } from './tooltip_button';
import * as constants from '../../../../common/constants';
import { FieldWithToken } from './field_with_token';

Expand All @@ -28,10 +27,7 @@ const spacingCSS = css`

export const ActionsColumnTooltip = () => {
return (
<HoverPopover
button={<TooltipButtonComponent displayText={actionsLabelLowerCase} />}
title={actionsLabel}
>
<TooltipButton displayText={actionsLabelLowerCase} popoverTitle={actionsLabel}>
<div style={{ width: '230px' }}>
<EuiText size="s" css={spacingCSS}>
<p>{actionsHeaderTooltipParagraph}</p>
Expand Down Expand Up @@ -94,6 +90,6 @@ export const ActionsColumnTooltip = () => {
))}
</div>
</div>
</HoverPopover>
</TooltipButton>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import {
contentHeaderTooltipParagraph2,
contentLabel,
} from '../../common/translations';
import { HoverPopover } from '../../common/hover_popover';
import { TooltipButtonComponent } from './tooltip_button';
import { TooltipButton } from './tooltip_button';
import { FieldWithToken } from './field_with_token';
import * as constants from '../../../../common/constants';

Expand All @@ -26,14 +25,10 @@ export const ContentColumnTooltip = ({ column, headerRowHeight }: CustomGridColu
`;

return (
<HoverPopover
button={
<TooltipButtonComponent
displayText={column.displayAsText}
headerRowHeight={headerRowHeight}
/>
}
title={contentLabel}
<TooltipButton
displayText={column.displayAsText}
headerRowHeight={headerRowHeight}
popoverTitle={contentLabel}
>
<div style={{ width: '230px' }}>
<EuiText size="s" css={spacingCSS}>
Expand All @@ -45,6 +40,6 @@ export const ContentColumnTooltip = ({ column, headerRowHeight }: CustomGridColu
<FieldWithToken field={constants.ERROR_MESSAGE_FIELD} />
<FieldWithToken field={constants.EVENT_ORIGINAL_FIELD} iconType="tokenEvent" />
</div>
</HoverPopover>
</TooltipButton>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { EuiText } from '@elastic/eui';
import type { CustomGridColumnProps } from '@kbn/unified-data-table';
import { euiThemeVars } from '@kbn/ui-theme';
import { resourceHeaderTooltipParagraph, resourceLabel } from '../../common/translations';
import { HoverPopover } from '../../common/hover_popover';
import { TooltipButtonComponent } from './tooltip_button';
import { TooltipButton } from './tooltip_button';
import * as constants from '../../../../common/constants';
import { FieldWithToken } from './field_with_token';

Expand All @@ -22,14 +21,10 @@ const spacingCSS = css`

export const ResourceColumnTooltip = ({ column, headerRowHeight }: CustomGridColumnProps) => {
return (
<HoverPopover
button={
<TooltipButtonComponent
displayText={column.displayAsText}
headerRowHeight={headerRowHeight}
/>
}
title={resourceLabel}
<TooltipButton
displayText={column.displayAsText}
headerRowHeight={headerRowHeight}
popoverTitle={resourceLabel}
>
<div style={{ width: '230px' }}>
<EuiText size="s" css={spacingCSS}>
Expand All @@ -45,6 +40,6 @@ export const ResourceColumnTooltip = ({ column, headerRowHeight }: CustomGridCol
<FieldWithToken field={field} key={field} />
))}
</div>
</HoverPopover>
</TooltipButton>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,74 @@
* 2.0.
*/

import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
import { EuiIcon } from '@elastic/eui';
import React from 'react';
import ColumnHeaderTruncateContainer from '@kbn/unified-data-table/src/components/column_header_truncate_container';

export const TooltipButtonComponent = ({
import { EuiPopover, EuiPopoverTitle } from '@elastic/eui';

export const TooltipButton = ({
children,
popoverTitle,
displayText,
headerRowHeight,
iconType = 'questionInCircle',
}: {
children: React.ReactChild;
popoverTitle: string;
displayText?: string;
headerRowHeight?: number;
}) => (
<ColumnHeaderTruncateContainer headerRowHeight={headerRowHeight}>
{displayText} <EuiIcon type="questionInCircle" />
</ColumnHeaderTruncateContainer>
);
iconType?: string;
}) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const leaveTimer = useRef<NodeJS.Timeout | null>(null);

const clearTimer = useMemo(
() => () => {
if (leaveTimer.current) {
clearTimeout(leaveTimer.current);
}
},
[]
);

const onMouseEnter = useCallback(() => {
clearTimer();
setIsPopoverOpen(true);
}, [clearTimer]);

const onMouseLeave = useCallback(() => {
leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100);
}, []);

useEffect(() => {
return () => {
clearTimer();
};
}, [clearTimer]);

return (
<ColumnHeaderTruncateContainer headerRowHeight={headerRowHeight}>
{displayText}{' '}
<EuiPopover
button={
<EuiIcon
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onFocus={onMouseEnter}
onBlur={onMouseLeave}
type={iconType}
tabIndex={0}
/>
}
isOpen={isPopoverOpen}
anchorPosition="upCenter"
panelPaddingSize="s"
ownFocus={false}
>
<EuiPopoverTitle>{popoverTitle}</EuiPopoverTitle>
{children}
</EuiPopover>
</ColumnHeaderTruncateContainer>
);
};

0 comments on commit d982bae

Please sign in to comment.