mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 00:14:12 +00:00
fix: open a folder from anywhere in its sidebar row #1501
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import type { Mailbox } from '@/lib/jmap/types';
|
||||
|
||||
// A folder row opens its folder wherever it is clicked, not only on the
|
||||
// button inside it. Mountain View draws a 32px pill around a ~20px button,
|
||||
// and clicks on the pill's padding, the chevron slot or the count did
|
||||
// nothing (#1501). The row's own controls keep their click.
|
||||
|
||||
vi.mock('@/components/plugins/plugin-slot', () => ({ PluginSlot: () => null }));
|
||||
vi.mock('@/components/tour/tour-provider', () => ({
|
||||
useTour: () => ({ startTour: vi.fn(), resetTourCompletion: vi.fn(), activeTour: null, endTour: vi.fn(), registerStep: vi.fn() }),
|
||||
}));
|
||||
vi.mock('@/contexts/drag-drop-context', () => ({
|
||||
useDragDropContext: () => ({ isDragging: false }),
|
||||
}));
|
||||
vi.mock('@/hooks/use-mailbox-drop', () => ({
|
||||
useMailboxDrop: () => ({ dropHandlers: {}, isValidDropTarget: false, isInvalidDropTarget: false }),
|
||||
}));
|
||||
vi.mock('@/hooks/use-tag-drop', () => ({
|
||||
useTagDrop: () => ({ dropHandlers: {}, isValidDropTarget: false }),
|
||||
}));
|
||||
vi.mock('../account-switcher', () => ({ AccountSwitcher: () => null }));
|
||||
|
||||
const { Sidebar } = await import('../sidebar');
|
||||
const { useSettingsStore } = await import('@/stores/settings-store');
|
||||
const { useUIStore } = await import('@/stores/ui-store');
|
||||
|
||||
const mailboxes = [
|
||||
{ id: 'inbox', name: 'Inbox', role: 'inbox', parentId: null, totalEmails: 9, unreadEmails: 4, sortOrder: 0, myRights: { mayAddItems: true } },
|
||||
{ id: 'receipts', name: 'Receipts', role: null, parentId: null, totalEmails: 5, unreadEmails: 3, sortOrder: 1, myRights: { mayAddItems: true } },
|
||||
{ id: 'shops', name: 'Shops', role: null, parentId: 'receipts', totalEmails: 0, unreadEmails: 0, sortOrder: 0, myRights: { mayAddItems: true } },
|
||||
] as unknown as Mailbox[];
|
||||
|
||||
const row = (id: string) => document.querySelector<HTMLElement>(`[data-testid="folder-row"][data-mailbox-id="${id}"]`)!;
|
||||
|
||||
describe.each([
|
||||
['Mountain View', 'mountain-view', true],
|
||||
['the default layout', 'default', false],
|
||||
] as const)('folder row click area in %s', (_name, interfaceLayout, mvShell) => {
|
||||
beforeEach(() => {
|
||||
useUIStore.setState({ sidebarCollapsed: false } as never);
|
||||
useSettingsStore.setState({ interfaceLayout, hideAccountSwitcher: true, enableUnifiedMailbox: false } as never);
|
||||
});
|
||||
|
||||
it('opens the folder from a click on the row outside its button', () => {
|
||||
const onMailboxSelect = vi.fn();
|
||||
render(<Sidebar mailboxes={mailboxes} selectedMailbox="inbox" onMailboxSelect={onMailboxSelect} mvShell={mvShell} />);
|
||||
fireEvent.click(row('receipts'));
|
||||
expect(onMailboxSelect).toHaveBeenCalledWith('receipts');
|
||||
});
|
||||
|
||||
it('opens the folder from a click on its count', () => {
|
||||
const onMailboxSelect = vi.fn();
|
||||
render(<Sidebar mailboxes={mailboxes} selectedMailbox="inbox" onMailboxSelect={onMailboxSelect} mvShell={mvShell} />);
|
||||
fireEvent.click(row('receipts').querySelector('[data-testid="folder-counts"]')!);
|
||||
expect(onMailboxSelect).toHaveBeenCalledWith('receipts');
|
||||
});
|
||||
|
||||
it('shows the pointer over the whole row', () => {
|
||||
render(<Sidebar mailboxes={mailboxes} selectedMailbox="inbox" onMailboxSelect={() => {}} mvShell={mvShell} />);
|
||||
expect(row('receipts').className).toContain('cursor-pointer');
|
||||
});
|
||||
|
||||
it('only expands or collapses the folder from its chevron', () => {
|
||||
const onMailboxSelect = vi.fn();
|
||||
render(<Sidebar mailboxes={mailboxes} selectedMailbox="inbox" onMailboxSelect={onMailboxSelect} mvShell={mvShell} />);
|
||||
const shownBefore = row('shops') !== null;
|
||||
fireEvent.click(row('receipts').querySelector('[data-testid="folder-expand-toggle"]')!);
|
||||
expect(row('shops') !== null).toBe(!shownBefore);
|
||||
expect(onMailboxSelect).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('folder row click area with the unread filter', () => {
|
||||
it('filters to unread from the unread count without also opening the folder', () => {
|
||||
useSettingsStore.setState({ interfaceLayout: 'default', hideAccountSwitcher: true, enableUnifiedMailbox: false } as never);
|
||||
const onMailboxSelect = vi.fn();
|
||||
const onUnreadFilterClick = vi.fn();
|
||||
render(<Sidebar mailboxes={mailboxes} selectedMailbox="inbox" onMailboxSelect={onMailboxSelect} onUnreadFilterClick={onUnreadFilterClick} />);
|
||||
fireEvent.click(row('receipts').querySelector('[data-testid="folder-counts"] button')!);
|
||||
expect(onUnreadFilterClick).toHaveBeenCalledWith('receipts');
|
||||
expect(onMailboxSelect).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -430,11 +430,20 @@ function SidebarRow({
|
||||
const longPress = useLongPress((point) => {
|
||||
if (rowRef.current && onContextMenu) onContextMenu(menuEventAt(rowRef.current, point));
|
||||
}, hasMenu);
|
||||
// The whole row opens the folder, not just its button: the row's padding,
|
||||
// the chevron slot and the space around the count were dead (#1501). The
|
||||
// button stays the keyboard target; controls of their own keep their click.
|
||||
const openFromRow = !isVirtual && onClick
|
||||
? (e: React.MouseEvent) => {
|
||||
if (!(e.target as Element).closest('button, a, input, [role="button"]')) onClick();
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rowRef}
|
||||
{...(dropHandlers || {})}
|
||||
onClick={openFromRow}
|
||||
onContextMenu={onContextMenu}
|
||||
onTouchStart={hasMenu ? longPress.onTouchStart : undefined}
|
||||
onTouchMove={hasMenu ? longPress.onTouchMove : undefined}
|
||||
@@ -452,6 +461,7 @@ function SidebarRow({
|
||||
className={cn(
|
||||
"group w-full flex items-center max-lg:min-h-[44px] text-sm transition-colors duration-150",
|
||||
isCollapsed ? "justify-center px-1" : "pe-2",
|
||||
openFromRow && "cursor-pointer select-none",
|
||||
isVirtual
|
||||
? "text-muted-foreground"
|
||||
: isSelected
|
||||
|
||||
Reference in New Issue
Block a user