@gitlab/ui 137.2.4 → 137.2.6

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.
@@ -338,14 +338,10 @@ var script = {
338
338
  },
339
339
  handleAutoClose(e) {
340
340
  var _this$$refs$baseDropd, _this$$refs$baseDropd2;
341
- if (this.autoClose && e.target.closest(ITEM_SELECTOR) && // The optional chaining here is to accommodate specs that use shallow mounting.
342
- // Ideally, those specs would either:
343
- // - stub out the `BaseDropdown` component to include a stub `containsElement` method, or
344
- // - use a full mount.
345
- //
346
- // Unfortunately, GitLab has many specs which do neither of these. So the simpler thing to do
347
- // is to optionally chain the method call here.
348
- (_this$$refs$baseDropd = (_this$$refs$baseDropd2 = this.$refs.baseDropdown).containsElement) !== null && _this$$refs$baseDropd !== void 0 && _this$$refs$baseDropd.call(_this$$refs$baseDropd2, e.target)) {
341
+ if (this.autoClose && e.target.closest(ITEM_SELECTOR) && // The ref is null when an item action destroys this component before the click bubbles here.
342
+ // The method is optionally chained to accommodate specs that shallow mount without stubbing
343
+ // `BaseDropdown`'s `containsElement`; GitLab has many such specs.
344
+ (_this$$refs$baseDropd = this.$refs.baseDropdown) !== null && _this$$refs$baseDropd !== void 0 && (_this$$refs$baseDropd2 = _this$$refs$baseDropd.containsElement) !== null && _this$$refs$baseDropd2 !== void 0 && _this$$refs$baseDropd2.call(_this$$refs$baseDropd, e.target)) {
349
345
  this.closeAndFocus();
350
346
  }
351
347
  },
@@ -215,7 +215,7 @@ var script = {
215
215
  const __vue_script__ = script;
216
216
 
217
217
  /* template */
218
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"dropdown b-dropdown gl-dropdown gl-relative",class:{ show: _vm.show }},[_c('ul',{ref:"dropdownMenu",staticClass:"dropdown-menu gl-absolute",class:[{ show: _vm.show }, _vm.menuClass],attrs:{"id":_vm.componentId,"role":"listbox"}},[(_vm.loading)?_c('gl-dropdown-item',{attrs:{"role":"option","disabled":""}},[_vm._t("loading-content",function(){return [_vm._v("Searching...")]})],2):_vm._e(),_vm._v(" "),_vm._l((_vm.dropdownItems),function(dropdownItem){return _c('gl-dropdown-item',{key:dropdownItem.id,ref:"dropdownItems",refInFor:true,attrs:{"id":_vm.dropdownItemIdAttribute(dropdownItem),"data-dropdown-item-id":dropdownItem.id,"active":_vm.dropdownItemIsFocused(dropdownItem),"aria-selected":_vm.dropdownItemIsFocused(dropdownItem).toString(),"active-class":"is-focused","role":"option","tabindex":"-1"},on:{"click":function($event){return _vm.handleDropdownItemClick(dropdownItem)}}},[_c('div',{staticClass:"-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3",on:{"mousedown":function($event){return _vm.handleMousedown(dropdownItem)}}},[_vm._t("dropdown-item-content",function(){return [_vm._v("\n "+_vm._s(dropdownItem.name)+"\n ")]},{"dropdownItem":dropdownItem})],2)])}),_vm._v(" "),(_vm.userDefinedTokenCanBeAdded)?_c('gl-dropdown-item',{ref:_vm.userDefinedToken.id,attrs:{"id":_vm.dropdownItemIdAttribute(_vm.userDefinedToken),"data-dropdown-item-id":_vm.userDefinedToken.id,"active":_vm.dropdownItemIsFocused(_vm.userDefinedToken),"active-class":"is-focused","role":"option","tabindex":"-1"},on:{"click":function($event){return _vm.handleDropdownItemClick(_vm.userDefinedToken)}}},[_c('div',{staticClass:"-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3",on:{"mousedown":function($event){return _vm.handleMousedown(_vm.userDefinedToken)}}},[_vm._t("user-defined-token-content",function(){return [_vm._v("\n Add \""+_vm._s(_vm.inputText)+"\"\n ")]},{"inputText":_vm.inputText})],2)]):(!_vm.dropdownItems.length)?_c('gl-dropdown-item',{attrs:{"role":"option","disabled":""}},[_vm._t("no-results-content",function(){return [_vm._v("No matches found")]})],2):_vm._e(),_vm._v(" "),_vm._t("dropdown-footer")],2)])};
218
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"dropdown b-dropdown gl-dropdown gl-relative",class:{ show: _vm.show }},[_c('ul',{ref:"dropdownMenu",staticClass:"dropdown-menu gl-absolute",class:[{ show: _vm.show }, _vm.menuClass],attrs:{"id":_vm.componentId,"role":"listbox"}},[(_vm.loading)?_c('gl-dropdown-item',{attrs:{"role":"option","disabled":""}},[_vm._t("loading-content",function(){return [_vm._v("Searching...")]})],2):_vm._e(),_vm._v(" "),_vm._l((_vm.dropdownItems),function(dropdownItem){return _c('gl-dropdown-item',{key:dropdownItem.id,ref:"dropdownItems",refInFor:true,attrs:{"id":_vm.dropdownItemIdAttribute(dropdownItem),"data-dropdown-item-id":dropdownItem.id,"active":_vm.dropdownItemIsFocused(dropdownItem),"aria-selected":_vm.dropdownItemIsFocused(dropdownItem).toString(),"active-class":"is-focused","role":"option","tabindex":"-1"},on:{"click":function($event){return _vm.handleDropdownItemClick(dropdownItem)}}},[_c('div',{staticClass:"-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3",on:{"mousedown":function($event){return _vm.handleMousedown(dropdownItem)}}},[_vm._t("dropdown-item-content",function(){return [_vm._v("\n "+_vm._s(dropdownItem.name)+"\n ")]},{"dropdownItem":dropdownItem})],2)])}),_vm._v(" "),(_vm.userDefinedTokenCanBeAdded)?_c('gl-dropdown-item',{ref:_vm.userDefinedToken.id,attrs:{"id":_vm.dropdownItemIdAttribute(_vm.userDefinedToken),"data-dropdown-item-id":_vm.userDefinedToken.id,"active":_vm.dropdownItemIsFocused(_vm.userDefinedToken),"active-class":"is-focused","role":"option","tabindex":"-1"},on:{"click":function($event){return _vm.handleDropdownItemClick(_vm.userDefinedToken)}}},[_c('div',{staticClass:"-gl-mx-4 -gl-my-3 gl-px-4 gl-py-3",on:{"mousedown":function($event){return _vm.handleMousedown(_vm.userDefinedToken)}}},[_vm._t("user-defined-token-content",function(){return [_vm._v("\n Add \""+_vm._s(_vm.inputText)+"\"\n ")]},{"inputText":_vm.inputText})],2)]):(!_vm.dropdownItems.length && !_vm.loading)?_c('gl-dropdown-item',{attrs:{"role":"option","disabled":""}},[_vm._t("no-results-content",function(){return [_vm._v("No matches found")]})],2):_vm._e(),_vm._v(" "),_vm._t("dropdown-footer")],2)])};
219
219
  var __vue_staticRenderFns__ = [];
