Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support cmd+click to open module + decl links in new tab #3187

Merged
merged 6 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/console/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export async function navigateToDecl(page: Page, moduleName: string, declName: s
await navigateToModule(page, moduleName)

// Navigate to the decl page
await page.locator(`div#decl-${declName}`).click()
await page.locator(`a#decl-${declName}`).click()
await expect(page).toHaveURL(new RegExp(`/modules/${moduleName}/verb/${declName}`))
}
28 changes: 16 additions & 12 deletions frontend/console/src/components/List.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import { ArrowRight01Icon } from 'hugeicons-react'
import { Link } from 'react-router-dom'
import { classNames } from '../utils'

type ListProps<T> = {
items: T[]
renderItem: (item: T) => React.ReactNode
onClick?: (item: T) => void
href?: (item: T) => string
className?: string
}

export const List = <T,>({ items, renderItem, onClick, className }: ListProps<T>) => {
export const List = <T,>({ items, renderItem, href, className }: ListProps<T>) => {
const baseClasses = 'relative flex justify-between items-center gap-x-4 p-4'
return (
<ul className={classNames('divide-y divide-gray-100 dark:divide-gray-700 overflow-hidden', className)}>
{items.map((item, index) => (
<li
key={index}
className={`relative flex justify-between items-center gap-x-4 p-4 ${onClick ? 'cursor-pointer hover:bg-gray-100/50 dark:hover:bg-gray-700/50' : ''}`}
onClick={onClick ? () => onClick(item) : undefined}
>
{renderItem(item)}
{onClick && <ArrowRight01Icon className='size-5 text-gray-400' />}
</li>
))}
{items.map((item, index) =>
href ? (
<Link key={index} className={`${baseClasses} cursor-pointer hover:bg-gray-100/50 dark:hover:bg-gray-700/50`} to={href(item)}>
{renderItem(item)}
<ArrowRight01Icon className='size-5 text-gray-400' />
</Link>
) : (
<li key={index} className={baseClasses}>
{renderItem(item)}
</li>
),
)}
</ul>
)
}
8 changes: 2 additions & 6 deletions frontend/console/src/features/modules/ModulesPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useNavigate } from 'react-router-dom'
import { useModules } from '../../api/modules/use-modules'
import { AttributeBadge } from '../../components'
import { List } from '../../components/List'
Expand All @@ -8,17 +7,14 @@ import { deploymentTextColor } from '../deployments/deployment.utils'

