fix: correct catalog error contrast and add catalog Storybook coverage
Quality / test (push) Canceled after 0s
Quality / test (push) Canceled after 0s
This commit is contained in:
@@ -1,4 +1,11 @@
|
|||||||
|
import { setup } from '@storybook/vue3';
|
||||||
|
|
||||||
import '../src/assets/styles/global.scss';
|
import '../src/assets/styles/global.scss';
|
||||||
|
import { storybookRouter } from './router.js';
|
||||||
|
|
||||||
|
setup((app) => {
|
||||||
|
app.use(storybookRouter);
|
||||||
|
});
|
||||||
|
|
||||||
export const globalTypes = {
|
export const globalTypes = {
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { createMemoryHistory, createRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const stub = { render: () => null };
|
||||||
|
|
||||||
|
// Mirrors the named routes in src/router/index.js so RouterLink resolution
|
||||||
|
// works in isolated stories without importing the real app router/guards.
|
||||||
|
export const storybookRouter = createRouter({
|
||||||
|
history: createMemoryHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', name: 'catalog', component: stub },
|
||||||
|
{ path: '/listings/:id', name: 'listing-detail', component: stub },
|
||||||
|
{ path: '/products/:slug', name: 'product-detail', component: stub },
|
||||||
|
{ path: '/login', name: 'login', component: stub },
|
||||||
|
{ path: '/admin', name: 'admin-home', component: stub },
|
||||||
|
{ path: '/admin/inventory/:id/images', name: 'admin-image-manager', component: stub },
|
||||||
|
{ path: '/admin/inventory', name: 'admin-inventory', component: stub },
|
||||||
|
{ path: '/admin/listings', name: 'admin-listings', component: stub },
|
||||||
|
{ path: '/admin/sales', name: 'admin-sales', component: stub },
|
||||||
|
{ path: '/admin/acquisitions', name: 'admin-acquisitions', component: stub },
|
||||||
|
{ path: '/admin/settings', name: 'admin-settings', component: stub },
|
||||||
|
{ path: '/admin/saved-views', name: 'admin-saved-views', component: stub },
|
||||||
|
{ path: '/:pathMatch(.*)*', name: 'storybook-catch-all', component: stub },
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Forces a narrow viewport for stories that specifically exercise mobile-only
|
||||||
|
// CSS breakpoints, since @storybook/addon-viewport does not affect the
|
||||||
|
// headless iframe size that @storybook/test-runner navigates to directly.
|
||||||
|
export default {
|
||||||
|
async preVisit(page, context) {
|
||||||
|
if (context.id?.startsWith('pages-catalogpage--mobile')) {
|
||||||
|
await page.setViewportSize({ width: 375, height: 900 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -34,7 +34,7 @@ h1 { max-width: 760px; margin: var(--space-3) 0 var(--space-4); font-size: var(-
|
|||||||
.product-facts dt { color: var(--color-text-muted); font-size: var(--text-xs); font-weight: var(--weight-bold); letter-spacing: 0.1em; text-transform: uppercase; }
|
.product-facts dt { color: var(--color-text-muted); font-size: var(--text-xs); font-weight: var(--weight-bold); letter-spacing: 0.1em; text-transform: uppercase; }
|
||||||
.product-facts dd { margin: 0; }
|
.product-facts dd { margin: 0; }
|
||||||
.catalog-state { padding: var(--space-12) 0; color: var(--color-text-muted); }
|
.catalog-state { padding: var(--space-12) 0; color: var(--color-text-muted); }
|
||||||
.catalog-state--error { color: var(--color-danger-500); }
|
.catalog-state--error { color: var(--color-danger-text); }
|
||||||
.pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-6); margin-top: var(--space-8); color: var(--color-text-muted); font-size: var(--text-sm); }
|
.pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-6); margin-top: var(--space-8); color: var(--color-text-muted); font-size: var(--text-sm); }
|
||||||
.pagination button { min-width: 44px; min-height: 44px; border: var(--border-thin) solid var(--color-border-strong); border-radius: var(--radius-md); padding: var(--space-2) var(--space-4); background: var(--color-surface); color: var(--color-interactive); }
|
.pagination button { min-width: 44px; min-height: 44px; border: var(--border-thin) solid var(--color-border-strong); border-radius: var(--radius-md); padding: var(--space-2) var(--space-4); background: var(--color-surface); color: var(--color-interactive); }
|
||||||
.pagination button:disabled { cursor: not-allowed; opacity: 0.45; }
|
.pagination button:disabled { cursor: not-allowed; opacity: 0.45; }
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { expect, userEvent, within } from '@storybook/test';
|
||||||
|
|
||||||
|
import CatalogPage from './CatalogPage.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Pages/CatalogPage',
|
||||||
|
component: CatalogPage,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Overview = {};
|
||||||
|
|
||||||
|
export const MobileFilterDisclosure = {
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
'On narrow viewports, catalog filters collapse behind a keyboard-operable disclosure that hides filter controls from the accessibility tree until expanded.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
const toggle = canvas.getByRole('button', { name: /Filters/ });
|
||||||
|
const setFilter = canvas.getByPlaceholderText('SV151');
|
||||||
|
|
||||||
|
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
||||||
|
await expect(setFilter).not.toBeVisible();
|
||||||
|
|
||||||
|
await userEvent.click(toggle);
|
||||||
|
|
||||||
|
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
||||||
|
await expect(setFilter).toBeVisible();
|
||||||
|
|
||||||
|
await userEvent.click(toggle);
|
||||||
|
|
||||||
|
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
||||||
|
await expect(setFilter).not.toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -184,6 +184,8 @@ Remaining roadmap areas are approved external provider credentials/adapters, bro
|
|||||||
- The guided wizard now manages focus and announces step changes: selecting a stage (via the stepper or Back/Next) moves keyboard/assistive-technology focus to that stage's labeled region and updates a live-region status announcement, with Storybook interaction coverage.
|
- The guided wizard now manages focus and announces step changes: selecting a stage (via the stepper or Back/Next) moves keyboard/assistive-technology focus to that stage's labeled region and updates a live-region status announcement, with Storybook interaction coverage.
|
||||||
- Seller-scoped latest market valuations are now readable through `GET /api/v1/admin/valuations`.
|
- Seller-scoped latest market valuations are now readable through `GET /api/v1/admin/valuations`.
|
||||||
- Physical inventory management (bulk archive, bulk pricing refresh, bulk tag updates, market valuations, and pricing job retry) is now a dedicated `/admin/inventory` screen with its own navigation entry; the guided workflow overview links to it and only tracks a lightweight inventory count for wizard completion.
|
- Physical inventory management (bulk archive, bulk pricing refresh, bulk tag updates, market valuations, and pricing job retry) is now a dedicated `/admin/inventory` screen with its own navigation entry; the guided workflow overview links to it and only tracks a lightweight inventory count for wizard completion.
|
||||||
|
- Storybook now installs a lightweight router mirroring the app's named routes plus a mobile-viewport-forcing test-runner hook, enabling isolated page stories (including the public catalog) that use `useRoute`/`useRouter` or narrow-viewport CSS breakpoints.
|
||||||
|
- The public catalog now has its own Storybook coverage, including an interaction test proving the mobile filter disclosure is keyboard-operable and correctly hides/reveals filter controls via `aria-expanded`. This review also found and fixed a real color-contrast failure in the catalog error-state text.
|
||||||
|
|
||||||
## Local Commands
|
## Local Commands
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user