feat(dashboard): customise widget visibility order and size
This commit is contained in:
@@ -5,6 +5,7 @@ import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import DashboardView from './components/DashboardView';
|
||||
import { I18nProvider } from './i18n/I18nProvider';
|
||||
import { api } from './api';
|
||||
import { ConfirmProvider } from './confirm';
|
||||
|
||||
jest.mock('./components/OnboardingChecklist', () => ({ hasJobs }: { hasJobs: boolean | null }) => <><div>Setup retained</div><output aria-label="Setup job status">{hasJobs === null ? 'unknown' : String(hasJobs)}</output></>);
|
||||
const mockedApi = api as jest.Mocked<typeof api>;
|
||||
@@ -12,8 +13,10 @@ const stats = { total: 9, active: 8, deleted: 1, byStatus: { Applied: 3, Intervi
|
||||
const overview = { funnel: [{ label: 'Applied', count: 3 }, { label: 'Interview', count: 2 }, { label: 'Offer', count: 1 }], totalActive: 8, totalResponses: 3, responseRateBySource: [{ label: 'NAV', total: 8, responses: 3, rate: 37.5 }], topCompanies: [{ companyId: 7, company: 'Nordlys AS', count: 8, responses: 3, responseRate: 37.5 }], timeInStage: [{ stage: 'Applied', medianDays: 4, count: 3 }], salaryInsights: [{ currency: 'NOK', period: 'year', count: 2, minimum: 600000, maximum: 800000, averageMidpoint: 700000 }] };
|
||||
const monthly = [{ month: '2026-08', applied: 4, responses: 1 }, { month: '2026-09', applied: 2, responses: 1 }];
|
||||
function Location() { return <output aria-label="Destination">{useLocation().pathname}{useLocation().search}</output>; }
|
||||
function open() { return render(<I18nProvider><MemoryRouter><Location /><DashboardView /></MemoryRouter></I18nProvider>); }
|
||||
function open() { return render(<I18nProvider><ConfirmProvider><MemoryRouter><Location /><DashboardView /></MemoryRouter></ConfirmProvider></I18nProvider>); }
|
||||
let layout: any;
|
||||
function data(url: string) {
|
||||
if (url.endsWith('/preferences/dashboard')) return layout;
|
||||
if (url.endsWith('/stats')) return stats;
|
||||
if (url.endsWith('/analytics-overview')) return overview;
|
||||
if (url.endsWith('/analytics')) return monthly;
|
||||
@@ -21,7 +24,11 @@ function data(url: string) {
|
||||
if (url.endsWith('/tag-trends')) return { months: ['2026-08', '2026-09'], series: [{ tag: 'TypeScript', counts: [3, 2] }] };
|
||||
return [];
|
||||
}
|
||||
beforeEach(() => { localStorage.clear(); mockedApi.get.mockReset(); mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: data(url) }) as any); });
|
||||
beforeEach(() => {
|
||||
localStorage.clear(); layout = { version: 1, revision: 0, widgets: [] };
|
||||
mockedApi.get.mockReset(); mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: data(url) }) as any);
|
||||
mockedApi.put.mockImplementation((_url: string, body: any) => { layout = { ...body, revision: body.revision + 1 }; return Promise.resolve({ data: layout }) as any; });
|
||||
});
|
||||
|
||||
test('[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable', async () => {
|
||||
open();
|
||||
@@ -37,11 +44,14 @@ test('[PRES-S01] exact monthly values, sample, custom statuses and separate sala
|
||||
expect(screen.queryByText(/upcoming|conversion funnel|active applications/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('[PRES-S02] all five stored visibility flags hide their groups and survive remount', async () => {
|
||||
test('[PRES-S02] all widget visibility choices save and survive remount', async () => {
|
||||
let view = open(); await screen.findByText('Tracked jobs');
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Customize dashboard' }));
|
||||
for (const item of screen.getAllByRole('menuitemcheckbox')) fireEvent.click(item);
|
||||
expect(screen.getAllByRole('menuitemcheckbox').every(item => item.getAttribute('aria-checked') === 'false')).toBe(true);
|
||||
const editor = await screen.findByRole('region', { name: 'Editing dashboard' });
|
||||
for (const item of within(editor).getAllByRole('checkbox')) fireEvent.click(item);
|
||||
expect(within(editor).getAllByRole('checkbox').every(item => !(item as HTMLInputElement).checked)).toBe(true);
|
||||
fireEvent.click(within(editor).getByRole('button', { name: 'Save layout' }));
|
||||
await waitFor(() => expect(screen.queryByRole('region', { name: 'Editing dashboard' })).not.toBeInTheDocument());
|
||||
view.unmount(); view = open();
|
||||
await screen.findByText('Your dashboard sections are hidden.');
|
||||
expect(screen.queryByText('Tracked jobs')).not.toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user