feat(dashboard): add revision-safe interactive widget grid
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import DashboardGrid from './DashboardGrid';
|
||||
import { defaultDashboardLayout } from '../dashboardLayout';
|
||||
import { I18nProvider } from '../i18n/I18nProvider';
|
||||
|
||||
let mockGridProps: any;
|
||||
let mockMeasuredWidth = 1200;
|
||||
jest.mock('react-grid-layout', () => ({
|
||||
...jest.requireActual('react-grid-layout'),
|
||||
__esModule: true,
|
||||
default: (props: any) => { mockGridProps = props; return <div>{props.children}</div>; },
|
||||
useContainerWidth: () => ({ width: mockMeasuredWidth, containerRef: { current: null } }),
|
||||
}));
|
||||
beforeEach(() => { mockMeasuredWidth = 1200; mockGridProps = null; });
|
||||
|
||||
test('library collision handling resolves resize overlaps but leaves deliberate gaps unchanged', () => {
|
||||
render(<I18nProvider><DashboardGrid layout={defaultDashboardLayout()} editing disabled={false} onChange={jest.fn()} childrenFor={() => <p>Report</p>} /></I18nProvider>);
|
||||
const items = [
|
||||
{ i: 'a', x: 0, y: 17, w: 6, h: 10 },
|
||||
{ i: 'b', x: 0, y: 25, w: 6, h: 8 },
|
||||
{ i: 'c', x: 6, y: 25, w: 6, h: 8 },
|
||||
];
|
||||
const original = JSON.stringify(items);
|
||||
const resolved = mockGridProps.compactor.compact(items, 12);
|
||||
const { getAllCollisions } = jest.requireActual('react-grid-layout');
|
||||
expect(resolved.every((item: any) => getAllCollisions(resolved, item).length === 0)).toBe(true);
|
||||
expect(JSON.stringify(items)).toBe(original);
|
||||
const spaced = [{ i: 'a', x: 4, y: 17, w: 8, h: 10 }];
|
||||
expect(mockGridProps.compactor.compact(spaced, 12)).toMatchObject(spaced);
|
||||
});
|
||||
|
||||
test('mount and compaction do not save; completed resize keeps hidden geometry and uses the draft owner revision', () => {
|
||||
const layout = defaultDashboardLayout(); layout.revision = 8;
|
||||
layout.widgets[2] = { ...layout.widgets[2], visible: false, x: 4, y: 17, w: 8, h: 10 };
|
||||
const change = jest.fn();
|
||||
render(<I18nProvider><DashboardGrid layout={layout} editing disabled={false} onChange={change} childrenFor={widget => <p>{widget.id} content</p>} /></I18nProvider>);
|
||||
expect(change).not.toHaveBeenCalled();
|
||||
expect(mockGridProps.onLayoutChange).toBeUndefined();
|
||||
const next = mockGridProps.layout.map((row: any) => row.i === 'attention' ? { ...row, w: 8, h: 12 } : row);
|
||||
mockGridProps.onResizeStop(next);
|
||||
expect(change).toHaveBeenCalledTimes(1);
|
||||
const saved = change.mock.calls[0][0];
|
||||
expect(saved.revision).toBe(8);
|
||||
expect(saved.widgets[1]).toMatchObject({ w: 8, h: 12, size: 'standard' });
|
||||
expect(saved.widgets[2]).toEqual(layout.widgets[2]);
|
||||
});
|
||||
|
||||
test('narrow viewport retains desktop geometry and keyboard height changes are bounded', () => {
|
||||
mockMeasuredWidth = 390;
|
||||
const layout = defaultDashboardLayout();
|
||||
const change = jest.fn();
|
||||
const view = render(<I18nProvider><DashboardGrid layout={layout} editing disabled={false} onChange={change} childrenFor={widget => <p>{widget.id} content</p>} /></I18nProvider>);
|
||||
expect(mockGridProps).toBeNull();
|
||||
expect(change).not.toHaveBeenCalled();
|
||||
const preview = screen.getByRole('region', { name: 'Dashboard preview' });
|
||||
fireEvent.click(within(preview).getByText('Today'));
|
||||
fireEvent.change(screen.getByLabelText('Height for Today'), { target: { value: '25' } });
|
||||
expect(change).not.toHaveBeenCalled();
|
||||
fireEvent.change(screen.getByLabelText('Height for Today'), { target: { value: '12' } });
|
||||
expect(change.mock.calls[0][0].widgets[1].h).toBe(12);
|
||||
mockMeasuredWidth = 1200;
|
||||
view.rerender(<I18nProvider><DashboardGrid layout={layout} editing disabled={false} onChange={change} childrenFor={widget => <p>{widget.id} content</p>} /></I18nProvider>);
|
||||
expect(change).toHaveBeenCalledTimes(1);
|
||||
expect(mockGridProps.layout[1]).toMatchObject({ x: layout.widgets[1].x, y: layout.widgets[1].y });
|
||||
});
|
||||
|
||||
test('conflict disables pointer operations and ignores a late completion callback', () => {
|
||||
const change = jest.fn();
|
||||
render(<I18nProvider><DashboardGrid layout={defaultDashboardLayout()} editing disabled onChange={change} childrenFor={() => <p>Report</p>} /></I18nProvider>);
|
||||
expect(mockGridProps.dragConfig.enabled).toBe(false);
|
||||
expect(mockGridProps.resizeConfig.enabled).toBe(false);
|
||||
mockGridProps.onDragStop(mockGridProps.layout);
|
||||
expect(change).not.toHaveBeenCalled();
|
||||
});
|
||||
Reference in New Issue
Block a user