@gitlab/ui 72.7.0 → 72.8.0

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/components/base/dropdown/dropdown.js +2 -6
  3. package/dist/components/base/filtered_search/filtered_search_term.js +2 -0
  4. package/dist/components/base/filtered_search/filtered_search_token.js +3 -1
  5. package/dist/components/base/filtered_search/filtered_search_token_segment.js +4 -1
  6. package/dist/components/base/new_dropdowns/disclosure/utils.js +5 -1
  7. package/dist/components/base/new_dropdowns/listbox/utils.js +6 -0
  8. package/dist/components/base/sorting/sorting.js +1 -0
  9. package/dist/components/charts/column/column.js +1 -1
  10. package/dist/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.js +2 -0
  11. package/dist/components/experimental/duo/chat/duo_chat.js +2 -0
  12. package/dist/components/utilities/sprintf/sprintf.js +3 -1
  13. package/dist/tokens/css/tokens.css +1 -1
  14. package/dist/tokens/css/tokens.dark.css +1 -1
  15. package/dist/tokens/js/tokens.dark.js +1 -1
  16. package/dist/tokens/js/tokens.js +1 -1
  17. package/dist/tokens/scss/_tokens.dark.scss +1 -1
  18. package/dist/tokens/scss/_tokens.scss +1 -1
  19. package/dist/utils/is_slot_empty.js +4 -0
  20. package/dist/utils/number_utils.js +1 -1
  21. package/dist/utils/utils.js +1 -1
  22. package/package.json +3 -3
  23. package/src/components/base/broadcast_message/broadcast_message.stories.js +1 -0
  24. package/src/components/base/drawer/drawer.spec.js +1 -0
  25. package/src/components/base/dropdown/dropdown.vue +2 -6
  26. package/src/components/base/filtered_search/filtered_search.spec.js +11 -0
  27. package/src/components/base/filtered_search/filtered_search_term.vue +1 -0
  28. package/src/components/base/filtered_search/filtered_search_token.vue +1 -0
  29. package/src/components/base/filtered_search/filtered_search_token_segment.vue +3 -1
  30. package/src/components/base/form/form_fields/form_fields.spec.js +1 -0
  31. package/src/components/base/new_dropdowns/disclosure/utils.js +2 -0
  32. package/src/components/base/new_dropdowns/listbox/listbox.spec.js +2 -0
  33. package/src/components/base/new_dropdowns/listbox/listbox.stories.js +4 -0
  34. package/src/components/base/new_dropdowns/listbox/utils.js +3 -0
  35. package/src/components/base/sorting/sorting.vue +1 -0
  36. package/src/components/charts/column/__snapshots__/column_chart.spec.js.snap +1 -1
  37. package/src/components/charts/column/column.stories.js +18 -3
  38. package/src/components/charts/column/column.vue +16 -1
  39. package/src/components/charts/column/column_chart.spec.js +106 -20
  40. package/src/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.vue +1 -0
  41. package/src/components/experimental/duo/chat/duo_chat.vue +1 -0
  42. package/src/components/utilities/intersection_observer/intersection_observer.spec.js +1 -0
  43. package/src/components/utilities/intersperse/intersperse.spec.js +1 -0
  44. package/src/components/utilities/sprintf/sprintf.vue +1 -0
  45. package/src/utils/is_slot_empty.js +3 -0
  46. package/src/utils/number_utils.js +1 -1
  47. package/src/utils/utils.js +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [72.8.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v72.7.0...v72.8.0) (2024-01-16)
2
+
3
+
4
+ ### Features
5
+
6
+ * **GlColumnChart:** allow customising chart tooltip ([ed6ee9e](https://gitlab.com/gitlab-org/gitlab-ui/commit/ed6ee9e3139cba2793d90d955c39c41f6ab2b5af))
7
+
1
8
  # [72.7.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v72.6.0...v72.7.0) (2024-01-14)
2
9
 
3
10
 
@@ -1,8 +1,9 @@
1
1
  import Vue from 'vue';
2
2
  import { BDropdown } from 'bootstrap-vue/esm/index.js';
3
- import { selectAll, isVisible } from 'bootstrap-vue/esm/utils/dom';
3
+ import { selectAll } from 'bootstrap-vue/esm/utils/dom';
4
4
  import merge from 'lodash/merge';
5
5
  import { buttonCategoryOptions, dropdownVariantOptions, buttonSizeOptions } from '../../../utils/constants';
6
+ import { filterVisible } from '../../../utils/utils';
6
7
  import { ButtonMixin } from '../../mixins/button_mixin';
7
8
  import GlButton from '../button/button';
8
9
  import GlIcon from '../icon/icon';
@@ -11,11 +12,6 @@ import GlDropdownDivider from './dropdown_divider';
11
12
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
12
13
 
13
14
  //
14
-
15
- // Return an Array of visible items
16
- function filterVisible(els) {
17
- return (els || []).filter(isVisible);
18
- }
19
15
  const Selector = {
20
16
  ITEM_SELECTOR: '.dropdown-item:not(.disabled):not([disabled]),.form-control:not(.disabled):not([disabled])'
21
17
  };
@@ -102,6 +102,8 @@ var script = {
102
102
  title: this.searchTextOptionLabel
103
103
  });
104
104
  }
105
+
106
+ // eslint-disable-next-line unicorn/no-array-callback-reference
105
107
  return tokens.map(tokenToOption);
106
108
  },
