refactor(documents): rework timeline UX after live testing (#385)
Some checks failed
CI / Unit & Component Tests (push) Failing after 4m29s
CI / OCR Service Tests (push) Successful in 48s
CI / Backend Unit Tests (push) Failing after 3m46s
CI / Unit & Component Tests (pull_request) Failing after 4m31s
CI / OCR Service Tests (pull_request) Successful in 38s
CI / Backend Unit Tests (pull_request) Failing after 3m31s
Some checks failed
CI / Unit & Component Tests (push) Failing after 4m29s
CI / OCR Service Tests (push) Successful in 48s
CI / Backend Unit Tests (push) Failing after 3m46s
CI / Unit & Component Tests (pull_request) Failing after 4m31s
CI / OCR Service Tests (pull_request) Successful in 38s
CI / Backend Unit Tests (pull_request) Failing after 3m31s
Replaces the discrete zoom-in button with a Graylog-style drag-to-zoom range selector and adds X/Y axis labels so the chart is readable. Drag interaction - Pointerdown on a bar attaches document-level pointermove/pointerup/ pointercancel listeners; pointermove maps clientX to a bar index via the row's bounding rect, so the mint-bordered window expands smoothly even when the cursor leaves the bar or the chart entirely. - pointerup commits filter + zoom atomically. Same-bar release on a year bar (year-aggregated mode) zooms into that year's months; same-bar release on a month bar emits filter-only. - setPointerCapture removed — it was suppressing pointerenter on sibling bars and preventing the drag window from expanding. - Bar buttons are now h-full so the entire 80 px column is the hit target, not just the visible bar height. Axis labels - Y-axis: max-count and 0 labels left of the bar area. - X-axis: tickIndicesFor() picks decadal years for long ranges, evenly spaced months for short year-zoom views, January boundaries for multi-year month ranges. formatTickLabel() drops the year when the visible range is a single year so 12-month zooms read "Jan Feb Mär…". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -253,7 +253,16 @@ $effect(() => {
|
||||
onchange={(event) => {
|
||||
from = event.from;
|
||||
to = event.to;
|
||||
triggerSearch();
|
||||
// Drag commits filter + zoom atomically (Graylog-style range selector).
|
||||
// Single click and clear omit zoomFrom/zoomTo so existing zoom is preserved.
|
||||
if ('zoomFrom' in event) {
|
||||
triggerSearch({
|
||||
zoomFrom: event.zoomFrom ?? null,
|
||||
zoomTo: event.zoomTo ?? null
|
||||
});
|
||||
} else {
|
||||
triggerSearch();
|
||||
}
|
||||
}}
|
||||
onzoomchange={(event) => {
|
||||
triggerSearch({
|
||||
|
||||
@@ -177,30 +177,18 @@ describe('documents page — timeline density widget', () => {
|
||||
expect(url).toContain('to=1915-08-31');
|
||||
});
|
||||
|
||||
it('clicking the zoom-in button writes zoomFrom/zoomTo URL params', async () => {
|
||||
const { goto } = await import('$app/navigation');
|
||||
vi.mocked(goto).mockClear();
|
||||
|
||||
it('the standalone zoom-in button no longer exists (drag replaces it)', async () => {
|
||||
render(Page, {
|
||||
data: makeData({
|
||||
density: [
|
||||
{ month: '1915-08', count: 3 },
|
||||
{ month: '1915-09', count: 2 }
|
||||
],
|
||||
density: [{ month: '1915-08', count: 3 }],
|
||||
minDate: '1915-08-01',
|
||||
maxDate: '1915-09-30',
|
||||
maxDate: '1915-08-31',
|
||||
from: '1915-08-01',
|
||||
to: '1915-09-30'
|
||||
to: '1915-08-31'
|
||||
})
|
||||
});
|
||||
|
||||
const zoomBtn = document.querySelector('[data-testid="timeline-zoom-in"]') as HTMLButtonElement;
|
||||
zoomBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
|
||||
expect(goto).toHaveBeenCalledOnce();
|
||||
const [url] = vi.mocked(goto).mock.calls[0];
|
||||
expect(url).toContain('zoomFrom=1915-08-01');
|
||||
expect(url).toContain('zoomTo=1915-09-30');
|
||||
expect(document.querySelector('[data-testid="timeline-zoom-in"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('clicking reset-zoom drops zoomFrom/zoomTo from the URL', async () => {
|
||||
|
||||
Reference in New Issue
Block a user