Add tooltip to instances of IconButton (#68880)

* refactor: tooltip is required

* refactor: add tooltips

* refactor: add tooltips

* refactor: add tooltips

* refactor: add tooltips

* refactor: add tooltips

* refactor: add tooltips

* refactor: adjust tests

* refactor: apply changes from code review

* refactor: adjust component for e2e test

* refactor: adjust fallback

* refactor: apply changes from code review

* refactor: apply changes from code review

* refactor: set IconButton default as type=button and remove from use cases

* refactor:  remove aria-labels when duplicated and type=button from use cases

* refactor: clean up

* refactor: fix tests

* refactor: fix type errors

* refactor: remove changes in order in order to add them to a separate PR

* refactor: set IconButton default as type=button

* refactor: remove tooltip

* refactor: apply changes requested in review
This commit is contained in:
Laura Benz 2023-06-08 10:23:28 +02:00 committed by GitHub
parent c826739ee8
commit 24502c4c4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 191 additions and 184 deletions

View File

@ -2883,9 +2883,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"] [0, 0, 0, "Unexpected any. Specify a different type.", "4"]
], ],
"public/app/features/playlist/PlaylistTableRows.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"public/app/features/plugins/admin/components/AppConfigWrapper.tsx:5381": [ "public/app/features/plugins/admin/components/AppConfigWrapper.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"] [0, 0, 0, "Unexpected any. Specify a different type.", "1"]

View File

@ -27,7 +27,7 @@ describe('Card', () => {
<Button>Click Me</Button> <Button>Click Me</Button>
</Card.Actions> </Card.Actions>
<Card.SecondaryActions> <Card.SecondaryActions>
<IconButton name="trash-alt" aria-label="Delete" /> <IconButton name="trash-alt" aria-label="Delete" tooltip="Delete" />
</Card.SecondaryActions> </Card.SecondaryActions>
</Card> </Card>
); );
@ -42,7 +42,7 @@ describe('Card', () => {
<Button>Click Me</Button> <Button>Click Me</Button>
</Card.Actions> </Card.Actions>
<Card.SecondaryActions> <Card.SecondaryActions>
<IconButton name="trash-alt" aria-label="Delete" /> <IconButton name="trash-alt" aria-label="Delete" tooltip="Delete" />
</Card.SecondaryActions> </Card.SecondaryActions>
</Card> </Card>
); );
@ -59,7 +59,7 @@ describe('Card', () => {
<Button disabled>Click Me</Button> <Button disabled>Click Me</Button>
</Card.Actions> </Card.Actions>
<Card.SecondaryActions> <Card.SecondaryActions>
<IconButton name="trash-alt" aria-label="Delete" disabled /> <IconButton name="trash-alt" aria-label="Delete" tooltip="Delete" disabled />
</Card.SecondaryActions> </Card.SecondaryActions>
</Card> </Card>
); );
@ -74,7 +74,7 @@ describe('Card', () => {
<Button disabled={false}>Click Me</Button> <Button disabled={false}>Click Me</Button>
</Card.Actions> </Card.Actions>
<Card.SecondaryActions> <Card.SecondaryActions>
<IconButton name="trash-alt" aria-label="Delete" disabled={false} /> <IconButton name="trash-alt" aria-label="Delete" tooltip="Delete" disabled={false} />
</Card.SecondaryActions> </Card.SecondaryActions>
</Card> </Card>
); );
@ -93,7 +93,7 @@ describe('Card', () => {
{shouldNotRender && <Button>Delete</Button>} {shouldNotRender && <Button>Delete</Button>}
</Card.Actions> </Card.Actions>
<Card.SecondaryActions> <Card.SecondaryActions>
{shouldNotRender && <IconButton name="trash-alt" aria-label="Delete" disabled={false} />} {shouldNotRender && <IconButton name="trash-alt" aria-label="Delete" tooltip="Delete" disabled={false} />}
</Card.SecondaryActions> </Card.SecondaryActions>
</Card> </Card>
); );

View File

