mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
fix: sort folders in the role assignment dropdown #984
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { FolderSettings } from '../folder-settings';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import type { Mailbox } from '@/lib/jmap/types';
|
||||
|
||||
// #984: the role assignment dropdowns listed folders in raw server order.
|
||||
// They must follow the sidebar order the folder tree produces.
|
||||
|
||||
vi.mock('../settings-section', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('../settings-section')>()),
|
||||
SettingsSection: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
function makeMailbox(overrides: Partial<Mailbox>): Mailbox {
|
||||
return {
|
||||
id: 'mb',
|
||||
name: 'Folder',
|
||||
sortOrder: 0,
|
||||
totalEmails: 0,
|
||||
unreadEmails: 0,
|
||||
totalThreads: 0,
|
||||
unreadThreads: 0,
|
||||
myRights: {
|
||||
mayReadItems: true,
|
||||
mayAddItems: true,
|
||||
mayRemoveItems: true,
|
||||
maySetSeen: true,
|
||||
maySetKeywords: true,
|
||||
mayCreateChild: true,
|
||||
mayRename: true,
|
||||
mayDelete: true,
|
||||
maySubmit: true,
|
||||
},
|
||||
isSubscribed: true,
|
||||
isShared: false,
|
||||
...overrides,
|
||||
} as Mailbox;
|
||||
}
|
||||
|
||||
describe('FolderSettings role dropdown order (#984)', () => {
|
||||
beforeEach(() => {
|
||||
useAuthStore.setState({ client: null } as never);
|
||||
useEmailStore.setState({
|
||||
// Deliberately unsorted: a child before its parent, a plain folder
|
||||
// before the Inbox, and names out of alphabetical order.
|
||||
mailboxes: [
|
||||
makeMailbox({ id: 'zeta', name: 'Zeta' }),
|
||||
makeMailbox({ id: 'proj-b', name: 'Beta', parentId: 'projects' }),
|
||||
makeMailbox({ id: 'inbox', name: 'Inbox', role: 'inbox' }),
|
||||
makeMailbox({ id: 'projects', name: 'Projects' }),
|
||||
makeMailbox({ id: 'proj-a', name: 'Alpha', parentId: 'projects' }),
|
||||
makeMailbox({ id: 'alpha', name: 'Alpha' }),
|
||||
],
|
||||
fetchMailboxes: vi.fn(),
|
||||
} as never);
|
||||
});
|
||||
|
||||
it('lists options in folder-tree order, children under their parent', () => {
|
||||
render(<FolderSettings />);
|
||||
|
||||
const selects = screen.getAllByRole('combobox');
|
||||
expect(selects.length).toBeGreaterThan(0);
|
||||
const values = within(selects[0] as HTMLElement)
|
||||
.getAllByRole('option')
|
||||
.map((o) => (o as HTMLOptionElement).value)
|
||||
.filter((v) => v !== '');
|
||||
|
||||
expect(values).toEqual(['inbox', 'alpha', 'projects', 'proj-a', 'proj-b', 'zeta']);
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
AlertTriangle, NotebookPen, CalendarClock, BellOff,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import { cn, buildMailboxTree, type MailboxNode } from '@/lib/utils';
|
||||
import { cn, buildMailboxTree, flattenMailboxTree, type MailboxNode } from '@/lib/utils';
|
||||
import {
|
||||
flattenVisibleTree, removeSubtree, getProjection, getDropPlan,
|
||||
type FlatFolder,
|
||||
@@ -670,9 +670,13 @@ export function FolderSettings() {
|
||||
{/* Standard Folder Roles - advanced section */}
|
||||
<SettingsSection title={t('standard_roles')} description={t('standard_roles_description')}>
|
||||
{STANDARD_ROLES.map((role) => {
|
||||
// Offer the folders in sidebar order (the built tree: role priority,
|
||||
// then name, children under their parent) rather than raw server
|
||||
// order, so the dropdown reads like the folder list. (#984)
|
||||
const orderedMailboxes = flattenMailboxTree(folderTree);
|
||||
// Disambiguate duplicate folder names by appending parent path
|
||||
const nameCounts = new Map<string, number>();
|
||||
ownMailboxes.forEach(mb => nameCounts.set(mb.name, (nameCounts.get(mb.name) || 0) + 1));
|
||||
orderedMailboxes.forEach(mb => nameCounts.set(mb.name, (nameCounts.get(mb.name) || 0) + 1));
|
||||
const getParentPath = (mb: { parentId?: string; name: string }) => {
|
||||
if (!mb.parentId) return '';
|
||||
const parent = ownMailboxes.find(p => p.id === mb.parentId);
|
||||
@@ -686,7 +690,7 @@ export function FolderSettings() {
|
||||
onChange={(value) => handleRoleChange(role, value)}
|
||||
options={[
|
||||
{ value: '', label: t('role_none') },
|
||||
...ownMailboxes.map(mb => ({
|
||||
...orderedMailboxes.map(mb => ({
|
||||
value: mb.id,
|
||||
label: (nameCounts.get(mb.name) || 0) > 1
|
||||
? `${getParentPath(mb)}${mb.name} (${mb.id.slice(-6)})`
|
||||
|
||||
Reference in New Issue
Block a user