Skip to content

Commit acdf831

Browse files
authored
Allow ExtendedPopover z-index override to render above modals (#771)
* feat(ExtendedPopover): add zIndex prop for customizable layering Add a zIndex prop to allow users to specify the layering order of the popover. Default to theme.zindex.sticky for backward compatibility. Adjust style handling to incorporate zIndex and prevent blinking effect by ensuring popover is rendered outside viewport when style is empty. * chore(package.json): bump version from 11.2.6 to 11.2.7 to prepare for new release * feat(ExtendedPopover.tsx): add scrollTarget and closeOnScroll props Introduce `scrollTarget` prop to specify a custom scroll container, defaulting to window, enhancing flexibility for modals with internal scroll. Add `closeOnScroll` prop to control scroll event handling, defaulting to true to maintain existing behavior.
1 parent fe8ae47 commit acdf831

3 files changed

Lines changed: 44 additions & 19 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@nordcloud/gnui",
33
"description": "Nordcloud Design System - a collection of reusable React components used in Nordcloud's SaaS products",
4-
"version": "11.2.6",
4+
"version": "11.2.7",
55
"license": "MIT",
66
"repository": {
77
"type": "git",

src/components/extendedPopover/ExtendedPopover.tsx

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ type Props = {
2626
triggerOn?: ExtendedPopoverAction;
2727
closeOn?: ExtendedPopoverAction;
2828
adjustPositionToViewportSize?: boolean;
29+
zIndex?: number;
30+
/** Optional scroll container to listen on; defaults to window. Useful for modals with internal scroll. */
31+
scrollTarget?:
32+
| (EventTarget & {
33+
addEventListener: EventTarget["addEventListener"];
34+
removeEventListener: EventTarget["removeEventListener"];
35+
})
36+
| null;
37+
/** Whether to close/reposition on scroll events. Defaults to true to preserve previous behavior. */
38+
closeOnScroll?: boolean;
2939
};
3040

3141
export function ExtendedPopover({
@@ -42,6 +52,9 @@ export function ExtendedPopover({
4252
triggerOn = "click",
4353
closeOn = "click",
4454
adjustPositionToViewportSize = false,
55+
zIndex = theme.zindex.sticky,
56+
scrollTarget,
57+
closeOnScroll = true,
4558
}: Props) {
4659
const triggerRef = React.useRef<HTMLDivElement>(null);
4760
const contentRef = React.useRef<HTMLDivElement>(null);
@@ -96,7 +109,11 @@ export function ExtendedPopover({
96109
}, [])
97110
);
98111

99-
useEvent({ name: "scroll", handler: handleScroll });
112+
useEvent({
113+
name: "scroll",
114+
handler: closeOnScroll ? handleScroll : null,
115+
target: scrollTarget ?? window,
116+
});
100117

101118
if (content == null) {
102119
return null;
@@ -117,14 +134,17 @@ export function ExtendedPopover({
117134
}),
118135
};
119136

120-
const style = getStyle({
121-
wrapperDimensions: triggerDimensions,
122-
tooltipDimensions: contentDimensions,
123-
viewportDimensions,
124-
placement,
125-
position,
126-
adjustPositionToViewportSize,
127-
});
137+
const style = {
138+
...getStyle({
139+
wrapperDimensions: triggerDimensions,
140+
tooltipDimensions: contentDimensions,
141+
viewportDimensions,
142+
placement,
143+
position,
144+
adjustPositionToViewportSize,
145+
}),
146+
zIndex,
147+
};
128148

129149
return (
130150
<TriggerWrapper ref={triggerRef} {...triggerProps}>
@@ -136,6 +156,7 @@ export function ExtendedPopover({
136156
style={style}
137157
margin={margin}
138158
placement={placement}
159+
zIndex={zIndex}
139160
/>
140161
</TriggerWrapper>
141162
);
@@ -145,9 +166,10 @@ type PopoverProps = {
145166
content: React.ReactNode;
146167
contentRef: React.RefObject<HTMLDivElement>;
147168
visible: boolean;
148-
style: { top?: number; left?: number };
169+
style: { top?: number; left?: number; zIndex?: number };
149170
margin: Margin;
150171
placement: Placement;
172+
zIndex: number;
151173
};
152174

153175
function Popover({
@@ -157,14 +179,17 @@ function Popover({
157179
margin,
158180
content,
159181
placement,
182+
zIndex,
160183
}: PopoverProps) {
161184
// on empty style render popover outside viewport to prevent blinking effect
185+
const positionStyle =
186+
style.top != null && style.left != null
187+
? style
188+
: { ...OUTSIDE_VIEWPORT_STYLE, zIndex };
189+
162190
return visible
163191
? ReactDOM.createPortal(
164-
<ContentWrapper
165-
ref={contentRef}
166-
style={style.top && style.left ? style : OUTSIDE_VIEWPORT_STYLE}
167-
>
192+
<ContentWrapper ref={contentRef} style={positionStyle} zIndex={zIndex}>
168193
<PaddingWrapper {...margin} placement={placement}>
169194
{content}
170195
</PaddingWrapper>
@@ -178,9 +203,9 @@ const TriggerWrapper = styled.div`
178203
display: inline-block;
179204
`;
180205

181-
const ContentWrapper = styled.div`
206+
const ContentWrapper = styled.div<{ zIndex: number }>`
182207
position: fixed;
183-
z-index: ${theme.zindex.sticky};
208+
z-index: ${({ zIndex }) => zIndex};
184209
`;
185210

186211
export type ExtendedPopoverAction = "click" | "hover";

0 commit comments

Comments
 (0)