Skip to content

Commit 7d012aa

Browse files
authored
feat(ui5-popover): support setting "null" for opener (#11995)
Enable setting `null` for the `opener` property of popover-based components, such as: Popover, ResponsivePopover, Menu, UserMenu and more. The change is mainly to improve dev experience in React as it's typical to initialize [Refs](https://react.dev/reference/react/useRef#manipulating-the-dom-with-a-ref) with `null` (e.g., useRef<T>(null)), not undefined. Fixes: #11903 **Note:** With this change, when the opener is `null` or `undefined`, and `open` is set to `true`, the popover will be displayed in the top-left corner. Alternatively, we can change this behavior - to `close` the popover and fire `close` event, but either way, the opener should be fixed. Whether it shows in the top-left (0,0) coordinates or does not show at all, the popover won't work without opener.
1 parent 12866b3 commit 7d012aa

File tree

6 files changed

+53
-12
lines changed

6 files changed

+53
-12
lines changed

packages/fiori/src/UserMenu.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class UserMenu extends UI5Element {
157157
* @default undefined
158158
*/
159159
@property({ converter: DOMReferenceConverter })
160-
opener?: HTMLElement | string;
160+
opener?: HTMLElement | string | null;
161161

162162
/**
163163
* Defines if the User Menu shows the Manage Account option.

packages/main/src/ColorPalettePopover.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class ColorPalettePopover extends UI5Element {
132132
* @since 1.21.0
133133
*/
134134
@property({ converter: DOMReferenceConverter })
135-
opener?: HTMLElement | string;
135+
opener?: HTMLElement | string | null;
136136

137137
/**
138138
* Defines the content of the component.

packages/main/src/Menu.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class Menu extends UI5Element {
235235
* @since 1.10.0
236236
*/
237237
@property({ converter: DOMReferenceConverter })
238-
opener?: HTMLElement | string;
238+
opener?: HTMLElement | string | null;
239239

240240
/**
241241
* Defines the items of this component.

packages/main/src/Popover.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ class Popover extends Popup {
190190
@slot({ type: HTMLElement })
191191
footer!: Array<HTMLElement>;
192192

193-
_opener?: HTMLElement | string;
193+
_opener?: HTMLElement | string | null | undefined;
194194
_openerRect?: DOMRect;
195195
_preventRepositionAndClose?: boolean;
196196
_top?: number;
@@ -216,7 +216,7 @@ class Popover extends Popup {
216216
* @since 1.2.0
217217
*/
218218
@property({ converter: DOMReferenceConverter })
219-
set opener(value: HTMLElement | string) {
219+
set opener(value: HTMLElement | string | null) {
220220
if (this._opener === value) {
221221
return;
222222
}
@@ -228,7 +228,7 @@ class Popover extends Popup {
228228
}
229229
}
230230

231-
get opener(): HTMLElement | string | undefined {
231+
get opener(): HTMLElement | string | null | undefined {
232232
return this._opener;
233233
}
234234

@@ -243,7 +243,7 @@ class Popover extends Popup {
243243
return;
244244
}
245245

246-
if (this.isOpenerOutsideViewport(opener.getBoundingClientRect())) {
246+
if (!opener || this.isOpenerOutsideViewport(opener.getBoundingClientRect())) {
247247
await renderFinished();
248248
this.open = false;
249249
this.fireDecoratorEvent("close");
@@ -290,8 +290,8 @@ class Popover extends Popup {
290290
removeOpenedPopover(this);
291291
}
292292

293-
getOpenerHTMLElement(opener: HTMLElement | string | undefined): HTMLElement | null | undefined {
294-
if (opener === undefined) {
293+
getOpenerHTMLElement(opener: HTMLElement | string | null | undefined): HTMLElement | null | undefined {
294+
if (opener === undefined || opener === null) {
295295
return opener;
296296
}
297297

@@ -375,6 +375,14 @@ class Popover extends Popup {
375375

376376
const opener = this.getOpenerHTMLElement(this.opener);
377377

378+
if (!opener) {
379+
Object.assign(this.style, {
380+
top: `0px`,
381+
left: `0px`,
382+
});
383+
return;
384+
}
385+
378386
if (opener && instanceOfUI5Element(opener) && !opener.getDomRef()) {
379387
return;
380388
}
@@ -393,7 +401,7 @@ class Popover extends Popup {
393401

394402
if (this.open) {
395403
// update opener rect if it was changed during the popover being opened
396-
this._openerRect = opener!.getBoundingClientRect();
404+
this._openerRect = opener.getBoundingClientRect();
397405
}
398406

399407
if (this._oldPlacement && this.shouldCloseDueToNoOpener(this._openerRect!) && this.isFocusWithin()) {

packages/main/src/Tokenizer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ class Tokenizer extends UI5Element {
282282
@property({
283283
converter: DOMReferenceConverter,
284284
})
285-
opener?: HTMLElement;
285+
opener?: HTMLElement | string | null;
286286

287287
/**
288288
* Sets the min-width of the nMore Popover.
@@ -1032,7 +1032,7 @@ class Tokenizer extends UI5Element {
10321032
return this.getSlottedNodes<Token>("tokens");
10331033
}
10341034

1035-
get morePopoverOpener(): HTMLElement {
1035+
get morePopoverOpener(): HTMLElement | string | null {
10361036
// return this.opener ? this : this.opener;
10371037
if (this.opener) {
10381038
return this.opener;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7+
8+
<title>Popover opener null</title>
9+
10+
<script data-ui5-config type="application/json">
11+
{
12+
"language": "EN"
13+
}
14+
</script>
15+
<script src="%VITE_BUNDLE_PATH%" type="module"></script>
16+
<script>
17+
function init() {
18+
const popoverWithoutOpener = document.getElementById("popoverWithoutOpener");
19+
const popoverWithoutOpener2 = document.getElementById("popoverWithoutOpener2");
20+
popoverWithoutOpener.opener = null;
21+
popoverWithoutOpener2.opener = null;
22+
setTimeout(() => {
23+
popoverWithoutOpener2.opener = "btn1";
24+
}, timeout = 1000);
25+
}
26+
</script>
27+
</head>
28+
<body onload="init()">
29+
<ui5-button id="btn1" style="margin-top: 100px">Open Popover</ui5-button>
30+
<ui5-popover id="popoverWithoutOpener" open>Popover</ui5-popover>
31+
<ui5-popover id="popoverWithoutOpener2" open opener="btn1">Popover open</ui5-popover>
32+
</body>
33+
</html>

0 commit comments

Comments
 (0)