mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 08:15:41 +00:00
fix: keep date and size filters usable when the Mountain View search field is narrow
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { FilterPanelHost } from '../filter-panel-host';
|
||||
import { FilterPanelHost, filterPanelBox } from '../filter-panel-host';
|
||||
|
||||
// Mountain View moves the filter button into the global top bar. The panel
|
||||
// follows it there, given an anchor; without one it stays in the flow of the
|
||||
@@ -36,6 +36,17 @@ describe('FilterPanelHost', () => {
|
||||
anchor.remove();
|
||||
});
|
||||
|
||||
it('stays wide enough for its fields when the search field is narrow', () => {
|
||||
// The field is 286px wide at 1024: a panel that size cut the date fields to "dd".
|
||||
expect(filterPanelBox({ left: 165, right: 451, bottom: 56, width: 286 }, 1024, false)).toEqual({ top: 60, left: 165, width: 480 });
|
||||
});
|
||||
|
||||
it('grows towards the trailing side in RTL and stays on screen', () => {
|
||||
expect(filterPanelBox({ left: 573, right: 859, bottom: 56, width: 286 }, 1024, true)).toEqual({ top: 60, left: 379, width: 480 });
|
||||
expect(filterPanelBox({ left: 700, right: 986, bottom: 56, width: 286 }, 1024, false).left).toBe(536);
|
||||
expect(filterPanelBox({ left: 20, right: 300, bottom: 56, width: 280 }, 400, false)).toEqual({ top: 60, left: 8, width: 384 });
|
||||
});
|
||||
|
||||
it('draws it at the end of <body>, above the sidebar column and not inside the top bar', () => {
|
||||
const anchor = document.createElement('div');
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
@@ -52,6 +52,26 @@ export function FilterPanelHost({
|
||||
);
|
||||
}
|
||||
|
||||
/** Below this the form's labelled rows leave its date and size fields a few characters wide. */
|
||||
const MIN_PANEL_WIDTH = 480;
|
||||
const VIEWPORT_MARGIN = 8;
|
||||
|
||||
/**
|
||||
* Where the panel goes for a given search field: under it, sharing its leading
|
||||
* edge and width. The field shrinks with the window, so a narrow one gets a
|
||||
* panel wider than itself, growing towards the trailing side and kept on screen.
|
||||
*/
|
||||
export function filterPanelBox(
|
||||
rect: { left: number; right: number; bottom: number; width: number },
|
||||
viewportWidth: number,
|
||||
rtl: boolean,
|
||||
): CSSProperties {
|
||||
const width = Math.max(rect.width, Math.min(MIN_PANEL_WIDTH, viewportWidth - VIEWPORT_MARGIN * 2));
|
||||
const preferred = rtl ? rect.right - width : rect.left;
|
||||
const left = Math.max(VIEWPORT_MARGIN, Math.min(preferred, viewportWidth - width - VIEWPORT_MARGIN));
|
||||
return { top: rect.bottom + 4, left, width };
|
||||
}
|
||||
|
||||
/** The anchor's leading edge, width and bottom, kept current as the bar resizes. */
|
||||
function useAnchorBox(anchor: HTMLElement | null): CSSProperties | null {
|
||||
const [box, setBox] = useState<CSSProperties | null>(null);
|
||||
@@ -62,8 +82,7 @@ function useAnchorBox(anchor: HTMLElement | null): CSSProperties | null {
|
||||
return;
|
||||
}
|
||||
const measure = () => {
|
||||
const rect = anchor.getBoundingClientRect();
|
||||
setBox({ top: rect.bottom + 4, left: rect.left, width: rect.width });
|
||||
setBox(filterPanelBox(anchor.getBoundingClientRect(), window.innerWidth, getComputedStyle(anchor).direction === "rtl"));
|
||||
};
|
||||
measure();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user