@hyperframes/studio 0.8.52 → 0.8.53
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/dist/assets/{hyperframes-player-I5YRzB02.js → hyperframes-player-DKWM-Ons.js} +1 -1
- package/dist/assets/index-DrHQAuGO.css +1 -0
- package/dist/assets/index-HfP3sap2.js +390 -0
- package/dist/assets/{index-BTAfgesM.js → index-UvW6BEwf.js} +1 -1
- package/dist/assets/{index-Du5x7KUR.js → index-Y4OnpTEM.js} +1 -1
- package/dist/index.css +29 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.html +2 -2
- package/dist/index.js +7664 -7953
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -0
- package/package.json +8 -7
- package/src/App.tsx +42 -48
- package/src/components/EditorShell.selectionSync.test.tsx +9 -4
- package/src/components/EditorShell.tsx +52 -56
- package/src/components/StudioHeader.dom.test.tsx +1 -1
- package/src/components/StudioHeader.test.ts +0 -12
- package/src/components/StudioHeader.tsx +10 -13
- package/src/components/StudioLeftPanels.tsx +202 -0
- package/src/components/{StudioRightPanel.tsx → StudioRightPanels.tsx} +57 -206
- package/src/components/{StudioRightPanel.types.ts → StudioRightPanels.types.ts} +4 -3
- package/src/components/dock/Dock.test.tsx +16 -2
- package/src/components/dock/Dock.tsx +13 -3
- package/src/components/dock/dockLayoutStore.test.ts +28 -0
- package/src/components/dock/dockLayoutStore.ts +21 -4
- package/src/components/editor/SnapToolbar.test.tsx +0 -3
- package/src/components/nle/NLEContext.tsx +0 -43
- package/src/components/nle/TimelinePane.tsx +46 -67
- package/src/components/panels/SlideshowPanel.tsx +1 -1
- package/src/components/panels/VariablesPanel.tsx +1 -1
- package/src/components/renders/RenderQueue.test.tsx +133 -25
- package/src/components/renders/RenderQueue.tsx +99 -92
- package/src/components/renders/RenderQueueItem.tsx +69 -64
- package/src/components/renders/RenderQueuePanel.tsx +1 -1
- package/src/components/sidebar/{LeftSidebar.masterComposition.test.tsx → CompositionsPanel.masterComposition.test.tsx} +6 -9
- package/src/components/sidebar/CompositionsPanel.tsx +15 -0
- package/src/components/sidebar/{LeftSidebar.lintBadge.test.tsx → SidebarLintButton.test.tsx} +6 -11
- package/src/components/sidebar/SidebarLintButton.tsx +50 -0
- package/src/components/ui/Select.tsx +4 -0
- package/src/contexts/PanelLayoutContext.tsx +3 -57
- package/src/headerExports.test.tsx +23 -0
- package/src/hooks/appHotkeysDispatch.ts +3 -4
- package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +0 -2
- package/src/hooks/useAppHotkeys.test.ts +0 -1
- package/src/hooks/useAppHotkeys.textEditing.test.tsx +0 -3
- package/src/hooks/useAppHotkeys.ts +0 -4
- package/src/hooks/useDomEditPreviewSync.ts +4 -6
- package/src/hooks/useDomEditSession.ts +0 -7
- package/src/hooks/useDomEditWiring.ts +4 -9
- package/src/hooks/usePanelLayout.test.ts +108 -296
- package/src/hooks/usePanelLayout.ts +50 -255
- package/src/hooks/useRightPanelIntents.test.ts +181 -0
- package/src/hooks/useRightPanelIntents.ts +64 -0
- package/src/hooks/useSlideshowTabState.test.ts +9 -31
- package/src/hooks/useSlideshowTabState.ts +6 -19
- package/src/hooks/useStudioContextValue.test.ts +6 -18
- package/src/hooks/useStudioContextValue.ts +5 -26
- package/src/index.ts +2 -1
- package/src/main.tsx +26 -0
- package/src/player/components/Timeline.tsx +1 -1
- package/src/player/lib/timelinePerformanceDiagnostics.test.ts +3 -2
- package/src/player/lib/timelinePerformanceDiagnostics.ts +1 -1
- package/src/utils/studioHelpers.ts +0 -7
- package/src/utils/studioUiPreferences.test.ts +10 -16
- package/src/utils/studioUiPreferences.ts +0 -16
- package/dist/assets/index-CxAu_Okg.css +0 -1
- package/dist/assets/index-DocJlW2v.js +0 -376
- package/src/components/StudioLeftSidebar.tsx +0 -207
- package/src/components/nle/TimelineResizeDivider.tsx +0 -101
- package/src/components/sidebar/LeftSidebar.storage.test.ts +0 -28
- package/src/components/sidebar/LeftSidebar.tsx +0 -348
- package/src/hooks/useInspectorSplitResize.ts +0 -51
- package/src/utils/fitPanels.test.ts +0 -164
- package/src/utils/fitPanels.ts +0 -151
|
@@ -3,338 +3,150 @@
|
|
|
3
3
|
import React, { act } from "react";
|
|
4
4
|
import { createRoot } from "react-dom/client";
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
-
import {
|
|
6
|
+
import { useDockLayoutStore, type DockController } from "../components/dock/dockLayoutStore";
|
|
7
|
+
import { PANEL_IDS, type PanelId } from "../components/dock/panelRegistry";
|
|
7
8
|
import { trackStudioEvent } from "../utils/studioTelemetry";
|
|
8
|
-
import { usePanelLayout } from "./usePanelLayout";
|
|
9
|
+
import { usePanelLayout, type InitialPanelLayoutState } from "./usePanelLayout";
|
|
9
10
|
|
|
10
11
|
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
11
12
|
|
|
12
13
|
vi.mock("../utils/studioTelemetry", () => ({ trackStudioEvent: vi.fn() }));
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
getItem: (key: string) => entries.get(key) ?? null,
|
|
25
|
-
key: (index: number) => Array.from(entries.keys())[index] ?? null,
|
|
26
|
-
removeItem: (key: string) => entries.delete(key),
|
|
27
|
-
setItem: (key: string, value: string) => entries.set(key, value),
|
|
28
|
-
} satisfies Storage,
|
|
29
|
-
});
|
|
30
|
-
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1496 });
|
|
31
|
-
});
|
|
15
|
+
function fakeController(): DockController {
|
|
16
|
+
return {
|
|
17
|
+
open: vi.fn(),
|
|
18
|
+
activate: vi.fn(),
|
|
19
|
+
close: vi.fn(),
|
|
20
|
+
setTitle: vi.fn(),
|
|
21
|
+
setGroupVisible: vi.fn(),
|
|
22
|
+
reset: vi.fn(),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
function showDock(visible: PanelId[], controller = fakeController()) {
|
|
27
|
+
useDockLayoutStore.setState({
|
|
28
|
+
controller,
|
|
29
|
+
openPanels: new Set(PANEL_IDS),
|
|
30
|
+
visiblePanels: new Set(visible),
|
|
31
|
+
lastActive: {},
|
|
32
|
+
activePanel: null,
|
|
33
|
+
});
|
|
34
|
+
return controller;
|
|
35
|
+
}
|
|
38
36
|
|
|
39
|
-
function
|
|
37
|
+
function renderLayout(initial?: InitialPanelLayoutState) {
|
|
40
38
|
const host = document.createElement("div");
|
|
41
39
|
document.body.append(host);
|
|
42
40
|
const root = createRoot(host);
|
|
43
41
|
let current: ReturnType<typeof usePanelLayout> | null = null;
|
|
44
|
-
|
|
45
42
|
function Harness() {
|
|
46
|
-
current =
|
|
43
|
+
current = usePanelLayout(initial);
|
|
47
44
|
return null;
|
|
48
45
|
}
|
|
49
|
-
|
|
50
|
-
act(() => {
|
|
51
|
-
root.render(React.createElement(Harness));
|
|
52
|
-
});
|
|
53
|
-
|
|
46
|
+
act(() => root.render(React.createElement(Harness)));
|
|
54
47
|
return {
|
|
55
|
-
|
|
56
|
-
if (!current) throw new Error("
|
|
48
|
+
get layout() {
|
|
49
|
+
if (!current) throw new Error("layout not rendered");
|
|
57
50
|
return current;
|
|
58
51
|
},
|
|
59
52
|
unmount: () => act(() => root.unmount()),
|
|
60
53
|
};
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
|
|
69
|
-
window.dispatchEvent(new Event("resize"));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe("usePanelLayout — right inspector panes", () => {
|
|
73
|
-
it("opens Design with the intended viewport-scaled panel widths", () => {
|
|
74
|
-
const harness = renderPanelLayout();
|
|
75
|
-
|
|
76
|
-
expect(harness.getState()).toMatchObject({
|
|
77
|
-
leftWidth: 384,
|
|
78
|
-
rightWidth: 424,
|
|
79
|
-
rightCollapsed: false,
|
|
80
|
-
rightPanelTab: "design",
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
harness.unmount();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("persists the latest pointer width even before React rerenders", () => {
|
|
87
|
-
const harness = renderPanelLayout();
|
|
88
|
-
const state = harness.getState();
|
|
89
|
-
const target = { setPointerCapture: vi.fn() };
|
|
90
|
-
|
|
91
|
-
act(() => {
|
|
92
|
-
state.handlePanelResizeStart("left", {
|
|
93
|
-
preventDefault: vi.fn(),
|
|
94
|
-
target,
|
|
95
|
-
pointerId: 1,
|
|
96
|
-
clientX: 100,
|
|
97
|
-
} as unknown as React.PointerEvent);
|
|
98
|
-
state.handlePanelResizeMove({ clientX: 140 } as React.PointerEvent);
|
|
99
|
-
state.handlePanelResizeEnd();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
expect(harness.getState().leftWidth).toBe(424);
|
|
103
|
-
expect(readStudioUiPreferences().leftWidth).toBe(424);
|
|
104
|
-
harness.unmount();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("accumulates and persists rapid keyboard resize steps", () => {
|
|
108
|
-
const harness = renderPanelLayout();
|
|
109
|
-
const state = harness.getState();
|
|
110
|
-
|
|
111
|
-
act(() => {
|
|
112
|
-
state.adjustPanelWidth("right", 16);
|
|
113
|
-
state.adjustPanelWidth("right", 16);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
expect(harness.getState().rightWidth).toBe(456);
|
|
117
|
-
expect(readStudioUiPreferences().rightWidth).toBe(456);
|
|
118
|
-
harness.unmount();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("toggleRightInspectorPane independently flips one pane, allowing both open at once", () => {
|
|
122
|
-
const harness = renderPanelLayout();
|
|
123
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true });
|
|
124
|
-
|
|
125
|
-
act(() => harness.getState().toggleRightInspectorPane("layers"));
|
|
126
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: true });
|
|
127
|
-
|
|
128
|
-
harness.unmount();
|
|
129
|
-
});
|
|
56
|
+
beforeEach(() => vi.mocked(trackStudioEvent).mockClear());
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
document.body.innerHTML = "";
|
|
59
|
+
useDockLayoutStore.setState({ controller: null });
|
|
60
|
+
});
|
|
130
61
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
expect(
|
|
136
|
-
|
|
62
|
+
describe("usePanelLayout over the dock", () => {
|
|
63
|
+
it("reads the tab of the right-zone panel that is showing", () => {
|
|
64
|
+
showDock(["preview", "timeline", "compositions", "renders"]);
|
|
65
|
+
const view = renderLayout();
|
|
66
|
+
expect(view.layout.rightPanelTab).toBe("renders");
|
|
67
|
+
expect(view.layout.rightCollapsed).toBe(false);
|
|
137
68
|
});
|
|
138
69
|
|
|
139
|
-
it("
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
expect(
|
|
143
|
-
|
|
144
|
-
act(() => harness.getState().setExclusiveRightInspectorPane("layers"));
|
|
145
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: false });
|
|
146
|
-
|
|
147
|
-
act(() => harness.getState().setExclusiveRightInspectorPane("design"));
|
|
148
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true });
|
|
149
|
-
|
|
150
|
-
harness.unmount();
|
|
70
|
+
it("is collapsed when no right-zone panel is showing", () => {
|
|
71
|
+
showDock(["preview", "timeline", "compositions"]);
|
|
72
|
+
const view = renderLayout();
|
|
73
|
+
expect(view.layout.rightCollapsed).toBe(true);
|
|
74
|
+
expect(view.layout.rightPanelTab).toBe("design");
|
|
151
75
|
});
|
|
152
76
|
|
|
153
|
-
it("
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
77
|
+
it("opens the Design panel for the legacy block-params tab and reports the tab switch", () => {
|
|
78
|
+
const controller = showDock(["preview"]);
|
|
79
|
+
const view = renderLayout();
|
|
80
|
+
act(() => view.layout.setRightPanelTab("block-params"));
|
|
81
|
+
expect(controller.setGroupVisible).toHaveBeenCalledWith("design", true);
|
|
82
|
+
expect(controller.activate).toHaveBeenCalledWith("design");
|
|
83
|
+
expect(trackStudioEvent).toHaveBeenCalledWith("tab_switch", {
|
|
84
|
+
panel: "right_panel",
|
|
85
|
+
tab: "block-params",
|
|
160
86
|
});
|
|
161
|
-
const { usePanelLayout: usePanelLayoutFlatOff } = await import("./usePanelLayout");
|
|
162
|
-
const harness = renderPanelLayoutWith(usePanelLayoutFlatOff);
|
|
163
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true });
|
|
164
|
-
|
|
165
|
-
act(() => harness.getState().setRightPanelTab("layers"));
|
|
166
|
-
// Legacy (split-view) behavior: additive, both panes end up open.
|
|
167
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: true });
|
|
168
|
-
|
|
169
|
-
harness.unmount();
|
|
170
87
|
});
|
|
171
88
|
|
|
172
|
-
it("
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
harness.getState().setRightPanelTab("design");
|
|
178
|
-
});
|
|
89
|
+
it("reports a tab switch only when the tab actually changes", () => {
|
|
90
|
+
showDock(["preview", "design"]);
|
|
91
|
+
const view = renderLayout();
|
|
92
|
+
vi.mocked(trackStudioEvent).mockClear();
|
|
93
|
+
act(() => view.layout.setRightPanelTab("design"));
|
|
179
94
|
expect(trackStudioEvent).not.toHaveBeenCalled();
|
|
180
|
-
|
|
181
|
-
act(() => {
|
|
182
|
-
harness.getState().setRightPanelTab("layers");
|
|
183
|
-
harness.getState().setRightPanelTab("layers");
|
|
184
|
-
});
|
|
185
|
-
expect(trackStudioEvent).toHaveBeenCalledOnce();
|
|
95
|
+
act(() => view.layout.setRightPanelTab("layers"));
|
|
186
96
|
expect(trackStudioEvent).toHaveBeenCalledWith("tab_switch", {
|
|
187
97
|
panel: "right_panel",
|
|
188
98
|
tab: "layers",
|
|
189
99
|
});
|
|
190
|
-
expect(harness.getState().rightPanelTab).toBe("layers");
|
|
191
|
-
|
|
192
|
-
harness.unmount();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("caps a panel relative to the window instead of at a flat 600px", () => {
|
|
196
|
-
resizeWindowTo(700);
|
|
197
|
-
const harness = renderPanelLayout();
|
|
198
|
-
// The old flat cap let the inspector claim 600 of a 700px window.
|
|
199
|
-
expect(harness.getState().rightWidth).toBeLessThanOrEqual(280);
|
|
200
|
-
harness.unmount();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("rails both panels once the window cannot fit them", () => {
|
|
204
|
-
resizeWindowTo(560);
|
|
205
|
-
const harness = renderPanelLayout();
|
|
206
|
-
expect(harness.getState()).toMatchObject({
|
|
207
|
-
effectiveLeftCollapsed: true,
|
|
208
|
-
effectiveRightCollapsed: true,
|
|
209
|
-
leftCollapsed: false,
|
|
210
|
-
rightCollapsed: false,
|
|
211
|
-
});
|
|
212
|
-
harness.unmount();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("auto-collapse never writes the user's persisted or URL-synced intent", () => {
|
|
216
|
-
const harness = renderPanelLayout();
|
|
217
|
-
act(() => resizeWindowTo(560));
|
|
218
|
-
|
|
219
|
-
expect(harness.getState().effectiveLeftCollapsed).toBe(true);
|
|
220
|
-
// localStorage carries leftCollapsed; the shareable URL carries rightCollapsed.
|
|
221
|
-
// A ten-second window drag must rewrite neither.
|
|
222
|
-
expect(readStudioUiPreferences().leftCollapsed).toBeUndefined();
|
|
223
|
-
expect(harness.getState().leftCollapsed).toBe(false);
|
|
224
|
-
expect(harness.getState().rightCollapsed).toBe(false);
|
|
225
|
-
harness.unmount();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("returns the user's own width when the window grows back", () => {
|
|
229
|
-
const harness = renderPanelLayout();
|
|
230
|
-
const wide = harness.getState().leftWidth;
|
|
231
|
-
|
|
232
|
-
act(() => resizeWindowTo(560));
|
|
233
|
-
expect(harness.getState().leftWidth).toBeLessThan(wide);
|
|
234
|
-
|
|
235
|
-
act(() => resizeWindowTo(1496));
|
|
236
|
-
expect(harness.getState().leftWidth).toBe(wide);
|
|
237
|
-
harness.unmount();
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("keeps an explicitly collapsed sidebar collapsed after a narrow trip", () => {
|
|
241
|
-
const harness = renderPanelLayout();
|
|
242
|
-
act(() => harness.getState().toggleLeftSidebar());
|
|
243
|
-
expect(readStudioUiPreferences().leftCollapsed).toBe(true);
|
|
244
|
-
|
|
245
|
-
act(() => resizeWindowTo(560));
|
|
246
|
-
act(() => resizeWindowTo(1496));
|
|
247
|
-
|
|
248
|
-
expect(harness.getState().effectiveLeftCollapsed).toBe(true);
|
|
249
|
-
harness.unmount();
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("lets the user reopen a panel the window auto-collapsed", () => {
|
|
253
|
-
const harness = renderPanelLayout();
|
|
254
|
-
act(() => resizeWindowTo(560));
|
|
255
|
-
expect(harness.getState().effectiveRightCollapsed).toBe(true);
|
|
256
|
-
|
|
257
|
-
// Without this the header Inspector button would be dead below 700px.
|
|
258
|
-
act(() => harness.getState().setRightCollapsed(false));
|
|
259
|
-
expect(harness.getState().effectiveRightCollapsed).toBe(false);
|
|
260
|
-
harness.unmount();
|
|
261
100
|
});
|
|
262
101
|
|
|
263
|
-
it("
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
expect(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
act(() =>
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
act(() =>
|
|
297
|
-
expect(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
>("../components/editor/manualEditingAvailability");
|
|
313
|
-
return { ...actual, STUDIO_FLAT_INSPECTOR_ENABLED: true };
|
|
314
|
-
});
|
|
315
|
-
const { usePanelLayout: usePanelLayoutFlatOn } = await import("./usePanelLayout");
|
|
316
|
-
const harness = renderPanelLayoutWith(usePanelLayoutFlatOn);
|
|
317
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true });
|
|
318
|
-
|
|
319
|
-
// The flat inspector can change its visible pane without changing the
|
|
320
|
-
// umbrella rightPanelTab value. A later element selection must still bring
|
|
321
|
-
// Design back even though rightPanelTab already says "design".
|
|
322
|
-
act(() => harness.getState().setExclusiveRightInspectorPane("layers"));
|
|
323
|
-
expect(harness.getState()).toMatchObject({
|
|
324
|
-
rightPanelTab: "design",
|
|
325
|
-
rightInspectorPanes: { layers: true, design: false },
|
|
326
|
-
});
|
|
327
|
-
act(() => harness.getState().setRightPanelTab("design"));
|
|
328
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true });
|
|
329
|
-
|
|
330
|
-
// Element-select / block-params-close / header Inspector-button callers
|
|
331
|
-
// all reach setRightPanelTab directly, not through the in-panel tab
|
|
332
|
-
// click's own setExclusiveRightInspectorPane call — this must still
|
|
333
|
-
// enforce exclusivity under the flat flag, or both tabs end up
|
|
334
|
-
// highlighted while only one pane actually renders.
|
|
335
|
-
act(() => harness.getState().setRightPanelTab("layers"));
|
|
336
|
-
expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: false });
|
|
337
|
-
|
|
338
|
-
harness.unmount();
|
|
102
|
+
it("collapses every open right-zone panel group and leaves the other zones alone", () => {
|
|
103
|
+
const controller = showDock(["preview", "design"]);
|
|
104
|
+
const view = renderLayout();
|
|
105
|
+
act(() => view.layout.setRightCollapsed(true));
|
|
106
|
+
const hidden = vi.mocked(controller.setGroupVisible).mock.calls;
|
|
107
|
+
expect(hidden.map(([id]) => id).sort()).toEqual(
|
|
108
|
+
["design", "layers", "renders", "slideshow", "variables"].sort(),
|
|
109
|
+
);
|
|
110
|
+
expect(hidden.every(([, visible]) => visible === false)).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("expanding an already-showing right column does nothing to the tab", () => {
|
|
114
|
+
const controller = showDock(["design"]);
|
|
115
|
+
const view = renderLayout();
|
|
116
|
+
act(() => view.layout.setRightCollapsed(false));
|
|
117
|
+
expect(controller.activate).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("expanding a fully hidden right column brings Design back", () => {
|
|
121
|
+
const controller = showDock(["preview"]);
|
|
122
|
+
const view = renderLayout();
|
|
123
|
+
act(() => view.layout.setRightCollapsed(false));
|
|
124
|
+
expect(controller.activate).toHaveBeenCalledWith("design");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("applies the URL's tab and collapse state once, when the dock attaches", () => {
|
|
128
|
+
useDockLayoutStore.setState({ controller: null, openPanels: new Set(PANEL_IDS) });
|
|
129
|
+
const view = renderLayout({ rightPanelTab: "variables", rightCollapsed: true });
|
|
130
|
+
const controller = fakeController();
|
|
131
|
+
act(() => useDockLayoutStore.getState().attach(controller));
|
|
132
|
+
expect(controller.activate).toHaveBeenCalledWith("variables");
|
|
133
|
+
expect(controller.setGroupVisible).toHaveBeenCalledWith("design", false);
|
|
134
|
+
const later = fakeController();
|
|
135
|
+
act(() => useDockLayoutStore.getState().attach(later));
|
|
136
|
+
expect(later.activate).not.toHaveBeenCalled();
|
|
137
|
+
expect(later.setGroupVisible).not.toHaveBeenCalled();
|
|
138
|
+
view.unmount();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("keeps a URL's slideshow tab until the panel opens, then activates it once", () => {
|
|
142
|
+
const opened = new Set<PanelId>(PANEL_IDS.filter((id) => id !== "slideshow"));
|
|
143
|
+
useDockLayoutStore.setState({ controller: null, openPanels: opened });
|
|
144
|
+
const view = renderLayout({ rightPanelTab: "slideshow" });
|
|
145
|
+
const controller = fakeController();
|
|
146
|
+
act(() => useDockLayoutStore.getState().attach(controller));
|
|
147
|
+
expect(controller.activate).not.toHaveBeenCalled();
|
|
148
|
+
act(() => useDockLayoutStore.setState({ openPanels: new Set(PANEL_IDS) }));
|
|
149
|
+
expect(controller.activate).toHaveBeenCalledWith("slideshow");
|
|
150
|
+
view.unmount();
|
|
339
151
|
});
|
|
340
152
|
});
|