mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(studio): reveal the shown tab only when its strip narrows
A strip the user scrolled by hand no longer snaps back when another panel changes. The load smoke measures the focused group's strip directly.
This commit is contained in:
@@ -177,10 +177,25 @@ describe("dock tab fill", () => {
|
|||||||
const strip = stripOf("design");
|
const strip = stripOf("design");
|
||||||
if (!strip) throw new Error("no design strip");
|
if (!strip) throw new Error("no design strip");
|
||||||
await activate("variables");
|
await activate("variables");
|
||||||
// The four tabs fit, so the strip is unscrolled.
|
// The four tabs fit, so the strip is unscrolled; then it narrows to two and a half tabs.
|
||||||
strip.scrollLeft = 0;
|
strip.scrollLeft = 0;
|
||||||
|
Object.defineProperty(strip, "clientWidth", { value: 1000, configurable: true });
|
||||||
|
act(() => resizeAll());
|
||||||
Object.defineProperty(strip, "clientWidth", { value: 250, configurable: true });
|
Object.defineProperty(strip, "clientWidth", { value: 250, configurable: true });
|
||||||
act(() => resizeAll());
|
act(() => resizeAll());
|
||||||
expect(strip.scrollLeft).toBe(3 * TAB_STEP + TAB_WIDTH + 1 - 250);
|
expect(strip.scrollLeft).toBe(3 * TAB_STEP + TAB_WIDTH + 1 - 250);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("leaves a strip the user scrolled alone when something else in the dock changes", async () => {
|
||||||
|
const strip = stripOf("design");
|
||||||
|
if (!strip) throw new Error("no design strip");
|
||||||
|
await activate("variables");
|
||||||
|
Object.defineProperty(strip, "clientWidth", { value: 250, configurable: true });
|
||||||
|
act(() => resizeAll());
|
||||||
|
// Scrolled back by hand to look at the first tabs, then another group takes focus.
|
||||||
|
strip.scrollLeft = 0;
|
||||||
|
await activate("assets");
|
||||||
|
act(() => resizeAll());
|
||||||
|
expect(strip.scrollLeft).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,13 +22,16 @@ function revealedScroll(box: { left: number; width: number }, scrollLeft: number
|
|||||||
return Math.max(scrollLeft, box.left + box.width + 1 - width);
|
return Math.max(scrollLeft, box.left + box.width + 1 - width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reads where the shown tab sits; the write happens after every strip is read, so layout runs once. */
|
/**
|
||||||
function measureFill(list: HTMLElement): () => void {
|
* Reads where the shown tab sits; the write happens after every strip is read, so layout runs once.
|
||||||
|
* Only a strip that got narrower is scrolled, so a strip the user scrolled by hand stays put.
|
||||||
|
*/
|
||||||
|
function measureFill(list: HTMLElement, narrowed: boolean): () => void {
|
||||||
const fill = fillOf(list);
|
const fill = fillOf(list);
|
||||||
const tab = list.querySelector<HTMLElement>(":scope > .dv-active-tab");
|
const tab = list.querySelector<HTMLElement>(":scope > .dv-active-tab");
|
||||||
const box = tab ? { width: tab.offsetWidth, left: tab.offsetLeft } : null;
|
const box = tab ? { width: tab.offsetWidth, left: tab.offsetLeft } : null;
|
||||||
const scrollLeft = list.scrollLeft;
|
const scrollLeft = list.scrollLeft;
|
||||||
const target = box ? revealedScroll(box, scrollLeft, list.clientWidth) : scrollLeft;
|
const target = box && narrowed ? revealedScroll(box, scrollLeft, list.clientWidth) : scrollLeft;
|
||||||
return () => {
|
return () => {
|
||||||
fill.hidden = !box;
|
fill.hidden = !box;
|
||||||
if (!box) return;
|
if (!box) return;
|
||||||
@@ -70,9 +73,16 @@ export function installTabFill(api: DockviewApi, root: HTMLElement): () => void
|
|||||||
observed.add(element);
|
observed.add(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const widths = new WeakMap<HTMLElement, number>();
|
||||||
|
function narrowed(list: HTMLElement) {
|
||||||
|
const width = list.clientWidth;
|
||||||
|
const before = widths.get(list) ?? width;
|
||||||
|
widths.set(list, width);
|
||||||
|
return width < before;
|
||||||
|
}
|
||||||
function placeAll() {
|
function placeAll() {
|
||||||
const strips = [...lists()];
|
const strips = [...lists()];
|
||||||
const writes = strips.map(measureFill);
|
const writes = strips.map((list) => measureFill(list, narrowed(list)));
|
||||||
for (const write of writes) write();
|
for (const write of writes) write();
|
||||||
for (const list of strips) markClippedEdges(list);
|
for (const list of strips) markClippedEdges(list);
|
||||||
observe(strips.flatMap((list) => [list, ...list.querySelectorAll(":scope > .dv-tab")]));
|
observe(strips.flatMap((list) => [list, ...list.querySelectorAll(":scope > .dv-tab")]));
|
||||||
|
|||||||
@@ -101,9 +101,10 @@ export function studioSmokeApiResponse(method, requestUrl) {
|
|||||||
// about 10px narrower than macOS does; keep that much room so a strip that fits here fits on a Mac.
|
// about 10px narrower than macOS does; keep that much room so a strip that fits here fits on a Mac.
|
||||||
const MAC_FONT_ALLOWANCE_PX = 10;
|
const MAC_FONT_ALLOWANCE_PX = 10;
|
||||||
|
|
||||||
/** Runs in the page: the strip's tabs, plus the allowance, must fit before its action slot. */
|
/** Runs in the page: the active group's tabs, plus the allowance, must fit before its actions. */
|
||||||
function clippedStrip(index, allowance) {
|
function clippedStrip(allowance) {
|
||||||
const strip = document.querySelectorAll(".dv-tabs-container")[index];
|
const strip = document.querySelector(".dv-groupview.dv-active-group .dv-tabs-container");
|
||||||
|
if (!strip) return "no active dock strip";
|
||||||
const actions = strip
|
const actions = strip
|
||||||
.closest(".dv-tabs-and-actions-container")
|
.closest(".dv-tabs-and-actions-container")
|
||||||
?.querySelector(".dv-right-actions-container");
|
?.querySelector(".dv-right-actions-container");
|
||||||
@@ -125,13 +126,13 @@ async function dockStripErrors(page) {
|
|||||||
found.push(`${reservedSlots} inactive dock strips hold width for actions they do not draw`);
|
found.push(`${reservedSlots} inactive dock strips hold width for actions they do not draw`);
|
||||||
}
|
}
|
||||||
const shownTabs = await page.$$(".dv-tabs-container .dv-active-tab");
|
const shownTabs = await page.$$(".dv-tabs-container .dv-active-tab");
|
||||||
for (const [index, tab] of shownTabs.entries()) {
|
for (const tab of shownTabs) {
|
||||||
// An active group draws its strip actions, so this is the least room its tabs get.
|
// An active group draws its strip actions, so this is the least room its tabs get.
|
||||||
await tab.click();
|
await tab.click();
|
||||||
await page.evaluate(
|
await page.evaluate(
|
||||||
() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))),
|
() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))),
|
||||||
);
|
);
|
||||||
const clipped = await page.evaluate(clippedStrip, index, MAC_FONT_ALLOWANCE_PX);
|
const clipped = await page.evaluate(clippedStrip, MAC_FONT_ALLOWANCE_PX);
|
||||||
if (clipped) {
|
if (clipped) {
|
||||||
found.push(
|
found.push(
|
||||||
`Dock tab strip clips a label at the default layout (${MAC_FONT_ALLOWANCE_PX}px macOS allowance): ${clipped}`,
|
`Dock tab strip clips a label at the default layout (${MAC_FONT_ALLOWANCE_PX}px macOS allowance): ${clipped}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user