mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Icon: Simplify and remove wrapping <div>
(#76819)
* remove wrapping div * update tests, just gotta figure out how to handle fontawesome :( * add spinner.svg which matches the font-awesome spinner * add mock for react-inlinesvg * update mock and fix some tests * fix FormField test * fix CorrelationsPage tests * increase timeout
This commit is contained in:
parent
cad3c43bb1
commit
07cc7504ee
@ -28,6 +28,7 @@ module.exports = {
|
|||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.svg': '<rootDir>/public/test/mocks/svg.ts',
|
'\\.svg': '<rootDir>/public/test/mocks/svg.ts',
|
||||||
'\\.css': '<rootDir>/public/test/mocks/style.ts',
|
'\\.css': '<rootDir>/public/test/mocks/style.ts',
|
||||||
|
'react-inlinesvg': '<rootDir>/public/test/mocks/react-inlinesvg.tsx',
|
||||||
'monaco-editor/esm/vs/editor/editor.api': '<rootDir>/public/test/mocks/monaco.ts',
|
'monaco-editor/esm/vs/editor/editor.api': '<rootDir>/public/test/mocks/monaco.ts',
|
||||||
// near-membrane-dom won't work in a nodejs environment.
|
// near-membrane-dom won't work in a nodejs environment.
|
||||||
'@locker/near-membrane-dom': '<rootDir>/public/test/mocks/nearMembraneDom.ts',
|
'@locker/near-membrane-dom': '<rootDir>/public/test/mocks/nearMembraneDom.ts',
|
||||||
|
@ -93,6 +93,7 @@ export const availableIconsIndex = {
|
|||||||
eye: true,
|
eye: true,
|
||||||
'eye-slash': true,
|
'eye-slash': true,
|
||||||
'ellipsis-h': true,
|
'ellipsis-h': true,
|
||||||
|
/* @deprecated, use 'spinner' instead */
|
||||||
'fa fa-spinner': true,
|
'fa fa-spinner': true,
|
||||||
favorite: true,
|
favorite: true,
|
||||||
'file-alt': true,
|
'file-alt': true,
|
||||||
@ -198,6 +199,7 @@ export const availableIconsIndex = {
|
|||||||
sitemap: true,
|
sitemap: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
'sliders-v-alt': true,
|
'sliders-v-alt': true,
|
||||||
|
spinner: true,
|
||||||
'sort-amount-down': true,
|
'sort-amount-down': true,
|
||||||
'sort-amount-up': true,
|
'sort-amount-up': true,
|
||||||
'square-shape': true,
|
'square-shape': true,
|
||||||
|
@ -84,7 +84,6 @@ describe('DataLinksListItem', () => {
|
|||||||
setupTestContext({ link });
|
setupTestContext({ link });
|
||||||
|
|
||||||
expect(screen.getByText(/data link url not provided/i)).toBeInTheDocument();
|
expect(screen.getByText(/data link url not provided/i)).toBeInTheDocument();
|
||||||
expect(screen.getByTitle('')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -109,7 +108,6 @@ describe('DataLinksListItem', () => {
|
|||||||
setupTestContext({ link });
|
setupTestContext({ link });
|
||||||
|
|
||||||
expect(screen.getByText(/data link url not provided/i)).toBeInTheDocument();
|
expect(screen.getByText(/data link url not provided/i)).toBeInTheDocument();
|
||||||
expect(screen.getByTitle('')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -68,7 +68,7 @@ export const TimeRangeInput = ({
|
|||||||
onChange(timeRange);
|
onChange(timeRange);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRangeClear = (event: MouseEvent<HTMLDivElement>) => {
|
const onRangeClear = (event: MouseEvent<SVGElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const from = dateTime(null);
|
const from = dateTime(null);
|
||||||
const to = dateTime(null);
|
const to = dateTime(null);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { render, screen, waitFor } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@ -42,8 +42,6 @@ describe('FormField', () => {
|
|||||||
screen.getAllByRole('textbox')[0].focus();
|
screen.getAllByRole('textbox')[0].focus();
|
||||||
await userEvent.tab();
|
await userEvent.tab();
|
||||||
|
|
||||||
await waitFor(() => {
|
expect(await screen.findByText(tooltip)).toBeInTheDocument();
|
||||||
screen.getByText(tooltip);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -37,7 +37,7 @@ export const FormLabel = ({
|
|||||||
{children}
|
{children}
|
||||||
{tooltip && (
|
{tooltip && (
|
||||||
<Tooltip placement="top" content={tooltip} theme={'info'} interactive={interactive}>
|
<Tooltip placement="top" content={tooltip} theme={'info'} interactive={interactive}>
|
||||||
<Icon tabIndex={0} name="info-circle" size="sm" style={{ marginLeft: '10px' }} />
|
<Icon name="info-circle" size="sm" style={{ marginLeft: '10px' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</label>
|
</label>
|
||||||
|
@ -9,7 +9,7 @@ import { IconName, IconType, IconSize } from '../../types/icon';
|
|||||||
|
|
||||||
import { getIconRoot, getIconSubDir, getSvgSize } from './utils';
|
import { getIconRoot, getIconSubDir, getSvgSize } from './utils';
|
||||||
|
|
||||||
export interface IconProps extends React.HTMLAttributes<HTMLDivElement> {
|
export interface IconProps extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> {
|
||||||
name: IconName;
|
name: IconName;
|
||||||
size?: IconSize;
|
size?: IconSize;
|
||||||
type?: IconType;
|
type?: IconType;
|
||||||
@ -18,16 +18,14 @@ export interface IconProps extends React.HTMLAttributes<HTMLDivElement> {
|
|||||||
|
|
||||||
const getIconStyles = (theme: GrafanaTheme2) => {
|
const getIconStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
// line-height: 0; is needed for correct icon alignment in Safari
|
|
||||||
container: css({
|
|
||||||
label: 'Icon',
|
|
||||||
display: 'inline-block',
|
|
||||||
lineHeight: 0,
|
|
||||||
}),
|
|
||||||
icon: css({
|
icon: css({
|
||||||
verticalAlign: 'middle',
|
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
|
flexShrink: 0,
|
||||||
|
label: 'Icon',
|
||||||
|
// line-height: 0; is needed for correct icon alignment in Safari
|
||||||
|
lineHeight: 0,
|
||||||
|
verticalAlign: 'middle',
|
||||||
}),
|
}),
|
||||||
orange: css({
|
orange: css({
|
||||||
fill: theme.v1.palette.orange,
|
fill: theme.v1.palette.orange,
|
||||||
@ -35,53 +33,44 @@ const getIconStyles = (theme: GrafanaTheme2) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
|
export const Icon = React.forwardRef<SVGElement, IconProps>(
|
||||||
({ size = 'md', type = 'default', name, className, style, title = '', ...divElementProps }, ref) => {
|
({ size = 'md', type = 'default', name, className, style, title = '', ...rest }, ref) => {
|
||||||
const styles = useStyles2(getIconStyles);
|
const styles = useStyles2(getIconStyles);
|
||||||
|
|
||||||
/* Temporary solution to display also font awesome icons */
|
|
||||||
if (name?.startsWith('fa fa-')) {
|
|
||||||
return <i className={getFontAwesomeIconStyles(name, className)} {...divElementProps} style={style} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isIconName(name)) {
|
if (!isIconName(name)) {
|
||||||
console.warn('Icon component passed an invalid icon name', name);
|
console.warn('Icon component passed an invalid icon name', name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name || name.includes('..')) {
|
// handle the deprecated 'fa fa-spinner'
|
||||||
return <div ref={ref}>invalid icon name</div>;
|
const iconName: IconName = name === 'fa fa-spinner' ? 'spinner' : name;
|
||||||
}
|
|
||||||
|
|
||||||
const iconRoot = getIconRoot();
|
const iconRoot = getIconRoot();
|
||||||
const svgSize = getSvgSize(size);
|
const svgSize = getSvgSize(size);
|
||||||
const svgHgt = svgSize;
|
const svgHgt = svgSize;
|
||||||
const svgWid = name.startsWith('gf-bar-align') ? 16 : name.startsWith('gf-interp') ? 30 : svgSize;
|
const svgWid = name.startsWith('gf-bar-align') ? 16 : name.startsWith('gf-interp') ? 30 : svgSize;
|
||||||
const subDir = getIconSubDir(name, type);
|
const subDir = getIconSubDir(iconName, type);
|
||||||
const svgPath = `${iconRoot}${subDir}/${name}.svg`;
|
const svgPath = `${iconRoot}${subDir}/${iconName}.svg`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} {...divElementProps} ref={ref}>
|
<SVG
|
||||||
<SVG
|
innerRef={ref}
|
||||||
src={svgPath}
|
src={svgPath}
|
||||||
width={svgWid}
|
width={svgWid}
|
||||||
height={svgHgt}
|
height={svgHgt}
|
||||||
title={title}
|
title={title}
|
||||||
className={cx(styles.icon, className, type === 'mono' ? { [styles.orange]: name === 'favorite' } : '')}
|
className={cx(
|
||||||
style={style}
|
styles.icon,
|
||||||
/>
|
{
|
||||||
</div>
|
'fa-spin': iconName === 'spinner',
|
||||||
|
},
|
||||||
|
className,
|
||||||
|
type === 'mono' ? { [styles.orange]: name === 'favorite' } : ''
|
||||||
|
)}
|
||||||
|
style={style}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Icon.displayName = 'Icon';
|
Icon.displayName = 'Icon';
|
||||||
|
|
||||||
function getFontAwesomeIconStyles(iconName: string, className?: string): string {
|
|
||||||
return cx(
|
|
||||||
iconName,
|
|
||||||
{
|
|
||||||
'fa-spin': iconName === 'fa fa-spinner',
|
|
||||||
},
|
|
||||||
className
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -112,7 +112,7 @@ export class RefreshPicker extends PureComponent<Props> {
|
|||||||
tooltip={tooltip}
|
tooltip={tooltip}
|
||||||
onClick={onRefresh}
|
onClick={onRefresh}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
icon={isLoading ? 'fa fa-spinner' : 'sync'}
|
icon={isLoading ? 'spinner' : 'sync'}
|
||||||
style={width ? { width } : undefined}
|
style={width ? { width } : undefined}
|
||||||
data-testid={selectors.components.RefreshPicker.runButtonV2}
|
data-testid={selectors.components.RefreshPicker.runButtonV2}
|
||||||
>
|
>
|
||||||
|
@ -28,7 +28,7 @@ export const Spinner = ({ className, inline = false, iconClassName, style, size
|
|||||||
const styles = getStyles(size, inline);
|
const styles = getStyles(size, inline);
|
||||||
return (
|
return (
|
||||||
<div data-testid="Spinner" style={style} className={cx(styles, className)}>
|
<div data-testid="Spinner" style={style} className={cx(styles, className)}>
|
||||||
<Icon className={cx('fa-spin', iconClassName)} name="fa fa-spinner" aria-label="loading spinner" />
|
<Icon className={cx('fa-spin', iconClassName)} name="spinner" aria-label="loading spinner" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ export interface Props {
|
|||||||
label: string;
|
label: string;
|
||||||
removeIcon: boolean;
|
removeIcon: boolean;
|
||||||
count: number;
|
count: number;
|
||||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
onClick?: React.MouseEventHandler<SVGElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TagBadge extends React.Component<Props> {
|
export class TagBadge extends React.Component<Props> {
|
||||||
|
@ -91,7 +91,7 @@ export const GlobalConfigForm = ({ config, alertManagerSourceName }: Props) => {
|
|||||||
{!readOnly && (
|
{!readOnly && (
|
||||||
<>
|
<>
|
||||||
{loading && (
|
{loading && (
|
||||||
<Button disabled={true} icon="fa fa-spinner" variant="primary">
|
<Button disabled={true} icon="spinner" variant="primary">
|
||||||
Saving...
|
Saving...
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -203,7 +203,7 @@ export const TemplateForm = ({ existing, alertManagerSourceName, config, provena
|
|||||||
</Field>
|
</Field>
|
||||||
<div className={styles.buttons}>
|
<div className={styles.buttons}>
|
||||||
{loading && (
|
{loading && (
|
||||||
<Button disabled={true} icon="fa fa-spinner" variant="primary">
|
<Button disabled={true} icon="spinner" variant="primary">
|
||||||
Saving...
|
Saving...
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -159,7 +159,7 @@ export function ChannelSubForm<R extends ChannelValues>({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleTest()}
|
onClick={() => handleTest()}
|
||||||
icon={testingReceiver ? 'fa fa-spinner' : 'message'}
|
icon={testingReceiver ? 'spinner' : 'message'}
|
||||||
>
|
>
|
||||||
Test
|
Test
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -186,7 +186,7 @@ export function ReceiverForm<R extends ChannelValues>({
|
|||||||
{isEditable && (
|
{isEditable && (
|
||||||
<>
|
<>
|
||||||
{isSubmitting && (
|
{isSubmitting && (
|
||||||
<Button disabled={true} icon="fa fa-spinner" variant="primary">
|
<Button disabled={true} icon="spinner" variant="primary">
|
||||||
Saving...
|
Saving...
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -488,7 +488,7 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange }: P
|
|||||||
{config.expressionsEnabled && <TypeSelectorButton onClickType={onClickType} />}
|
{config.expressionsEnabled && <TypeSelectorButton onClickType={onClickType} />}
|
||||||
|
|
||||||
{isPreviewLoading && (
|
{isPreviewLoading && (
|
||||||
<Button icon="fa fa-spinner" type="button" variant="destructive" onClick={cancelQueries}>
|
<Button icon="spinner" type="button" variant="destructive" onClick={cancelQueries}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -242,7 +242,7 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
|
|||||||
</FieldSet>
|
</FieldSet>
|
||||||
<div className={styles.flexRow}>
|
<div className={styles.flexRow}>
|
||||||
{loading && (
|
{loading && (
|
||||||
<Button disabled={true} icon="fa fa-spinner" variant="primary">
|
<Button disabled={true} icon="spinner" variant="primary">
|
||||||
Saving...
|
Saving...
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -292,7 +292,9 @@ describe('CorrelationsPage', () => {
|
|||||||
|
|
||||||
await userEvent.click(await screen.findByRole('button', { name: /add$/i }));
|
await userEvent.click(await screen.findByRole('button', { name: /add$/i }));
|
||||||
|
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_added');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_added');
|
||||||
|
});
|
||||||
|
|
||||||
// the table showing correlations should have appeared
|
// the table showing correlations should have appeared
|
||||||
expect(await screen.findByRole('table')).toBeInTheDocument();
|
expect(await screen.findByRole('table')).toBeInTheDocument();
|
||||||
@ -439,7 +441,9 @@ describe('CorrelationsPage', () => {
|
|||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /add$/i }));
|
await userEvent.click(screen.getByRole('button', { name: /add$/i }));
|
||||||
|
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_added');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_added');
|
||||||
|
});
|
||||||
|
|
||||||
// the table showing correlations should have appeared
|
// the table showing correlations should have appeared
|
||||||
expect(await screen.findByRole('table')).toBeInTheDocument();
|
expect(await screen.findByRole('table')).toBeInTheDocument();
|
||||||
@ -474,7 +478,9 @@ describe('CorrelationsPage', () => {
|
|||||||
|
|
||||||
expect(screen.queryByRole('cell', { name: /some label$/i })).not.toBeInTheDocument();
|
expect(screen.queryByRole('cell', { name: /some label$/i })).not.toBeInTheDocument();
|
||||||
|
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_deleted');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_deleted');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly edits correlations', async () => {
|
it('correctly edits correlations', async () => {
|
||||||
@ -486,7 +492,9 @@ describe('CorrelationsPage', () => {
|
|||||||
const rowExpanderButton = within(tableRows[0]).getByRole('button', { name: /toggle row expanded/i });
|
const rowExpanderButton = within(tableRows[0]).getByRole('button', { name: /toggle row expanded/i });
|
||||||
await userEvent.click(rowExpanderButton);
|
await userEvent.click(rowExpanderButton);
|
||||||
|
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_details_expanded');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_details_expanded');
|
||||||
|
});
|
||||||
|
|
||||||
await userEvent.clear(screen.getByRole('textbox', { name: /label/i }));
|
await userEvent.clear(screen.getByRole('textbox', { name: /label/i }));
|
||||||
await userEvent.type(screen.getByRole('textbox', { name: /label/i }), 'edited label');
|
await userEvent.type(screen.getByRole('textbox', { name: /label/i }), 'edited label');
|
||||||
@ -500,9 +508,11 @@ describe('CorrelationsPage', () => {
|
|||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /save$/i }));
|
await userEvent.click(screen.getByRole('button', { name: /save$/i }));
|
||||||
|
|
||||||
expect(await screen.findByRole('cell', { name: /edited label$/i })).toBeInTheDocument();
|
expect(await screen.findByRole('cell', { name: /edited label$/i }, { timeout: 5000 })).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_edited');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_edited');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly edits transformations', async () => {
|
it('correctly edits transformations', async () => {
|
||||||
@ -553,7 +563,9 @@ describe('CorrelationsPage', () => {
|
|||||||
expect(screen.getByText('Please define an expression')).toBeInTheDocument();
|
expect(screen.getByText('Please define an expression')).toBeInTheDocument();
|
||||||
await userEvent.type(screen.getByLabelText(/expression/i), 'test expression');
|
await userEvent.type(screen.getByLabelText(/expression/i), 'test expression');
|
||||||
await userEvent.click(screen.getByRole('button', { name: /save$/i }));
|
await userEvent.click(screen.getByRole('button', { name: /save$/i }));
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_edited');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_edited');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -684,7 +696,9 @@ describe('CorrelationsPage', () => {
|
|||||||
|
|
||||||
await userEvent.click(rowExpanderButton);
|
await userEvent.click(rowExpanderButton);
|
||||||
|
|
||||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_details_expanded');
|
await waitFor(() => {
|
||||||
|
expect(mocks.reportInteraction).toHaveBeenCalledWith('grafana_correlations_details_expanded');
|
||||||
|
});
|
||||||
|
|
||||||
// form elements should be readonly
|
// form elements should be readonly
|
||||||
const labelInput = await screen.findByRole('textbox', { name: /label/i });
|
const labelInput = await screen.findByRole('textbox', { name: /label/i });
|
||||||
|
@ -11,7 +11,7 @@ export const CorrelationFormNavigation = () => {
|
|||||||
const { readOnly, loading, correlation } = useCorrelationsFormContext();
|
const { readOnly, loading, correlation } = useCorrelationsFormContext();
|
||||||
|
|
||||||
const LastPageNext = !readOnly && (
|
const LastPageNext = !readOnly && (
|
||||||
<Button variant="primary" icon={loading ? 'fa fa-spinner' : 'save'} type="submit" disabled={loading}>
|
<Button variant="primary" icon={loading ? 'spinner' : 'save'} type="submit" disabled={loading}>
|
||||||
{correlation === undefined ? 'Add' : 'Save'}
|
{correlation === undefined ? 'Add' : 'Save'}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -45,7 +45,7 @@ export const SaveDashboardForm = ({ dashboard, onCancel, onSubmit, onSuccess, sa
|
|||||||
<Button variant="secondary" onClick={onCancel} fill="outline">
|
<Button variant="secondary" onClick={onCancel} fill="outline">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={!hasChanges} icon={saving ? 'fa fa-spinner' : undefined}>
|
<Button type="submit" disabled={!hasChanges} icon={saving ? 'spinner' : undefined}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
{!hasChanges && <div>No changes to save</div>}
|
{!hasChanges && <div>No changes to save</div>}
|
||||||
|
@ -126,7 +126,7 @@ export const SaveDashboardForm = ({
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!saveModel.hasChanges || isLoading}
|
disabled={!saveModel.hasChanges || isLoading}
|
||||||
icon={saving ? 'fa fa-spinner' : undefined}
|
icon={saving ? 'spinner' : undefined}
|
||||||
aria-label={selectors.pages.SaveDashboardModal.save}
|
aria-label={selectors.pages.SaveDashboardModal.save}
|
||||||
>
|
>
|
||||||
{isLoading ? 'Saving...' : 'Save'}
|
{isLoading ? 'Saving...' : 'Save'}
|
||||||
|
@ -92,12 +92,7 @@ export const PlaylistForm = ({ onSubmit, playlist }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HorizontalGroup>
|
<HorizontalGroup>
|
||||||
<Button
|
<Button type="submit" variant="primary" disabled={isDisabled} icon={saving ? 'spinner' : undefined}>
|
||||||
type="submit"
|
|
||||||
variant="primary"
|
|
||||||
disabled={isDisabled}
|
|
||||||
icon={saving ? 'fa fa-spinner' : undefined}
|
|
||||||
>
|
|
||||||
<Trans i18nKey="playlist-edit.form.save">Save</Trans>
|
<Trans i18nKey="playlist-edit.form.save">Save</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
<LinkButton variant="secondary" href={`${config.appSubUrl}/playlists`}>
|
<LinkButton variant="secondary" href={`${config.appSubUrl}/playlists`}>
|
||||||
|
@ -132,7 +132,7 @@ export const MoveToFolderModal = ({ results, onMoveItems, onDismiss }: Props) =>
|
|||||||
<Button variant="secondary" onClick={onDismiss} fill="outline">
|
<Button variant="secondary" onClick={onDismiss} fill="outline">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button icon={moving ? 'fa fa-spinner' : undefined} variant="primary" onClick={moveTo}>
|
<Button icon={moving ? 'spinner' : undefined} variant="primary" onClick={moveTo}>
|
||||||
Move
|
Move
|
||||||
</Button>
|
</Button>
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
|
@ -138,7 +138,9 @@ describe('VisualMetricQueryEditor', () => {
|
|||||||
expect(screen.getByText('metric.test_label')).toBeInTheDocument();
|
expect(screen.getByText('metric.test_label')).toBeInTheDocument();
|
||||||
const service = await screen.findByLabelText('Service');
|
const service = await screen.findByLabelText('Service');
|
||||||
openMenu(service);
|
openMenu(service);
|
||||||
await select(service, 'Srv 2', { container: document.body });
|
await act(async () => {
|
||||||
|
await select(service, 'Srv 2', { container: document.body });
|
||||||
|
});
|
||||||
expect(onChange).toBeCalledWith(expect.objectContaining({ filters: ['metric.type', '=', 'type2'] }));
|
expect(onChange).toBeCalledWith(expect.objectContaining({ filters: ['metric.type', '=', 'type2'] }));
|
||||||
expect(query).toEqual(defaultQuery);
|
expect(query).toEqual(defaultQuery);
|
||||||
expect(screen.queryByText('metric.test_label')).not.toBeInTheDocument();
|
expect(screen.queryByText('metric.test_label')).not.toBeInTheDocument();
|
||||||
|
@ -88,8 +88,12 @@ exports[`VariableQueryEditor renders correctly 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="css-zyjsuv-input-suffix"
|
className="css-zyjsuv-input-suffix"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
className="css-1j2891d-Icon"
|
className="css-1d3xu67-Icon"
|
||||||
|
height={16}
|
||||||
|
id="public/img/icons/unicons/angle-down.svg"
|
||||||
|
title=""
|
||||||
|
width={16}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,7 +107,7 @@ const QueryHeader = ({
|
|||||||
variant={dataIsStale ? 'primary' : 'secondary'}
|
variant={dataIsStale ? 'primary' : 'secondary'}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={onRunQuery}
|
onClick={onRunQuery}
|
||||||
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
|
icon={data?.state === LoadingState.Loading ? 'spinner' : undefined}
|
||||||
disabled={data?.state === LoadingState.Loading || emptyLogsExpression}
|
disabled={data?.state === LoadingState.Loading || emptyLogsExpression}
|
||||||
>
|
>
|
||||||
Run queries
|
Run queries
|
||||||
|
@ -285,7 +285,7 @@ describe('LogGroupsSelector', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
await userEvent.click(screen.getByText('Select log groups'));
|
await userEvent.click(screen.getByText('Select log groups'));
|
||||||
await screen.getByRole('button', { name: 'select-clear-value' }).click();
|
await userEvent.click(screen.getByRole('button', { name: 'select-clear-value' }));
|
||||||
await userEvent.click(screen.getByText('Add log groups'));
|
await userEvent.click(screen.getByText('Add log groups'));
|
||||||
expect(onChange).toHaveBeenCalledWith([
|
expect(onChange).toHaveBeenCalledWith([
|
||||||
{
|
{
|
||||||
|
@ -176,7 +176,7 @@ export const LokiQueryEditor = React.memo<LokiQueryEditorProps>((props) => {
|
|||||||
variant={dataIsStale ? 'primary' : 'secondary'}
|
variant={dataIsStale ? 'primary' : 'secondary'}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={onRunQuery}
|
onClick={onRunQuery}
|
||||||
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
|
icon={data?.state === LoadingState.Loading ? 'spinner' : undefined}
|
||||||
disabled={data?.state === LoadingState.Loading}
|
disabled={data?.state === LoadingState.Loading}
|
||||||
>
|
>
|
||||||
{queries && queries.length > 1 ? `Run queries` : `Run query`}
|
{queries && queries.length > 1 ? `Run queries` : `Run query`}
|
||||||
|
@ -130,7 +130,7 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
|
|||||||
variant={dataIsStale ? 'primary' : 'secondary'}
|
variant={dataIsStale ? 'primary' : 'secondary'}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={onRunQuery}
|
onClick={onRunQuery}
|
||||||
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
|
icon={data?.state === LoadingState.Loading ? 'spinner' : undefined}
|
||||||
disabled={data?.state === LoadingState.Loading}
|
disabled={data?.state === LoadingState.Loading}
|
||||||
>
|
>
|
||||||
Run queries
|
Run queries
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 24 24"><path d="M6.8042,15A.99956.99956,0,0,0,5.438,14.63379l-1.73242,1a1.00016,1.00016,0,0,0,1,1.73242l1.73242-1A1.00073,1.00073,0,0,0,6.8042,15ZM3.70557,8.36621l1.73242,1a1.00016,1.00016,0,1,0,1-1.73242l-1.73242-1a1.00016,1.00016,0,0,0-1,1.73242ZM6,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H5A1,1,0,0,0,6,12ZM17.1958,9a1.0006,1.0006,0,0,0,1.36621.36621l1.73242-1a1.00016,1.00016,0,1,0-1-1.73242l-1.73242,1A1.00073,1.00073,0,0,0,17.1958,9ZM15,6.8042A1.0006,1.0006,0,0,0,16.36621,6.438l1-1.73242a1.00016,1.00016,0,1,0-1.73242-1l-1,1.73242A1.00073,1.00073,0,0,0,15,6.8042Zm5.29443,8.82959-1.73242-1a1.00016,1.00016,0,1,0-1,1.73242l1.73242,1a1.00016,1.00016,0,0,0,1-1.73242ZM16.36621,17.562a1.00016,1.00016,0,1,0-1.73242,1l1,1.73242a1.00016,1.00016,0,1,0,1.73242-1ZM21,11H19a1,1,0,0,0,0,2h2a1,1,0,0,0,0-2Zm-9,7a1,1,0,0,0-1,1v2a1,1,0,0,0,2,0V19A1,1,0,0,0,12,18Zm-3-.8042a.99954.99954,0,0,0-1.36621.36621l-1,1.73242a1.00016,1.00016,0,0,0,1.73242,1l1-1.73242A1.00073,1.00073,0,0,0,9,17.1958ZM12,2a1,1,0,0,0-1,1V5a1,1,0,0,0,2,0V3A1,1,0,0,0,12,2Z"/></svg>
|
|
Before Width: | Height: | Size: 1.1 KiB |
@ -1 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24"><path d="M5.1,16c-0.3-0.5-0.9-0.6-1.4-0.4c-0.5,0.3-0.6,0.9-0.4,1.4c0.3,0.5,0.9,0.6,1.4,0.4C5.2,17.1,5.3,16.5,5.1,16C5.1,16,5.1,16,5.1,16z M4.7,6.6C4.2,6.4,3.6,6.5,3.3,7C3.1,7.5,3.2,8.1,3.7,8.4C4.2,8.6,4.8,8.5,5.1,8C5.3,7.5,5.2,6.9,4.7,6.6z M20.3,8.4c0.5-0.3,0.6-0.9,0.4-1.4c-0.3-0.5-0.9-0.6-1.4-0.4c-0.5,0.3-0.6,0.9-0.4,1.4C19.2,8.5,19.8,8.6,20.3,8.4z M4,12c0-0.6-0.4-1-1-1s-1,0.4-1,1s0.4,1,1,1S4,12.6,4,12z M7.2,18.8c-0.5,0.1-0.9,0.7-0.7,1.2c0.1,0.5,0.7,0.9,1.2,0.7c0.5-0.1,0.9-0.7,0.7-1.2C8.3,19,7.8,18.7,7.2,18.8z M21,11c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S21.6,11,21,11z M20.3,15.6c-0.5-0.3-1.1-0.1-1.4,0.4c-0.3,0.5-0.1,1.1,0.4,1.4c0.5,0.3,1.1,0.1,1.4-0.4c0,0,0,0,0,0C20.9,16.5,20.8,15.9,20.3,15.6z M17,3.3c-0.5-0.3-1.1-0.1-1.4,0.4c-0.3,0.5-0.1,1.1,0.4,1.4c0.5,0.3,1.1,0.1,1.4-0.4c0,0,0,0,0,0C17.6,4.2,17.5,3.6,17,3.3z M16.8,18.8c-0.5-0.1-1.1,0.2-1.2,0.7c-0.1,0.5,0.2,1.1,0.7,1.2c0.5,0.1,1.1-0.2,1.2-0.7C17.6,19.5,17.3,19,16.8,18.8z M12,20c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S12.6,20,12,20z M12,2c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S12.6,2,12,2z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="1664" height="1728" viewBox="0 0 1664 1728">
|
||||||
|
<path fill="currentColor" d="M462 1394q0 53-37.5 90.5T334 1522q-52 0-90-38t-38-90q0-53 37.5-90.5T334 1266t90.5 37.5T462 1394zm498 206q0 53-37.5 90.5T832 1728t-90.5-37.5T704 1600t37.5-90.5T832 1472t90.5 37.5T960 1600zM256 896q0 53-37.5 90.5T128 1024t-90.5-37.5T0 896t37.5-90.5T128 768t90.5 37.5T256 896zm1202 498q0 52-38 90t-90 38q-53 0-90.5-37.5T1202 1394t37.5-90.5t90.5-37.5t90.5 37.5t37.5 90.5zM494 398q0 66-47 113t-113 47t-113-47t-47-113t47-113t113-47t113 47t47 113zm1170 498q0 53-37.5 90.5T1536 1024t-90.5-37.5T1408 896t37.5-90.5T1536 768t90.5 37.5T1664 896zm-640-704q0 80-56 136t-136 56t-136-56t-56-136t56-136T832 0t136 56t56 136zm530 206q0 93-66 158.5T1330 622q-93 0-158.5-65.5T1106 398q0-92 65.5-158t158.5-66q92 0 158 66t66 158z"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 840 B |
7
public/test/mocks/react-inlinesvg.tsx
Normal file
7
public/test/mocks/react-inlinesvg.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function ReactInlineSVG({ src, innerRef, cacheRequests, preProcessor, ...rest }) {
|
||||||
|
return <svg id={src} ref={innerRef} {...rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cacheStore = {};
|
Loading…
Reference in New Issue
Block a user