mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
Accessibility: enable rule jsx-a11y/label-has-associated-control
(#57964)
* use labels correctly * fix opentsdb labels * fix unit tests * use aria-label instead of data-testid
This commit is contained in:
parent
3770f4f2b7
commit
e5c68f40c2
@ -73,7 +73,6 @@
|
|||||||
// we should remove the corresponding line and fix them one by one
|
// we should remove the corresponding line and fix them one by one
|
||||||
// any marked "error" contain specific overrides we'll need to keep
|
// any marked "error" contain specific overrides we'll need to keep
|
||||||
"jsx-a11y/click-events-have-key-events": "off",
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
"jsx-a11y/label-has-associated-control": "off",
|
|
||||||
"jsx-a11y/mouse-events-have-key-events": "off",
|
"jsx-a11y/mouse-events-have-key-events": "off",
|
||||||
"jsx-a11y/no-autofocus": [
|
"jsx-a11y/no-autofocus": [
|
||||||
"error",
|
"error",
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { FunctionComponent, PureComponent } from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
import { withTheme2, useStyles2 } from '../../themes';
|
import { withTheme2, useStyles2 } from '../../themes';
|
||||||
import { Button } from '../Button';
|
|
||||||
import { Switch } from '../Forms/Legacy/Switch/Switch';
|
import { Switch } from '../Forms/Legacy/Switch/Switch';
|
||||||
import { PopoverContentProps } from '../Tooltip';
|
import { PopoverContentProps } from '../Tooltip';
|
||||||
|
|
||||||
@ -43,52 +42,6 @@ export const SeriesColorPickerPopover: FunctionComponent<SeriesColorPickerPopove
|
|||||||
return <ColorPickerPopover {...colorPickerProps} color={color || '#000000'} customPickers={customPickers} />;
|
return <ColorPickerPopover {...colorPickerProps} color={color || '#000000'} customPickers={customPickers} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AxisSelectorProps {
|
|
||||||
yaxis: number;
|
|
||||||
onToggleAxis?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AxisSelectorState {
|
|
||||||
yaxis: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AxisSelector extends PureComponent<AxisSelectorProps, AxisSelectorState> {
|
|
||||||
constructor(props: AxisSelectorProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
yaxis: this.props.yaxis,
|
|
||||||
};
|
|
||||||
this.onToggleAxis = this.onToggleAxis.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggleAxis() {
|
|
||||||
this.setState({
|
|
||||||
yaxis: this.state.yaxis === 2 ? 1 : 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.props.onToggleAxis) {
|
|
||||||
this.props.onToggleAxis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const leftButtonVariant = this.state.yaxis === 1 ? 'primary' : 'secondary';
|
|
||||||
const rightButtonVariant = this.state.yaxis === 2 ? 'primary' : 'secondary';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-b-1">
|
|
||||||
<label className="small p-r-1">Y Axis:</label>
|
|
||||||
<Button onClick={this.onToggleAxis} size="sm" variant={leftButtonVariant}>
|
|
||||||
Left
|
|
||||||
</Button>
|
|
||||||
<Button onClick={this.onToggleAxis} size="sm" variant={rightButtonVariant}>
|
|
||||||
Right
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This component is to enable SeriesColorPickerPopover usage via series-color-picker-popover directive
|
// This component is to enable SeriesColorPickerPopover usage via series-color-picker-popover directive
|
||||||
export const SeriesColorPickerPopoverWithTheme = withTheme2(SeriesColorPickerPopover);
|
export const SeriesColorPickerPopoverWithTheme = withTheme2(SeriesColorPickerPopover);
|
||||||
|
|
||||||
|
@ -155,13 +155,14 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
|||||||
<FormField label="Access" labelWidth={13} inputWidth={20} inputEl={accessSelect} />
|
<FormField label="Access" labelWidth={13} inputWidth={20} inputEl={accessSelect} />
|
||||||
</div>
|
</div>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<label
|
<button
|
||||||
|
type="button"
|
||||||
className="gf-form-label query-keyword pointer"
|
className="gf-form-label query-keyword pointer"
|
||||||
onClick={() => setIsAccessHelpVisible((isVisible) => !isVisible)}
|
onClick={() => setIsAccessHelpVisible((isVisible) => !isVisible)}
|
||||||
>
|
>
|
||||||
Help
|
Help
|
||||||
<Icon name={isAccessHelpVisible ? 'angle-down' : 'angle-right'} style={{ marginBottom: 0 }} />
|
<Icon name={isAccessHelpVisible ? 'angle-down' : 'angle-right'} style={{ marginBottom: 0 }} />
|
||||||
</label>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isAccessHelpVisible && <HttpAccessHelp />}
|
{isAccessHelpVisible && <HttpAccessHelp />}
|
||||||
|
@ -14,7 +14,7 @@ export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'value'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Switch = React.forwardRef<HTMLInputElement, Props>(
|
export const Switch = React.forwardRef<HTMLInputElement, Props>(
|
||||||
({ value, checked, disabled, onChange, id, ...inputProps }, ref) => {
|
({ value, checked, disabled, onChange, id, label, ...inputProps }, ref) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
deprecationWarning('Switch', 'checked prop', 'value');
|
deprecationWarning('Switch', 'checked prop', 'value');
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ export const Switch = React.forwardRef<HTMLInputElement, Props>(
|
|||||||
{...inputProps}
|
{...inputProps}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={switchIdRef.current} />
|
<label htmlFor={switchIdRef.current} aria-label={label ?? 'Toggle switch'} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ export const SliderValueEditor: React.FC<FieldConfigEditorProps<number, SliderFi
|
|||||||
<div className={cx(styles.container, styles.slider)}>
|
<div className={cx(styles.container, styles.slider)}>
|
||||||
{/** Slider tooltip's parent component is body and therefore we need Global component to do css overrides for it. */}
|
{/** Slider tooltip's parent component is body and therefore we need Global component to do css overrides for it. */}
|
||||||
<Global styles={styles.slider} />
|
<Global styles={styles.slider} />
|
||||||
<label className={cx(styles.sliderInput, ...sliderInputClassNames)}>
|
<div className={cx(styles.sliderInput, ...sliderInputClassNames)}>
|
||||||
<SliderWithTooltip
|
<SliderWithTooltip
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
@ -113,7 +113,7 @@ export const SliderValueEditor: React.FC<FieldConfigEditorProps<number, SliderFi
|
|||||||
<span className={stylesSlider.numberInputWrapper} ref={inputRef}>
|
<span className={stylesSlider.numberInputWrapper} ref={inputRef}>
|
||||||
<NumberInput value={sliderValue} onChange={onSliderInputChange} max={max} min={min} step={step} />
|
<NumberInput value={sliderValue} onChange={onSliderInputChange} max={max} min={min} step={step} />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,6 @@ describe('<BasicSettings>', () => {
|
|||||||
setup();
|
setup();
|
||||||
|
|
||||||
expect(screen.getByRole('textbox', { name: selectors.pages.DataSource.name })).toBeInTheDocument();
|
expect(screen.getByRole('textbox', { name: selectors.pages.DataSource.name })).toBeInTheDocument();
|
||||||
expect(screen.getByRole('checkbox', { name: 'Default' })).toBeInTheDocument();
|
expect(screen.getByRole('checkbox', { name: /Default/ })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,10 +10,10 @@ export type Props = {
|
|||||||
export function DataSourcePluginState({ state }: Props) {
|
export function DataSourcePluginState({ state }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<label className="gf-form-label width-10">Plugin state</label>
|
<div className="gf-form-label width-10">Plugin state</div>
|
||||||
<label className="gf-form-label gf-form-label--transparent">
|
<div className="gf-form-label gf-form-label--transparent">
|
||||||
<PluginStateInfo state={state} />
|
<PluginStateInfo state={state} />
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ export const switchToQueryHistoryTab = async (
|
|||||||
|
|
||||||
export const selectStarredTabFirst = async (exploreId: ExploreId = ExploreId.left) => {
|
export const selectStarredTabFirst = async (exploreId: ExploreId = ExploreId.left) => {
|
||||||
const checkbox = withinExplore(exploreId).getByRole('checkbox', {
|
const checkbox = withinExplore(exploreId).getByRole('checkbox', {
|
||||||
name: 'Change the default active tab from “Query history” to “Starred”',
|
name: /Change the default active tab from “Query history” to “Starred”/,
|
||||||
});
|
});
|
||||||
await userEvent.click(checkbox);
|
await userEvent.click(checkbox);
|
||||||
};
|
};
|
||||||
|
@ -94,10 +94,13 @@ export class FolderSettingsPage extends PureComponent<Props, State> {
|
|||||||
<div className="section gf-form-group">
|
<div className="section gf-form-group">
|
||||||
<form name="folderSettingsForm" onSubmit={this.onSave}>
|
<form name="folderSettingsForm" onSubmit={this.onSave}>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<label className="gf-form-label width-7">Name</label>
|
<label htmlFor="folder-title" className="gf-form-label width-7">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
className="gf-form-input width-30"
|
className="gf-form-input width-30"
|
||||||
|
id="folder-title"
|
||||||
value={folder.title}
|
value={folder.title}
|
||||||
onChange={this.onTitleChange}
|
onChange={this.onTitleChange}
|
||||||
/>
|
/>
|
||||||
|
@ -18,7 +18,7 @@ export function Preview({ rawSql }: PreviewProps) {
|
|||||||
|
|
||||||
const labelElement = (
|
const labelElement = (
|
||||||
<div className={styles.labelWrapper}>
|
<div className={styles.labelWrapper}>
|
||||||
<label className={styles.label}>Preview</label>
|
<span className={styles.label}>Preview</span>
|
||||||
<IconButton tooltip="Copy to clipboard" onClick={() => copyToClipboard(rawSql)} name="copy" />
|
<IconButton tooltip="Copy to clipboard" onClick={() => copyToClipboard(rawSql)} name="copy" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@ export const AnnotationsHelp = () => {
|
|||||||
<p>
|
<p>
|
||||||
Example Result: <code>monitoring.googleapis.com/uptime_check/http_status has this value: 502</code>
|
Example Result: <code>monitoring.googleapis.com/uptime_check/http_status has this value: 502</code>
|
||||||
</p>
|
</p>
|
||||||
<label>Patterns:</label>
|
<span>Patterns:</span>
|
||||||
<p>
|
<p>
|
||||||
<code>{`${'{{metric.value}}'}`}</code> = value of the metric/point
|
<code>{`${'{{metric.value}}'}`}</code> = value of the metric/point
|
||||||
</p>
|
</p>
|
||||||
|
@ -24,7 +24,7 @@ export default class CloudMonitoringCheatSheet extends PureComponent<
|
|||||||
Result: <code>cpu/usage_time - server1-europe-west-1</code>
|
Result: <code>cpu/usage_time - server1-europe-west-1</code>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<label>Patterns</label>
|
<span>Patterns:</span>
|
||||||
<br />
|
<br />
|
||||||
<ul
|
<ul
|
||||||
className={css`
|
className={css`
|
||||||
|
@ -53,7 +53,7 @@ describe('FilterSection', () => {
|
|||||||
describe('filter editor', () => {
|
describe('filter editor', () => {
|
||||||
it('open the editor on clicking +', () => {
|
it('open the editor on clicking +', () => {
|
||||||
setup();
|
setup();
|
||||||
fireEvent.click(screen.getByTestId(testIds.open));
|
fireEvent.click(screen.getByRole('button', { name: /Add filter/ }));
|
||||||
expect(screen.getByText('Group by')).toBeInTheDocument();
|
expect(screen.getByText('Group by')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ describe('FilterSection', () => {
|
|||||||
|
|
||||||
it('should call runQuery on adding a filter', () => {
|
it('should call runQuery on adding a filter', () => {
|
||||||
setup();
|
setup();
|
||||||
fireEvent.click(screen.getByTestId(testIds.open));
|
fireEvent.click(screen.getByRole('button', { name: /Add filter/ }));
|
||||||
fireEvent.click(screen.getByText('add filter'));
|
fireEvent.click(screen.getByText('add filter'));
|
||||||
expect(onRunQuery).toHaveBeenCalled();
|
expect(onRunQuery).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -78,7 +78,7 @@ describe('FilterSection', () => {
|
|||||||
tags: [{}],
|
tags: [{}],
|
||||||
};
|
};
|
||||||
setup({ query });
|
setup({ query });
|
||||||
fireEvent.click(screen.getByTestId(testIds.open));
|
fireEvent.click(screen.getByRole('button', { name: /Add filter/ }));
|
||||||
fireEvent.click(screen.getByText('add filter'));
|
fireEvent.click(screen.getByText('add filter'));
|
||||||
expect(screen.getByTestId(testIds.error)).toBeInTheDocument();
|
expect(screen.getByTestId(testIds.error)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -138,11 +138,9 @@ export function FilterSection({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{!addFilterMode && (
|
{!addFilterMode && (
|
||||||
<label className="gf-form-label query-keyword">
|
<button className="gf-form-label" type="button" onClick={changeAddFilterMode} aria-label="Add filter">
|
||||||
<button type="button" className={buttonStyles} onClick={changeAddFilterMode} data-testid={testIds.open}>
|
<Icon name={'plus'} />
|
||||||
<Icon name={'plus'} />
|
</button>
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{addFilterMode && (
|
{addFilterMode && (
|
||||||
@ -225,19 +223,18 @@ export function FilterSection({
|
|||||||
/>
|
/>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
{errors && (
|
{errors && (
|
||||||
<label className="gf-form-label" title={errors} data-testid={testIds.error}>
|
<div className="gf-form-label" title={errors} data-testid={testIds.error}>
|
||||||
<Icon name={'exclamation-triangle'} color={'rgb(229, 189, 28)'} />
|
<Icon name={'exclamation-triangle'} color={'rgb(229, 189, 28)'} />
|
||||||
</label>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="gf-form-label">
|
||||||
<label className="gf-form-label">
|
|
||||||
<button type="button" className={buttonStyles} onClick={addFilter}>
|
<button type="button" className={buttonStyles} onClick={addFilter}>
|
||||||
add filter
|
add filter
|
||||||
</button>
|
</button>
|
||||||
<button type="button" className={buttonStyles} onClick={changeAddFilterMode}>
|
<button type="button" className={buttonStyles} onClick={changeAddFilterMode}>
|
||||||
<Icon name={'times'} />
|
<Icon name={'times'} />
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -250,7 +247,6 @@ export function FilterSection({
|
|||||||
|
|
||||||
export const testIds = {
|
export const testIds = {
|
||||||
section: 'opentsdb-filter',
|
section: 'opentsdb-filter',
|
||||||
open: 'opentsdb-filter-editor',
|
|
||||||
list: 'opentsdb-filter-list',
|
list: 'opentsdb-filter-list',
|
||||||
error: 'opentsdb-filter-error',
|
error: 'opentsdb-filter-error',
|
||||||
remove: 'opentsdb-filter-remove',
|
remove: 'opentsdb-filter-remove',
|
||||||
|
@ -48,7 +48,7 @@ describe('Tag Section', () => {
|
|||||||
describe('tag editor', () => {
|
describe('tag editor', () => {
|
||||||
it('open the editor on clicking +', () => {
|
it('open the editor on clicking +', () => {
|
||||||
setup();
|
setup();
|
||||||
fireEvent.click(screen.getByTestId(testIds.open));
|
fireEvent.click(screen.getByRole('button', { name: /Add tag/ }));
|
||||||
expect(screen.getByText('add tag')).toBeInTheDocument();
|
expect(screen.getByText('add tag')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ describe('Tag Section', () => {
|
|||||||
|
|
||||||
it('should call runQuery on adding a tag', () => {
|
it('should call runQuery on adding a tag', () => {
|
||||||
setup();
|
setup();
|
||||||
fireEvent.click(screen.getByTestId(testIds.open));
|
fireEvent.click(screen.getByRole('button', { name: /Add tag/ }));
|
||||||
fireEvent.click(screen.getByText('add tag'));
|
fireEvent.click(screen.getByText('add tag'));
|
||||||
expect(onRunQuery).toHaveBeenCalled();
|
expect(onRunQuery).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -80,7 +80,7 @@ describe('Tag Section', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
setup({ query });
|
setup({ query });
|
||||||
fireEvent.click(screen.getByTestId(testIds.open));
|
fireEvent.click(screen.getByRole('button', { name: /Add tag/ }));
|
||||||
fireEvent.click(screen.getByText('add tag'));
|
fireEvent.click(screen.getByText('add tag'));
|
||||||
expect(screen.getByTestId(testIds.error)).toBeInTheDocument();
|
expect(screen.getByTestId(testIds.error)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -136,11 +136,9 @@ export function TagSection({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{!addTagMode && (
|
{!addTagMode && (
|
||||||
<label className="gf-form-label query-keyword">
|
<button className="gf-form-label" type="button" onClick={changeAddTagMode} aria-label="Add tag">
|
||||||
<button type="button" className={buttonStyles} onClick={changeAddTagMode} data-testid={testIds.open}>
|
<Icon name={'plus'} />
|
||||||
<Icon name={'plus'} />
|
</button>
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{addTagMode && (
|
{addTagMode && (
|
||||||
@ -196,19 +194,19 @@ export function TagSection({
|
|||||||
|
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
{errors && (
|
{errors && (
|
||||||
<label className="gf-form-label" title={errors} data-testid={testIds.error}>
|
<div className="gf-form-label" title={errors} data-testid={testIds.error}>
|
||||||
<Icon name={'exclamation-triangle'} color={'rgb(229, 189, 28)'} />
|
<Icon name={'exclamation-triangle'} color={'rgb(229, 189, 28)'} />
|
||||||
</label>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<label className="gf-form-label">
|
<div className="gf-form-label">
|
||||||
<button type="button" className={buttonStyles} onClick={addTag}>
|
<button type="button" className={buttonStyles} onClick={addTag}>
|
||||||
add tag
|
add tag
|
||||||
</button>
|
</button>
|
||||||
<button type="button" className={buttonStyles} onClick={changeAddTagMode}>
|
<button type="button" className={buttonStyles} onClick={changeAddTagMode}>
|
||||||
<Icon name={'times'} />
|
<Icon name={'times'} />
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -221,7 +219,6 @@ export function TagSection({
|
|||||||
|
|
||||||
export const testIds = {
|
export const testIds = {
|
||||||
section: 'opentsdb-tag',
|
section: 'opentsdb-tag',
|
||||||
open: 'opentsdb-tag-editor',
|
|
||||||
list: 'opentsdb-tag-list',
|
list: 'opentsdb-tag-list',
|
||||||
error: 'opentsdb-tag-error',
|
error: 'opentsdb-tag-error',
|
||||||
remove: 'opentsdb-tag-remove',
|
remove: 'opentsdb-tag-remove',
|
||||||
|
Loading…
Reference in New Issue
Block a user