mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
A11y: enable rule jsx-a11y/anchor-is-valid (#56690)
This commit is contained in:
parent
7eac79b5f8
commit
e402a8f27d
@ -72,7 +72,6 @@
|
||||
// rules marked "off" are those left in the recommended preset we need to fix
|
||||
// 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/anchor-is-valid": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/interactive-supports-focus": "off",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
|
@ -302,3 +302,12 @@ export function getPropertiesForVariant(theme: GrafanaTheme2, variant: ButtonVar
|
||||
return getButtonVariantStyles(theme, theme.colors.primary, fill);
|
||||
}
|
||||
}
|
||||
|
||||
export const clearButtonStyles = (theme: GrafanaTheme2) => {
|
||||
return css`
|
||||
background: transparent;
|
||||
color: ${theme.colors.text.primary};
|
||||
border: none;
|
||||
padding: 0;
|
||||
`;
|
||||
};
|
||||
|
@ -7,9 +7,9 @@ import { Segment, Icon, SegmentSection } from '@grafana/ui';
|
||||
import { SegmentSyncProps } from './Segment';
|
||||
|
||||
const AddButton = (
|
||||
<a className="gf-form-label query-part">
|
||||
<span className="gf-form-label query-part">
|
||||
<Icon name="plus-circle" />
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
|
||||
const toOption = (value: any) => ({ label: value, value: value });
|
||||
|
@ -9,9 +9,9 @@ import { SegmentAsync, Icon, SegmentSection } from '@grafana/ui';
|
||||
import { SegmentAsyncProps } from './SegmentAsync';
|
||||
|
||||
const AddButton = (
|
||||
<a className="gf-form-label query-part">
|
||||
<span className="gf-form-label query-part">
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
|
||||
const toOption = (value: any) => ({ label: value, value: value });
|
||||
|
@ -87,14 +87,15 @@ export const InputWithAutoFocus = () => {
|
||||
{inputComponents.map((InputComponent: any, i: number) => (
|
||||
<InputComponent initialValue="test" key={i} />
|
||||
))}
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
className="gf-form-label query-part"
|
||||
onClick={() => {
|
||||
setInputComponents([...inputComponents, InputComponent]);
|
||||
}}
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
</button>
|
||||
</SegmentFrame>
|
||||
);
|
||||
};
|
||||
|
@ -191,7 +191,7 @@ export { RangeSlider } from './Slider/RangeSlider';
|
||||
export { Form } from './Forms/Form';
|
||||
export { sharedInputStyle } from './Forms/commonStyles';
|
||||
export { InputControl } from './InputControl';
|
||||
export { Button, LinkButton, type ButtonVariant, ButtonGroup, type ButtonProps } from './Button';
|
||||
export { Button, LinkButton, type ButtonVariant, ButtonGroup, type ButtonProps, clearButtonStyles } from './Button';
|
||||
export { ToolbarButton, ToolbarButtonRow } from './ToolbarButton';
|
||||
export { ValuePicker } from './ValuePicker/ValuePicker';
|
||||
export { fieldMatchersUI } from './MatchersUI/fieldMatchersUI';
|
||||
|
@ -83,6 +83,10 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
&:hover small {
|
||||
text-decoration: none;
|
||||
}
|
||||
/* Adapt styles when changing from a element into button */
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
border: none;
|
||||
`,
|
||||
TracePageHeaderDetailToggle: css`
|
||||
label: TracePageHeaderDetailToggle;
|
||||
@ -236,7 +240,8 @@ export default function TracePageHeader(props: TracePageHeaderEmbedProps) {
|
||||
<div className={styles.TracePageHeaderTitleRow}>
|
||||
{links && links.length > 0 && <ExternalLinks links={links} className={styles.TracePageHeaderBack} />}
|
||||
{canCollapse ? (
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
className={styles.TracePageHeaderTitleLink}
|
||||
onClick={onSlimViewClicked}
|
||||
role="switch"
|
||||
@ -249,7 +254,7 @@ export default function TracePageHeader(props: TracePageHeaderEmbedProps) {
|
||||
)}
|
||||
/>
|
||||
{title}
|
||||
</a>
|
||||
</button>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
|
@ -240,6 +240,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => {
|
||||
&:hover > small {
|
||||
color: ${autoColor(theme, '#000')};
|
||||
}
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: none;
|
||||
`,
|
||||
nameDetailExpanded: css`
|
||||
label: nameDetailExpanded;
|
||||
@ -437,7 +440,8 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
||||
addHoverIndentGuideId={addHoverIndentGuideId}
|
||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||
/>
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
className={cx(styles.name, { [styles.nameDetailExpanded]: isDetailExpanded })}
|
||||
aria-checked={isDetailExpanded}
|
||||
title={labelDetail}
|
||||
@ -478,7 +482,7 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
||||
</span>
|
||||
<small className={styles.endpointName}>{rpc ? rpc.operationName : operationName}</small>
|
||||
<small className={styles.endpointName}> {this.getSpanBarLabel(span, spanBarOptions, label)}</small>
|
||||
</a>
|
||||
</button>
|
||||
{createSpanLink &&
|
||||
(() => {
|
||||
const links = createSpanLink(span);
|
||||
|
@ -76,8 +76,9 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
||||
|
||||
return (
|
||||
<div className={classes} data-testid="dashboard-row-container">
|
||||
<a
|
||||
<button
|
||||
className="dashboard-row__title pointer"
|
||||
type="button"
|
||||
data-testid={selectors.components.DashboardRow.title(title)}
|
||||
onClick={this.onToggle}
|
||||
>
|
||||
@ -86,7 +87,7 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
||||
<span className="dashboard-row__panel_count">
|
||||
({count} {panels})
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
{canEdit && (
|
||||
<div className="dashboard-row__actions">
|
||||
<RowOptionsButton
|
||||
@ -94,9 +95,9 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
||||
repeat={this.props.panel.repeat}
|
||||
onUpdate={this.onUpdate}
|
||||
/>
|
||||
<a className="pointer" onClick={this.onDelete} role="button" aria-label="Delete row">
|
||||
<button type="button" className="pointer" onClick={this.onDelete} aria-label="Delete row">
|
||||
<Icon name="trash-alt" />
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{this.props.panel.collapsed === true && (
|
||||
|
@ -21,16 +21,16 @@ export const RowOptionsButton: FC<RowOptionsButtonProps> = ({ repeat, title, onU
|
||||
<ModalsController>
|
||||
{({ showModal, hideModal }) => {
|
||||
return (
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
className="pointer"
|
||||
role="button"
|
||||
aria-label="Row options"
|
||||
onClick={() => {
|
||||
showModal(RowOptionsModal, { title, repeat, onDismiss: hideModal, onUpdate: onUpdateChange(hideModal) });
|
||||
}}
|
||||
>
|
||||
<Icon name="cog" />
|
||||
</a>
|
||||
</button>
|
||||
);
|
||||
}}
|
||||
</ModalsController>
|
||||
|
@ -52,9 +52,9 @@ export const REMOVE_FILTER_KEY = '-- remove filter --';
|
||||
const REMOVE_VALUE = { label: REMOVE_FILTER_KEY, value: REMOVE_FILTER_KEY };
|
||||
|
||||
const plusSegment: ReactElement = (
|
||||
<a className="gf-form-label query-part" aria-label="Add Filter">
|
||||
<span className="gf-form-label query-part" aria-label="Add Filter">
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
|
||||
const fetchFilterKeys = async (
|
||||
|
@ -16,7 +16,7 @@ import { VariableOption, VariableWithMultiSupport, VariableWithOptions } from '.
|
||||
import { toKeyedVariableIdentifier } from '../../utils';
|
||||
import { VariableInput } from '../shared/VariableInput';
|
||||
import { VariableLink } from '../shared/VariableLink';
|
||||
import { VariableOptions } from '../shared/VariableOptions';
|
||||
import VariableOptions from '../shared/VariableOptions';
|
||||
import { NavigationKey, VariablePickerProps } from '../types';
|
||||
|
||||
import { commitChangesToVariable, filterOrSearchOptions, navigateOptions, openOptions } from './actions';
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import classNames from 'classnames';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
import { Tooltip, Themeable2, withTheme2, clearButtonStyles } from '@grafana/ui';
|
||||
|
||||
import { VariableOption } from '../../types';
|
||||
|
||||
export interface Props extends React.HTMLProps<HTMLUListElement> {
|
||||
export interface Props extends React.HTMLProps<HTMLUListElement>, Themeable2 {
|
||||
multi: boolean;
|
||||
values: VariableOption[];
|
||||
selectedValues: VariableOption[];
|
||||
@ -19,19 +20,19 @@ export interface Props extends React.HTMLProps<HTMLUListElement> {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class VariableOptions extends PureComponent<Props> {
|
||||
onToggle = (option: VariableOption) => (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
class VariableOptions extends PureComponent<Props> {
|
||||
onToggle = (option: VariableOption) => (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const clearOthers = event.shiftKey || event.ctrlKey || event.metaKey;
|
||||
this.handleEvent(event);
|
||||
this.props.onToggle(option, clearOthers);
|
||||
};
|
||||
|
||||
onToggleAll = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
onToggleAll = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
this.handleEvent(event);
|
||||
this.props.onToggleAll();
|
||||
};
|
||||
|
||||
handleEvent(event: React.MouseEvent<HTMLAnchorElement>) {
|
||||
handleEvent(event: React.MouseEvent<HTMLButtonElement>) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
@ -57,24 +58,30 @@ export class VariableOptions extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
renderOption(option: VariableOption, index: number) {
|
||||
const { highlightIndex } = this.props;
|
||||
const { highlightIndex, theme } = this.props;
|
||||
const selectClass = option.selected ? 'variable-option pointer selected' : 'variable-option pointer';
|
||||
const highlightClass = index === highlightIndex ? `${selectClass} highlighted` : selectClass;
|
||||
|
||||
return (
|
||||
<li key={`${option.value}`}>
|
||||
<a role="checkbox" aria-checked={option.selected} className={highlightClass} onClick={this.onToggle(option)}>
|
||||
<button
|
||||
role="checkbox"
|
||||
type="button"
|
||||
aria-checked={option.selected}
|
||||
className={classNames(highlightClass, clearButtonStyles(theme), noStyledButton)}
|
||||
onClick={this.onToggle(option)}
|
||||
>
|
||||
<span className="variable-option-icon"></span>
|
||||
<span data-testid={selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(`${option.text}`)}>
|
||||
{option.text}
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
renderMultiToggle() {
|
||||
const { multi, selectedValues } = this.props;
|
||||
const { multi, selectedValues, theme } = this.props;
|
||||
|
||||
if (!multi) {
|
||||
return null;
|
||||
@ -82,12 +89,12 @@ export class VariableOptions extends PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<Tooltip content={'Clear selections'} placement={'top'}>
|
||||
<a
|
||||
<button
|
||||
className={`${
|
||||
selectedValues.length > 1
|
||||
? 'variable-options-column-header many-selected'
|
||||
: 'variable-options-column-header'
|
||||
}`}
|
||||
} ${noStyledButton} ${clearButtonStyles(theme)}`}
|
||||
role="checkbox"
|
||||
aria-checked={selectedValues.length > 1 ? 'mixed' : 'false'}
|
||||
onClick={this.onToggleAll}
|
||||
@ -96,7 +103,7 @@ export class VariableOptions extends PureComponent<Props> {
|
||||
>
|
||||
<span className="variable-option-icon"></span>
|
||||
Selected ({selectedValues.length})
|
||||
</a>
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@ -108,3 +115,10 @@ const listStyles = cx(
|
||||
list-style-type: none;
|
||||
`
|
||||
);
|
||||
|
||||
const noStyledButton = css`
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
export default withTheme2(VariableOptions);
|
||||
|
@ -10,7 +10,7 @@ import { CloudWatchDatasource } from '../datasource';
|
||||
import { CloudWatchJsonData, CloudWatchLogsQuery, CloudWatchQuery } from '../types';
|
||||
|
||||
import CloudWatchLink from './CloudWatchLink';
|
||||
import { CloudWatchLogsQueryField } from './LogsQueryField';
|
||||
import CloudWatchLogsQueryField from './LogsQueryField';
|
||||
|
||||
type Props = QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData> & {
|
||||
query: CloudWatchLogsQuery;
|
||||
|
@ -6,7 +6,7 @@ import { act } from 'react-dom/test-utils';
|
||||
import { ExploreId } from '../../../../types';
|
||||
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
|
||||
|
||||
import { CloudWatchLogsQueryField } from './LogsQueryField';
|
||||
import CloudWatchLogsQueryField from './LogsQueryField';
|
||||
|
||||
jest
|
||||
.spyOn(_, 'debounce')
|
||||
|
@ -1,11 +1,21 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { LanguageMap, languages as prismLanguages } from 'prismjs';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Node, Plugin } from 'slate';
|
||||
import { Editor } from 'slate-react';
|
||||
|
||||
import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data';
|
||||
import { BracesPlugin, LegacyForms, QueryField, SlatePrism, TypeaheadInput, TypeaheadOutput } from '@grafana/ui';
|
||||
import {
|
||||
BracesPlugin,
|
||||
LegacyForms,
|
||||
QueryField,
|
||||
SlatePrism,
|
||||
TypeaheadInput,
|
||||
TypeaheadOutput,
|
||||
Themeable2,
|
||||
withTheme2,
|
||||
clearButtonStyles,
|
||||
} from '@grafana/ui';
|
||||
import { ExploreId } from 'app/types';
|
||||
// Utils & Services
|
||||
// dom also includes Element polyfills
|
||||
@ -20,7 +30,8 @@ import { LogGroupSelector } from './LogGroupSelector';
|
||||
import QueryHeader from './QueryHeader';
|
||||
|
||||
export interface CloudWatchLogsQueryFieldProps
|
||||
extends QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData> {
|
||||
extends QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData>,
|
||||
Themeable2 {
|
||||
absoluteRange: AbsoluteTimeRange;
|
||||
onLabelsRefresh?: () => void;
|
||||
ExtraFieldElement?: ReactNode;
|
||||
@ -32,6 +43,10 @@ const rowGap = css`
|
||||
gap: 3px;
|
||||
`;
|
||||
|
||||
const addPaddingToButton = css`
|
||||
padding: 1px 4px;
|
||||
`;
|
||||
|
||||
interface State {
|
||||
hint:
|
||||
| {
|
||||
@ -44,7 +59,7 @@ interface State {
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogsQueryFieldProps, State> {
|
||||
class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogsQueryFieldProps, State> {
|
||||
state: State = {
|
||||
hint: undefined,
|
||||
};
|
||||
@ -112,7 +127,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onRunQuery, onChange, ExtraFieldElement, data, query, datasource } = this.props;
|
||||
const { onRunQuery, onChange, ExtraFieldElement, data, query, datasource, theme } = this.props;
|
||||
const { region, refId, expression, logGroupNames } = query;
|
||||
const { hint } = this.state;
|
||||
|
||||
@ -167,9 +182,13 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
<div className="query-row-break">
|
||||
<div className="text-warning">
|
||||
{hint.message}
|
||||
<a className="text-link muted" onClick={hint.fix.action}>
|
||||
<button
|
||||
type="button"
|
||||
className={cx(clearButtonStyles(theme), 'text-link', 'muted', addPaddingToButton)}
|
||||
onClick={hint.fix.action}
|
||||
>
|
||||
{hint.fix.label}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -182,3 +201,5 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme2(CloudWatchLogsQueryField);
|
||||
|
@ -2,7 +2,7 @@ import { size } from 'lodash';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { InlineLabel, Select, InlineFormLabel, InlineSwitch, Icon } from '@grafana/ui';
|
||||
import { InlineLabel, Select, InlineFormLabel, InlineSwitch, Icon, clearButtonStyles, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { OpenTsdbFilter, OpenTsdbQuery } from '../types';
|
||||
|
||||
@ -23,6 +23,8 @@ export function FilterSection({
|
||||
filterTypes,
|
||||
suggestTagValues,
|
||||
}: FilterSectionProps) {
|
||||
const buttonStyles = useStyles2(clearButtonStyles);
|
||||
|
||||
const [tagKeys, updTagKeys] = useState<Array<SelectableValue<string>>>();
|
||||
const [keyIsLoading, updKeyIsLoading] = useState<boolean>();
|
||||
|
||||
@ -121,20 +123,25 @@ export function FilterSection({
|
||||
return (
|
||||
<InlineFormLabel key={idx} width="auto" data-testid={testIds.list + idx}>
|
||||
{fil.tagk} = {fil.type}({fil.filter}), groupBy = {'' + fil.groupBy}
|
||||
<a onClick={() => editFilter(fil, idx)}>
|
||||
<button type="button" className={buttonStyles} onClick={() => editFilter(fil, idx)}>
|
||||
<Icon name={'pen'} />
|
||||
</a>
|
||||
<a onClick={() => removeFilter(idx)} data-testid={testIds.remove}>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={buttonStyles}
|
||||
onClick={() => removeFilter(idx)}
|
||||
data-testid={testIds.remove}
|
||||
>
|
||||
<Icon name={'times'} />
|
||||
</a>
|
||||
</button>
|
||||
</InlineFormLabel>
|
||||
);
|
||||
})}
|
||||
{!addFilterMode && (
|
||||
<label className="gf-form-label query-keyword">
|
||||
<a onClick={changeAddFilterMode} data-testid={testIds.open}>
|
||||
<button type="button" className={buttonStyles} onClick={changeAddFilterMode} data-testid={testIds.open}>
|
||||
<Icon name={'plus'} />
|
||||
</a>
|
||||
</button>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
@ -224,10 +231,12 @@ export function FilterSection({
|
||||
)}
|
||||
|
||||
<label className="gf-form-label">
|
||||
<a onClick={addFilter}>add filter</a>
|
||||
<a onClick={changeAddFilterMode}>
|
||||
<button type="button" className={buttonStyles} onClick={addFilter}>
|
||||
add filter
|
||||
</button>
|
||||
<button type="button" className={buttonStyles} onClick={changeAddFilterMode}>
|
||||
<Icon name={'times'} />
|
||||
</a>
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ import { has, size } from 'lodash';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { Select, InlineFormLabel, Icon } from '@grafana/ui';
|
||||
import { Select, InlineFormLabel, Icon, clearButtonStyles, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { OpenTsdbQuery } from '../types';
|
||||
|
||||
@ -23,6 +23,8 @@ export function TagSection({
|
||||
suggestTagValues,
|
||||
tsdbVersion,
|
||||
}: TagSectionProps) {
|
||||
const buttonStyles = useStyles2(clearButtonStyles);
|
||||
|
||||
const [tagKeys, updTagKeys] = useState<Array<SelectableValue<string>>>();
|
||||
const [keyIsLoading, updKeyIsLoading] = useState<boolean>();
|
||||
|
||||
@ -119,20 +121,25 @@ export function TagSection({
|
||||
return (
|
||||
<InlineFormLabel key={idx} width="auto" data-testid={testIds.list + idx}>
|
||||
{tagKey}={tagValue}
|
||||
<a onClick={() => editTag(tagKey, tagValue)}>
|
||||
<button type="button" className={buttonStyles} onClick={() => editTag(tagKey, tagValue)}>
|
||||
<Icon name={'pen'} />
|
||||
</a>
|
||||
<a onClick={() => removeTag(tagKey)} data-testid={testIds.remove}>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={buttonStyles}
|
||||
onClick={() => removeTag(tagKey)}
|
||||
data-testid={testIds.remove}
|
||||
>
|
||||
<Icon name={'times'} />
|
||||
</a>
|
||||
</button>
|
||||
</InlineFormLabel>
|
||||
);
|
||||
})}
|
||||
{!addTagMode && (
|
||||
<label className="gf-form-label query-keyword">
|
||||
<a onClick={changeAddTagMode} data-testid={testIds.open}>
|
||||
<button type="button" className={buttonStyles} onClick={changeAddTagMode} data-testid={testIds.open}>
|
||||
<Icon name={'plus'} />
|
||||
</a>
|
||||
</button>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
@ -195,10 +202,12 @@ export function TagSection({
|
||||
)}
|
||||
|
||||
<label className="gf-form-label">
|
||||
<a onClick={addTag}>add tag</a>
|
||||
<a onClick={changeAddTagMode}>
|
||||
<button type="button" className={buttonStyles} onClick={addTag}>
|
||||
add tag
|
||||
</button>
|
||||
<button type="button" className={buttonStyles} onClick={changeAddTagMode}>
|
||||
<Icon name={'times'} />
|
||||
</a>
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { cx } from '@emotion/css';
|
||||
import { LanguageMap, languages as prismLanguages } from 'prismjs';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Plugin } from 'slate';
|
||||
@ -13,6 +14,9 @@ import {
|
||||
SuggestionsState,
|
||||
TypeaheadInput,
|
||||
TypeaheadOutput,
|
||||
Themeable2,
|
||||
withTheme2,
|
||||
clearButtonStyles,
|
||||
} from '@grafana/ui';
|
||||
import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider';
|
||||
import {
|
||||
@ -74,7 +78,7 @@ export function willApplySuggestion(suggestion: string, { typeaheadContext, type
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
interface PromQueryFieldProps extends QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions> {
|
||||
interface PromQueryFieldProps extends QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions>, Themeable2 {
|
||||
ExtraFieldElement?: ReactNode;
|
||||
'data-testid'?: string;
|
||||
}
|
||||
@ -277,6 +281,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
query,
|
||||
ExtraFieldElement,
|
||||
history = [],
|
||||
theme,
|
||||
} = this.props;
|
||||
|
||||
const { labelBrowserVisible, syntaxLoaded, hint } = this.state;
|
||||
@ -333,9 +338,13 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
<div className="prom-query-field-info text-warning">
|
||||
{hint.label}{' '}
|
||||
{hint.fix ? (
|
||||
<a className="text-link muted" onClick={this.onClickHintFix}>
|
||||
<button
|
||||
type="button"
|
||||
className={cx(clearButtonStyles(theme), 'text-link', 'muted')}
|
||||
onClick={this.onClickHintFix}
|
||||
>
|
||||
{hint.fix.label}
|
||||
</a>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@ -348,4 +357,4 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
}
|
||||
}
|
||||
|
||||
export default PromQueryField;
|
||||
export default withTheme2(PromQueryField);
|
||||
|
@ -18,7 +18,13 @@ export const TutorialCard: FC<Props> = ({ card }) => {
|
||||
const styles = getStyles(theme, card.done);
|
||||
|
||||
return (
|
||||
<a className={styles.card} onClick={(event: MouseEvent<HTMLAnchorElement>) => handleTutorialClick(event, card)}>
|
||||
<a
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`${card.href}?utm_source=grafana_gettingstarted`}
|
||||
onClick={(event: MouseEvent<HTMLAnchorElement>) => handleTutorialClick(event, card)}
|
||||
>
|
||||
<div className={cardContent}>
|
||||
<div className={styles.type}>{card.type}</div>
|
||||
<div className={styles.heading}>{card.done ? 'complete' : card.heading}</div>
|
||||
@ -36,7 +42,6 @@ const handleTutorialClick = (event: MouseEvent<HTMLAnchorElement>, card: Tutoria
|
||||
if (!isSet) {
|
||||
store.set(card.key, true);
|
||||
}
|
||||
window.open(`${card.href}?utm_source=grafana_gettingstarted`, '_blank');
|
||||
};
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme, complete: boolean) => {
|
||||
|
@ -150,7 +150,8 @@ class LegendSeriesLabel extends PureComponent<LegendSeriesLabelProps & LegendSer
|
||||
onColorChange={onColorChange}
|
||||
onToggleAxis={onToggleAxis}
|
||||
/>,
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
className="graph-legend-alias pointer"
|
||||
title={label}
|
||||
key="label"
|
||||
@ -158,7 +159,7 @@ class LegendSeriesLabel extends PureComponent<LegendSeriesLabelProps & LegendSer
|
||||
aria-label={selectors.components.Panels.Visualization.Graph.Legend.legendItemAlias(label)}
|
||||
>
|
||||
{label}
|
||||
</a>,
|
||||
</button>,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.graph-legend-alias {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.graph-legend-content {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -40,6 +40,8 @@
|
||||
font-size: $font-size-h5;
|
||||
font-weight: $font-weight-semi-bold;
|
||||
color: $text-color;
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
.fa {
|
||||
color: $text-muted;
|
||||
@ -54,9 +56,11 @@
|
||||
opacity: 0;
|
||||
transition: 200ms opacity ease-in 200ms;
|
||||
|
||||
a {
|
||||
button {
|
||||
color: $text-color-weak;
|
||||
padding-left: $spacer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
color: $link-hover-color;
|
||||
|
Loading…
Reference in New Issue
Block a user