Documentation
Tell us what plugin you are looking for and we'll let our community plugin developers know in case they are in search for inspiration!
diff --git a/packages/core/admin/admin/src/pages/MarketplacePage/tests/index.test.js b/packages/core/admin/admin/src/pages/MarketplacePage/tests/index.test.js
index 99413b5ba5..8e2bba5753 100644
--- a/packages/core/admin/admin/src/pages/MarketplacePage/tests/index.test.js
+++ b/packages/core/admin/admin/src/pages/MarketplacePage/tests/index.test.js
@@ -11,6 +11,8 @@ import {
} from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import { QueryClient, QueryClientProvider } from 'react-query';
+import { Router } from 'react-router-dom';
+import { createMemoryHistory } from 'history';
import { ThemeProvider, lightTheme } from '@strapi/design-system';
import { useTracking, useAppInfos } from '@strapi/helper-plugin';
import useNavigatorOnLine from '../../../hooks/useNavigatorOnLine';
@@ -47,11 +49,15 @@ const client = new QueryClient({
},
});
+const history = createMemoryHistory();
+
const App = (
-
+
+
+
@@ -65,19 +71,47 @@ describe('Marketplace page', () => {
afterAll(() => server.close());
it('renders and matches the plugin tab snapshot', async () => {
+ // Check snapshot
const { container, getByTestId, getByRole } = render(App);
await waitForElementToBeRemoved(() => getByTestId('loader'));
await waitFor(() => expect(getByRole('heading', { name: /marketplace/i })).toBeInTheDocument());
expect(container.firstChild).toMatchSnapshot();
+
+ // Make sure it defaults to the plugins tab
+ const button = screen.getByRole('tab', { selected: true });
+ const pluginsTabActive = getByText(button, /plugins/i);
+
+ const tabPanel = screen.getByRole('tabpanel');
+ const pluginCardText = getByText(tabPanel, 'Comments');
+ const providerCardText = queryByText(tabPanel, 'Cloudinary');
+ const submitPluginText = queryByText(container, 'Submit plugin');
+
+ expect(pluginsTabActive).not.toBe(null);
+ expect(pluginCardText).toBeVisible();
+ expect(submitPluginText).toBeVisible();
+ expect(providerCardText).toEqual(null);
});
- it('renders and matches the provider tab snapshot', async () => {
+ it('renders and matches the provider tab snapshot', () => {
+ // Make sure it switches to the providers tab
const { container, getByRole } = render(App);
- await waitFor(() => expect(getByRole('heading', { name: /marketplace/i })).toBeInTheDocument());
- const providersTab = screen.getByRole('tab', { selected: false });
+ const providersTab = getByRole('tab', { name: /providers/i });
fireEvent.click(providersTab);
+ const button = getByRole('tab', { selected: true });
+ const providersTabActive = getByText(button, /Providers/i);
+ const tabPanel = getByRole('tabpanel');
+ const providerCardText = getByText(tabPanel, 'Cloudinary');
+ const pluginCardText = queryByText(tabPanel, 'Comments');
+ const submitProviderText = queryByText(container, 'Submit provider');
+
+ expect(providersTabActive).not.toBe(null);
+ expect(providerCardText).toBeVisible();
+ expect(submitProviderText).toBeVisible();
+ expect(pluginCardText).toEqual(null);
+
+ // Check snapshot
expect(container.firstChild).toMatchSnapshot();
});
@@ -90,9 +124,12 @@ describe('Marketplace page', () => {
expect(trackUsage).toHaveBeenCalledTimes(1);
});
- it('should return plugin search results matching the query', async () => {
+ it('should return plugin search results matching the query', () => {
const { container } = render(App);
- const input = await getByPlaceholderText(container, 'Search');
+ const pluginsTab = screen.getByRole('tab', { name: /plugins/i });
+ fireEvent.click(pluginsTab);
+
+ const input = getByPlaceholderText(container, 'Search');
fireEvent.change(input, { target: { value: 'comment' } });
const match = screen.getByText('Comments');
const notMatch = screen.queryByText('Sentry');
@@ -103,12 +140,12 @@ describe('Marketplace page', () => {
expect(provider).toEqual(null);
});
- it('should return provider search results matching the query', async () => {
+ it('should return provider search results matching the query', () => {
const { container } = render(App);
- const providersTab = screen.getByRole('tab', { selected: false });
+ const providersTab = screen.getByRole('tab', { name: /providers/i });
fireEvent.click(providersTab);
- const input = await getByPlaceholderText(container, 'Search');
+ const input = getByPlaceholderText(container, 'Search');
fireEvent.change(input, { target: { value: 'cloudina' } });
const match = screen.getByText('Cloudinary');
const notMatch = screen.queryByText('Mailgun');
@@ -119,9 +156,9 @@ describe('Marketplace page', () => {
expect(plugin).toEqual(null);
});
- it('should return empty plugin search results given a bad query', async () => {
+ it('should return empty plugin search results given a bad query', () => {
const { container } = render(App);
- const input = await getByPlaceholderText(container, 'Search');
+ const input = getByPlaceholderText(container, 'Search');
const badQuery = 'asdf';
fireEvent.change(input, { target: { value: badQuery } });
const noResult = screen.getByText(`No result for "${badQuery}"`);
@@ -129,11 +166,11 @@ describe('Marketplace page', () => {
expect(noResult).toBeVisible();
});
- it('should return empty provider search results given a bad query', async () => {
+ it('should return empty provider search results given a bad query', () => {
const { container } = render(App);
- const providersTab = screen.getByRole('tab', { selected: false });
+ const providersTab = screen.getByRole('tab', { name: /providers/i });
fireEvent.click(providersTab);
- const input = await getByPlaceholderText(container, 'Search');
+ const input = getByPlaceholderText(container, 'Search');
const badQuery = 'asdf';
fireEvent.change(input, { target: { value: badQuery } });
const noResult = screen.getByText(`No result for "${badQuery}"`);
@@ -141,6 +178,25 @@ describe('Marketplace page', () => {
expect(noResult).toBeVisible();
});
+ it('shows filters popover on plugins and providers', () => {
+ render(App);
+
+ // Show collections and categories filters on plugins
+ const pluginsTab = screen.getByRole('tab', { name: /plugins/i });
+ fireEvent.click(pluginsTab);
+ const filtersButton = screen.getByRole('button', { name: /filters/i });
+ fireEvent.click(filtersButton);
+ screen.getByLabelText(/no collections selected/i);
+ screen.getByLabelText(/no categories selected/i);
+ fireEvent.click(filtersButton);
+
+ // Only show collections filters on providers
+ const providersTab = screen.getByRole('tab', { name: /providers/i });
+ fireEvent.click(providersTab);
+ fireEvent.click(filtersButton);
+ screen.getByLabelText(/no collections selected/i);
+ });
+
it('handles production environment', () => {
// Simulate production environment
useAppInfos.mockImplementationOnce(() => ({
@@ -181,41 +237,7 @@ describe('Marketplace page', () => {
expect(offlineText).toBeVisible();
});
- it('defaults to plugins tab', async () => {
- const { container } = render(App);
- const button = screen.getByRole('tab', { selected: true });
- const pluginsTabActive = await getByText(button, /Plugins/i);
-
- const tabPanel = screen.getByRole('tabpanel');
- const pluginCardText = await getByText(tabPanel, 'Comments');
- const providerCardText = await queryByText(tabPanel, 'Cloudinary');
- const submitPluginText = await queryByText(container, 'Submit plugin');
-
- expect(pluginsTabActive).not.toBe(null);
- expect(pluginCardText).toBeVisible();
- expect(submitPluginText).toBeVisible();
- expect(providerCardText).toEqual(null);
- });
-
- it('switches to providers tab', async () => {
- const { container } = render(App);
- const providersTab = screen.getByRole('tab', { selected: false });
- fireEvent.click(providersTab);
- const button = screen.getByRole('tab', { selected: true });
- const providersTabActive = await getByText(button, /Providers/i);
-
- const tabPanel = screen.getByRole('tabpanel');
- const providerCardText = await getByText(tabPanel, 'Cloudinary');
- const pluginCardText = await queryByText(tabPanel, 'Comments');
- const submitProviderText = await queryByText(container, 'Submit provider');
-
- expect(providersTabActive).not.toBe(null);
- expect(providerCardText).toBeVisible();
- expect(submitProviderText).toBeVisible();
- expect(pluginCardText).toEqual(null);
- });
-
- it('shows the installed text for installed plugins', async () => {
+ it('shows the installed text for installed plugins', () => {
render(App);
const pluginsTab = screen.getByRole('tab', { name: /plugins/i });
fireEvent.click(pluginsTab);
@@ -235,7 +257,7 @@ describe('Marketplace page', () => {
expect(notInstalledText).toBeVisible();
});
- it('shows the installed text for installed providers', async () => {
+ it('shows the installed text for installed providers', () => {
// Open providers tab
render(App);
const providersTab = screen.getByRole('tab', { name: /providers/i });
diff --git a/packages/core/admin/admin/src/pages/MarketplacePage/tests/server.js b/packages/core/admin/admin/src/pages/MarketplacePage/tests/server.js
index d5f59d1eec..b3bc187a2a 100644
--- a/packages/core/admin/admin/src/pages/MarketplacePage/tests/server.js
+++ b/packages/core/admin/admin/src/pages/MarketplacePage/tests/server.js
@@ -491,6 +491,19 @@ const handlers = [
},
},
],
+ meta: {
+ collections: {
+ 'Made by official partners': 9,
+ 'Made by Strapi': 13,
+ 'Made by the community': 69,
+ Verified: 29,
+ },
+ categories: {
+ 'Custom fields': 4,
+ Deployment: 2,
+ Monitoring: 1,
+ },
+ },
})
);
}),
@@ -903,6 +916,14 @@ const handlers = [
},
},
],
+ meta: {
+ collections: {
+ 'Made by official partners': 0,
+ 'Made by Strapi': 6,
+ 'Made by the community': 2,
+ Verified: 6,
+ },
+ },
})
);
}),
diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json
index 52320dfbe3..e0c7f58b08 100644
--- a/packages/core/admin/admin/src/translations/en.json
+++ b/packages/core/admin/admin/src/translations/en.json
@@ -100,11 +100,11 @@
"Settings.apiTokens.duration.30-days": "30 days",
"Settings.apiTokens.duration.90-days": "90 days",
"Settings.apiTokens.duration.unlimited": "Unlimited",
- "Settings.apiTokens.form.duration":"Token duration",
- "Settings.apiTokens.form.type":"Token type",
- "Settings.apiTokens.duration.expiration-date":"Expiration date",
- "Settings.apiTokens.createPage.permissions.title":"Permissions",
- "Settings.apiTokens.createPage.permissions.description":"Only actions bound by a route are listed below.",
+ "Settings.apiTokens.form.duration": "Token duration",
+ "Settings.apiTokens.form.type": "Token type",
+ "Settings.apiTokens.duration.expiration-date": "Expiration date",
+ "Settings.apiTokens.createPage.permissions.title": "Permissions",
+ "Settings.apiTokens.createPage.permissions.description": "Only actions bound by a route are listed below.",
"Settings.apiTokens.RegenerateDialog.title": "Regenerate token",
"Settings.apiTokens.popUpWarning.message": "Are you sure you want to regenerate this token?",
"Settings.apiTokens.Button.cancel": "Cancel",
@@ -280,6 +280,10 @@
"admin.pages.MarketPlacePage.tab-group.label": "Plugins and Providers for Strapi",
"admin.pages.MarketPlacePage.missingPlugin.title": "Missing a plugin?",
"admin.pages.MarketPlacePage.missingPlugin.description": "Tell us what plugin you are looking for and we'll let our community plugin developers know in case they are in search for inspiration!",
+ "admin.pages.MarketPlacePage.filters.collections": "Collections",
+ "admin.pages.MarketPlacePage.filters.collectionsSelected": "{count, plural, =0 {No collections} one {# collection} other {# collections}} selected",
+ "admin.pages.MarketPlacePage.filters.categories": "Categories",
+ "admin.pages.MarketPlacePage.filters.categoriesSelected": "{count, plural, =0 {No categories} one {# category} other {# categories}} selected",
"anErrorOccurred": "Woops! Something went wrong. Please, try again.",
"app.component.CopyToClipboard.label": "Copy to clipboard",
"app.component.search.label": "Search for {target}",