vueless 1.4.12-beta.6 → 1.4.12-beta.7
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/ui.navigation-tab/UTab.vue +14 -9
- package/ui.navigation-tab/tests/UTab.test.ts +14 -0
- package/ui.navigation-tabs/UTabs.vue +152 -2
- package/ui.navigation-tabs/config.ts +2 -1
- package/ui.navigation-tabs/constants.ts +2 -0
- package/ui.navigation-tabs/storybook/stories.ts +8 -0
- package/ui.navigation-tabs/tests/UTabs.test.ts +213 -1
- package/ui.navigation-tabs/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vueless",
|
|
3
|
-
"version": "1.4.12-beta.
|
|
3
|
+
"version": "1.4.12-beta.7",
|
|
4
4
|
"description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
|
|
5
5
|
"author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
|
|
6
6
|
"homepage": "https://vueless.com",
|
|
@@ -13,12 +13,13 @@ import type { Props as UTabsProps, SetUTabsSelectedItem } from "../ui.navigation
|
|
|
13
13
|
|
|
14
14
|
defineOptions({ inheritAttrs: false });
|
|
15
15
|
|
|
16
|
-
const setUTabsSelectedItem = inject<SetUTabsSelectedItem>("setUTabsSelectedItem");
|
|
17
|
-
const getUTabsSelectedItem = inject("getUTabsSelectedItem");
|
|
18
|
-
const getUTabsScrollable = inject<UTabsProps["scrollable"]>("getUTabsScrollable");
|
|
19
|
-
const getUTabsSquare = inject<UTabsProps["square"]>("getUTabsSquare");
|
|
20
|
-
const getUTabsBlock = inject<UTabsProps["block"]>("getUTabsBlock");
|
|
16
|
+
const setUTabsSelectedItem = inject<SetUTabsSelectedItem | null>("setUTabsSelectedItem", null);
|
|
17
|
+
const getUTabsSelectedItem = inject("getUTabsSelectedItem", null);
|
|
18
|
+
const getUTabsScrollable = inject<UTabsProps["scrollable"]>("getUTabsScrollable", false);
|
|
19
|
+
const getUTabsSquare = inject<UTabsProps["square"]>("getUTabsSquare", false);
|
|
20
|
+
const getUTabsBlock = inject<UTabsProps["block"]>("getUTabsBlock", false);
|
|
21
21
|
const getUTabsSize = inject<UTabsProps["size"]>("getUTabsSize", "md");
|
|
22
|
+
const getUTabsPreventTabClick = inject<() => boolean>("getUTabsPreventTabClick", () => false);
|
|
22
23
|
|
|
23
24
|
const props = withDefaults(defineProps<Props>(), {
|
|
24
25
|
...getDefaults<Props, Config>(defaultConfig, COMPONENT_NAME),
|
|
@@ -34,12 +35,16 @@ const size = computed(() => toValue(getUTabsSize));
|
|
|
34
35
|
const block = computed(() => toValue(getUTabsBlock));
|
|
35
36
|
const square = computed(() => toValue(getUTabsSquare));
|
|
36
37
|
const scrollable = computed(() => toValue(getUTabsScrollable));
|
|
37
|
-
const isActive = computed(() =>
|
|
38
|
+
const isActive = computed(() => {
|
|
39
|
+
if (!setUTabsSelectedItem) return true;
|
|
40
|
+
|
|
41
|
+
return toValue(getUTabsSelectedItem) === props.value;
|
|
42
|
+
});
|
|
38
43
|
|
|
39
44
|
async function onClickSetValue() {
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
if (toValue(getUTabsPreventTabClick) || props.disabled || !setUTabsSelectedItem) return;
|
|
46
|
+
|
|
47
|
+
setUTabsSelectedItem(props.value ?? "");
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
defineExpose({
|
|
@@ -149,6 +149,20 @@ describe("UTab.vue", () => {
|
|
|
149
149
|
|
|
150
150
|
// Active state tests
|
|
151
151
|
describe("Active state", () => {
|
|
152
|
+
it("Active – applies active classes when rendered without UTabs", () => {
|
|
153
|
+
const expectedClass = "border-primary";
|
|
154
|
+
|
|
155
|
+
const component = mount(UTab, {
|
|
156
|
+
props: {
|
|
157
|
+
label: "Tab Item",
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const button = component.findComponent(UButton);
|
|
162
|
+
|
|
163
|
+
expect(button.attributes("class")).toContain(expectedClass);
|
|
164
|
+
});
|
|
165
|
+
|
|
152
166
|
it("Active – applies active classes when tab is selected", () => {
|
|
153
167
|
const value = "tab1";
|
|
154
168
|
const expectedClass = "border-primary";
|
|
@@ -7,7 +7,7 @@ import { getDefaults } from "../utils/ui";
|
|
|
7
7
|
import UTab from "../ui.navigation-tab/UTab.vue";
|
|
8
8
|
import UButton from "../ui.button/UButton.vue";
|
|
9
9
|
|
|
10
|
-
import { COMPONENT_NAME, SCROLL_OFFSET } from "./constants";
|
|
10
|
+
import { COMPONENT_NAME, SCROLL_OFFSET, DRAG_THRESHOLD, DRAG_CLICK_SUPPRESS_MS } from "./constants";
|
|
11
11
|
import defaultConfig from "./config";
|
|
12
12
|
|
|
13
13
|
import type { Props, Config } from "./types";
|
|
@@ -37,6 +37,15 @@ const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
|
|
|
37
37
|
const scrollContainerRef = useTemplateRef<HTMLDivElement | null>("scroll-container");
|
|
38
38
|
const showLeftArrow = ref(false);
|
|
39
39
|
const showRightArrow = ref(false);
|
|
40
|
+
const isDragging = ref(false);
|
|
41
|
+
const preventTabClick = ref(false);
|
|
42
|
+
|
|
43
|
+
let isPointerDown = false;
|
|
44
|
+
let dragAxis: "x" | "y" | null = null;
|
|
45
|
+
let startX = 0;
|
|
46
|
+
let startY = 0;
|
|
47
|
+
let startScrollLeft = 0;
|
|
48
|
+
let suppressClickTimer: ReturnType<typeof setTimeout> | null = null;
|
|
40
49
|
|
|
41
50
|
function checkScroll() {
|
|
42
51
|
if (!scrollContainerRef.value) return;
|
|
@@ -59,17 +68,148 @@ function scrollNext() {
|
|
|
59
68
|
scrollContainerRef.value.scrollBy({ left: SCROLL_OFFSET, behavior: "smooth" });
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
function isScrollableOverflow() {
|
|
72
|
+
if (!scrollContainerRef.value) return false;
|
|
73
|
+
|
|
74
|
+
return scrollContainerRef.value.scrollWidth > scrollContainerRef.value.clientWidth;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function clearSuppressClickTimer() {
|
|
78
|
+
if (!suppressClickTimer) return;
|
|
79
|
+
|
|
80
|
+
clearTimeout(suppressClickTimer);
|
|
81
|
+
suppressClickTimer = null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function stopDragListeners() {
|
|
85
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
86
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
87
|
+
document.removeEventListener("pointercancel", onPointerUp);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resetDragState() {
|
|
91
|
+
isPointerDown = false;
|
|
92
|
+
dragAxis = null;
|
|
93
|
+
isDragging.value = false;
|
|
94
|
+
document.body.style.cursor = "";
|
|
95
|
+
document.body.style.userSelect = "";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function suppressTabClick() {
|
|
99
|
+
preventTabClick.value = true;
|
|
100
|
+
clearSuppressClickTimer();
|
|
101
|
+
|
|
102
|
+
suppressClickTimer = setTimeout(() => {
|
|
103
|
+
preventTabClick.value = false;
|
|
104
|
+
suppressClickTimer = null;
|
|
105
|
+
}, DRAG_CLICK_SUPPRESS_MS);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function onPointerDown(event: PointerEvent) {
|
|
109
|
+
if (!props.scrollable || event.button > 0 || !isScrollableOverflow()) return;
|
|
110
|
+
|
|
111
|
+
isPointerDown = true;
|
|
112
|
+
dragAxis = null;
|
|
113
|
+
startX = event.clientX;
|
|
114
|
+
startY = event.clientY;
|
|
115
|
+
startScrollLeft = scrollContainerRef.value?.scrollLeft ?? 0;
|
|
116
|
+
|
|
117
|
+
document.addEventListener("pointermove", onPointerMove, { passive: false });
|
|
118
|
+
document.addEventListener("pointerup", onPointerUp);
|
|
119
|
+
document.addEventListener("pointercancel", onPointerUp);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function onPointerMove(event: PointerEvent) {
|
|
123
|
+
if (!isPointerDown || !scrollContainerRef.value) return;
|
|
124
|
+
|
|
125
|
+
if (event.pointerType === "mouse" && event.buttons === 0) {
|
|
126
|
+
onPointerUp();
|
|
127
|
+
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const deltaX = event.clientX - startX;
|
|
132
|
+
const deltaY = event.clientY - startY;
|
|
133
|
+
|
|
134
|
+
if (!dragAxis) {
|
|
135
|
+
if (Math.abs(deltaX) < DRAG_THRESHOLD && Math.abs(deltaY) < DRAG_THRESHOLD) return;
|
|
136
|
+
|
|
137
|
+
dragAxis = Math.abs(deltaX) >= Math.abs(deltaY) ? "x" : "y";
|
|
138
|
+
|
|
139
|
+
if (dragAxis === "y") {
|
|
140
|
+
stopDragListeners();
|
|
141
|
+
resetDragState();
|
|
142
|
+
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
isDragging.value = true;
|
|
147
|
+
document.body.style.cursor = "move";
|
|
148
|
+
document.body.style.userSelect = "none";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (dragAxis !== "x") return;
|
|
152
|
+
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
scrollContainerRef.value.scrollLeft = startScrollLeft - deltaX;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function onPointerUp() {
|
|
158
|
+
const wasDragging = isDragging.value;
|
|
159
|
+
|
|
160
|
+
stopDragListeners();
|
|
161
|
+
resetDragState();
|
|
162
|
+
|
|
163
|
+
if (wasDragging) {
|
|
164
|
+
suppressTabClick();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function onClickCapture(event: MouseEvent) {
|
|
169
|
+
if (!preventTabClick.value) return;
|
|
170
|
+
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
event.stopPropagation();
|
|
173
|
+
preventTabClick.value = false;
|
|
174
|
+
clearSuppressClickTimer();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getHorizontalWheelDelta(event: WheelEvent) {
|
|
178
|
+
if (Math.abs(event.deltaX) >= Math.abs(event.deltaY)) {
|
|
179
|
+
return event.deltaX;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return event.shiftKey ? event.deltaY : 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function onWheel(event: WheelEvent) {
|
|
186
|
+
if (!props.scrollable || !scrollContainerRef.value || !isScrollableOverflow()) return;
|
|
187
|
+
|
|
188
|
+
const deltaX = getHorizontalWheelDelta(event);
|
|
189
|
+
|
|
190
|
+
if (!deltaX) return;
|
|
191
|
+
|
|
192
|
+
event.preventDefault();
|
|
193
|
+
scrollContainerRef.value.scrollLeft += deltaX;
|
|
194
|
+
}
|
|
195
|
+
|
|
62
196
|
onMounted(() => {
|
|
63
197
|
if (scrollContainerRef.value) {
|
|
64
198
|
scrollContainerRef.value.addEventListener("scroll", checkScroll, { passive: true });
|
|
199
|
+
scrollContainerRef.value.addEventListener("wheel", onWheel, { passive: false });
|
|
65
200
|
|
|
66
201
|
checkScroll();
|
|
67
202
|
}
|
|
68
203
|
});
|
|
69
204
|
|
|
70
205
|
onUnmounted(() => {
|
|
206
|
+
stopDragListeners();
|
|
207
|
+
resetDragState();
|
|
208
|
+
clearSuppressClickTimer();
|
|
209
|
+
|
|
71
210
|
if (scrollContainerRef.value) {
|
|
72
211
|
scrollContainerRef.value.removeEventListener("scroll", checkScroll);
|
|
212
|
+
scrollContainerRef.value.removeEventListener("wheel", onWheel);
|
|
73
213
|
}
|
|
74
214
|
});
|
|
75
215
|
|
|
@@ -79,6 +219,7 @@ provide("getUTabsSquare", () => props.square);
|
|
|
79
219
|
provide("getUTabsScrollable", () => props.scrollable);
|
|
80
220
|
provide("getUTabsSelectedItem", () => selectedItem.value);
|
|
81
221
|
provide("setUTabsSelectedItem", (value: string) => (selectedItem.value = value));
|
|
222
|
+
provide("getUTabsPreventTabClick", () => preventTabClick.value);
|
|
82
223
|
|
|
83
224
|
defineExpose({
|
|
84
225
|
/**
|
|
@@ -97,6 +238,7 @@ const {
|
|
|
97
238
|
config,
|
|
98
239
|
wrapperAttrs,
|
|
99
240
|
tabsAttrs,
|
|
241
|
+
dragAttrs,
|
|
100
242
|
tabAttrs,
|
|
101
243
|
prevAttrs,
|
|
102
244
|
nextAttrs,
|
|
@@ -117,7 +259,15 @@ const {
|
|
|
117
259
|
</slot>
|
|
118
260
|
</div>
|
|
119
261
|
|
|
120
|
-
<div
|
|
262
|
+
<div
|
|
263
|
+
ref="scroll-container"
|
|
264
|
+
v-bind="tabsAttrs"
|
|
265
|
+
:class="isDragging && dragAttrs.class"
|
|
266
|
+
:data-test="getDataTest()"
|
|
267
|
+
@scroll="checkScroll"
|
|
268
|
+
@pointerdown="onPointerDown"
|
|
269
|
+
@click.capture="onClickCapture"
|
|
270
|
+
>
|
|
121
271
|
<!-- @slot Use it to add the UTab component. -->
|
|
122
272
|
<slot>
|
|
123
273
|
<UTab
|
|
@@ -4,10 +4,11 @@ export default /*tw*/ {
|
|
|
4
4
|
base: "flex border-b border-default w-full",
|
|
5
5
|
variants: {
|
|
6
6
|
scrollable: {
|
|
7
|
-
true: "overflow-hidden flex-nowrap
|
|
7
|
+
true: "overflow-hidden flex-nowrap touch-pan-y select-none",
|
|
8
8
|
},
|
|
9
9
|
},
|
|
10
10
|
},
|
|
11
|
+
drag: "icon-drag cursor-move scroll-auto! *:pointer-events-none",
|
|
11
12
|
tab: "{UTab}",
|
|
12
13
|
prev: "",
|
|
13
14
|
next: "",
|
|
@@ -89,6 +89,14 @@ Scrollable.args = {
|
|
|
89
89
|
options: getOptionsArray(),
|
|
90
90
|
scrollable: true,
|
|
91
91
|
};
|
|
92
|
+
Scrollable.parameters = {
|
|
93
|
+
docs: {
|
|
94
|
+
description: {
|
|
95
|
+
story:
|
|
96
|
+
"Scroll overflowing tabs with the arrow buttons, by dragging the tab list, or with horizontal wheel.",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
92
100
|
|
|
93
101
|
export const Block = DefaultTemplate.bind({});
|
|
94
102
|
Block.args = { block: true };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nextTick } from "vue";
|
|
1
2
|
import { mount } from "@vue/test-utils";
|
|
2
3
|
import { describe, it, expect } from "vitest";
|
|
3
4
|
|
|
@@ -7,6 +8,25 @@ import UButton from "../../ui.button/UButton.vue";
|
|
|
7
8
|
|
|
8
9
|
import type { Props, UTabsOption } from "../types";
|
|
9
10
|
|
|
11
|
+
function dispatchPointer(
|
|
12
|
+
target: EventTarget,
|
|
13
|
+
type: "pointerdown" | "pointermove" | "pointerup",
|
|
14
|
+
clientX: number,
|
|
15
|
+
extra: PointerEventInit = {},
|
|
16
|
+
) {
|
|
17
|
+
target.dispatchEvent(
|
|
18
|
+
new PointerEvent(type, {
|
|
19
|
+
bubbles: true,
|
|
20
|
+
cancelable: true,
|
|
21
|
+
clientX,
|
|
22
|
+
clientY: 0,
|
|
23
|
+
pointerId: 1,
|
|
24
|
+
pointerType: "mouse",
|
|
25
|
+
...extra,
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
describe("UTabs.vue", () => {
|
|
11
31
|
// Global options definition
|
|
12
32
|
const options: UTabsOption[] = [
|
|
@@ -94,7 +114,7 @@ describe("UTabs.vue", () => {
|
|
|
94
114
|
const tabsContainer = component.find(`[vl-key="tabs"]`);
|
|
95
115
|
|
|
96
116
|
// Check that the container has the scrollable class
|
|
97
|
-
expect(tabsContainer.classes()).toContain("
|
|
117
|
+
expect(tabsContainer.classes()).toContain("overflow-hidden");
|
|
98
118
|
});
|
|
99
119
|
|
|
100
120
|
it("Scroll – shows scroll buttons when scrollable and content overflows", async () => {
|
|
@@ -134,6 +154,198 @@ describe("UTabs.vue", () => {
|
|
|
134
154
|
expect(nextButton).toBeDefined();
|
|
135
155
|
});
|
|
136
156
|
|
|
157
|
+
it("Scrollable – scrolls the tab list on pointer drag", async () => {
|
|
158
|
+
const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({
|
|
159
|
+
value: `tab${i}`,
|
|
160
|
+
label: `Tab ${i}`,
|
|
161
|
+
}));
|
|
162
|
+
|
|
163
|
+
const component = mount(UTabs, {
|
|
164
|
+
props: {
|
|
165
|
+
options: manyOptions,
|
|
166
|
+
scrollable: true,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const tabsContainer = component.find(`[vl-key="tabs"]`);
|
|
171
|
+
const element = tabsContainer.element;
|
|
172
|
+
|
|
173
|
+
let scrollLeft = 0;
|
|
174
|
+
|
|
175
|
+
Object.defineProperty(element, "scrollWidth", { configurable: true, value: 1000 });
|
|
176
|
+
Object.defineProperty(element, "clientWidth", { configurable: true, value: 300 });
|
|
177
|
+
Object.defineProperty(element, "scrollLeft", {
|
|
178
|
+
configurable: true,
|
|
179
|
+
get: () => scrollLeft,
|
|
180
|
+
set: (value: number) => {
|
|
181
|
+
scrollLeft = value;
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
dispatchPointer(element, "pointerdown", 200, { button: 0 });
|
|
186
|
+
dispatchPointer(document, "pointermove", 120, { buttons: 1 });
|
|
187
|
+
|
|
188
|
+
await nextTick();
|
|
189
|
+
|
|
190
|
+
expect(scrollLeft).toBe(80);
|
|
191
|
+
expect(tabsContainer.classes()).toContain("cursor-move");
|
|
192
|
+
expect(tabsContainer.classes()).toContain("icon-drag");
|
|
193
|
+
|
|
194
|
+
dispatchPointer(document, "pointerup", 120);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("Scrollable – does not select a tab after a drag gesture", async () => {
|
|
198
|
+
const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({
|
|
199
|
+
value: `tab${i}`,
|
|
200
|
+
label: `Tab ${i}`,
|
|
201
|
+
}));
|
|
202
|
+
|
|
203
|
+
const component = mount(UTabs, {
|
|
204
|
+
props: {
|
|
205
|
+
options: manyOptions,
|
|
206
|
+
modelValue: "tab0",
|
|
207
|
+
scrollable: true,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const tabsContainer = component.find(`[vl-key="tabs"]`);
|
|
212
|
+
const element = tabsContainer.element;
|
|
213
|
+
|
|
214
|
+
Object.defineProperty(element, "scrollWidth", { configurable: true, value: 1000 });
|
|
215
|
+
Object.defineProperty(element, "clientWidth", { configurable: true, value: 300 });
|
|
216
|
+
Object.defineProperty(element, "scrollLeft", {
|
|
217
|
+
configurable: true,
|
|
218
|
+
writable: true,
|
|
219
|
+
value: 0,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
dispatchPointer(element, "pointerdown", 200, { button: 0 });
|
|
223
|
+
dispatchPointer(document, "pointermove", 120, { buttons: 1 });
|
|
224
|
+
dispatchPointer(document, "pointerup", 120);
|
|
225
|
+
|
|
226
|
+
await component.findAllComponents(UTab)[1].trigger("click");
|
|
227
|
+
|
|
228
|
+
expect(component.emitted("update:modelValue")).toBeFalsy();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("Scrollable – scrolls the tab list on horizontal wheel", () => {
|
|
232
|
+
const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({
|
|
233
|
+
value: `tab${i}`,
|
|
234
|
+
label: `Tab ${i}`,
|
|
235
|
+
}));
|
|
236
|
+
|
|
237
|
+
const component = mount(UTabs, {
|
|
238
|
+
props: {
|
|
239
|
+
options: manyOptions,
|
|
240
|
+
scrollable: true,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const element = component.find(`[vl-key="tabs"]`).element;
|
|
245
|
+
|
|
246
|
+
let scrollLeft = 0;
|
|
247
|
+
|
|
248
|
+
Object.defineProperty(element, "scrollWidth", { configurable: true, value: 1000 });
|
|
249
|
+
Object.defineProperty(element, "clientWidth", { configurable: true, value: 300 });
|
|
250
|
+
Object.defineProperty(element, "scrollLeft", {
|
|
251
|
+
configurable: true,
|
|
252
|
+
get: () => scrollLeft,
|
|
253
|
+
set: (value: number) => {
|
|
254
|
+
scrollLeft = value;
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
element.dispatchEvent(
|
|
259
|
+
new WheelEvent("wheel", {
|
|
260
|
+
deltaX: 40,
|
|
261
|
+
deltaY: 0,
|
|
262
|
+
bubbles: true,
|
|
263
|
+
cancelable: true,
|
|
264
|
+
}),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
expect(scrollLeft).toBe(40);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("Scrollable – does not scroll the tab list on vertical wheel", () => {
|
|
271
|
+
const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({
|
|
272
|
+
value: `tab${i}`,
|
|
273
|
+
label: `Tab ${i}`,
|
|
274
|
+
}));
|
|
275
|
+
|
|
276
|
+
const component = mount(UTabs, {
|
|
277
|
+
props: {
|
|
278
|
+
options: manyOptions,
|
|
279
|
+
scrollable: true,
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const element = component.find(`[vl-key="tabs"]`).element;
|
|
284
|
+
|
|
285
|
+
let scrollLeft = 0;
|
|
286
|
+
|
|
287
|
+
Object.defineProperty(element, "scrollWidth", { configurable: true, value: 1000 });
|
|
288
|
+
Object.defineProperty(element, "clientWidth", { configurable: true, value: 300 });
|
|
289
|
+
Object.defineProperty(element, "scrollLeft", {
|
|
290
|
+
configurable: true,
|
|
291
|
+
get: () => scrollLeft,
|
|
292
|
+
set: (value: number) => {
|
|
293
|
+
scrollLeft = value;
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
element.dispatchEvent(
|
|
298
|
+
new WheelEvent("wheel", {
|
|
299
|
+
deltaX: 0,
|
|
300
|
+
deltaY: 40,
|
|
301
|
+
bubbles: true,
|
|
302
|
+
cancelable: true,
|
|
303
|
+
}),
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
expect(scrollLeft).toBe(0);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("Scrollable – scrolls the tab list on shift+wheel", () => {
|
|
310
|
+
const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({
|
|
311
|
+
value: `tab${i}`,
|
|
312
|
+
label: `Tab ${i}`,
|
|
313
|
+
}));
|
|
314
|
+
|
|
315
|
+
const component = mount(UTabs, {
|
|
316
|
+
props: {
|
|
317
|
+
options: manyOptions,
|
|
318
|
+
scrollable: true,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const element = component.find(`[vl-key="tabs"]`).element;
|
|
323
|
+
|
|
324
|
+
let scrollLeft = 0;
|
|
325
|
+
|
|
326
|
+
Object.defineProperty(element, "scrollWidth", { configurable: true, value: 1000 });
|
|
327
|
+
Object.defineProperty(element, "clientWidth", { configurable: true, value: 300 });
|
|
328
|
+
Object.defineProperty(element, "scrollLeft", {
|
|
329
|
+
configurable: true,
|
|
330
|
+
get: () => scrollLeft,
|
|
331
|
+
set: (value: number) => {
|
|
332
|
+
scrollLeft = value;
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
element.dispatchEvent(
|
|
337
|
+
new WheelEvent("wheel", {
|
|
338
|
+
deltaX: 0,
|
|
339
|
+
deltaY: 40,
|
|
340
|
+
shiftKey: true,
|
|
341
|
+
bubbles: true,
|
|
342
|
+
cancelable: true,
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
expect(scrollLeft).toBe(40);
|
|
347
|
+
});
|
|
348
|
+
|
|
137
349
|
it("Block – provides block value to tabs", () => {
|
|
138
350
|
const block = true;
|
|
139
351
|
|