A11y/Dashboard: Fix misc. fastpass issues (#40296)

* A11y/Dashboard: Fix misc. fastpass issues
See #39429
This commit is contained in:
kay delaney 2021-10-12 13:26:01 +01:00 committed by GitHub
parent 0ac81e9e47
commit c443f244a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 74 additions and 27 deletions

View File

@ -14,7 +14,7 @@ export const smokeTestScenario = {
e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer() e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer()
.should('be.visible') .should('be.visible')
.within(() => { .within(() => {
e2e.components.Select.input().should('be.visible').click(); e2e().get('input[id*="scenario-input-"]').should('be.visible').click();
}); });
cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').click(); cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').click();

View File

@ -14,7 +14,7 @@ e2e.scenario({
e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer() e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer()
.should('be.visible') .should('be.visible')
.within(() => { .within(() => {
e2e.components.Select.input().should('be.visible').click(); e2e().get('input[id*="scenario-input-"]').should('be.visible').click();
}); });
cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').click(); cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').click();

View File

@ -50,7 +50,7 @@ e2e.scenario({
e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer() e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer()
.should('be.visible') .should('be.visible')
.within(() => { .within(() => {
e2e.components.Select.input().eq(0).should('be.visible').click(); e2e().get('input[id*="scenario-input-"]').eq(0).should('be.visible').click();
}); });
cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').eq(0).click(); cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').eq(0).click();

View File

@ -39,6 +39,7 @@ export interface SliderFieldConfigSettings {
min: number; min: number;
max: number; max: number;
step?: number; step?: number;
ariaLabelForHandle?: string;
} }
export interface DataLinksFieldConfigSettings {} export interface DataLinksFieldConfigSettings {}

View File

@ -19,6 +19,7 @@ export interface StandardEditorProps<TValue = any, TSettings = any, TOptions = a
onChange: (value?: TValue) => void; onChange: (value?: TValue) => void;
item: StandardEditorsRegistryItem<TValue, TSettings>; item: StandardEditorsRegistryItem<TValue, TSettings>;
context: StandardEditorContext<TOptions, TState>; context: StandardEditorContext<TOptions, TState>;
id?: string;
} }
export interface StandardEditorsRegistryItem<TValue = any, TSettings = any> extends RegistryItem { export interface StandardEditorsRegistryItem<TValue = any, TSettings = any> extends RegistryItem {
editor: ComponentType<StandardEditorProps<TValue, TSettings>>; editor: ComponentType<StandardEditorProps<TValue, TSettings>>;

View File

@ -17,6 +17,8 @@ export interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
tooltip?: string; tooltip?: string;
/** For image icons */ /** For image icons */
imgSrc?: string; imgSrc?: string;
/** Alt text for imgSrc */
imgAlt?: string;
/** if true or false will show angle-down/up */ /** if true or false will show angle-down/up */
isOpen?: boolean; isOpen?: boolean;
/** Controls flex-grow: 1 */ /** Controls flex-grow: 1 */
@ -39,6 +41,7 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
className, className,
children, children,
imgSrc, imgSrc,
imgAlt,
fullWidth, fullWidth,
isOpen, isOpen,
narrow, narrow,
@ -72,12 +75,12 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
<button <button
ref={ref} ref={ref}
className={buttonStyles} className={buttonStyles}
aria-label={getButttonAriaLabel(ariaLabel, tooltip)} aria-label={getButtonAriaLabel(ariaLabel, tooltip)}
aria-expanded={isOpen} aria-expanded={isOpen}
{...rest} {...rest}
> >
{renderIcon(icon)} {renderIcon(icon)}
{imgSrc && <img className={styles.img} src={imgSrc} />} {imgSrc && <img className={styles.img} src={imgSrc} alt={imgAlt ?? ''} />}
{children && !iconOnly && <div className={contentStyles}>{children}</div>} {children && !iconOnly && <div className={contentStyles}>{children}</div>}
{isOpen === false && <Icon name="angle-down" />} {isOpen === false && <Icon name="angle-down" />}
{isOpen === true && <Icon name="angle-up" />} {isOpen === true && <Icon name="angle-up" />}
@ -94,7 +97,7 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
} }
); );
function getButttonAriaLabel(ariaLabel: string | undefined, tooltip: string | undefined) { function getButtonAriaLabel(ariaLabel: string | undefined, tooltip: string | undefined) {
return ariaLabel ? ariaLabel : tooltip ? selectors.components.PageToolbar.item(tooltip) : undefined; return ariaLabel ? ariaLabel : tooltip ? selectors.components.PageToolbar.item(tooltip) : undefined;
} }

