Plugins: Catalog to show all plugins by default (#87168)

* update plugin install filter to show all by default

* make pretty

* remove aria-label on lock icon

* wip: attempt to fix overflow

* switch to stack, add aria label back for testing

* make pretty

* switch to TextLink

* remove learn more link from catalog

* add hover text

* docs: update plugin install to reflect state filter change

* Revert "docs: update plugin install to reflect state filter change"

This reverts commit 16222aee0b.
This commit is contained in:
David Harris
2024-05-07 16:48:09 +03:00
committed by GitHub
parent 6b218b11cf
commit a4d296559c
6 changed files with 23 additions and 26 deletions
-3
View File
@@ -2869,9 +2869,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not re-export imported variable (\`./localPlugin.mock\`)", "1"],
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "2"]
],
"public/app/features/plugins/admin/components/Badges/PluginEnterpriseBadge.tsx:5381": [
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"]
],
"public/app/features/plugins/admin/components/Badges/PluginUpdateAvailableBadge.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
@@ -1,7 +1,7 @@
import React from 'react';
import { featureEnabled } from '@grafana/runtime';
import { Badge, Button, HorizontalGroup, PluginSignatureBadge, useStyles2 } from '@grafana/ui';
import { Badge, PluginSignatureBadge, Stack, useStyles2 } from '@grafana/ui';
import { CatalogPlugin } from '../../types';
@@ -11,26 +11,23 @@ type Props = { plugin: CatalogPlugin };
export function PluginEnterpriseBadge({ plugin }: Props): React.ReactElement {
const customBadgeStyles = useStyles2(getBadgeColor);
const onClick = (ev: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
ev.preventDefault();
window.open(
`https://grafana.com/grafana/plugins/${plugin.id}?utm_source=grafana_catalog_learn_more`,
'_blank',
'noopener,noreferrer'
);
};
if (featureEnabled('enterprise.plugins')) {
return <Badge text="Enterprise" color="blue" />;
}
return (
<HorizontalGroup>
<Stack wrap={'wrap'}>
<PluginSignatureBadge status={plugin.signature} />
<Badge icon="lock" aria-label="lock icon" text="Enterprise" color="blue" className={customBadgeStyles} />
<Button size="sm" fill="text" icon="external-link-alt" onClick={onClick}>
Learn more
</Button>
</HorizontalGroup>
<Badge
icon="lock"
role="img"
aria-label="lock icon"
text="Enterprise"
color="blue"
className={customBadgeStyles}
title="Requires a Grafana Enterprise license"
/>
</Stack>
);
}
@@ -151,6 +151,8 @@ export const getStyles = (theme: GrafanaTheme2) => {
fontSize: theme.typography.h4.fontSize,
color: theme.colors.text.primary,
margin: 0,
wordBreak: 'normal',
overflowWrap: 'anywhere',
}),
};
};
@@ -60,12 +60,11 @@ describe('PluginListItemBadges', () => {
expect(screen.queryByRole('button', { name: /learn more/i })).not.toBeInTheDocument();
});
it('renders an enterprise badge with icon and link (when a license is invalid)', () => {
it('renders an enterprise badge with icon (when a license is invalid)', () => {
config.licenseInfo.enabledFeatures = {};
render(<PluginListItemBadges plugin={{ ...plugin, isEnterprise: true }} />);
expect(screen.getByText(/enterprise/i)).toBeVisible();
expect(screen.getByLabelText(/lock icon/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /learn more/i })).toBeInTheDocument();
});
it('renders a error badge (when plugin has an error)', () => {
@@ -45,20 +45,22 @@ const renderBrowse = (
describe('Browse list of plugins', () => {
describe('when filtering', () => {
it('should list installed plugins by default', async () => {
it('should list all plugins (including core plugins) by default', async () => {
const { queryByText } = renderBrowse('/plugins', [
getCatalogPluginMock({ id: 'plugin-1', name: 'Plugin 1', isInstalled: true }),
getCatalogPluginMock({ id: 'plugin-2', name: 'Plugin 2', isInstalled: true }),
getCatalogPluginMock({ id: 'plugin-3', name: 'Plugin 3', isInstalled: true }),
getCatalogPluginMock({ id: 'plugin-4', name: 'Plugin 4', isInstalled: false }),
getCatalogPluginMock({ id: 'plugin-3', name: 'Plugin 3', isInstalled: false }),
getCatalogPluginMock({ id: 'plugin-4', name: 'Plugin 4', isInstalled: true, isCore: true }),
]);
await waitFor(() => expect(queryByText('Plugin 1')).toBeInTheDocument());
expect(queryByText('Plugin 1')).toBeInTheDocument();
expect(queryByText('Plugin 2')).toBeInTheDocument();
// Plugins which are not installed should still be listed
expect(queryByText('Plugin 3')).toBeInTheDocument();
expect(queryByText('Plugin 4')).toBeNull();
// Core plugins should still be listed
expect(queryByText('Plugin 4')).toBeInTheDocument();
});
it('should list all plugins (including core plugins) when filtering by all', async () => {
@@ -28,7 +28,7 @@ export default function Browse({ route }: GrafanaRouteComponentProps): ReactElem
const history = useHistory();
const remotePluginsAvailable = useIsRemotePluginsAvailable();
const keyword = locationSearch.q?.toString() || '';
const filterBy = locationSearch.filterBy?.toString() || 'installed';
const filterBy = locationSearch.filterBy?.toString() || 'all';
const filterByType = (locationSearch.filterByType as PluginType | 'all') || 'all';
const sortBy = (locationSearch.sortBy as Sorters) || Sorters.nameAsc;
const { isLoading, error, plugins } = useGetAll(