mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: keep Mountain View's row actions, checkbox and star on the subject line when attachment chips make the row taller
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Email } from '@/lib/jmap/types';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import { EmailHoverActions } from '../email-hover-actions';
|
||||
|
||||
vi.mock('@/components/plugins/plugin-actions', () => ({
|
||||
PluginActions: () => null,
|
||||
usePluginActions: () => [],
|
||||
useMessageActionTarget: () => ({ accountKind: 'personal', keywords: [] }),
|
||||
}));
|
||||
|
||||
// Gmail keeps a row's actions on its first line when attachment chips make the
|
||||
// row taller; ours sat centred between the subject and the chips.
|
||||
|
||||
const email = { id: 'e1', keywords: { $seen: true }, mailboxIds: { inbox: true } } as unknown as Email;
|
||||
|
||||
function toolbar(pinToFirstLine?: boolean) {
|
||||
render(<EmailHoverActions email={email} onDelete={vi.fn()} pinToFirstLine={pinToFirstLine} />);
|
||||
return screen.getByTestId('hover-action-delete').closest('.absolute') as HTMLElement;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
useSettingsStore.setState({ hoverActions: ['delete'], interfaceLayout: 'mountain-view', hoverActionsMode: 'inline' } as never);
|
||||
});
|
||||
|
||||
describe('row quick action placement', () => {
|
||||
it('stays on the first line of a Mountain View row', () => {
|
||||
const el = toolbar(true);
|
||||
expect(el.className).toContain('h-10');
|
||||
expect(el.className).not.toContain('bottom-0');
|
||||
});
|
||||
|
||||
it('spans the whole row otherwise', () => {
|
||||
const el = toolbar();
|
||||
expect(el.className).toContain('bottom-0');
|
||||
expect(el.className).not.toContain('h-10');
|
||||
});
|
||||
});
|
||||
@@ -32,6 +32,11 @@ interface EmailHoverActionsProps {
|
||||
spamApplicable?: boolean;
|
||||
/** Role of the folder the row is listed in; plugin buttons can be limited to some. */
|
||||
mailboxRole?: string | null;
|
||||
/**
|
||||
* Keep the actions on the row's first 40px line rather than centred on the
|
||||
* whole row, as Gmail does when attachment chips make the row taller.
|
||||
*/
|
||||
pinToFirstLine?: boolean;
|
||||
}
|
||||
|
||||
const ACTION_CONFIG: Record<HoverAction, {
|
||||
@@ -111,6 +116,7 @@ export function EmailHoverActions({
|
||||
onUndoSpam,
|
||||
spamApplicable = true,
|
||||
mailboxRole = null,
|
||||
pinToFirstLine = false,
|
||||
}: EmailHoverActionsProps) {
|
||||
const tagScope = useEmailTagScope(email);
|
||||
const configuredHoverActions = useSettingsStore((state) => state.hoverActions);
|
||||
@@ -337,7 +343,7 @@ export function EmailHoverActions({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn("absolute end-0 top-0 bottom-0 z-10 items-center", visibilityClass)}
|
||||
className={cn("absolute end-0 top-0 z-10 items-center", pinToFirstLine ? "h-10" : "bottom-0", visibilityClass)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -115,7 +115,7 @@ function RowChips({ email, loadAttachments, onOpenAttachment, mountainView, mvRo
|
||||
const code = useListVerificationCode(email);
|
||||
const leading = code ? <VerificationCodeChip code={code} variant={variant} /> : undefined;
|
||||
// The sender column is w-32 (lg:w-40) and the row line has gap-3 after it.
|
||||
const className = cn(variant === "mountain-view" ? "mt-2" : "mt-1.5", mvRow && "mt-1 pb-2.5 ps-[8.75rem] lg:ps-[10.75rem]");
|
||||
const className = cn(variant === "mountain-view" ? "mt-2" : "mt-1.5", mvRow && "-mt-0.5 pb-2.5 ps-[8.75rem] lg:ps-[10.75rem]");
|
||||
// Most rows have no attachment: no loader for them, nor an empty chip row.
|
||||
const mayHaveAttachments = email.hasAttachment || (email.attachments?.length ?? 0) > 0;
|
||||
if (onOpenAttachment && mayHaveAttachments) {
|
||||
@@ -155,7 +155,7 @@ function MvRowLead({
|
||||
starLabel: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-0.5 flex-shrink-0 self-center">
|
||||
<div className="flex h-10 items-center gap-0.5 flex-shrink-0 self-start">
|
||||
<button
|
||||
type="button"
|
||||
role="checkbox"
|
||||
@@ -664,7 +664,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
||||
<div className="flex-1 min-w-0">
|
||||
{oneLineRow ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className={cn("flex items-center justify-between gap-3", mvRow && "min-h-10")}>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||
{/* In columns the dot sits inside the From cell, so a row
|
||||
with it starts its columns where the header does. */}
|
||||
@@ -904,6 +904,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
||||
isInJunk={currentMailboxRole === 'junk'}
|
||||
spamApplicable={!['sent', 'drafts', 'scheduled'].includes(currentMailboxRole || '')}
|
||||
mailboxRole={currentMailboxRole ?? null}
|
||||
pinToFirstLine={mvRow}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -1339,7 +1340,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
||||
<div className="flex-1 min-w-0">
|
||||
{oneLineRow ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className={cn("flex items-center justify-between gap-3", mvRow && "min-h-10")}>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||
{!columns && isUnifiedView && latestEmail.accountId && threadAccountColor && showAccountDot && (
|
||||
<span
|
||||
@@ -1632,6 +1633,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
||||
isInJunk={currentMailboxRole === 'junk'}
|
||||
spamApplicable={!['sent', 'drafts', 'scheduled'].includes(currentMailboxRole || '')}
|
||||
mailboxRole={currentMailboxRole ?? null}
|
||||
pinToFirstLine={mvRow}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user