diff --git a/.tmp/proof/issue-130-empty-filters.png b/.tmp/proof/issue-130-empty-filters.png
new file mode 100644
index 0000000..a329aaa
Binary files /dev/null and b/.tmp/proof/issue-130-empty-filters.png differ
diff --git a/.tmp/proof/issue-130-mobile-filters.png b/.tmp/proof/issue-130-mobile-filters.png
new file mode 100644
index 0000000..7271c24
Binary files /dev/null and b/.tmp/proof/issue-130-mobile-filters.png differ
diff --git a/.tmp/proof/issue-130-restored-filters.png b/.tmp/proof/issue-130-restored-filters.png
new file mode 100644
index 0000000..efb3909
Binary files /dev/null and b/.tmp/proof/issue-130-restored-filters.png differ
diff --git a/packages/app/src/components/showcase/ShowcasePage.test.tsx b/packages/app/src/components/showcase/ShowcasePage.test.tsx
index b032828..ad149f6 100644
--- a/packages/app/src/components/showcase/ShowcasePage.test.tsx
+++ b/packages/app/src/components/showcase/ShowcasePage.test.tsx
@@ -4,6 +4,66 @@ import { ShowcasePage } from './ShowcasePage';
import { showcaseItems, countByCategory } from './showcaseConfig';
describe('ShowcasePage', () => {
+ it('labels search and exposes the active category and result count', async () => {
+ await renderInTestApp();
+
+ expect(
+ screen.getByRole('textbox', { name: 'Search showcase' }),
+ ).toBeInTheDocument();
+ const categories = within(
+ screen.getByRole('group', { name: 'Showcase categories' }),
+ );
+ expect(
+ categories.getByRole('button', { name: /^All \(/, pressed: true }),
+ ).toBeInTheDocument();
+
+ fireEvent.click(categories.getByRole('button', { name: /^GitHub \(/ }));
+
+ expect(
+ categories.getByRole('button', { name: /^GitHub \(/, pressed: true }),
+ ).toBeInTheDocument();
+ expect(
+ categories.getByRole('button', { name: /^All \(/, pressed: false }),
+ ).toBeInTheDocument();
+ expect(screen.getByRole('status')).toHaveTextContent('3 items available');
+ });
+
+ it('combines search and category filters and restores all cards from an empty result', async () => {
+ await renderInTestApp();
+ const search = screen.getByRole('textbox', { name: 'Search showcase' });
+
+ fireEvent.change(search, { target: { value: 'mcp' } });
+ expect(screen.getByRole('status')).toHaveTextContent('1 item available');
+ expect(
+ screen.getByTestId('showcase-card-template-mcp-server'),
+ ).toBeInTheDocument();
+
+ fireEvent.click(screen.getByRole('button', { name: /^GitHub \(/ }));
+
+ expect(screen.getByRole('status')).toHaveTextContent('0 items available');
+ expect(screen.getByText(/No showcase items found/)).toHaveTextContent(
+ 'in GitHub',
+ );
+ expect(screen.queryAllByTestId(/^showcase-card-/)).toHaveLength(0);
+
+ fireEvent.click(screen.getByRole('button', { name: 'Clear filters' }));
+
+ expect(search).toHaveValue('');
+ expect(search).toHaveFocus();
+ expect(
+ screen.getByRole('button', { name: /^All \(/, pressed: true }),
+ ).toBeInTheDocument();
+ expect(screen.getByRole('status')).toHaveTextContent(
+ `${showcaseItems.length} items available`,
+ );
+ expect(screen.getAllByTestId(/^showcase-card-/)).toHaveLength(
+ showcaseItems.length,
+ );
+ expect(
+ screen.queryByRole('button', { name: 'Clear filters' }),
+ ).not.toBeInTheDocument();
+ });
+
it('distinguishes live keyword search from the answer-engine extension', async () => {
await renderInTestApp();
@@ -28,7 +88,9 @@ describe('ShowcasePage', () => {
expect(
extension.getByText(/your own document index and AI Hub/),
).toBeInTheDocument();
- expect(extension.getByRole('link', { name: 'Use template' })).toHaveAttribute(
+ expect(
+ extension.getByRole('link', { name: 'Use template' }),
+ ).toHaveAttribute(
'href',
'/self-service/templates/default/answer-engine-worker',
);
@@ -71,7 +133,9 @@ describe('ShowcasePage', () => {
).not.toBeNull();
});
expect(
- document.querySelector('[data-testid="showcase-card-template-mcp-server"]'),
+ document.querySelector(
+ '[data-testid="showcase-card-template-mcp-server"]',
+ ),
).toBeNull();
});
@@ -83,7 +147,9 @@ describe('ShowcasePage', () => {
});
expect(
- document.querySelector('[data-testid="showcase-card-template-mcp-server"]'),
+ document.querySelector(
+ '[data-testid="showcase-card-template-mcp-server"]',
+ ),
).not.toBeNull();
expect(
document.querySelector(
diff --git a/packages/app/src/components/showcase/ShowcasePage.tsx b/packages/app/src/components/showcase/ShowcasePage.tsx
index c2e60bf..6451a65 100644
--- a/packages/app/src/components/showcase/ShowcasePage.tsx
+++ b/packages/app/src/components/showcase/ShowcasePage.tsx
@@ -1,4 +1,4 @@
-import { useMemo, useState } from 'react';
+import { useMemo, useRef, useState } from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import TextField from '@mui/material/TextField';
@@ -6,6 +6,7 @@ import InputAdornment from '@mui/material/InputAdornment';
import Chip from '@mui/material/Chip';
import Grid from '@mui/material/Grid';
import Alert from '@mui/material/Alert';
+import Button from '@mui/material/Button';
import { Page, Header, Content } from '@backstage/core-components';
import SearchIcon from '@mui/icons-material/Search';
import {
@@ -25,6 +26,7 @@ const categories: ShowcaseCategory[] = [
];
export const ShowcasePage = () => {
+ const searchInputRef = useRef(null);
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState<
ShowcaseCategory | 'all'
@@ -57,8 +59,8 @@ export const ShowcasePage = () => {
Catalog and TechDocs keyword search runs in this portal. Generated
answers are an extension you can build with the Answer Engine Worker
- template, your own document index and AI Hub. The demo does not generate
- answers.
+ template, your own document index and AI Hub. The demo does not
+ generate answers.
{
}}
>
setSearchTerm(e.target.value)}
size="small"
- sx={{ minWidth: 300, maxWidth: 400 }}
+ sx={{ width: '100%', maxWidth: 400 }}
InputProps={{
startAdornment: (
@@ -84,14 +88,19 @@ export const ShowcasePage = () => {
),
}}
/>
-
+
{filtered.length} item{filtered.length !== 1 ? 's' : ''} available
-
+
setSelectedCategory('all')}
variant={selectedCategory === 'all' ? 'filled' : 'outlined'}
color={selectedCategory === 'all' ? 'primary' : 'default'}
@@ -101,6 +110,7 @@ export const ShowcasePage = () => {
setSelectedCategory(category)}
variant={selectedCategory === category ? 'filled' : 'outlined'}
color={selectedCategory === category ? 'primary' : 'default'}
@@ -126,6 +136,17 @@ export const ShowcasePage = () => {
{selectedCategory !== 'all' &&
` in ${showcaseCategoryLabels[selectedCategory]}`}
+
)}