Skip to content

Commit

Permalink
Ensure double click event is not ignored in the browser tree
Browse files Browse the repository at this point in the history
  • Loading branch information
yogeshmahajan-1903 committed Jan 15, 2025
1 parent 99e1f00 commit 0978abf
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSingleAndDoubleClick } from '../../../custom_hooks';
import * as React from 'react';
import PropTypes from 'prop-types';
import CustomPropTypes from '../../../../js/custom_prop_types';

export default function DoubleClickHandler({onSingleClick, onDoubleClick, children}){
const onClick = useSingleAndDoubleClick(onSingleClick, onDoubleClick) ;
return(
<div onClick={onClick}>
{children}
</div>
);
}
DoubleClickHandler.propTypes = {
onSingleClick: PropTypes.func,
onDoubleClick: PropTypes.func,
children: CustomPropTypes.children
};
13 changes: 6 additions & 7 deletions web/pgadmin/static/js/components/PgTree/FileTreeItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import * as React from 'react';
import { ClasslistComposite } from 'aspen-decorations';
import { Directory, FileEntry, IItemRendererProps, ItemType, RenamePromptHandle, FileType, FileOrDir} from 'react-aspen';
import {IFileTreeXTriggerEvents, FileTreeXEvent } from '../types';
import _ from 'lodash';
import { Notificar } from 'notificar';

