cbvirtua 1.0.10 → 1.0.12
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/package.json +1 -1
- package/smoothscroll.js +431 -0
- package/vant/style/README.md +79 -0
- package/vant/style/README.zh-CN.md +79 -0
- package/vant/style/animation.less +139 -0
- package/vant/style/base.less +10 -0
- package/vant/style/clearfix.less +5 -0
- package/vant/style/demo/index.vue +110 -0
- package/vant/style/ellipsis.less +13 -0
- package/vant/style/hairline.less +47 -0
- package/vant/style/mixins/clearfix.less +7 -0
- package/vant/style/mixins/ellipsis.less +15 -0
- package/vant/style/mixins/hairline.less +39 -0
- package/vant/style/normalize.less +38 -0
- package/vant/style/reset.less +171 -0
- package/vant/style/var.less +901 -0
- package/vant/tab/README.md +307 -0
- package/vant/tab/README.zh-CN.md +342 -0
- package/vant/tab/demo/index.vue +193 -0
- package/vant/tab/index.js +95 -0
- package/vant/tab/index.less +17 -0
- package/vant/tab/test/__snapshots__/demo.spec.js.snap +349 -0
- package/vant/tab/test/__snapshots__/index.spec.js.snap +352 -0
- package/vant/tab/test/__snapshots__/insert.spec.js.snap +63 -0
- package/vant/tab/test/demo.spec.js +4 -0
- package/vant/tab/test/index.spec.js +435 -0
- package/vant/tab/test/insert.spec.js +75 -0
- package/vant/tabs/Content.js +79 -0
- package/vant/tabs/Title.js +91 -0
- package/vant/tabs/index.js +453 -0
- package/vant/tabs/index.less +153 -0
- package/vant/tabs/utils.ts +53 -0
- package/vant/utils/constant.ts +11 -0
- package/vant/utils/create/bem.ts +45 -0
- package/vant/utils/create/component.ts +86 -0
- package/vant/utils/create/i18n.ts +16 -0
- package/vant/utils/create/index.ts +14 -0
- package/vant/utils/deep-assign.ts +27 -0
- package/vant/utils/deep-clone.ts +23 -0
- package/vant/utils/dom/event.ts +56 -0
- package/vant/utils/dom/node.ts +7 -0
- package/vant/utils/dom/raf.ts +40 -0
- package/vant/utils/dom/reset-scroll.ts +16 -0
- package/vant/utils/dom/scroll.ts +81 -0
- package/vant/utils/dom/style.ts +11 -0
- package/vant/utils/format/number.ts +52 -0
- package/vant/utils/format/string.ts +15 -0
- package/vant/utils/format/unit.ts +61 -0
- package/vant/utils/functional.ts +73 -0
- package/vant/utils/index.ts +79 -0
- package/vant/utils/interceptor.ts +27 -0
- package/vant/utils/router.ts +54 -0
- package/vant/utils/test/bem.spec.js +39 -0
- package/vant/utils/test/index.spec.js +152 -0
- package/vant/utils/test/interceptor.spec.js +50 -0
- package/vant/utils/types.ts +40 -0
- package/vant/utils/validate/date.ts +8 -0
- package/vant/utils/validate/email.ts +5 -0
- package/vant/utils/validate/mobile.ts +6 -0
- package/vant/utils/validate/number.ts +12 -0
- package/vant/utils/validate/system.ts +13 -0
- package/vant/utils/vnodes.ts +33 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import Tabs from '../../tabs';
|
|
2
|
+
import { mount, later, triggerDrag, mockScrollTop } from '../../../test';
|
|
3
|
+
|
|
4
|
+
function createWrapper(options = {}) {
|
|
5
|
+
return mount({
|
|
6
|
+
template: `
|
|
7
|
+
<van-tabs
|
|
8
|
+
:color="color"
|
|
9
|
+
:type="type"
|
|
10
|
+
:sticky="sticky"
|
|
11
|
+
:line-width="lineWidth"
|
|
12
|
+
:lazy-render="lazyRender"
|
|
13
|
+
@change="onChange"
|
|
14
|
+
>
|
|
15
|
+
${options.extraTemplate || ''}
|
|
16
|
+
<van-tab title="title1">Text</van-tab>
|
|
17
|
+
<van-tab title="title2">Text</van-tab>
|
|
18
|
+
<van-tab title="title3" disabled>Text</van-tab>
|
|
19
|
+
</van-tabs>
|
|
20
|
+
`,
|
|
21
|
+
data() {
|
|
22
|
+
return {
|
|
23
|
+
color: '#ee0a24',
|
|
24
|
+
type: 'line',
|
|
25
|
+
sticky: true,
|
|
26
|
+
lineWidth: 2,
|
|
27
|
+
lazyRender: true,
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
...options,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test('click to switch tab', async () => {
|
|
35
|
+
const onChange = jest.fn();
|
|
36
|
+
const wrapper = mount({
|
|
37
|
+
template: `
|
|
38
|
+
<van-tabs @change="onChange">
|
|
39
|
+
<van-tab title="title1">Text</van-tab>
|
|
40
|
+
<van-tab title="title2">Text</van-tab>
|
|
41
|
+
<van-tab title="title3" disabled>Text</van-tab>
|
|
42
|
+
</van-tabs>
|
|
43
|
+
`,
|
|
44
|
+
methods: {
|
|
45
|
+
onChange,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await later();
|
|
50
|
+
expect(wrapper).toMatchSnapshot();
|
|
51
|
+
|
|
52
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
53
|
+
tabs.at(1).trigger('click');
|
|
54
|
+
tabs.at(2).trigger('click');
|
|
55
|
+
await later();
|
|
56
|
+
expect(wrapper).toMatchSnapshot();
|
|
57
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('swipe to switch tab', async () => {
|
|
61
|
+
const onChange = jest.fn();
|
|
62
|
+
const wrapper = mount({
|
|
63
|
+
template: `
|
|
64
|
+
<van-tabs v-model="active" swipeable @change="onChange">
|
|
65
|
+
<van-tab title="title1">Text</van-tab>
|
|
66
|
+
<van-tab title="title2">Text</van-tab>
|
|
67
|
+
<van-tab title="title3" disabled>Text</van-tab>
|
|
68
|
+
</van-tabs>
|
|
69
|
+
`,
|
|
70
|
+
data() {
|
|
71
|
+
return {
|
|
72
|
+
active: 0,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
methods: {
|
|
76
|
+
onChange,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const content = wrapper.find('.van-tabs__content');
|
|
81
|
+
await later();
|
|
82
|
+
expect(wrapper).toMatchSnapshot();
|
|
83
|
+
|
|
84
|
+
triggerDrag(content, -100, 0);
|
|
85
|
+
expect(wrapper).toMatchSnapshot();
|
|
86
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
87
|
+
expect(onChange).toHaveBeenCalledWith(1, 'title2');
|
|
88
|
+
|
|
89
|
+
triggerDrag(content, -100, 0);
|
|
90
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
91
|
+
expect(wrapper).toMatchSnapshot();
|
|
92
|
+
|
|
93
|
+
await later();
|
|
94
|
+
wrapper.destroy();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('change tabs data', async () => {
|
|
98
|
+
const wrapper = createWrapper();
|
|
99
|
+
|
|
100
|
+
await later();
|
|
101
|
+
|
|
102
|
+
expect(wrapper).toMatchSnapshot();
|
|
103
|
+
|
|
104
|
+
wrapper.setData({
|
|
105
|
+
swipeable: false,
|
|
106
|
+
sticky: false,
|
|
107
|
+
type: 'card',
|
|
108
|
+
color: 'blue',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await later();
|
|
112
|
+
expect(wrapper).toMatchSnapshot();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('lazy render', async () => {
|
|
116
|
+
const wrapper = createWrapper();
|
|
117
|
+
|
|
118
|
+
expect(wrapper).toMatchSnapshot();
|
|
119
|
+
|
|
120
|
+
wrapper.setData({
|
|
121
|
+
lazyRender: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await later();
|
|
125
|
+
expect(wrapper).toMatchSnapshot();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('render nav-left & nav-right slot', async () => {
|
|
129
|
+
const wrapper = createWrapper({
|
|
130
|
+
extraTemplate: `
|
|
131
|
+
<template v-slot:nav-left>Nav Left</template>
|
|
132
|
+
<template v-slot:nav-right>Nav Right</template>
|
|
133
|
+
`,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(wrapper).toMatchSnapshot();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('border props', async () => {
|
|
140
|
+
const wrapper = mount(Tabs, {
|
|
141
|
+
propsData: {
|
|
142
|
+
border: false,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(wrapper).toMatchSnapshot();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('click event', async () => {
|
|
150
|
+
const onClick = jest.fn();
|
|
151
|
+
const onDisabled = jest.fn();
|
|
152
|
+
|
|
153
|
+
const wrapper = mount({
|
|
154
|
+
template: `
|
|
155
|
+
<van-tabs @click="onClick" @disabled="onDisabled">
|
|
156
|
+
<van-tab title="title1">Text</van-tab>
|
|
157
|
+
<van-tab title="title2" disabled>Text</van-tab>
|
|
158
|
+
</van-tabs>
|
|
159
|
+
`,
|
|
160
|
+
methods: {
|
|
161
|
+
onClick,
|
|
162
|
+
onDisabled,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
167
|
+
|
|
168
|
+
tabs.at(0).trigger('click');
|
|
169
|
+
expect(onClick).toHaveBeenCalledWith(0, 'title1');
|
|
170
|
+
|
|
171
|
+
tabs.at(1).trigger('click');
|
|
172
|
+
expect(onDisabled).toHaveBeenCalledWith(1, 'title2');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('name prop', async () => {
|
|
176
|
+
const onClick = jest.fn();
|
|
177
|
+
const onChange = jest.fn();
|
|
178
|
+
const onDisabled = jest.fn();
|
|
179
|
+
|
|
180
|
+
const wrapper = mount({
|
|
181
|
+
template: `
|
|
182
|
+
<van-tabs v-model="active" @click="onClick" @disabled="onDisabled" @change="onChange">
|
|
183
|
+
<van-tab title="title1" name="a">Text</van-tab>
|
|
184
|
+
<van-tab title="title2" name="b">Text</van-tab>
|
|
185
|
+
<van-tab title="title3" name="c" disabled>Text</van-tab>
|
|
186
|
+
</van-tabs>
|
|
187
|
+
`,
|
|
188
|
+
methods: {
|
|
189
|
+
onClick,
|
|
190
|
+
onChange,
|
|
191
|
+
onDisabled,
|
|
192
|
+
},
|
|
193
|
+
data() {
|
|
194
|
+
return {
|
|
195
|
+
active: 0,
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
await later();
|
|
201
|
+
expect(wrapper).toMatchSnapshot();
|
|
202
|
+
|
|
203
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
204
|
+
tabs.at(1).trigger('click');
|
|
205
|
+
|
|
206
|
+
expect(onClick).toHaveBeenCalledWith('b', 'title2');
|
|
207
|
+
expect(onChange).toHaveBeenCalledWith('b', 'title2');
|
|
208
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
209
|
+
|
|
210
|
+
tabs.at(2).trigger('click');
|
|
211
|
+
expect(onDisabled).toHaveBeenCalledWith('c', 'title3');
|
|
212
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('set name to zero', async () => {
|
|
216
|
+
const onClick = jest.fn();
|
|
217
|
+
|
|
218
|
+
const wrapper = mount({
|
|
219
|
+
template: `
|
|
220
|
+
<van-tabs @click="onClick">
|
|
221
|
+
<van-tab title="title1" :name="1">Text</van-tab>
|
|
222
|
+
<van-tab title="title2" :name="0">Text</van-tab>
|
|
223
|
+
</van-tabs>
|
|
224
|
+
`,
|
|
225
|
+
methods: {
|
|
226
|
+
onClick,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
231
|
+
tabs.at(1).trigger('click');
|
|
232
|
+
expect(onClick).toHaveBeenCalledWith(0, 'title2');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('title-style prop', () => {
|
|
236
|
+
const wrapper = mount({
|
|
237
|
+
template: `
|
|
238
|
+
<van-tabs>
|
|
239
|
+
<van-tab title="title1" title-style="color: red;">Text</van-tab>
|
|
240
|
+
</van-tabs>
|
|
241
|
+
`,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(wrapper.find('.van-tab').element.style.color).toEqual('red');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('dot prop', () => {
|
|
248
|
+
const wrapper = mount({
|
|
249
|
+
template: `
|
|
250
|
+
<van-tabs>
|
|
251
|
+
<van-tab dot>Text</van-tab>
|
|
252
|
+
</van-tabs>
|
|
253
|
+
`,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
expect(wrapper).toMatchSnapshot();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('badge prop', () => {
|
|
260
|
+
const wrapper = mount({
|
|
261
|
+
template: `
|
|
262
|
+
<van-tabs>
|
|
263
|
+
<van-tab badge="10">Text</van-tab>
|
|
264
|
+
</van-tabs>
|
|
265
|
+
`,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(wrapper).toMatchSnapshot();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('scrollspy prop', async () => {
|
|
272
|
+
const onChange = jest.fn();
|
|
273
|
+
window.scrollTo = jest.fn();
|
|
274
|
+
|
|
275
|
+
const wrapper = mount({
|
|
276
|
+
template: `
|
|
277
|
+
<van-tabs scrollspy sticky @change="onChange">
|
|
278
|
+
<van-tab name="a" title="title1">Text</van-tab>
|
|
279
|
+
<van-tab name="b" title="title2">Text</van-tab>
|
|
280
|
+
<van-tab name="c" title="title3">Text</van-tab>
|
|
281
|
+
</van-tabs>
|
|
282
|
+
`,
|
|
283
|
+
methods: {
|
|
284
|
+
onChange,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
await later();
|
|
289
|
+
expect(wrapper).toMatchSnapshot();
|
|
290
|
+
|
|
291
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
292
|
+
tabs.at(2).trigger('click');
|
|
293
|
+
expect(onChange).toHaveBeenCalledWith('c', 'title3');
|
|
294
|
+
|
|
295
|
+
await later();
|
|
296
|
+
mockScrollTop(100);
|
|
297
|
+
expect(wrapper).toMatchSnapshot();
|
|
298
|
+
expect(onChange).toHaveBeenCalledWith('c', 'title3');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('scrollTo method', async () => {
|
|
302
|
+
const onChange = jest.fn();
|
|
303
|
+
window.scrollTo = jest.fn();
|
|
304
|
+
|
|
305
|
+
mount({
|
|
306
|
+
template: `
|
|
307
|
+
<van-tabs scrollspy sticky @change="onChange" ref="root">
|
|
308
|
+
<van-tab name="a" title="title1">Text</van-tab>
|
|
309
|
+
<van-tab name="b" title="title2">Text</van-tab>
|
|
310
|
+
<van-tab name="c" title="title3">Text</van-tab>
|
|
311
|
+
</van-tabs>
|
|
312
|
+
`,
|
|
313
|
+
methods: {
|
|
314
|
+
onChange,
|
|
315
|
+
},
|
|
316
|
+
mounted() {
|
|
317
|
+
this.$refs.root.scrollTo('b');
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
await later();
|
|
322
|
+
expect(onChange).toHaveBeenCalledWith('b', 'title2');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test('rendered event', async () => {
|
|
326
|
+
const onRendered = jest.fn();
|
|
327
|
+
|
|
328
|
+
const wrapper = mount({
|
|
329
|
+
template: `
|
|
330
|
+
<van-tabs v-model="active" @rendered="onRendered">
|
|
331
|
+
<van-tab name="a" title="title1">Text</van-tab>
|
|
332
|
+
<van-tab name="b" title="title2">Title2</van-tab>
|
|
333
|
+
</van-tabs>
|
|
334
|
+
`,
|
|
335
|
+
data() {
|
|
336
|
+
return {
|
|
337
|
+
active: 'a',
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
methods: {
|
|
341
|
+
onRendered,
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
await later();
|
|
346
|
+
expect(onRendered).toHaveBeenCalledWith('a', 'title1');
|
|
347
|
+
expect(wrapper.find('.van-tab__pane')).toMatchSnapshot();
|
|
348
|
+
|
|
349
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
350
|
+
tabs.at(1).trigger('click');
|
|
351
|
+
tabs.at(0).trigger('click');
|
|
352
|
+
|
|
353
|
+
await later();
|
|
354
|
+
expect(onRendered).toHaveBeenCalledTimes(2);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test('should not trigger rendered event when disable lazy-render', async () => {
|
|
358
|
+
const onRendered = jest.fn();
|
|
359
|
+
|
|
360
|
+
mount({
|
|
361
|
+
template: `
|
|
362
|
+
<van-tabs :lazy-render="false" @rendered="onRendered">
|
|
363
|
+
<van-tab>Text</van-tab>
|
|
364
|
+
<van-tab>Title2</van-tab>
|
|
365
|
+
</van-tabs>
|
|
366
|
+
`,
|
|
367
|
+
methods: {
|
|
368
|
+
onRendered,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
await later();
|
|
373
|
+
expect(onRendered).toHaveBeenCalledTimes(0);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('before-change prop', async () => {
|
|
377
|
+
const onChange = jest.fn();
|
|
378
|
+
const wrapper = mount({
|
|
379
|
+
template: `
|
|
380
|
+
<van-tabs @change="onChange" :before-change="beforeChange">
|
|
381
|
+
<van-tab title="title1">Text</van-tab>
|
|
382
|
+
<van-tab title="title2">Text</van-tab>
|
|
383
|
+
<van-tab title="title3">Text</van-tab>
|
|
384
|
+
<van-tab title="title4">Text</van-tab>
|
|
385
|
+
<van-tab title="title5">Text</van-tab>
|
|
386
|
+
</van-tabs>
|
|
387
|
+
`,
|
|
388
|
+
methods: {
|
|
389
|
+
onChange,
|
|
390
|
+
beforeChange(name) {
|
|
391
|
+
switch (name) {
|
|
392
|
+
case 1:
|
|
393
|
+
return false;
|
|
394
|
+
case 2:
|
|
395
|
+
return true;
|
|
396
|
+
case 3:
|
|
397
|
+
return Promise.resolve(false);
|
|
398
|
+
case 4:
|
|
399
|
+
return Promise.resolve(true);
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
await later();
|
|
406
|
+
|
|
407
|
+
const tabs = wrapper.findAll('.van-tab');
|
|
408
|
+
tabs.at(1).trigger('click');
|
|
409
|
+
expect(onChange).toHaveBeenCalledTimes(0);
|
|
410
|
+
|
|
411
|
+
tabs.at(2).trigger('click');
|
|
412
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
413
|
+
expect(onChange).toHaveBeenLastCalledWith(2, 'title3');
|
|
414
|
+
|
|
415
|
+
tabs.at(3).trigger('click');
|
|
416
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
417
|
+
|
|
418
|
+
tabs.at(4).trigger('click');
|
|
419
|
+
await later();
|
|
420
|
+
expect(onChange).toHaveBeenCalledTimes(2);
|
|
421
|
+
expect(onChange).toHaveBeenLastCalledWith(4, 'title5');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('render empty tab', async () => {
|
|
425
|
+
const wrapper = mount({
|
|
426
|
+
template: `
|
|
427
|
+
<van-tabs>
|
|
428
|
+
<van-tab title="title1" />
|
|
429
|
+
<van-tab title="title2" />
|
|
430
|
+
</van-tabs>
|
|
431
|
+
`,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
expect(wrapper).toMatchSnapshot();
|
|
435
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { mount, later } from '../../../test';
|
|
2
|
+
|
|
3
|
+
// this case will throw wierd error in index.spec.js
|
|
4
|
+
// so separate it
|
|
5
|
+
test('insert tab dynamically', async () => {
|
|
6
|
+
const wrapper = mount({
|
|
7
|
+
template: `
|
|
8
|
+
<van-tabs v-model="active">
|
|
9
|
+
<van-tab title="1">1</van-tab>
|
|
10
|
+
<div v-if="insert">
|
|
11
|
+
<van-tab title="2">2</van-tab>
|
|
12
|
+
</div>
|
|
13
|
+
<van-tab title="3">3</van-tab>
|
|
14
|
+
</van-tabs>
|
|
15
|
+
`,
|
|
16
|
+
data() {
|
|
17
|
+
return {
|
|
18
|
+
insert: false,
|
|
19
|
+
active: 1,
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
await later();
|
|
25
|
+
wrapper.setData({ insert: true });
|
|
26
|
+
expect(wrapper).toMatchSnapshot();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('insert tab with name dynamically', async () => {
|
|
30
|
+
const onChange = jest.fn();
|
|
31
|
+
const wrapper = mount({
|
|
32
|
+
template: `
|
|
33
|
+
<van-tabs v-model="active" @change="onChange">
|
|
34
|
+
<van-tab v-if="insert" title="1" name="bar">2</van-tab>
|
|
35
|
+
<van-tab title="2" name="foo">1</van-tab>
|
|
36
|
+
</van-tabs>
|
|
37
|
+
`,
|
|
38
|
+
data() {
|
|
39
|
+
return {
|
|
40
|
+
insert: false,
|
|
41
|
+
active: 'foo',
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
methods: {
|
|
45
|
+
onChange,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await later();
|
|
50
|
+
wrapper.setData({ insert: true });
|
|
51
|
+
expect(wrapper).toMatchSnapshot();
|
|
52
|
+
expect(onChange).toHaveBeenCalledTimes(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// this case will throw wierd error in index.spec.js
|
|
56
|
+
// so separate it
|
|
57
|
+
test('insert tab with child component', async () => {
|
|
58
|
+
const wrapper = mount({
|
|
59
|
+
template: `
|
|
60
|
+
<van-tabs v-model="active">
|
|
61
|
+
<van-tab title="1">1</van-tab>
|
|
62
|
+
<my-tab />
|
|
63
|
+
<van-tab title="3">3</van-tab>
|
|
64
|
+
</van-tabs>
|
|
65
|
+
`,
|
|
66
|
+
components: {
|
|
67
|
+
'my-tab': {
|
|
68
|
+
template: `<van-tab title="2">2</van-tab>`,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await later();
|
|
74
|
+
expect(wrapper).toMatchSnapshot();
|
|
75
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createNamespace } from '../utils';
|
|
2
|
+
import { TouchMixin } from '../mixins/touch';
|
|
3
|
+
|
|
4
|
+
const [createComponent, bem] = createNamespace('tabs');
|
|
5
|
+
const MIN_SWIPE_DISTANCE = 50;
|
|
6
|
+
|
|
7
|
+
export default createComponent({
|
|
8
|
+
mixins: [TouchMixin],
|
|
9
|
+
|
|
10
|
+
props: {
|
|
11
|
+
count: Number,
|
|
12
|
+
duration: [Number, String],
|
|
13
|
+
animated: Boolean,
|
|
14
|
+
swipeable: Boolean,
|
|
15
|
+
currentIndex: Number,
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
computed: {
|
|
19
|
+
style() {
|
|
20
|
+
if (this.animated) {
|
|
21
|
+
return {
|
|
22
|
+
transform: `translate3d(${-1 * this.currentIndex * 100}%, 0, 0)`,
|
|
23
|
+
transitionDuration: `${this.duration}s`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
listeners() {
|
|
29
|
+
if (this.swipeable) {
|
|
30
|
+
return {
|
|
31
|
+
touchstart: this.touchStart,
|
|
32
|
+
touchmove: this.touchMove,
|
|
33
|
+
touchend: this.onTouchEnd,
|
|
34
|
+
touchcancel: this.onTouchEnd,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
methods: {
|
|
41
|
+
// watch swipe touch end
|
|
42
|
+
onTouchEnd() {
|
|
43
|
+
const { direction, deltaX, currentIndex } = this;
|
|
44
|
+
|
|
45
|
+
/* istanbul ignore else */
|
|
46
|
+
if (direction === 'horizontal' && this.offsetX >= MIN_SWIPE_DISTANCE) {
|
|
47
|
+
/* istanbul ignore else */
|
|
48
|
+
if (deltaX > 0 && currentIndex !== 0) {
|
|
49
|
+
this.$emit('change', currentIndex - 1);
|
|
50
|
+
} else if (deltaX < 0 && currentIndex !== this.count - 1) {
|
|
51
|
+
this.$emit('change', currentIndex + 1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
genChildren() {
|
|
57
|
+
if (this.animated) {
|
|
58
|
+
return (
|
|
59
|
+
<div class={bem('track')} style={this.style}>
|
|
60
|
+
{this.slots()}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return this.slots();
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
render() {
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
class={bem('content', { animated: this.animated })}
|
|
73
|
+
{...{ on: this.listeners }}
|
|
74
|
+
>
|
|
75
|
+
{this.genChildren()}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createNamespace, isDef } from '../utils';
|
|
2
|
+
import Info from '../info';
|
|
3
|
+
|
|
4
|
+
const [createComponent, bem] = createNamespace('tab');
|
|
5
|
+
|
|
6
|
+
export default createComponent({
|
|
7
|
+
props: {
|
|
8
|
+
dot: Boolean,
|
|
9
|
+
type: String,
|
|
10
|
+
info: [Number, String],
|
|
11
|
+
color: String,
|
|
12
|
+
title: String,
|
|
13
|
+
isActive: Boolean,
|
|
14
|
+
disabled: Boolean,
|
|
15
|
+
scrollable: Boolean,
|
|
16
|
+
activeColor: String,
|
|
17
|
+
inactiveColor: String,
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
computed: {
|
|
21
|
+
style() {
|
|
22
|
+
const style = {};
|
|
23
|
+
const { color, isActive } = this;
|
|
24
|
+
const isCard = this.type === 'card';
|
|
25
|
+
|
|
26
|
+
// card theme color
|
|
27
|
+
if (color && isCard) {
|
|
28
|
+
style.borderColor = color;
|
|
29
|
+
|
|
30
|
+
if (!this.disabled) {
|
|
31
|
+
if (isActive) {
|
|
32
|
+
style.backgroundColor = color;
|
|
33
|
+
} else {
|
|
34
|
+
style.color = color;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const titleColor = isActive ? this.activeColor : this.inactiveColor;
|
|
40
|
+
if (titleColor) {
|
|
41
|
+
style.color = titleColor;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return style;
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
methods: {
|
|
49
|
+
onClick() {
|
|
50
|
+
this.$emit('click');
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
genText() {
|
|
54
|
+
const Text = (
|
|
55
|
+
<span class={bem('text', { ellipsis: !this.scrollable })}>
|
|
56
|
+
{this.slots() || this.title}
|
|
57
|
+
</span>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (this.dot || (isDef(this.info) && this.info !== '')) {
|
|
61
|
+
return (
|
|
62
|
+
<span class={bem('text-wrapper')}>
|
|
63
|
+
{Text}
|
|
64
|
+
{<Info dot={this.dot} info={this.info} />}
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return Text;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
render() {
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
role="tab"
|
|
77
|
+
aria-selected={this.isActive}
|
|
78
|
+
class={[
|
|
79
|
+
bem({
|
|
80
|
+
active: this.isActive,
|
|
81
|
+
disabled: this.disabled,
|
|
82
|
+
}),
|
|
83
|
+
]}
|
|
84
|
+
style={this.style}
|
|
85
|
+
onClick={this.onClick}
|
|
86
|
+
>
|
|
87
|
+
{this.genText()}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
});
|