mirror of
https://github.com/grafana/grafana.git
synced 2024-11-23 09:26:43 -06:00
Logs: Improved keyboard accessibility of log rows menu (#72686)
* Log Row Menu Cell: improve accessibility * Explain implementation in comments * Make onBlur mandatory
This commit is contained in:
parent
ad2705fa0b
commit
9c6a9a3977
@ -203,6 +203,12 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
||||
onClick={this.toggleDetails}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
/**
|
||||
* For better accessibility support, we listen to the onFocus event here (to display the LogRowMenuCell), and
|
||||
* to onBlur event in the LogRowMenuCell (to hide it). This way, the LogRowMenuCell is displayed when the user navigates
|
||||
* using the keyboard.
|
||||
*/
|
||||
onFocus={this.onMouseEnter}
|
||||
>
|
||||
{showDuplicates && (
|
||||
<td className={styles.logsRowDuplicates}>
|
||||
@ -241,6 +247,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
||||
onUnpinLine={this.props.onUnpinLine}
|
||||
pinned={this.props.pinned}
|
||||
mouseIsOver={this.state.mouseIsOver}
|
||||
onBlur={this.onMouseLeave}
|
||||
/>
|
||||
) : (
|
||||
<LogRowMessage
|
||||
@ -256,6 +263,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
||||
onUnpinLine={this.props.onUnpinLine}
|
||||
pinned={this.props.pinned}
|
||||
mouseIsOver={this.state.mouseIsOver}
|
||||
onBlur={this.onMouseLeave}
|
||||
/>
|
||||
)}
|
||||
</tr>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { SyntheticEvent, useCallback } from 'react';
|
||||
import React, { FocusEvent, SyntheticEvent, useCallback } from 'react';
|
||||
|
||||
import { LogRowModel } from '@grafana/data';
|
||||
import { ClipboardButton, IconButton } from '@grafana/ui';
|
||||
@ -16,6 +16,7 @@ interface Props {
|
||||
pinned?: boolean;
|
||||
styles: LogRowStyles;
|
||||
mouseIsOver: boolean;
|
||||
onBlur: () => void;
|
||||
}
|
||||
|
||||
export const LogRowMenuCell = React.memo(
|
||||
@ -30,6 +31,7 @@ export const LogRowMenuCell = React.memo(
|
||||
showContextToggle,
|
||||
styles,
|
||||
mouseIsOver,
|
||||
onBlur,
|
||||
}: Props) => {
|
||||
const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false;
|
||||
const onLogRowClick = useCallback((e: SyntheticEvent) => {
|
||||
@ -42,11 +44,23 @@ export const LogRowMenuCell = React.memo(
|
||||
},
|
||||
[onOpenContext, row]
|
||||
);
|
||||
/**
|
||||
* For better accessibility support, we listen to the onBlur event here (to hide this component), and
|
||||
* to onFocus in LogRow (to show this component).
|
||||
*/
|
||||
const handleBlur = useCallback(
|
||||
(e: FocusEvent) => {
|
||||
if (!e.currentTarget.contains(e.relatedTarget) && onBlur) {
|
||||
onBlur();
|
||||
}
|
||||
},
|
||||
[onBlur]
|
||||
);
|
||||
const getLogText = useCallback(() => logText, [logText]);
|
||||
return (
|
||||
// TODO: fix keyboard a11y
|
||||
// We keep this click listener here to prevent the row from being selected when clicking on the menu.
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<span className={`log-row-menu ${styles.rowMenu}`} onClick={onLogRowClick}>
|
||||
<span className={`log-row-menu ${styles.rowMenu}`} onClick={onLogRowClick} onBlur={handleBlur}>
|
||||
{pinned && !mouseIsOver && (
|
||||
<IconButton
|
||||
className={styles.unPinButton}
|
||||
@ -56,6 +70,7 @@ export const LogRowMenuCell = React.memo(
|
||||
tooltip="Unpin line"
|
||||
tooltipPlacement="top"
|
||||
aria-label="Unpin line"
|
||||
tabIndex={0}
|
||||
/>
|
||||
)}
|
||||
{mouseIsOver && (
|
||||
@ -68,6 +83,7 @@ export const LogRowMenuCell = React.memo(
|
||||
tooltip="Show context"
|
||||
tooltipPlacement="top"
|
||||
aria-label="Show context"
|
||||
tabIndex={0}
|
||||
/>
|
||||
)}
|
||||
<ClipboardButton
|
||||
@ -79,6 +95,7 @@ export const LogRowMenuCell = React.memo(
|
||||
getText={getLogText}
|
||||
tooltip="Copy to clipboard"
|
||||
tooltipPlacement="top"
|
||||
tabIndex={0}
|
||||
/>
|
||||
{pinned && onUnpinLine && (
|
||||
<IconButton
|
||||
@ -89,6 +106,7 @@ export const LogRowMenuCell = React.memo(
|
||||
tooltip="Unpin line"
|
||||
tooltipPlacement="top"
|
||||
aria-label="Unpin line"
|
||||
tabIndex={0}
|
||||
/>
|
||||
)}
|
||||
{!pinned && onPinLine && (
|
||||
@ -100,6 +118,7 @@ export const LogRowMenuCell = React.memo(
|
||||
tooltip="Pin line"
|
||||
tooltipPlacement="top"
|
||||
aria-label="Pin line"
|
||||
tabIndex={0}
|
||||
/>
|
||||
)}
|
||||
{onPermalinkClick && row.rowId !== undefined && row.uid && (
|
||||
@ -110,6 +129,7 @@ export const LogRowMenuCell = React.memo(
|
||||
size="md"
|
||||
name="share-alt"
|
||||
onClick={() => onPermalinkClick(row)}
|
||||
tabIndex={0}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -19,6 +19,7 @@ const setup = (propOverrides?: Partial<ComponentProps<typeof LogRowMessage>>, ro
|
||||
app: CoreApp.Explore,
|
||||
styles,
|
||||
mouseIsOver: true,
|
||||
onBlur: jest.fn(),
|
||||
...(propOverrides || {}),
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,7 @@ interface Props {
|
||||
pinned?: boolean;
|
||||
styles: LogRowStyles;
|
||||
mouseIsOver: boolean;
|
||||
onBlur: () => void;
|
||||
}
|
||||
|
||||
interface LogMessageProps {
|
||||
@ -75,10 +76,11 @@ export const LogRowMessage = React.memo((props: Props) => {
|
||||
onPinLine,
|
||||
pinned,
|
||||
mouseIsOver,
|
||||
onBlur,
|
||||
} = props;
|
||||
const { hasAnsi, raw } = row;
|
||||
const restructuredEntry = useMemo(() => restructureLog(raw, prettifyLogMessage), [raw, prettifyLogMessage]);
|
||||
const shouldShowMenu = useMemo(() => mouseIsOver || pinned, [mouseIsOver, pinned]);
|
||||
const shouldShowMenu = useMemo(() => mouseIsOver || pinned || true, [mouseIsOver, pinned]);
|
||||
return (
|
||||
<>
|
||||
{
|
||||
@ -105,6 +107,7 @@ export const LogRowMessage = React.memo((props: Props) => {
|
||||
pinned={pinned}
|
||||
styles={styles}
|
||||
mouseIsOver={mouseIsOver}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
|
@ -21,6 +21,7 @@ const setup = (propOverrides: Partial<Props> = {}, detectedFields = ['place', 'p
|
||||
styles,
|
||||
detectedFields,
|
||||
mouseIsOver: true,
|
||||
onBlur: jest.fn(),
|
||||
...propOverrides,
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@ export interface Props {
|
||||
onUnpinLine?: (row: LogRowModel) => void;
|
||||
pinned?: boolean;
|
||||
mouseIsOver: boolean;
|
||||
onBlur: () => void;
|
||||
}
|
||||
|
||||
export const LogRowMessageDisplayedFields = React.memo((props: Props) => {
|
||||
@ -65,8 +66,8 @@ export const LogRowMessageDisplayedFields = React.memo((props: Props) => {
|
||||
row={row}
|
||||
styles={styles}
|
||||
pinned={pinned}
|
||||
{...rest}
|
||||
mouseIsOver={mouseIsOver}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
|
Loading…
Reference in New Issue
Block a user