fix(web): isolate home template cards beneath sticky header (#42906)

This commit is contained in:
yyh authored and GitHub committed 2026-09-24 11:54:41 +00:00
1 parent 18e0cb102b
commit a7f890129e
3 files changed
+33 -30

No files matched your search

@@ -35,7 +35,7 @@ async function pngResponse(width: number, height: number) {
// object URL is unusable. These are the additional browser regression contracts.
it('decodes binary assets and retires object URLs on source replacement and unmount', async () => {
request.mockReset()
request.mockImplementation(() => pngResponse(2, 3))
request.mockResolvedValue(await pngResponse(2, 3))
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false, staleTime: 300_000 } },
})
@@ -49,24 +49,22 @@ it('decodes binary assets and retires object URLs on source replacement and unmo
const screen = await render(view('_assets/first.png'))
try {
await expect
.poll(() => screen.getByTestId('gallery-image').element().getAttribute('src'))
.toMatch(/^blob:/)
.element(screen.getByTestId('gallery-image'), { timeout: 5_000 })
.toHaveAttribute('src', expect.stringMatching(/^blob:/))
const firstImage = screen.getByTestId('gallery-image').element()
if (!(firstImage instanceof HTMLImageElement)) throw new TypeError('Expected an image')
await firstImage.decode()
expect([firstImage.naturalWidth, firstImage.naturalHeight]).toEqual([2, 3])
const firstUrl = firstImage.src
expect(revoke).not.toHaveBeenCalled()
request.mockImplementation(() => pngResponse(4, 5))
request.mockResolvedValue(await pngResponse(4, 5))
await screen.rerender(view('_assets/second.png'))
await expect
.poll(() => screen.getByTestId('gallery-image').element().getAttribute('src'))
.not.toBe(firstUrl)
await expect
.poll(() => screen.getByTestId('gallery-image').element().getAttribute('src'))
.toMatch(/^blob:/)
.element(screen.getByTestId('gallery-image'), { timeout: 5_000 })
.toHaveAttribute('src', expect.stringMatching(/^blob:/))
const secondImage = screen.getByTestId('gallery-image').element()
if (!(secondImage instanceof HTMLImageElement)) throw new TypeError('Expected an image')
expect(secondImage.src).not.toBe(firstUrl)
await secondImage.decode()
expect([secondImage.naturalWidth, secondImage.naturalHeight]).toEqual([4, 5])
expect(revoke).toHaveBeenCalledWith(firstUrl)
@@ -115,14 +113,14 @@ it('recovers after a malformed asset causes the real gallery to remove its image
.element(screen.getByRole('button', { name: /^common\.imageGallery\.previewImage/ }))
.not.toBeInTheDocument()
request.mockImplementation(() => pngResponse(6, 7))
request.mockResolvedValue(await pngResponse(6, 7))
await screen.rerender(view('_assets/valid.png'))
await expect
.element(screen.getByRole('button', { name: /^common\.imageGallery\.previewImage/ }))
.toBeVisible()
await expect
.poll(() => screen.getByTestId('gallery-image').element().getAttribute('src'))
.toMatch(/^blob:/)
.element(screen.getByTestId('gallery-image'), { timeout: 5_000 })
.toHaveAttribute('src', expect.stringMatching(/^blob:/))
const recoveredImage = screen.getByTestId('gallery-image').element()
if (!(recoveredImage instanceof HTMLImageElement)) throw new TypeError('Expected an image')
await recoveredImage.decode()
+20 -15
View File
@@ -440,21 +440,26 @@ export function HomeContent() {
/>
<div className={cn('relative flex flex-1 shrink-0 grow flex-col pb-6')}>
<section
aria-labelledby="home-templates-title"
className={cn('shrink-0 content-start gap-2.5 px-8', MAIN_NAV_APP_CARD_GRID_CLASS_NAME)}
>
{searchFilteredList.map((app) => (
<TemplateCard
key={app.app_id}
app={app}
canCreate={canCreateApp}
onCreate={() => handleCreateFromTemplate(app)}
onTry={handleTryApp}
/>
))}
{searchFilteredList.length === 0 && (
<div role="status" className="col-span-full rounded-lg bg-workflow-process-bg p-4">
<section aria-labelledby="home-templates-title" className="shrink-0 px-8">
{searchFilteredList.length > 0 ? (
<ul
// Safari drops list semantics from grid lists after preflight removes markers.
// oxlint-disable-next-line jsx-a11y/no-redundant-roles -- Restore Safari list semantics.
role="list"
className={cn('content-start gap-2.5', MAIN_NAV_APP_CARD_GRID_CLASS_NAME)}
>
{searchFilteredList.map((app) => (
<TemplateCard
key={app.app_id}
app={app}
canCreate={canCreateApp}
onCreate={() => handleCreateFromTemplate(app)}
onTry={handleTryApp}
/>
))}
</ul>
) : (
<div role="status" className="rounded-lg bg-workflow-process-bg p-4">
<p className="title-md-semi-bold text-text-primary">
{t(($) => $['newApp.noTemplateFound'], { ns: 'app' })}
</p>
+3 -3
View File
@@ -56,9 +56,9 @@ export function TemplateCard({ app, canCreate, onCreate, onTry }: TemplateCardPr
}
return (
<div
<li
className={cn(
'group relative col-span-1 flex h-35.5 flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg pb-3 text-left shadow-xs shadow-shadow-shadow-3',
'group relative isolate col-span-1 flex h-35.5 flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg pb-3 text-left shadow-xs shadow-shadow-shadow-3',
isClickable && 'cursor-pointer',
)}
>
@@ -129,6 +129,6 @@ export function TemplateCard({ app, canCreate, onCreate, onTry }: TemplateCardPr
{app.description}
</div>
</div>
</div>
</li>
)
}