Skip to content

Commit ffcb19e

Browse files
authored
Merge pull request #76 from opf/bump/primer-upstream
Bump/primer upstream
2 parents 37d1cf9 + e2eca87 commit ffcb19e

9 files changed

Lines changed: 114 additions & 7 deletions

File tree

.changeset/cool-meals-appear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@openproject/primer-view-components": patch
3+
---
4+
5+
Ensure Overlays that open dialogs do not close when the Dialog opens
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@openproject/primer-view-components": patch
3+
---
4+
5+
Ensure only direct clicks to the dialog can close it

app/components/primer/dialog_helper.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ function dialogInvokerButtonHandler(event: Event) {
77
// If the user is clicking a valid dialog trigger
88
let dialogId = button?.getAttribute('data-show-dialog-id')
99
if (dialogId) {
10-
event.stopPropagation()
1110
const dialog = document.getElementById(dialogId)
1211
if (dialog instanceof HTMLDialogElement) {
1312
dialog.showModal()
1413
// A buttons default behaviour in some browsers it to send a pointer event
1514
// If the behaviour is allowed through the dialog will be shown but then
1615
// quickly hidden- as if it were never shown. This prevents that.
1716
event.preventDefault()
18-
event.stopPropagation()
1917
}
2018
}
2119

@@ -46,6 +44,20 @@ export class DialogHelperElement extends HTMLElement {
4644
for (const record of records) {
4745
if (record.target === this.dialog) {
4846
this.ownerDocument.body.classList.toggle('has-modal', this.dialog.hasAttribute('open'))
47+
// In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
48+
// opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
49+
// any popover that is currently open, regardless of whether or not another top layer element,
50+
// such as a <dialog> is nested inside.
51+
// See https://github.com/whatwg/html/issues/9998.
52+
// This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers that present this bug,
53+
// we must undo the work they did to hide ancestral popovers of the dialog:
54+
if (this.dialog.hasAttribute('open')) {
55+
let node: HTMLElement | null = this.dialog
56+
while (node) {
57+
node = node.closest('[popover]:not(:popover-open)')
58+
if (node) node.showPopover()
59+
}
60+
}
4961
}
5062
}
5163
}).observe(this, {subtree: true, attributeFilter: ['open']})
@@ -58,11 +70,8 @@ export class DialogHelperElement extends HTMLElement {
5870
handleEvent(event: MouseEvent) {
5971
const target = event.target as HTMLElement
6072
const dialog = this.dialog
61-
if (!dialog?.open) return
62-
63-
// if the target is inside the dialog, but is not the dialog itself, leave
64-
// the dialog open
65-
if (target?.closest('dialog') === dialog && target !== dialog) return
73+
// The click target _must_ be the dialog element itself, and not elements underneath or inside.
74+
if (target !== dialog || !dialog?.open) return
6675

6776
const rect = dialog.getBoundingClientRect()
6877
const clickWasInsideDialog =

previews/primer/alpha/dialog_preview.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,29 @@ def scroll_container(title: "Test Dialog", subtitle: nil, position: :center, siz
248248
visually_hide_title: visually_hide_title
249249
})
250250
end
251+
252+
# @label Dialog inside Overlay
253+
#
254+
# @param title [String] text
255+
# @param subtitle [String] text
256+
# @param size [Symbol] select [small, medium, medium_portrait, large, xlarge]
257+
# @param position [Symbol] select [center, right, left]
258+
# @param position_narrow [Symbol] select [inherit, bottom, fullscreen, left, right]
259+
# @param visually_hide_title [Boolean] toggle
260+
# @param button_text [String] text
261+
# @param body_text [String] text
262+
def dialog_inside_overlay(title: "Test Dialog", subtitle: nil, position: :center, size: :medium, button_text: "Show Dialog", body_text: "Content", position_narrow: :fullscreen, visually_hide_title: false)
263+
render_with_template(locals: {
264+
title: title,
265+
subtitle: subtitle,
266+
position: position,
267+
size: size,
268+
button_text: button_text,
269+
body_text: body_text,
270+
position_narrow: position_narrow,
271+
visually_hide_title: visually_hide_title
272+
})
273+
end
251274
end
252275
end
253276
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<%= render(Primer::Alpha::Overlay.new(title: "An overlay")) do |o| %>
2+
<% o.with_show_button() { "Show overlay" } %>
3+
<% o.with_body() do %>
4+
<%= render(Primer::Alpha::Dialog.new(id: "dialog-one", title: title, position: position, subtitle: subtitle, visually_hide_title: false)) do |d| %>
5+
<% d.with_show_button { button_text } %>
6+
<% d.with_body { body_text} %>
7+
<% end %>
8+
<% end %>
9+
<% end %>

previews/primer/alpha/dialog_preview/nested_dialog.html.erb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,10 @@
1212
<% end %>
1313
<% end %>
1414
<% end %>
15+
16+
<div style="margin-top:2rem">
17+
<%= render(Primer::Beta::Flash.new(scheme: :warning)) do %>
18+
<p>Please be careful nesting dialogs! Note that in this example, opening the second dialog does not close the first.</p>
19+
<p>Closing a dialog while opening a dialog inside, will cause both to be invisible which will lead to undesired effects!</p>
20+
<% end %>
21+
</div>

static/info_arch.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3375,6 +3375,19 @@
33753375
"color-contrast"
33763376
]
33773377
}
3378+
},
3379+
{
3380+
"preview_path": "primer/alpha/dialog/dialog_inside_overlay",
3381+
"name": "dialog_inside_overlay",
3382+
"snapshot": "false",
3383+
"skip_rules": {
3384+
"wont_fix": [
3385+
"region"
3386+
],
3387+
"will_fix": [
3388+
"color-contrast"
3389+
]
3390+
}
33783391
}
33793392
],
33803393
"subcomponents": [

static/previews.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,6 +3186,19 @@
31863186
"color-contrast"
31873187
]
31883188
}
3189+
},
3190+
{
3191+
"preview_path": "primer/alpha/dialog/dialog_inside_overlay",
3192+
"name": "dialog_inside_overlay",
3193+
"snapshot": "false",
3194+
"skip_rules": {
3195+
"wont_fix": [
3196+
"region"
3197+
],
3198+
"will_fix": [
3199+
"color-contrast"
3200+
]
3201+
}
31893202
}
31903203
]
31913204
},

test/system/alpha/dialog_test.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,28 @@ def test_outside_menu_click_does_not_close_dialog
9393
find(".ActionListItem", text: "Avocado").click
9494
assert_selector "dialog[open]"
9595
end
96+
97+
def test_click_events_can_be_added_to_invoker_buttons
98+
# use this preview because it assigns a static ID to the invoker button
99+
visit_preview(:with_header)
100+
101+
page.evaluate_script(<<~JS)
102+
document.querySelector('#dialog-show-my-dialog').addEventListener('click', () => {
103+
window.dialogInvokerClicked = true
104+
})
105+
JS
106+
107+
click_button("Show Dialog")
108+
109+
assert page.evaluate_script("window.dialogInvokerClicked"), "click event was not fired"
110+
end
111+
112+
def test_dialog_inside_overlay_opens_when_clicked
113+
visit_preview(:dialog_inside_overlay)
114+
115+
click_button("Show overlay")
116+
click_button("Show Dialog")
117+
assert_selector "dialog[open]"
118+
end
96119
end
97120
end

0 commit comments

Comments
 (0)