Skip to content

Commit 22dfb33

Browse files
[WC-2583]: Popup Hover leave (#1170)
2 parents 98deb17 + db45852 commit 22dfb33

File tree

9 files changed

+64
-21
lines changed

9 files changed

+64
-21
lines changed

packages/pluggableWidgets/popup-menu-web/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- We added a new 'Close on' setting to customize the popup's closing behavior, allowing it to close when clicked or hovered outside the popup.
12+
13+
### Breaking changes
14+
15+
- The default behavior for closing a popup set to open on 'hover' is now 'hover leave'.
16+
917
## [3.5.3] - 2024-01-08
1018

1119
### Fixed

packages/pluggableWidgets/popup-menu-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@mendix/popup-menu-web",
33
"widgetName": "PopupMenu",
4-
"version": "3.5.3",
4+
"version": "3.6.0",
55
"copyright": "© Mendix Technology BV 2023. All rights reserved.",
66
"license": "Apache-2.0",
77
"repository": {

packages/pluggableWidgets/popup-menu-web/src/PopupMenu.editorConfig.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export function getProperties(
8181
transformGroupsIntoTabs(defaultProperties);
8282
}
8383

84+
if (values.trigger !== "onhover") {
85+
hidePropertyIn(defaultProperties, values, "hoverCloseOn");
86+
}
87+
8488
return defaultProperties;
8589
}
8690

packages/pluggableWidgets/popup-menu-web/src/PopupMenu.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@
9191
<enumerationValue key="onhover">Hover</enumerationValue>
9292
</enumerationValues>
9393
</property>
94+
<property key="hoverCloseOn" type="enumeration" defaultValue="onHoverLeave">
95+
<caption>Close on</caption>
96+
<description />
97+
<enumerationValues>
98+
<enumerationValue key="onClickOutside">Click outside</enumerationValue>
99+
<enumerationValue key="onHoverLeave">Hover leave</enumerationValue>
100+
</enumerationValues>
101+
</property>
94102
<property key="position" type="enumeration" defaultValue="bottom">
95103
<caption>Menu position</caption>
96104
<description>The location of the menu relative to the click area.</description>

packages/pluggableWidgets/popup-menu-web/src/__tests__/Menu.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe("Popup menu", () => {
3030
menuToggle: false,
3131
menuTrigger: createElement("button", null, "Trigger"),
3232
advancedMode: false,
33+
hoverCloseOn: "onClickOutside",
3334
position: "bottom",
3435
basicItems: [
3536
basicItemProps,

packages/pluggableWidgets/popup-menu-web/src/__tests__/PopupMenu.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe("Popup menu", () => {
2626
menuTrigger: createElement("button", null, "Trigger"),
2727
advancedMode: false,
2828
position: "bottom",
29+
hoverCloseOn: "onHoverLeave",
2930
basicItems: [
3031
basicItemProps,
3132
{ itemType: "divider", caption: dynamicValue("Caption"), styleClass: "defaultStyle" }

packages/pluggableWidgets/popup-menu-web/src/components/PopupMenu.tsx

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ export interface PopupMenuProps extends PopupMenuContainerProps {
99
preview?: boolean;
1010
}
1111
export function PopupMenu(props: PopupMenuProps): ReactElement {
12-
const preview = !!props.preview;
13-
const [visibility, setVisibility] = useState(preview && props.menuToggle);
12+
const { preview: isPreview, class: className, menuToggle, menuTrigger, trigger, hoverCloseOn } = props;
13+
const preview = !!isPreview;
14+
const [visibility, setVisibility] = useState(preview && menuToggle);
1415
const triggerRef = useRef<HTMLDivElement>(null);
1516

1617
const handleOnClickTrigger = useCallback(
@@ -22,14 +23,22 @@ export function PopupMenu(props: PopupMenuProps): ReactElement {
2223
[setVisibility]
2324
);
2425

25-
const handleOnHoverTrigger = useCallback(
26+
const handleOnHoverEnter = useCallback(
2627
(e: SyntheticEvent<HTMLElement>): void => {
2728
e.preventDefault();
2829
e.stopPropagation();
2930
setVisibility(true);
3031
},
3132
[setVisibility]
3233
);
34+
const handleOnHoverLeave = useCallback(
35+
(e: SyntheticEvent<HTMLElement>): void => {
36+
e.preventDefault();
37+
e.stopPropagation();
38+
setVisibility(false);
39+
},
40+
[setVisibility]
41+
);
3342

3443
const handleOnClickItem = useCallback((itemAction?: ActionValue): void => {
3544
setVisibility(false);
@@ -40,26 +49,34 @@ export function PopupMenu(props: PopupMenuProps): ReactElement {
4049
setVisibility(false);
4150
}, []);
4251

43-
const onHover =
44-
props.trigger === "onhover" && !preview
45-
? {
46-
onPointerEnter: handleOnHoverTrigger
47-
}
48-
: {};
49-
const onClick =
50-
props.trigger === "onclick" && !preview
51-
? {
52-
onClick: handleOnClickTrigger
53-
}
54-
: {};
52+
let eventHandlers = {};
53+
54+
if (!preview) {
55+
if (trigger === "onhover") {
56+
if (hoverCloseOn === "onHoverLeave") {
57+
eventHandlers = {
58+
onPointerEnter: handleOnHoverEnter,
59+
onPointerLeave: handleOnHoverLeave
60+
};
61+
} else {
62+
eventHandlers = {
63+
onPointerEnter: handleOnHoverEnter
64+
};
65+
}
66+
} else if (trigger === "onclick" && !preview) {
67+
eventHandlers = {
68+
onClick: handleOnClickTrigger
69+
};
70+
}
71+
}
5572

5673
useEffect(() => {
57-
setVisibility(props.menuToggle);
58-
}, [props.menuToggle]);
74+
setVisibility(menuToggle);
75+
}, [menuToggle]);
5976
const open = visibility && triggerRef.current;
6077
return (
61-
<div ref={triggerRef} className={classNames("popupmenu", props.class)} {...onHover} {...onClick}>
62-
<div className={"popupmenu-trigger"}>{props.menuTrigger}</div>
78+
<div ref={triggerRef} className={classNames("popupmenu", className)} {...eventHandlers}>
79+
<div className={"popupmenu-trigger"}>{menuTrigger}</div>
6380
{open ? (
6481
<Menu
6582
{...props}

packages/pluggableWidgets/popup-menu-web/src/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<package xmlns="http://www.mendix.com/package/1.0/">
3-
<clientModule name="PopupMenu" version="3.5.3" xmlns="http://www.mendix.com/clientModule/1.0/">
3+
<clientModule name="PopupMenu" version="3.6.0" xmlns="http://www.mendix.com/clientModule/1.0/">
44
<widgetFiles>
55
<widgetFile path="PopupMenu.xml" />
66
</widgetFiles>

packages/pluggableWidgets/popup-menu-web/typings/PopupMenuProps.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export interface CustomItemsType {
2626

2727
export type TriggerEnum = "onclick" | "onhover";
2828

29+
export type HoverCloseOnEnum = "onClickOutside" | "onHoverLeave";
30+
2931
export type PositionEnum = "left" | "right" | "top" | "bottom";
3032

3133
export interface BasicItemsPreviewType {
@@ -52,6 +54,7 @@ export interface PopupMenuContainerProps {
5254
basicItems: BasicItemsType[];
5355
customItems: CustomItemsType[];
5456
trigger: TriggerEnum;
57+
hoverCloseOn: HoverCloseOnEnum;
5558
position: PositionEnum;
5659
menuToggle: boolean;
5760
}
@@ -70,6 +73,7 @@ export interface PopupMenuPreviewProps {
7073
basicItems: BasicItemsPreviewType[];
7174
customItems: CustomItemsPreviewType[];
7275
trigger: TriggerEnum;
76+
hoverCloseOn: HoverCloseOnEnum;
7377
position: PositionEnum;
7478
menuToggle: boolean;
7579
}

0 commit comments

Comments
 (0)