View File

@ -22,6 +22,7 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
value, value,
onChange, onChange,
item, item,
id,
}) => { }) => {
const theme = useTheme2(); const theme = useTheme2();
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
@ -81,6 +82,7 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
value={mode} value={mode}
onChange={onModeChange} onChange={onModeChange}
className={styles.select} className={styles.select}
inputId={id}
/> />
<ColorValueEditor value={value?.fixedColor} onChange={onColorChange} /> <ColorValueEditor value={value?.fixedColor} onChange={onColorChange} />
</div> </div>
@ -97,7 +99,14 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
return ( return (
<> <>
<div style={{ marginBottom: theme.spacing(2) }}> <div style={{ marginBottom: theme.spacing(2) }}>
<Select menuShouldPortal minMenuHeight={200} options={options} value={mode} onChange={onModeChange} /> <Select
menuShouldPortal
minMenuHeight={200}
options={options}
value={mode}
onChange={onModeChange}
inputId={id}
/>
</div> </div>
<Field label="Color series by"> <Field label="Color series by">
<RadioButtonGroup value={value?.seriesBy ?? 'last'} options={seriesModes} onChange={onSeriesModeChange} /> <RadioButtonGroup value={value?.seriesBy ?? 'last'} options={seriesModes} onChange={onSeriesModeChange} />
@ -106,7 +115,9 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
); );
} }
return <Select menuShouldPortal minMenuHeight={200} options={options} value={mode} onChange={onModeChange} />; return (
<Select menuShouldPortal minMenuHeight={200} options={options} value={mode} onChange={onModeChange} inputId={id} />
);
}; };
interface ModeProps { interface ModeProps {

View File

@ -17,6 +17,7 @@ export const SliderValueEditor: React.FC<FieldConfigEditorProps<number, SliderFi
max={settings?.max || 100} max={settings?.max || 100}
step={settings?.step} step={settings?.step}
onChange={onChange} onChange={onChange}
ariaLabelForHandle={settings?.ariaLabelForHandle}
/> />
); );
}; };

View File

@ -6,6 +6,7 @@ export const StatsPickerEditor: React.FC<FieldConfigEditorProps<string[], StatsP
value, value,
onChange, onChange,
item, item,
id,
}) => { }) => {
return ( return (
<StatsPicker <StatsPicker
@ -13,6 +14,7 @@ export const StatsPickerEditor: React.FC<FieldConfigEditorProps<string[], StatsP
onChange={onChange} onChange={onChange}
allowMultiple={!!item.settings?.allowMultiple} allowMultiple={!!item.settings?.allowMultiple}
defaultStat={item.settings?.defaultStat} defaultStat={item.settings?.defaultStat}
inputId={id}
/> />
); );
}; };

View File

