Merge pull request #31113 from drodil/microsite_plugin_search

feat(microsite): add search for plugins
This commit is contained in:
Fredrik Adelöw
2025-09-11 12:04:42 +02:00
committed by GitHub
3 changed files with 88 additions and 29 deletions
@@ -0,0 +1,19 @@
import React from 'react';
type Props = {
searchTerm: string;
onSearchTermChange: (newTerm: string) => void;
};
export const PluginsSearch = (props: Props) => {
const { searchTerm, onSearchTermChange } = props;
return (
<input
name="search"
onInput={e => onSearchTermChange((e.target as HTMLInputElement).value)}
value={searchTerm}
placeholder="Search plugins..."
className="DocSearch-Input search"
/>
);
};
+55 -29
View File
@@ -5,10 +5,11 @@ import { truncateDescription } from '@site/src/util/truncateDescription';
import { ChipCategory } from '@site/src/util/types';
import Layout from '@theme/Layout';
import clsx from 'clsx';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { IPluginData, PluginCard } from './_pluginCard';
import pluginsStyles from './plugins.module.scss';
import { PluginsSearch } from '@site/src/components/pluginsSearch/pluginsSearch';
interface IPluginsList {
corePlugins: IPluginData[];
@@ -55,6 +56,7 @@ const Plugins = () => {
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const [showCoreFeatures, setShowCoreFeatures] = useState(true);
const [showOtherPlugins, setShowOtherPlugins] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const handleChipClick = (categoryName: string) => {
const isSelected =
@@ -97,6 +99,34 @@ const Plugins = () => {
}
};
const matchesSearch = (pluginData: IPluginData, term: string) => {
if (!term) return true;
const lowerTerm = term.toLowerCase();
return (
pluginData.title.toLowerCase().includes(lowerTerm) ||
pluginData.description.toLowerCase().includes(lowerTerm) ||
pluginData.category.toLowerCase().includes(lowerTerm) ||
(pluginData.author && pluginData.author.toLowerCase().includes(lowerTerm))
);
};
const matchesCategory = (pluginData: IPluginData, categories: string[]) => {
if (categories.length === 0) return true;
return categories.includes(pluginData.category);
};
const corePlugins = useMemo(() => {
return plugins.corePlugins
.filter(pluginData => matchesCategory(pluginData, selectedCategories))
.filter(pluginData => matchesSearch(pluginData, searchTerm));
}, [selectedCategories, searchTerm]);
const otherPlugins = useMemo(() => {
return plugins.otherPlugins
.filter(pluginData => matchesCategory(pluginData, selectedCategories))
.filter(pluginData => matchesSearch(pluginData, searchTerm));
}, [selectedCategories, searchTerm]);
return (
<Layout>
<div
@@ -126,31 +156,36 @@ const Plugins = () => {
categories={categories}
handleChipClick={handleChipClick}
/>
<PluginsSearch
searchTerm={searchTerm}
onSearchTermChange={setSearchTerm}
/>
</div>
{showCoreFeatures && (
{corePlugins.length === 0 && otherPlugins.length === 0 && (
<div className="margin-vert--lg">
<h3>No plugins found</h3>
<p>
We couldn't find any plugins matching your criteria. Please try
adjusting your search or filter settings.
</p>
</div>
)}
{showCoreFeatures && corePlugins.length > 0 && (
<div>
<h2>Core Features</h2>
<h2>Core Features ({corePlugins.length})</h2>
<div className="pluginsContainer margin-bottom--lg">
{plugins.corePlugins
.filter(
pluginData =>
!selectedCategories.length ||
selectedCategories.includes(pluginData.category),
)
.map(pluginData => (
<PluginCard
key={pluginData.title}
{...pluginData}
></PluginCard>
))}
{corePlugins.map(pluginData => (
<PluginCard key={pluginData.title} {...pluginData}></PluginCard>
))}
</div>
</div>
)}
{showOtherPlugins && (
{showOtherPlugins && otherPlugins.length > 0 && (
<div>
<h2>All Plugins</h2>
<h2>All Plugins ({otherPlugins.length})</h2>
<p>
Friendly reminder: While we love the variety and contributions of
our open source plugins, they haven't been fully vetted by the
@@ -158,18 +193,9 @@ const Plugins = () => {
your due diligence before installing. Happy exploring!
</p>
<div className="pluginsContainer margin-bottom--lg">
{plugins.otherPlugins
.filter(
pluginData =>
!selectedCategories.length ||
selectedCategories.includes(pluginData.category),
)
.map(pluginData => (
<PluginCard
key={pluginData.title}
{...pluginData}
></PluginCard>
))}
{otherPlugins.map(pluginData => (
<PluginCard key={pluginData.title} {...pluginData}></PluginCard>
))}
</div>
</div>
)}
@@ -76,6 +76,20 @@
height: 1rem;
}
:global(.search) {
float: right;
margin-bottom: 1rem;
margin-right: 1rem;
font-size: calc(0.875rem * var(--ifm-button-size-multiplier));
font-weight: var(--ifm-button-font-weight);
background-color: transparent;
border: var(--ifm-button-border-width) solid var(--ifm-color-primary);
border-radius: var(--ifm-button-border-radius);
color: var(--ifm-color-primary);
width: 220px;
height: 2.2rem;
}
:global(.dropdown) {
float: right;
margin-bottom: 1rem;