export const ModulesPanel = () => {
const modules = useModules()
const navigate = useNavigate()

const handleModuleClick = (module: Module) => {
navigate(`/modules/${module.name}`)
}
const moduleHref = (module: Module) => `/modules/${module.name}`

return (
<div className='p-2'>
<List
items={modules.data?.modules ?? []}
onClick={handleModuleClick}
href={moduleHref}
renderItem={(module) => (
<div className='flex w-full' data-module-row={module.name}>
<div className='flex gap-x-4 items-center w-1/2'>
Expand Down
45 changes: 17 additions & 28 deletions frontend/console/src/features/modules/ModulesTree.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ArrowRight01Icon, ArrowShrink02Icon, CircleArrowRight02Icon, FileExportIcon, PackageIcon } from 'hugeicons-react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { Link, useParams, useSearchParams } from 'react-router-dom'
import { Multiselect, sortMultiselectOpts } from '../../components/Multiselect'
import type { MultiselectOpt } from '../../components/Multiselect'
import { classNames } from '../../utils'
Expand All @@ -24,7 +24,6 @@ const ExportedIcon = () => (
)

const DeclNode = ({ decl, href, isSelected }: { decl: DeclInfo; href: string; isSelected: boolean }) => {
const navigate = useNavigate()
const declRef = useRef<HTMLDivElement>(null)

// Scroll to the selected decl on page load
Expand All @@ -41,22 +40,19 @@ const DeclNode = ({ decl, href, isSelected }: { decl: DeclInfo; href: string; is
const Icon = useMemo(() => declIcon(decl.declType), [decl.declType])
return (
<li className='my-1'>
<div
ref={declRef}
id={`decl-${decl.value.name}`}
className={classNames(
isSelected ? 'bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-gray-600' : 'hover:bg-gray-200 hover:dark:bg-gray-700',
'group flex items-center gap-x-2 pl-4 pr-2 text-sm font-light leading-6 w-full cursor-pointer scroll-mt-10',
)}
onClick={(e) => {
e.preventDefault()
navigate(href)
}}
>
<Icon aria-hidden='true' className='size-4 shrink-0 ml-3' />
{decl.value.name}
{declSumTypeIsExported(decl.value) ? <ExportedIcon /> : []}
</div>
<Link id={`decl-${decl.value.name}`} to={href}>
<div
ref={declRef}
className={classNames(
isSelected ? 'bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-gray-600' : 'hover:bg-gray-200 hover:dark:bg-gray-700',
'group flex items-center gap-x-2 pl-4 pr-2 text-sm font-light leading-6 w-full cursor-pointer scroll-mt-10',
)}
>
<Icon aria-hidden='true' className='size-4 shrink-0 ml-3' />
{decl.value.name}
{declSumTypeIsExported(decl.value) ? <ExportedIcon /> : []}
</div>
</Link>
</li>
)
}
Expand All @@ -68,7 +64,6 @@ const ModuleSection = ({
selectedDeclTypes,
}: { module: ModuleTreeItem; isExpanded: boolean; toggleExpansion: (m: string) => void; selectedDeclTypes: MultiselectOpt[] }) => {
const { moduleName, declName } = useParams()
const navigate = useNavigate()
const isSelected = useMemo(() => moduleName === module.name, [moduleName, module.name])
const moduleRef = useRef<HTMLDivElement>(null)

Expand Down Expand Up @@ -98,15 +93,9 @@ const ModuleSection = ({
>
<PackageIcon aria-hidden='true' className='size-4 my-1 ml-3 shrink-0' />
{module.name}
<CircleArrowRight02Icon
id={`module-${module.name}-view-icon`}
className='size-4 shrink-0 rounded-md hover:bg-gray-200 dark:hover:bg-gray-600'
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
navigate(`/modules/${module.name}`)
}}
/>
<Link to={`/modules/${module.name}`} onClick={(e) => e.stopPropagation()}>
<CircleArrowRight02Icon id={`module-${module.name}-view-icon`} className='size-4 shrink-0 rounded-md hover:bg-gray-200 dark:hover:bg-gray-600' />
</Link>
{filteredDecls.length === 0 || (
<ArrowRight01Icon aria-hidden='true' className={`ml-auto mr-2 h-4 w-4 shrink-0 ${isExpanded ? 'rotate-90 text-gray-500' : ''}`} />
)}
Expand Down
9 changes: 4 additions & 5 deletions frontend/console/src/features/modules/decls/DeclLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { useStreamModules } from '../../../api/modules/use-stream-modules'
import { classNames } from '../../../utils'
import { getTreeWidthFromLS } from '../module.utils'
Expand Down Expand Up @@ -60,14 +60,13 @@ export const DeclLink = ({
if (!moduleName || !declName) {
return
}
const navigate = useNavigate()
const modules = useStreamModules()
const decl = useMemo(
() => (moduleName && !!modules?.data ? declSchemaFromModules(moduleName, declName, modules?.data) : undefined),
[moduleName, declName, modules?.data],
)
const [isHovering, setIsHovering] = useState(false)
const linkRef = useRef<HTMLSpanElement>(null)
const linkRef = useRef<HTMLAnchorElement>(null)

const str = moduleName && slim !== true ? `${moduleName}.${declName}` : declName

Expand All @@ -81,9 +80,9 @@ export const DeclLink = ({
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
>
<span ref={linkRef} className={textColors} onClick={() => navigate(`/modules/${moduleName}/${decl.declType}/${declName}`)}>
<Link ref={linkRef} className={textColors} to={`/modules/${moduleName}/${decl.declType}/${declName}`}>
{str}
</span>
</Link>
{!slim && (
<SnippetContainer
decl={decl}
Expand Down
Loading