A11y: Fix fastpass issues for annotation/variables settings (#40364)

* A11y: Fix fastpass issues for annotation/variables settings

* Chore: fixes fastpass for annotations

* Chore: updates after PR comments

* Chore: readd div
This commit is contained in:
Hugo Häggmark 2021-10-13 15:21:34 +02:00 committed by GitHub
parent 281d60095f
commit a512dcf1bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 26 additions and 12 deletions

View File

@ -88,6 +88,9 @@ export const Pages = {
General: { General: {
headerLink: 'Variable editor Header link', headerLink: 'Variable editor Header link',
modeLabelNew: 'Variable editor Header mode New', modeLabelNew: 'Variable editor Header mode New',
/**
* @deprecated
*/
modeLabelEdit: 'Variable editor Header mode Edit', modeLabelEdit: 'Variable editor Header mode Edit',
generalNameInput: 'Variable editor Form Name field', generalNameInput: 'Variable editor Form Name field',
generalTypeSelect: 'Variable editor Form Type select', generalTypeSelect: 'Variable editor Form Type select',

View File

@ -29,6 +29,12 @@ export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
validationMessageHorizontalOverflow?: boolean; validationMessageHorizontalOverflow?: boolean;
className?: string; className?: string;
/**
* A unique id that associates the label of the Field component with the control with the unique id.
* If the `htmlFor` property is missing the `htmlFor` will be inferred from the `id` or `inputId` property of the first child.
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label#attr-for
*/
htmlFor?: string;
} }
export const getFieldStyles = stylesFactory((theme: GrafanaTheme2) => { export const getFieldStyles = stylesFactory((theme: GrafanaTheme2) => {
@ -72,11 +78,12 @@ export const Field: React.FC<FieldProps> = ({
children, children,
className, className,
validationMessageHorizontalOverflow, validationMessageHorizontalOverflow,
htmlFor,
...otherProps ...otherProps
}) => { }) => {
const theme = useTheme2(); const theme = useTheme2();
const styles = getFieldStyles(theme); const styles = getFieldStyles(theme);
const inputId = getChildId(children); const inputId = htmlFor ?? getChildId(children);
const labelElement = const labelElement =
typeof label === 'string' ? ( typeof label === 'string' ? (

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Checkbox, CollapsableSection, ColorValueEditor, Field, HorizontalGroup, Input } from '@grafana/ui'; import { Checkbox, CollapsableSection, ColorValueEditor, Field, HorizontalGroup, Input } from '@grafana/ui';
import { DashboardModel } from '../../state/DashboardModel'; import { DashboardModel } from '../../state/DashboardModel';
import { AnnotationQuery, DataSourceInstanceSettings } from '@grafana/data'; import { AnnotationQuery, DataSourceInstanceSettings } from '@grafana/data';
import { getDataSourceSrv, DataSourcePicker } from '@grafana/runtime'; import { DataSourcePicker, getDataSourceSrv } from '@grafana/runtime';
import { useAsync } from 'react-use'; import { useAsync } from 'react-use';
import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor'; import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor';
import { AngularEditorLoader } from './AngularEditorLoader'; import { AngularEditorLoader } from './AngularEditorLoader';
@ -78,7 +78,7 @@ export const AnnotationSettingsEdit: React.FC<Props> = ({ editIdx, dashboard })
width={50} width={50}
/> />
</Field> </Field>
<Field label="Data source"> <Field label="Data source" htmlFor="data-source-picker">
<DataSourcePicker <DataSourcePicker
width={50} width={50}
annotations annotations

View File

@ -74,7 +74,11 @@ export const AnnotationSettingsList: React.FC<Props> = ({ dashboard, onNew, onEd
) : null} ) : null}
</td> </td>
<td style={{ width: '1%' }}> <td style={{ width: '1%' }}>
<DeleteButton size="sm" onConfirm={() => onDelete(idx)} /> <DeleteButton
size="sm"
onConfirm={() => onDelete(idx)}
aria-label={`Delete query with title "${annotation.name}"`}
/>
</td> </td>
</tr> </tr>
))} ))}

View File

@ -148,7 +148,7 @@ describe('AnnotationsSettings', () => {
).toBeInTheDocument(); ).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /new query/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /new query/i })).not.toBeInTheDocument();
userEvent.click(screen.getByRole('button', { name: /delete/i })); userEvent.click(screen.getByRole('button', { name: /^delete$/i }));
expect(screen.queryAllByRole('row').length).toBe(0); expect(screen.queryAllByRole('row').length).toBe(0);
expect( expect(

View File

@ -335,6 +335,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
const containerClassNames = classnames(styles.dashboardContainer, { const containerClassNames = classnames(styles.dashboardContainer, {
'panel-in-fullscreen': viewPanel, 'panel-in-fullscreen': viewPanel,
}); });
const showSubMenu = !editPanel && kioskMode === KioskMode.Off && !this.props.queryParams.editview;
return ( return (
<div className={containerClassNames}> <div className={containerClassNames}>
@ -364,7 +365,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
> >
<div className={styles.dashboardContent}> <div className={styles.dashboardContent}>
{initError && <DashboardFailed />} {initError && <DashboardFailed />}
{!editPanel && kioskMode === KioskMode.Off && ( {showSubMenu && (
<section aria-label={selectors.pages.Dashboard.SubMenu.submenu}> <section aria-label={selectors.pages.Dashboard.SubMenu.submenu}>
<SubMenu dashboard={dashboard} annotations={dashboard.annotations.list} links={dashboard.links} /> <SubMenu dashboard={dashboard} annotations={dashboard.annotations.list} links={dashboard.links} />
</section> </section>

View File

@ -85,10 +85,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
</a> </a>
{this.props.idInEditor && ( {this.props.idInEditor && (
<span> <span>
<Icon <Icon name="angle-right" />
name="angle-right"
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.modeLabelEdit}
/>
Edit Edit
</span> </span>
)} )}

View File

@ -25,14 +25,16 @@ export function VariableSelectField({
labelWidth, labelWidth,
}: PropsWithChildren<VariableSelectFieldProps<any>>): ReactElement { }: PropsWithChildren<VariableSelectFieldProps<any>>): ReactElement {
const styles = useStyles(getStyles); const styles = useStyles(getStyles);
const inputId = `variable-select-input-${name}`;
return ( return (
<> <>
<InlineFormLabel width={labelWidth ?? 6} tooltip={tooltip}> <InlineFormLabel width={labelWidth ?? 6} tooltip={tooltip} htmlFor={inputId}>
{name} {name}
</InlineFormLabel> </InlineFormLabel>
<div aria-label={ariaLabel}> <div aria-label={ariaLabel}>
<Select <Select
inputId={inputId}
menuShouldPortal menuShouldPortal
onChange={onChange} onChange={onChange}
value={value} value={value}

View File

@ -161,7 +161,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
<VerticalGroup spacing="lg"> <VerticalGroup spacing="lg">
<VerticalGroup spacing="none"> <VerticalGroup spacing="none">
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Data source" labelWidth={20}> <InlineField label="Data source" labelWidth={20} htmlFor="data-source-picker">
<DataSourcePicker <DataSourcePicker
current={this.props.variable.datasource} current={this.props.variable.datasource}
onChange={this.onDataSourceChange} onChange={this.onDataSourceChange}