openproject-primer_view_components 0.22.0 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/components/primer/alpha/action_bar_element.js +12 -4
- data/app/components/primer/alpha/action_bar_element.ts +10 -4
- data/app/components/primer/alpha/action_menu/action_menu_element.js +4 -1
- data/app/components/primer/alpha/action_menu/action_menu_element.ts +4 -1
- data/app/components/primer/alpha/banner.html.erb +1 -2
- data/app/components/primer/alpha/banner.rb +7 -0
- data/app/components/primer/alpha/dialog.rb +0 -1
- data/app/components/primer/alpha/modal_dialog.js +3 -0
- data/app/components/primer/alpha/modal_dialog.ts +3 -0
- data/app/components/primer/alpha/toggle_switch.js +2 -2
- data/app/components/primer/alpha/toggle_switch.ts +2 -2
- data/app/components/primer/alpha/tool_tip.js +1 -0
- data/app/components/primer/alpha/tool_tip.ts +1 -0
- data/app/components/primer/beta/clipboard_copy.ts +1 -1
- data/app/components/primer/beta/nav_list.js +2 -0
- data/app/components/primer/beta/nav_list.ts +2 -0
- data/app/components/primer/dialog_helper.js +38 -7
- data/app/components/primer/dialog_helper.ts +42 -7
- data/app/components/primer/focus_group.ts +1 -1
- data/lib/primer/view_components/version.rb +1 -1
- data/previews/primer/alpha/dialog_preview/dialog_inside_overlay.html.erb +20 -0
- data/previews/primer/alpha/dialog_preview/nested_dialog.html.erb +7 -0
- data/previews/primer/alpha/dialog_preview.rb +24 -0
- data/static/info_arch.json +14 -1
- data/static/previews.json +13 -0
- metadata +4 -3
@@ -140,12 +140,20 @@ _ActionBarElement_firstItem_get = function _ActionBarElement_firstItem_get() {
|
|
140
140
|
return foundItem;
|
141
141
|
};
|
142
142
|
_ActionBarElement_showItem = function _ActionBarElement_showItem(index) {
|
143
|
-
this.items[index]
|
144
|
-
__classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index]
|
143
|
+
const item = this.items[index];
|
144
|
+
const menuItem = __classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index];
|
145
|
+
if (!item || !menuItem)
|
146
|
+
return;
|
147
|
+
item.style.setProperty('visibility', 'visible');
|
148
|
+
menuItem.hidden = true;
|
145
149
|
};
|
146
150
|
_ActionBarElement_hideItem = function _ActionBarElement_hideItem(index) {
|
147
|
-
this.items[index]
|
148
|
-
__classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index]
|
151
|
+
const item = this.items[index];
|
152
|
+
const menuItem = __classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index];
|
153
|
+
if (!item || !menuItem)
|
154
|
+
return;
|
155
|
+
item.style.setProperty('visibility', 'hidden');
|
156
|
+
menuItem.hidden = false;
|
149
157
|
};
|
150
158
|
_ActionBarElement_menuItems_get = function _ActionBarElement_menuItems_get() {
|
151
159
|
return this.moreMenu.querySelectorAll('[role="menu"] > li');
|
@@ -145,13 +145,19 @@ class ActionBarElement extends HTMLElement {
|
|
145
145
|
}
|
146
146
|
|
147
147
|
#showItem(index: number) {
|
148
|
-
this.items[index]
|
149
|
-
this.#menuItems[index]
|
148
|
+
const item = this.items[index]
|
149
|
+
const menuItem = this.#menuItems[index]
|
150
|
+
if (!item || !menuItem) return
|
151
|
+
item.style.setProperty('visibility', 'visible')
|
152
|
+
menuItem.hidden = true
|
150
153
|
}
|
151
154
|
|
152
155
|
#hideItem(index: number) {
|
153
|
-
this.items[index]
|
154
|
-
this.#menuItems[index]
|
156
|
+
const item = this.items[index]
|
157
|
+
const menuItem = this.#menuItems[index]
|
158
|
+
if (!item || !menuItem) return
|
159
|
+
item.style.setProperty('visibility', 'hidden')
|
160
|
+
menuItem.hidden = false
|
155
161
|
}
|
156
162
|
|
157
163
|
get #menuItems(): NodeListOf<HTMLElement> {
|
@@ -265,7 +265,9 @@ _ActionMenuElement_potentiallyDisallowActivation = function _ActionMenuElement_p
|
|
265
265
|
return false;
|
266
266
|
if (item.getAttribute('aria-disabled')) {
|
267
267
|
event.preventDefault();
|
268
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
268
269
|
event.stopPropagation();
|
270
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
269
271
|
event.stopImmediatePropagation();
|
270
272
|
return true;
|
271
273
|
}
|
@@ -294,6 +296,7 @@ _ActionMenuElement_isActivation = function _ActionMenuElement_isActivation(event
|
|
294
296
|
};
|
295
297
|
_ActionMenuElement_handleInvokerActivated = function _ActionMenuElement_handleInvokerActivated(event) {
|
296
298
|
event.preventDefault();
|
299
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
297
300
|
event.stopPropagation();
|
298
301
|
if (__classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isOpen).call(this)) {
|
299
302
|
__classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_hide).call(this);
|
@@ -320,7 +323,6 @@ _ActionMenuElement_handleDialogItemActivated = function _ActionMenuElement_handl
|
|
320
323
|
}
|
321
324
|
};
|
322
325
|
// a modal <dialog> element will close all popovers
|
323
|
-
setTimeout(() => __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_show).call(this), 0);
|
324
326
|
dialog.addEventListener('close', handleDialogClose, { signal });
|
325
327
|
dialog.addEventListener('cancel', handleDialogClose, { signal });
|
326
328
|
};
|
@@ -377,6 +379,7 @@ _ActionMenuElement_activateItem = function _ActionMenuElement_activateItem(event
|
|
377
379
|
return;
|
378
380
|
// otherwise, event will not result in activation by default, so we stop it and
|
379
381
|
// simulate a click
|
382
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
380
383
|
event.stopPropagation();
|
381
384
|
const elem = item;
|
382
385
|
elem.click();
|
@@ -141,7 +141,9 @@ export class ActionMenuElement extends HTMLElement {
|
|
141
141
|
|
142
142
|
if (item.getAttribute('aria-disabled')) {
|
143
143
|
event.preventDefault()
|
144
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
144
145
|
event.stopPropagation()
|
146
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
145
147
|
event.stopImmediatePropagation()
|
146
148
|
return true
|
147
149
|
}
|
@@ -260,6 +262,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
260
262
|
|
261
263
|
#handleInvokerActivated(event: Event) {
|
262
264
|
event.preventDefault()
|
265
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
263
266
|
event.stopPropagation()
|
264
267
|
|
265
268
|
if (this.#isOpen()) {
|
@@ -287,7 +290,6 @@ export class ActionMenuElement extends HTMLElement {
|
|
287
290
|
}
|
288
291
|
}
|
289
292
|
// a modal <dialog> element will close all popovers
|
290
|
-
setTimeout(() => this.#show(), 0)
|
291
293
|
dialog.addEventListener('close', handleDialogClose, {signal})
|
292
294
|
dialog.addEventListener('cancel', handleDialogClose, {signal})
|
293
295
|
}
|
@@ -354,6 +356,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
354
356
|
|
355
357
|
// otherwise, event will not result in activation by default, so we stop it and
|
356
358
|
// simulate a click
|
359
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
357
360
|
event.stopPropagation()
|
358
361
|
const elem = item as HTMLElement
|
359
362
|
elem.click()
|
@@ -23,8 +23,7 @@
|
|
23
23
|
scheme: :invisible,
|
24
24
|
icon: :x,
|
25
25
|
aria: { label: @dismiss_label },
|
26
|
-
data: { action: catalyst_action(event: "click", function: "dismiss") }
|
27
|
-
autofocus: true
|
26
|
+
data: { action: catalyst_action(event: "click", function: "dismiss") }
|
28
27
|
)
|
29
28
|
) %>
|
30
29
|
</div>
|
@@ -3,6 +3,13 @@
|
|
3
3
|
module Primer
|
4
4
|
module Alpha
|
5
5
|
# Use `Banner` to highlight important information.
|
6
|
+
#
|
7
|
+
# @accessibility
|
8
|
+
# Depending on the scenario, some Banners may need to receive focus when they appear. This helps to maximize discoverability of the message, especially in critical scenarios. Visit the [Banner's Accessibility section](https://primer.style/components/banner#accessibility) or defer to the accessibility team to determine if your scenario requires focusing the banner.
|
9
|
+
#
|
10
|
+
# To properly focus a banner, add a `tabindex="-1"` to the Banner container, and focus that container (one way is using the [`focus()` API](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus)).
|
11
|
+
#
|
12
|
+
# For more information about the focus management technique, visit the [Accessible Banner Prototype docs](https://github.com/github/accessibility/blob/main/docs/coaching-recommendations/toast-flash-banner/accessible-banner-prototype.md#consideration). This guidance is subject to change.
|
6
13
|
class Banner < Primer::Component
|
7
14
|
status :alpha
|
8
15
|
|
@@ -26,6 +26,7 @@ function clickHandler(event) {
|
|
26
26
|
// If the user is clicking a valid dialog trigger
|
27
27
|
let dialogId = button === null || button === void 0 ? void 0 : button.getAttribute('data-show-dialog-id');
|
28
28
|
if (dialogId) {
|
29
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
29
30
|
event.stopPropagation();
|
30
31
|
const dialog = document.getElementById(dialogId);
|
31
32
|
if (dialog instanceof ModalDialogElement) {
|
@@ -169,11 +170,13 @@ _ModalDialogElement_focusAbortController = new WeakMap(), _ModalDialogElement_in
|
|
169
170
|
case 'Escape':
|
170
171
|
this.close();
|
171
172
|
event.preventDefault();
|
173
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
172
174
|
event.stopPropagation();
|
173
175
|
break;
|
174
176
|
case 'Enter': {
|
175
177
|
const target = event.target;
|
176
178
|
if (target.getAttribute('data-close-dialog-id') === this.id) {
|
179
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
177
180
|
event.stopPropagation();
|
178
181
|
}
|
179
182
|
break;
|
@@ -18,6 +18,7 @@ function clickHandler(event: Event) {
|
|
18
18
|
// If the user is clicking a valid dialog trigger
|
19
19
|
let dialogId = button?.getAttribute('data-show-dialog-id')
|
20
20
|
if (dialogId) {
|
21
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
21
22
|
event.stopPropagation()
|
22
23
|
const dialog = document.getElementById(dialogId)
|
23
24
|
if (dialog instanceof ModalDialogElement) {
|
@@ -170,12 +171,14 @@ export class ModalDialogElement extends HTMLElement {
|
|
170
171
|
case 'Escape':
|
171
172
|
this.close()
|
172
173
|
event.preventDefault()
|
174
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
173
175
|
event.stopPropagation()
|
174
176
|
break
|
175
177
|
case 'Enter': {
|
176
178
|
const target = event.target as HTMLElement
|
177
179
|
|
178
180
|
if (target.getAttribute('data-close-dialog-id') === this.id) {
|
181
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
179
182
|
event.stopPropagation()
|
180
183
|
}
|
181
184
|
break
|
@@ -134,9 +134,9 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
|
|
134
134
|
credentials: 'same-origin',
|
135
135
|
method: 'POST',
|
136
136
|
headers: {
|
137
|
-
'Requested-With': 'XMLHttpRequest'
|
137
|
+
'Requested-With': 'XMLHttpRequest',
|
138
138
|
},
|
139
|
-
body
|
139
|
+
body,
|
140
140
|
});
|
141
141
|
}
|
142
142
|
catch (error) {
|
@@ -163,9 +163,9 @@ class ToggleSwitchElement extends HTMLElement {
|
|
163
163
|
credentials: 'same-origin',
|
164
164
|
method: 'POST',
|
165
165
|
headers: {
|
166
|
-
'Requested-With': 'XMLHttpRequest'
|
166
|
+
'Requested-With': 'XMLHttpRequest',
|
167
167
|
},
|
168
|
-
body
|
168
|
+
body,
|
169
169
|
})
|
170
170
|
} catch (error) {
|
171
171
|
throw new Error('A network error occurred, please try again.')
|
@@ -310,6 +310,7 @@ class ToolTipElement extends HTMLElement {
|
|
310
310
|
const isOpeningOtherPopover = event.type === 'beforetoggle' && event.currentTarget !== this;
|
311
311
|
const shouldHide = isMouseLeaveFromButton || isEscapeKeydown || isMouseDownOnButton || isOpeningOtherPopover;
|
312
312
|
if (showing && isEscapeKeydown) {
|
313
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
313
314
|
event.stopImmediatePropagation();
|
314
315
|
event.preventDefault();
|
315
316
|
}
|
@@ -316,6 +316,7 @@ class ToolTipElement extends HTMLElement {
|
|
316
316
|
const shouldHide = isMouseLeaveFromButton || isEscapeKeydown || isMouseDownOnButton || isOpeningOtherPopover
|
317
317
|
|
318
318
|
if (showing && isEscapeKeydown) {
|
319
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
319
320
|
event.stopImmediatePropagation()
|
320
321
|
event.preventDefault()
|
321
322
|
}
|
@@ -77,6 +77,7 @@ let NavListElement = class NavListElement extends HTMLElement {
|
|
77
77
|
else {
|
78
78
|
this.expandItem(button);
|
79
79
|
}
|
80
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
80
81
|
e.stopPropagation();
|
81
82
|
}
|
82
83
|
// collapse item
|
@@ -97,6 +98,7 @@ let NavListElement = class NavListElement extends HTMLElement {
|
|
97
98
|
if (this.itemIsExpanded(button) && e.key === 'Escape') {
|
98
99
|
this.collapseItem(button);
|
99
100
|
}
|
101
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
100
102
|
e.stopPropagation();
|
101
103
|
}
|
102
104
|
};
|
@@ -74,6 +74,7 @@ export class NavListElement extends HTMLElement {
|
|
74
74
|
this.expandItem(button)
|
75
75
|
}
|
76
76
|
|
77
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
77
78
|
e.stopPropagation()
|
78
79
|
}
|
79
80
|
|
@@ -96,6 +97,7 @@ export class NavListElement extends HTMLElement {
|
|
96
97
|
this.collapseItem(button)
|
97
98
|
}
|
98
99
|
|
100
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
99
101
|
e.stopPropagation()
|
100
102
|
}
|
101
103
|
|
@@ -11,6 +11,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
11
11
|
};
|
12
12
|
var _DialogHelperElement_abortController;
|
13
13
|
function dialogInvokerButtonHandler(event) {
|
14
|
+
var _a;
|
14
15
|
const target = event.target;
|
15
16
|
const button = target === null || target === void 0 ? void 0 : target.closest('button');
|
16
17
|
if (!button || button.hasAttribute('disabled') || button.getAttribute('aria-disabled') === 'true')
|
@@ -18,7 +19,6 @@ function dialogInvokerButtonHandler(event) {
|
|
18
19
|
// If the user is clicking a valid dialog trigger
|
19
20
|
let dialogId = button === null || button === void 0 ? void 0 : button.getAttribute('data-show-dialog-id');
|
20
21
|
if (dialogId) {
|
21
|
-
event.stopPropagation();
|
22
22
|
const dialog = document.getElementById(dialogId);
|
23
23
|
if (dialog instanceof HTMLDialogElement) {
|
24
24
|
dialog.showModal();
|
@@ -26,7 +26,41 @@ function dialogInvokerButtonHandler(event) {
|
|
26
26
|
// If the behaviour is allowed through the dialog will be shown but then
|
27
27
|
// quickly hidden- as if it were never shown. This prevents that.
|
28
28
|
event.preventDefault();
|
29
|
-
|
29
|
+
// In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
|
30
|
+
// opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
|
31
|
+
// any popover that is currently open, regardless of whether or not another top layer element,
|
32
|
+
// such as a <dialog> is nested inside.
|
33
|
+
// See https://github.com/whatwg/html/issues/9998.
|
34
|
+
// This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers
|
35
|
+
// that present this bug, we must undo the work they did to hide ancestral popovers of the dialog:
|
36
|
+
let node = button;
|
37
|
+
let fixed = false;
|
38
|
+
while (node) {
|
39
|
+
node = (_a = node.parentElement) === null || _a === void 0 ? void 0 : _a.closest('[popover]:not(:popover-open)');
|
40
|
+
if (node && node.popover === 'auto') {
|
41
|
+
node.classList.add('dialog-inside-popover-fix');
|
42
|
+
node.popover = 'manual';
|
43
|
+
node.showPopover();
|
44
|
+
fixed = true;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
if (fixed) {
|
48
|
+
// We need to re-open the dialog as modal, and also ensure no close event listeners
|
49
|
+
// are trying to act on the close
|
50
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
51
|
+
dialog.addEventListener('close', e => e.stopImmediatePropagation(), { once: true });
|
52
|
+
dialog.close();
|
53
|
+
dialog.showModal();
|
54
|
+
dialog.addEventListener('close', () => {
|
55
|
+
for (const el of dialog.ownerDocument.querySelectorAll('.dialog-inside-popover-fix')) {
|
56
|
+
if (el.contains(dialog)) {
|
57
|
+
el.classList.remove('dialog-inside-popover-fix');
|
58
|
+
el.popover = 'auto';
|
59
|
+
el.showPopover();
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}, { once: true });
|
63
|
+
}
|
30
64
|
}
|
31
65
|
}
|
32
66
|
dialogId = button.getAttribute('data-close-dialog-id') || button.getAttribute('data-submit-dialog-id');
|
@@ -65,11 +99,8 @@ export class DialogHelperElement extends HTMLElement {
|
|
65
99
|
handleEvent(event) {
|
66
100
|
const target = event.target;
|
67
101
|
const dialog = this.dialog;
|
68
|
-
|
69
|
-
|
70
|
-
// if the target is inside the dialog, but is not the dialog itself, leave
|
71
|
-
// the dialog open
|
72
|
-
if ((target === null || target === void 0 ? void 0 : target.closest('dialog')) === dialog && target !== dialog)
|
102
|
+
// The click target _must_ be the dialog element itself, and not elements underneath or inside.
|
103
|
+
if (target !== dialog || !(dialog === null || dialog === void 0 ? void 0 : dialog.open))
|
73
104
|
return;
|
74
105
|
const rect = dialog.getBoundingClientRect();
|
75
106
|
const clickWasInsideDialog = rect.top <= event.clientY &&
|
@@ -7,7 +7,6 @@ function dialogInvokerButtonHandler(event: Event) {
|
|
7
7
|
// If the user is clicking a valid dialog trigger
|
8
8
|
let dialogId = button?.getAttribute('data-show-dialog-id')
|
9
9
|
if (dialogId) {
|
10
|
-
event.stopPropagation()
|
11
10
|
const dialog = document.getElementById(dialogId)
|
12
11
|
if (dialog instanceof HTMLDialogElement) {
|
13
12
|
dialog.showModal()
|
@@ -15,7 +14,46 @@ function dialogInvokerButtonHandler(event: Event) {
|
|
15
14
|
// If the behaviour is allowed through the dialog will be shown but then
|
16
15
|
// quickly hidden- as if it were never shown. This prevents that.
|
17
16
|
event.preventDefault()
|
18
|
-
|
17
|
+
|
18
|
+
// In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
|
19
|
+
// opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
|
20
|
+
// any popover that is currently open, regardless of whether or not another top layer element,
|
21
|
+
// such as a <dialog> is nested inside.
|
22
|
+
// See https://github.com/whatwg/html/issues/9998.
|
23
|
+
// This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers
|
24
|
+
// that present this bug, we must undo the work they did to hide ancestral popovers of the dialog:
|
25
|
+
let node: HTMLElement | null | undefined = button
|
26
|
+
let fixed = false
|
27
|
+
while (node) {
|
28
|
+
node = node.parentElement?.closest('[popover]:not(:popover-open)')
|
29
|
+
if (node && node.popover === 'auto') {
|
30
|
+
node.classList.add('dialog-inside-popover-fix')
|
31
|
+
node.popover = 'manual'
|
32
|
+
node.showPopover()
|
33
|
+
fixed = true
|
34
|
+
}
|
35
|
+
}
|
36
|
+
if (fixed) {
|
37
|
+
// We need to re-open the dialog as modal, and also ensure no close event listeners
|
38
|
+
// are trying to act on the close
|
39
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
40
|
+
dialog.addEventListener('close', e => e.stopImmediatePropagation(), {once: true})
|
41
|
+
dialog.close()
|
42
|
+
dialog.showModal()
|
43
|
+
dialog.addEventListener(
|
44
|
+
'close',
|
45
|
+
() => {
|
46
|
+
for (const el of dialog.ownerDocument.querySelectorAll<HTMLElement>('.dialog-inside-popover-fix')) {
|
47
|
+
if (el.contains(dialog)) {
|
48
|
+
el.classList.remove('dialog-inside-popover-fix')
|
49
|
+
el.popover = 'auto'
|
50
|
+
el.showPopover()
|
51
|
+
}
|
52
|
+
}
|
53
|
+
},
|
54
|
+
{once: true},
|
55
|
+
)
|
56
|
+
}
|
19
57
|
}
|
20
58
|
}
|
21
59
|
|
@@ -58,11 +96,8 @@ export class DialogHelperElement extends HTMLElement {
|
|
58
96
|
handleEvent(event: MouseEvent) {
|
59
97
|
const target = event.target as HTMLElement
|
60
98
|
const dialog = this.dialog
|
61
|
-
|
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
|
99
|
+
// The click target _must_ be the dialog element itself, and not elements underneath or inside.
|
100
|
+
if (target !== dialog || !dialog?.open) return
|
66
101
|
|
67
102
|
const rect = dialog.getBoundingClientRect()
|
68
103
|
const clickWasInsideDialog =
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= render(Primer::Alpha::Overlay.new(title: "An overlay", id: "first-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
|
+
<% d.with_footer(show_divider: true) do %>
|
8
|
+
<%= render(Primer::ButtonComponent.new(data: { "close-dialog-id": "dialog-one" })) { "Cancel" } %>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<script type="module">
|
15
|
+
document.getElementById('overlay-show-first-overlay')?.addEventListener('click', e => {
|
16
|
+
setTimeout(() => {
|
17
|
+
document.getElementById('first-overlay').querySelector('button')?.click()
|
18
|
+
});
|
19
|
+
});
|
20
|
+
</script>
|
@@ -12,3 +12,10 @@
|
|
12
12
|
<% end %>
|
13
13
|
<% end %>
|
14
14
|
<% 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>
|
@@ -248,6 +248,30 @@ module Primer
|
|
248
248
|
visually_hide_title: visually_hide_title
|
249
249
|
})
|
250
250
|
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
|
+
# @snapshot interactive
|
263
|
+
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)
|
264
|
+
render_with_template(locals: {
|
265
|
+
title: title,
|
266
|
+
subtitle: subtitle,
|
267
|
+
position: position,
|
268
|
+
size: size,
|
269
|
+
button_text: button_text,
|
270
|
+
body_text: body_text,
|
271
|
+
position_narrow: position_narrow,
|
272
|
+
visually_hide_title: visually_hide_title
|
273
|
+
})
|
274
|
+
end
|
251
275
|
end
|
252
276
|
end
|
253
277
|
end
|
data/static/info_arch.json
CHANGED
@@ -2232,7 +2232,7 @@
|
|
2232
2232
|
{
|
2233
2233
|
"fully_qualified_name": "Primer::Alpha::Banner",
|
2234
2234
|
"description": "Use `Banner` to highlight important information.",
|
2235
|
-
"accessibility_docs":
|
2235
|
+
"accessibility_docs": "Depending on the scenario, some Banners may need to receive focus when they appear. This helps to maximize discoverability of the message, especially in critical scenarios. Visit the [Banner's Accessibility section](https://primer.style/components/banner#accessibility) or defer to the accessibility team to determine if your scenario requires focusing the banner.\n\nTo properly focus a banner, add a `tabindex=\"-1\"` to the Banner container, and focus that container (one way is using the [`focus()` API](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus)).\n\nFor more information about the focus management technique, visit the [Accessible Banner Prototype docs](https://github.com/github/accessibility/blob/main/docs/coaching-recommendations/toast-flash-banner/accessible-banner-prototype.md#consideration). This guidance is subject to change.",
|
2236
2236
|
"is_form_component": false,
|
2237
2237
|
"is_published": true,
|
2238
2238
|
"requires_js": true,
|
@@ -3375,6 +3375,19 @@
|
|
3375
3375
|
"color-contrast"
|
3376
3376
|
]
|
3377
3377
|
}
|
3378
|
+
},
|
3379
|
+
{
|
3380
|
+
"preview_path": "primer/alpha/dialog/dialog_inside_overlay",
|
3381
|
+
"name": "dialog_inside_overlay",
|
3382
|
+
"snapshot": "interactive",
|
3383
|
+
"skip_rules": {
|
3384
|
+
"wont_fix": [
|
3385
|
+
"region"
|
3386
|
+
],
|
3387
|
+
"will_fix": [
|
3388
|
+
"color-contrast"
|
3389
|
+
]
|
3390
|
+
}
|
3378
3391
|
}
|
3379
3392
|
],
|
3380
3393
|
"subcomponents": [
|
data/static/previews.json
CHANGED
@@ -3186,6 +3186,19 @@
|
|
3186
3186
|
"color-contrast"
|
3187
3187
|
]
|
3188
3188
|
}
|
3189
|
+
},
|
3190
|
+
{
|
3191
|
+
"preview_path": "primer/alpha/dialog/dialog_inside_overlay",
|
3192
|
+
"name": "dialog_inside_overlay",
|
3193
|
+
"snapshot": "interactive",
|
3194
|
+
"skip_rules": {
|
3195
|
+
"wont_fix": [
|
3196
|
+
"region"
|
3197
|
+
],
|
3198
|
+
"will_fix": [
|
3199
|
+
"color-contrast"
|
3200
|
+
]
|
3201
|
+
}
|
3189
3202
|
}
|
3190
3203
|
]
|
3191
3204
|
},
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openproject-primer_view_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.22.
|
4
|
+
version: 0.22.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-02-
|
12
|
+
date: 2024-02-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionview
|
@@ -760,6 +760,7 @@ files:
|
|
760
760
|
- previews/primer/alpha/dialog_preview/autofocus_element.html.erb
|
761
761
|
- previews/primer/alpha/dialog_preview/body_has_scrollbar_overflow.html.erb
|
762
762
|
- previews/primer/alpha/dialog_preview/custom_header.html.erb
|
763
|
+
- previews/primer/alpha/dialog_preview/dialog_inside_overlay.html.erb
|
763
764
|
- previews/primer/alpha/dialog_preview/nested_dialog.html.erb
|
764
765
|
- previews/primer/alpha/dialog_preview/scroll_container.html.erb
|
765
766
|
- previews/primer/alpha/dialog_preview/test.html.erb
|
@@ -915,7 +916,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
915
916
|
requirements:
|
916
917
|
- - ">="
|
917
918
|
- !ruby/object:Gem::Version
|
918
|
-
version: 2.
|
919
|
+
version: 2.7.0
|
919
920
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
920
921
|
requirements:
|
921
922
|
- - ">="
|