primer_view_components 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/app/assets/javascripts/app/components/primer/alpha/action_bar_element.d.ts +16 -0
  4. data/app/assets/javascripts/app/components/primer/alpha/action_menu/action_menu_element.d.ts +30 -0
  5. data/app/assets/javascripts/app/components/primer/alpha/dropdown/menu.d.ts +1 -0
  6. data/app/assets/javascripts/app/components/primer/alpha/dropdown.d.ts +1 -0
  7. data/app/assets/javascripts/app/components/primer/alpha/image_crop.d.ts +1 -0
  8. data/app/assets/javascripts/app/components/primer/alpha/modal_dialog.d.ts +18 -0
  9. data/app/assets/javascripts/app/components/primer/alpha/nav_list.d.ts +28 -0
  10. data/app/assets/javascripts/app/components/primer/alpha/segmented_control.d.ts +12 -0
  11. data/app/assets/javascripts/app/components/primer/alpha/tab_container.d.ts +1 -0
  12. data/app/assets/javascripts/app/components/primer/alpha/toggle_switch.d.ts +30 -0
  13. data/app/assets/javascripts/app/components/primer/alpha/tool_tip.d.ts +26 -0
  14. data/app/assets/javascripts/app/components/primer/alpha/x_banner.d.ts +11 -0
  15. data/app/assets/javascripts/app/components/primer/anchored_position.d.ts +27 -0
  16. data/app/assets/javascripts/app/components/primer/beta/auto_complete/auto_complete.d.ts +1 -0
  17. data/app/assets/javascripts/app/components/primer/beta/clipboard_copy.d.ts +1 -0
  18. data/app/assets/javascripts/app/components/primer/beta/relative_time.d.ts +1 -0
  19. data/app/assets/javascripts/app/components/primer/focus_group.d.ts +19 -0
  20. data/app/assets/javascripts/app/components/primer/primer.d.ts +21 -0
  21. data/app/assets/javascripts/lib/primer/forms/primer_multi_input.d.ts +10 -0
  22. data/app/assets/javascripts/lib/primer/forms/primer_text_field.d.ts +1 -0
  23. data/app/assets/javascripts/lib/primer/forms/toggle_switch_input.d.ts +5 -0
  24. data/app/assets/javascripts/primer_view_components.js +1 -1
  25. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  26. data/app/assets/styles/primer_view_components.css +1 -1
  27. data/app/assets/styles/primer_view_components.css.map +1 -1
  28. data/app/components/primer/alpha/action_bar/divider.rb +30 -0
  29. data/app/components/primer/alpha/action_bar/item.rb +26 -0
  30. data/app/components/primer/alpha/action_bar.css +1 -0
  31. data/app/components/primer/alpha/action_bar.css.json +17 -0
  32. data/app/components/primer/alpha/action_bar.css.map +1 -0
  33. data/app/components/primer/alpha/action_bar.html.erb +12 -0
  34. data/app/components/primer/alpha/action_bar.pcss +69 -0
  35. data/app/components/primer/alpha/action_bar.rb +110 -0
  36. data/app/components/primer/alpha/action_bar_element.d.ts +16 -0
  37. data/app/components/primer/alpha/action_bar_element.js +172 -0
  38. data/app/components/primer/alpha/action_bar_element.ts +175 -0
  39. data/app/components/primer/alpha/action_menu/action_menu_element.d.ts +2 -2
  40. data/app/components/primer/alpha/tool_tip.d.ts +3 -2
  41. data/app/components/primer/alpha/tool_tip.js +89 -44
  42. data/app/components/primer/alpha/tool_tip.ts +88 -41
  43. data/app/components/primer/alpha/tooltip.rb +1 -0
  44. data/app/components/primer/beta/link.css +1 -1
  45. data/app/components/primer/beta/link.css.map +1 -1
  46. data/app/components/primer/beta/link.pcss +4 -0
  47. data/app/components/primer/beta/link.rb +6 -10
  48. data/app/components/primer/primer.d.ts +1 -0
  49. data/app/components/primer/primer.js +1 -0
  50. data/app/components/primer/primer.pcss +1 -0
  51. data/app/components/primer/primer.ts +1 -0
  52. data/lib/primer/static/generate_previews.rb +9 -0
  53. data/lib/primer/view_components/linters/tooltipped_migration.rb +1 -3
  54. data/lib/primer/view_components/version.rb +2 -2
  55. data/lib/primer/yard.rb +5 -0
  56. data/previews/primer/alpha/action_bar_preview/inline.html.erb +16 -0
  57. data/previews/primer/alpha/action_bar_preview.rb +77 -0
  58. data/previews/primer/alpha/action_list_preview.rb +10 -0
  59. data/previews/primer/alpha/action_menu_preview.rb +5 -0
  60. data/previews/primer/alpha/auto_complete_preview.rb +1 -0
  61. data/previews/primer/alpha/banner_preview.rb +9 -1
  62. data/previews/primer/alpha/button_marketing_preview.rb +2 -0
  63. data/previews/primer/alpha/check_box_group_preview.rb +1 -0
  64. data/previews/primer/alpha/check_box_preview.rb +6 -0
  65. data/previews/primer/alpha/dialog_preview.rb +1 -0
  66. data/previews/primer/alpha/dropdown_preview.rb +1 -0
  67. data/previews/primer/alpha/hellip_button_preview.rb +1 -0
  68. data/previews/primer/alpha/hidden_text_expander_preview.rb +1 -0
  69. data/previews/primer/alpha/layout_preview.rb +4 -0
  70. data/previews/primer/alpha/menu_preview.rb +1 -0
  71. data/previews/primer/alpha/multi_input_preview.rb +4 -0
  72. data/previews/primer/alpha/nav_list_preview.rb +3 -0
  73. data/previews/primer/alpha/radio_button_group_preview.rb +2 -0
  74. data/previews/primer/alpha/radio_button_preview.rb +10 -0
  75. data/previews/primer/alpha/segmented_control_preview.rb +13 -0
  76. data/previews/primer/alpha/select_preview.rb +6 -0
  77. data/previews/primer/alpha/tab_nav_preview.rb +3 -0
  78. data/previews/primer/alpha/tab_panels_preview.rb +1 -0
  79. data/previews/primer/alpha/text_area_preview.rb +7 -0
  80. data/previews/primer/alpha/text_field_preview.rb +15 -0
  81. data/previews/primer/alpha/toggle_switch_preview.rb +7 -0
  82. data/previews/primer/alpha/tooltip_preview/tooltip_inside_primer_overlay.html.erb +20 -0
  83. data/previews/primer/alpha/tooltip_preview.rb +7 -0
  84. data/previews/primer/alpha/underline_nav_preview.rb +2 -0
  85. data/previews/primer/beta/auto_complete_item_preview.rb +2 -0
  86. data/previews/primer/beta/auto_complete_preview.rb +7 -0
  87. data/previews/primer/beta/avatar_preview.rb +10 -0
  88. data/previews/primer/beta/avatar_stack_preview.rb +3 -0
  89. data/previews/primer/beta/blankslate_preview.rb +9 -0
  90. data/previews/primer/beta/border_box_preview.rb +4 -0
  91. data/previews/primer/beta/breadcrumbs_preview.rb +1 -0
  92. data/previews/primer/beta/button_group_preview.rb +4 -0
  93. data/previews/primer/beta/button_preview.rb +10 -0
  94. data/previews/primer/beta/clipboard_copy_preview.rb +2 -0
  95. data/previews/primer/beta/close_button_preview.rb +1 -0
  96. data/previews/primer/beta/counter_preview.rb +11 -0
  97. data/previews/primer/beta/flash_preview.rb +8 -0
  98. data/previews/primer/beta/heading_preview.rb +1 -0
  99. data/previews/primer/beta/icon_button_preview.rb +3 -0
  100. data/previews/primer/beta/label_preview.rb +13 -0
  101. data/previews/primer/beta/link_preview.rb +11 -9
  102. data/previews/primer/beta/markdown_preview.rb +1 -0
  103. data/previews/primer/beta/octicon_preview.rb +1 -0
  104. data/previews/primer/beta/popover_preview.rb +6 -0
  105. data/previews/primer/beta/progress_bar_preview.rb +4 -0
  106. data/previews/primer/beta/spinner_preview.rb +1 -0
  107. data/previews/primer/beta/state_preview.rb +6 -0
  108. data/previews/primer/beta/subhead_preview.rb +4 -0
  109. data/previews/primer/beta/text_preview.rb +1 -0
  110. data/previews/primer/beta/timeline_item_preview.rb +1 -0
  111. data/previews/primer/beta/truncate_preview.rb +1 -0
  112. data/previews/primer/box_preview.rb +2 -0
  113. data/static/arguments.json +51 -7
  114. data/static/audited_at.json +3 -0
  115. data/static/classes.json +21 -0
  116. data/static/constants.json +20 -6
  117. data/static/info_arch.json +669 -7
  118. data/static/previews.json +571 -0
  119. data/static/statuses.json +3 -0
  120. metadata +43 -8
