@gitlab/ui 67.1.0 → 67.3.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.
- package/CHANGELOG.md +14 -0
- package/dist/components/charts/line/line.js +1 -1
- package/dist/components/shared_components/charts/tooltip_default_format.js +1 -1
- package/dist/directives/outside/outside.js +14 -1
- package/dist/tokens/css/tokens.css +1 -1
- package/dist/tokens/css/tokens.dark.css +1 -1
- package/dist/tokens/js/tokens.dark.js +1 -1
- package/dist/tokens/js/tokens.js +1 -1
- package/dist/tokens/scss/_tokens.dark.scss +1 -1
- package/dist/tokens/scss/_tokens.scss +1 -1
- package/package.json +3 -3
- package/src/components/charts/line/line.spec.js +66 -10
- package/src/components/charts/line/line.vue +6 -1
- package/src/components/shared_components/charts/tooltip_default_format.spec.js +69 -0
- package/src/components/shared_components/charts/tooltip_default_format.vue +1 -1
- package/src/directives/outside/outside.js +14 -1
- package/src/directives/outside/outside.spec.js +19 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [67.3.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v67.2.0...v67.3.0) (2023-10-31)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **GlLineChart:** Defines a #tooltip-value slot ([4624919](https://gitlab.com/gitlab-org/gitlab-ui/commit/462491952e19832c9abd257b54130ed93244fd88))
|
|
7
|
+
|
|
8
|
+
# [67.2.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v67.1.0...v67.2.0) (2023-10-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **GlOutsideDirective:** respect mousedown events before click ([e2d72d5](https://gitlab.com/gitlab-org/gitlab-ui/commit/e2d72d5cc4d90c57fea3eb54f43d68338cab5ab4))
|
|
14
|
+
|
|
1
15
|
# [67.1.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v67.0.0...v67.1.0) (2023-10-27)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -306,7 +306,7 @@ const __vue_script__ = script;
|
|
|
306
306
|
/* template */
|
|
307
307
|
var __vue_render__ = function () {
|
|
308
308
|
var _obj;
|
|
309
|
-
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.shouldShowAnnotationsTooltip)?_c('chart-tooltip',{ref:"annotationsTooltip",attrs:{"id":"annotationsTooltip","show":_vm.showAnnotationsTooltip,"chart":_vm.chart,"top":_vm.annotationsTooltipPosition.top,"left":_vm.annotationsTooltipPosition.left,"placement":"bottom"},scopedSlots:_vm._u([{key:"title",fn:function(){return [_c('div',[_vm._v(_vm._s(_vm.annotationsTooltipTitle))])]},proxy:true}],null,false,1889294429)},[_vm._v(" "),_c('div',[_vm._v(_vm._s(_vm.annotationsTooltipContent))])]):_vm._e(),_vm._v(" "),(_vm.chart)?_c('chart-tooltip',{ref:"dataTooltip",staticClass:"gl-pointer-events-none",attrs:{"id":"dataTooltip","show":_vm.showDataTooltip,"chart":_vm.chart,"top":_vm.dataTooltipPosition.top,"left":_vm.dataTooltipPosition.left},scopedSlots:_vm._u([{key:"title",fn:function(){return [(_vm.formatTooltipText)?_vm._t("tooltip-title"):_c('div',[_vm._v("\n "+_vm._s(_vm.dataTooltipTitle)+"\n "),(_vm.options.xAxis.name)?[_vm._v("("+_vm._s(_vm.options.xAxis.name)+")")]:_vm._e()],2)]},proxy:true}],null,true)},[_vm._v(" "),(_vm.formatTooltipText)?_vm._t("tooltip-content"):_c('tooltip-default-format',{attrs:{"tooltip-content":_vm.dataTooltipContent}})],2):_vm._e(),_vm._v(" "),(_vm.hasLegend)?_c('chart-legend',{style:(_vm.legendStyle),attrs:{"chart":_vm.chart,"series-info":_vm.seriesInfo,"text-style":_vm.compiledOptions.textStyle,"min-text":_vm.legendMinText,"max-text":_vm.legendMaxText,"average-text":_vm.legendAverageText,"current-text":_vm.legendCurrentText,"layout":_vm.legendLayout}}):_vm._e()],1)};
|
|
309
|
+
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.shouldShowAnnotationsTooltip)?_c('chart-tooltip',{ref:"annotationsTooltip",attrs:{"id":"annotationsTooltip","show":_vm.showAnnotationsTooltip,"chart":_vm.chart,"top":_vm.annotationsTooltipPosition.top,"left":_vm.annotationsTooltipPosition.left,"placement":"bottom"},scopedSlots:_vm._u([{key:"title",fn:function(){return [_c('div',[_vm._v(_vm._s(_vm.annotationsTooltipTitle))])]},proxy:true}],null,false,1889294429)},[_vm._v(" "),_c('div',[_vm._v(_vm._s(_vm.annotationsTooltipContent))])]):_vm._e(),_vm._v(" "),(_vm.chart)?_c('chart-tooltip',{ref:"dataTooltip",staticClass:"gl-pointer-events-none",attrs:{"id":"dataTooltip","show":_vm.showDataTooltip,"chart":_vm.chart,"top":_vm.dataTooltipPosition.top,"left":_vm.dataTooltipPosition.left},scopedSlots:_vm._u([{key:"title",fn:function(){return [(_vm.formatTooltipText)?_vm._t("tooltip-title"):_c('div',[_vm._v("\n "+_vm._s(_vm.dataTooltipTitle)+"\n "),(_vm.options.xAxis.name)?[_vm._v("("+_vm._s(_vm.options.xAxis.name)+")")]:_vm._e()],2)]},proxy:true}],null,true)},[_vm._v(" "),(_vm.formatTooltipText)?_vm._t("tooltip-content"):_c('tooltip-default-format',{attrs:{"tooltip-content":_vm.dataTooltipContent},scopedSlots:_vm._u([(_vm.$scopedSlots['tooltip-value'])?{key:"tooltip-value",fn:function(scope){return [_vm._t("tooltip-value",null,null,scope)]}}:null],null,true)})],2):_vm._e(),_vm._v(" "),(_vm.hasLegend)?_c('chart-legend',{style:(_vm.legendStyle),attrs:{"chart":_vm.chart,"series-info":_vm.seriesInfo,"text-style":_vm.compiledOptions.textStyle,"min-text":_vm.legendMinText,"max-text":_vm.legendMaxText,"average-text":_vm.legendAverageText,"current-text":_vm.legendCurrentText,"layout":_vm.legendLayout}}):_vm._e()],1)};
|
|
310
310
|
var __vue_staticRenderFns__ = [];
|
|
311
311
|
|
|
312
312
|
/* style */
|
|
@@ -18,7 +18,7 @@ var script = {
|
|
|
18
18
|
const __vue_script__ = script;
|
|
19
19
|
|
|
20
20
|
/* template */
|
|
21
|
-
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',_vm._l((_vm.tooltipContent),function(value,label){return _c('div',{key:("" + label + (value.value)),staticClass:"gl-charts-tooltip-default-format-series"},[_c('series-label',{staticClass:"gl-charts-tooltip-default-format-series-label",attrs:{"color":value.color}},[_vm._v("\n "+_vm._s(label)+"\n ")]),_vm._v(" "),_c('div',{staticClass:"gl-charts-tooltip-default-format-series-value"},[_vm.
|
|
21
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',_vm._l((_vm.tooltipContent),function(value,label){return _c('div',{key:("" + label + (value.value)),staticClass:"gl-charts-tooltip-default-format-series"},[_c('series-label',{staticClass:"gl-charts-tooltip-default-format-series-label",attrs:{"color":value.color}},[_vm._v("\n "+_vm._s(label)+"\n ")]),_vm._v(" "),_c('div',{staticClass:"gl-charts-tooltip-default-format-series-value"},[_vm._t("tooltip-value",function(){return [_vm._v(_vm._s(value.value))]},{"value":value.value})],2)],1)}),0)};
|
|
22
22
|
var __vue_staticRenderFns__ = [];
|
|
23
23
|
|
|
24
24
|
/* style */
|
|
@@ -9,15 +9,17 @@ const callbacks = new Map();
|
|
|
9
9
|
* Is a global listener already set up?
|
|
10
10
|
*/
|
|
11
11
|
let listening = false;
|
|
12
|
+
let lastMousedown = null;
|
|
12
13
|
const globalListener = event => {
|
|
13
14
|
callbacks.forEach((_ref, element) => {
|
|
14
15
|
let {
|
|
15
16
|
bindTimeStamp,
|
|
16
17
|
callback
|
|
17
18
|
} = _ref;
|
|
19
|
+
const originalEvent = lastMousedown || event;
|
|
18
20
|
if (
|
|
19
21
|
// Ignore events that aren't targeted outside the element
|
|
20
|
-
element.contains(
|
|
22
|
+
element.contains(originalEvent.target) ||
|
|
21
23
|
// Only consider events triggered after the directive was bound
|
|
22
24
|
event.timeStamp <= bindTimeStamp) {
|
|
23
25
|
return;
|
|
@@ -31,20 +33,31 @@ const globalListener = event => {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
});
|
|
36
|
+
lastMousedown = null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// We need to listen for mouse events because text selection fires click event only when selection ends.
|
|
40
|
+
// This means that the click event target could differ from the element where it originally started.
|
|
41
|
+
// As example: if we use mouse events we could guarantee that selecting text within a dropdown won't close it.
|
|
42
|
+
const onMousedown = event => {
|
|
43
|
+
lastMousedown = event;
|
|
34
44
|
};
|
|
35
45
|
const startListening = () => {
|
|
36
46
|
if (listening) {
|
|
37
47
|
return;
|
|
38
48
|
}
|
|
49
|
+
document.addEventListener('mousedown', onMousedown);
|
|
39
50
|
document.addEventListener('click', globalListener, {
|
|
40
51
|
capture: true
|
|
41
52
|
});
|
|
42
53
|
listening = true;
|
|
54
|
+
lastMousedown = null;
|
|
43
55
|
};
|
|
44
56
|
const stopListening = () => {
|
|
45
57
|
if (!listening) {
|
|
46
58
|
return;
|
|
47
59
|
}
|
|
60
|
+
document.removeEventListener('mousedown', onMousedown);
|
|
48
61
|
document.removeEventListener('click', globalListener);
|
|
49
62
|
listening = false;
|
|
50
63
|
};
|
package/dist/tokens/js/tokens.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "67.
|
|
3
|
+
"version": "67.3.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@gitlab/eslint-plugin": "19.2.0",
|
|
95
95
|
"@gitlab/fonts": "^1.3.0",
|
|
96
96
|
"@gitlab/stylelint-config": "5.0.1",
|
|
97
|
-
"@gitlab/svgs": "3.
|
|
97
|
+
"@gitlab/svgs": "3.68.0",
|
|
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",
|
|
@@ -122,7 +122,7 @@
|
|
|
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.
|
|
125
|
+
"cypress": "13.3.3",
|
|
126
126
|
"cypress-axe": "^1.4.0",
|
|
127
127
|
"dompurify": "^3.0.0",
|
|
128
128
|
"emoji-regex": "^10.0.0",
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { nextTick } from 'vue';
|
|
1
2
|
import { shallowMount } from '@vue/test-utils';
|
|
2
3
|
|
|
3
4
|
import { LEGEND_LAYOUT_INLINE, LEGEND_LAYOUT_TABLE } from '~/utils/charts/constants';
|
|
4
5
|
import { createMockChartInstance, ChartTooltipStub } from '~helpers/chart_stubs';
|
|
5
6
|
import { expectHeightAutoClasses } from '~helpers/chart_height';
|
|
7
|
+
|
|
6
8
|
import Chart from '../chart/chart.vue';
|
|
7
9
|
import ChartLegend from '../legend/legend.vue';
|
|
10
|
+
import TooltipDefaultFormat from '../../shared_components/charts/tooltip_default_format.vue';
|
|
11
|
+
|
|
8
12
|
import LineChart from './line.vue';
|
|
9
13
|
|
|
10
14
|
let mockChartInstance;
|
|
@@ -39,7 +43,7 @@ describe('line component', () => {
|
|
|
39
43
|
it('emits `created`, with the chart instance', async () => {
|
|
40
44
|
createShallowWrapper();
|
|
41
45
|
|
|
42
|
-
await
|
|
46
|
+
await nextTick();
|
|
43
47
|
|
|
44
48
|
expect(wrapper.emitted('created').length).toBe(1);
|
|
45
49
|
expect(wrapper.emitted('created')[0][0]).toBe(mockChartInstance);
|
|
@@ -49,7 +53,7 @@ describe('line component', () => {
|
|
|
49
53
|
it('are hidden by default', async () => {
|
|
50
54
|
createShallowWrapper();
|
|
51
55
|
|
|
52
|
-
await
|
|
56
|
+
await nextTick();
|
|
53
57
|
|
|
54
58
|
expect(findAnnotationsTooltip().exists()).toBe(false);
|
|
55
59
|
});
|
|
@@ -64,7 +68,7 @@ describe('line component', () => {
|
|
|
64
68
|
],
|
|
65
69
|
});
|
|
66
70
|
|
|
67
|
-
await
|
|
71
|
+
await nextTick();
|
|
68
72
|
|
|
69
73
|
expect(findAnnotationsTooltip().exists()).toBe(true);
|
|
70
74
|
});
|
|
@@ -88,7 +92,7 @@ describe('line component', () => {
|
|
|
88
92
|
},
|
|
89
93
|
});
|
|
90
94
|
|
|
91
|
-
await
|
|
95
|
+
await nextTick();
|
|
92
96
|
|
|
93
97
|
expect(findAnnotationsTooltip().exists()).toBe(true);
|
|
94
98
|
});
|
|
@@ -127,13 +131,65 @@ describe('line component', () => {
|
|
|
127
131
|
|
|
128
132
|
wrapper.vm.onChartDataPointMouseOver(params);
|
|
129
133
|
|
|
130
|
-
await
|
|
134
|
+
await nextTick();
|
|
131
135
|
|
|
132
136
|
expect(findAnnotationsTooltip().html()).toContain(params.data.xAxis);
|
|
133
137
|
expect(findAnnotationsTooltip().html()).toContain(params.data.tooltipData.content);
|
|
134
138
|
});
|
|
135
139
|
});
|
|
136
140
|
|
|
141
|
+
describe('tooltip', () => {
|
|
142
|
+
const tooltipParams = {
|
|
143
|
+
seriesData: [
|
|
144
|
+
{
|
|
145
|
+
seriesName: 'Series 1',
|
|
146
|
+
value: ['x', 1000],
|
|
147
|
+
color: '#fff',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
seriesName: 'Series 2',
|
|
151
|
+
value: ['x', 1001],
|
|
152
|
+
color: '#fff',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
it('renders tooltip', async () => {
|
|
158
|
+
createShallowWrapper(
|
|
159
|
+
{},
|
|
160
|
+
{
|
|
161
|
+
stubs: { TooltipDefaultFormat },
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
wrapper.vm.defaultFormatTooltipText(tooltipParams); // force render of a tooltip
|
|
166
|
+
await nextTick();
|
|
167
|
+
|
|
168
|
+
const tooltipText = findDataTooltip().text();
|
|
169
|
+
expect(tooltipText).toContain('1000');
|
|
170
|
+
expect(tooltipText).toContain('1001');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('renders formatted tooltip values', async () => {
|
|
174
|
+
createShallowWrapper(
|
|
175
|
+
{},
|
|
176
|
+
{
|
|
177
|
+
stubs: { TooltipDefaultFormat },
|
|
178
|
+
scopedSlots: {
|
|
179
|
+
'tooltip-value': ({ value }) => `$ ${value.toLocaleString()}`,
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
wrapper.vm.defaultFormatTooltipText(tooltipParams); // force render of a tooltip
|
|
185
|
+
await nextTick();
|
|
186
|
+
|
|
187
|
+
const tooltipText = findDataTooltip().text();
|
|
188
|
+
expect(tooltipText).toContain('$ 1,000');
|
|
189
|
+
expect(tooltipText).toContain('$ 1,001');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
137
193
|
describe('tooltip position', () => {
|
|
138
194
|
const dataTooltipTitle = 'FooBar';
|
|
139
195
|
|
|
@@ -160,7 +216,7 @@ describe('line component', () => {
|
|
|
160
216
|
|
|
161
217
|
wrapper.setData({ dataTooltipPosition: { left, top }, dataTooltipTitle });
|
|
162
218
|
|
|
163
|
-
await
|
|
219
|
+
await nextTick();
|
|
164
220
|
|
|
165
221
|
expect(findDataTooltip().props('left')).toBe(`${left}`);
|
|
166
222
|
expect(findDataTooltip().props('top')).toBe(`${top}`);
|
|
@@ -172,7 +228,7 @@ describe('line component', () => {
|
|
|
172
228
|
it('is inline by default', async () => {
|
|
173
229
|
createShallowWrapper();
|
|
174
230
|
|
|
175
|
-
await
|
|
231
|
+
await nextTick();
|
|
176
232
|
|
|
177
233
|
expect(findLegend().props('layout')).toBe(LEGEND_LAYOUT_INLINE);
|
|
178
234
|
});
|
|
@@ -182,7 +238,7 @@ describe('line component', () => {
|
|
|
182
238
|
legendLayout: LEGEND_LAYOUT_INLINE,
|
|
183
239
|
});
|
|
184
240
|
|
|
185
|
-
await
|
|
241
|
+
await nextTick();
|
|
186
242
|
|
|
187
243
|
expect(findLegend().props('layout')).toBe(LEGEND_LAYOUT_INLINE);
|
|
188
244
|
});
|
|
@@ -192,7 +248,7 @@ describe('line component', () => {
|
|
|
192
248
|
legendLayout: LEGEND_LAYOUT_TABLE,
|
|
193
249
|
});
|
|
194
250
|
|
|
195
|
-
await
|
|
251
|
+
await nextTick();
|
|
196
252
|
|
|
197
253
|
expect(findLegend().props('layout')).toBe(LEGEND_LAYOUT_TABLE);
|
|
198
254
|
});
|
|
@@ -201,7 +257,7 @@ describe('line component', () => {
|
|
|
201
257
|
showLegend: false,
|
|
202
258
|
});
|
|
203
259
|
|
|
204
|
-
await
|
|
260
|
+
await nextTick();
|
|
205
261
|
|
|
206
262
|
expect(findLegend().exists()).toBe(false);
|
|
207
263
|
});
|
|
@@ -390,7 +390,12 @@ export default {
|
|
|
390
390
|
</div>
|
|
391
391
|
</template>
|
|
392
392
|
<slot v-if="formatTooltipText" name="tooltip-content"></slot>
|
|
393
|
-
<tooltip-default-format v-else :tooltip-content="dataTooltipContent"
|
|
393
|
+
<tooltip-default-format v-else :tooltip-content="dataTooltipContent">
|
|
394
|
+
<template v-if="$scopedSlots['tooltip-value']" #tooltip-value="scope">
|
|
395
|
+
<!-- @slot Tooltip value formatter -->
|
|
396
|
+
<slot name="tooltip-value" v-bind="scope"></slot>
|
|
397
|
+
</template>
|
|
398
|
+
</tooltip-default-format>
|
|
394
399
|
</chart-tooltip>
|
|
395
400
|
<chart-legend
|
|
396
401
|
v-if="hasLegend"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
|
|
3
|
+
import SeriesLabel from '../../charts/series_label/series_label.vue';
|
|
4
|
+
import TooltipDefaultFormat from './tooltip_default_format.vue';
|
|
5
|
+
|
|
6
|
+
const mockTooltipContent = {
|
|
7
|
+
'Series A': {
|
|
8
|
+
color: '#000000',
|
|
9
|
+
value: 1000,
|
|
10
|
+
},
|
|
11
|
+
'Series B': {
|
|
12
|
+
color: '#ffffff',
|
|
13
|
+
value: 5555.5,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('tooltip default format', () => {
|
|
18
|
+
let wrapper;
|
|
19
|
+
|
|
20
|
+
const createComponent = ({ props, ...options } = {}) => {
|
|
21
|
+
wrapper = shallowMount(TooltipDefaultFormat, {
|
|
22
|
+
propsData: {
|
|
23
|
+
tooltipContent: mockTooltipContent,
|
|
24
|
+
...props,
|
|
25
|
+
},
|
|
26
|
+
...options,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const findSeriesLabelAt = (i) => wrapper.findAllComponents(SeriesLabel).at(i);
|
|
31
|
+
|
|
32
|
+
it('renders default tooltip', () => {
|
|
33
|
+
createComponent();
|
|
34
|
+
|
|
35
|
+
const text = wrapper.text();
|
|
36
|
+
|
|
37
|
+
expect(text).toContain('Series A');
|
|
38
|
+
expect(text).toContain('1000');
|
|
39
|
+
|
|
40
|
+
expect(text).toContain('Series B');
|
|
41
|
+
expect(text).toContain('5555.5');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders label colors', () => {
|
|
45
|
+
createComponent();
|
|
46
|
+
|
|
47
|
+
expect(findSeriesLabelAt(0).props('color')).toBe('#000000');
|
|
48
|
+
expect(findSeriesLabelAt(1).props('color')).toBe('#ffffff');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders formatted values', () => {
|
|
52
|
+
createComponent({
|
|
53
|
+
scopedSlots: {
|
|
54
|
+
'tooltip-value': ({ value }) =>
|
|
55
|
+
value.toLocaleString(undefined, {
|
|
56
|
+
minimumFractionDigits: 2,
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const text = wrapper.text();
|
|
62
|
+
|
|
63
|
+
expect(text).toContain('Series A');
|
|
64
|
+
expect(text).toContain('1,000.00');
|
|
65
|
+
|
|
66
|
+
expect(text).toContain('Series B');
|
|
67
|
+
expect(text).toContain('5,555.50');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -9,12 +9,14 @@ const callbacks = new Map();
|
|
|
9
9
|
* Is a global listener already set up?
|
|
10
10
|
*/
|
|
11
11
|
let listening = false;
|
|
12
|
+
let lastMousedown = null;
|
|
12
13
|
|
|
13
14
|
const globalListener = (event) => {
|
|
14
15
|
callbacks.forEach(({ bindTimeStamp, callback }, element) => {
|
|
16
|
+
const originalEvent = lastMousedown || event;
|
|
15
17
|
if (
|
|
16
18
|
// Ignore events that aren't targeted outside the element
|
|
17
|
-
element.contains(
|
|
19
|
+
element.contains(originalEvent.target) ||
|
|
18
20
|
// Only consider events triggered after the directive was bound
|
|
19
21
|
event.timeStamp <= bindTimeStamp
|
|
20
22
|
) {
|
|
@@ -30,6 +32,14 @@ const globalListener = (event) => {
|
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
});
|
|
35
|
+
lastMousedown = null;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// We need to listen for mouse events because text selection fires click event only when selection ends.
|
|
39
|
+
// This means that the click event target could differ from the element where it originally started.
|
|
40
|
+
// As example: if we use mouse events we could guarantee that selecting text within a dropdown won't close it.
|
|
41
|
+
const onMousedown = (event) => {
|
|
42
|
+
lastMousedown = event;
|
|
33
43
|
};
|
|
34
44
|
|
|
35
45
|
const startListening = () => {
|
|
@@ -37,8 +47,10 @@ const startListening = () => {
|
|
|
37
47
|
return;
|
|
38
48
|
}
|
|
39
49
|
|
|
50
|
+
document.addEventListener('mousedown', onMousedown);
|
|
40
51
|
document.addEventListener('click', globalListener, { capture: true });
|
|
41
52
|
listening = true;
|
|
53
|
+
lastMousedown = null;
|
|
42
54
|
};
|
|
43
55
|
|
|
44
56
|
const stopListening = () => {
|
|
@@ -46,6 +58,7 @@ const stopListening = () => {
|
|
|
46
58
|
return;
|
|
47
59
|
}
|
|
48
60
|
|
|
61
|
+
document.removeEventListener('mousedown', onMousedown);
|
|
49
62
|
document.removeEventListener('click', globalListener);
|
|
50
63
|
listening = false;
|
|
51
64
|
};
|
|
@@ -126,35 +126,14 @@ describe('outside directive', () => {
|
|
|
126
126
|
expect(document.addEventListener).not.toHaveBeenCalled();
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
-
it('attaches the global listener on first initialisation', async () => {
|
|
130
|
-
await createComponent();
|
|
131
|
-
|
|
132
|
-
expect(document.addEventListener.mock.calls).toEqual([
|
|
133
|
-
['click', expect.any(Function), { capture: true }],
|
|
134
|
-
]);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
129
|
it('detaches the global listener when last binding is removed', async () => {
|
|
138
130
|
await createComponent();
|
|
139
131
|
|
|
140
132
|
wrapper.destroy();
|
|
141
133
|
|
|
142
|
-
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('only binds once, even with multiple instances', async () => {
|
|
146
|
-
await createComponent({
|
|
147
|
-
template: `
|
|
148
|
-
<div>
|
|
149
|
-
<div v-outside="onClick"></div>
|
|
150
|
-
<div v-outside="onClick"></div>
|
|
151
|
-
</div>
|
|
152
|
-
`,
|
|
153
|
-
});
|
|
134
|
+
document.body.dispatchEvent(new MouseEvent('click'));
|
|
154
135
|
|
|
155
|
-
expect(
|
|
156
|
-
['click', expect.any(Function), { capture: true }],
|
|
157
|
-
]);
|
|
136
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
158
137
|
});
|
|
159
138
|
|
|
160
139
|
it('only unbinds once there are no instances', async () => {
|
|
@@ -173,12 +152,16 @@ describe('outside directive', () => {
|
|
|
173
152
|
wrapper.setData({ instances: 1 });
|
|
174
153
|
await wrapper.vm.$nextTick();
|
|
175
154
|
|
|
176
|
-
|
|
155
|
+
document.body.dispatchEvent(new MouseEvent('click'));
|
|
156
|
+
|
|
157
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
177
158
|
|
|
178
159
|
wrapper.setData({ instances: 0 });
|
|
179
160
|
await wrapper.vm.$nextTick();
|
|
180
161
|
|
|
181
|
-
|
|
162
|
+
document.body.dispatchEvent(new MouseEvent('click'));
|
|
163
|
+
|
|
164
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
182
165
|
});
|
|
183
166
|
});
|
|
184
167
|
|
|
@@ -341,4 +324,15 @@ describe('outside directive', () => {
|
|
|
341
324
|
expect(global.console.error.mock.calls).toEqual([[thrownError]]);
|
|
342
325
|
});
|
|
343
326
|
});
|
|
327
|
+
|
|
328
|
+
describe('mousedown before click', () => {
|
|
329
|
+
it('respects mousedown event before click', async () => {
|
|
330
|
+
await createComponent();
|
|
331
|
+
|
|
332
|
+
find('inside').trigger('mousedown');
|
|
333
|
+
find('outside').trigger('click');
|
|
334
|
+
|
|
335
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
336
|
+
});
|
|
337
|
+
});
|
|
344
338
|
});
|