220
220
 
221
221
  /* style */
@@ -65,19 +65,16 @@ const onMouseup = () => {
65
65
  const mousedown = 'mousedown';
66
66
  const mouseup = 'mouseup';
67
67
  const startListening = eventTypes => {
68
+ // Listening to mouse events ensures that a text selection doesn't trigger the
69
+ // GlOutsideDirective 'click' callback if the selection started within the target element,
70
+ // and that a Safari focus-loss 'focusin' doesn't trigger the callback either. Nothing is
71
+ // listening yet when the set is empty, so this is bound exactly once.
72
+ if (activeEventTypes.size === 0) {
73
+ document.addEventListener(mousedown, onMousedown);
74
+ document.addEventListener(mouseup, onMouseup);
75
+ }
68
76
  eventTypes.forEach(eventType => {
69
77
  if (!activeEventTypes.has(eventType)) {
70
- // Listening to mousedown events, ensures that a text selection doesn't trigger the
71
- // GlOutsideDirective 'click' callback if the selection started within the target element,
72
- // and that a Safari focus-loss 'focusin' doesn't trigger the callback either. Tracked via
73
- // activeEventTypes too, so it's only ever bound/unbound once, regardless of whether 'click'
74
- // and 'focusin' are (de)activated together or separately.
75
- if ((eventType === click || eventType === focusin) && !activeEventTypes.has(mousedown)) {
76
- document.addEventListener(mousedown, onMousedown);
77
- document.addEventListener(mouseup, onMouseup);
78
- activeEventTypes.add(mousedown);
79
- }
80
-
81
78
  // Added { capture: true } to all event types to prevent the behavior discussed in https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1686#note_412545027
82
79
  // Ensures the event listener handles the event in the capturing phase, avoiding issues encountered previously.
83
80
  // Cannot be tested with Jest or Cypress, but can be tested with Playwright in the future: https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/4272#note_1947425384
@@ -97,17 +94,18 @@ const stopListening = eventTypesToUnbind => {
97
94
  let eventTypes = _ref2.eventTypes;
98
95
  return !eventTypes.includes(eventType);
99
96
  })) {
100
- document.removeEventListener(eventType, globalListener);
97
+ document.removeEventListener(eventType, globalListener, {
98
+ capture: true
99
+ });
101
100
  activeEventTypes.delete(eventType);
102
101
  }
103
102
  }
104
103
  });
