mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Accessibility: Enable jsx-a11y/mouse-events-have-key-events
(#58050)
* implement VizLegend keyboard accessibility * add onBlur/onFocus
This commit is contained in:
parent
e6b088fbf5
commit
f76ba90078
@ -75,7 +75,6 @@
|
||||
// we should remove the corresponding line and fix them one by one
|
||||
// any marked "error" contain specific overrides we'll need to keep
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/mouse-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": [
|
||||
"error",
|
||||
{
|
||||
|
@ -28,8 +28,11 @@ export function VizLegend<T>({
|
||||
}: LegendProps<T>) {
|
||||
const { eventBus, onToggleSeriesVisibility, onToggleLegendSort } = usePanelContext();
|
||||
|
||||
const onMouseEnter = useCallback(
|
||||
(item: VizLegendItem, event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
const onMouseOver = useCallback(
|
||||
(
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.FocusEvent<HTMLButtonElement>
|
||||
) => {
|
||||
eventBus?.publish({
|
||||
type: DataHoverEvent.type,
|
||||
payload: {
|
||||
@ -44,7 +47,10 @@ export function VizLegend<T>({
|
||||
);
|
||||
|
||||
const onMouseOut = useCallback(
|
||||
(item: VizLegendItem, event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
(
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.FocusEvent<HTMLButtonElement>
|
||||
) => {
|
||||
eventBus?.publish({
|
||||
type: DataHoverClearEvent.type,
|
||||
payload: {
|
||||
@ -59,7 +65,7 @@ export function VizLegend<T>({
|
||||
);
|
||||
|
||||
const onLegendLabelClick = useCallback(
|
||||
(item: VizLegendItem, event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
(item: VizLegendItem, event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (onLabelClick) {
|
||||
onLabelClick(item, event);
|
||||
}
|
||||
@ -86,7 +92,7 @@ export function VizLegend<T>({
|
||||
sortDesc={sortDesc}
|
||||
onLabelClick={onLegendLabelClick}
|
||||
onToggleSort={onToggleSort || onToggleLegendSort}
|
||||
onLabelMouseEnter={onMouseEnter}
|
||||
onLabelMouseOver={onMouseOver}
|
||||
onLabelMouseOut={onMouseOut}
|
||||
itemRenderer={itemRenderer}
|
||||
readonly={readonly}
|
||||
@ -98,7 +104,7 @@ export function VizLegend<T>({
|
||||
className={className}
|
||||
items={items}
|
||||
placement={placement}
|
||||
onLabelMouseEnter={onMouseEnter}
|
||||
onLabelMouseOver={onMouseOver}
|
||||
onLabelMouseOut={onMouseOut}
|
||||
onLabelClick={onLegendLabelClick}
|
||||
itemRenderer={itemRenderer}
|
||||
|
@ -18,7 +18,7 @@ export interface Props<T> extends VizLegendBaseProps<T> {}
|
||||
export const VizLegendList = <T extends unknown>({
|
||||
items,
|
||||
itemRenderer,
|
||||
onLabelMouseEnter,
|
||||
onLabelMouseOver,
|
||||
onLabelMouseOut,
|
||||
onLabelClick,
|
||||
placement,
|
||||
@ -33,7 +33,7 @@ export const VizLegendList = <T extends unknown>({
|
||||
<VizLegendListItem
|
||||
item={item}
|
||||
onLabelClick={onLabelClick}
|
||||
onLabelMouseEnter={onLabelMouseEnter}
|
||||
onLabelMouseOver={onLabelMouseOver}
|
||||
onLabelMouseOut={onLabelMouseOut}
|
||||
readonly={readonly}
|
||||
/>
|
||||
|
@ -13,9 +13,15 @@ import { VizLegendItem } from './types';
|
||||
export interface Props<T> {
|
||||
item: VizLegendItem<T>;
|
||||
className?: string;
|
||||
onLabelClick?: (item: VizLegendItem<T>, event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onLabelClick?: (item: VizLegendItem<T>, event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onLabelMouseOver?: (
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement> | React.FocusEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
onLabelMouseOut?: (
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement> | React.FocusEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
@ -25,24 +31,24 @@ export interface Props<T> {
|
||||
export const VizLegendListItem = <T = unknown,>({
|
||||
item,
|
||||
onLabelClick,
|
||||
onLabelMouseEnter,
|
||||
onLabelMouseOver,
|
||||
onLabelMouseOut,
|
||||
className,
|
||||
readonly,
|
||||
}: Props<T>) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onMouseEnter = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
if (onLabelMouseEnter) {
|
||||
onLabelMouseEnter(item, event);
|
||||
const onMouseOver = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.FocusEvent<HTMLButtonElement>) => {
|
||||
if (onLabelMouseOver) {
|
||||
onLabelMouseOver(item, event);
|
||||
}
|
||||
},
|
||||
[item, onLabelMouseEnter]
|
||||
[item, onLabelMouseOver]
|
||||
);
|
||||
|
||||
const onMouseOut = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.FocusEvent<HTMLButtonElement>) => {
|
||||
if (onLabelMouseOut) {
|
||||
onLabelMouseOut(item, event);
|
||||
}
|
||||
@ -51,7 +57,7 @@ export const VizLegendListItem = <T = unknown,>({
|
||||
);
|
||||
|
||||
const onClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (onLabelClick) {
|
||||
onLabelClick(item, event);
|
||||
}
|
||||
@ -65,14 +71,18 @@ export const VizLegendListItem = <T = unknown,>({
|
||||
aria-label={selectors.components.VizLegend.seriesName(item.label)}
|
||||
>
|
||||
<VizLegendSeriesIcon seriesName={item.label} color={item.color} gradient={item.gradient} readonly={readonly} />
|
||||
<div
|
||||
onMouseEnter={onMouseEnter}
|
||||
<button
|
||||
disabled={readonly}
|
||||
type="button"
|
||||
onBlur={onMouseOut}
|
||||
onFocus={onMouseOver}
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseOut={onMouseOut}
|
||||
onClick={!readonly ? onClick : undefined}
|
||||
className={cx(styles.label, !readonly && styles.clickable)}
|
||||
onClick={onClick}
|
||||
className={styles.label}
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{item.getDisplayValues && <VizLegendStatsList stats={item.getDisplayValues()} />}
|
||||
</div>
|
||||
@ -85,10 +95,10 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
label: css`
|
||||
label: LegendLabel;
|
||||
white-space: nowrap;
|
||||
`,
|
||||
clickable: css`
|
||||
label: LegendClickabel;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
`,
|
||||
itemDisabled: css`
|
||||
label: LegendLabelDisabled;
|
||||
|
@ -21,7 +21,7 @@ export const VizLegendTable = <T extends unknown>({
|
||||
className,
|
||||
onToggleSort,
|
||||
onLabelClick,
|
||||
onLabelMouseEnter,
|
||||
onLabelMouseOver,
|
||||
onLabelMouseOut,
|
||||
readonly,
|
||||
}: VizLegendTableProps<T>): JSX.Element => {
|
||||
@ -57,7 +57,7 @@ export const VizLegendTable = <T extends unknown>({
|
||||
key={`${item.label}-${index}`}
|
||||
item={item}
|
||||
onLabelClick={onLabelClick}
|
||||
onLabelMouseEnter={onLabelMouseEnter}
|
||||
onLabelMouseOver={onLabelMouseOver}
|
||||
onLabelMouseOut={onLabelMouseOut}
|
||||
readonly={readonly}
|
||||
/>
|
||||
|
@ -13,9 +13,15 @@ export interface Props {
|
||||
key?: React.Key;
|
||||
item: VizLegendItem;
|
||||
className?: string;
|
||||
onLabelClick?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onLabelClick?: (item: VizLegendItem, event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onLabelMouseOver?: (
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement> | React.FocusEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
onLabelMouseOut?: (
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement> | React.FocusEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
@ -25,24 +31,24 @@ export interface Props {
|
||||
export const LegendTableItem: React.FunctionComponent<Props> = ({
|
||||
item,
|
||||
onLabelClick,
|
||||
onLabelMouseEnter,
|
||||
onLabelMouseOver,
|
||||
onLabelMouseOut,
|
||||
className,
|
||||
readonly,
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onMouseEnter = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
if (onLabelMouseEnter) {
|
||||
onLabelMouseEnter(item, event);
|
||||
const onMouseOver = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.FocusEvent<HTMLButtonElement>) => {
|
||||
if (onLabelMouseOver) {
|
||||
onLabelMouseOver(item, event);
|
||||
}
|
||||
},
|
||||
[item, onLabelMouseEnter]
|
||||
[item, onLabelMouseOver]
|
||||
);
|
||||
|
||||
const onMouseOut = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent> | React.FocusEvent<HTMLButtonElement>) => {
|
||||
if (onLabelMouseOut) {
|
||||
onLabelMouseOut(item, event);
|
||||
}
|
||||
@ -51,7 +57,7 @@ export const LegendTableItem: React.FunctionComponent<Props> = ({
|
||||
);
|
||||
|
||||
const onClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (onLabelClick) {
|
||||
onLabelClick(item, event);
|
||||
}
|
||||
@ -64,14 +70,18 @@ export const LegendTableItem: React.FunctionComponent<Props> = ({
|
||||
<td>
|
||||
<span className={styles.itemWrapper}>
|
||||
<VizLegendSeriesIcon color={item.color} seriesName={item.label} readonly={readonly} />
|
||||
<div
|
||||
onMouseEnter={onMouseEnter}
|
||||
<button
|
||||
disabled={readonly}
|
||||
type="button"
|
||||
onBlur={onMouseOut}
|
||||
onFocus={onMouseOver}
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseOut={onMouseOut}
|
||||
onClick={!readonly ? onClick : undefined}
|
||||
className={cx(styles.label, item.disabled && styles.labelDisabled, !readonly && styles.clickable)}
|
||||
className={cx(styles.label, item.disabled && styles.labelDisabled)}
|
||||
>
|
||||
{item.label} {item.yAxis === 2 && <span className={styles.yAxisLabel}>(right y-axis)</span>}
|
||||
</div>
|
||||
</button>
|
||||
</span>
|
||||
</td>
|
||||
{item.getDisplayValues &&
|
||||
@ -108,15 +118,15 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
label: css`
|
||||
label: LegendLabel;
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
`,
|
||||
labelDisabled: css`
|
||||
label: LegendLabelDisabled;
|
||||
color: ${theme.colors.text.disabled};
|
||||
`,
|
||||
clickable: css`
|
||||
label: LegendClickable;
|
||||
cursor: pointer;
|
||||
`,
|
||||
itemWrapper: css`
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
|
@ -13,10 +13,16 @@ export interface VizLegendBaseProps<T> {
|
||||
className?: string;
|
||||
items: Array<VizLegendItem<T>>;
|
||||
seriesVisibilityChangeBehavior?: SeriesVisibilityChangeBehavior;
|
||||
onLabelClick?: (item: VizLegendItem<T>, event: React.MouseEvent<HTMLElement>) => void;
|
||||
onLabelClick?: (item: VizLegendItem<T>, event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
itemRenderer?: (item: VizLegendItem<T>, index: number) => JSX.Element;
|
||||
onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent<HTMLElement>) => void;
|
||||
onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent<HTMLElement>) => void;
|
||||
onLabelMouseOver?: (
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement> | React.FocusEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
onLabelMouseOut?: (
|
||||
item: VizLegendItem,
|
||||
event: React.MouseEvent<HTMLButtonElement> | React.FocusEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
|
@ -147,8 +147,10 @@ function SpanBar({
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.wrapper, className)}
|
||||
onBlur={setShortLabel}
|
||||
onClick={onClick}
|
||||
onMouseLeave={setShortLabel}
|
||||
onFocus={setLongLabel}
|
||||
onMouseOut={setShortLabel}
|
||||
onMouseOver={setLongLabel}
|
||||
aria-hidden
|
||||
data-testid={selectors.components.TraceViewer.spanBar}
|
||||
|
@ -37,8 +37,10 @@ export function GraphiteFunctionEditor({ func }: FunctionEditorProps) {
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.container, { [styles.error]: func.def.unknown })}
|
||||
onBlur={() => setIsMouseOver(false)}
|
||||
onFocus={() => setIsMouseOver(true)}
|
||||
onMouseOver={() => setIsMouseOver(true)}
|
||||
onMouseLeave={() => setIsMouseOver(false)}
|
||||
onMouseOut={() => setIsMouseOver(false)}
|
||||
>
|
||||
<HorizontalGroup spacing="none">
|
||||
<FunctionEditor
|
||||
|
Loading…
Reference in New Issue
Block a user