@ -19,6 +19,7 @@ export const Slider: FunctionComponent<SliderProps> = ({
reverse, reverse,
step, step,
value, value,
ariaLabelForHandle,
}) => { }) => {
const isHorizontal = orientation === 'horizontal'; const isHorizontal = orientation === 'horizontal';
const theme = useTheme2(); const theme = useTheme2();
@ -79,6 +80,7 @@ export const Slider: FunctionComponent<SliderProps> = ({
onAfterChange={onAfterChange} onAfterChange={onAfterChange}
vertical={!isHorizontal} vertical={!isHorizontal}
reverse={reverse} reverse={reverse}
ariaLabelForHandle={ariaLabelForHandle}
/> />
{/* Uses text input so that the number spinners are not shown */} {/* Uses text input so that the number spinners are not shown */}
<Input <Input

View File

@ -12,6 +12,7 @@ export interface SliderProps {
formatTooltipResult?: (value: number) => number; formatTooltipResult?: (value: number) => number;
onChange?: (value: number) => void; onChange?: (value: number) => void;
onAfterChange?: (value?: number) => void; onAfterChange?: (value?: number) => void;
ariaLabelForHandle?: string;
} }
export interface RangeSliderProps { export interface RangeSliderProps {

View File

@ -15,6 +15,7 @@ export interface Props {
className?: string; className?: string;
width?: number; width?: number;
menuPlacement?: 'auto' | 'bottom' | 'top'; menuPlacement?: 'auto' | 'bottom' | 'top';
inputId?: string;
} }
export class StatsPicker extends PureComponent<Props> { export class StatsPicker extends PureComponent<Props> {
@ -63,7 +64,7 @@ export class StatsPicker extends PureComponent<Props> {
}; };
render() { render() {
const { stats, allowMultiple, defaultStat, placeholder, className, menuPlacement, width } = this.props; const { stats, allowMultiple, defaultStat, placeholder, className, menuPlacement, width, inputId } = this.props;
const select = fieldReducers.selectOptions(stats); const select = fieldReducers.selectOptions(stats);
return ( return (
@ -79,6 +80,7 @@ export class StatsPicker extends PureComponent<Props> {
placeholder={placeholder} placeholder={placeholder}
onChange={this.onSelectionChange} onChange={this.onSelectionChange}
menuPlacement={menuPlacement} menuPlacement={menuPlacement}
inputId={inputId}
/> />
); );
} }

View File

@ -151,11 +151,13 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
renderInput(threshold: ThresholdWithKey, styles: ThresholdStyles, idx: number) { renderInput(threshold: ThresholdWithKey, styles: ThresholdStyles, idx: number) {
const isPercent = this.props.thresholds.mode === ThresholdsMode.Percentage; const isPercent = this.props.thresholds.mode === ThresholdsMode.Percentage;
const ariaLabel = `Threshold ${idx + 1}`;
if (!isFinite(threshold.value)) { if (!isFinite(threshold.value)) {
return ( return (
<Input <Input
type="text" type="text"
value={'Base'} value={'Base'}
aria-label={ariaLabel}
disabled disabled
prefix={ prefix={
<div className={styles.colorPicker}> <div className={styles.colorPicker}>
@ -177,6 +179,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
key={isPercent.toString()} key={isPercent.toString()}
onChange={(event: ChangeEvent<HTMLInputElement>) => this.onChangeThresholdValue(event, threshold)} onChange={(event: ChangeEvent<HTMLInputElement>) => this.onChangeThresholdValue(event, threshold)}
value={threshold.value} value={threshold.value}
aria-label={ariaLabel}
ref={idx === 0 ? this.latestThresholdInputRef : null} ref={idx === 0 ? this.latestThresholdInputRef : null}
onBlur={this.onBlur} onBlur={this.onBlur}
prefix={ prefix={

View File

@ -225,7 +225,7 @@ const AddPanelWidgetHandle: React.FC<AddPanelWidgetHandleProps> = ({ children, o
)} )}
{children && <span>{children}</span>} {children && <span>{children}</span>}
<div className="flex-grow-1" /> <div className="flex-grow-1" />
<IconButton name="times" onClick={onCancel} surface="header" /> <IconButton aria-label="Close 'Add Panel' widget" name="times" onClick={onCancel} surface="header" />
</div> </div>
); );
}; };

View File

@ -83,9 +83,7 @@ export const OptionsPaneCategory: FC<OptionsPaneCategoryProps> = React.memo(
<div className={cx(styles.toggle, 'editor-options-group-toggle')}> <div className={cx(styles.toggle, 'editor-options-group-toggle')}>
<Icon name={isExpanded ? 'angle-down' : 'angle-right'} /> <Icon name={isExpanded ? 'angle-down' : 'angle-right'} />
</div> </div>
<div className={styles.title} role="heading"> <h6 className={styles.title}>{renderTitle(isExpanded)}</h6>
{renderTitle(isExpanded)}
</div>
</div> </div>
{isExpanded && <div className={bodyStyles}>{children}</div>} {isExpanded && <div className={bodyStyles}>{children}</div>}
</div> </div>
@ -108,6 +106,10 @@ const getStyles = (theme: GrafanaTheme2) => {
title: css` title: css`
flex-grow: 1; flex-grow: 1;
overflow: hidden; overflow: hidden;
line-height: 1.5;
font-size: 1rem;
font-weight: ${theme.typography.fontWeightMedium};
margin: 0;
`, `,
header: css` header: css`
display: flex; display: flex;

View File

@ -39,6 +39,7 @@ export function getPanelFrameCategory(props: OptionPaneRenderProps): OptionsPane
render: function renderDescription() { render: function renderDescription() {
return ( return (
<TextArea <TextArea
id="description-text-area"
defaultValue={panel.description} defaultValue={panel.description}
onBlur={(e) => onPanelConfigChange('description', e.currentTarget.value)} onBlur={(e) => onPanelConfigChange('description', e.currentTarget.value)}
/> />
@ -96,6 +97,7 @@ export function getPanelFrameCategory(props: OptionPaneRenderProps): OptionsPane
render: function renderRepeatOptions() { render: function renderRepeatOptions() {
return ( return (
<RepeatRowSelect <RepeatRowSelect
id="repeat-by-variable-select"
repeat={panel.repeat} repeat={panel.repeat}
onChange={(value?: string | null) => { onChange={(value?: string | null) => {
onPanelConfigChange('repeat', value); onPanelConfigChange('repeat', value);

View File

@ -98,7 +98,7 @@ export function getVizualizationOptions(props: OptionPaneRenderProps): OptionsPa
); );
}; };
return <Editor value={value} onChange={onChange} item={fieldOption} context={context} />; return <Editor value={value} onChange={onChange} item={fieldOption} context={context} id={fieldOption.id} />;
}, },
}) })
); );
@ -161,6 +161,7 @@ export function fillOptionsPaneItems(
}} }}
item={pluginOption} item={pluginOption}
context={context} context={context}
id={pluginOption.id}
/> />
); );
}, },

View File

@ -7,11 +7,12 @@ import { getVariables } from '../../../variables/state/selectors';
import { StoreState } from '../../../../types'; import { StoreState } from '../../../../types';
export interface Props { export interface Props {
id?: string;
repeat?: string | null; repeat?: string | null;
onChange: (name: string | null) => void; onChange: (name: string | null) => void;
} }
export const RepeatRowSelect: FC<Props> = ({ repeat, onChange }) => { export const RepeatRowSelect: FC<Props> = ({ repeat, onChange, id }) => {
const variables = useSelector((state: StoreState) => getVariables(state)); const variables = useSelector((state: StoreState) => getVariables(state));
const variableOptions = useMemo(() => { const variableOptions = useMemo(() => {
@ -36,5 +37,5 @@ export const RepeatRowSelect: FC<Props> = ({ repeat, onChange }) => {
const onSelectChange = useCallback((option: SelectableValue<string | null>) => onChange(option.value!), [onChange]); const onSelectChange = useCallback((option: SelectableValue<string | null>) => onChange(option.value!), [onChange]);
return <Select menuShouldPortal value={repeat} onChange={onSelectChange} options={variableOptions} />; return <Select inputId={id} menuShouldPortal value={repeat} onChange={onSelectChange} options={variableOptions} />;
}; };

View File

@ -114,6 +114,7 @@ export class ShareLink extends PureComponent<Props, State> {
<Field label="Link URL"> <Field label="Link URL">
<Input <Input
id="link-url-input"
value={shareUrl} value={shareUrl}
readOnly readOnly
addonAfter={ addonAfter={

View File

@ -41,7 +41,7 @@ export const PanelTypeCard: React.FC<Props> = ({
onClick={disabled ? undefined : onClick} onClick={disabled ? undefined : onClick}
title={isCurrent ? 'Click again to close this section' : plugin.name} title={isCurrent ? 'Click again to close this section' : plugin.name}
> >
<img className={styles.img} src={plugin.info.logos.small} alt={`${plugin.name} logo`} /> <img className={styles.img} src={plugin.info.logos.small} alt="" />
<div className={styles.itemContent}> <div className={styles.itemContent}>
<div className={styles.name}>{title}</div> <div className={styles.name}>{title}</div>

View File

@ -203,7 +203,9 @@ export class QueryGroup extends PureComponent<Props, State> {
return ( return (
<div> <div>
<div className={styles.dataSourceRow}> <div className={styles.dataSourceRow}>
<InlineFormLabel width={'auto'}>Data source</InlineFormLabel> <InlineFormLabel htmlFor="data-source-picker" width={'auto'}>
Data source
</InlineFormLabel>
<div className={styles.dataSourceRowItem}> <div className={styles.dataSourceRowItem}>
<DataSourcePicker <DataSourcePicker
onChange={this.onChangeDataSource} onChange={this.onChangeDataSource}

View File

@ -175,6 +175,7 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
value={options.find((item) => item.value === query.scenarioId)} value={options.find((item) => item.value === query.scenarioId)}
onChange={onScenarioChange} onChange={onScenarioChange}
width={32} width={32}
inputId={`scenario-input-${query.refId}`}
/> />
</InlineField> </InlineField>
{currentScenario?.stringInput && ( {currentScenario?.stringInput && (

View File

@ -16,9 +16,10 @@
></gf-form-switch> ></gf-form-switch>
<div class="gf-form" ng-if="ctrl.panel.lines"> <div class="gf-form" ng-if="ctrl.panel.lines">
<label class="gf-form-label width-8">Line width</label> <label class="gf-form-label width-8" for="linewidth-select-input">Line width</label>
<div class="gf-form-select-wrapper max-width-5"> <div class="gf-form-select-wrapper max-width-5">
<select <select
id="linewidth-select-input"
class="gf-form-input" class="gf-form-input"
ng-model="ctrl.panel.linewidth" ng-model="ctrl.panel.linewidth"
ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"
@ -37,9 +38,10 @@
></gf-form-switch> ></gf-form-switch>
<div class="gf-form" ng-if="ctrl.panel.lines"> <div class="gf-form" ng-if="ctrl.panel.lines">
<label class="gf-form-label width-8">Area fill</label> <label class="gf-form-label width-8" for="fill-select-input">Area fill</label>
<div class="gf-form-select-wrapper max-width-5"> <div class="gf-form-select-wrapper max-width-5">
<select <select
id="fill-select-input"
class="gf-form-input" class="gf-form-input"
ng-model="ctrl.panel.fill" ng-model="ctrl.panel.fill"
ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"
@ -69,9 +71,10 @@
></gf-form-switch> ></gf-form-switch>
<div class="gf-form" ng-if="ctrl.panel.points"> <div class="gf-form" ng-if="ctrl.panel.points">
<label class="gf-form-label width-8">Point Radius</label> <label class="gf-form-label width-8" for="pointradius-select-input">Point Radius</label>
<div class="gf-form-select-wrapper max-width-5"> <div class="gf-form-select-wrapper max-width-5">
<select <select
id="pointradius-select-input"
class="gf-form-input" class="gf-form-input"
ng-model="ctrl.panel.pointradius" ng-model="ctrl.panel.pointradius"
ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]" ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]"
@ -87,7 +90,6 @@
checked="ctrl.panel.options.alertThreshold" checked="ctrl.panel.options.alertThreshold"
on-change="ctrl.render()" on-change="ctrl.render()"
></gf-form-switch> ></gf-form-switch>
</div> </div>
<div class="gf-form-group"> <div class="gf-form-group">
@ -110,9 +112,10 @@
> >
</gf-form-switch> </gf-form-switch>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-7">Null value</label> <label class="gf-form-label width-7" for="null-value-select-input">Null value</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select <select
id="null-value-select-input"
class="gf-form-input max-width-9" class="gf-form-input max-width-9"
ng-model="ctrl.panel.nullPointMode" ng-model="ctrl.panel.nullPointMode"
ng-options="f for f in ['connected', 'null', 'null as zero']" ng-options="f for f in ['connected', 'null', 'null as zero']"
@ -125,9 +128,10 @@
<div class="gf-form-group"> <div class="gf-form-group">
<h5 class="section-heading">Hover tooltip</h5> <h5 class="section-heading">Hover tooltip</h5>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-9">Mode</label> <label class="gf-form-label width-9" for="tooltip-mode-select-input">Mode</label>
<div class="gf-form-select-wrapper max-width-8"> <div class="gf-form-select-wrapper max-width-8">
<select <select
id="tooltip-mode-select-input"
class="gf-form-input" class="gf-form-input"
ng-model="ctrl.panel.tooltip.shared" ng-model="ctrl.panel.tooltip.shared"
ng-options="f.value as f.text for f in [{text: 'All series', value: true}, {text: 'Single', value: false}]" ng-options="f.value as f.text for f in [{text: 'All series', value: true}, {text: 'Single', value: false}]"
@ -136,9 +140,10 @@
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-9">Sort order</label> <label class="gf-form-label width-9" for="tooltip-sort-select-input">Sort order</label>
<div class="gf-form-select-wrapper max-width-8"> <div class="gf-form-select-wrapper max-width-8">
<select <select
id="tooltip-sort-select-input"
class="gf-form-input" class="gf-form-input"
ng-model="ctrl.panel.tooltip.sort" ng-model="ctrl.panel.tooltip.sort"
ng-options="f.value as f.text for f in [{text: 'None', value: 0}, {text: 'Increasing', value: 1}, {text: 'Decreasing', value: 2}]" ng-options="f.value as f.text for f in [{text: 'None', value: 0}, {text: 'Increasing', value: 1}, {text: 'Decreasing', value: 2}]"

View File

@ -5,7 +5,7 @@ import { Select } from '@grafana/ui';
export const ThresholdsStyleEditor: React.FC< export const ThresholdsStyleEditor: React.FC<
FieldOverrideEditorProps<SelectableValue<{ mode: GraphTresholdsStyleMode }>, any> FieldOverrideEditorProps<SelectableValue<{ mode: GraphTresholdsStyleMode }>, any>
> = ({ item, value, onChange }) => { > = ({ item, value, onChange, id }) => {
const onChangeCb = useCallback( const onChangeCb = useCallback(
(v: SelectableValue<GraphTresholdsStyleMode>) => { (v: SelectableValue<GraphTresholdsStyleMode>) => {
onChange({ onChange({
@ -14,5 +14,7 @@ export const ThresholdsStyleEditor: React.FC<
}, },
[onChange] [onChange]
); );
return <Select menuShouldPortal value={value.mode} options={item.settings.options} onChange={onChangeCb} />; return (
<Select inputId={id} menuShouldPortal value={value.mode} options={item.settings.options} onChange={onChangeCb} />
);
}; };

View File

@ -95,6 +95,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
min: 0, min: 0,
max: 10, max: 10,
step: 1, step: 1,
ariaLabelForHandle: 'Line width',
}, },
showIf: (c) => c.drawStyle !== GraphDrawStyle.Points, showIf: (c) => c.drawStyle !== GraphDrawStyle.Points,
}) })
@ -107,6 +108,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
min: 0, min: 0,
max: 100, max: 100,
step: 1, step: 1,
ariaLabelForHandle: 'Fill opacity',
}, },
showIf: (c) => c.drawStyle !== GraphDrawStyle.Points, showIf: (c) => c.drawStyle !== GraphDrawStyle.Points,
}) })
@ -173,6 +175,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
min: 1, min: 1,
max: 40, max: 40,
step: 1, step: 1,
ariaLabelForHandle: 'Point size',
}, },
showIf: (c) => c.showPoints !== VisibilityMode.Never || c.drawStyle === GraphDrawStyle.Points, showIf: (c) => c.showPoints !== VisibilityMode.Never || c.drawStyle === GraphDrawStyle.Points,
}); });