105
104
 
106
- // Only unbind mousedown once neither 'click' nor 'focusin' need it anymore.
107
- if (activeEventTypes.has(mousedown) && !activeEventTypes.has(click) && !activeEventTypes.has(focusin)) {
105
+ // Nothing is listening anymore, so the mouse gesture tracking isn't needed either.
106
+ if (activeEventTypes.size === 0) {
108
107
  document.removeEventListener(mousedown, onMousedown);
109
108
  document.removeEventListener(mouseup, onMouseup);
110
- activeEventTypes.delete(mousedown);
111
109
  }
112
110
  };
113
111
  function parseBinding(_ref3) {
@@ -1,4 +1,4 @@
1
- import { extend, isVue3 } from '../../vue';
1
+ import { extend, isVue3, getVue3Constructor } from '../../vue';
2
2
  import { NAME_TRANSPORTER, NAME_TRANSPORTER_TARGET } from '../../constants/components';
3
3
  import { IS_BROWSER } from '../../constants/env';
4
4
  import { HTMLElement } from '../../constants/safe-types';
@@ -193,7 +193,8 @@ const BVTransporter = /*#__PURE__*/extend({
193
193
  return $nodes[0];
194
194
  }
195
195
  }
196
- const Teleport = this.$.appContext.config.globalProperties.constructor.Teleport;
196
+ const _getVue3Constructor = getVue3Constructor(this),
197
+ Teleport = _getVue3Constructor.Teleport;
197
198
  return h(Teleport, {
198
199
  to: this.container
199
200
  }, this.normalizeSlot());
@@ -1,4 +1,4 @@
1
- import { isVue3 } from '../vue';
1
+ import { isVue3, getVue3Constructor } from '../vue';
2
2
 
3
3
  function _arrayLikeToArray(r, a) {
4
4
  (null == a || a > r.length) && (a = r.length);
@@ -119,9 +119,6 @@ const extractEventHandlers = config => {
119
119
  cleanConfig
120
120
  };
121
121
  };
122
- const getVue3Constructor = parent => {
123
- return parent.$.appContext.config.globalProperties.constructor;
124
- };
125
122
  const BUILTIN_DIRECTIVES = ['show', 'model', 'bind', 'cloak', 'else', 'else-if', 'for', 'html', 'if', 'memo', 'on', 'once', 'pre', 'slot', 'text'];
126
123
  const resolveComponentOptions = Component => {
127
124
  const opts = typeof Component === 'function' && Component.options ? {
@@ -41,6 +41,16 @@ if (isGlobalVue3) {
41
41
  };
42
42
  }
43
43
  const isVue3 = instance => Boolean(instance.$);
44
+
45
+ // Roots created with `new (Vue.extend(Comp))()` expose `Comp`, not `Vue`, as the
46
+ // constructor. Walk up to the base so its options are never merged into helpers.
47
+ const getVue3Constructor = instance => {
48
+ let Ctor = instance.$.appContext.config.globalProperties.constructor;
49
+ while (Ctor.super) {
50
+ Ctor = Ctor.super;
51
+ }
52
+ return Ctor;
53
+ };
44
54
  const extend = Vue.extend.bind(Vue);
45
55
  function mergeData() {
46
56
  var _result$attrs, _result$props;
@@ -52,4 +62,4 @@ function mergeData() {
52
62
  };
53
63
  }
54
64
 
55
- export { COMPONENT_UID_KEY, REF_FOR_KEY, extend, isGlobalVue3, isVue3, mergeData };
65
+ export { COMPONENT_UID_KEY, REF_FOR_KEY, extend, getVue3Constructor, isGlobalVue3, isVue3, mergeData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "137.2.4",
3
+ "version": "137.2.6",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -109,7 +109,7 @@
109
109
  "@gitlab/fonts": "^1.3.1",
110
110
  "@gitlab/hybrid-vue": "npm:@vue/compat@3.5.34",
111
111
  "@gitlab/svgs": "*",
112
- "@jest/test-sequencer": "30.5.1",
112
+ "@jest/test-sequencer": "30.5.2",
113
113
  "@rollup/plugin-commonjs": "^28.0.9",
114
114
  "@rollup/plugin-node-resolve": "^16.0.3",
115
115
  "@rollup/plugin-replace": "^6.0.3",
@@ -131,13 +131,13 @@
131
131
  "@vue/compiler-sfc": "3.5.32",
132
132
  "@vue/server-renderer": "3.5.32",
133
133
  "@vue/test-utils": "1.3.6",
134
- "@vue/test-utils-vue3": "npm:@vue/test-utils@^2.5.0",
134
+ "@vue/test-utils-vue3": "npm:@vue/test-utils@^2.5.1",
135
135
  "@vue/vue2-jest": "29.2.6",
136
136
  "@vue/vue3-jest": "^29.2.6",
137
137
  "@yarnpkg/lockfile": "^1.1.0",
138
138
  "acorn": "^8.18.0",
139
139
  "acorn-walk": "^8.3.5",
140
- "autoprefixer": "10.6.0",
140
+ "autoprefixer": "10.6.1",
141
141
  "axe-playwright": "^2.2.2",
142
142
  "babel-loader": "^9.2.1",
143
143
  "cypress": "15.21.1",
@@ -147,10 +147,10 @@
147
147
  "gitlab-api-async-iterator": "^1.3.1",
148
148
  "glob": "11.1.0",
149
149
  "identity-obj-proxy": "^3.0.0",
150
- "jest": "30.5.1",
151
- "jest-circus": "30.5.1",
152
- "jest-environment-jsdom": "30.5.1",
153
- "jest-environment-node": "30.5.1",
150
+ "jest": "30.5.2",
151
+ "jest-circus": "30.5.2",
152
+ "jest-environment-jsdom": "30.5.2",
153
+ "jest-environment-node": "30.5.2",
154
154
  "jest-image-snapshot": "^6.5.2",
155
155
  "merge-cobertura": "^1.0.9",
156
156
  "mockdate": "^3.0.5",
@@ -175,7 +175,7 @@
175
175
  "start-server-and-test": "^2.1.5",
176
176
  "storybook": "^7.6.24",
177
177
  "storybook-dark-mode": "4.0.2",
178
- "style-dictionary": "^5.5.3",
178
+ "style-dictionary": "^5.5.4",
179
179
  "style-loader": "^4",
180
180
  "tailwindcss": "3.4.19",
181
181
  "vue": "2.7.16",
@@ -370,14 +370,10 @@ export default {
370
370
  if (
371
371
  this.autoClose &&
372
372
  e.target.closest(ITEM_SELECTOR) &&
373
- // The optional chaining here is to accommodate specs that use shallow mounting.
374
- // Ideally, those specs would either:
375
- // - stub out the `BaseDropdown` component to include a stub `containsElement` method, or
376
- // - use a full mount.
377
- //
378
- // Unfortunately, GitLab has many specs which do neither of these. So the simpler thing to do
379
- // is to optionally chain the method call here.
380
- this.$refs.baseDropdown.containsElement?.(e.target)
373
+ // The ref is null when an item action destroys this component before the click bubbles here.
374
+ // The method is optionally chained to accommodate specs that shallow mount without stubbing
375
+ // `BaseDropdown`'s `containsElement`; GitLab has many such specs.
376
+ this.$refs.baseDropdown?.containsElement?.(e.target)
381
377
  ) {
382
378
  this.closeAndFocus();
383
379
  }
@@ -276,7 +276,7 @@ export default {
276
276
  </div>
277
277
  </gl-dropdown-item>
278
278
 
279
- <gl-dropdown-item v-else-if="!dropdownItems.length" role="option" disabled>
279
+ <gl-dropdown-item v-else-if="!dropdownItems.length && !loading" role="option" disabled>
280
280
  <slot name="no-results-content">No matches found</slot>
281
281
  </gl-dropdown-item>
282
282
 
@@ -68,19 +68,17 @@ const mousedown = 'mousedown';
68
68
  const mouseup = 'mouseup';
69
69
 
70
70
  const startListening = (eventTypes) => {
71
+ // Listening to mouse events ensures that a text selection doesn't trigger the
72
+ // GlOutsideDirective 'click' callback if the selection started within the target element,
73
+ // and that a Safari focus-loss 'focusin' doesn't trigger the callback either. Nothing is
74
+ // listening yet when the set is empty, so this is bound exactly once.
75
+ if (activeEventTypes.size === 0) {
76
+ document.addEventListener(mousedown, onMousedown);
77
+ document.addEventListener(mouseup, onMouseup);
78
+ }
79
+
71
80
  eventTypes.forEach((eventType) => {
72
81
  if (!activeEventTypes.has(eventType)) {
73
- // Listening to mousedown events, ensures that a text selection doesn't trigger the
74
- // GlOutsideDirective 'click' callback if the selection started within the target element,
75
- // and that a Safari focus-loss 'focusin' doesn't trigger the callback either. Tracked via
76
- // activeEventTypes too, so it's only ever bound/unbound once, regardless of whether 'click'
77
- // and 'focusin' are (de)activated together or separately.
78
- if ((eventType === click || eventType === focusin) && !activeEventTypes.has(mousedown)) {
79
- document.addEventListener(mousedown, onMousedown);
80
- document.addEventListener(mouseup, onMouseup);
81
- activeEventTypes.add(mousedown);
82
- }
83
-
84
82
  // Added { capture: true } to all event types to prevent the behavior discussed in https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1686#note_412545027
85
83
  // Ensures the event listener handles the event in the capturing phase, avoiding issues encountered previously.
86
84
  // Cannot be tested with Jest or Cypress, but can be tested with Playwright in the future: https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/4272#note_1947425384
@@ -97,21 +95,16 @@ const stopListening = (eventTypesToUnbind) => {
97
95
  eventTypesToUnbind.forEach((eventType) => {
98
96
  if (activeEventTypes.has(eventType)) {
99
97
  if ([...callbacks.values()].every(({ eventTypes }) => !eventTypes.includes(eventType))) {
100
- document.removeEventListener(eventType, globalListener);
98
+ document.removeEventListener(eventType, globalListener, { capture: true });
101
99
  activeEventTypes.delete(eventType);
102
100
  }
103
101
  }
104
102
  });
105
103
 
106
- // Only unbind mousedown once neither 'click' nor 'focusin' need it anymore.
107
- if (
108
- activeEventTypes.has(mousedown) &&
109
- !activeEventTypes.has(click) &&
110
- !activeEventTypes.has(focusin)
111
- ) {
104
+ // Nothing is listening anymore, so the mouse gesture tracking isn't needed either.
105
+ if (activeEventTypes.size === 0) {
112
106
  document.removeEventListener(mousedown, onMousedown);
113
107
  document.removeEventListener(mouseup, onMouseup);
114
- activeEventTypes.delete(mousedown);
115
108
  }
116
109
  };
117
110
 
@@ -1,4 +1,4 @@
1
- import { isVue3, extend } from '../../vue'
1
+ import { isVue3, extend, getVue3Constructor } from '../../vue'
2
2
  import { NAME_TRANSPORTER, NAME_TRANSPORTER_TARGET } from '../../constants/components'
3
3
  import { IS_BROWSER } from '../../constants/env'
4
4
  import { HTMLElement } from '../../constants/safe-types'
@@ -199,7 +199,7 @@ export const BVTransporter = /*#__PURE__*/ extend({
199
199
  }
200
200
  }
201
201
 
202
- const { Teleport } = this.$.appContext.config.globalProperties.constructor
202
+ const { Teleport } = getVue3Constructor(this)
203
203
 
204
204
  return h(
205
205
  Teleport,
@@ -1,4 +1,4 @@
1
- import { isVue3 } from '../vue'
1
+ import { isVue3, getVue3Constructor } from '../vue'
2
2
 
3
3
  // Regex to detect event handler props: onSomething or onSomethingOnce
4
4
  const EVENT_HANDLER_RE = /^on([A-Z][a-zA-Z]*)$/
@@ -61,10 +61,6 @@ const extractEventHandlers = config => {
61
61
  return { handlers, cleanConfig }
62
62
  }
63
63
 
64
- const getVue3Constructor = parent => {
65
- return parent.$.appContext.config.globalProperties.constructor
66
- }
67
-
68
64
  const BUILTIN_DIRECTIVES = [
69
65
  'show',
70
66
  'model',
@@ -47,6 +47,16 @@ if (isGlobalVue3) {
47
47
 
48
48
  const isVue3 = instance => Boolean(instance.$)
49
49
 
50
+ // Roots created with `new (Vue.extend(Comp))()` expose `Comp`, not `Vue`, as the
51
+ // constructor. Walk up to the base so its options are never merged into helpers.
52
+ const getVue3Constructor = instance => {
53
+ let Ctor = instance.$.appContext.config.globalProperties.constructor
54
+ while (Ctor.super) {
55
+ Ctor = Ctor.super
56
+ }
57
+ return Ctor
58
+ }
59
+
50
60
  const extend = Vue.extend.bind(Vue)
51
61
 
52
62
  function mergeData(...data) {
@@ -54,4 +64,4 @@ function mergeData(...data) {
54
64
  return { ...result, ...(result.attrs ?? {}), ...(result.props ?? {}) }
55
65
  }
56
66
 
57
- export { COMPONENT_UID_KEY, mergeData, isGlobalVue3, isVue3, extend }
67
+ export { COMPONENT_UID_KEY, mergeData, isGlobalVue3, isVue3, getVue3Constructor, extend }