@@ -10,10 +10,11 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
12
  var _ToolTipElement_instances, _ToolTipElement_abortController, _ToolTipElement_align, _ToolTipElement_side, _ToolTipElement_allowUpdatePosition, _ToolTipElement_update, _ToolTipElement_updateControlReference, _ToolTipElement_updateDirection, _ToolTipElement_updatePosition;
13
+ import '@oddbird/popover-polyfill';
13
14
  import { getAnchoredPosition } from '@primer/behaviors';
14
- const TOOLTIP_OPEN_CLASS = 'tooltip-open';
15
15
  const TOOLTIP_ARROW_EDGE_OFFSET = 6;
16
16
  const TOOLTIP_SR_ONLY_CLASS = 'sr-only';
17
+ const TOOLTIP_OFFSET = 10;
17
18
  const DIRECTION_CLASSES = [
18
19
  'tooltip-n',
19
20
  'tooltip-s',
@@ -24,6 +25,23 @@ const DIRECTION_CLASSES = [
24
25
  'tooltip-nw',
25
26
  'tooltip-sw'
26
27
  ];
28
+ function closeOpenTooltips(except) {
29
+ for (const tooltip of openTooltips) {
30
+ if (tooltip === except)
31
+ continue;
32
+ if (tooltip.matches(':popover-open')) {
33
+ tooltip.hidePopover();
34
+ }
35
+ else {
36
+ openTooltips.delete(tooltip);
37
+ }
38
+ }
39
+ }
40
+ function focusOutListener() {
41
+ closeOpenTooltips();
42
+ }
43
+ const tooltips = new Set();
44
+ const openTooltips = new Set();
27
45
  class ToolTipElement extends HTMLElement {
28
46
  constructor() {
29
47
  super(...arguments);
@@ -36,12 +54,10 @@ class ToolTipElement extends HTMLElement {
36
54
  styles() {
37
55
  return `
38
56
  :host {
39
- position: absolute;
40
- z-index: 1000000;
41
- padding: .5em .75em;
57
+ padding: .5em .75em !important;
42
58
  font: normal normal 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
43
59
  -webkit-font-smoothing: subpixel-antialiased;
44
- color: var(--color-fg-on-emphasis);
60
+ color: var(--color-fg-on-emphasis) !important;
45
61
  text-align: center;
46
62
  text-decoration: none;
47
63
  text-shadow: none;
@@ -49,13 +65,16 @@ class ToolTipElement extends HTMLElement {
49
65
  letter-spacing: normal;
50
66
  word-wrap: break-word;
51
67
  white-space: pre;
52
- background: var(--color-neutral-emphasis-plus);
68
+ background: var(--color-neutral-emphasis-plus) !important;
53
69
  border-radius: 6px;
70
+ border: 0 !important;
54
71
  opacity: 0;
55
72
  max-width: 250px;
56
73
  word-wrap: break-word;
57
74
  white-space: normal;
58
- width: max-content;
75
+ width: max-content !important;
76
+ inset: var(--tool-tip-position-top, 0) auto auto var(--tool-tip-position-left, 0) !important;
77
+ overflow: visible !important;
59
78
  }
60
79
 
61
80
  :host:before{
@@ -69,7 +88,7 @@ class ToolTipElement extends HTMLElement {
69
88
 
70
89
  @keyframes tooltip-appear {
71
90
  from {
72
- opacity: 0
91
+ opacity: 0;
73
92
  }
74
93
  to {
75
94
  opacity: 1
@@ -85,8 +104,17 @@ class ToolTipElement extends HTMLElement {
85
104
  content: ""
86
105
  }
87
106
 
88
- :host(.${TOOLTIP_OPEN_CLASS}),
89
- :host(.${TOOLTIP_OPEN_CLASS}):before {
107
+ :host(:popover-open),
108
+ :host(:popover-open):before {
109
+ animation-name: tooltip-appear;
110
+ animation-duration: .1s;
111
+ animation-fill-mode: forwards;
112
+ animation-timing-function: ease-in;
113
+ animation-delay: .4s
114
+ }
115
+
116
+ :host(.\\:popover-open),
117
+ :host(.\\:popover-open):before {
90
118
  animation-name: tooltip-appear;
91
119
  animation-duration: .1s;
92
120
  animation-fill-mode: forwards;
@@ -177,16 +205,22 @@ class ToolTipElement extends HTMLElement {
177
205
  get control() {
178
206
  return this.ownerDocument.getElementById(this.htmlFor);
179
207
  }
208
+ /* @deprecated */
180
209
  set hiddenFromView(value) {
181
- this.classList.toggle(TOOLTIP_SR_ONLY_CLASS, value);
182
- if (this.isConnected)
183
- __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_update).call(this);
210
+ if (value && this.matches(':popover-open')) {
211
+ this.hidePopover();
212
+ }
213
+ else if (!value && !this.matches(':popover-open')) {
214
+ this.showPopover();
215
+ }
184
216
  }
217
+ /* @deprecated */
185
218
  get hiddenFromView() {
186
- return this.classList.contains(TOOLTIP_SR_ONLY_CLASS);
219
+ return !this.matches(':popover-open');
187
220
  }
188
221
  connectedCallback() {
189
- var _a;
222
+ var _a, _b;
223
+ tooltips.add(this);
190
224
  __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_updateControlReference).call(this);
191
225
  __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_updateDirection).call(this);
192
226
  if (!this.shadowRoot) {
@@ -195,7 +229,7 @@ class ToolTipElement extends HTMLElement {
195
229
  style.textContent = this.styles();
196
230
  shadow.appendChild(document.createElement('slot'));
197
231
  }
198
- this.hiddenFromView = true;
232
+ __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_update).call(this, false);
199
233
  __classPrivateFieldSet(this, _ToolTipElement_allowUpdatePosition, true, "f");
200
234
  if (!this.control)
201
235
  return;
@@ -204,35 +238,48 @@ class ToolTipElement extends HTMLElement {
204
238
  __classPrivateFieldSet(this, _ToolTipElement_abortController, new AbortController(), "f");
205
239
  const { signal } = __classPrivateFieldGet(this, _ToolTipElement_abortController, "f");
206
240
  this.addEventListener('mouseleave', this, { signal });
241
+ this.addEventListener('toggle', this, { signal });
207
242
  this.control.addEventListener('mouseenter', this, { signal });
208
243
  this.control.addEventListener('mouseleave', this, { signal });
209
244
  this.control.addEventListener('focus', this, { signal });
210
- this.control.addEventListener('blur', this, { signal });
245
+ this.control.addEventListener('mousedown', this, { signal });
246
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
247
+ // @ts-ignore popoverTargetElement is not in the type definition
248
+ (_b = this.control.popoverTargetElement) === null || _b === void 0 ? void 0 : _b.addEventListener('beforetoggle', this, {
249
+ signal
250
+ });
251
+ this.ownerDocument.addEventListener('focusout', focusOutListener);
211
252
  this.ownerDocument.addEventListener('keydown', this, { signal });
212
- __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_update).call(this);
213
253
  }
214
254
  disconnectedCallback() {
215
255
  var _a;
256
+ tooltips.delete(this);
257
+ openTooltips.delete(this);
216
258
  (_a = __classPrivateFieldGet(this, _ToolTipElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
217
259
  }
218
- handleEvent(event) {
260
+ async handleEvent(event) {
219
261
  if (!this.control)
220
262
  return;
263
+ const showing = this.matches(':popover-open');
221
264
  // Ensures that tooltip stays open when hovering between tooltip and element
222
265
  // WCAG Success Criterion 1.4.13 Hoverable
223
- if ((event.type === 'mouseenter' || event.type === 'focus') && this.hiddenFromView) {
224
- this.hiddenFromView = false;
225
- }
226
- else if (event.type === 'blur') {
227
- this.hiddenFromView = true;
228
- }
229
- else if (event.type === 'mouseleave' &&
266
+ const shouldShow = event.type === 'mouseenter' || event.type === 'focus';
267
+ const isMouseLeaveFromButton = event.type === 'mouseleave' &&
230
268
  event.relatedTarget !== this.control &&
231
- event.relatedTarget !== this) {
232
- this.hiddenFromView = true;
269
+ event.relatedTarget !== this;
270
+ const isEscapeKeydown = event.type === 'keydown' && event.key === 'Escape';
271
+ const isMouseDownOnButton = event.type === 'mousedown' && event.currentTarget === this.control;
272
+ const isOpeningOtherPopover = event.type === 'beforetoggle' && event.currentTarget !== this;
273
+ const shouldHide = isMouseLeaveFromButton || isEscapeKeydown || isMouseDownOnButton || isOpeningOtherPopover;
274
+ await Promise.resolve();
275
+ if (!showing && shouldShow) {
276
+ this.showPopover();
277
+ }
278
+ else if (showing && shouldHide) {
279
+ this.hidePopover();
233
280
  }
234
- else if (event.type === 'keydown' && event.key === 'Escape' && !this.hiddenFromView) {
235
- this.hiddenFromView = true;
281
+ if (event.type === 'toggle') {
282
+ __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_update).call(this, event.newState === 'open');
236
283
  }
237
284
  }
238
285
  attributeChangedCallback(name) {
@@ -246,17 +293,17 @@ class ToolTipElement extends HTMLElement {
246
293
  }
247
294
  }
248
295
  }
249
- _ToolTipElement_abortController = new WeakMap(), _ToolTipElement_align = new WeakMap(), _ToolTipElement_side = new WeakMap(), _ToolTipElement_allowUpdatePosition = new WeakMap(), _ToolTipElement_instances = new WeakSet(), _ToolTipElement_update = function _ToolTipElement_update() {
250
- if (this.hiddenFromView) {
251
- this.classList.remove(TOOLTIP_OPEN_CLASS, ...DIRECTION_CLASSES);
296
+ _ToolTipElement_abortController = new WeakMap(), _ToolTipElement_align = new WeakMap(), _ToolTipElement_side = new WeakMap(), _ToolTipElement_allowUpdatePosition = new WeakMap(), _ToolTipElement_instances = new WeakSet(), _ToolTipElement_update = function _ToolTipElement_update(isOpen) {
297
+ if (isOpen) {
298
+ openTooltips.add(this);
299
+ this.classList.remove(TOOLTIP_SR_ONLY_CLASS);
300
+ closeOpenTooltips(this);
301
+ __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_updatePosition).call(this);
252
302
  }
253
303
  else {
254
- this.classList.add(TOOLTIP_OPEN_CLASS);
255
- for (const tooltip of this.ownerDocument.querySelectorAll(this.tagName)) {
256
- if (tooltip !== this)
257
- tooltip.hiddenFromView = true;
258
- }
259
- __classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_updatePosition).call(this);
304
+ openTooltips.delete(this);
305
+ this.classList.remove(...DIRECTION_CLASSES);
306
+ this.classList.add(TOOLTIP_SR_ONLY_CLASS);
260
307
  }
261
308
  }, _ToolTipElement_updateControlReference = function _ToolTipElement_updateControlReference() {
262
309
  if (!this.id || !this.control)
@@ -331,10 +378,8 @@ _ToolTipElement_abortController = new WeakMap(), _ToolTipElement_align = new Wea
331
378
  }, _ToolTipElement_updatePosition = function _ToolTipElement_updatePosition() {
332
379
  if (!this.control)
333
380
  return;
334
- if (!__classPrivateFieldGet(this, _ToolTipElement_allowUpdatePosition, "f") || this.hiddenFromView)
381
+ if (!__classPrivateFieldGet(this, _ToolTipElement_allowUpdatePosition, "f") || !this.matches(':popover-open'))
335
382
  return;
336
- const TOOLTIP_OFFSET = 10;
337
- this.style.left = `0px`; // Ensures we have reliable tooltip width in `getAnchoredPosition`
338
383
  const position = getAnchoredPosition(this, this.control, {
339
384
  side: __classPrivateFieldGet(this, _ToolTipElement_side, "f"),
340
385
  align: __classPrivateFieldGet(this, _ToolTipElement_align, "f"),
@@ -342,8 +387,8 @@ _ToolTipElement_abortController = new WeakMap(), _ToolTipElement_align = new Wea
342
387
  });
343
388
  const anchorSide = position.anchorSide;
344
389
  const align = position.anchorAlign;
345
- this.style.top = `${position.top}px`;
346
- this.style.left = `${position.left}px`;
390
+ this.style.setProperty('--tool-tip-position-top', `${position.top}px`);
391
+ this.style.setProperty('--tool-tip-position-left', `${position.left}px`);
347
392
  let direction = 's';
348
393
  if (anchorSide === 'outside-left') {
349
394
  direction = 'w';
@@ -1,9 +1,10 @@
1
1
  import type {AnchorAlignment, AnchorSide} from '@primer/behaviors'
2
+ import '@oddbird/popover-polyfill'
2
3
  import {getAnchoredPosition} from '@primer/behaviors'
3
4
 
4
- const TOOLTIP_OPEN_CLASS = 'tooltip-open'
5
5
  const TOOLTIP_ARROW_EDGE_OFFSET = 6
6
6
  const TOOLTIP_SR_ONLY_CLASS = 'sr-only'
7
+ const TOOLTIP_OFFSET = 10
7
8
 
8
9
  type Direction = 'n' | 's' | 'e' | 'w' | 'ne' | 'se' | 'nw' | 'sw'
9
10
 
@@ -18,16 +19,31 @@ const DIRECTION_CLASSES = [
18
19
  'tooltip-sw'
19
20
  ]
20
21
 
22
+ function closeOpenTooltips(except?: Element) {
23
+ for (const tooltip of openTooltips) {
24
+ if (tooltip === except) continue
25
+ if (tooltip.matches(':popover-open')) {
26
+ tooltip.hidePopover()
27
+ } else {
28
+ openTooltips.delete(tooltip)
29
+ }
30
+ }
31
+ }
32
+
33
+ function focusOutListener() {
34
+ closeOpenTooltips()
35
+ }
36
+
37
+ const tooltips = new Set<ToolTipElement>()
38
+ const openTooltips = new Set<ToolTipElement>()
21
39
  class ToolTipElement extends HTMLElement {
22
40
  styles() {
23
41
  return `
24
42
  :host {
25
- position: absolute;
26
- z-index: 1000000;
27
- padding: .5em .75em;
43
+ padding: .5em .75em !important;
28
44
  font: normal normal 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
29
45
  -webkit-font-smoothing: subpixel-antialiased;
30
- color: var(--color-fg-on-emphasis);
46
+ color: var(--color-fg-on-emphasis) !important;
31
47
  text-align: center;
32
48
  text-decoration: none;
33
49
  text-shadow: none;
@@ -35,13 +51,16 @@ class ToolTipElement extends HTMLElement {
35
51
  letter-spacing: normal;
36
52
  word-wrap: break-word;
37
53
  white-space: pre;
38
- background: var(--color-neutral-emphasis-plus);
54
+ background: var(--color-neutral-emphasis-plus) !important;
39
55
  border-radius: 6px;
56
+ border: 0 !important;
40
57
  opacity: 0;
41
58
  max-width: 250px;
42
59
  word-wrap: break-word;
43
60
  white-space: normal;
44
- width: max-content;
61
+ width: max-content !important;
62
+ inset: var(--tool-tip-position-top, 0) auto auto var(--tool-tip-position-left, 0) !important;
63
+ overflow: visible !important;
45
64
  }
46
65
 
47
66
  :host:before{
@@ -55,7 +74,7 @@ class ToolTipElement extends HTMLElement {
55
74
 
56
75
  @keyframes tooltip-appear {
57
76
  from {
58
- opacity: 0
77
+ opacity: 0;
59
78
  }
60
79
  to {
61
80
  opacity: 1
@@ -71,8 +90,17 @@ class ToolTipElement extends HTMLElement {
71
90
  content: ""
72
91
  }
73
92
 
74
- :host(.${TOOLTIP_OPEN_CLASS}),
75
- :host(.${TOOLTIP_OPEN_CLASS}):before {
93
+ :host(:popover-open),
94
+ :host(:popover-open):before {
95
+ animation-name: tooltip-appear;
96
+ animation-duration: .1s;
97
+ animation-fill-mode: forwards;
98
+ animation-timing-function: ease-in;
99
+ animation-delay: .4s
100
+ }
101
+
102
+ :host(.\\:popover-open),
103
+ :host(.\\:popover-open):before {
76
104
  animation-name: tooltip-appear;
77
105
  animation-duration: .1s;
78
106
  animation-fill-mode: forwards;
@@ -176,16 +204,22 @@ class ToolTipElement extends HTMLElement {
176
204
  return this.ownerDocument.getElementById(this.htmlFor)
177
205
  }
178
206
 
207
+ /* @deprecated */
179
208
  set hiddenFromView(value: true | false) {
180
- this.classList.toggle(TOOLTIP_SR_ONLY_CLASS, value)
181
- if (this.isConnected) this.#update()
209
+ if (value && this.matches(':popover-open')) {
210
+ this.hidePopover()
211
+ } else if (!value && !this.matches(':popover-open')) {
212
+ this.showPopover()
213
+ }
182
214
  }
183
215
 
216
+ /* @deprecated */
184
217
  get hiddenFromView() {
185
- return this.classList.contains(TOOLTIP_SR_ONLY_CLASS)
218
+ return !this.matches(':popover-open')
186
219
  }
187
220
 
188
221
  connectedCallback() {
222
+ tooltips.add(this)
189
223
  this.#updateControlReference()
190
224
  this.#updateDirection()
191
225
  if (!this.shadowRoot) {
@@ -194,7 +228,7 @@ class ToolTipElement extends HTMLElement {
194
228
  style.textContent = this.styles()
195
229
  shadow.appendChild(document.createElement('slot'))
196
230
  }
197
- this.hiddenFromView = true
231
+ this.#update(false)
198
232
  this.#allowUpdatePosition = true
199
233
 
200
234
  if (!this.control) return
@@ -206,49 +240,66 @@ class ToolTipElement extends HTMLElement {
206
240
  const {signal} = this.#abortController
207
241
 
208
242
  this.addEventListener('mouseleave', this, {signal})
243
+ this.addEventListener('toggle', this, {signal})
209
244
  this.control.addEventListener('mouseenter', this, {signal})
210
245
  this.control.addEventListener('mouseleave', this, {signal})
211
246
  this.control.addEventListener('focus', this, {signal})
212
- this.control.addEventListener('blur', this, {signal})
247
+ this.control.addEventListener('mousedown', this, {signal})
248
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
249
+ // @ts-ignore popoverTargetElement is not in the type definition
250
+ this.control.popoverTargetElement?.addEventListener('beforetoggle', this, {
251
+ signal
252
+ })
253
+ this.ownerDocument.addEventListener('focusout', focusOutListener)
213
254
  this.ownerDocument.addEventListener('keydown', this, {signal})
214
- this.#update()
215
255
  }
216
256
 
217
257
  disconnectedCallback() {
258
+ tooltips.delete(this)
259
+ openTooltips.delete(this)
218
260
  this.#abortController?.abort()
219
261
  }
220
262
 
221
- handleEvent(event: Event) {
263
+ async handleEvent(event: Event) {
222
264
  if (!this.control) return
265
+ const showing = this.matches(':popover-open')
223
266
 
224
267
  // Ensures that tooltip stays open when hovering between tooltip and element
225
268
  // WCAG Success Criterion 1.4.13 Hoverable
226
- if ((event.type === 'mouseenter' || event.type === 'focus') && this.hiddenFromView) {
227
- this.hiddenFromView = false
228
- } else if (event.type === 'blur') {
229
- this.hiddenFromView = true
230
- } else if (
269
+ const shouldShow = event.type === 'mouseenter' || event.type === 'focus'
270
+ const isMouseLeaveFromButton =
231
271
  event.type === 'mouseleave' &&
232
272
  (event as MouseEvent).relatedTarget !== this.control &&
233
273
  (event as MouseEvent).relatedTarget !== this
234
- ) {
235
- this.hiddenFromView = true
236
- } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Escape' && !this.hiddenFromView) {
237
- this.hiddenFromView = true
274
+ const isEscapeKeydown = event.type === 'keydown' && (event as KeyboardEvent).key === 'Escape'
275
+ const isMouseDownOnButton = event.type === 'mousedown' && event.currentTarget === this.control
276
+ const isOpeningOtherPopover = event.type === 'beforetoggle' && event.currentTarget !== this
277
+ const shouldHide = isMouseLeaveFromButton || isEscapeKeydown || isMouseDownOnButton || isOpeningOtherPopover
278
+
279
+ await Promise.resolve()
280
+ if (!showing && shouldShow) {
281
+ this.showPopover()
282
+ } else if (showing && shouldHide) {
283
+ this.hidePopover()
284
+ }
285
+
286
+ if (event.type === 'toggle') {
287
+ this.#update((event as ToggleEvent).newState === 'open')
238
288
  }
239
289
  }
240
290
 
241
291
  static observedAttributes = ['data-type', 'data-direction', 'id']
242
292
 
243
- #update() {
244
- if (this.hiddenFromView) {
245
- this.classList.remove(TOOLTIP_OPEN_CLASS, ...DIRECTION_CLASSES)
246
- } else {
247
- this.classList.add(TOOLTIP_OPEN_CLASS)
248
- for (const tooltip of this.ownerDocument.querySelectorAll<ToolTipElement>(this.tagName)) {
249
- if (tooltip !== this) tooltip.hiddenFromView = true
250
- }
293
+ #update(isOpen: boolean) {
294
+ if (isOpen) {
295
+ openTooltips.add(this)
296
+ this.classList.remove(TOOLTIP_SR_ONLY_CLASS)
297
+ closeOpenTooltips(this)
251
298
  this.#updatePosition()
299
+ } else {
300
+ openTooltips.delete(this)
301
+ this.classList.remove(...DIRECTION_CLASSES)
302
+ this.classList.add(TOOLTIP_SR_ONLY_CLASS)
252
303
  }
253
304
  }
254
305
 
@@ -326,11 +377,7 @@ class ToolTipElement extends HTMLElement {
326
377
 
327
378
  #updatePosition() {
328
379
  if (!this.control) return
329
- if (!this.#allowUpdatePosition || this.hiddenFromView) return
330
-
331
- const TOOLTIP_OFFSET = 10
332
-
333
- this.style.left = `0px` // Ensures we have reliable tooltip width in `getAnchoredPosition`
380
+ if (!this.#allowUpdatePosition || !this.matches(':popover-open')) return
334
381
 
335
382
  const position = getAnchoredPosition(this, this.control, {
336
383
  side: this.#side,
@@ -340,8 +387,8 @@ class ToolTipElement extends HTMLElement {
340
387
  const anchorSide = position.anchorSide
341
388
  const align = position.anchorAlign
342
389
 
343
- this.style.top = `${position.top}px`
344
- this.style.left = `${position.left}px`
390
+ this.style.setProperty('--tool-tip-position-top', `${position.top}px`)
391
+ this.style.setProperty('--tool-tip-position-left', `${position.left}px`)
345
392
 
346
393
  let direction: Direction = 's'
347
394
 
@@ -110,6 +110,7 @@ module Primer
110
110
  @system_arguments[:id] ||= self.class.generate_id
111
111
  @system_arguments[:tag] = :"tool-tip"
112
112
  @system_arguments[:for] = for_id
113
+ @system_arguments[:popover] = "manual"
113
114
  @system_arguments[:classes] = class_names(
114
115
  @system_arguments[:classes],
115
116
  "sr-only"
@@ -1 +1 @@
1
- .Link{color:var(--color-accent-fg)}.Link:hover{cursor:pointer;text-decoration:underline}.Link:focus,.Link:focus-visible{outline-offset:0}.Link--primary{color:var(--color-fg-default)!important}.Link--primary:hover{color:var(--color-accent-fg)!important}.Link--secondary{color:var(--color-fg-muted)!important}.Link--secondary:hover{color:var(--color-accent-fg)!important}.Link--muted{color:var(--color-fg-muted)!important}.Link--muted:hover{text-decoration:none}.Link--muted:hover,.Link--onHover:hover{color:var(--color-accent-fg)!important}.Link--onHover:hover{cursor:pointer;text-decoration:underline}.Link--muted:hover [class*=color-fg],.Link--primary:hover [class*=color-fg],.Link--secondary:hover [class*=color-fg]{color:inherit!important}
1
+ .Link{color:var(--color-accent-fg)}.Link:hover{cursor:pointer}.Link:focus,.Link:hover{text-decoration:underline}.Link:focus,.Link:focus-visible{outline-offset:0}.Link--primary{color:var(--color-fg-default)!important}.Link--primary:hover{color:var(--color-accent-fg)!important}.Link--secondary{color:var(--color-fg-muted)!important}.Link--secondary:hover{color:var(--color-accent-fg)!important}.Link--muted{color:var(--color-fg-muted)!important}.Link--muted:hover{text-decoration:none}.Link--muted:hover,.Link--onHover:hover{color:var(--color-accent-fg)!important}.Link--onHover:hover{cursor:pointer;text-decoration:underline}.Link--muted:hover [class*=color-fg],.Link--primary:hover [class*=color-fg],.Link--secondary:hover [class*=color-fg]{color:inherit!important}
@@ -1 +1 @@
1
- {"version":3,"sources":["link.pcss"],"names":[],"mappings":"AAEA,MACE,4BAWF,CATE,YAEE,cAAe,CADf,yBAEF,CAEA,gCAEE,gBACF,CAGF,eACE,uCAKF,CAHE,qBACE,sCACF,CAGF,iBACE,qCAKF,CAHE,uBACE,sCACF,CAGF,aACE,qCAMF,CAJE,mBAEE,oBACF,CAMA,wCARE,sCAYF,CAJA,qBAGE,cAAe,CADf,yBAEF,CAQA,qHACE,uBACF","file":"link.css","sourcesContent":["/* Links */\n\n.Link {\n color: var(--color-accent-fg);\n\n &:hover {\n text-decoration: underline;\n cursor: pointer;\n }\n\n &:focus,\n &:focus-visible {\n outline-offset: 0;\n }\n}\n\n.Link--primary {\n color: var(--color-fg-default) !important;\n\n &:hover {\n color: var(--color-accent-fg) !important;\n }\n}\n\n.Link--secondary {\n color: var(--color-fg-muted) !important;\n\n &:hover {\n color: var(--color-accent-fg) !important;\n }\n}\n\n.Link--muted {\n color: var(--color-fg-muted) !important;\n\n &:hover {\n color: var(--color-accent-fg) !important;\n text-decoration: none;\n }\n}\n\n/* Set the link color only on hover\n Useful when you want only part of a link to turn blue on hover */\n.Link--onHover {\n &:hover {\n color: var(--color-accent-fg) !important;\n text-decoration: underline;\n cursor: pointer;\n }\n}\n\n/* When using a color utility class inside of a link class\n color should change with link on hover. */\n.Link--secondary,\n.Link--primary,\n.Link--muted {\n &:hover [class*='color-fg'] {\n color: inherit !important;\n }\n}\n"]}
1
+ {"version":3,"sources":["link.pcss"],"names":[],"mappings":"AAEA,MACE,4BAeF,CAbE,YAEE,cACF,CAEA,wBAJE,yBAMF,CAEA,gCAEE,gBACF,CAGF,eACE,uCAKF,CAHE,qBACE,sCACF,CAGF,iBACE,qCAKF,CAHE,uBACE,sCACF,CAGF,aACE,qCAMF,CAJE,mBAEE,oBACF,CAMA,wCARE,sCAYF,CAJA,qBAGE,cAAe,CADf,yBAEF,CAQA,qHACE,uBACF","file":"link.css","sourcesContent":["/* Links */\n\n.Link {\n color: var(--color-accent-fg);\n\n &:hover {\n text-decoration: underline;\n cursor: pointer;\n }\n\n &:focus {\n text-decoration: underline;\n }\n\n &:focus,\n &:focus-visible {\n outline-offset: 0;\n }\n}\n\n.Link--primary {\n color: var(--color-fg-default) !important;\n\n &:hover {\n color: var(--color-accent-fg) !important;\n }\n}\n\n.Link--secondary {\n color: var(--color-fg-muted) !important;\n\n &:hover {\n color: var(--color-accent-fg) !important;\n }\n}\n\n.Link--muted {\n color: var(--color-fg-muted) !important;\n\n &:hover {\n color: var(--color-accent-fg) !important;\n text-decoration: none;\n }\n}\n\n/* Set the link color only on hover\n Useful when you want only part of a link to turn blue on hover */\n.Link--onHover {\n &:hover {\n color: var(--color-accent-fg) !important;\n text-decoration: underline;\n cursor: pointer;\n }\n}\n\n/* When using a color utility class inside of a link class\n color should change with link on hover. */\n.Link--secondary,\n.Link--primary,\n.Link--muted {\n &:hover [class*='color-fg'] {\n color: inherit !important;\n }\n}\n"]}
@@ -8,6 +8,10 @@
8
8
  cursor: pointer;
9
9
  }
10
10
 
11
+ &:focus {
12
+ text-decoration: underline;
13
+ }
14
+
11
15
  &:focus,
12
16
  &:focus-visible {
13
17
  outline-offset: 0;
@@ -13,9 +13,6 @@ module Primer
13
13
  :secondary => "Link--secondary"
14
14
  }.freeze
15
15
 
16
- DEFAULT_TAG = :a
17
- TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
18
-
19
16
  # `Tooltip` that appears on mouse hover or keyboard focus over the link. Use tooltips sparingly and as a last resort.
20
17
  # **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be more appropriate.
21
18
  # The tooltip will appear adjacent to the anchor element. Both the tooltip and the anchor will be nested
@@ -55,30 +52,29 @@ module Primer
55
52
  # Link
56
53
  # <% end %>
57
54
  #
58
- # @param tag [String] <%= one_of(Primer::Beta::Link::TAG_OPTIONS) %>
59
- # @param href [String] URL to be used for the Link. Required if tag is `:a`. If the requirements are not met an error will be raised in non production environments. In production, an empty link element will be rendered.
55
+ # @param href [String] URL to be used for the Link. Required. If the requirements are not met an error will be raised in non production environments. In production, an empty link element will be rendered.
60
56
  # @param scheme [Symbol] <%= one_of(Primer::Beta::Link::SCHEME_MAPPINGS.keys) %>
61
57
  # @param muted [Boolean] Uses light gray for Link color, and blue on hover.
62
58
  # @param underline [Boolean] Whether or not to underline the link.
63
59
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
64
- def initialize(href: nil, tag: DEFAULT_TAG, scheme: DEFAULT_SCHEME, muted: false, underline: true, **system_arguments)
65
- @system_arguments = system_arguments
60
+ def initialize(href: nil, scheme: DEFAULT_SCHEME, muted: false, underline: true, **system_arguments)
61
+ @system_arguments = deny_tag_argument(**system_arguments)
66
62
 
67
63
  @id = @system_arguments[:id]
68
64
 
69
- @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
65
+ @system_arguments[:tag] = :a
70
66
  @system_arguments[:href] = href
71
67
  @system_arguments[:classes] = class_names(
72
68
  @system_arguments[:classes],
73
69
  SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme, DEFAULT_SCHEME)],
74
- "Link" => tag == :span,
70
+ "Link",
75
71
  "Link--muted" => muted,
76
72
  "no-underline" => !underline
77
73
  )
78
74
  end
79
75
 
80
76
  def before_render
81
- raise ArgumentError, "href is required when using <a> tag" if @system_arguments[:tag] == :a && @system_arguments[:href].nil? && !Rails.env.production?
77
+ raise ArgumentError, "href is required" if @system_arguments[:href].nil? && !Rails.env.production?
82
78
  end
83
79
 
84
80
  def call
@@ -1,5 +1,6 @@
1
1
  import '@github/include-fragment-element';
2
2
  import '@oddbird/popover-polyfill';
3
+ import './alpha/action_bar_element';
3
4
  import './alpha/dropdown';
4
5
  import './anchored_position';
5
6
  import './focus_group';
@@ -1,5 +1,6 @@
1
1
  import '@github/include-fragment-element';
2
2
  import '@oddbird/popover-polyfill';
3
+ import './alpha/action_bar_element';
3
4
  import './alpha/dropdown';
4
5
  import './anchored_position';
5
6
  import './focus_group';
@@ -39,3 +39,4 @@
39
39
  /* to be renamed */
40
40
  @import "./beta/timeline_item.pcss";
41
41
  @import "./truncate.pcss";
42
+ @import "./alpha/action_bar.pcss";
@@ -1,5 +1,6 @@
1
1
  import '@github/include-fragment-element'
2
2
  import '@oddbird/popover-polyfill'
3
+ import './alpha/action_bar_element'
3
4
  import './alpha/dropdown'
4
5
  import './anchored_position'
5
6
  import './focus_group'
@@ -33,9 +33,18 @@ module Primer
33
33
  scenarios = parent_scenario.type == :scenario_group ? parent_scenario.scenarios : [parent_scenario]
34
34
 
35
35
  scenarios.map do |scenario|
36
+ snapshot_tag = scenario.tags.find { |tag| tag.tag_name == "snapshot" }
37
+ snapshot = if snapshot_tag.nil?
38
+ "false"
39
+ elsif snapshot_tag.text.blank?
40
+ "true"
41
+ else
42
+ snapshot_tag.text
43
+ end
36
44
  {
37
45
  preview_path: scenario.lookup_path,
38
46
  name: scenario.name,
47
+ snapshot: snapshot,
39
48
  skip_rules: Primer::Accessibility.axe_rules_to_skip(
40
49
  component: component,
41
50
  scenario_name: scenario.name
@@ -10,9 +10,7 @@ module ERBLint
10
10
  include LinterRegistry
11
11
  include Helpers::RuleHelpers
12
12
 
13
- MIGRATE_TO_NEWER_TOOLTIP = ".tooltipped has been deprecated. Tooltip should never be set on a non-interactive element (e.g. <span>, <div>, <li>). " \
14
- "Find alternatives in https://primer.style/design/guides/accessibility/tooltip-alternatives/. " \
15
- "If you're setting a tooltip on an interactive element, use the latest tooltip in https://primer.style/view-components."
13
+ MIGRATE_TO_NEWER_TOOLTIP = ".tooltipped has been deprecated. There are major accessibility concerns with using this tooltip so please take action. See https://github.com/primer/view_components/blob/main/docs/content/guides/accessibility/tooltipped_migration.md."
16
14
  TOOLTIPPED_RUBY_PATTERN = /classes:.*tooltipped|class:.*tooltipped/.freeze
17
15
 
18
16
  def run(processed_source)
@@ -5,8 +5,8 @@ module Primer
5
5
  module ViewComponents
6
6
  module VERSION
7
7
  MAJOR = 0
8
- MINOR = 3
9
- PATCH = 1
8
+ MINOR = 4
9
+ PATCH = 0
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end