@gitlab/ui 66.35.1 → 66.36.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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 20 Oct 2023 18:59:55 GMT
3
+ * Generated on Tue, 24 Oct 2023 13:47:52 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 20 Oct 2023 18:59:55 GMT
3
+ * Generated on Tue, 24 Oct 2023 13:47:52 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 Fri, 20 Oct 2023 18:59:55 GMT
3
+ * Generated on Tue, 24 Oct 2023 13:47:52 GMT
4
4
  */
5
5
 
6
6
  export const BLACK = "#fff";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 20 Oct 2023 18:59:55 GMT
3
+ * Generated on Tue, 24 Oct 2023 13:47:52 GMT
4
4
  */
5
5
 
6
6
  export const BLACK = "#000";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Fri, 20 Oct 2023 18:59:55 GMT
3
+ // Generated on Tue, 24 Oct 2023 13:47:52 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 Fri, 20 Oct 2023 18:59:55 GMT
3
+ // Generated on Tue, 24 Oct 2023 13:47:52 GMT
4
4
 
5
5
  $gl-line-height-52: 3.25rem;
6
6
  $gl-line-height-44: 2.75rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "66.35.1",
3
+ "version": "66.36.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -98,18 +98,18 @@
98
98
  "@rollup/plugin-commonjs": "^11.1.0",
99
99
  "@rollup/plugin-node-resolve": "^7.1.3",
100
100
  "@rollup/plugin-replace": "^2.3.2",
101
- "@storybook/addon-a11y": "7.4.6",
102
- "@storybook/addon-docs": "7.4.6",
103
- "@storybook/addon-essentials": "7.4.6",
104
- "@storybook/addon-storyshots": "7.4.6",
105
- "@storybook/addon-storyshots-puppeteer": "7.4.6",
106
- "@storybook/addon-viewport": "7.4.6",
107
- "@storybook/builder-webpack5": "7.4.6",
108
- "@storybook/theming": "7.4.6",
109
- "@storybook/vue": "7.4.6",
110
- "@storybook/vue-webpack5": "7.4.6",
111
- "@storybook/vue3": "7.4.6",
112
- "@storybook/vue3-webpack5": "7.4.6",
101
+ "@storybook/addon-a11y": "7.5.1",
102
+ "@storybook/addon-docs": "7.5.1",
103
+ "@storybook/addon-essentials": "7.5.1",
104
+ "@storybook/addon-storyshots": "7.5.1",
105
+ "@storybook/addon-storyshots-puppeteer": "7.5.1",
106
+ "@storybook/addon-viewport": "7.5.1",
107
+ "@storybook/builder-webpack5": "7.5.1",
108
+ "@storybook/theming": "7.5.1",
109
+ "@storybook/vue": "7.5.1",
110
+ "@storybook/vue-webpack5": "7.5.1",
111
+ "@storybook/vue3": "7.5.1",
112
+ "@storybook/vue3-webpack5": "7.5.1",
113
113
  "@vue/compat": "^3.2.40",
114
114
  "@vue/compiler-sfc": "^3.2.40",
115
115
  "@vue/test-utils": "1.3.0",
@@ -122,11 +122,11 @@
122
122
  "babel-loader": "^8.0.5",
123
123
  "babel-plugin-require-context-hook": "^1.0.0",
124
124
  "bootstrap": "4.6.2",
125
- "cypress": "13.3.1",
125
+ "cypress": "13.3.2",
126
126
  "cypress-axe": "^1.4.0",
127
127
  "dompurify": "^3.0.0",
128
128
  "emoji-regex": "^10.0.0",
129
- "eslint": "8.51.0",
129
+ "eslint": "8.52.0",
130
130
  "eslint-import-resolver-jest": "3.0.2",
131
131
  "eslint-plugin-cypress": "2.15.1",
132
132
  "eslint-plugin-storybook": "0.6.15",
@@ -160,7 +160,7 @@
160
160
  "sass-loader": "^10.2.0",