@ -41,7 +41,7 @@ export const Basic: StoryFn<typeof ContextMenu> = (args: ContextMenuProps) => {
export const WithState: StoryFn<typeof WithContextMenu> = (args: WithContextMenuProps) => { export const WithState: StoryFn<typeof WithContextMenu> = (args: WithContextMenuProps) => {
return ( return (
<WithContextMenu {...args}> <WithContextMenu {...args}>
{({ openMenu }) => <IconButton name="info-circle" onClick={openMenu} />} {({ openMenu }) => <IconButton name="info-circle" onClick={openMenu} tooltip="More information" />}
</WithContextMenu> </WithContextMenu>
); );
}; };

View File

@ -35,8 +35,8 @@ export const DataLinksListItem = ({ link, onEdit, onRemove }: DataLinksListItemP
{hasTitle ? title : 'Data link title not provided'} {hasTitle ? title : 'Data link title not provided'}
</div> </div>
<div className={styles.actionButtons}> <div className={styles.actionButtons}>
<IconButton name="pen" onClick={onEdit} /> <IconButton name="pen" onClick={onEdit} tooltip="Edit data link title" />
<IconButton name="times" onClick={onRemove} /> <IconButton name="times" onClick={onRemove} tooltip="Remove data link title" />
</div> </div>
</div> </div>
<div <div

View File

@ -26,24 +26,13 @@ export function FileListItem({ file: customFile, removeFile }: FileListItemProps
return ( return (
<> <>
<span className={styles.error}>{error.message}</span> <span className={styles.error}>{error.message}</span>
{retryUpload && ( {retryUpload && <IconButton name="sync" tooltip="Retry" tooltipPlacement="top" onClick={retryUpload} />}
<IconButton
type="button"
aria-label="Retry"
name="sync"
tooltip="Retry"
tooltipPlacement="top"
onClick={retryUpload}
/>
)}
{removeFile && ( {removeFile && (
<IconButton <IconButton
className={retryUpload ? styles.marginLeft : ''} className={retryUpload ? styles.marginLeft : ''}
type="button"
name="trash-alt" name="trash-alt"
onClick={() => removeFile(customFile)} onClick={() => removeFile(customFile)}
tooltip={REMOVE_FILE} tooltip={REMOVE_FILE}
aria-label={REMOVE_FILE}
/> />
)} )}
</> </>
@ -69,8 +58,6 @@ export function FileListItem({ file: customFile, removeFile }: FileListItemProps
name="trash-alt" name="trash-alt"
onClick={() => removeFile(customFile)} onClick={() => removeFile(customFile)}
tooltip={REMOVE_FILE} tooltip={REMOVE_FILE}
aria-label={REMOVE_FILE}
type="button"
tooltipPlacement="top" tooltipPlacement="top"
/> />
) )

View File

@ -66,7 +66,7 @@ export const ExamplesSizes = () => {
<div className={rowStyle} key={icon}> <div className={rowStyle} key={icon}>
{sizes.map((size) => ( {sizes.map((size) => (
<span key={icon + size}> <span key={icon + size}>
<IconButton name={icon} size={size} variant={variant} /> <IconButton name={icon} size={size} variant={variant} tooltip="Tooltip example" />
</span> </span>
))} ))}
</div> </div>
@ -81,7 +81,7 @@ export const ExamplesSizes = () => {
<div className={rowStyle} key={icon}> <div className={rowStyle} key={icon}>
{sizes.map((size) => ( {sizes.map((size) => (
<span key={icon + size}> <span key={icon + size}>
<IconButton name={icon} size={size} disabled /> <IconButton name={icon} size={size} tooltip="Tooltip example" disabled />
</span> </span>
))} ))}
</div> </div>
@ -121,9 +121,9 @@ const RenderBackgroundScenario = ({ background }: ScenarioProps) => {
`} `}
> >
{variants.map((variant) => { {variants.map((variant) => {
return <IconButton name="times" size="xl" variant={variant} key={variant} />; return <IconButton name="times" size="xl" variant={variant} key={variant} tooltip="Tooltip example" />;
})} })}
<IconButton name="times" size="xl" disabled /> <IconButton name="times" size="xl" tooltip="Tooltip example" disabled />
</div> </div>
</VerticalGroup> </VerticalGroup>
</div> </div>

View File

@ -28,7 +28,7 @@ export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
tooltipPlacement?: TooltipPlacement; tooltipPlacement?: TooltipPlacement;
/** Variant to change the color of the Icon */ /** Variant to change the color of the Icon */
variant?: IconButtonVariant; variant?: IconButtonVariant;
/** Text avilable ony for screenscreen readers. Will use tooltip text as fallback. */ /** Text available only for screen readers. Will use tooltip text as fallback. */
ariaLabel?: string; ariaLabel?: string;
} }
@ -68,6 +68,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, Props>(
aria-label={ariaLabel || tooltipString} aria-label={ariaLabel || tooltipString}
{...restProps} {...restProps}
className={cx(styles.button, className)} className={cx(styles.button, className)}
type="button"
> >
<Icon name={name} size={limitedIconSize} className={styles.icon} type={iconType} /> <Icon name={name} size={limitedIconSize} className={styles.icon} type={iconType} />
</button> </button>

View File

@ -84,7 +84,7 @@ export function Modal(props: PropsWithChildren<Props>) {
typeof title !== 'string' && title typeof title !== 'string' && title
} }
<div className={styles.modalHeaderClose}> <div className={styles.modalHeaderClose}>
<IconButton aria-label="Close dialog" name="times" size="xl" onClick={onDismiss} /> <IconButton aria-label="Close dialog" name="times" size="xl" onClick={onDismiss} tooltip="Close" />
</div> </div>
</div> </div>
<div className={cx(styles.modalContent, contentClassName)}>{children}</div> <div className={cx(styles.modalContent, contentClassName)}>{children}</div>

View File

@ -34,8 +34,8 @@ export const Examples = () => {
titleHref="" titleHref=""
parentHref="" parentHref=""
leftItems={[ leftItems={[
<IconButton name="share-alt" size="lg" key="share" />, <IconButton name="share-alt" size="lg" key="share" tooltip="Share" />,
<IconButton name="favorite" iconType="mono" size="lg" key="favorite" />, <IconButton name="favorite" iconType="mono" size="lg" key="favorite" tooltip="Add to favourites" />,
]} ]}
> >
<ToolbarButton icon="panel-add" /> <ToolbarButton icon="panel-add" />

View File

@ -358,7 +358,7 @@ Component used for rendering content wrapped in the same style as grafana panels
variant="secondary" variant="secondary"
tooltip="extra content to render" tooltip="extra content to render"
/> />
<IconButton name="sliders-v-alt" variant="secondary" tooltip="extra content2 to render" /> <IconButton name="sliders-v-alt" variant="secondary" tooltip="extra content to render" />
</> </>
} }
actions={ actions={

View File

@ -27,5 +27,5 @@ export type MultiValueRemoveProps = {
export const MultiValueRemove = ({ children, innerProps }: React.PropsWithChildren<MultiValueRemoveProps>) => { export const MultiValueRemove = ({ children, innerProps }: React.PropsWithChildren<MultiValueRemoveProps>) => {
const theme = useTheme2(); const theme = useTheme2();
const styles = getSelectStyles(theme); const styles = getSelectStyles(theme);
return <IconButton {...innerProps} name="times" size="sm" className={styles.multiValueRemove} />; return <IconButton {...innerProps} name="times" size="sm" className={styles.multiValueRemove} tooltip="Remove" />;
}; };

View File

@ -72,7 +72,7 @@ export function TabbedContainer(props: TabbedContainerProps) {
icon={t.icon} icon={t.icon}
/> />
))} ))}
<IconButton className={styles.close} onClick={onClose} name="times" title={closeIconTooltip ?? 'Close'} /> <IconButton className={styles.close} onClick={onClose} name="times" tooltip={closeIconTooltip ?? 'Close'} />
</TabsBar> </TabsBar>
<CustomScrollbar autoHeightMin="100%"> <CustomScrollbar autoHeightMin="100%">
<TabContent className={styles.tabContent}>{tabs.find((t) => t.value === activeTab)?.content}</TabContent> <TabContent className={styles.tabContent}>{tabs.find((t) => t.value === activeTab)?.content}</TabContent>

View File

@ -29,8 +29,8 @@ export const TagItem = ({ name, disabled, onRemove }: Props) => {
size="lg" size="lg"
disabled={disabled} disabled={disabled}
ariaLabel={`Remove "${name}" tag`} ariaLabel={`Remove "${name}" tag`}
tooltip="Remove tag"
onClick={() => onRemove(name)} onClick={() => onRemove(name)}
type="button"
className={styles.buttonStyles} className={styles.buttonStyles}
/> />
</li> </li>

View File

@ -97,9 +97,8 @@ export const Toggletip = React.memo(
{closeButton && ( {closeButton && (
<div className={style.headerClose}> <div className={style.headerClose}>
<IconButton <IconButton
aria-label="Close Toggletip" tooltip="Close"
name="times" name="times"
size="md"
data-testid="toggletip-header-close" data-testid="toggletip-header-close"
onClick={closeToggletip} onClick={closeToggletip}
/> />

View File

@ -8,15 +8,7 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { StackingConfig, StackingMode } from '@grafana/schema'; import { StackingConfig, StackingMode } from '@grafana/schema';
import { import { GraphFieldConfig, graphFieldOptions, HorizontalGroup, IconButton, Input, RadioButtonGroup } from '../..';
GraphFieldConfig,
graphFieldOptions,
HorizontalGroup,
IconButton,
Input,
RadioButtonGroup,
Tooltip,
} from '../..';
export const StackingEditor = ({ value, context, onChange, item }: FieldOverrideEditorProps<StackingConfig, any>) => { export const StackingEditor = ({ value, context, onChange, item }: FieldOverrideEditorProps<StackingConfig, any>) => {
return ( return (
@ -35,11 +27,7 @@ export const StackingEditor = ({ value, context, onChange, item }: FieldOverride
<Input <Input
type="text" type="text"
placeholder="Group" placeholder="Group"
suffix={ suffix={<IconButton name="question-circle" tooltip="Name of the stacking group" tooltipPlacement="top" />}
<Tooltip content="Name of the stacking group" placement="top">
<IconButton name="question-circle" />
</Tooltip>
}
defaultValue={value?.group} defaultValue={value?.group}
onChange={(v) => { onChange={(v) => {
onChange({ onChange({

View File

@ -67,6 +67,7 @@ export function NavBarMenu({ activeItem, navItems, searchBarHidden, onClose }: P
<Icon name="bars" size="xl" /> <Icon name="bars" size="xl" />
<IconButton <IconButton
aria-label="Close navigation menu" aria-label="Close navigation menu"
tooltip="Close menu"
name="times" name="times"
onClick={onMenuClose} onClick={onMenuClose}
size="xl" size="xl"

View File

@ -13,7 +13,14 @@ type Props = {
export const CloseButton = ({ onClick, 'aria-label': ariaLabel, style }: Props) => { export const CloseButton = ({ onClick, 'aria-label': ariaLabel, style }: Props) => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
return ( return (
<IconButton aria-label={ariaLabel ?? 'Close'} className={styles} name="times" onClick={onClick} style={style} /> <IconButton
aria-label={ariaLabel ?? 'Close'}
className={styles}
name="times"
onClick={onClick}
style={style}
tooltip="Close"
/>
); );
}; };

View File

@ -80,8 +80,8 @@ export const LayerDragDropList = <T extends LayerElement>({
{onDuplicate ? ( {onDuplicate ? (
<IconButton <IconButton
name="copy" name="copy"
title={'Duplicate'} tooltip="Duplicate"
ariaLabel={'Duplicate button'} ariaLabel="Duplicate button"
className={style.actionIcon} className={style.actionIcon}
onClick={() => onDuplicate(element)} onClick={() => onDuplicate(element)}
/> />
@ -89,8 +89,8 @@ export const LayerDragDropList = <T extends LayerElement>({
<IconButton <IconButton
name="trash-alt" name="trash-alt"
title={'remove'} tooltip="Remove"
ariaLabel={'Remove button'} ariaLabel="Remove button"
className={cx(style.actionIcon, style.dragIcon)} className={cx(style.actionIcon, style.dragIcon)}
onClick={() => onDelete(element)} onClick={() => onDelete(element)}
/> />

View File

@ -53,7 +53,9 @@ export const ColorValueEditor = ({ value, settings, onChange, details }: Props)
{settings?.placeholder ?? 'Select color'} {settings?.placeholder ?? 'Select color'}
</span> </span>
)} )}
{settings?.isClearable && value && <IconButton name="times" onClick={() => onChange(undefined)} />} {settings?.isClearable && value && (
<IconButton name="times" onClick={() => onChange(undefined)} tooltip="Clear settings" />
)}
</> </>
)} )}
</div> </div>

View File

@ -14,7 +14,7 @@ export function UnitValueEditor({ value, onChange, item }: Props) {
<span className={styles.first}> <span className={styles.first}>
<UnitPicker value={value} onChange={onChange} /> <UnitPicker value={value} onChange={onChange} />
</span> </span>
<IconButton ariaLabel="clear unit selection" name="times" onClick={() => onChange(undefined)} /> <IconButton name="times" onClick={() => onChange(undefined)} tooltip="Clear unit selection" />
</div> </div>
); );
} }

View File

@ -27,14 +27,13 @@ export const PasswordField = React.forwardRef<HTMLInputElement, Props>(
suffix={ suffix={
<IconButton <IconButton
name={showPassword ? 'eye-slash' : 'eye'} name={showPassword ? 'eye-slash' : 'eye'}
type="button"
aria-controls={id} aria-controls={id}
role="switch" role="switch"
aria-checked={showPassword} aria-checked={showPassword}
aria-label="Show password"
onClick={() => { onClick={() => {
setShowPassword(!showPassword); setShowPassword(!showPassword);
}} }}
tooltip={showPassword ? 'Hide password' : 'Show password'}
/> />
} }
/> />

View File

@ -37,11 +37,10 @@ export const QueryOperationRowHeader = ({
<div className={styles.column}> <div className={styles.column}>
<IconButton <IconButton
name={isContentVisible ? 'angle-down' : 'angle-right'} name={isContentVisible ? 'angle-down' : 'angle-right'}
title="toggle collapse and expand"
aria-label="toggle collapse and expand query row" aria-label="toggle collapse and expand query row"
tooltip={isContentVisible ? 'Collapse query row' : 'Expand query row'}
className={styles.collapseIcon} className={styles.collapseIcon}
onClick={onRowToggle} onClick={onRowToggle}
type="button"
aria-expanded={isContentVisible} aria-expanded={isContentVisible}
aria-controls={id} aria-controls={id}
/> />

View File

@ -260,7 +260,7 @@ function GrafanaRuleUID({ rule }: { rule: GrafanaRuleDefinition }) {
return ( return (
<DetailsField label="Rule UID" childrenWrapperClassName={styles.ruleUid}> <DetailsField label="Rule UID" childrenWrapperClassName={styles.ruleUid}>
{rule.uid} <IconButton name="copy" onClick={copyUID} /> {rule.uid} <IconButton name="copy" onClick={copyUID} tooltip="Copy rule" />
</DetailsField> </DetailsField>
); );
} }

View File

@ -19,7 +19,7 @@ export const AlertLabel = ({ labelKey, value, operator = '=', onRemoveLabel }: P
{labelKey} {labelKey}
{operator} {operator}
{value} {value}
{!!onRemoveLabel && <IconButton name="times" size="xs" onClick={onRemoveLabel} />} {!!onRemoveLabel && <IconButton name="times" size="xs" onClick={onRemoveLabel} tooltip="Remove label" />}
</div> </div>
); );
}; };

View File

@ -124,12 +124,10 @@ export const DynamicTable = <T extends object>({
{isExpandable && ( {isExpandable && (
<div className={cx(styles.cell, styles.expandCell)}> <div className={cx(styles.cell, styles.expandCell)}>
<IconButton <IconButton
aria-label={`${isItemExpanded ? 'Collapse' : 'Expand'} row`} tooltip={`${isItemExpanded ? 'Collapse' : 'Expand'} row`}
size="md"
data-testid="collapse-toggle" data-testid="collapse-toggle"
name={isItemExpanded ? 'angle-down' : 'angle-right'} name={isItemExpanded ? 'angle-down' : 'angle-right'}
onClick={() => toggleExpanded(item)} onClick={() => toggleExpanded(item)}
type="button"
/> />
</div> </div>
)} )}

View File

@ -216,7 +216,7 @@ const Header: FC<HeaderProps> = ({ refId, queryType, onUpdateRefId, onUpdateExpr
* There are 3 edit modes: * There are 3 edit modes:
* *
* 1. "refId": Editing the refId (ie. A -> B) * 1. "refId": Editing the refId (ie. A -> B)
* 2. "epressionType": Editing the type of the expression (ie. Reduce -> Math) * 2. "expressionType": Editing the type of the expression (ie. Reduce -> Math)
* 3. "false": This means we're not editing either of those * 3. "false": This means we're not editing either of those
*/ */
const [editMode, setEditMode] = useState<'refId' | 'expressionType' | false>(false); const [editMode, setEditMode] = useState<'refId' | 'expressionType' | false>(false);
@ -281,11 +281,11 @@ const Header: FC<HeaderProps> = ({ refId, queryType, onUpdateRefId, onUpdateExpr
</Stack> </Stack>
<Spacer /> <Spacer />
<IconButton <IconButton
type="button"
name="trash-alt" name="trash-alt"
variant="secondary" variant="secondary"
className={styles.mutedIcon} className={styles.mutedIcon}
onClick={onRemoveExpression} onClick={onRemoveExpression}
tooltip="Remove expression"
/> />
</Stack> </Stack>
</header> </header>

View File

@ -121,13 +121,13 @@ export const MuteTimingTimeRange = ({ intervalIndex }: Props) => {
</InlineField> </InlineField>
<IconButton <IconButton
className={styles.deleteTimeRange} className={styles.deleteTimeRange}
title={'Remove'} title="Remove"
name={'trash-alt'} name="trash-alt"
type="button"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
removeTimeRange(index); removeTimeRange(index);
}} }}
tooltip="Remove time range"
/> />
</InlineFieldRow> </InlineFieldRow>
</div> </div>
@ -139,7 +139,7 @@ export const MuteTimingTimeRange = ({ intervalIndex }: Props) => {
className={styles.addTimeRange} className={styles.addTimeRange}
variant="secondary" variant="secondary"
type="button" type="button"
icon={'plus'} icon="plus"
onClick={() => addTimeRange({ start_time: '', end_time: '' })} onClick={() => addTimeRange({ start_time: '', end_time: '' })}
> >
Add another time range Add another time range

View File

@ -150,7 +150,7 @@ function useColumns(alertManagerSourceName: string, hideActions = false, setMute
muteName: data.name, muteName: data.name,
})} })}
> >
<IconButton name="file-alt" title="View mute timing" /> <IconButton name="file-alt" tooltip="View mute timing" />
</Link> </Link>
</div> </div>
); );
@ -163,13 +163,13 @@ function useColumns(alertManagerSourceName: string, hideActions = false, setMute
muteName: data.name, muteName: data.name,
})} })}
> >
<IconButton name="edit" title="Edit mute timing" /> <IconButton name="edit" tooltip="Edit mute timing" />
</Link> </Link>
</Authorize> </Authorize>
<Authorize actions={[permissions.delete]}> <Authorize actions={[permissions.delete]}>
<IconButton <IconButton
name={'trash-alt'} name="trash-alt"
title="Delete mute timing" tooltip="Delete mute timing"
onClick={() => setMuteTimingName(data.name)} onClick={() => setMuteTimingName(data.name)}
/> />
</Authorize> </Authorize>

View File

@ -137,12 +137,7 @@ export const AmRoutesExpandedForm = ({
placeholder="value" placeholder="value"
/> />
</Field> </Field>
<IconButton <IconButton tooltip="Remove matcher" name={'trash-alt'} onClick={() => remove(index)}>
type="button"
tooltip="Remove matcher"
name={'trash-alt'}
onClick={() => remove(index)}
>
Remove Remove
</IconButton> </IconButton>
</Stack> </Stack>

View File

@ -79,7 +79,7 @@ const MatchersField = ({ className }: Props) => {
<IconButton <IconButton
className={styles.removeButton} className={styles.removeButton}
tooltip="Remove matcher" tooltip="Remove matcher"
name={'trash-alt'} name="trash-alt"
onClick={() => remove(index)} onClick={() => remove(index)}
> >
Remove Remove

View File

@ -229,9 +229,8 @@ export const TransformationsEditor = (props: Props) => {
{!readOnly && ( {!readOnly && (
<div className={styles.removeButton}> <div className={styles.removeButton}>
<IconButton <IconButton
type="button"
tooltip="Remove transformation" tooltip="Remove transformation"
name={'trash-alt'} name="trash-alt"
onClick={() => { onClick={() => {
remove(index); remove(index);
const keptValsCopy: Array<{ expression?: string; mapValue?: string } | undefined> = [ const keptValsCopy: Array<{ expression?: string; mapValue?: string } | undefined> = [
@ -240,7 +239,6 @@ export const TransformationsEditor = (props: Props) => {
keptValsCopy[index] = undefined; keptValsCopy[index] = undefined;
setKeptVals(compact(keptValsCopy)); setKeptVals(compact(keptValsCopy));
}} }}
ariaLabel="Remove transformation"
> >
Remove Remove
</IconButton> </IconButton>

View File

@ -47,7 +47,12 @@ export const AddLibraryPanelWidget = ({ panel, dashboard }: Props) => {
<Trans i18nKey="library-panel.add-widget.title">Add panel from panel library</Trans> <Trans i18nKey="library-panel.add-widget.title">Add panel from panel library</Trans>
</span> </span>
<div className="flex-grow-1" /> <div className="flex-grow-1" />
<IconButton aria-label="Close 'Add Panel' widget" name="times" onClick={onCancelAddPanel} /> <IconButton
aria-label="Close 'Add Panel' widget"
name="times"
onClick={onCancelAddPanel}
tooltip="Close widget"
/>
</div> </div>
<LibraryPanelsSearch onClick={onAddLibraryPanel} variant={LibraryPanelsSearchVariant.Tight} showPanelFilter /> <LibraryPanelsSearch onClick={onAddLibraryPanel} variant={LibraryPanelsSearchVariant.Tight} showPanelFilter />
</div> </div>

View File

@ -213,7 +213,7 @@ const AddPanelWidgetHandle = ({ children, onBack, onCancel, styles }: AddPanelWi
<div className={cx(styles.headerRow, 'grid-drag-handle')}> <div className={cx(styles.headerRow, 'grid-drag-handle')}>
{onBack && ( {onBack && (
<div className={styles.backButton}> <div className={styles.backButton}>
<IconButton aria-label="Go back" name="arrow-left" onClick={onBack} size="xl" /> <IconButton name="arrow-left" onClick={onBack} size="xl" tooltip="Go back" />
</div> </div>
)} )}
{!onBack && ( {!onBack && (
@ -223,7 +223,7 @@ const AddPanelWidgetHandle = ({ children, onBack, onCancel, styles }: AddPanelWi
)} )}
{children && <span>{children}</span>} {children && <span>{children}</span>}
<div className="flex-grow-1" /> <div className="flex-grow-1" />
<IconButton aria-label="Close 'Add Panel' widget" name="times" onClick={onCancel} /> <IconButton aria-label="Close 'Add Panel' widget" name="times" onClick={onCancel} tooltip="Close widget" />
</div> </div>
); );
}; };

View File

@ -81,11 +81,11 @@ export const AnnotationSettingsList = ({ dashboard, onNew, onEdit }: Props) => {
{dataSourceSrv.getInstanceSettings(annotation.datasource)?.name || annotation.datasource?.uid} {dataSourceSrv.getInstanceSettings(annotation.datasource)?.name || annotation.datasource?.uid}
</td> </td>
<td role="gridcell" style={{ width: '1%' }}> <td role="gridcell" style={{ width: '1%' }}>
{idx !== 0 && <IconButton name="arrow-up" aria-label="arrow-up" onClick={() => onMove(idx, -1)} />} {idx !== 0 && <IconButton name="arrow-up" onClick={() => onMove(idx, -1)} tooltip="Move up" />}
</td> </td>
<td role="gridcell" style={{ width: '1%' }}> <td role="gridcell" style={{ width: '1%' }}>
{dashboard.annotations.list.length > 1 && idx !== dashboard.annotations.list.length - 1 ? ( {dashboard.annotations.list.length > 1 && idx !== dashboard.annotations.list.length - 1 ? (
<IconButton name="arrow-down" aria-label="arrow-down" onClick={() => onMove(idx, 1)} /> <IconButton name="arrow-down" onClick={() => onMove(idx, 1)} tooltip="Move down" />
) : null} ) : null}
</td> </td>
<td role="gridcell" style={{ width: '1%' }}> <td role="gridcell" style={{ width: '1%' }}>

View File

@ -158,23 +158,23 @@ describe('AnnotationsSettings', () => {
setup(dashboard); setup(dashboard);
// Check that we have sorting buttons // Check that we have sorting buttons
expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'arrow-up' })).not.toBeInTheDocument(); expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'Move up' })).not.toBeInTheDocument();
expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'arrow-down' })).toBeInTheDocument(); expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'Move down' })).toBeInTheDocument();
expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'arrow-up' })).toBeInTheDocument(); expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'Move up' })).toBeInTheDocument();
expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'arrow-down' })).toBeInTheDocument(); expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'Move down' })).toBeInTheDocument();
expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'arrow-up' })).toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'Move up' })).toBeInTheDocument();
expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'arrow-down' })).not.toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'Move down' })).not.toBeInTheDocument();
// Check the original order // Check the original order
expect(within(getTableBodyRows()[0]).queryByText(/annotations & alerts/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[0]).queryByText(/annotations & alerts/i)).toBeInTheDocument();
expect(within(getTableBodyRows()[1]).queryByText(/annotation 2/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[1]).queryByText(/annotation 2/i)).toBeInTheDocument();
expect(within(getTableBodyRows()[2]).queryByText(/annotation 3/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByText(/annotation 3/i)).toBeInTheDocument();
await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'Move down' })[0]);
await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'Move down' })[1]);
await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-up' })[0]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'Move up' })[0]);
// Checking if it has changed the sorting accordingly // Checking if it has changed the sorting accordingly
expect(within(getTableBodyRows()[0]).queryByText(/annotation 3/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[0]).queryByText(/annotation 3/i)).toBeInTheDocument();

