@gitlab/ui 72.6.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 (77) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -14
  3. package/dist/components/base/dropdown/dropdown.js +2 -6
  4. package/dist/components/base/filtered_search/filtered_search_term.js +2 -0
  5. package/dist/components/base/filtered_search/filtered_search_token.js +3 -1
  6. package/dist/components/base/filtered_search/filtered_search_token_segment.js +4 -1
  7. package/dist/components/base/new_dropdowns/disclosure/utils.js +5 -1
  8. package/dist/components/base/new_dropdowns/listbox/utils.js +6 -0
  9. package/dist/components/base/sorting/sorting.js +1 -0
  10. package/dist/components/charts/column/column.js +1 -1
  11. package/dist/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.js +2 -0
  12. package/dist/components/experimental/duo/chat/duo_chat.js +2 -0
  13. package/dist/components/regions/empty_state/empty_state.js +1 -1
  14. package/dist/components/utilities/sprintf/sprintf.js +3 -1
  15. package/dist/tokens/css/tokens.css +1 -1
  16. package/dist/tokens/css/tokens.dark.css +1 -1
  17. package/dist/tokens/js/tokens.dark.js +1 -1
  18. package/dist/tokens/js/tokens.js +1 -1
  19. package/dist/tokens/scss/_tokens.dark.scss +1 -1
  20. package/dist/tokens/scss/_tokens.scss +1 -1
  21. package/dist/utility_classes.css +1 -1
  22. package/dist/utility_classes.css.map +1 -1
  23. package/dist/utils/is_slot_empty.js +4 -0
  24. package/dist/utils/number_utils.js +1 -1
  25. package/dist/utils/utils.js +1 -1
  26. package/package.json +14 -12
  27. package/src/components/base/accordion/accordion_item.stories.js +1 -1
  28. package/src/components/base/alert/alert.stories.js +4 -12
  29. package/src/components/base/badge/badge.stories.js +2 -6
  30. package/src/components/base/broadcast_message/broadcast_message.stories.js +2 -3
  31. package/src/components/base/drawer/drawer.spec.js +1 -0
  32. package/src/components/base/dropdown/dropdown.vue +2 -6
  33. package/src/components/base/filtered_search/filtered_search.spec.js +11 -0
  34. package/src/components/base/filtered_search/filtered_search_term.vue +1 -0
  35. package/src/components/base/filtered_search/filtered_search_token.vue +1 -0
  36. package/src/components/base/filtered_search/filtered_search_token_segment.vue +3 -1
  37. package/src/components/base/form/form_fields/form_fields.spec.js +1 -0
  38. package/src/components/base/icon/icon.stories.js +1 -1
  39. package/src/components/base/new_dropdowns/disclosure/utils.js +2 -0
  40. package/src/components/base/new_dropdowns/listbox/listbox.spec.js +2 -0
  41. package/src/components/base/new_dropdowns/listbox/listbox.stories.js +5 -3
  42. package/src/components/base/new_dropdowns/listbox/utils.js +3 -0
  43. package/src/components/base/popover/popover.stories.js +1 -3
  44. package/src/components/base/sorting/sorting.vue +1 -0
  45. package/src/components/base/tabs/tabs/tabs.stories.js +1 -3
  46. package/src/components/base/token_selector/token_selector.stories.js +1 -1
  47. package/src/components/charts/area/area.stories.js +1 -3
  48. package/src/components/charts/chart/chart.stories.js +1 -3
  49. package/src/components/charts/column/__snapshots__/column_chart.spec.js.snap +1 -1
  50. package/src/components/charts/column/column.stories.js +18 -3
  51. package/src/components/charts/column/column.vue +16 -1
  52. package/src/components/charts/column/column_chart.spec.js +106 -20
  53. package/src/components/charts/line/line.stories.js +1 -3
  54. package/src/components/charts/sparkline/sparkline.stories.js +2 -2
  55. package/src/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.vue +1 -0
  56. package/src/components/experimental/duo/chat/duo_chat.vue +1 -0
  57. package/src/components/experimental/duo/user_feedback/user_feedback.stories.js +1 -1
  58. package/src/components/experimental/experiment_badge/experiment_badge.stories.js +1 -1
  59. package/src/components/regions/empty_state/empty_state.vue +0 -1
  60. package/src/components/utilities/animated_number/animated_number.stories.js +1 -0
  61. package/src/components/utilities/intersection_observer/intersection_observer.spec.js +1 -0
  62. package/src/components/utilities/intersection_observer/intersection_observer.stories.js +1 -1
  63. package/src/components/utilities/intersperse/intersperse.spec.js +1 -0
  64. package/src/components/utilities/sprintf/sprintf.stories.js +4 -4
  65. package/src/components/utilities/sprintf/sprintf.vue +1 -0
  66. package/src/components/utilities/truncate/truncate.stories.js +1 -1
  67. package/src/components/utilities/truncate_text/truncate_text.stories.js +1 -1
  68. package/src/directives/hover_load/hover_load.stories.js +1 -1
  69. package/src/directives/outside/outside.stories.js +1 -3
  70. package/src/directives/resize_observer/resize_observer.stories.js +1 -1
  71. package/src/directives/safe_html/safe_html.stories.js +1 -1
  72. package/src/directives/safe_link/safe_link.stories.js +1 -1
  73. package/src/scss/utilities.scss +8 -0
  74. package/src/scss/utility-mixins/sizing.scss +4 -0
  75. package/src/utils/is_slot_empty.js +3 -0
  76. package/src/utils/number_utils.js +1 -1
  77. package/src/utils/utils.js +1 -1