import _ from 'lodash';
import DoubleClickHandler from './DoubleClickHandler';
interface IItemRendererXProps {
/**
* In this implementation, decoration are null when item is `PromptHandle`
Expand Down Expand Up @@ -58,7 +58,6 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen

public render() {
const { item, itemType, decorations } = this.props;

const isRenamePrompt = itemType === ItemType.RenamePrompt;
const isNewPrompt = itemType === ItemType.NewDirectoryPrompt || itemType === ItemType.NewFilePrompt;
const isDirExpanded = itemType === ItemType.Directory
Expand Down Expand Up @@ -93,7 +92,6 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
data-depth={item.depth}
onContextMenu={this.handleContextMenu}
onClick={this.handleClick}
onDoubleClick={this.handleDoubleClick}
onDragStart={this.handleDragStartItem}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
Expand All @@ -107,8 +105,8 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
: null
}

<span className='file-label'>
{
<DoubleClickHandler onDoubleClick={this.handleDoubleClick} onSingleClick={this.handleClick} >
<span className='file-label'>{
item._metadata?.data?.icon ?
<i className={cn('file-icon', item._metadata?.data?.icon ? item._metadata.data.icon : fileOrDir)} /> : null
}
Expand All @@ -121,7 +119,8 @@ export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRen
{tag.text}
</div>
))}
</span>
</span>
</DoubleClickHandler>
</div>);
}

Expand Down
27 changes: 27 additions & 0 deletions web/pgadmin/static/js/custom_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,33 @@ export function useInterval(callback, delay) {
}, [delay]);
}

/* React hook for handling double and single click events */
export function useSingleAndDoubleClick(handleSingleClick, handleDoubleClick, delay = 250) {
const [state, setState] = useState({ click: 0, props: undefined });

useEffect(() => {
const timer = setTimeout(() => {
// simple click
if (state.click === 1){
handleSingleClick(state.props);
setState({ click: 0, props: state.props });
}
}, delay);

if (state.click === 2) {
handleDoubleClick(state.props);
setState({ click: 0, props: state.props });
}

return () => clearTimeout(timer);
}, [state, handleSingleClick, handleDoubleClick, delay ]);

return (props) => {
setState((prevState) => ({ click: prevState.click + 1, props }));
};
}


export function useDelayedCaller(callback) {
let timer;
useEffect(() => {
Expand Down
43 changes: 22 additions & 21 deletions web/regression/feature_utils/tree_area_locators.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def server_group_node(server_group_name):
@staticmethod
def server_group_node_exp_status(server_group_name):
return "//i[@class='directory-toggle open']/following-sibling::" \
"span//span[starts-with(text(),'%s')]" % server_group_name
"div//span[starts-with(text(),'%s')]" % server_group_name

# Server Node
@staticmethod
Expand All @@ -31,7 +31,7 @@ def server_node(server_name):
@staticmethod
def server_node_exp_status(server_name):
return "//i[@class='directory-toggle open']/following-sibling::" \
"span//span[starts-with(text(),'%s')]" % server_name
"div//span[starts-with(text(),'%s')]" % server_name

# Server Connection
@staticmethod
Expand All @@ -43,36 +43,37 @@ def server_connection_status_element(server_name):
# Databases Node
@staticmethod
def databases_node(server_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='Databases']" % server_name

@staticmethod
def databases_node_exp_status(server_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='Databases']]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[span[text()='Databases']]]/" \
"preceding-sibling::i[@class='directory-toggle open']" \
% server_name

# Database Node
@staticmethod
def database_node(database_name):
return "//div[@data-depth='4']/span/span[text()='%s']" % database_name
return "//div[@data-depth='4']/div/span/span[text()='%s']" \
% database_name

@staticmethod
def database_node_exp_status(database_name):
return "//i[@class='directory-toggle open']/following-sibling::" \
"span//span[text()='%s']" % database_name
"div//span[text()='%s']" % database_name

# Schemas Node
@staticmethod
def schemas_node(database_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='Schemas']" % database_name

@staticmethod
def schemas_node_exp_status(database_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='Schemas']]/" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[span[text()='Schemas']]]/" \
"preceding-sibling::i[@class='directory-toggle open']" \
% database_name

Expand All @@ -85,28 +86,28 @@ def schema_node(schema_name):
@staticmethod
def schema_node_exp_status(schema_name):
return "//i[@class='directory-toggle open']/" \
"following-sibling::span//span[text()='%s']" % schema_name
"following-sibling::div//span[text()='%s']" % schema_name

# Tables Node
@staticmethod
def tables_node(schema_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
return "//div[divdiv[[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='Tables']" % schema_name

@staticmethod
def tables_node_exp_status(schema_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='Tables']]/" \
"following-sibling::div//div[span[span[text()='Tables']]]/" \
"preceding-sibling::i[@class='directory-toggle open']"\
% schema_name

# Schema child
child_node_exp_status = \
"//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[span[text()='%s']]/" \
"//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[span[text()='%s']]]/" \
"preceding-sibling::i[@class='directory-toggle open']"

child_node = "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
child_node = "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//span[text()='%s']"

@staticmethod
Expand All @@ -120,8 +121,8 @@ def schema_child_node(schema_name, child_node_name):

@staticmethod
def schema_child_node_expand_icon_xpath(schema_name, child_node_name):
return "//div[div[span[span[starts-with(text(),'%s')]]]]/" \
"following-sibling::div//span[text()='%s']/../" \
return "//div[div[div[span[span[starts-with(text(),'%s')]]]]]/" \
"following-sibling::div//div[span[text()='%s']]/../" \
"preceding-sibling::i" % (schema_name, child_node_name)

# Database child
Expand All @@ -147,17 +148,17 @@ def server_child_node(server_name, child_node_name):
# Table Node
@staticmethod
def table_node(table_name):
return "//div[@data-depth='8']/span/span[text()='%s']" % table_name
return "//div[@data-depth='8']/div/span/span[text()='%s']" % table_name

# Function Node
@staticmethod
def function_node(table_name):
return "//div[@data-depth='8']/span/span[text()='%s']" % table_name
return "//div[@data-depth='8']/div/span/span[text()='%s']" % table_name

# Role Node
@staticmethod
def role_node(role_name):
return "//div[@data-depth='4']/span/span[text()='%s']" % role_name
return "//div[@data-depth='4']/div/span/span[text()='%s']" % role_name

# Context element option
@staticmethod
Expand Down

0 comments on commit 0978abf

Please sign in to comment.