feat(web): convert navbar profile pill into a dropdown menu
The profile pill in the top nav was a static `<div>` showing the
avatar + name + role with no way to reach the dashboard, profile
or logout from the desktop layout — testers reported "không có
dropdown dashboard" after login.
Changes to `components/design-system/navbar.tsx`:
* The pill is now a `<button>` that toggles an absolutely-positioned
menu (right-aligned, `z-popover`, elevation-3 shadow). A chevron
rotates to indicate state.
* Outside-click and Escape close the menu (effect listens only while
the menu is open).
* The menu has:
- A header card with the bigger avatar + full name + email/phone.
- Dashboard / Admin entry (icon depends on role) — replaces the
separate green dashboard button that used to live to the right
of the pill.
- Profile entry → `profileHref`.
- Divider, then a destructive "Đăng xuất" button calling `onLogout`.
* Each link uses the existing `renderLink` slot so framework-specific
Link components (Next.js / next-intl) keep working, and they close
the menu on click.
Tests updated: the dashboard / admin assertions now click the
trigger to open the menu, then look for `role="menuitem"` entries.
All 16 navbar tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -99,24 +99,31 @@ describe('Navbar', () => {
|
||||
expect(screen.getAllByText('Nguyễn Văn A').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('renders dashboard button for authenticated user', () => {
|
||||
it('renders dashboard menu item for authenticated user (after opening dropdown)', () => {
|
||||
render(
|
||||
<Navbar
|
||||
{...defaultProps}
|
||||
user={{ fullName: 'Nguyễn Văn A', role: 'BUYER' }}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('Quản lý')).toBeInTheDocument();
|
||||
// The pill is the dropdown trigger; click it to reveal the menu.
|
||||
const trigger = screen.getByRole('button', { name: /Nguyễn Văn A/ });
|
||||
fireEvent.click(trigger);
|
||||
expect(screen.getByRole('menuitem', { name: /Quản lý/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders admin label for ADMIN role', () => {
|
||||
it('renders admin label as a role badge AND in the dropdown for ADMIN role', () => {
|
||||
render(
|
||||
<Navbar
|
||||
{...defaultProps}
|
||||
user={{ fullName: 'Admin User', role: 'ADMIN' }}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('Quản trị')).toBeInTheDocument();
|
||||
// Role badge in the trigger pill is always visible.
|
||||
expect(screen.getByText('Quản trị viên')).toBeInTheDocument();
|
||||
// After opening, the ADMIN-specific menu item shows.
|
||||
fireEvent.click(screen.getByRole('button', { name: /Admin User/ }));
|
||||
expect(screen.getByRole('menuitem', { name: /Quản trị/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows moon icon in light theme', () => {
|
||||
|
||||
Reference in New Issue
Block a user