Skip to content

Commit

Permalink
Merge pull request #427 from cytoscape/CW-473
Browse files Browse the repository at this point in the history
Tooltip for duplicated App Menu Items
  • Loading branch information
keiono authored Feb 14, 2025
2 parents 5d78f70 + e9fbb55 commit 280d79c
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/components/TableBrowser/TableBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { useViewModelStore } from '../../store/ViewModelStore'
import { IdType } from '../../models/IdType'
import { useVisualStyleStore } from '../../store/VisualStyleStore'

import { isValidUrl } from '../../utils/is-url'
import { isValidUrl } from '../../utils/url-util'
import {
EditTableColumnForm,
CreateTableColumnForm,
Expand Down
93 changes: 84 additions & 9 deletions src/components/ToolBar/AppMenu/menu-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import { ParameterUiType } from '../../../models/AppModel/ParameterUiType'
import { useAppStore } from '../../../store/AppStore'
import { inputColumnFilterFn } from '../../../models/AppModel/impl'
import { useWorkspaceStore } from '../../../store/WorkspaceStore'
import { getDomain } from '../../../utils/url-util'
import React from 'react'

interface AppMenuItemProps {
handleClose: () => void
handleConfirm: () => Promise<void>
app: ServiceApp
open: boolean
showTooltip?: boolean
}

export const InputColumns = (props: AppMenuItemProps) => {
Expand Down Expand Up @@ -452,10 +454,11 @@ export const AppMenuItem: React.FC<{
handleClose: () => void
handleConfirm: () => Promise<void>
app: ServiceApp
showTooltip?: boolean
}> = (props: AppMenuItemProps) => {
const [openDialog, setOpenDialog] = useState<boolean>(false)

const { handleClose, app } = props
const { handleClose, app, showTooltip } = props
const handleOpenDialog = (): void => {
setOpenDialog(true)
}
Expand All @@ -467,7 +470,32 @@ export const AppMenuItem: React.FC<{

return (
<>
<MenuItem onClick={() => handleOpenDialog()}>{app.name}</MenuItem>
<Tooltip
title={
showTooltip ? (
<Box sx={{ maxWidth: '220px' }}>
<div>
<strong>Hosted at: </strong>
{getDomain(app.url)}
</div>
<div>
<strong>Version: </strong>
{app.version}
</div>
<div>
<strong>Author: </strong>
{app.author}
</div>
</Box>
) : (
''
)
}
arrow
placement="right"
>
<MenuItem onClick={() => handleOpenDialog()}>{app.name}</MenuItem>
</Tooltip>
<AppMenuItemDialog
handleConfirm={props.handleConfirm}
open={openDialog}
Expand Down Expand Up @@ -514,19 +542,66 @@ const path2menu = (

let currentMenuItem: NestedMenuItem = baseMenu
for (let i = 1; i < path.length; i++) {
const item: MenuPathElement = path[i]
const newMenuItem: NestedMenuItem = existingMenuItems[item.name] || {
label: item.name,
const itemName = path[i].name
const isLastItem = i === path.length - 1

const newMenuItem: NestedMenuItem = {
label: itemName,
items: [],
}
if (path.length === i + 1) {
newMenuItem.template = (
template: isLastItem ? (
<AppMenuItem handleClose={() => {}} app={app} handleConfirm={command} />
)
) : undefined,
}
if (currentMenuItem.items === undefined) {
currentMenuItem.items = []
}
const existingDupMenuItems = currentMenuItem.items.filter(
(item) => (item as any).label === itemName,
) as NestedMenuItem[]

const isNameDuplicated = existingDupMenuItems.length > 0
if (isNameDuplicated) {
if (isLastItem) {
// add tooltip for the menu item to be added
newMenuItem.template = (
<AppMenuItem
handleClose={() => {}}
app={app}
handleConfirm={command}
showTooltip={true}
/>
)
// add tooltip for the existing duplicated menu item
existingDupMenuItems.forEach((item) => {
// Ensure item.template is a valid ReactElement before modifying it
if (item.template && React.isValidElement(item.template)) {
const { app, handleConfirm } = item.template
.props as AppMenuItemProps
item.template = (
<AppMenuItem
handleClose={() => {}}
app={app}
handleConfirm={handleConfirm}
showTooltip={true}
/>
)
}
})
currentMenuItem.items.push(newMenuItem as any)
break
}
// Find the duplicated menu item (which is not the last item in original path)
// and follow that path
const intermediateItem = existingDupMenuItems.filter(
(item) =>
!item.hasOwnProperty('template') || item.template === undefined,
) as NestedMenuItem[]

if (intermediateItem.length > 0) {
currentMenuItem = intermediateItem[0]
continue
}
}
currentMenuItem.items.push(newMenuItem as any)
currentMenuItem = newMenuItem
}
Expand Down
3 changes: 2 additions & 1 deletion src/models/AppModel/ServiceMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ServiceMetadata {
serviceInputDefinition?: ServiceInputDefinition
cyWebAction: ServiceAppAction[]
cyWebMenuItem: CyWebMenuItem

author: string
citation:string
parameters: ServiceAppParameter[]
}
6 changes: 0 additions & 6 deletions src/utils/is-url.ts

This file was deleted.

16 changes: 16 additions & 0 deletions src/utils/url-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function isValidUrl(input: string): boolean {
const urlPattern =
/^(https?:\/\/)?(www\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(\/[a-zA-Z0-9-._~:\/?#[\]@!$&'()*+,;=]*)?$/

return urlPattern.test(input)
}

export const getDomain = (url: string): string => {
try {
const parsedUrl = new URL(url);
return `${parsedUrl.protocol}//${parsedUrl.host}/`;
} catch (error) {
console.error("Invalid URL:", error);
return "";
}
};

0 comments on commit 280d79c

Please sign in to comment.