View File

@ -113,23 +113,23 @@ describe('LinksSettings', () => {
setup(dashboard); setup(dashboard);
// Check that we have sorting buttons // Check that we have sorting buttons
expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'arrow-up' })).not.toBeInTheDocument(); expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'Move link up' })).not.toBeInTheDocument();
expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'arrow-down' })).toBeInTheDocument(); expect(within(getTableBodyRows()[0]).queryByRole('button', { name: 'Move link down' })).toBeInTheDocument();
expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'arrow-up' })).toBeInTheDocument(); expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'Move link up' })).toBeInTheDocument();
expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'arrow-down' })).toBeInTheDocument(); expect(within(getTableBodyRows()[1]).queryByRole('button', { name: 'Move link down' })).toBeInTheDocument();
expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'arrow-up' })).toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'Move link up' })).toBeInTheDocument();
expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'arrow-down' })).not.toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByRole('button', { name: 'Move link down' })).not.toBeInTheDocument();
// Checking the original order // Checking the original order
assertRowHasText(0, links[0].title); assertRowHasText(0, links[0].title);
assertRowHasText(1, links[1].title); assertRowHasText(1, links[1].title);
assertRowHasText(2, links[2].url); assertRowHasText(2, links[2].url);
await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'Move link down' })[0]);
await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'Move link down' })[1]);
await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-up' })[0]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'Move link up' })[0]);
// Checking if it has changed the sorting accordingly // Checking if it has changed the sorting accordingly
assertRowHasText(0, links[2].url); assertRowHasText(0, links[2].url);

