Skip to content

Commit aabf35a

Browse files
authored
feat: add help menu (#430)
* feat: add help menu * simplify vibe coded part * remove irrelevant test cases
1 parent 0d1f28e commit aabf35a

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { render, screen } from '@testing-library/react'
2+
import userEvent from '@testing-library/user-event'
3+
import { HelpDropdown } from '../help-dropdown'
4+
5+
describe('HelpDropdown', () => {
6+
it('renders documentation link with correct href', async () => {
7+
const user = userEvent.setup()
8+
render(<HelpDropdown />)
9+
10+
const helpButton = screen.getByRole('button', { name: /help/i })
11+
await user.click(helpButton)
12+
13+
const documentationLink = screen.getByRole('menuitem', {
14+
name: /documentation/i,
15+
})
16+
expect(documentationLink).toHaveAttribute(
17+
'href',
18+
'https://github.com/StacklokLabs/toolhive-studio?tab=readme-ov-file#getting-started'
19+
)
20+
expect(documentationLink).toHaveAttribute('target', '_blank')
21+
expect(documentationLink).toHaveAttribute('rel', 'noopener noreferrer')
22+
})
23+
24+
it('renders Discord link with correct href', async () => {
25+
const user = userEvent.setup()
26+
render(<HelpDropdown />)
27+
28+
const helpButton = screen.getByRole('button', { name: /help/i })
29+
await user.click(helpButton)
30+
31+
const discordLink = screen.getByRole('menuitem', {
32+
name: /discord community/i,
33+
})
34+
expect(discordLink).toHaveAttribute('href', 'https://discord.gg/stacklok')
35+
expect(discordLink).toHaveAttribute('target', '_blank')
36+
expect(discordLink).toHaveAttribute('rel', 'noopener noreferrer')
37+
})
38+
39+
it('renders feedback link with correct href', async () => {
40+
const user = userEvent.setup()
41+
render(<HelpDropdown />)
42+
43+
const helpButton = screen.getByRole('button', { name: /help/i })
44+
await user.click(helpButton)
45+
46+
const feedbackLink = screen.getByRole('menuitem', {
47+
name: /send feedback/i,
48+
})
49+
expect(feedbackLink).toHaveAttribute(
50+
'href',
51+
'https://github.com/StacklokLabs/toolhive-studio/issues'
52+
)
53+
expect(feedbackLink).toHaveAttribute('target', '_blank')
54+
expect(feedbackLink).toHaveAttribute('rel', 'noopener noreferrer')
55+
})
56+
57+
it('renders GitHub repository link with correct href', async () => {
58+
const user = userEvent.setup()
59+
render(<HelpDropdown />)
60+
61+
const helpButton = screen.getByRole('button', { name: /help/i })
62+
await user.click(helpButton)
63+
64+
const githubLink = screen.getByRole('menuitem', {
65+
name: /github repository/i,
66+
})
67+
expect(githubLink).toHaveAttribute(
68+
'href',
69+
'https://github.com/StacklokLabs/toolhive-studio'
70+
)
71+
expect(githubLink).toHaveAttribute('target', '_blank')
72+
expect(githubLink).toHaveAttribute('rel', 'noopener noreferrer')
73+
})
74+
})
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { HelpCircle } from 'lucide-react'
2+
import {
3+
DropdownMenu,
4+
DropdownMenuContent,
5+
DropdownMenuItem,
6+
DropdownMenuTrigger,
7+
} from '@/common/components/ui/dropdown-menu'
8+
import { Button } from '@/common/components/ui/button'
9+
import { twMerge } from 'tailwind-merge'
10+
11+
export function HelpDropdown({ className }: { className?: string }) {
12+
return (
13+
<DropdownMenu>
14+
<DropdownMenuTrigger asChild>
15+
<Button
16+
variant="ghost"
17+
size="sm"
18+
className={twMerge('cursor-pointer', className)}
19+
>
20+
<HelpCircle className="text-muted-foreground size-4" />
21+
<span className="sr-only">Help</span>
22+
</Button>
23+
</DropdownMenuTrigger>
24+
<DropdownMenuContent align="end" className="w-48">
25+
<DropdownMenuItem asChild>
26+
<a
27+
href="https://github.com/StacklokLabs/toolhive-studio?tab=readme-ov-file#getting-started"
28+
target="_blank"
29+
rel="noopener noreferrer"
30+
className="cursor-pointer"
31+
>
32+
Documentation
33+
</a>
34+
</DropdownMenuItem>
35+
<DropdownMenuItem asChild>
36+
<a
37+
href="https://discord.gg/stacklok"
38+
target="_blank"
39+
rel="noopener noreferrer"
40+
className="cursor-pointer"
41+
>
42+
Discord Community
43+
</a>
44+
</DropdownMenuItem>
45+
<DropdownMenuItem asChild>
46+
<a
47+
href="https://github.com/StacklokLabs/toolhive-studio/issues"
48+
target="_blank"
49+
rel="noopener noreferrer"
50+
className="cursor-pointer"
51+
>
52+
Send Feedback
53+
</a>
54+
</DropdownMenuItem>
55+
<DropdownMenuItem asChild>
56+
<a
57+
href="https://github.com/StacklokLabs/toolhive-studio"
58+
target="_blank"
59+
rel="noopener noreferrer"
60+
className="cursor-pointer"
61+
>
62+
GitHub Repository
63+
</a>
64+
</DropdownMenuItem>
65+
</DropdownMenuContent>
66+
</DropdownMenu>
67+
)
68+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { HelpDropdown } from './help-dropdown'

renderer/src/common/components/layout/top-nav/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { HTMLProps } from 'react'
22

33
import { ThemeToggle } from '../../theme/theme-toggle'
44
import { SettingsDropdown } from '../../settings/settings-dropdown'
5+
import { HelpDropdown } from '../../help'
56
import { WindowControls } from './window-controls'
67
import {
78
NavigationMenu,
@@ -138,6 +139,7 @@ export function TopNav(props: HTMLProps<HTMLElement>) {
138139
<div className="flex items-center gap-2">
139140
<ThemeToggle className="app-region-no-drag" />
140141
<SettingsDropdown className="app-region-no-drag" />
142+
<HelpDropdown className="app-region-no-drag" />
141143
</div>
142144
</div>
143145

0 commit comments

Comments
 (0)