Skip to content

Commit 4bf3358

Browse files
katinthehatsiteKateryna Kodonenko
andauthored
Studio: Add search to current library (#3384)
## Related issues <!-- Link a related issue to this PR. If the PR does not immediately resolve the issue, for example, it requires a separate deployment to production, avoid using the "Fixes" keyword and use "Related to" instead. --> Related to STU-1634 ## How AI was used in this PR <!-- Help reviewers understand what to look for and verify that you've reviewed the code yourself. --> It was used to implement the feature. ## Proposed Changes This PR ensures that we add the search feature to the current blueprints library that is pulled from the public Blueprints Library through the API. <img width="894" height="736" alt="Screenshot 2026-05-07 at 4 42 45 PM" src="https://github.com/user-attachments/assets/cb30061c-91ca-47cd-a041-b2c0df609bca" /> ## Testing Instructions <!-- Add as many details as possible to help others reproduce the issue and test the fix. "Before / After" screenshots can also be very helpful when the change is visual. --> * Pull the changes from this branch * Start the app with npm start * Enable the Blueprints Gallery Library feature flag * Click on `Add site > Build new site` * Scroll down to the blueprints library * Confirm that you can use the search box and search by title or category ## Pre-merge Checklist <!-- Complete applicable items on this checklist **before** merging into trunk. Inapplicable items can be left unchecked. Both the PR author and reviewer are responsible for ensuring the checklist is completed. --> - [ ] Have you checked for TypeScript, React or other console errors? Co-authored-by: Kateryna Kodonenko <kateryna@automattic.com>
1 parent eeeb922 commit 4bf3358

2 files changed

Lines changed: 43 additions & 12 deletions

File tree

‎apps/studio/src/modules/add-site/components/new-site-options.tsx‎

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ import {
22
__experimentalVStack as VStack,
33
__experimentalHeading as Heading,
44
__experimentalText as Text,
5+
SearchControl as SearchControlWp,
56
Spinner,
67
} from '@wordpress/components';
78
import { useI18n } from '@wordpress/react-i18n';
8-
import { useCallback } from 'react';
9+
import { useCallback, useMemo, useState } from 'react';
910
import { ArrowIcon } from 'src/components/arrow-icon';
1011
import StudioButton from 'src/components/button';
1112
import { EMPTY_SITE_PLAYGROUND_URL } from 'src/constants';
1213
import { cx } from 'src/lib/cx';
1314
import { getIpcApi } from 'src/lib/get-ipc-api';
1415
import { GalleryBlueprint } from 'src/stores/gallery-blueprints-api';
1516

17+
const SearchControl = process.env.NODE_ENV === 'test' ? () => null : SearchControlWp;
18+
1619
interface Blueprint {
1720
slug: string;
1821
title: string;
@@ -279,6 +282,22 @@ export function NewSiteOptions( {
279282
gallerySelectionError,
280283
}: NewSiteOptionsProps ) {
281284
const { __ } = useI18n();
285+
const [ searchQuery, setSearchQuery ] = useState( '' );
286+
287+
const filteredGalleryBlueprints = useMemo( () => {
288+
const query = searchQuery.toLowerCase().trim();
289+
if ( ! query ) {
290+
return galleryBlueprints;
291+
}
292+
return galleryBlueprints.filter( ( blueprint ) => {
293+
const titleMatch = blueprint.title.toLowerCase().includes( query );
294+
const descriptionMatch = blueprint.description.toLowerCase().includes( query );
295+
const categoryMatch = blueprint.categories.some( ( category ) =>
296+
category.toLowerCase().includes( query )
297+
);
298+
return titleMatch || descriptionMatch || categoryMatch;
299+
} );
300+
}, [ galleryBlueprints, searchQuery ] );
282301

283302
const handleEmptyClick = useCallback( () => {
284303
onBlueprintChange( 'empty' );
@@ -335,12 +354,20 @@ export function NewSiteOptions( {
335354

336355
{ onGalleryBlueprintSelect && (
337356
<>
338-
<Heading
339-
className="text-[18px] text-frame-text mt-8 mb-4 w-full max-w-2xl mx-auto"
340-
weight={ 500 }
341-
>
342-
{ __( 'Explore more blueprints' ) }
343-
</Heading>
357+
<div className="flex items-center justify-between w-full max-w-2xl mx-auto mt-8 mb-4">
358+
<Heading className="text-[18px] text-frame-text" weight={ 500 }>
359+
{ __( 'Explore more blueprints' ) }
360+
</Heading>
361+
{ ! isLoadingGallery && ! galleryErrorMessage && (
362+
<SearchControl
363+
className="!w-48 text-frame-text"
364+
placeholder={ __( 'Search blueprints' ) }
365+
onChange={ setSearchQuery }
366+
value={ searchQuery }
367+
__nextHasNoMarginBottom={ true }
368+
/>
369+
) }
370+
</div>
344371

345372
{ gallerySelectionError && (
346373
<div className="bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 text-sm rounded-lg px-4 py-3 mb-4 max-w-2xl mx-auto w-full">
@@ -356,13 +383,17 @@ export function NewSiteOptions( {
356383
<div className="flex items-center justify-center text-sm text-frame-text-secondary py-8">
357384
{ galleryErrorMessage }
358385
</div>
386+
) : filteredGalleryBlueprints.length === 0 ? (
387+
<div className="flex items-center justify-center text-sm text-frame-text-secondary py-8 max-w-2xl mx-auto">
388+
{ __( 'No blueprints found.' ) }
389+
</div>
359390
) : (
360391
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 w-full max-w-2xl mx-auto pb-1">
361-
{ galleryBlueprints.map( ( bp ) => (
392+
{ filteredGalleryBlueprints.map( ( blueprint ) => (
362393
<GalleryBlueprintCard
363-
key={ bp.slug }
364-
blueprint={ bp }
365-
onClick={ () => onGalleryBlueprintSelect( bp ) }
394+
key={ blueprint.slug }
395+
blueprint={ blueprint }
396+
onClick={ () => onGalleryBlueprintSelect( blueprint ) }
366397
disabled={ isSelectingGalleryBlueprint }
367398
/>
368399
) ) }

‎apps/studio/src/stores/gallery-blueprints-api.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const galleryIndexEntrySchema = z.object( {
2424
description: z.string(),
2525
author: z.string(),
2626
categories: z.array( z.string() ).optional().default( [] ),
27-
screenshot_url: z.string(),
27+
screenshot_url: z.string().optional().default( '' ),
2828
featured: z.boolean(),
2929
} );
3030

0 commit comments

Comments
 (0)