View File

@ -75,15 +75,15 @@ export const LinkSettingsList = ({ dashboard, onNew, onEdit }: LinkSettingsListP
</HorizontalGroup> </HorizontalGroup>
</td> </td>
<td style={{ width: '1%' }} role="gridcell"> <td style={{ width: '1%' }} role="gridcell">
{idx !== 0 && <IconButton name="arrow-up" aria-label="arrow-up" onClick={() => moveLink(idx, -1)} />} {idx !== 0 && <IconButton name="arrow-up" onClick={() => moveLink(idx, -1)} tooltip="Move link up" />}
</td> </td>
<td style={{ width: '1%' }} role="gridcell"> <td style={{ width: '1%' }} role="gridcell">
{links.length > 1 && idx !== links.length - 1 ? ( {links.length > 1 && idx !== links.length - 1 ? (
<IconButton name="arrow-down" aria-label="arrow-down" onClick={() => moveLink(idx, 1)} /> <IconButton name="arrow-down" onClick={() => moveLink(idx, 1)} tooltip="Move link down" />
) : null} ) : null}
</td> </td>
<td style={{ width: '1%' }} role="gridcell"> <td style={{ width: '1%' }} role="gridcell">
<IconButton aria-label="copy" name="copy" onClick={() => duplicateLink(link, idx)} /> <IconButton name="copy" onClick={() => duplicateLink(link, idx)} tooltip="Copy link" />
</td> </td>
<td style={{ width: '1%' }} role="gridcell"> <td style={{ width: '1%' }} role="gridcell">
<DeleteButton <DeleteButton

View File

@ -66,7 +66,7 @@ export const DynamicConfigValueEditor = ({
</Label> </Label>
{!isSystemOverride && ( {!isSystemOverride && (
<div> <div>
<IconButton name="times" onClick={onRemove} /> <IconButton name="times" onClick={onRemove} tooltip="Remove label" />
</div> </div>
)} )}
</HorizontalGroup> </HorizontalGroup>

View File

@ -30,7 +30,7 @@ export const OverrideCategoryTitle = ({
<div> <div>
<HorizontalGroup justify="space-between"> <HorizontalGroup justify="space-between">
<div>{overrideName}</div> <div>{overrideName}</div>
<IconButton name="trash-alt" onClick={onOverrideRemove} title="Remove override" /> <IconButton name="trash-alt" onClick={onOverrideRemove} tooltip="Remove override" />
</HorizontalGroup> </HorizontalGroup>
{!isExpanded && ( {!isExpanded && (
<div className={styles.overrideDetails}> <div className={styles.overrideDetails}>

View File

@ -249,6 +249,7 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
onClick={() => { onClick={() => {
this.setState({ search: '' }); this.setState({ search: '' });
}} }}
tooltip="Clear search"
/> />
</> </>
); );
@ -266,6 +267,7 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
onClick={() => { onClick={() => {
this.setState({ showPicker: false }); this.setState({ showPicker: false });
}} }}
tooltip="Close picker"
/> />
); );
} }

View File

@ -22,7 +22,7 @@ export const VersionHistoryHeader = ({
return ( return (
<h3 className={styles.header}> <h3 className={styles.header}>
<IconButton name="arrow-left" size="xl" onClick={onClick} /> <IconButton name="arrow-left" size="xl" onClick={onClick} tooltip="Reset version" />
<span> <span>
Comparing {baseVersion} <Icon name="arrows-h" /> {newVersion}{' '} Comparing {baseVersion} <Icon name="arrows-h" /> {newVersion}{' '}
{isNewLatest && <cite className="muted">(Latest)</cite>} {isNewLatest && <cite className="muted">(Latest)</cite>}

View File

@ -199,10 +199,10 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
} }
suffix={ suffix={
<IconButton <IconButton
aria-label={`Remove ${ariaLabel}`}
className={styles.trashIcon} className={styles.trashIcon}
name="trash-alt" name="trash-alt"
onClick={() => this.onRemoveThreshold(threshold)} onClick={() => this.onRemoveThreshold(threshold)}
tooltip={`Remove ${ariaLabel}`}
/> />
} }
/> />

View File

@ -220,8 +220,20 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
)} )}
<td className={styles.textAlignCenter}> <td className={styles.textAlignCenter}>
<HorizontalGroup spacing="sm"> <HorizontalGroup spacing="sm">
<IconButton name="copy" onClick={() => onDuplicate(index)} data-testid="duplicate-value-mapping" /> <IconButton
<IconButton name="trash-alt" onClick={() => onRemove(index)} data-testid="remove-value-mapping" /> name="copy"
onClick={() => onDuplicate(index)}
data-testid="duplicate-value-mapping"
aria-label="Duplicate value mapping"
tooltip="Duplicate"
/>
<IconButton
name="trash-alt"
onClick={() => onRemove(index)}
data-testid="remove-value-mapping"
aria-label="Delete value mapping"
tooltip="Delete"
/>
</HorizontalGroup> </HorizontalGroup>
</td> </td>
</tr> </tr>

View File

@ -67,7 +67,7 @@ const setup = (propOverrides = {}) => {
describe('ExploreQueryInspector', () => { describe('ExploreQueryInspector', () => {
it('should render closable drawer component', () => { it('should render closable drawer component', () => {
setup(); setup();
expect(screen.getByTitle(/close query inspector/i)).toBeInTheDocument(); expect(screen.getByLabelText(/close query inspector/i)).toBeInTheDocument();
}); });
it('should render 4 Tabs if queryResponse has no error', () => { it('should render 4 Tabs if queryResponse has no error', () => {
setup(); setup();

View File

@ -384,28 +384,28 @@ describe('RichHistoryCard', () => {
}); });
it('should have title "Edit comment" at comment icon, if comment present', async () => { it('should have title "Edit comment" at comment icon, if comment present', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const editComment = await screen.findByTitle('Edit comment'); const editComment = await screen.findByLabelText('Edit comment');
const addComment = screen.queryByTitle('Add comment'); const addComment = screen.queryByTitle('Add comment');
expect(editComment).toBeInTheDocument(); expect(editComment).toBeInTheDocument();
expect(addComment).not.toBeInTheDocument(); expect(addComment).not.toBeInTheDocument();
}); });
it('should have title "Add comment" at comment icon, if no comment present', async () => { it('should have title "Add comment" at comment icon, if no comment present', async () => {
setup(); setup();
const addComment = await screen.findByTitle('Add comment'); const addComment = await screen.findByLabelText('Add comment');
const editComment = await screen.queryByTitle('Edit comment'); const editComment = await screen.queryByTitle('Edit comment');
expect(addComment).toBeInTheDocument(); expect(addComment).toBeInTheDocument();
expect(editComment).not.toBeInTheDocument(); expect(editComment).not.toBeInTheDocument();
}); });
it('should open update comment form when edit comment button clicked', async () => { it('should open update comment form when edit comment button clicked', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const editComment = await screen.findByTitle('Edit comment'); const editComment = await screen.findByLabelText('Edit comment');
await userEvent.click(editComment); await userEvent.click(editComment);
const updateCommentForm = await screen.findByLabelText('Update comment form'); const updateCommentForm = await screen.findByLabelText('Update comment form');
expect(updateCommentForm).toBeInTheDocument(); expect(updateCommentForm).toBeInTheDocument();
}); });
it('should close update comment form when escape key pressed', async () => { it('should close update comment form when escape key pressed', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const editComment = await screen.findByTitle('Edit comment'); const editComment = await screen.findByLabelText('Edit comment');
await userEvent.click(editComment); await userEvent.click(editComment);
const updateCommentForm = await screen.findByLabelText('Update comment form'); const updateCommentForm = await screen.findByLabelText('Update comment form');
await userEvent.click(updateCommentForm); await userEvent.click(updateCommentForm);
@ -417,7 +417,7 @@ describe('RichHistoryCard', () => {
}); });
it('should close update comment form when enter and shift keys pressed', async () => { it('should close update comment form when enter and shift keys pressed', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const editComment = await screen.findByTitle('Edit comment'); const editComment = await screen.findByLabelText('Edit comment');
await userEvent.click(editComment); await userEvent.click(editComment);
const updateCommentForm = await screen.findByLabelText('Update comment form'); const updateCommentForm = await screen.findByLabelText('Update comment form');
await userEvent.click(updateCommentForm); await userEvent.click(updateCommentForm);
@ -430,7 +430,7 @@ describe('RichHistoryCard', () => {
}); });
it('should close update comment form when enter and ctrl keys pressed', async () => { it('should close update comment form when enter and ctrl keys pressed', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const editComment = await screen.findByTitle('Edit comment'); const editComment = await screen.findByLabelText('Edit comment');
await userEvent.click(editComment); await userEvent.click(editComment);
const updateCommentForm = await screen.findByLabelText('Update comment form'); const updateCommentForm = await screen.findByLabelText('Update comment form');
await userEvent.click(updateCommentForm); await userEvent.click(updateCommentForm);
@ -443,7 +443,7 @@ describe('RichHistoryCard', () => {
}); });
it('should not close update comment form when enter key pressed', async () => { it('should not close update comment form when enter key pressed', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const editComment = await screen.findByTitle('Edit comment'); const editComment = await screen.findByLabelText('Edit comment');
await userEvent.click(editComment); await userEvent.click(editComment);
const updateCommentForm = await screen.findByLabelText('Update comment form'); const updateCommentForm = await screen.findByLabelText('Update comment form');
await userEvent.click(updateCommentForm); await userEvent.click(updateCommentForm);
@ -459,14 +459,14 @@ describe('RichHistoryCard', () => {
describe('starring', () => { describe('starring', () => {
it('should have title "Star query", if not starred', async () => { it('should have title "Star query", if not starred', async () => {
setup(); setup();
const starButton = await screen.findByTitle('Star query'); const starButton = await screen.findByLabelText('Star query');
expect(starButton).toBeInTheDocument(); expect(starButton).toBeInTheDocument();
await userEvent.click(starButton); await userEvent.click(starButton);
expect(starRichHistoryMock).toBeCalledWith(starredQueryWithComment.id, true); expect(starRichHistoryMock).toBeCalledWith(starredQueryWithComment.id, true);
}); });
it('should have title "Unstar query", if not starred', async () => { it('should have title "Unstar query", if not starred', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const unstarButton = await screen.findByTitle('Unstar query'); const unstarButton = await screen.findByLabelText('Unstar query');
expect(unstarButton).toBeInTheDocument(); expect(unstarButton).toBeInTheDocument();
await userEvent.click(unstarButton); await userEvent.click(unstarButton);
expect(starRichHistoryMock).toBeCalledWith(starredQueryWithComment.id, false); expect(starRichHistoryMock).toBeCalledWith(starredQueryWithComment.id, false);
@ -476,14 +476,14 @@ describe('RichHistoryCard', () => {
describe('deleting', () => { describe('deleting', () => {
it('should delete if not starred', async () => { it('should delete if not starred', async () => {
setup(); setup();
const deleteButton = await screen.findByTitle('Delete query'); const deleteButton = await screen.findByLabelText('Delete query');
expect(deleteButton).toBeInTheDocument(); expect(deleteButton).toBeInTheDocument();
await userEvent.click(deleteButton); await userEvent.click(deleteButton);
expect(deleteRichHistoryMock).toBeCalledWith(starredQueryWithComment.id); expect(deleteRichHistoryMock).toBeCalledWith(starredQueryWithComment.id);
}); });
it('should display modal before deleting if starred', async () => { it('should display modal before deleting if starred', async () => {
setup({ query: starredQueryWithComment }); setup({ query: starredQueryWithComment });
const deleteButton = await screen.findByTitle('Delete query'); const deleteButton = await screen.findByLabelText('Delete query');
await userEvent.click(deleteButton); await userEvent.click(deleteButton);
expect(deleteRichHistoryMock).not.toBeCalled(); expect(deleteRichHistoryMock).not.toBeCalled();
expect(appEvents.publish).toHaveBeenCalledWith(new ShowConfirmModalEvent(expect.anything())); expect(appEvents.publish).toHaveBeenCalledWith(new ShowConfirmModalEvent(expect.anything()));

View File

@ -304,18 +304,18 @@ export function RichHistoryCard(props: Props) {
<IconButton <IconButton
name="comment-alt" name="comment-alt"
onClick={toggleActiveUpdateComment} onClick={toggleActiveUpdateComment}
title={query.comment?.length > 0 ? 'Edit comment' : 'Add comment'} tooltip={query.comment?.length > 0 ? 'Edit comment' : 'Add comment'}
/> />
<IconButton name="copy" onClick={onCopyQuery} title="Copy query to clipboard" /> <IconButton name="copy" onClick={onCopyQuery} tooltip="Copy query to clipboard" />
{value?.dsInstance && ( {value?.dsInstance && (
<IconButton name="share-alt" onClick={onCreateShortLink} title="Copy shortened link to clipboard" /> <IconButton name="share-alt" onClick={onCreateShortLink} tooltip="Copy shortened link to clipboard" />
)} )}
<IconButton name="trash-alt" title={'Delete query'} onClick={onDeleteQuery} /> <IconButton name="trash-alt" title="Delete query" tooltip="Delete query" onClick={onDeleteQuery} />
<IconButton <IconButton
name={query.starred ? 'favorite' : 'star'} name={query.starred ? 'favorite' : 'star'}
iconType={query.starred ? 'mono' : 'default'} iconType={query.starred ? 'mono' : 'default'}
onClick={onStarrQuery} onClick={onStarrQuery}
title={query.starred ? 'Unstar query' : 'Star query'} tooltip={query.starred ? 'Unstar query' : 'Star query'}
/> />
</div> </div>
); );

View File

@ -44,7 +44,9 @@ export default class SearchBarInput extends React.PureComponent<Props> {
const suffix = ( const suffix = (
<> <>
{inputProps.suffix} {inputProps.suffix}
{allowClear && value && value.length && <IconButton name="times" onClick={this.clearUiFind} />} {allowClear && value && value.length && (
<IconButton name="times" onClick={this.clearUiFind} tooltip="Clear input" />
)}
</> </>
); );

View File

@ -103,11 +103,11 @@ export class LogRowMessage extends PureComponent<Props> {
<span className={cx('log-row-menu', styles.rowMenu)} onClick={this.onLogRowClick}> <span className={cx('log-row-menu', styles.rowMenu)} onClick={this.onLogRowClick}>
{shouldShowContextToggle && ( {shouldShowContextToggle && (
<IconButton <IconButton
tooltip="Show context"
tooltipPlacement="top"
size="md" size="md"
name="gf-show-context" name="gf-show-context"
onClick={this.onShowContextClick} onClick={this.onShowContextClick}
tooltip="Show context"
tooltipPlacement="top"
/> />
)} )}
<ClipboardButton <ClipboardButton

View File

@ -64,6 +64,7 @@ export const PanelTypeCard = ({
}} }}
className={styles.deleteButton} className={styles.deleteButton}
aria-label="Delete button on panel type card" aria-label="Delete button on panel type card"
tooltip="Delete"
/> />
)} )}
</div> </div>

View File

@ -80,8 +80,7 @@ export const PlaylistTableRows = ({ items, onDelete }: Props) => {
name="times" name="times"
size="md" size="md"
onClick={() => onDelete(index)} onClick={() => onDelete(index)}
aria-label={selectors.pages.PlaylistForm.itemDelete} tooltip={selectors.pages.PlaylistForm.itemDelete}
type="button"
/> />
<Icon title="Drag and drop to reorder" name="draggabledots" size="md" /> <Icon title="Drag and drop to reorder" name="draggabledots" size="md" />
</div> </div>

View File

@ -44,7 +44,7 @@ export function ManageActions({ items, folder, onChange, clearSelection }: Props
return ( return (
<div className={styles.actionRow} data-testid="manage-actions"> <div className={styles.actionRow} data-testid="manage-actions">
<HorizontalGroup spacing="md" width="auto"> <HorizontalGroup spacing="md" width="auto">
<IconButton name="check-square" onClick={clearSelection} title="Uncheck everything" /> <IconButton name="check-square" onClick={clearSelection} tooltip="Uncheck everything" />
<Button disabled={!canMove} onClick={onMove} icon="exchange-alt" variant="secondary"> <Button disabled={!canMove} onClick={onMove} icon="exchange-alt" variant="secondary">
Move Move
</Button> </Button>

View File

@ -65,7 +65,7 @@ export const generateColumns = (
if (selection('*', '*')) { if (selection('*', '*')) {
return ( return (
<div className={styles.checkboxHeader}> <div className={styles.checkboxHeader}>
<IconButton name="check-square" onClick={clearSelection} /> <IconButton name="check-square" onClick={clearSelection} tooltip="Clear selection" />
</div> </div>
); );
} }

View File

@ -133,6 +133,7 @@ const ServiceAccountListItem = memo(
size="md" size="md"
onClick={() => onRemoveButtonClick(serviceAccount)} onClick={() => onRemoveButtonClick(serviceAccount)}
aria-label={`Delete service account ${serviceAccount.name}`} aria-label={`Delete service account ${serviceAccount.name}`}
tooltip="Delete account"
/> />
)} )}
</HorizontalGroup> </HorizontalGroup>

View File

@ -93,7 +93,12 @@ const SupportBundlesUnconnected = ({ supportBundles, isLoading, loadBundles, rem
</th> </th>
<th> <th>
{hasDeleteAccess && ( {hasDeleteAccess && (
<IconButton onClick={() => removeBundle(bundle.uid)} name="trash-alt" variant="destructive" /> <IconButton
onClick={() => removeBundle(bundle.uid)}
name="trash-alt"
variant="destructive"
tooltip="Remove bundle"
/>
)} )}
</th> </th>
</tr> </tr>

View File

@ -145,6 +145,7 @@ const DraggableFieldName = ({
size="md" size="md"
name={visible ? 'eye' : 'eye-slash'} name={visible ? 'eye' : 'eye-slash'}
onClick={() => onToggleVisibility(fieldName, visible)} onClick={() => onToggleVisibility(fieldName, visible)}
tooltip={visible ? 'Disable' : 'Enable'}
/> />
<span className={styles.name} title={fieldName}> <span className={styles.name} title={fieldName}>
{fieldName} {fieldName}

View File

@ -67,7 +67,7 @@ export function JSONPathEditor({ options, onChange }: Props) {
/> />
</InlineField> </InlineField>
<InlineField className={cx(style.removeIcon)}> <InlineField className={cx(style.removeIcon)}>
<IconButton onClick={() => removeJSONPath(key)} name={'trash-alt'} /> <IconButton onClick={() => removeJSONPath(key)} name={'trash-alt'} tooltip="Remove path" />
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
</li> </li>

View File

@ -94,7 +94,7 @@ export function VariableEditorListRow({
propsOnDuplicate(identifier); propsOnDuplicate(identifier);
}} }}
name="copy" name="copy"
title="Duplicate variable" tooltip="Duplicate variable"
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowDuplicateButtons(variable.name)} aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowDuplicateButtons(variable.name)}
/> />
</td> </td>
@ -107,7 +107,7 @@ export function VariableEditorListRow({
propsOnDelete(identifier); propsOnDelete(identifier);
}} }}
name="trash-alt" name="trash-alt"
title="Remove variable" tooltip="Remove variable"
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowRemoveButtons(variable.name)} aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowRemoveButtons(variable.name)}
/> />
</td> </td>

View File

@ -35,7 +35,7 @@ export const VariableUsagesButton = ({ id, usages, isAdhoc }: Props) => {
showModal(); showModal();
}} }}
name="code-branch" name="code-branch"
title="Show usages" tooltip="Show usages"
/> />
); );
}} }}

View File

@ -31,7 +31,7 @@ export const VariablesUnknownButton = ({ id, usages }: Props) => {
<IconButton <IconButton
onClick={() => showModal()} onClick={() => showModal()}
name="code-branch" name="code-branch"
title="Show usages" tooltip="Show usages"
data-testid="VariablesUnknownButton" data-testid="VariablesUnknownButton"
/> />
); );

View File

@ -33,9 +33,8 @@ export const QueryEditorRow = ({
onClick={onHideClick} onClick={onHideClick}
size="sm" size="sm"
aria-pressed={hidden} aria-pressed={hidden}
aria-label="hide metric"
className={styles.icon} className={styles.icon}
type="button" tooltip="Hide metric"
/> />
)} )}
<IconButton <IconButton
@ -44,8 +43,7 @@ export const QueryEditorRow = ({
className={styles.icon} className={styles.icon}
onClick={onRemoveClick || noop} onClick={onRemoveClick || noop}
disabled={!onRemoveClick} disabled={!onRemoveClick}
aria-label="remove metric" tooltip="Remove metric"
type="button"
/> />
</span> </span>
</InlineLabel> </InlineLabel>

View File

@ -72,7 +72,7 @@ export const NestedQuery = React.memo<Props>(
/> />
</div> </div>
<FlexItem grow={1} /> <FlexItem grow={1} />
<IconButton ariaLabel="Remove nested query" name="times" size="sm" onClick={() => onRemove(index)} /> <IconButton name="times" size="sm" onClick={() => onRemove(index)} tooltip="Remove nested query" />
</div> </div>
<div className={styles.body}> <div className={styles.body}>
<EditorRows> <EditorRows>

View File

@ -71,7 +71,7 @@ export const NestedQuery = React.memo<Props>((props) => {
/> />
</div> </div>
<FlexItem grow={1} /> <FlexItem grow={1} />
<IconButton name="times" size="sm" onClick={() => onRemove(index)} /> <IconButton name="times" size="sm" onClick={() => onRemove(index)} tooltip="Remove match" />
</div> </div>
<div className={styles.body}> <div className={styles.body}>
<EditorRows> <EditorRows>

View File

@ -65,45 +65,45 @@ export const QuickPositioning = ({ onPositionChange, element, settings }: Props)
return ( return (
<div className={styles.buttonGroup}> <div className={styles.buttonGroup}>
<IconButton <IconButton
name={'horizontal-align-left'} name="horizontal-align-left"
onClick={() => onQuickPositioningChange(QuickPlacement.Left)} onClick={() => onQuickPositioningChange(QuickPlacement.Left)}
className={styles.button} className={styles.button}
size={'lg'} size="lg"
tooltip={'Align left'} tooltip="Align left"
/> />
<IconButton <IconButton
name={'horizontal-align-center'} name="horizontal-align-center"
onClick={() => onQuickPositioningChange(QuickPlacement.HorizontalCenter)} onClick={() => onQuickPositioningChange(QuickPlacement.HorizontalCenter)}
className={styles.button} className={styles.button}
size={'lg'} size="lg"
tooltip={'Align horizontal centers'} tooltip="Align horizontal centers"
/> />
<IconButton <IconButton
name={'horizontal-align-right'} name="horizontal-align-right"
onClick={() => onQuickPositioningChange(QuickPlacement.Right)} onClick={() => onQuickPositioningChange(QuickPlacement.Right)}
className={styles.button} className={styles.button}
size={'lg'} size="lg"
tooltip={'Align right'} tooltip="Align right"
/> />
<IconButton <IconButton
name={'vertical-align-top'} name="vertical-align-top"
onClick={() => onQuickPositioningChange(QuickPlacement.Top)} onClick={() => onQuickPositioningChange(QuickPlacement.Top)}
size={'lg'} size="lg"
tooltip={'Align top'} tooltip="Align top"
/> />
<IconButton <IconButton
name={'vertical-align-center'} name="vertical-align-center"
onClick={() => onQuickPositioningChange(QuickPlacement.VerticalCenter)} onClick={() => onQuickPositioningChange(QuickPlacement.VerticalCenter)}
className={styles.button} className={styles.button}
size={'lg'} size="lg"
tooltip={'Align vertical centers'} tooltip="Align vertical centers"
/> />
<IconButton <IconButton
name={'vertical-align-bottom'} name="vertical-align-bottom"
onClick={() => onQuickPositioningChange(QuickPlacement.Bottom)} onClick={() => onQuickPositioningChange(QuickPlacement.Bottom)}
className={styles.button} className={styles.button}
size={'lg'} size="lg"
tooltip={'Align bottom'} tooltip="Align bottom"
/> />
</div> </div>
); );

View File

@ -84,7 +84,13 @@ export function InlineEdit({ onClose, id, scene }: Props) {
<strong className={styles.inlineEditorHeader}> <strong className={styles.inlineEditorHeader}>
<div className={styles.placeholder} /> <div className={styles.placeholder} />
<div>Canvas Inline Editor</div> <div>Canvas Inline Editor</div>
<IconButton name="times" size="xl" className={styles.inlineEditorClose} onClick={onClose} /> <IconButton
name="times"
size="xl"
className={styles.inlineEditorClose}
onClick={onClose}
tooltip="Close inline editor"
/>
</strong> </strong>
<div className={styles.inlineEditorContentWrapper}> <div className={styles.inlineEditorContentWrapper}>
<div className={styles.inlineEditorContent}> <div className={styles.inlineEditorContent}>

View File

@ -73,15 +73,17 @@ export const TreeNodeTitle = ({ settings, nodeData, setAllowSelection }: Props)
<div className={styles.actionButtonsWrapper}> <div className={styles.actionButtonsWrapper}>
<IconButton <IconButton
name="copy" name="copy"
title={'Duplicate'} title="Duplicate"
className={styles.actionIcon} className={styles.actionIcon}
onClick={() => onDuplicate(element)} onClick={() => onDuplicate(element)}
tooltip="Duplicate"
/> />
<IconButton <IconButton
name="trash-alt" name="trash-alt"
title={'remove'} title="remove"
className={styles.actionIcon} className={styles.actionIcon}
onClick={() => onDelete(element)} onClick={() => onDelete(element)}
tooltip="Remove"
/> />
</div> </div>
)} )}

View File

@ -74,7 +74,7 @@ export class RenderInfoViewer extends Component<Props> {
return ( return (
<div> <div>
<div> <div>
<IconButton name="step-backward" title="reset counters" onClick={this.resetCounters} /> <IconButton name="step-backward" title="reset counters" onClick={this.resetCounters} tooltip="Step back" />
<span> <span>
{showCounters.render && <span>Render: {this.counters.render}&nbsp;</span>} {showCounters.render && <span>Render: {this.counters.render}&nbsp;</span>}
{showCounters.dataChanged && <span>Data: {this.counters.dataChanged}&nbsp;</span>} {showCounters.dataChanged && <span>Data: {this.counters.dataChanged}&nbsp;</span>}

View File

@ -113,7 +113,7 @@ export const LineStyleEditor = ({ value, onChange }: Props) => {
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
<IconButton name="question-circle" /> <IconButton name="question-circle" tooltip="Help" />
</a> </a>
</div> </div>
</> </>

View File

@ -46,9 +46,9 @@ export const TimezonesEditor = ({ value, onChange }: Props) => {
/> />
</span> </span>
{idx === value.length - 1 ? ( {idx === value.length - 1 ? (
<IconButton ariaLabel="Add timezone" name="plus" onClick={addTimezone} /> <IconButton name="plus" onClick={addTimezone} tooltip="Add timezone" />
) : ( ) : (
<IconButton ariaLabel="Remove timezone" name="times" onClick={() => removeTimezone(idx)} /> <IconButton name="times" onClick={() => removeTimezone(idx)} tooltip="Remove timezone" />
)} )}
</div> </div>
))} ))}

View File

@ -56,8 +56,8 @@ export const AnnotationTooltip = ({
if (canEdit || canDelete) { if (canEdit || canDelete) {
editControls = ( editControls = (
<div className={styles.editControls}> <div className={styles.editControls}>
{canEdit && <IconButton name={'pen'} size={'sm'} onClick={onEdit} />} {canEdit && <IconButton name={'pen'} size={'sm'} onClick={onEdit} tooltip="Edit" />}
{canDelete && <IconButton name={'trash-alt'} size={'sm'} onClick={onDelete} />} {canDelete && <IconButton name={'trash-alt'} size={'sm'} onClick={onDelete} tooltip="Delete" />}
</div> </div>
); );
} }

View File

@ -132,6 +132,7 @@ export const AutoEditor = ({
exclude, exclude,
}); });
}} }}
tooltip={v.value ? 'Disable' : 'Enable'}
/> />
{v.label} {v.label}
</div> </div>

View File

@ -77,6 +77,7 @@ export const ManualEditor = ({
title={'remove'} title={'remove'}
className={cx(style.actionIcon)} className={cx(style.actionIcon)}
onClick={() => onSeriesDelete(index)} onClick={() => onSeriesDelete(index)}
tooltip="Delete series"
/> />
</div> </div>
); );