diff --git a/apps/web/src/__tests__/datahub-table-controls.test.tsx b/apps/web/src/__tests__/datahub-table-controls.test.tsx new file mode 100644 index 000000000..5f96647d4 --- /dev/null +++ b/apps/web/src/__tests__/datahub-table-controls.test.tsx @@ -0,0 +1,40 @@ +import { cleanup, render, screen } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { Route } from '@/routes/_app/datahub/index'; + +import '@/services/i18n'; + +vi.mock('@tanstack/react-router', async (importOriginal) => ({ + ...(await importOriginal()), + useNavigate: () => vi.fn() +})); +vi.mock('@/hooks/useSubjectsQuery', () => ({ + subjectsQueryOptions: vi.fn(), + useSubjectsQuery: () => ({ data: [] }) +})); +vi.mock('@/store', () => ({ + useAppStore: (selector: (store: { currentGroup: null; currentUser: null }) => unknown) => + selector({ currentGroup: null, currentUser: null }) +})); + +describe('data hub table controls', () => { + beforeEach(() => { + // The toggles call `Route.useNavigate()`, which warns outside a RouterProvider; no navigation happens here. + vi.spyOn(console, 'warn').mockImplementation(() => undefined); + }); + + afterEach(() => { + cleanup(); + vi.restoreAllMocks(); + }); + + // happy-dom computes no layout, so the wrapping is asserted through its utility classes; + // testing/src/specs/datahub.spec.ts measures the rendered result at phone width. + it('should let the controls wrap below the md breakpoint, so a phone never pushes Export off screen', () => { + const Component = Route.options.component!; + render(); + const controls = screen.getByTestId('subject-lookup-search-button').parentElement!; + expect([...controls.classList]).toEqual(expect.arrayContaining(['flex-wrap', 'md:flex-nowrap'])); + }); +}); diff --git a/apps/web/src/components/InstrumentShowcase/InstrumentShowcase.tsx b/apps/web/src/components/InstrumentShowcase/InstrumentShowcase.tsx index 5e24adc2a..df5ce225b 100644 --- a/apps/web/src/components/InstrumentShowcase/InstrumentShowcase.tsx +++ b/apps/web/src/components/InstrumentShowcase/InstrumentShowcase.tsx @@ -99,18 +99,18 @@ export const InstrumentShowcase: React.FC<{ tabIndex={0} onKeyDown={handleKeyDown} > -
+
-
-
+
+
-
+
-
+
diff --git a/apps/web/src/components/InstrumentShowcase/__tests__/InstrumentShowcase.test.tsx b/apps/web/src/components/InstrumentShowcase/__tests__/InstrumentShowcase.test.tsx index 794169635..7d3a9d759 100644 --- a/apps/web/src/components/InstrumentShowcase/__tests__/InstrumentShowcase.test.tsx +++ b/apps/web/src/components/InstrumentShowcase/__tests__/InstrumentShowcase.test.tsx @@ -17,6 +17,15 @@ describe('InstrumentShowcase', () => { expect(fireEvent.keyDown(searchBar, { key: 'Enter' })).toBe(false); }); + // happy-dom computes no layout, so the responsive stacking is asserted through its utility classes; + // testing/src/specs/accessible-instruments.spec.ts measures the rendered result at phone width. + it('should stack the search bar above the filters below the lg breakpoint, so a phone gives it the full width', () => { + render(); + const searchBar = screen.getByTestId('instrument-search-bar'); + expect([...searchBar.classList]).toContain('w-full'); + expect([...searchBar.parentElement!.classList]).toEqual(expect.arrayContaining(['flex-col', 'lg:flex-row'])); + }); + it('should not select anything when Enter is pressed with no matching instruments', () => { const onSelect = vi.fn(); render(); diff --git a/apps/web/src/routes/_app/datahub/index.tsx b/apps/web/src/routes/_app/datahub/index.tsx index 4b2b785c1..186af5d93 100644 --- a/apps/web/src/routes/_app/datahub/index.tsx +++ b/apps/web/src/routes/_app/datahub/index.tsx @@ -61,7 +61,7 @@ const Filters: React.FC<{