@window-splitter/web-component 1.0.1

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.
@@ -0,0 +1,299 @@
1
+ import "../.storybook/preview.css";
2
+
3
+ import { test, expect, describe, vi, afterEach } from "vitest";
4
+ import { getByText, waitFor, fireEvent } from "@testing-library/dom";
5
+ import { createTestUtils, dragHandle } from "@window-splitter/interface/test";
6
+ import * as Cookies from "tiny-cookie";
7
+
8
+ import * as stories from "./WebComponentWindowSplitter.stories.js";
9
+ import { Panel, PanelGroup, PanelResizer } from "./index.js";
10
+ import { render as litRender, TemplateResult } from "lit";
11
+
12
+ customElements.define("window-splitter", PanelGroup);
13
+ customElements.define("window-panel", Panel);
14
+ customElements.define("window-panel-resizer", PanelResizer);
15
+
16
+ async function getHandle(selector: string) {
17
+ await waitFor(() => {
18
+ const handle = document.querySelector(selector) as PanelGroup;
19
+ if (!handle) {
20
+ throw new Error("Handle not found");
21
+ }
22
+ return true;
23
+ });
24
+
25
+ return document.querySelector(selector) as PanelGroup;
26
+ }
27
+
28
+ const containers = new Set<HTMLElement>();
29
+
30
+ function cleanup(): void {
31
+ for (const container of containers) {
32
+ container.remove();
33
+ }
34
+ containers.clear();
35
+ }
36
+
37
+ function render(template: TemplateResult) {
38
+ const container = document.body.appendChild(document.createElement("div"));
39
+ litRender(template, container);
40
+ containers.add(container);
41
+ }
42
+
43
+ const { waitForMeasurement, expectTemplate, waitForCondition } =
44
+ createTestUtils({
45
+ waitFor,
46
+ });
47
+
48
+ afterEach(cleanup);
49
+
50
+ test("horizontal layout", async () => {
51
+ render(stories.Simple.render());
52
+
53
+ const handle = await getHandle("window-splitter");
54
+
55
+ await waitForMeasurement(handle!);
56
+
57
+ expect(getByText(document.body, "Panel 1")).toBeInTheDocument();
58
+ expect(getByText(document.body, "Panel 2")).toBeInTheDocument();
59
+
60
+ await expectTemplate(handle!, "244px 10px 244px");
61
+
62
+ // Should respect the min
63
+ await dragHandle({ delta: 300 });
64
+ await expectTemplate(handle!, "388px 10px 100px");
65
+ });
66
+
67
+ test("vertical layout", async () => {
68
+ render(stories.VerticalLayout.render());
69
+
70
+ const handle = await getHandle("window-splitter");
71
+
72
+ await waitForMeasurement(handle!);
73
+
74
+ expect(getByText(document.body, "top")).toBeInTheDocument();
75
+ expect(getByText(document.body, "middle")).toBeInTheDocument();
76
+ expect(getByText(document.body, "bottom")).toBeInTheDocument();
77
+
78
+ await expectTemplate(handle!, "96px 10px 108px 10px 96px");
79
+
80
+ // Should respect the min
81
+ await dragHandle({ delta: 100, orientation: "vertical" });
82
+ await expectTemplate(handle!, "172px 10px 64px 10px 64px");
83
+ });
84
+
85
+ test("Conditional Panels", async () => {
86
+ render(stories.ConditionalPanel.render());
87
+ const handle = await getHandle("window-splitter");
88
+
89
+ await waitForMeasurement(handle!);
90
+
91
+ await expectTemplate(handle!, "244px 10px 244px");
92
+
93
+ getByText(document.body, "Expand").click();
94
+ await expectTemplate(handle!, "244px 10px 134px 10px 100px");
95
+
96
+ getByText(document.body, "Close").click();
97
+ await expectTemplate(handle!, "244px 10px 244px");
98
+ });
99
+
100
+ test("Dynamic constraints", async () => {
101
+ render(stories.DynamicConstraints.render());
102
+
103
+ const handle = await getHandle("window-splitter");
104
+
105
+ await waitForMeasurement(handle!);
106
+ await expectTemplate(handle!, "100px 10px 178px 10px 700px");
107
+
108
+ getByText(document.body, "Toggle Custom").click();
109
+ await expectTemplate(handle!, "500px 10px 178px 10px 300px");
110
+
111
+ getByText(document.body, "Toggle Custom").click();
112
+ await expectTemplate(handle!, "400px 10px 178px 10px 400px");
113
+ });
114
+
115
+ describe("Autosave", () => {
116
+ test("localStorage", async () => {
117
+ localStorage.clear();
118
+
119
+ render(stories.Autosave.render());
120
+ const handle = await getHandle("window-splitter");
121
+
122
+ await waitForMeasurement(handle!);
123
+ await expectTemplate(handle!, "244px 10px 244px");
124
+
125
+ await dragHandle({ delta: 100 });
126
+ await expectTemplate(handle!, "342px 10px 146px");
127
+
128
+ await waitForCondition(() =>
129
+ Boolean(localStorage.getItem("autosave-example-web-component"))
130
+ );
131
+ const obj = JSON.parse(
132
+ localStorage.getItem("autosave-example-web-component") || "{}"
133
+ );
134
+ expect(obj.items).toMatchSnapshot();
135
+ });
136
+
137
+ test("callback", async () => {
138
+ localStorage.clear();
139
+
140
+ const spy = vi.fn();
141
+
142
+ render(stories.AutosaveCollapsible.render({ onCollapseChange: spy }));
143
+ const handle = await getHandle("window-splitter");
144
+
145
+ await waitForMeasurement(handle!);
146
+
147
+ await dragHandle({ delta: -200, handleId: "resizer-1" });
148
+ await expectTemplate(handle!, "100px 10px 388px");
149
+ expect(spy).toHaveBeenCalledWith(true);
150
+
151
+ cleanup();
152
+
153
+ const spy2 = vi.fn();
154
+ render(stories.AutosaveCollapsible.render({ onCollapseChange: spy2 }));
155
+
156
+ await expectTemplate(handle!, "100px 10px 388px");
157
+ await dragHandle({ delta: 200 });
158
+ expect(spy2).toHaveBeenCalledWith(false);
159
+ });
160
+
161
+ test("cookie", async () => {
162
+ // clear cookies
163
+ document.cookie = document.cookie.replace(
164
+ new RegExp(`autosave-cookie-web-component=.*`),
165
+ ""
166
+ );
167
+
168
+ render(stories.AutosaveCookie.render());
169
+ let handle = await getHandle("window-splitter");
170
+
171
+ await waitForMeasurement(handle!);
172
+ await expectTemplate(handle!, "244px 10px 244px");
173
+
174
+ await dragHandle({ delta: 100 });
175
+ await expectTemplate(handle!, "342px 10px 146px");
176
+
177
+ await waitForCondition(() => {
178
+ return document.cookie.includes("autosave-cookie-web-component");
179
+ });
180
+
181
+ expect(document.cookie).toMatchSnapshot();
182
+
183
+ const snapshot = Cookies.get("autosave-cookie-web-component");
184
+
185
+ render(stories.AutosaveCookie.render({ snapshot: snapshot || undefined }));
186
+ handle = await getHandle("window-splitter");
187
+
188
+ await expectTemplate(handle!, "342px 10px 146px");
189
+ });
190
+ });
191
+
192
+ test("Keyboard interactions with collapsed panels", async () => {
193
+ render(stories.Collapsible.render());
194
+
195
+ const handle = await getHandle("window-splitter");
196
+
197
+ await waitForMeasurement(handle!);
198
+ await expectTemplate(handle!, "209px 10px 209px 10px 60px");
199
+
200
+ const resizer2 = document.getElementById("resizer-2")!;
201
+ fireEvent.keyDown(resizer2, { key: "Enter" });
202
+ await expectTemplate(handle!, "209px 10px 169px 10px 100px");
203
+
204
+ fireEvent.keyDown(resizer2, { key: "ArrowLeft" });
205
+ fireEvent.keyDown(resizer2, { key: "ArrowLeft" });
206
+ fireEvent.keyDown(resizer2, { key: "ArrowLeft" });
207
+ fireEvent.keyDown(resizer2, { key: "ArrowLeft" });
208
+ await expectTemplate(handle!, "209px 10px 165px 10px 104px");
209
+
210
+ fireEvent.keyDown(resizer2, { key: "ArrowLeft", shiftKey: true });
211
+ await expectTemplate(
212
+ handle!,
213
+ "209.03125px 10px 149.984375px 10px 118.984375px"
214
+ );
215
+
216
+ fireEvent.keyDown(resizer2, { key: "Enter" });
217
+ await expectTemplate(handle!, "209.03125px 10px 208.96875px 10px 60px");
218
+
219
+ fireEvent.keyDown(resizer2, { key: "Enter" });
220
+ await expectTemplate(handle!, "209.0625px 10px 149.96875px 10px 118.96875px");
221
+ });
222
+
223
+ describe("imperative panel API", async () => {
224
+ test("panel group", async () => {
225
+ render(stories.Collapsible.render());
226
+
227
+ const handle = await getHandle("window-splitter");
228
+
229
+ await waitForMeasurement(handle!);
230
+
231
+ expect(handle!.getPercentageSizes()).toMatchInlineSnapshot(`
232
+ [
233
+ 0.5,
234
+ 0.020080321285140562,
235
+ 0.5,
236
+ 0.020080321285140562,
237
+ 0.12048192771084337,
238
+ ]
239
+ `);
240
+
241
+ expect(handle!.getPixelSizes()).toMatchInlineSnapshot(`
242
+ [
243
+ 209,
244
+ 10,
245
+ 209,
246
+ 10,
247
+ 60,
248
+ ]
249
+ `);
250
+
251
+ await expectTemplate(handle!, "209px 10px 209px 10px 60px");
252
+ });
253
+
254
+ test("panel", async () => {
255
+ render(stories.Collapsible.render());
256
+
257
+ const handle = await getHandle("window-splitter");
258
+ const rightHandle = handle!.querySelector("#panel-3") as Panel;
259
+ const leftHandle = handle!.querySelector("#panel-1") as Panel;
260
+
261
+ await waitForMeasurement(handle!);
262
+
263
+ expect(rightHandle.isCollapsed()).toBe(true);
264
+ expect(rightHandle.isExpanded()).toBe(false);
265
+
266
+ rightHandle.expand();
267
+ await new Promise((resolve) => setTimeout(resolve, 2000));
268
+
269
+ expect(rightHandle.isCollapsed()).toBe(false);
270
+ expect(rightHandle.isExpanded()).toBe(true);
271
+
272
+ rightHandle.collapse();
273
+ await new Promise((resolve) => setTimeout(resolve, 2000));
274
+
275
+ expect(rightHandle.isCollapsed()).toBe(true);
276
+ expect(rightHandle.isExpanded()).toBe(false);
277
+
278
+ // Test the non controlled version
279
+
280
+ expect(leftHandle.isCollapsed()).toBe(false);
281
+ expect(leftHandle.isExpanded()).toBe(true);
282
+
283
+ leftHandle.collapse();
284
+ await new Promise((resolve) => setTimeout(resolve, 2000));
285
+
286
+ expect(leftHandle.isCollapsed()).toBe(true);
287
+ expect(leftHandle.isExpanded()).toBe(false);
288
+ expect(rightHandle.getPercentageSize()).toBe(
289
+ leftHandle.getPercentageSize()
290
+ );
291
+ expect(rightHandle.getPixelSize()).toBe(leftHandle.getPixelSize());
292
+
293
+ leftHandle.expand();
294
+ await new Promise((resolve) => setTimeout(resolve, 5000));
295
+
296
+ expect(leftHandle.isCollapsed()).toBe(false);
297
+ expect(leftHandle.isExpanded()).toBe(true);
298
+ });
299
+ });
@@ -0,0 +1,54 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Autosave > cookie 1`] = `"autosave-cookie-web-component=%7B%22size%22%3A%7B%22x%22%3A0%2C%22y%22%3A0%2C%22width%22%3A498%2C%22height%22%3A198%2C%22top%22%3A0%2C%22right%22%3A498%2C%22bottom%22%3A198%2C%22left%22%3A0%7D%2C%22items%22%3A%5B%7B%22type%22%3A%22panel%22%2C%22currentValue%22%3A%7B%22type%22%3A%22percent%22%2C%22value%22%3A%220.70081967213114754098%22%7D%2C%22min%22%3A%7B%22type%22%3A%22pixel%22%2C%22value%22%3A%220%22%7D%2C%22max%22%3A%221fr%22%2C%22collapsedSize%22%3A%7B%22type%22%3A%22pixel%22%2C%22value%22%3A%220%22%7D%2C%22onResize%22%3A%7B%7D%2C%22collapseIsControlled%22%3Afalse%2C%22id%22%3A%221%22%7D%2C%7B%22type%22%3A%22handle%22%2C%22id%22%3A%22resizer-1%22%2C%22size%22%3A%7B%22type%22%3A%22pixel%22%2C%22value%22%3A%2210%22%7D%7D%2C%7B%22type%22%3A%22panel%22%2C%22currentValue%22%3A%7B%22type%22%3A%22percent%22%2C%22value%22%3A%220.29918032786885245902%22%7D%2C%22min%22%3A%7B%22type%22%3A%22pixel%22%2C%22value%22%3A%220%22%7D%2C%22max%22%3A%221fr%22%2C%22collapsedSize%22%3A%7B%22type%22%3A%22pixel%22%2C%22value%22%3A%220%22%7D%2C%22onResize%22%3A%7B%7D%2C%22collapseIsControlled%22%3Afalse%2C%22id%22%3A%222%22%7D%5D%2C%22orientation%22%3A%22horizontal%22%2C%22dragOvershoot%22%3A%220%22%2C%22groupId%22%3A%22autosave-cookie-web-component%22%2C%22autosaveStrategy%22%3A%22cookie%22%7D"`;
4
+
5
+ exports[`Autosave > localStorage 1`] = `
6
+ [
7
+ {
8
+ "collapseIsControlled": false,
9
+ "collapsedSize": {
10
+ "type": "pixel",
11
+ "value": "0",
12
+ },
13
+ "currentValue": {
14
+ "type": "percent",
15
+ "value": "0.70081967213114754098",
16
+ },
17
+ "id": "1",
18
+ "max": "1fr",
19
+ "min": {
20
+ "type": "pixel",
21
+ "value": "0",
22
+ },
23
+ "onResize": {},
24
+ "type": "panel",
25
+ },
26
+ {
27
+ "id": "15",
28
+ "size": {
29
+ "type": "pixel",
30
+ "value": "10",
31
+ },
32
+ "type": "handle",
33
+ },
34
+ {
35
+ "collapseIsControlled": false,
36
+ "collapsedSize": {
37
+ "type": "pixel",
38
+ "value": "0",
39
+ },
40
+ "currentValue": {
41
+ "type": "percent",
42
+ "value": "0.29918032786885245902",
43
+ },
44
+ "id": "2",
45
+ "max": "1fr",
46
+ "min": {
47
+ "type": "pixel",
48
+ "value": "0",
49
+ },
50
+ "onResize": {},
51
+ "type": "panel",
52
+ },
53
+ ]
54
+ `;