openproject-primer_view_components 0.22.0 → 0.22.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/components/primer/alpha/action_bar_element.js +12 -4
  6. data/app/components/primer/alpha/action_bar_element.ts +10 -4
  7. data/app/components/primer/alpha/action_menu/action_menu_element.js +4 -1
  8. data/app/components/primer/alpha/action_menu/action_menu_element.ts +4 -1
  9. data/app/components/primer/alpha/banner.html.erb +1 -2
  10. data/app/components/primer/alpha/banner.rb +7 -0
  11. data/app/components/primer/alpha/dialog.rb +0 -1
  12. data/app/components/primer/alpha/modal_dialog.js +3 -0
  13. data/app/components/primer/alpha/modal_dialog.ts +3 -0
  14. data/app/components/primer/alpha/toggle_switch.js +2 -2
  15. data/app/components/primer/alpha/toggle_switch.ts +2 -2
  16. data/app/components/primer/alpha/tool_tip.js +1 -0
  17. data/app/components/primer/alpha/tool_tip.ts +1 -0
  18. data/app/components/primer/beta/clipboard_copy.ts +1 -1
  19. data/app/components/primer/beta/nav_list.js +2 -0
  20. data/app/components/primer/beta/nav_list.ts +2 -0
  21. data/app/components/primer/dialog_helper.js +38 -7
  22. data/app/components/primer/dialog_helper.ts +42 -7
  23. data/app/components/primer/focus_group.ts +1 -1
  24. data/lib/primer/view_components/version.rb +1 -1
  25. data/previews/primer/alpha/dialog_preview/dialog_inside_overlay.html.erb +20 -0
  26. data/previews/primer/alpha/dialog_preview/nested_dialog.html.erb +7 -0
  27. data/previews/primer/alpha/dialog_preview.rb +24 -0
  28. data/static/info_arch.json +14 -1
  29. data/static/previews.json +13 -0
  30. 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].style.setProperty('visibility', 'visible');
144
- __classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index].hidden = true;
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].style.setProperty('visibility', 'hidden');
148
- __classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index].hidden = false;
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].style.setProperty('visibility', 'visible')
149
- this.#menuItems[index]!.hidden = true
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].style.setProperty('visibility', 'hidden')
154
- this.#menuItems[index]!.hidden = false
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
 
@@ -131,7 +131,6 @@ module Primer
131
131
  @system_arguments[:aria] = merge_aria(
132
132
  @system_arguments, {
133
133
  aria: {
134
- disabled: true,
135
134
  labelledby: labelledby,
136
135
  describedby: "#{@id}-description"
137
136
  }
@@ -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
  }
@@ -50,6 +50,6 @@ document.addEventListener('clipboard-copy', ({target}) => {
50
50
  setTimeout(() => {
51
51
  showCopy(target)
52
52
  clipboardCopyElementTimers.delete(target)
53
- }, CLIPBOARD_COPY_TIMER_DURATION)
53
+ }, CLIPBOARD_COPY_TIMER_DURATION),
54
54
  )
55
55
  })
@@ -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
- event.stopPropagation();
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
- if (!(dialog === null || dialog === void 0 ? void 0 : dialog.open))
69
- return;
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
- event.stopPropagation()
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
- 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
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 =
@@ -85,7 +85,7 @@ export default class FocusGroupElement extends HTMLElement {
85
85
  }
86
86
  }
87
87
  },
88
- {signal}
88
+ {signal},
89
89
  )
90
90
  }
91
91
  }
@@ -6,7 +6,7 @@ module Primer
6
6
  module VERSION
7
7
  MAJOR = 0
8
8
  MINOR = 22
9
- PATCH = 0
9
+ PATCH = 2
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -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
@@ -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": null,
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.0
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-01 00:00:00.000000000 Z
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.6.0
919
+ version: 2.7.0
919
920
  required_rubygems_version: !ruby/object:Gem::Requirement
920
921
  requirements:
921
922
  - - ">="