107
109
  internalValue: {
@@ -109,7 +109,9 @@ var script = {
109
109
  return hasData || this.isSegmentActive(SEGMENT_DATA);
110
110
  },
111
111
  availableTokensWithSelf() {
112
- return [this.config, ...this.availableTokens.filter(token => token !== this.config)].map(tokenToOption);
112
+ return [this.config, ...this.availableTokens.filter(token => token !== this.config)].map(
113
+ // eslint-disable-next-line unicorn/no-array-callback-reference
114
+ tokenToOption);
113
115
  },
114
116
  operatorDescription() {
115
117
  const operator = this.operators.find(op => op.value === this.tokenValue.operator);
@@ -28,7 +28,9 @@ const isVue3Fragment = vnode => {
28
28
  const isVNodeEmpty = vnode => {
29
29
  if (isVue3Fragment(vnode)) {
30
30
  // vnode.children might be an array or single node in edge cases
31
- return Array.isArray(vnode.children) ? vnode.children.every(isVNodeEmpty) : isVNodeEmpty(vnode.children);
31
+ return Array.isArray(vnode.children) ?
32
+ // eslint-disable-next-line unicorn/no-array-callback-reference
33
+ vnode.children.every(isVNodeEmpty) : isVNodeEmpty(vnode.children);
32
34
  }
33
35
  if (isVue3Comment(vnode)) {
34
36
  return true;
@@ -40,6 +42,7 @@ const isSlotNotEmpty = slot => {
40
42
  return false;
41
43
  }
42
44
  const vnodes = typeof slot === 'function' ? slot() : slot;
45
+ // eslint-disable-next-line unicorn/no-array-callback-reference
43
46
  return !(Array.isArray(vnodes) ? vnodes.every(isVNodeEmpty) : isVNodeEmpty(vnodes));
44
47
  };
45
48
  var script = {
@@ -6,7 +6,11 @@ const itemValidator = item => {
6
6
  return (item === null || item === void 0 ? void 0 : (_item$text = item.text) === null || _item$text === void 0 ? void 0 : _item$text.length) > 0 && !Array.isArray(item === null || item === void 0 ? void 0 : item.items);
7
7
  };
8
8
  const isItem = item => Boolean(item) && itemValidator(item);
9
- const isGroup = group => Boolean(group) && Array.isArray(group.items) && Boolean(group.items.length) && group.items.every(isItem);
9
+ const isGroup = group => Boolean(group) && Array.isArray(group.items) && Boolean(group.items.length) &&
10
+ // eslint-disable-next-line unicorn/no-array-callback-reference
11
+ group.items.every(isItem);
12
+
13
+ // eslint-disable-next-line unicorn/no-array-callback-reference
10
14
  const itemsValidator = items => items.every(isItem) || items.every(isGroup);
11
15
  const isListItem = tag => ['gl-disclosure-dropdown-group', 'gl-disclosure-dropdown-item', 'li'].includes(tag);
12
16
  const isValidSlotTagVue2 = vNode => {
@@ -2,6 +2,8 @@ import isNumber from 'lodash/isNumber';
2
2
  import isString from 'lodash/isString';
3
3
 
4
4
  const isOption = item => Boolean(item) && (isString(item.value) || isNumber(item.value));
5
+
6
+ // eslint-disable-next-line unicorn/no-array-callback-reference
5
7
  const isGroup = function () {
6
8
  let {
7
9
  options
@@ -10,6 +12,8 @@ const isGroup = function () {
10
12
  };
11
13
  const hasNoDuplicates = array => array.length === new Set(array).size;
12
14
  const flattenedOptions = items => items.flatMap(item => isOption(item) ? item : item.options);
15
+
16
+ // eslint-disable-next-line unicorn/no-array-callback-reference
13
17
  const isAllOptionsOrAllGroups = items => items.every(isOption) || items.every(isGroup);
14
18
  const hasUniqueValues = items => hasNoDuplicates(flattenedOptions(items).map(_ref => {
15
19
  let {
@@ -17,6 +21,8 @@ const hasUniqueValues = items => hasNoDuplicates(flattenedOptions(items).map(_re
17
21
  } = _ref;
18
22
  return value;
19
23
  }));
24
+
25
+ // eslint-disable-next-line unicorn/no-array-callback-reference
20
26
  const hasUniqueGroups = items => hasNoDuplicates(items.filter(isGroup).map(_ref2 => {
21
27
  let {
22
28
  text
@@ -34,6 +34,7 @@ var script = {
34
34
  type: Array,
35
35
  required: false,
36
36
  default: null,
37
+ // eslint-disable-next-line unicorn/no-array-callback-reference
37
38
  validator: sortOptions => sortOptions.every(isOption)
38
39
  },
39
40
  /**
@@ -191,7 +191,7 @@ const __vue_script__ = script;
191
191
  /* template */
192
192
  var __vue_render__ = function () {
193
193
  var _obj;
194
- var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"position-relative",class:( _obj = {}, _obj[_vm.$options.HEIGHT_AUTO_CLASSES] = _vm.autoHeight, _obj )},[_c('chart',_vm._g(_vm._b({class:{ 'gl-flex-grow-1': _vm.autoHeight },attrs:{"height":_vm.height,"options":_vm.options},on:{"created":_vm.onCreated}},'chart',_vm.$attrs,false),_vm.$listeners)),_vm._v(" "),(_vm.chart)?_c('chart-tooltip',{attrs:{"chart":_vm.chart,"use-default-tooltip-formatter":true}}):_vm._e()],1)};
194
+ var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"position-relative",class:( _obj = {}, _obj[_vm.$options.HEIGHT_AUTO_CLASSES] = _vm.autoHeight, _obj )},[_c('chart',_vm._g(_vm._b({class:{ 'gl-flex-grow-1': _vm.autoHeight },attrs:{"height":_vm.height,"options":_vm.options},on:{"created":_vm.onCreated}},'chart',_vm.$attrs,false),_vm.$listeners)),_vm._v(" "),(_vm.chart)?_c('chart-tooltip',{ref:"dataTooltip",attrs:{"chart":_vm.chart,"use-default-tooltip-formatter":true},scopedSlots:_vm._u([(_vm.$scopedSlots['tooltip-title'])?{key:"title",fn:function(scope){return [_vm._t("tooltip-title",null,null,scope)]}}:null,(_vm.$scopedSlots['tooltip-content'])?{key:"default",fn:function(scope){return [_vm._t("tooltip-content",null,null,scope)]}}:null,(_vm.$scopedSlots['tooltip-value'])?{key:"tooltip-value",fn:function(scope){return [_vm._t("tooltip-value",null,null,scope)]}}:null],null,true)}):_vm._e()],1)};
195
195
  var __vue_staticRenderFns__ = [];
196
196
 
197
197
  /* style */
@@ -5,6 +5,8 @@ const i18n = {
5
5
  CONVERSATION_NEW_CHAT: 'New chat'
6
6
  };
7
7
  const isMessage = item => Boolean(item) && (item === null || item === void 0 ? void 0 : item.role);
8
+
9
+ // eslint-disable-next-line unicorn/no-array-callback-reference
8
10
  const itemsValidator = items => items.every(isMessage);
9
11
  var script = {
10
12
  name: 'GlDuoChatConversation',
@@ -48,6 +48,8 @@ const slashCommands = [{
48
48
  description: 'Explain the selected snippet.'
49
49
  }];
50
50
  const isMessage = item => Boolean(item) && (item === null || item === void 0 ? void 0 : item.role);
51
+
52
+ // eslint-disable-next-line unicorn/no-array-callback-reference
51
53
  const itemsValidator = items => items.every(isMessage);
52
54
  var script = {
53
55
  name: 'GlDuoChat',
@@ -63,7 +63,9 @@ var script = {
63
63
  type: Object,
64
64
  required: false,
65
65
  default: undefined,
66
- validator: value => Object.values(value).every(tagPair => Array.isArray(tagPair) && tagPair.length === 2 && tagPair.every(isString))
66
+ validator: value => Object.values(value).every(
67
+ // eslint-disable-next-line unicorn/no-array-callback-reference
68
+ tagPair => Array.isArray(tagPair) && tagPair.length === 2 && tagPair.every(isString))
67
69
  }
68
70
  },
69
71
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Sun, 14 Jan 2024 23:39:36 GMT
3
+ * Generated on Mon, 15 Jan 2024 23:51:36 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Sun, 14 Jan 2024 23:39:37 GMT
3
+ * Generated on Mon, 15 Jan 2024 23:51:36 GMT
4
4
  */
5
5
 
6
6
  :root.gl-dark {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Sun, 14 Jan 2024 23:39:37 GMT
3
+ * Generated on Mon, 15 Jan 2024 23:51:36 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#133a03";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Sun, 14 Jan 2024 23:39:36 GMT
3
+ * Generated on Mon, 15 Jan 2024 23:51:36 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#ddfab7";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Sun, 14 Jan 2024 23:39:37 GMT
3
+ // Generated on Mon, 15 Jan 2024 23:51:36 GMT
4
4
 
5
5
  $red-950: #fff4f3;
6
6
  $red-900: #fcf1ef;
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Sun, 14 Jan 2024 23:39:37 GMT
3
+ // Generated on Mon, 15 Jan 2024 23:51:36 GMT
4
4
 
5
5
  $gl-line-height-52: 3.25rem;
6
6
  $gl-line-height-44: 2.75rem;
@@ -13,10 +13,12 @@ function isEmpty(vnode) {
13
13
  return true;
14
14
  }
15
15
  if (Array.isArray(vnode)) {
16
+ // eslint-disable-next-line unicorn/no-array-callback-reference
16
17
  return vnode.every(isEmpty);
17
18
  }
18
19
  if (Fragment && vnode.type === Fragment) {
19
20
  // Vue.js 3 fragment, check children
21
+ // eslint-disable-next-line unicorn/no-array-callback-reference
20
22
  return vnode.children.every(isEmpty);
21
23
  }
22
24
  return false;
@@ -28,6 +30,8 @@ function isSlotEmpty(vueInstance, slot, slotArgs) {
28
30
  // we need to check both $slots and $scopedSlots due to https://github.com/vuejs/core/issues/8869
29
31
  // additionally, in @vue/compat $slot might be a function instead of array of vnodes (sigh)
30
32
  callIfNeeded(vueInstance.$slots[slot] || vueInstance.$scopedSlots[slot], slotArgs) : (_vueInstance$$scopedS = (_vueInstance$$scopedS2 = vueInstance.$scopedSlots)[slot]) === null || _vueInstance$$scopedS === void 0 ? void 0 : _vueInstance$$scopedS.call(_vueInstance$$scopedS2, slotArgs);
33
+
34
+ // eslint-disable-next-line unicorn/no-array-callback-reference
31
35
  return isEmpty(slotContent);
32
36
  }
33
37
 
@@ -14,7 +14,7 @@ const sum = function () {
14
14
  numbers[_key] = arguments[_key];
15
15
  }
16
16
  return numbers.reduce(addition);
17
- };
17
+ }; // eslint-disable-line unicorn/no-array-callback-reference
18
18
 
19
19
  /**
20
20
  * Returns the average of all arguments
@@ -171,7 +171,7 @@ function stopEvent(event) {
171
171
  * Return an Array of visible items
172
172
  */
173
173
  function filterVisible(els) {
174
- return (els || []).filter(isVisible);
174
+ return (els || []).filter(el => isVisible(el));
175
175
  }
176
176
 
177
177
  export { colorFromBackground, debounceByAnimationFrame, filterVisible, focusFirstFocusableElement, hexToRgba, isDev, isElementFocusable, isElementTabbable, logWarning, relativeLuminance, rgbFromHex, rgbFromString, stopEvent, throttle, toSrgb, uid };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "72.7.0",
3
+ "version": "72.8.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -93,10 +93,10 @@
93
93
  "@babel/preset-env": "^7.23.8",
94
94
  "@babel/preset-react": "^7.23.3",
95
95
  "@cypress/grep": "^4.0.1",
96
- "@gitlab/eslint-plugin": "19.2.0",
96
+ "@gitlab/eslint-plugin": "19.4.0",
97
97
  "@gitlab/fonts": "^1.3.0",
98
98
  "@gitlab/stylelint-config": "5.0.1",
99
- "@gitlab/svgs": "3.74.0",
99
+ "@gitlab/svgs": "3.75.0",
100
100
  "@rollup/plugin-commonjs": "^11.1.0",
101
101
  "@rollup/plugin-node-resolve": "^7.1.3",
102
102
  "@rollup/plugin-replace": "^2.3.2",
@@ -69,6 +69,7 @@ const StackedStory = (args, { argTypes }) => ({
69
69
  GlBroadcastMessage,
70
70
  },
71
71
  props: Object.keys(argTypes),
72
+ // eslint-disable-next-line unicorn/no-array-callback-reference
72
73
  template: `<div>${Object.keys(colorThemes).map(templateWithTheme).join('')}</div>`,
73
74
  });
74
75
  export const Stacked = StackedStory.bind({});
@@ -98,6 +98,7 @@ describe('drawer component', () => {
98
98
  },
99
99
  });
100
100
 
101
+ // eslint-disable-next-line unicorn/no-array-callback-reference
101
102
  expect(wrapper.find(parentSelector).find(`[data-testid="${slot}"]`).exists()).toBe(true);
102
103
  });
103
104
 
@@ -2,24 +2,20 @@
2
2
  <script>
3
3
  import Vue from 'vue';
4
4
  import { BDropdown } from 'bootstrap-vue';
5
- import { isVisible, selectAll } from 'bootstrap-vue/src/utils/dom';
5
+ import { selectAll } from 'bootstrap-vue/src/utils/dom';
6
6
  import merge from 'lodash/merge';
7
7
  import {
8
8
  buttonCategoryOptions,
9
9
  buttonSizeOptions,
10
10
  dropdownVariantOptions,
11
11
  } from '../../../utils/constants';
12
+ import { filterVisible } from '../../../utils/utils';
12
13
  import { ButtonMixin } from '../../mixins/button_mixin';
13
14
  import GlButton from '../button/button.vue';
14
15
  import GlIcon from '../icon/icon.vue';
15
16
  import GlLoadingIcon from '../loading_icon/loading_icon.vue';
16
17
  import GlDropdownDivider from './dropdown_divider.vue';
17
18
 
18
- // Return an Array of visible items
19
- function filterVisible(els) {
20
- return (els || []).filter(isVisible);
21
- }
22
-
23
19
  const Selector = {
24
20
  ITEM_SELECTOR:
25
21
  '.dropdown-item:not(.disabled):not([disabled]),.form-control:not(.disabled):not([disabled])',
@@ -59,6 +59,7 @@ describe('Filtered search', () => {
59
59
  describe('value manipulation', () => {
60
60
  it('creates term when empty', () => {
61
61
  createComponent();
62
+ // eslint-disable-next-line unicorn/no-array-callback-reference
62
63
  expect(wrapper.emitted().input[0][0].map(stripId)).toStrictEqual([
63
64
  { type: TERM_TOKEN_TYPE, value: { data: '' } },
64
65
  ]);
@@ -202,6 +203,7 @@ describe('Filtered search', () => {
202
203
 
203
204
  await nextTick();
204
205
 
206
+ // eslint-disable-next-line unicorn/no-array-callback-reference
205
207
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
206
208
  { type: 'faketoken', value: { data: '' } },
207
209
  { type: TERM_TOKEN_TYPE, value: { data: 'one' } },
@@ -220,6 +222,7 @@ describe('Filtered search', () => {
220
222
 
221
223
  await nextTick();
222
224
 
225
+ // eslint-disable-next-line unicorn/no-array-callback-reference
223
226
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
224
227
  { type: TERM_TOKEN_TYPE, value: { data: 'one' } },
225
228
  { type: TERM_TOKEN_TYPE, value: { data: '' } },
@@ -335,6 +338,7 @@ describe('Filtered search', () => {
335
338
 
336
339
  await nextTick();
337
340
 
341
+ // eslint-disable-next-line unicorn/no-array-callback-reference
338
342
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
339
343
  { type: TERM_TOKEN_TYPE, value: { data: '' } },
340
344
  ]);
@@ -348,6 +352,7 @@ describe('Filtered search', () => {
348
352
 
349
353
  await nextTick();
350
354
 
355
+ // eslint-disable-next-line unicorn/no-array-callback-reference
351
356
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
352
357
  { type: 'faketoken', value: { data: 'test' } },
353
358
  { type: TERM_TOKEN_TYPE, value: { data: '' } },
@@ -369,6 +374,7 @@ describe('Filtered search', () => {
369
374
 
370
375
  await nextTick();
371
376
 
377
+ // eslint-disable-next-line unicorn/no-array-callback-reference
372
378
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
373
379
  { type: 'faketoken', value: { data: 'test' } },
374
380
  { type: TERM_TOKEN_TYPE, value: { data: '' } },
@@ -382,6 +388,7 @@ describe('Filtered search', () => {
382
388
 
383
389
  await nextTick();
384
390
 
391
+ // eslint-disable-next-line unicorn/no-array-callback-reference
385
392
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
386
393
  { type: TERM_TOKEN_TYPE, value: { data: 'one' } },
387
394
  { type: TERM_TOKEN_TYPE, value: { data: '' } },
@@ -398,6 +405,7 @@ describe('Filtered search', () => {
398
405
  await nextTick();
399
406
 
400
407
  expect(wrapper.findAllComponents(GlFilteredSearchTerm).at(2).props('active')).toBe(true);
408
+ // eslint-disable-next-line unicorn/no-array-callback-reference
401
409
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
402
410
  { type: TERM_TOKEN_TYPE, value: { data: 'one' } },
403
411
  { type: TERM_TOKEN_TYPE, value: { data: 'two' } },
@@ -415,6 +423,7 @@ describe('Filtered search', () => {
415
423
 
416
424
  await nextTick();
417
425
 
426
+ // eslint-disable-next-line unicorn/no-array-callback-reference
418
427
  expect(wrapper.emitted().input.pop()[0].map(stripId)).toStrictEqual([
419
428
  { type: TERM_TOKEN_TYPE, value: { data: 'one' } },
420
429
  { type: TERM_TOKEN_TYPE, value: { data: 'foo' } },
@@ -445,6 +454,7 @@ describe('Filtered search', () => {
445
454
  });
446
455
  wrapper.findComponent(GlFilteredSearchTerm).vm.$emit('submit');
447
456
  expect(wrapper.emitted('submit')).toBeDefined();
457
+ // eslint-disable-next-line unicorn/no-array-callback-reference
448
458
  expect(wrapper.emitted().submit[0][0].map(stripId)).toStrictEqual([
449
459
  'one two',
450
460
  { type: 'faketoken', value: { data: 'smth' } },
@@ -505,6 +515,7 @@ describe('Filtered search', () => {
505
515
  });
506
516
  await nextTick();
507
517
 
518
+ // eslint-disable-next-line unicorn/no-array-callback-reference
508
519
  expect(wrapper.findComponent(GlFilteredSearchTerm).props('currentValue').map(stripId)).toEqual([
509
520
  { type: 'filtered-search-term', value: { data: 'one' } },
510
521
  { type: 'filtered-search-term', value: { data: '' } },
@@ -108,6 +108,7 @@ export default {
108
108
  });
109
109
  }
110
110
 
111
+ // eslint-disable-next-line unicorn/no-array-callback-reference
111
112
  return tokens.map(tokenToOption);
112
113
  },
113
114
  internalValue: {
@@ -109,6 +109,7 @@ export default {
109
109
 
110
110
  availableTokensWithSelf() {
111
111
  return [this.config, ...this.availableTokens.filter((token) => token !== this.config)].map(
112
+ // eslint-disable-next-line unicorn/no-array-callback-reference
112
113
  tokenToOption
113
114
  );
114
115
  },
@@ -24,7 +24,8 @@ const isVNodeEmpty = (vnode) => {
24
24
  if (isVue3Fragment(vnode)) {
25
25
  // vnode.children might be an array or single node in edge cases
26
26
  return Array.isArray(vnode.children)
27
- ? vnode.children.every(isVNodeEmpty)
27
+ ? // eslint-disable-next-line unicorn/no-array-callback-reference
28
+ vnode.children.every(isVNodeEmpty)
28
29
  : isVNodeEmpty(vnode.children);
29
30
  }
30
31
 
@@ -41,6 +42,7 @@ const isSlotNotEmpty = (slot) => {
41
42
  }
42
43
 
43
44
  const vnodes = typeof slot === 'function' ? slot() : slot;
45
+ // eslint-disable-next-line unicorn/no-array-callback-reference
44
46
  return !(Array.isArray(vnodes) ? vnodes.every(isVNodeEmpty) : isVNodeEmpty(vnodes));
45
47
  };
46
48
 
@@ -78,6 +78,7 @@ describe('GlFormFields', () => {
78
78
  };
79
79
  };
80
80
  const findFormGroups = () => wrapper.findAllComponents(GlFormGroup).wrappers;
81
+ // eslint-disable-next-line unicorn/no-array-callback-reference
81
82
  const findFormGroupsAsData = () => findFormGroups().map(mapFormGroupToData);
82
83
  const findFormGroupFromLabel = (label) =>
83
84
  wrapper.findAllComponents(GlFormGroup).wrappers.find((x) => x.attributes('label') === label);
@@ -9,8 +9,10 @@ const isGroup = (group) =>
9
9
  Boolean(group) &&
10
10
  Array.isArray(group.items) &&
11
11
  Boolean(group.items.length) &&
12
+ // eslint-disable-next-line unicorn/no-array-callback-reference
12
13
  group.items.every(isItem);
13
14
 
15
+ // eslint-disable-next-line unicorn/no-array-callback-reference
14
16
  const itemsValidator = (items) => items.every(isItem) || items.every(isGroup);
15
17
 
16
18
  const isListItem = (tag) =>
@@ -48,6 +48,7 @@ describe('GlCollapsibleListbox', () => {
48
48
  const findListContainer = () => wrapper.find('[role="listbox"]');
49
49
  const findListboxItems = (root = wrapper) => root.findAllComponents(GlListboxItem);
50
50
  const findListboxGroups = () => wrapper.findAllComponents(GlListboxGroup);
51
+ // eslint-disable-next-line unicorn/no-array-callback-reference
51
52
  const findListItem = (index) => findListboxItems().at(index).find(ITEM_SELECTOR);
52
53
  const findHeaderText = () => wrapper.find("[data-testid='listbox-header-text']");
53
54
  const findSearchBox = () => wrapper.find("[data-testid='listbox-search-input']");
@@ -241,6 +242,7 @@ describe('GlCollapsibleListbox', () => {
241
242
 
242
243
  it('should focus the first selected item', async () => {
243
244
  await showDropdown();
245
+ // eslint-disable-next-line unicorn/no-array-callback-reference
244
246
  expect(findListboxItems().at(1).find(ITEM_SELECTOR).element).toHaveFocus();
245
247
  });
246
248
 
@@ -388,9 +388,13 @@ export const Groups = makeGroupedExample({
388
388
  const isSelected = (option) => this.selected.includes(option.value);
389
389
  const notSelected = (option) => !isSelected(option);
390
390
 
391
+ // eslint-disable-next-line unicorn/no-array-callback-reference
391
392
  const selectedBranches = mockGroups[0].options.filter(isSelected);
393
+ // eslint-disable-next-line unicorn/no-array-callback-reference
392
394
  const availableBranches = mockGroups[0].options.filter(notSelected);
395
+ // eslint-disable-next-line unicorn/no-array-callback-reference
393
396
  const selectedTags = mockGroups[1].options.filter(isSelected);
397
+ // eslint-disable-next-line unicorn/no-array-callback-reference
394
398
  const availableTags = mockGroups[1].options.filter(notSelected);
395
399
 
396
400
  return [
@@ -3,17 +3,20 @@ import isString from 'lodash/isString';
3
3
 
4
4
  const isOption = (item) => Boolean(item) && (isString(item.value) || isNumber(item.value));
5
5
 
6
+ // eslint-disable-next-line unicorn/no-array-callback-reference
6
7
  const isGroup = ({ options } = {}) => Array.isArray(options) && options.every(isOption);
7
8
 
8
9
  const hasNoDuplicates = (array) => array.length === new Set(array).size;
9
10
 
10
11
  const flattenedOptions = (items) => items.flatMap((item) => (isOption(item) ? item : item.options));
11
12
 
13
+ // eslint-disable-next-line unicorn/no-array-callback-reference
12
14
  const isAllOptionsOrAllGroups = (items) => items.every(isOption) || items.every(isGroup);
13
15
 
14
16
  const hasUniqueValues = (items) =>
15
17
  hasNoDuplicates(flattenedOptions(items).map(({ value }) => value));
16
18
 
19
+ // eslint-disable-next-line unicorn/no-array-callback-reference
17
20
  const hasUniqueGroups = (items) => hasNoDuplicates(items.filter(isGroup).map(({ text }) => text));
18
21
 
19
22
  const itemsValidator = (items) =>
@@ -34,6 +34,7 @@ export default {
34
34
  type: Array,
35
35
  required: false,
36
36
  default: null,
37
+ // eslint-disable-next-line unicorn/no-array-callback-reference
37
38
  validator: (sortOptions) => sortOptions.every(isOption),
38
39
  },
39
40
  /**
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`column chart component should correctly render the chart 1`] = `
3
+ exports[`column chart component mounted should correctly render the chart 1`] = `
4
4
  Object {
5
5
  "grid": Object {
6
6
  "bottom": 44,
@@ -7,7 +7,7 @@ import {
7
7
  } from '../../../utils/charts/mock_data';
8
8
  import { toolbox } from '../../../utils/charts/story_config';
9
9
 
10
- const template = `
10
+ const template = (content = '') => `
11
11
  <gl-column-chart
12
12
  :bars="bars"
13
13
  :lines="lines"
@@ -18,7 +18,9 @@ const template = `
18
18
  :x-axis-title="xAxisTitle"
19
19
  :x-axis-type="xAxisType"
20
20
  :height="height"
21
- />
21
+ >
22
+ ${content}
23
+ </gl-column-chart>
22
24
  `;
23
25
 
24
26
  const generateProps = ({
@@ -46,7 +48,7 @@ const generateProps = ({
46
48
  const Template = (args, { argTypes }) => ({
47
49
  components: { GlColumnChart },
48
50
  props: Object.keys(argTypes),
49
- template,
51
+ template: template(),
50
52
  });
51
53
 
52
54
  export const Default = Template.bind({});
@@ -90,6 +92,19 @@ SecondaryYAxisLine.args = generateProps({
90
92
  secondaryDataTitle: 'New line data',
91
93
  });
92
94
 
95
+ export const WithCustomTooltip = (args, { argTypes }) => ({
96
+ components: { GlColumnChart },
97
+ props: Object.keys(argTypes),
98
+ template: template(`
99
+ <template #tooltip-title="{ params }">Custom tooltip title: {{params && params.value}}</template>
100
+ <template #tooltip-content="{ params }">
101
+ <div v-for="p in params && params.seriesData">Wow so custom: {{p.seriesName}}: {{p.value[1]}}</div>
102
+ </template>
103
+ `),
104
+ });
105
+ WithCustomTooltip.args = generateProps();
106
+ WithCustomTooltip.tags = ['skip-visual-test'];
107
+
93
108
  export default {
94
109
  title: 'charts/column-chart',
95
110
  component: GlColumnChart,
@@ -184,6 +184,21 @@ export default {
184
184
  v-on="$listeners"
185
185
  @created="onCreated"
186
186
  />
187
- <chart-tooltip v-if="chart" :chart="chart" :use-default-tooltip-formatter="true" />
187
+ <chart-tooltip
188
+ v-if="chart"
189
+ ref="dataTooltip"
190
+ :chart="chart"
191
+ :use-default-tooltip-formatter="true"
192
+ >
193
+ <template v-if="$scopedSlots['tooltip-title']" #title="scope">
194
+ <slot name="tooltip-title" v-bind="scope"></slot>
195
+ </template>
196
+ <template v-if="$scopedSlots['tooltip-content']" #default="scope">
197
+ <slot name="tooltip-content" v-bind="scope"></slot>
198
+ </template>
199
+ <template v-if="$scopedSlots['tooltip-value']" #tooltip-value="scope">
200
+ <slot name="tooltip-value" v-bind="scope"></slot>
201
+ </template>
202
+ </chart-tooltip>
188
203
  </div>
189
204
  </template>
@@ -1,5 +1,10 @@
1
+ import { nextTick } from 'vue';
1
2
  import { shallowMount } from '@vue/test-utils';
2
- import { createMockChartInstance } from '~helpers/chart_stubs';
3
+ import {
4
+ createMockChartInstance,
5
+ ChartTooltipStub,
6
+ chartTooltipStubData,
7
+ } from '~helpers/chart_stubs';
3
8
  import { expectHeightAutoClasses } from '~helpers/chart_height';
4
9
  import {
5
10
  mockDefaultLineData,
@@ -8,7 +13,6 @@ import {
8
13
  mockDefaultStackedBarData,
9
14
  } from '../../../utils/charts/mock_data';
10
15
  import Chart from '../chart/chart.vue';
11
- import ChartTooltip from '../tooltip/tooltip.vue';
12
16
  import ColumnChart from './column.vue';
13
17
 
14
18
  let mockChartInstance;
@@ -28,42 +32,45 @@ describe('column chart component', () => {
28
32
 
29
33
  const chartItemClickedSpy = jest.fn();
30
34
  const findChart = () => wrapper.findComponent(Chart);
31
- const findChartTooltip = () => wrapper.findComponent(ChartTooltip);
35
+ const findChartTooltip = () => wrapper.findComponent({ ref: 'dataTooltip' });
32
36
 
33
37
  const emitChartCreated = () => findChart().vm.$emit('created', mockChartInstance);
34
38
 
35
- const factory = (props = defaultChartProps, slots = {}) => {
39
+ const factory = (props = defaultChartProps, options = {}) => {
36
40
  wrapper = shallowMount(ColumnChart, {
37
41
  propsData: { ...props },
38
42
  listeners: {
39
43
  chartItemClicked: chartItemClickedSpy,
40
44
  },
41
- slots,
45
+ ...options,
42
46
  });
43
47
  };
44
48
 
45
- beforeEach(() => {
46
- factory();
49
+ describe('mounted', () => {
50
+ beforeEach(() => {
51
+ factory();
47
52
 
48
- mockChartInstance = createMockChartInstance();
49
- emitChartCreated();
50
- });
53
+ mockChartInstance = createMockChartInstance();
54
+ emitChartCreated();
55
+ });
51
56
 
52
- it('emits "created" when onCreated is called', () => {
53
- expect(wrapper.emitted('created')).toHaveLength(1);
54
- });
57
+ it('emits "created" when onCreated is called', () => {
58
+ expect(wrapper.emitted('created')).toHaveLength(1);
59
+ });
55
60
 
56
- it('calls event listener when "chartItemClicked" is emitted on the Chart component', () => {
57
- findChart().vm.$emit('chartItemClicked');
61
+ it('calls event listener when "chartItemClicked" is emitted on the Chart component', () => {
62
+ findChart().vm.$emit('chartItemClicked');
58
63
 
59
- expect(chartItemClickedSpy).toHaveBeenCalled();
60
- });
64
+ expect(chartItemClickedSpy).toHaveBeenCalled();
65
+ });
61
66
 
62
- it('should correctly render the chart', () => {
63
- const chart = findChart();
67
+ it('should correctly render the chart', () => {
68
+ const chart = findChart();
64
69
 
65
- expect(chart.props('options')).toMatchSnapshot();
70
+ expect(chart.props('options')).toMatchSnapshot();
71
+ });
66
72
  });
73
+
67
74
  describe('with line data provided', () => {
68
75
  beforeEach(() => {
69
76
  factory({
@@ -120,11 +127,90 @@ describe('column chart component', () => {
120
127
 
121
128
  describe('tooltip', () => {
122
129
  it('configures chart tooltip', async () => {
130
+ factory(defaultChartProps, {
131
+ stubs: {
132
+ 'chart-tooltip': ChartTooltipStub,
133
+ },
134
+ });
135
+ mockChartInstance = createMockChartInstance();
136
+ emitChartCreated();
137
+ await nextTick();
138
+
123
139
  expect(findChartTooltip().props()).toMatchObject({
124
140
  chart: mockChartInstance,
125
141
  useDefaultTooltipFormatter: true,
126
142
  });
127
143
  });
144
+
145
+ describe('custom tooltip slots', () => {
146
+ const { params, title, content } = chartTooltipStubData;
147
+
148
+ it('customizes value', async () => {
149
+ const tooltipValueSlot = jest.fn().mockReturnValue('Custom tooltip value');
150
+
151
+ factory(defaultChartProps, {
152
+ stubs: {
153
+ 'chart-tooltip': ChartTooltipStub,
154
+ },
155
+ scopedSlots: {
156
+ 'tooltip-value': tooltipValueSlot,
157
+ },
158
+ });
159
+
160
+ mockChartInstance = createMockChartInstance();
161
+ emitChartCreated();
162
+ await nextTick();
163
+
164
+ expect(tooltipValueSlot).toHaveBeenCalledWith({ value: 9 });
165
+ expect(findChartTooltip().text()).toBe('Custom tooltip value');
166
+ });
167
+
168
+ it('customizes title', async () => {
169
+ const tooltipTitleSlot = jest.fn().mockReturnValue('Custom tooltip title');
170
+
171
+ factory(defaultChartProps, {
172
+ stubs: {
173
+ 'chart-tooltip': ChartTooltipStub,
174
+ },
175
+ scopedSlots: {
176
+ 'tooltip-title': tooltipTitleSlot,
177
+ },
178
+ });
179
+
180
+ mockChartInstance = createMockChartInstance();
181
+ emitChartCreated();
182
+ await nextTick();
183
+
184
+ expect(tooltipTitleSlot).toHaveBeenCalledWith({
185
+ params,
186
+ title,
187
+ });
188
+ expect(findChartTooltip().text()).toBe('Custom tooltip title');
189
+ });
190
+
191
+ it('customizes content', async () => {
192
+ const tooltipContentSlot = jest.fn().mockReturnValue('Custom tooltip content');
193
+
194
+ factory(defaultChartProps, {
195
+ stubs: {
196
+ 'chart-tooltip': ChartTooltipStub,
197
+ },
198
+ scopedSlots: {
199
+ 'tooltip-content': tooltipContentSlot,
200
+ },
201
+ });
202
+
203
+ mockChartInstance = createMockChartInstance();
204
+ emitChartCreated();
205
+ await nextTick();
206
+
207
+ expect(tooltipContentSlot).toHaveBeenCalledWith({
208
+ params,
209
+ content,
210
+ });
211
+ expect(findChartTooltip().text()).toBe('Custom tooltip content');
212
+ });
213
+ });
128
214
  });
129
215
 
130
216
  describe('height', () => {
@@ -7,6 +7,7 @@ const i18n = {
7
7
 
8
8
  const isMessage = (item) => Boolean(item) && item?.role;
9
9
 
10
+ // eslint-disable-next-line unicorn/no-array-callback-reference
10
11
  const itemsValidator = (items) => items.every(isMessage);
11
12
 
12
13
  export default {
@@ -62,6 +62,7 @@ export const slashCommands = [
62
62
 
63
63
  const isMessage = (item) => Boolean(item) && item?.role;
64
64
 
65
+ // eslint-disable-next-line unicorn/no-array-callback-reference
65
66
  const itemsValidator = (items) => items.every(isMessage);
66
67
 
67
68
  export default {
@@ -98,6 +98,7 @@ describe('IntersectionObserver', () => {
98
98
  triggerIntersectionObserver(entry);
99
99
 
100
100
  expect(allWrappers).toHaveLength(2);
101
+ // eslint-disable-next-line unicorn/no-array-callback-reference
101
102
  expect(allWrappers.map((x) => x.emitted())).toEqual(allWrappers.map(createExpectation));
102
103
  });
103
104
  });
@@ -23,6 +23,7 @@ describe('Intersperse Component', () => {
23
23
  createComponent(defaultSlotContent);
24
24
 
25
25
  selectorsToCheck.forEach((selector) => {
26
+ // eslint-disable-next-line unicorn/no-array-callback-reference
26
27
  expect(wrapper.find(selector).exists()).toBe(true);
27
28
  });
28
29
  }
@@ -68,6 +68,7 @@ export default {
68
68
  default: undefined,
69
69
  validator: (value) =>
70
70
  Object.values(value).every(
71
+ // eslint-disable-next-line unicorn/no-array-callback-reference
71
72
  (tagPair) => Array.isArray(tagPair) && tagPair.length === 2 && tagPair.every(isString)
72
73
  ),
73
74
  },
@@ -13,11 +13,13 @@ function isEmpty(vnode) {
13
13
  }
14
14
 
15
15
  if (Array.isArray(vnode)) {
16
+ // eslint-disable-next-line unicorn/no-array-callback-reference
16
17
  return vnode.every(isEmpty);
17
18
  }
18
19
 
19
20
  if (Fragment && vnode.type === Fragment) {
20
21
  // Vue.js 3 fragment, check children
22
+ // eslint-disable-next-line unicorn/no-array-callback-reference
21
23
  return vnode.children.every(isEmpty);
22
24
  }
23
25
 
@@ -33,5 +35,6 @@ export function isSlotEmpty(vueInstance, slot, slotArgs) {
33
35
  callIfNeeded(vueInstance.$slots[slot] || vueInstance.$scopedSlots[slot], slotArgs)
34
36
  : vueInstance.$scopedSlots[slot]?.(slotArgs);
35
37
 
38
+ // eslint-disable-next-line unicorn/no-array-callback-reference
36
39
  return isEmpty(slotContent);
37
40
  }
@@ -9,7 +9,7 @@ export const addition = (a, b) => a + b;
9
9
  * Returns the sum of all arguments
10
10
  * @param {...Number} numbers
11
11
  */
12
- export const sum = (...numbers) => numbers.reduce(addition);
12
+ export const sum = (...numbers) => numbers.reduce(addition); // eslint-disable-line unicorn/no-array-callback-reference
13
13
 
14
14
  /**
15
15
  * Returns the average of all arguments
@@ -182,5 +182,5 @@ export function stopEvent(
182
182
  * Return an Array of visible items
183
183
  */
184
184
  export function filterVisible(els) {
185
- return (els || []).filter(isVisible);
185
+ return (els || []).filter((el) => isVisible(el));
186
186
  }