@@ -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', () => {
@@ -213,9 +213,7 @@ export const WithCustomTooltip = (_args, { argTypes }) => ({
213
213
  `),
214
214
  });
215
215
  WithCustomTooltip.args = generateProps();
216
- WithCustomTooltip.parameters = {
217
- storyshots: { disable: true },
218
- };
216
+ WithCustomTooltip.tags = ['skip-visual-test'];
219
217
 
220
218
  export default {
221
219
  title: 'charts/line-chart',
@@ -55,14 +55,14 @@ Default.args = generateProps();
55
55
 
56
56
  export const WithoutLastYValue = Template.bind({});
57
57
  WithoutLastYValue.args = generateProps({ showLastYValue: false });
58
- WithoutLastYValue.parameters = { storyshots: { disable: true } };
58
+ WithoutLastYValue.tags = ['skip-visual-test'];
59
59
 
60
60
  export const WithChartColorGradient = Template.bind({});
61
61
  WithChartColorGradient.args = generateProps({ gradient: customGradient });
62
62
 
63
63
  export const WithSmoothing = Template.bind({});
64
64
  WithSmoothing.args = generateProps({ smooth: 0.5 });
65
- WithSmoothing.parameters = { storyshots: { disable: true } };
65
+ WithSmoothing.tags = ['skip-visual-test'];
66
66
 
67
67
  export const AutoHeight = Template.bind({});
68
68
  Object.assign(AutoHeight, {
@@ -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,8 +98,8 @@ Slots.parameters = { controls: { disable: true } };
98
98
  export default {
99
99
  title: 'experimental/duo/duo-user-feedback',
100
100
  component: GlDuoUserFeedback,
101
+ tags: ['skip-visual-test'],
101
102
  parameters: {
102
- storyshots: { disable: true },
103
103
  docs: {
104
104
  description: {
105
105
  component: readme,
@@ -46,8 +46,8 @@ CustomPlacement.args = {
46
46
  export default {
47
47
  title: 'experimental/experiment_badge',
48
48
  component: GlExperimentBadge,
49
+ tags: ['skip-visual-test'],
49
50
  parameters: {
50
- storyshots: { disable: true },
51
51
  docs: {
52
52
  description: {
53
53
  component: readme,
@@ -131,7 +131,6 @@ export default {
131
131
  v-if="svgPath"
132
132
  :src="svgPath"
133
133
  alt=""
134
- role="img"
135
134
  :class="{ 'gl-dark-invert-keep-hue': invertInDarkMode }"
136
135
  class="gl-max-w-full"
137
136
  :height="height"
@@ -72,4 +72,5 @@ export default {
72
72
  },
73
73
  },
74
74
  argTypes: {},
75
+ tags: ['skip-visual-test'],
75
76
  };
@@ -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
  });
@@ -151,8 +151,8 @@ export const InfiniteScrolling = () => ({
151
151
  export default {
152
152
  title: 'utilities/intersection-observer',
153
153
  component: GlIntersectionObserver,
154
+ tags: ['skip-visual-test'],
154
155
  parameters: {
155
- storyshots: { disable: true },
156
156
  docs: {
157
157
  description: {
158
158
  component: readme,
@@ -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
  }
@@ -35,7 +35,7 @@ Object.assign(SentenceWithLink, {
35
35
  args: generateProps({
36
36
  message: 'Click %{linkStart}here%{linkEnd} to reticulate splines.',
37
37
  }),
38
- parameters: { storyshots: { disable: true } },
38
+ tags: ['skip-visual-test'],
39
39
  });
40
40
 
41
41
  export const SentenceWithLinkWithCustomPlaceholders = makeStory({
@@ -54,7 +54,7 @@ Object.assign(SentenceWithLinkWithCustomPlaceholders, {
54
54
  message: 'Click %{my_custom_start}here%{my_custom_end} to reticulate splines.',
55
55
  placeholders: { link: ['my_custom_start', 'my_custom_end'] },
56
56
  }),
57
- parameters: { storyshots: { disable: true } },
57
+ tags: ['skip-visual-test'],
58
58
  });
59
59
 
60
60
  export const BasicPlaceholder = makeStory({
@@ -71,7 +71,7 @@ export const BasicPlaceholder = makeStory({
71
71
  });
72
72
  Object.assign(BasicPlaceholder, {
73
73
  args: generateProps(),
74
- parameters: { storyshots: { disable: true } },
74
+ tags: ['skip-visual-test'],
75
75
  });
76
76
 
77
77
  export const BasicButtonPlaceholder = makeStory({
@@ -88,7 +88,7 @@ export const BasicButtonPlaceholder = makeStory({
88
88
  });
89
89
  Object.assign(BasicButtonPlaceholder, {
90
90
  args: generateProps(),
91
- parameters: { storyshots: { disable: true } },
91
+ tags: ['skip-visual-test'],
92
92
  });
93
93
 
94
94
  export default {
@@ -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
  },
@@ -27,8 +27,8 @@ Default.args = generateProps();
27
27
  export default {
28
28
  title: 'utilities/truncate',
29
29
  component: GlTruncate,
30
+ tags: ['skip-visual-test'],
30
31
  parameters: {
31
- storyshots: { disable: true },
32
32
  docs: {
33
33
  description: {
34
34
  component: readme,
@@ -34,8 +34,8 @@ Default.args = generateProps();
34
34
  export default {
35
35
  title: 'utilities/truncate-text',
36
36
  component: GlTruncateText,
37
+ tags: ['skip-visual-test'],
37
38
  parameters: {
38
- storyshots: { disable: true },
39
39
  docs: {
40
40
  description: {
41
41
  component: readme,
@@ -40,8 +40,8 @@ Default.args = generateProps();
40
40
  export default {
41
41
  title: 'directives/hover-load-directive',
42
42
  component: GlHoverLoadDirective,
43
+ tags: ['skip-visual-test'],
43
44
  parameters: {
44
- storyshots: { disable: true },
45
45
  docs: {
46
46
  description: {
47
47
  component: readme,
@@ -20,9 +20,7 @@ export const Default = () => ({
20
20
  template: `<gl-button v-outside="onClick">Clicks outside me: {{ clicks }}</gl-button>`,
21
21
  });
22
22
 
23
- Default.parameters = {
24
- storyshots: { disable: true },
25
- };
23
+ Default.tags = ['skip-visual-test'];
26
24
 
27
25
  export default {
28
26
  title: 'directives/outside-directive',
@@ -61,8 +61,8 @@ const makeControl = () => ({
61
61
  export default {
62
62
  title: 'directives/resize-observer-directive',
63
63
  component: GlResizeObserver,
64
+ tags: ['skip-visual-test'],
64
65
  parameters: {
65
- storyshots: { disable: true },
66
66
  docs: {
67
67
  description: {
68
68
  component: readme,
@@ -49,8 +49,8 @@ Default.args = generateProps();
49
49
  export default {
50
50
  title: 'directives/safe-html-directive',
51
51
  component: GlSafeHtml,
52
+ tags: ['skip-visual-test'],
52
53
  parameters: {
53
- storyshots: { disable: true },
54
54
  docs: {
55
55
  description: {
56
56
  component: readme,
@@ -28,8 +28,8 @@ Default.args = generateProps();
28
28
  export default {
29
29
  title: 'directives/safe-link-directive',
30
30
  component: SafeLink,
31
+ tags: ['skip-visual-test'],
31
32
  parameters: {
32
- storyshots: { disable: true },
33
33
  docs: {
34
34
  description: {
35
35
  component: readme,
@@ -5577,6 +5577,14 @@ $gl-animate-skeleton-loader-max-width: 64 * $grid-size;
5577
5577
  max-width: 0 !important;
5578
5578
  }
5579
5579
 
5580
+ .gl-max-w-12 {
5581
+ max-width: $gl-spacing-scale-12;
5582
+ }
5583
+
5584
+ .gl-max-w-12\! {
5585
+ max-width: $gl-spacing-scale-12 !important;
5586
+ }
5587
+
5580
5588
  .gl-max-w-15 {
5581
5589
  max-width: $gl-spacing-scale-15;
5582
5590
  }
@@ -451,6 +451,10 @@
451
451
  max-width: 0;
452
452
  }
453
453
 
454
+ @mixin gl-max-w-12 {
455
+ max-width: $gl-spacing-scale-12;
456
+ }
457
+
454
458
  @mixin gl-max-w-15 {
455
459
  max-width: $gl-spacing-scale-15;
456
460
  }
@@ -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
  }