mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
fix: keep the names of Reply, Archive, Delete and Move in the reader toolbar while they fit
This commit is contained in:
+4
-2
@@ -593,8 +593,10 @@ optgroup {
|
||||
animation: toast-exit 0.28s cubic-bezier(0.06, 0.71, 0.55, 1) forwards;
|
||||
}
|
||||
|
||||
/* The reader toolbar with no room for its labels: icons only, the names stay in the tooltips. */
|
||||
[data-toolbar-compact] [data-toolbar-label] {
|
||||
/* The reader toolbar with no room for its labels: icons only, the names stay in the tooltips.
|
||||
"secondary" first keeps the names of Reply, Archive, Delete and Move (mail-p2-15). */
|
||||
[data-toolbar-compact="secondary"] [data-toolbar-label]:not([data-toolbar-label="primary"]),
|
||||
[data-toolbar-compact="all"] [data-toolbar-label] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,24 @@ describe('reader toolbar overflow', () => {
|
||||
expect(labels).toEqual(expect.arrayContaining(['reply', 'reply_all', 'forward', 'archive', 'delete', 'move']));
|
||||
});
|
||||
|
||||
it('keeps the names of Reply, Archive, Delete and Move while only those fit (mail-p2-15)', () => {
|
||||
// 600 px: every label needs 400 per group, the main four's 280, none 250.
|
||||
const spies = [
|
||||
vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockImplementation(function (this: HTMLElement) {
|
||||
const step = this.closest('[data-toolbar-compact]')?.getAttribute('data-toolbar-compact');
|
||||
return step === 'all' ? 250 : step === 'secondary' ? 280 : 400;
|
||||
}),
|
||||
vi.spyOn(HTMLElement.prototype, 'clientWidth', 'get').mockReturnValue(600),
|
||||
vi.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(40),
|
||||
];
|
||||
restore = () => spies.forEach((spy) => spy.mockRestore());
|
||||
const { container } = viewer();
|
||||
expect(toolbar(container).getAttribute('data-toolbar-compact')).toBe('secondary');
|
||||
expect(hiddenPriorities(container)).toEqual([]);
|
||||
const kept = Array.from(container.querySelectorAll('[data-toolbar-label="primary"]')).map((label) => label.textContent);
|
||||
expect(kept.sort()).toEqual(['archive', 'delete', 'move', 'reply']);
|
||||
});
|
||||
|
||||
it('then hides the optional buttons, never Reply, Reply all, Forward, Archive or Move', () => {
|
||||
restore = layout(500, 400);
|
||||
const { container } = viewer();
|
||||
|
||||
@@ -231,6 +231,8 @@ const TOOLBAR_PRIORITY_ACTIONS: Record<string, MessageActionKey> = {
|
||||
const REQUIRED_TOOLBAR_PRIORITIES = [5, 3, 2, 4, 1] as const;
|
||||
/** Set on the toolbar while it shows icons without their labels. */
|
||||
const TOOLBAR_COMPACT_ATTRIBUTE = 'data-toolbar-compact';
|
||||
/** Which labels the reader toolbar has dropped: none, all but the main four, or all. */
|
||||
type ToolbarCompact = false | 'secondary' | 'all';
|
||||
const NO_ACTION_KEYS: ReadonlySet<MessageActionKey> = new Set();
|
||||
const NO_PLUGIN_KEYS: ReadonlySet<string> = new Set();
|
||||
|
||||
@@ -1854,7 +1856,7 @@ export function EmailViewer({
|
||||
// looked like (see layoutKey), and the width it last had: an open that
|
||||
// brings back a toolbar already measured at this width needs no measuring.
|
||||
// A loading message and a loaded one can differ, so a few are kept.
|
||||
const measuredToolbarRef = useRef(new Map<string, { hidden: Set<number>; compact: boolean }>());
|
||||
const measuredToolbarRef = useRef(new Map<string, { hidden: Set<number>; compact: ToolbarCompact }>());
|
||||
const toolbarWidthRef = useRef<number | undefined>(undefined);
|
||||
const currentTagIds = getEmailTagIds(email?.keywords);
|
||||
const sortedTagIds = sortTagIds(currentTagIds);
|
||||
@@ -2117,7 +2119,8 @@ export function EmailViewer({
|
||||
const key = layoutKey(items);
|
||||
const known = toolbarWidthRef.current !== undefined ? measuredToolbarRef.current.get(key) : undefined;
|
||||
if (known) {
|
||||
el.toggleAttribute(TOOLBAR_COMPACT_ATTRIBUTE, known.compact);
|
||||
if (known.compact) el.setAttribute(TOOLBAR_COMPACT_ATTRIBUTE, known.compact);
|
||||
else el.removeAttribute(TOOLBAR_COMPACT_ATTRIBUTE);
|
||||
for (const item of items) item.style.display = known.hidden.has(Number(item.dataset.overflowPriority)) ? 'none' : '';
|
||||
applyHidden(known.hidden);
|
||||
return;
|
||||
@@ -2135,10 +2138,13 @@ export function EmailViewer({
|
||||
const containerWidth = el.clientWidth;
|
||||
const overflowNow = () => leftGroup.scrollWidth + rightGroup.scrollWidth + mainGap - (containerWidth + 1);
|
||||
let overflow = overflowNow();
|
||||
// The labels go before any button does (icons keep their tooltips).
|
||||
const compact = overflow > 0;
|
||||
if (compact) {
|
||||
el.setAttribute(TOOLBAR_COMPACT_ATTRIBUTE, '');
|
||||
// The labels go before any button does (icons keep their tooltips):
|
||||
// first all but Reply, Archive, Delete and Move, then those too.
|
||||
let compact: ToolbarCompact = false;
|
||||
for (const step of ['secondary', 'all'] as const) {
|
||||
if (overflow <= 0) break;
|
||||
compact = step;
|
||||
el.setAttribute(TOOLBAR_COMPACT_ATTRIBUTE, step);
|
||||
overflow = overflowNow();
|
||||
}
|
||||
const hidden = new Set<number>();
|
||||
@@ -4477,7 +4483,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
title={t('tooltips.reply')}
|
||||
>
|
||||
<Reply className="w-4 h-4" />
|
||||
{showToolbarLabels && <span data-toolbar-label="" className="hidden @[30rem]/reader-toolbar:inline text-sm whitespace-nowrap">{t('reply')}</span>}
|
||||
{showToolbarLabels && <span data-toolbar-label="primary" className="hidden @[30rem]/reader-toolbar:inline text-sm whitespace-nowrap">{t('reply')}</span>}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -4535,7 +4541,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
title={t('spam.not_spam_title')}
|
||||
>
|
||||
<ShieldCheck className={cn("h-4 w-4", !mvViewer && "text-emerald-600 dark:text-emerald-400")} />
|
||||
{showToolbarLabels && <span data-toolbar-label="" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{t('spam.not_spam_title')}</span>}
|
||||
{showToolbarLabels && <span data-toolbar-label="primary" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{t('spam.not_spam_title')}</span>}
|
||||
</Button>
|
||||
)}
|
||||
{isInTrashFolder && restoreTarget && onMoveToMailbox && (
|
||||
@@ -4551,7 +4557,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
title={tActions('restore_to', { folder: localizeMailboxName(restoreTarget.role, restoreTarget.name, (key) => tSidebarNames(`mailboxes.${key}`)) })}
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
{showToolbarLabels && <span data-toolbar-label="" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{tActions('restore')}</span>}
|
||||
{showToolbarLabels && <span data-toolbar-label="primary" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{tActions('restore')}</span>}
|
||||
</Button>
|
||||
)}
|
||||
{archiveActionEnabled && !isInJunkFolder && !isInTrashFolder && (
|
||||
@@ -4565,7 +4571,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
title={t('tooltips.archive')}
|
||||
>
|
||||
<Archive className="w-4 h-4" />
|
||||
{showToolbarLabels && <span data-toolbar-label="" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{t('archive')}</span>}
|
||||
{showToolbarLabels && <span data-toolbar-label="primary" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{t('archive')}</span>}
|
||||
</Button>
|
||||
)}
|
||||
{/* Delete */}
|
||||
@@ -4578,7 +4584,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
data-testid="reader-delete"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
{showToolbarLabels && <span data-toolbar-label="" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{deleteIsPermanent ? tActions('delete_forever') : t('delete')}</span>}
|
||||
{showToolbarLabels && <span data-toolbar-label="primary" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{deleteIsPermanent ? tActions('delete_forever') : t('delete')}</span>}
|
||||
</Button>
|
||||
{/* Move to folder */}
|
||||
{canMoveAnywhere && onMoveToMailbox && (
|
||||
@@ -4598,7 +4604,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
aria-expanded={moveMenuOpen}
|
||||
>
|
||||
<FolderInput className="w-4 h-4" />
|
||||
{showToolbarLabels && <span data-toolbar-label="" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{t('move')}</span>}
|
||||
{showToolbarLabels && <span data-toolbar-label="primary" className="text-[0.625rem] leading-tight sm:hidden sm:text-sm sm:whitespace-nowrap sm:@[30rem]/reader-toolbar:inline">{t('move')}</span>}
|
||||
</Button>
|
||||
<ContextMenu ref={moveMenu.menuRef} isOpen={moveMenuOpen} position={moveMenu.contextMenu.position} onClose={closeMoveMenu} label={t('move_to')}>
|
||||
<div className="w-64 max-w-full" data-reader-move-menu="">
|
||||
|
||||
Reference in New Issue
Block a user