fix(list): shift-click on the checkbox extends the selection (range)
selectRangeEmails was only wired to shift-clicking the row, but the checkbox handler called stopPropagation and a plain toggle — so shift- clicking checkboxes (the obvious affordance in selection mode) selected single messages instead of the range. Make all three checkbox handlers (email-list-item, thread single-email, thread header) shift-aware: shift -> selectRangeEmails, otherwise toggle. Adds a regression test.
This commit is contained in:
@@ -121,3 +121,34 @@ describe('EmailListItem tag badge', () => {
|
|||||||
expect(container.querySelector('p')).toBeNull();
|
expect(container.querySelector('p')).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('EmailListItem shift-range checkbox', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
useSettingsStore.setState({ emailKeywords: [...DEFAULT_KEYWORDS], showPreview: false, mailLayout: 'split' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shift-clicking the checkbox extends the selection from the anchor', () => {
|
||||||
|
const e1 = makeEmail({ id: 'e1', threadId: 't1' });
|
||||||
|
const e2 = makeEmail({ id: 'e2', threadId: 't2' });
|
||||||
|
const e3 = makeEmail({ id: 'e3', threadId: 't3' });
|
||||||
|
// selection mode active (so the checkbox renders), anchor on e1
|
||||||
|
useEmailStore.setState({
|
||||||
|
emails: [e1, e2, e3],
|
||||||
|
selectedEmailIds: new Set(['e1']),
|
||||||
|
lastSelectedEmailId: 'e1',
|
||||||
|
selectedMailbox: 'inbox',
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<EmailListItem email={e3} />);
|
||||||
|
// the checkbox is the first button in the row (shown in selection mode)
|
||||||
|
const checkbox = screen.getAllByRole('button')[0];
|
||||||
|
act(() => {
|
||||||
|
checkbox.dispatchEvent(new MouseEvent('click', { bubbles: true, shiftKey: true }));
|
||||||
|
});
|
||||||
|
|
||||||
|
const sel = useEmailStore.getState().selectedEmailIds;
|
||||||
|
expect(sel.has('e1')).toBe(true);
|
||||||
|
expect(sel.has('e2')).toBe(true); // the in-between row got filled in
|
||||||
|
expect(sel.has('e3')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -87,7 +87,14 @@ export function EmailListItem({ email, selected, onClick, onDoubleClick, onConte
|
|||||||
|
|
||||||
const handleCheckboxClick = (e: React.MouseEvent) => {
|
const handleCheckboxClick = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggleEmailSelection(email.id);
|
if (e.shiftKey) {
|
||||||
|
// Shift-click extends the selection from the anchor to here, like
|
||||||
|
// shift-clicking the row (the checkbox stops propagation, so the
|
||||||
|
// row's shift handler never runs — replicate it here).
|
||||||
|
selectRangeEmails(email.id);
|
||||||
|
} else {
|
||||||
|
toggleEmailSelection(email.id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContextMenu = (e: React.MouseEvent) => {
|
const handleContextMenu = (e: React.MouseEvent) => {
|
||||||
|
|||||||
@@ -134,7 +134,11 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
|||||||
|
|
||||||
const handleCheckboxClick = (e: React.MouseEvent) => {
|
const handleCheckboxClick = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggleEmailSelection(email.id);
|
if (e.shiftKey) {
|
||||||
|
selectRangeEmails(email.id);
|
||||||
|
} else {
|
||||||
|
toggleEmailSelection(email.id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContextMenu = (e: React.MouseEvent) => {
|
const handleContextMenu = (e: React.MouseEvent) => {
|
||||||
@@ -513,6 +517,10 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
|||||||
|
|
||||||
const handleThreadCheckboxClick = (e: React.MouseEvent) => {
|
const handleThreadCheckboxClick = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
if (e.shiftKey) {
|
||||||
|
selectRangeEmails(latestEmail.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Toggle selection for all emails in this thread
|
// Toggle selection for all emails in this thread
|
||||||
const allSelected = thread.emails.every(em => selectedEmailIds.has(em.id));
|
const allSelected = thread.emails.every(em => selectedEmailIds.has(em.id));
|
||||||
const newSelection = new Set(selectedEmailIds);
|
const newSelection = new Set(selectedEmailIds);
|
||||||
|
|||||||
Reference in New Issue
Block a user