diff --git a/frontends/ui/src/adapters/ui/icons.tsx b/frontends/ui/src/adapters/ui/icons.tsx index 14db1d57..54c48d49 100644 --- a/frontends/ui/src/adapters/ui/icons.tsx +++ b/frontends/ui/src/adapters/ui/icons.tsx @@ -111,6 +111,7 @@ export const Copy = createIcon('copy-generic') export const Download = createIcon('download') export const Upload = createIcon('upload') export const Share = createIcon('share') +export const OpenExternal = createIcon('open-external') export const Refresh = createIcon('refresh') // --------------------------------------------------------------------------- diff --git a/frontends/ui/src/features/layout/components/AppBar.spec.tsx b/frontends/ui/src/features/layout/components/AppBar.spec.tsx index 536cf45a..017d6bbb 100644 --- a/frontends/ui/src/features/layout/components/AppBar.spec.tsx +++ b/frontends/ui/src/features/layout/components/AppBar.spec.tsx @@ -10,6 +10,7 @@ import { AppBar } from './AppBar' const mockToggleSessionsPanel = vi.fn() const mockOpenRightPanel = vi.fn() const mockCloseRightPanel = vi.fn() +const mockSetTheme = vi.fn() vi.mock('../store', () => ({ useLayoutStore: () => ({ @@ -17,6 +18,8 @@ vi.mock('../store', () => ({ rightPanel: null, openRightPanel: mockOpenRightPanel, closeRightPanel: mockCloseRightPanel, + theme: 'system', + setTheme: mockSetTheme, }), })) @@ -66,7 +69,7 @@ describe('AppBar', () => { expect(screen.getByRole('button', { name: /create new session/i })).toBeDisabled() expect(screen.getByRole('button', { name: /toggle sessions sidebar/i })).toBeDisabled() expect(screen.getByRole('button', { name: /add data sources/i })).toBeDisabled() - expect(screen.getByRole('button', { name: /open settings/i })).toBeDisabled() + expect(screen.getByRole('button', { name: /open documentation/i })).not.toBeDisabled() }) test('enables action buttons when authenticated', () => { @@ -75,7 +78,7 @@ describe('AppBar', () => { expect(screen.getByRole('button', { name: /create new session/i })).not.toBeDisabled() expect(screen.getByRole('button', { name: /toggle sessions sidebar/i })).not.toBeDisabled() expect(screen.getByRole('button', { name: /add data sources/i })).not.toBeDisabled() - expect(screen.getByRole('button', { name: /open settings/i })).not.toBeDisabled() + expect(screen.getByRole('button', { name: /open documentation/i })).not.toBeDisabled() }) test('calls onNewSession when logo button clicked', async () => { @@ -117,16 +120,6 @@ describe('AppBar', () => { expect(mockOpenRightPanel).toHaveBeenCalledWith('data-sources') }) - test('opens settings panel when Settings clicked', async () => { - const user = userEvent.setup() - - render() - - await user.click(screen.getByRole('button', { name: /open settings/i })) - - expect(mockOpenRightPanel).toHaveBeenCalledWith('settings') - }) - test('renders Docs button that opens in new tab', () => { render() @@ -168,8 +161,9 @@ describe('AppBar', () => { }) await user.click(avatarButton) - // Popover should show "Default User" and info message + // Popover should show "Default User", theme control, and info message expect(screen.getByText('Default User')).toBeInTheDocument() + expect(screen.getByRole('radiogroup', { name: /theme/i })).toBeInTheDocument() expect(screen.getByText('Authentication Not Configured')).toBeInTheDocument() }) @@ -193,7 +187,7 @@ describe('AppBar', () => { expect(screen.getByRole('button', { name: /create new session/i })).not.toBeDisabled() expect(screen.getByRole('button', { name: /toggle sessions sidebar/i })).not.toBeDisabled() expect(screen.getByRole('button', { name: /add data sources/i })).not.toBeDisabled() - expect(screen.getByRole('button', { name: /open settings/i })).not.toBeDisabled() + expect(screen.getByRole('button', { name: /open documentation/i })).not.toBeDisabled() }) test('shows session title when auth is disabled', () => { diff --git a/frontends/ui/src/features/layout/components/AppBar.tsx b/frontends/ui/src/features/layout/components/AppBar.tsx index 6919c343..d11124df 100644 --- a/frontends/ui/src/features/layout/components/AppBar.tsx +++ b/frontends/ui/src/features/layout/components/AppBar.tsx @@ -5,7 +5,7 @@ * AppBar Component * * Top navigation bar with menu toggle, logo, session title, - * and action buttons (Add Sources, Settings, Docs, User Avatar). + * and action buttons (Add Sources, Docs, User Avatar). * * Shows different states based on authentication: * - Auth disabled: Default User avatar with info tooltip (no sign in/out) @@ -15,10 +15,11 @@ 'use client' -import { type FC, useCallback, useState } from 'react' +import { type CSSProperties, type FC, useCallback, useState } from 'react' import { Flex, Text, Button, Logo, Avatar, Popover, Divider } from '@/adapters/ui' -import { Menu, Globe, Settings, Book, Lock, Logout, ChevronRight, Info } from '@/adapters/ui/icons' +import { Menu, Globe, Book, Lock, Logout, OpenExternal, Info, Moon, Sun } from '@/adapters/ui/icons' import { useLayoutStore } from '../store' +import type { ThemeMode } from '../types' interface AppBarProps { /** Current session title to display */ @@ -43,6 +44,17 @@ interface AppBarProps { onSignOut?: () => void } +/** Transparent popover shell with outline only (no KUI fill/shadow). */ +const USER_MENU_POPOVER_CLASS = + '!bg-transparent !p-0 !shadow-none rounded-[var(--radius-md)] border border-base text-primary' + +const USER_MENU_POPOVER_STYLE: CSSProperties = { + backgroundColor: 'transparent', + boxShadow: 'none', + padding: 0, + marginTop: 4, +} + /** * Main navigation bar at the top of the application. * Controls sidebar toggles and navigation actions. @@ -74,15 +86,6 @@ export const AppBar: FC = ({ } }, [rightPanel, openRightPanel, closeRightPanel, isAuthenticated]) - const handleSettingsClick = useCallback(() => { - if (!isAuthenticated) return - if (rightPanel === 'settings') { - closeRightPanel() - } else { - openRightPanel('settings') - } - }, [rightPanel, openRightPanel, closeRightPanel, isAuthenticated]) - const handleDocsClick = useCallback(() => { window.open('https://github.com/NVIDIA-AI-Blueprints/aiq', '_blank') }, []) @@ -164,21 +167,6 @@ export const AppBar: FC = ({ Data Sources - - - @@ -200,6 +188,8 @@ export const AppBar: FC = ({ onOpenChange={setIsUserMenuOpen} side="bottom" align="end" + className={USER_MENU_POPOVER_CLASS} + style={USER_MENU_POPOVER_STYLE} slotContent={} > + ) + })} + + + ) +} + const UserDropdownContent: FC = ({ user, onSignOut }) => { return ( @@ -291,6 +354,7 @@ const UserDropdownContent: FC = ({ user, onSignOut }) + {/* Sign out button */}