Skip to content

Commit

Permalink
Merge pull request #13501 from strapi/providers/search
Browse files Browse the repository at this point in the history
Add search to marketplace providers
  • Loading branch information
remidej committed Jun 13, 2022
2 parents 1b93931 + cf22c0f commit c8cf579
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 87 deletions.
Expand Up @@ -9,7 +9,7 @@ const EmptyPluginCard = styled(Box)`
opacity: 0.33;
`;

export const EmptyPluginGrid = () => {
export const EmptyNpmPackageGrid = () => {
return (
<GridLayout>
{Array(12)
Expand Down
Expand Up @@ -5,12 +5,12 @@ import { Box } from '@strapi/design-system/Box';
import { Flex } from '@strapi/design-system/Flex';
import { Icon } from '@strapi/design-system/Icon';
import EmptyStateDocument from '@strapi/icons/EmptyDocuments';
import { EmptyPluginGrid } from './EmptyPluginGrid';
import { EmptyNpmPackageGrid } from './EmptyNpmPackageGrid';

export const EmptyPluginSearch = ({ content }) => {
const EmptyNpmPackageSearch = ({ content }) => {
return (
<Box position="relative">
<EmptyPluginGrid />
<EmptyNpmPackageGrid />
<Box position="absolute" top={11} width="100%">
<Flex alignItems="center" justifyContent="center" direction="column">
<Icon as={EmptyStateDocument} color="" width="160px" height="88px" />
Expand All @@ -25,6 +25,8 @@ export const EmptyPluginSearch = ({ content }) => {
);
};

EmptyPluginSearch.propTypes = {
EmptyNpmPackageSearch.propTypes = {
content: PropTypes.string.isRequired,
};

export default EmptyNpmPackageSearch;
139 changes: 77 additions & 62 deletions packages/core/admin/admin/src/pages/MarketplacePage/index.js
Expand Up @@ -21,7 +21,7 @@ import { Typography } from '@strapi/design-system/Typography';
import { Flex } from '@strapi/design-system/Flex';
import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs';

import { EmptyPluginSearch } from './components/EmptyPluginSearch';
import EmptyNpmPackageSearch from './components/EmptyNpmPackageSearch';
import PageHeader from './components/PageHeader';
import { fetchAppInformation } from './utils/api';
import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
Expand All @@ -33,8 +33,8 @@ import useNavigatorOnLine from '../../hooks/useNavigatorOnLine';
import MissingPluginBanner from './components/MissingPluginBanner';
import NpmPackagesGrid from './components/NpmPackagesGrid';

const matchSearch = (plugins, search) => {
return matchSorter(plugins, search, {
const matchSearch = (npmPackages, search) => {
return matchSorter(npmPackages, search, {
keys: [
{
threshold: matchSorter.rankings.WORD_STARTS_WITH,
Expand Down Expand Up @@ -74,14 +74,20 @@ const MarketPlacePage = () => {
);
};

const { status: marketplacePluginsStatus, data: marketplacePluginsResponse } =
useFetchMarketplacePlugins(notifyMarketplaceLoad);
const {
status: marketplacePluginsStatus,
data: marketplacePluginsResponse,
} = useFetchMarketplacePlugins(notifyMarketplaceLoad);

const { status: marketplaceProvidersStatus, data: marketplaceProvidersResponse } =
useFetchMarketplaceProviders(notifyMarketplaceLoad);
const {
status: marketplaceProvidersStatus,
data: marketplaceProvidersResponse,
} = useFetchMarketplaceProviders(notifyMarketplaceLoad);

const { status: installedPluginsStatus, data: installedPluginsResponse } =
useFetchInstalledPlugins();
const {
status: installedPluginsStatus,
data: installedPluginsResponse,
} = useFetchInstalledPlugins();

const { data: appInfoResponse, status: appInfoStatus } = useQuery(
'app-information',
Expand Down Expand Up @@ -185,8 +191,19 @@ const MarketPlacePage = () => {
);
}

const searchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
const installedPluginNames = installedPluginsResponse.plugins.map((plugin) => plugin.packageName);
// Search for plugins and providers that match the search query
const pluginSearchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
const providerSearchResults = matchSearch(marketplaceProvidersResponse.data, searchQuery);
const emptySearchMessage = formatMessage(
{
id: 'admin.pages.MarketPlacePage.search.empty',
defaultMessage: 'No result for "{target}"',
},
{ target: searchQuery }
);

// Check if plugins are installed already
const installedPluginNames = installedPluginsResponse.plugins.map(plugin => plugin.packageName);

return (
<Layout>
Expand All @@ -204,80 +221,78 @@ const MarketPlacePage = () => {
name="searchbar"
onClear={() => setSearchQuery('')}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onChange={e => setSearchQuery(e.target.value)}
clearLabel={formatMessage({
id: 'admin.pages.MarketPlacePage.search.clear',
defaultMessage: 'Clear the plugin search',
defaultMessage: 'Clear the search',
})}
placeholder={formatMessage({
id: 'admin.pages.MarketPlacePage.search.placeholder',
defaultMessage: 'Search for a plugin',
defaultMessage: 'Search',
})}
>
{formatMessage({
id: 'admin.pages.MarketPlacePage.search.placeholder',
defaultMessage: 'Search for a plugin',
defaultMessage: 'Search',
})}
</Searchbar>
</Box>
{searchQuery.length > 0 && !searchResults.length ? (
<EmptyPluginSearch
content={formatMessage(
{
id: 'admin.pages.MarketPlacePage.search.empty',
defaultMessage: 'No result for "{target}"',
},
{ target: searchQuery }
)}
/>
) : (
<TabGroup
label={formatMessage({
id: 'admin.pages.MarketPlacePage.tab-group.label',
defaultMessage: 'Plugins and Providers for Strapi',
})}
id="tabs"
variant="simple"
>
<Box paddingBottom={4}>
<Tabs>
<Tab>
{formatMessage({
id: 'admin.pages.MarketPlacePage.plugins',
defaultMessage: 'Plugins',
})}{' '}
({searchResults.length})
</Tab>
<Tab>
{formatMessage({
id: 'admin.pages.MarketPlacePage.providers',
defaultMessage: 'Providers',
})}{' '}
({marketplaceProvidersResponse.data.length})
</Tab>
</Tabs>
</Box>
<TabPanels>
<TabPanel>
<TabGroup
label={formatMessage({
id: 'admin.pages.MarketPlacePage.tab-group.label',
defaultMessage: 'Plugins and Providers for Strapi',
})}
id="tabs"
variant="simple"
>
<Box paddingBottom={4}>
<Tabs>
<Tab>
{formatMessage({
id: 'admin.pages.MarketPlacePage.plugins',
defaultMessage: 'Plugins',
})}{' '}
({pluginSearchResults.length})
</Tab>
<Tab>
{formatMessage({
id: 'admin.pages.MarketPlacePage.providers',
defaultMessage: 'Providers',
})}{' '}
({providerSearchResults.length})
</Tab>
</Tabs>
</Box>
<TabPanels>
{/* Plugins panel */}
<TabPanel>
{searchQuery.length > 0 && !pluginSearchResults.length ? (
<EmptyNpmPackageSearch content={emptySearchMessage} />
) : (
<NpmPackagesGrid
npmPackages={searchResults}
npmPackages={pluginSearchResults}
installedPackageNames={installedPluginNames}
useYarn={appInfoResponse.data.useYarn}
isInDevelopmentMode={isInDevelopmentMode}
npmPackageType="plugin"
/>
</TabPanel>
<TabPanel>
)}
</TabPanel>
{/* Providers panel */}
<TabPanel>
{searchQuery.length > 0 && !providerSearchResults.length ? (
<EmptyNpmPackageSearch content={emptySearchMessage} />
) : (
<NpmPackagesGrid
npmPackages={marketplaceProvidersResponse.data}
npmPackages={providerSearchResults}
useYarn={appInfoResponse.data.useYarn}
isInDevelopmentMode={isInDevelopmentMode}
npmPackageType="provider"
/>
</TabPanel>
</TabPanels>
</TabGroup>
)}
)}
</TabPanel>
</TabPanels>
</TabGroup>
<Box paddingTop={7}>
<MissingPluginBanner />
</Box>
Expand Down
Expand Up @@ -954,7 +954,7 @@ describe('Marketplace page', () => {
<div
class="c21"
>
Search for a plugin
Search
</div>
</label>
</div>
Expand Down Expand Up @@ -990,7 +990,7 @@ describe('Marketplace page', () => {
class="c28"
id="field-1"
name="searchbar"
placeholder="Search for a plugin"
placeholder="Search"
value=""
/>
</div>
Expand Down Expand Up @@ -1760,20 +1760,50 @@ describe('Marketplace page', () => {
expect(trackUsage).toHaveBeenCalledTimes(1);
});

it('should return search results matching the query', async () => {
it('should return plugin search results matching the query', async () => {
const { container } = render(App);
const input = await getByPlaceholderText(container, 'Search for a plugin');
const input = await getByPlaceholderText(container, 'Search');
fireEvent.change(input, { target: { value: 'comment' } });
const match = screen.getByText('Comments');
const notMatch = screen.queryByText('Sentry');
const provider = screen.queryByText('Cloudinary');

expect(match).toBeVisible();
expect(notMatch).toEqual(null);
expect(provider).toEqual(null);
});

it('should return empty search results given a bad query', async () => {
it('should return provider search results matching the query', async () => {
const { container } = render(App);
const input = await getByPlaceholderText(container, 'Search for a plugin');
const providersTab = screen.getByRole('tab', { selected: false });
fireEvent.click(providersTab);

const input = await getByPlaceholderText(container, 'Search');
fireEvent.change(input, { target: { value: 'cloudina' } });
const match = screen.getByText('Cloudinary');
const notMatch = screen.queryByText('Mailgun');
const plugin = screen.queryByText('Comments');

expect(match).toBeVisible();
expect(notMatch).toEqual(null);
expect(plugin).toEqual(null);
});

it('should return empty plugin search results given a bad query', async () => {
const { container } = render(App);
const input = await getByPlaceholderText(container, 'Search');
const badQuery = 'asdf';
fireEvent.change(input, { target: { value: badQuery } });
const noResult = screen.getByText(`No result for "${badQuery}"`);

expect(noResult).toBeVisible();
});

it('should return empty provider search results given a bad query', async () => {
const { container } = render(App);
const providersTab = screen.getByRole('tab', { selected: false });
fireEvent.click(providersTab);
const input = await getByPlaceholderText(container, 'Search');
const badQuery = 'asdf';
fireEvent.change(input, { target: { value: badQuery } });
const noResult = screen.getByText(`No result for "${badQuery}"`);
Expand Down
4 changes: 2 additions & 2 deletions packages/core/admin/admin/src/translations/en.json
Expand Up @@ -217,9 +217,9 @@
"admin.pages.MarketPlacePage.plugin.tooltip.madeByStrapi": "Made by Strapi",
"admin.pages.MarketPlacePage.plugin.tooltip.verified": "Plugin verified by Strapi",
"admin.pages.MarketPlacePage.providers": "Providers",
"admin.pages.MarketPlacePage.search.clear": "Clear the plugin search",
"admin.pages.MarketPlacePage.search.clear": "Clear the search",
"admin.pages.MarketPlacePage.search.empty": "No result for \"{target}\"",
"admin.pages.MarketPlacePage.search.placeholder": "Search for a plugin",
"admin.pages.MarketPlacePage.search.placeholder": "Search",
"admin.pages.MarketPlacePage.submit.plugin.link": "Submit your plugin",
"admin.pages.MarketPlacePage.subtitle": "Get more out of Strapi",
"admin.pages.MarketPlacePage.tab-group.label": "Plugins and Providers for Strapi",
Expand Down

0 comments on commit c8cf579

Please sign in to comment.