fix: move community link to user menu (#442)
## Summary - Move Community link from sidebar footer into user dropdown above Feedback. - Hide empty sidebar footer nav group. - Add tests for dropdown order and footer removal. ## Tests - `npm test -- --run resources/js/components/user-menu-content.test.tsx resources/js/providers/menu-item-provider.test.ts` Note: `npm run types` currently fails on unrelated existing TypeScript errors.
This commit is contained in:
parent
10da06ed84
commit
4f46ae3e2d
|
|
@ -78,7 +78,9 @@ export function AppSidebar() {
|
|||
</SidebarContent>
|
||||
|
||||
<SidebarFooter>
|
||||
<NavFooter items={footerNavItems} className="mt-auto" />
|
||||
{footerNavItems.length > 0 && (
|
||||
<NavFooter items={footerNavItems} className="mt-auto" />
|
||||
)}
|
||||
<NavUser />
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
import { type User } from '@/types';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { UserMenuContent } from './user-menu-content';
|
||||
|
||||
vi.mock('@/components/ui/dropdown-menu', () => ({
|
||||
DropdownMenuGroup: ({ children }: { children: ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
DropdownMenuItem: ({ children }: { children: ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
DropdownMenuLabel: ({ children }: { children: ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
DropdownMenuSeparator: () => <hr />,
|
||||
}));
|
||||
|
||||
vi.mock('@/components/user-info', () => ({
|
||||
UserInfo: () => <div>User</div>,
|
||||
}));
|
||||
|
||||
vi.mock('@/contexts/privacy-mode-context', () => ({
|
||||
usePrivacyMode: () => ({
|
||||
isPrivacyModeEnabled: false,
|
||||
togglePrivacyMode: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/hooks/use-mobile-navigation', () => ({
|
||||
useMobileNavigation: () => vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/key-storage', () => ({
|
||||
clearKey: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@inertiajs/react', () => ({
|
||||
Link: ({
|
||||
children,
|
||||
href,
|
||||
...props
|
||||
}: {
|
||||
children: ReactNode;
|
||||
href: string;
|
||||
as?: string;
|
||||
prefetch?: boolean;
|
||||
}) => {
|
||||
delete props.as;
|
||||
delete props.prefetch;
|
||||
|
||||
return (
|
||||
<a href={href} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
router: {
|
||||
flushAll: vi.fn(),
|
||||
},
|
||||
usePage: () => ({
|
||||
props: {
|
||||
version: 'test-version',
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('UserMenuContent', () => {
|
||||
it('shows community above feedback in the user dropdown', () => {
|
||||
const user: User = {
|
||||
id: '0194d20b-2b25-7000-8000-000000000001',
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
currency_code: 'USD',
|
||||
locale: 'en',
|
||||
timezone: 'UTC',
|
||||
email_verified_at: null,
|
||||
created_at: '2026-01-01T00:00:00.000000Z',
|
||||
updated_at: '2026-01-01T00:00:00.000000Z',
|
||||
};
|
||||
|
||||
render(<UserMenuContent user={user} />);
|
||||
|
||||
const community = screen.getByRole('link', { name: /community/i });
|
||||
const feedback = screen.getByRole('link', { name: /feedback/i });
|
||||
|
||||
expect(community.getAttribute('href')).toBe(
|
||||
'https://discord.gg/2WZmDW9QZ8',
|
||||
);
|
||||
expect(community.compareDocumentPosition(feedback)).toBe(
|
||||
Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import DiscordIcon from '@/components/icons/DiscordIcon';
|
||||
import {
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
|
|
@ -92,6 +93,18 @@ export function UserMenuContent({ user }: UserMenuContentProps) {
|
|||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem asChild>
|
||||
<a
|
||||
className="block w-full cursor-pointer"
|
||||
href="https://discord.gg/2WZmDW9QZ8"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={cleanup}
|
||||
>
|
||||
<DiscordIcon className="mr-2 size-4" />
|
||||
{__('Community')}
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<a
|
||||
className="block w-full cursor-pointer"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { footerNavItems } from './menu-item-provider';
|
||||
|
||||
describe('menu item provider', () => {
|
||||
it('does not include community in sidebar footer nav items', () => {
|
||||
expect(footerNavItems).not.toContainEqual(
|
||||
expect.objectContaining({ title: 'Community' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { index as accountsIndex } from '@/actions/App/Http/Controllers/AccountController';
|
||||
import { index as budgetsIndex } from '@/actions/App/Http/Controllers/BudgetController';
|
||||
import { index as transactionsIndex } from '@/actions/App/Http/Controllers/TransactionController';
|
||||
import DiscordIcon from '@/components/icons/DiscordIcon';
|
||||
import { cashflow, dashboard } from '@/routes';
|
||||
import { Features, NavItem } from '@/types';
|
||||
import {
|
||||
|
|
@ -81,11 +80,4 @@ export function getMainNavItems(features: Features, locale: string): NavItem[] {
|
|||
return items;
|
||||
}
|
||||
|
||||
export const footerNavItems: NavItem[] = [
|
||||
{
|
||||
type: 'nav-item',
|
||||
title: 'Community',
|
||||
href: 'https://discord.gg/2WZmDW9QZ8',
|
||||
icon: <DiscordIcon className="size-5" />,
|
||||
},
|
||||
];
|
||||
export const footerNavItems: NavItem[] = [];
|
||||
|
|
|
|||
Loading…
Reference in New Issue