From 5b6831ab4d33859f9ae0c8e805955ca907fa25d3 Mon Sep 17 00:00:00 2001 From: Maria Alexandra <239999+axelavargas@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:36:51 +0200 Subject: [PATCH] Plugin Catalog: Fix A11y issues on plugin list (#40629) --- .../grafana-ui/src/components/Icon/Icon.tsx | 4 +- .../admin/components/PluginListItem.test.tsx | 37 ++++++++++++++++--- .../admin/components/PluginListItem.tsx | 2 +- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/grafana-ui/src/components/Icon/Icon.tsx b/packages/grafana-ui/src/components/Icon/Icon.tsx index 8e3b9129ecf..ab7734d4f80 100644 --- a/packages/grafana-ui/src/components/Icon/Icon.tsx +++ b/packages/grafana-ui/src/components/Icon/Icon.tsx @@ -13,6 +13,7 @@ export interface IconProps extends React.HTMLAttributes { 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( - ({ 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( src={svgPath} width={svgWid} height={svgHgt} + title={title} className={cx(styles.icon, className, type === 'mono' ? { [styles.orange]: name === 'favorite' } : '')} style={style} /> diff --git a/public/app/features/plugins/admin/components/PluginListItem.test.tsx b/public/app/features/plugins/admin/components/PluginListItem.test.tsx index 30ee6565c7e..f23c2f9a3c2 100644 --- a/public/app/features/plugins/admin/components/PluginListItem.test.tsx +++ b/public/app/features/plugins/admin/components/PluginListItem.test.tsx @@ -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 ( + + {title} + + ); + }; + 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(); - 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(); - 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(); - 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(); - 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(); - 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(); - 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', () => { diff --git a/public/app/features/plugins/admin/components/PluginListItem.tsx b/public/app/features/plugins/admin/components/PluginListItem.tsx index 0fd69691be0..0928d689b66 100644 --- a/public/app/features/plugins/admin/components/PluginListItem.tsx +++ b/public/app/features/plugins/admin/components/PluginListItem.tsx @@ -30,7 +30,7 @@ export function PluginListItem({ plugin, pathName, displayMode = PluginListDispl
- {plugin.type && } + {plugin.type && }
);