mirror of
https://github.com/grafana/grafana.git
synced 2026-07-29 15:59:50 -05:00
Plugin Catalog: Fix A11y issues on plugin list (#40629)
This commit is contained in:
@@ -13,6 +13,7 @@ export interface IconProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: IconName;
|
||||
size?: IconSize;
|
||||
type?: IconType;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const getIconStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
@@ -44,7 +45,7 @@ function getIconSubDir(name: IconName, type: string): string {
|
||||
}
|
||||
|
||||
export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
|
||||
({ size = 'md', type = 'default', name, className, style, ...divElementProps }, ref) => {
|
||||
({ size = 'md', type = 'default', name, className, style, title = '', ...divElementProps }, ref) => {
|
||||
const theme = useTheme();
|
||||
|
||||
/* Temporary solution to display also font awesome icons */
|
||||
@@ -73,6 +74,7 @@ export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
|
||||
src={svgPath}
|
||||
width={svgWid}
|
||||
height={svgHgt}
|
||||
title={title}
|
||||
className={cx(styles.icon, className, type === 'mono' ? { [styles.orange]: name === 'favorite' } : '')}
|
||||
style={style}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,32 @@ import { PluginErrorCode, PluginSignatureStatus, PluginType } from '@grafana/dat
|
||||
import { PluginListItem } from './PluginListItem';
|
||||
import { CatalogPlugin, PluginListDisplayMode } from '../types';
|
||||
|
||||
/**
|
||||
* The whole Icon component needs to be mock
|
||||
* currently is using react-inlinesvg that does not render the icon svg in the test.
|
||||
*
|
||||
* There is solution to mock the library on __mocks__
|
||||
* https://github.com/gilbarbara/react-inlinesvg/issues/145
|
||||
* But unfortunately that causes conflict with DashboardSearch.test.tsx
|
||||
*/
|
||||
|
||||
jest.mock('@grafana/ui', () => {
|
||||
const IconMock = ({ title }: { title: string }) => {
|
||||
return (
|
||||
<svg>
|
||||
<title> {title} </title>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
IconMock.displayName = 'Icon';
|
||||
return Object.assign({}, jest.requireActual('@grafana/ui'), { Icon: IconMock });
|
||||
});
|
||||
|
||||
describe('PluginListItem', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const plugin: CatalogPlugin = {
|
||||
description: 'The test plugin',
|
||||
downloads: 5,
|
||||
@@ -49,21 +74,21 @@ describe('PluginListItem', () => {
|
||||
const datasourcePlugin = { ...plugin, type: PluginType.datasource };
|
||||
render(<PluginListItem plugin={datasourcePlugin} pathName="" />);
|
||||
|
||||
expect(screen.getByLabelText(/datasource plugin icon/i)).toBeVisible();
|
||||
expect(screen.getByTitle(/datasource plugin/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a panel plugin with correct icon', () => {
|
||||
const panelPlugin = { ...plugin, type: PluginType.panel };
|
||||
render(<PluginListItem plugin={panelPlugin} pathName="" />);
|
||||
|
||||
expect(screen.getByLabelText(/panel plugin icon/i)).toBeVisible();
|
||||
expect(screen.getByTitle(/panel plugin/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders an app plugin with correct icon', () => {
|
||||
const appPlugin = { ...plugin, type: PluginType.app };
|
||||
render(<PluginListItem plugin={appPlugin} pathName="" />);
|
||||
|
||||
expect(screen.getByLabelText(/app plugin icon/i)).toBeVisible();
|
||||
expect(screen.getByTitle(/app plugin/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a disabled plugin with a badge to indicate its error', () => {
|
||||
@@ -92,21 +117,21 @@ describe('PluginListItem', () => {
|
||||
const datasourcePlugin = { ...plugin, type: PluginType.datasource };
|
||||
render(<PluginListItem plugin={datasourcePlugin} pathName="" displayMode={PluginListDisplayMode.List} />);
|
||||
|
||||
expect(screen.getByLabelText(/datasource plugin icon/i)).toBeVisible();
|
||||
expect(screen.getByTitle(/datasource plugin/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a panel plugin with correct icon', () => {
|
||||
const panelPlugin = { ...plugin, type: PluginType.panel };
|
||||
render(<PluginListItem plugin={panelPlugin} pathName="" displayMode={PluginListDisplayMode.List} />);
|
||||
|
||||
expect(screen.getByLabelText(/panel plugin icon/i)).toBeVisible();
|
||||
expect(screen.getByTitle(/panel plugin/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders an app plugin with correct icon', () => {
|
||||
const appPlugin = { ...plugin, type: PluginType.app };
|
||||
render(<PluginListItem plugin={appPlugin} pathName="" displayMode={PluginListDisplayMode.List} />);
|
||||
|
||||
expect(screen.getByLabelText(/app plugin icon/i)).toBeVisible();
|
||||
expect(screen.getByTitle(/app plugin/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a disabled plugin with a badge to indicate its error', () => {
|
||||
|
||||
@@ -30,7 +30,7 @@ export function PluginListItem({ plugin, pathName, displayMode = PluginListDispl
|
||||
<PluginListItemBadges plugin={plugin} />
|
||||
</div>
|
||||
<div className={styles.pluginType}>
|
||||
{plugin.type && <Icon name={PluginIconName[plugin.type]} aria-label={`${plugin.type} plugin icon`} />}
|
||||
{plugin.type && <Icon name={PluginIconName[plugin.type]} title={`${plugin.type} plugin`} />}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user