A11y: Fix misc. fastpass issues (#41088)

This commit is contained in:
kay delaney 2021-11-01 14:45:23 +00:00 committed by GitHub
parent d98cb8274c
commit 124c3f537f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 48 additions and 21 deletions

View File

@ -96,7 +96,7 @@ class DarkColors implements ThemeColorsBase<Partial<ThemeRichColor>> {
text = { text = {
primary: `rgb(${this.whiteBase})`, primary: `rgb(${this.whiteBase})`,
secondary: `rgba(${this.whiteBase}, 0.65)`, secondary: `rgba(${this.whiteBase}, 0.65)`,
disabled: `rgba(${this.whiteBase}, 0.57)`, disabled: `rgba(${this.whiteBase}, 0.58)`,
link: palette.blueDarkText, link: palette.blueDarkText,
maxContrast: palette.white, maxContrast: palette.white,
}; };

View File

@ -60,7 +60,7 @@ export const Alert = React.forwardRef<HTMLDivElement, Props>(
{/* If onRemove is specified, giving preference to onRemove */} {/* If onRemove is specified, giving preference to onRemove */}
{onRemove && !buttonContent && ( {onRemove && !buttonContent && (
<div className={styles.close}> <div className={styles.close}>
<IconButton name="times" onClick={onRemove} size="lg" type="button" /> <IconButton aria-label="Close alert" name="times" onClick={onRemove} size="lg" type="button" />
</div> </div>
)} )}
{onRemove && buttonContent && ( {onRemove && buttonContent && (

View File

@ -135,7 +135,7 @@ const DateTimeInput: FC<InputProps> = ({ date, label, onChange, isFullscreen, on
} }
}, [internalDate.value, onChange]); }, [internalDate.value, onChange]);
const icon = <Button icon="calendar-alt" variant="secondary" onClick={onOpen} />; const icon = <Button aria-label="Time picker" icon="calendar-alt" variant="secondary" onClick={onOpen} />;
return ( return (
<InlineField <InlineField
label={label} label={label}
@ -195,7 +195,9 @@ const DateTimeCalendar: FC<DateTimeCalendarProps> = ({ date, onClose, onChange,
prev2Label={null} prev2Label={null}
value={internalDate} value={internalDate}
nextLabel={<Icon name="angle-right" />} nextLabel={<Icon name="angle-right" />}
nextAriaLabel="Next month"
prevLabel={<Icon name="angle-left" />} prevLabel={<Icon name="angle-left" />}
prevAriaLabel="Previous month"
onChange={onChangeDate} onChange={onChangeDate}
locale="en" locale="en"
className={calendarStyles.body} className={calendarStyles.body}

View File

@ -5,7 +5,7 @@ import { Select } from '../Select/Select';
import { useFieldDisplayNames, useSelectOptions, frameHasName } from './utils'; import { useFieldDisplayNames, useSelectOptions, frameHasName } from './utils';
export const FieldNameMatcherEditor = memo<MatcherUIProps<string>>((props) => { export const FieldNameMatcherEditor = memo<MatcherUIProps<string>>((props) => {
const { data, options, onChange: onChangeFromProps } = props; const { data, options, onChange: onChangeFromProps, id } = props;
const names = useFieldDisplayNames(data); const names = useFieldDisplayNames(data);
const selectOptions = useSelectOptions(names, options); const selectOptions = useSelectOptions(names, options);
@ -20,7 +20,7 @@ export const FieldNameMatcherEditor = memo<MatcherUIProps<string>>((props) => {
); );
const selectedOption = selectOptions.find((v) => v.value === options); const selectedOption = selectOptions.find((v) => v.value === options);
return <Select menuShouldPortal value={selectedOption} options={selectOptions} onChange={onChange} />; return <Select menuShouldPortal value={selectedOption} options={selectOptions} onChange={onChange} inputId={id} />;
}); });
FieldNameMatcherEditor.displayName = 'FieldNameMatcherEditor'; FieldNameMatcherEditor.displayName = 'FieldNameMatcherEditor';

View File

@ -4,7 +4,7 @@ import { FieldMatcherID, fieldMatchers, SelectableValue, FieldType, DataFrame }
import { Select } from '../Select/Select'; import { Select } from '../Select/Select';
export const FieldTypeMatcherEditor = memo<MatcherUIProps<string>>((props) => { export const FieldTypeMatcherEditor = memo<MatcherUIProps<string>>((props) => {
const { data, options, onChange: onChangeFromProps } = props; const { data, options, onChange: onChangeFromProps, id } = props;
const counts = useFieldCounts(data); const counts = useFieldCounts(data);
const selectOptions = useSelectOptions(counts, options); const selectOptions = useSelectOptions(counts, options);
@ -16,7 +16,7 @@ export const FieldTypeMatcherEditor = memo<MatcherUIProps<string>>((props) => {
); );
const selectedOption = selectOptions.find((v) => v.value === options); const selectedOption = selectOptions.find((v) => v.value === options);
return <Select menuShouldPortal value={selectedOption} options={selectOptions} onChange={onChange} />; return <Select inputId={id} value={selectedOption} options={selectOptions} onChange={onChange} menuShouldPortal />;
}); });
FieldTypeMatcherEditor.displayName = 'FieldTypeMatcherEditor'; FieldTypeMatcherEditor.displayName = 'FieldTypeMatcherEditor';

View File

@ -10,6 +10,7 @@ export interface FieldMatcherUIRegistryItem<TOptions> extends RegistryItem {
export interface MatcherUIProps<T> { export interface MatcherUIProps<T> {
matcher: FieldMatcherInfo<T>; matcher: FieldMatcherInfo<T>;
id?: string;
data: DataFrame[]; data: DataFrame[];
options: T; options: T;
onChange: (options: T) => void; onChange: (options: T) => void;

View File

@ -3,6 +3,7 @@ import { cx } from '@emotion/css';
export interface CardProps { export interface CardProps {
logoUrl?: string; logoUrl?: string;
logoAlt?: string;
title: string; title: string;
description?: string; description?: string;
labels?: React.ReactNode; labels?: React.ReactNode;
@ -14,6 +15,7 @@ export interface CardProps {
export const Card: React.FC<CardProps> = ({ export const Card: React.FC<CardProps> = ({
logoUrl, logoUrl,
logoAlt,
title, title,
description, description,
labels, labels,
@ -26,7 +28,7 @@ export const Card: React.FC<CardProps> = ({
return ( return (
<div className={mainClassName} onClick={onClick} aria-label={ariaLabel}> <div className={mainClassName} onClick={onClick} aria-label={ariaLabel}>
{logoUrl && <img className="add-data-source-item-logo" src={logoUrl} />} {logoUrl && <img className="add-data-source-item-logo" src={logoUrl} alt={logoAlt ?? ''} />}
<div className="add-data-source-item-text-wrapper"> <div className="add-data-source-item-text-wrapper">
<span className="add-data-source-item-text">{title}</span> <span className="add-data-source-item-text">{title}</span>
{description && <span className="add-data-source-item-desc">{description}</span>} {description && <span className="add-data-source-item-desc">{description}</span>}

View File

@ -9,6 +9,7 @@ export type OrgSelectItem = SelectableValue<Organization>;
export interface Props { export interface Props {
onSelected: (org: OrgSelectItem) => void; onSelected: (org: OrgSelectItem) => void;
className?: string; className?: string;
inputId?: string;
} }
export interface State { export interface State {
@ -43,12 +44,13 @@ export class OrgPicker extends PureComponent<Props, State> {
}; };
render() { render() {
const { className, onSelected } = this.props; const { className, onSelected, inputId } = this.props;
const { isLoading } = this.state; const { isLoading } = this.state;
return ( return (
<AsyncSelect <AsyncSelect
menuShouldPortal menuShouldPortal
inputId={inputId}
className={className} className={className}
isLoading={isLoading} isLoading={isLoading}
defaultOptions={true} defaultOptions={true}

View File

@ -121,6 +121,7 @@ export function getFieldOverrideCategories(props: OptionPaneRenderProps): Option
render: function renderMatcherUI() { render: function renderMatcherUI() {
return ( return (
<matcherUi.component <matcherUi.component
id={`${matcherUi.matcher.id}-${idx}`}
matcher={matcherUi.matcher} matcher={matcherUi.matcher}
data={props.data?.series ?? []} data={props.data?.series ?? []}
options={override.matcher.options} options={override.matcher.options}

View File

@ -132,13 +132,14 @@ export const InspectDataOptions: FC<Props> = ({
description="Table data is formatted with options defined in the Field and Override tabs." description="Table data is formatted with options defined in the Field and Override tabs."
> >
<Switch <Switch
id="formatted-data-toggle"
value={!!options.withFieldConfig} value={!!options.withFieldConfig}
onChange={() => onOptionsChange({ ...options, withFieldConfig: !options.withFieldConfig })} onChange={() => onOptionsChange({ ...options, withFieldConfig: !options.withFieldConfig })}
/> />
</Field> </Field>
)} )}
<Field label="Download for Excel" description="Adds header to CSV for use with Excel"> <Field label="Download for Excel" description="Adds header to CSV for use with Excel">
<Switch value={downloadForExcel} onChange={toggleDownloadForExcel} /> <Switch id="excel-toggle" value={downloadForExcel} onChange={toggleDownloadForExcel} />
</Field> </Field>
</HorizontalGroup> </HorizontalGroup>
</VerticalGroup> </VerticalGroup>

View File

@ -132,7 +132,13 @@ export class InspectJSONTab extends PureComponent<Props, State> {
<> <>
<div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}> <div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}>
<Field label="Select source" className="flex-grow-1"> <Field label="Select source" className="flex-grow-1">
<Select menuShouldPortal options={jsonOptions} value={selected} onChange={this.onSelectChanged} /> <Select
inputId="select-source-dropdown"
options={jsonOptions}
value={selected}
onChange={this.onSelectChanged}
menuShouldPortal
/>
</Field> </Field>
{this.hasPanelJSON && isPanelJSON && canEdit && ( {this.hasPanelJSON && isPanelJSON && canEdit && (
<Button className={styles.toolbarItem} onClick={this.onApplyPanelModel}> <Button className={styles.toolbarItem} onClick={this.onApplyPanelModel}>

View File

@ -2,6 +2,7 @@ import React, { PropsWithChildren, ReactElement } from 'react';
import { InlineFormLabel, Select, useStyles } from '@grafana/ui'; import { InlineFormLabel, Select, useStyles } from '@grafana/ui';
import { GrafanaTheme, SelectableValue } from '@grafana/data'; import { GrafanaTheme, SelectableValue } from '@grafana/data';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId';
interface VariableSelectFieldProps<T> { interface VariableSelectFieldProps<T> {
name: string; name: string;
@ -25,7 +26,8 @@ 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}`; const uniqueId = useUniqueId();
const inputId = `variable-select-input-${name}-${uniqueId}`;
return ( return (
<> <>

View File

@ -1,5 +1,6 @@
import React, { ChangeEvent, PropsWithChildren, ReactElement } from 'react'; import React, { ChangeEvent, PropsWithChildren, ReactElement } from 'react';
import { InlineField, InlineSwitch } from '@grafana/ui'; import { InlineField, InlineSwitch } from '@grafana/ui';
import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId';
interface VariableSwitchFieldProps { interface VariableSwitchFieldProps {
value: boolean; value: boolean;
name: string; name: string;
@ -15,9 +16,16 @@ export function VariableSwitchField({
onChange, onChange,
ariaLabel, ariaLabel,
}: PropsWithChildren<VariableSwitchFieldProps>): ReactElement { }: PropsWithChildren<VariableSwitchFieldProps>): ReactElement {
const uniqueId = useUniqueId();
return ( return (
<InlineField label={name} labelWidth={20} tooltip={tooltip}> <InlineField label={name} labelWidth={20} tooltip={tooltip}>
<InlineSwitch label={name} value={value} onChange={onChange} aria-label={ariaLabel} /> <InlineSwitch
id={`var-switch-${uniqueId}`}
label={name}
value={value}
onChange={onChange}
aria-label={ariaLabel}
/>
</InlineField> </InlineField>
); );
} }

View File

@ -22,9 +22,10 @@
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-6">Scale</label> <label for="yaxis-scale-select-{{$index}}" class="gf-form-label width-6">Scale</label>
<div class="gf-form-select-wrapper max-width-20"> <div class="gf-form-select-wrapper max-width-20">
<select <select
id="yaxis-scale-select-{{$index}}"
class="gf-form-input" class="gf-form-input"
ng-model="yaxis.logBase" ng-model="yaxis.logBase"
ng-options="v as k for (k, v) in ctrl.logScales" ng-options="v as k for (k, v) in ctrl.logScales"
@ -75,8 +76,9 @@
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-6">Label</label> <label for="yaxis-label-select-{{$index}}" class="gf-form-label width-6">Label</label>
<input <input
id="yaxis-label-select-{{$index}}"
type="text" type="text"
class="gf-form-input max-width-20" class="gf-form-input max-width-20"
ng-model="yaxis.label" ng-model="yaxis.label"
@ -125,15 +127,15 @@
></gf-form-switch> ></gf-form-switch>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-6">Mode</label> <label for="xaxis-mode-select" class="gf-form-label width-6">Mode</label>
<div class="gf-form-select-wrapper max-width-15"> <div class="gf-form-select-wrapper max-width-15">
<select <select
id="xaxis-mode-select"
class="gf-form-input" class="gf-form-input"
ng-model="ctrl.panel.xaxis.mode" ng-model="ctrl.panel.xaxis.mode"
ng-options="v as k for (k, v) in ctrl.xAxisModes" ng-options="v as k for (k, v) in ctrl.xAxisModes"
ng-change="ctrl.xAxisModeChanged()" ng-change="ctrl.xAxisModeChanged()"
> ></select>
</select>
</div> </div>
</div> </div>

View File

@ -101,7 +101,7 @@ $text-color-strong: #fff;
$text-color: rgb(204, 204, 220); $text-color: rgb(204, 204, 220);
$text-color-semi-weak: rgba(204, 204, 220, 0.65); $text-color-semi-weak: rgba(204, 204, 220, 0.65);
$text-color-weak: rgba(204, 204, 220, 0.65); $text-color-weak: rgba(204, 204, 220, 0.65);
$text-color-faint: rgba(204, 204, 220, 0.57); $text-color-faint: rgba(204, 204, 220, 0.58);
$text-color-emphasis: #fff; $text-color-emphasis: #fff;
$text-blue: #6E9FFF; $text-blue: #6E9FFF;
@ -115,7 +115,7 @@ $brand-gradient-vertical: linear-gradient(#f05a28 30%, #fbca0a 99%);
// Links // Links
// ------------------------- // -------------------------
$link-color: rgb(204, 204, 220); $link-color: rgb(204, 204, 220);
$link-color-disabled: rgba(204, 204, 220, 0.57); $link-color-disabled: rgba(204, 204, 220, 0.58);
$link-hover-color: #fff; $link-hover-color: #fff;
$external-link-color: #6E9FFF; $external-link-color: #6E9FFF;
@ -216,7 +216,7 @@ $input-border-color: rgba(204, 204, 220, 0.15);
$input-box-shadow: none; $input-box-shadow: none;
$input-border-focus: #6E9FFF; $input-border-focus: #6E9FFF;
$input-box-shadow-focus: #6E9FFF !default; $input-box-shadow-focus: #6E9FFF !default;
$input-color-placeholder: rgba(204, 204, 220, 0.57); $input-color-placeholder: rgba(204, 204, 220, 0.58);
$input-label-bg: #22252b; $input-label-bg: #22252b;
$input-color-select-arrow: $white; $input-color-select-arrow: $white;