161
161
  "sass-true": "^6.1.0",
162
162
  "start-server-and-test": "^1.10.6",
163
- "storybook": "7.4.6",
163
+ "storybook": "7.5.1",
164
164
  "storybook-dark-mode": "3.0.1",
165
165
  "style-dictionary": "^3.8.0",
166
166
  "stylelint": "15.10.2",
@@ -1,5 +1,4 @@
1
1
  .gl-link {
2
- @include gl-font-base;
3
2
  @include gl-text-blue-500;
4
3
 
5
4
  &:hover {
@@ -1,8 +1,10 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
+ import { nextTick } from 'vue';
2
3
  import { createMockChartInstance } from '~helpers/chart_stubs';
3
4
  import { expectHeightAutoClasses } from '~helpers/chart_height';
4
5
  import Chart from '../chart/chart.vue';
5
6
  import ChartTooltip from '../tooltip/tooltip.vue';
7
+ import TooltipDefaultFormat from '../../shared_components/charts/tooltip_default_format.vue';
6
8
  import DiscreteScatterChart from './discrete_scatter.vue';
7
9
 
8
10
  let mockChartInstance;
@@ -12,11 +14,21 @@ jest.mock('echarts', () => ({
12
14
  }));
13
15
 
14
16
  describe('column chart component', () => {
17
+ const mockData = {
18
+ x: '19 May',
19
+ y: 6.95,
20
+ };
21
+
22
+ const mockDataPixel = {
23
+ x: 1,
24
+ y: 2,
25
+ };
26
+
15
27
  const defaultChartProps = {
16
28
  xAxisTitle: 'x axis',
17
29
  yAxisTitle: 'y axis',
18
30
  xAxisType: 'category',
19
- data: [['19 May', 6.95]],
31
+ data: [[mockData.x, mockData.y]],
20
32
  };
21
33
  let wrapper;
22
34
 
@@ -30,6 +42,7 @@ describe('column chart component', () => {
30
42
 
31
43
  beforeEach(() => {
32
44
  mockChartInstance = createMockChartInstance();
45
+ mockChartInstance.convertToPixel.mockReturnValue([mockDataPixel.x, mockDataPixel.y]);
33
46
  });
34
47
 
35
48
  describe('height', () => {
@@ -40,19 +53,121 @@ describe('column chart component', () => {
40
53
  });
41
54
  });
42
55
 
43
- describe('disable-tooltip', () => {
44
- it('is set to false by default', async () => {
56
+ describe('tooltip formatter', () => {
57
+ let onLabelChange;
58
+
59
+ beforeEach(async () => {
45
60
  createComponent();
46
61
 
47
62
  await findChart().vm.$emit('created', mockChartInstance);
63
+ onLabelChange = findChart().props('options').tooltip.formatter;
64
+ });
65
+
66
+ describe.each([
67
+ {
68
+ params: {
69
+ data: [mockData.x, mockData.y],
70
+ },
71
+ descr: 'when data is an array',
72
+ },
73
+ {
74
+ params: {
75
+ data: { value: [mockData.x, mockData.y] },
76
+ },
77
+ descr: 'when data is an object',
78
+ },
79
+ ])('$descr', ({ params }) => {
80
+ it('sets the popover content', async () => {
81
+ onLabelChange(params);
82
+ await nextTick();
48
83
 
49
- expect(wrapper.findComponent(ChartTooltip).exists()).toBe(true);
84
+ expect(mockChartInstance.convertToPixel).toHaveBeenCalledTimes(1);
85
+ expect(mockChartInstance.convertToPixel).toHaveBeenLastCalledWith('grid', [
86
+ mockData.x,
87
+ mockData.y,
88
+ ]);
89
+
90
+ expect(wrapper.findComponent(ChartTooltip).props('left')).toBe(`${mockDataPixel.x}px`);
91
+ expect(wrapper.findComponent(ChartTooltip).props('top')).toBe(`${mockDataPixel.y}px`);
92
+ expect(wrapper.findComponent(TooltipDefaultFormat).props('tooltipContent')).toEqual({
93
+ 'y axis': { color: '', value: mockData.y },
94
+ });
95
+ });
50
96
  });
51
97
 
52
- it('disables the tooltip', () => {
53
- createComponent({ disableTooltip: true });
98
+ describe.each([
99
+ {
100
+ params: {},
101
+ descr: 'when data is missing',
102
+ },
103
+ {
104
+ params: {
105
+ data: [],
106
+ },
107
+ descr: 'when data is an empty array',
108
+ },
109
+ {
110
+ params: { data: 'foo' },
111
+ descr: 'when data is not an array',
112
+ },
113
+ {
114
+ params: {
115
+ data: { value: [] },
116
+ },
117
+ descr: 'when data.value is an empty array',
118
+ },
119
+ {
120
+ params: { data: { value: undefined } },
121
+ descr: 'when data.value is undefined',
122
+ },
123
+ {
124
+ params: { data: { value: 'foo' } },
125
+ descr: 'when data.value is not an array',
126
+ },
127
+ ])('$descr', ({ params }) => {
128
+ it('does not set the tooltip content', async () => {
129
+ onLabelChange(params);
130
+ await nextTick();
131
+
132
+ expect(mockChartInstance.convertToPixel).not.toHaveBeenCalled();
133
+ expect(wrapper.findComponent(ChartTooltip).props('left')).toBe('0');
134
+ expect(wrapper.findComponent(ChartTooltip).props('top')).toBe('0');
135
+ expect(wrapper.findComponent(TooltipDefaultFormat).props('tooltipContent')).toEqual({
136
+ 'y axis': { color: '', value: undefined },
137
+ });
138
+ });
139
+ });
140
+ });
141
+
142
+ describe('disable-tooltip', () => {
143
+ describe('when false', () => {
144
+ beforeEach(async () => {
145
+ createComponent();
146
+ await findChart().vm.$emit('created', mockChartInstance);
147
+ });
148
+
149
+ it('renders the ChartTooltip component', async () => {
150
+ expect(wrapper.findComponent(ChartTooltip).exists()).toBe(true);
151
+ });
152
+
153
+ it('sets chart tooltip options', () => {
154
+ expect(findChart().props('options').tooltip).not.toBeUndefined();
155
+ });
156
+ });
157
+
158
+ describe('when true', () => {
159
+ beforeEach(async () => {
160
+ createComponent({ disableTooltip: true });
161
+ await findChart().vm.$emit('created', mockChartInstance);
162
+ });
163
+
164
+ it('does not render the ChartTooltip component', async () => {
165
+ expect(wrapper.findComponent(ChartTooltip).exists()).toBe(false);
166
+ });
54
167
 
55
- expect(wrapper.findComponent(ChartTooltip).exists()).toBe(false);
168
+ it('does not set chart tooltip options', () => {
169
+ expect(findChart().props('options').tooltip).toBeUndefined();
170
+ });
56
171
  });
57
172
  });
58
173
  });
@@ -52,7 +52,7 @@ export default {
52
52
  disableTooltip: {
53
53
  type: Boolean,
54
54
  required: false,
55
- default: () => false,
55
+ default: false,
56
56
  },
57
57
  /**
58
58
  * Sets the chart's height in pixels. Set to `"auto"` to use the height of the container.
@@ -103,9 +103,11 @@ export default {
103
103
  {},
104
104
  defaultChartOptions,
105
105
  {
106
- tooltip: {
107
- formatter: this.onLabelChange,
108
- },
106
+ tooltip: this.disableTooltip
107
+ ? undefined
108
+ : {
109
+ formatter: this.onLabelChange,
110
+ },
109
111
  xAxis: {
110
112
  type: 'category',
111
113
  name: this.xAxisTitle,
@@ -142,7 +144,7 @@ export default {
142
144
  },
143
145
  methods: {
144
146
  defaultFormatTooltipText(params) {
145
- const { data } = params;
147
+ const data = this.getChartData(params);
146
148
  const [title, content] = data;
147
149
  this.tooltipTitle = title;
148
150
  const seriesName = this.yAxisTitle;
@@ -166,7 +168,7 @@ export default {
166
168
  onLabelChange(params) {
167
169
  this.selectedFormatTooltipText(params);
168
170
 
169
- const { data = [] } = params;
171
+ const data = this.getChartData(params);
170
172
 
171
173
  if (data.length) {
172
174
  const [left, top] = this.chart.convertToPixel('grid', data);
@@ -177,6 +179,10 @@ export default {
177
179
  };
178
180
  }
179
181
  },
182
+ getChartData({ data }) {
183
+ const chartData = data?.value || data;
184
+ return Array.isArray(chartData) ? chartData : [];
185
+ },
180
186
  },
181
187
  HEIGHT_AUTO_CLASSES,
182
188
  };
@@ -342,6 +342,33 @@ describe('DuoChatMessage', () => {
342
342
  expect(renderGFM).not.toHaveBeenCalled();
343
343
  });
344
344
 
345
+ it('does not re-render when chunk is received after final message', async () => {
346
+ const finalMessageContent = content1 + content2;
347
+
348
+ // setProps is justified here because we are testing the component's
349
+ // reactive behavior which consistutes an exception
350
+ // See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
351
+ await wrapper.setProps({
352
+ message: chunk1,
353
+ });
354
+ expect(findContent().text()).toBe(content1);
355
+
356
+ await wrapper.setProps({
357
+ message: {
358
+ ...MOCK_RESPONSE_MESSAGE,
359
+ content: finalMessageContent,
360
+ contentHtml: finalMessageContent,
361
+ chunkId: null,
362
+ },
363
+ });
364
+ expect(findContent().text()).toBe(finalMessageContent);
365
+
366
+ await wrapper.setProps({
367
+ message: chunk2,
368
+ });
369
+ expect(findContent().text()).toBe(finalMessageContent);
370
+ });
371
+
345
372
  it.each`
346
373
  content | contentHtml | errors | expectedContent
347
374
  ${'alpha'} | ${'beta'} | ${['foo', 'bar']} | ${'beta'}
@@ -35,6 +35,7 @@ export default {
35
35
  data() {
36
36
  return {
37
37
  messageContent: '',
38
+ messageWatcher: null,
38
39
  };
39
40
  },
40
41
  computed: {
@@ -54,21 +55,8 @@ export default {
54
55
  );
55
56
  },
56
57
  },
57
- watch: {
58
- message: {
59
- handler() {
60
- const { chunkId, content } = this.message;
61
- if (!chunkId) {
62
- this.messageChunks = [];
63
- this.messageContent = this.content;
64
- this.hydrateContentWithGFM();
65
- } else {
66
- this.messageChunks[chunkId] = content;
67
- this.messageContent = this.renderMarkdown(concatIndicesUntilEmpty(this.messageChunks));
68
- }
69
- },
70
- deep: true,
71
- },
58
+ created() {
59
+ this.messageWatcher = this.$watch('message', this.messageUpdateHandler, { deep: true });
72
60
  },
73
61
  beforeCreate() {
74
62
  /**
@@ -89,6 +77,19 @@ export default {
89
77
  await nextTick();
90
78
  this.renderGFM(this.$refs.content);
91
79
  },
80
+ async messageUpdateHandler() {
81
+ const { chunkId, content } = this.message;
82
+ if (!chunkId) {
83
+ this.messageChunks = [];
84
+ this.messageContent = this.content;
85
+
86
+ this.messageWatcher();
87
+ this.hydrateContentWithGFM();
88
+ } else {
89
+ this.messageChunks[chunkId] = content;
90
+ this.messageContent = this.renderMarkdown(concatIndicesUntilEmpty(this.messageChunks));
91
+ }
92
+ },
92
93
  },
93
94
  };
94
95
  </script>