Frontend: Rename lightbox 'info' sidebar state to 'sidebar' #4966 #5580

This commit is contained in:
Michael Mayer 2026-05-18 07:48:42 +00:00
parent 408c6fc6aa
commit ebf872d5dd
7 changed files with 93 additions and 94 deletions

View file

@ -26,7 +26,7 @@
- Follow the lint/format scripts in `frontend/package.json`; all added JS, Vue, and tests must conform.
- Unit tests (Vitest): `make test-js`, `make vitest-watch`, `make vitest-coverage`. Acceptance: `acceptance-*` targets in the root `Makefile`.
- **Always invoke Vitest through the npm/make wrapper, never bare `npx vitest run`.** `frontend/package.json`'s `test` script wraps the call in `cross-env TZ=UTC BUILD_ENV=development NODE_ENV=development BABEL_ENV=test`. Without those env vars ~50 component tests (Vuetify renders, chip-selector, login, location-input, batch-edit, people-tab, lightbox `toggleInfo`, etc.) and TZ-sensitive date tests fail spuriously — the failures look real but only reproduce in the unwrapped invocation. Do not compare a "failed N, passed M" report from bare `npx vitest run` against a `make test-js` baseline. For ad-hoc filtering on a single file, mirror the env explicitly: `(cd frontend && TZ=UTC BUILD_ENV=development NODE_ENV=development BABEL_ENV=test npx vitest run <path>)`.
- **Always invoke Vitest through the npm/make wrapper, never bare `npx vitest run`.** `frontend/package.json`'s `test` script wraps the call in `cross-env TZ=UTC BUILD_ENV=development NODE_ENV=development BABEL_ENV=test`. Without those env vars ~50 component tests (Vuetify renders, chip-selector, login, location-input, batch-edit, people-tab, lightbox `toggleSidebar`, etc.) and TZ-sensitive date tests fail spuriously — the failures look real but only reproduce in the unwrapped invocation. Do not compare a "failed N, passed M" report from bare `npx vitest run` against a `make test-js` baseline. For ad-hoc filtering on a single file, mirror the env explicitly: `(cd frontend && TZ=UTC BUILD_ENV=development NODE_ENV=development BABEL_ENV=test npx vitest run <path>)`.
- One-off TestCafe (single case by `testID`):
```bash
make storage/acceptance

View file

@ -31,7 +31,7 @@
class="p-lightbox__content no-transition"
:class="{
'hide-caption': hideCaption,
'sidebar-visible': info,
'sidebar-visible': sidebarVisible,
'face-marker-mode': faceMarkers.active,
'slideshow-active': slideshow.active,
'is-fullscreen': isFullscreen(),
@ -90,11 +90,11 @@
</div>
</div>
</div>
<div v-if="info" ref="sidebar" tabindex="-1" class="p-lightbox__sidebar bg-background">
<div v-if="sidebarVisible" tabindex="-1" class="p-lightbox__sidebar bg-background">
<p-lightbox-sidebar
ref="sidebarInfo"
ref="sidebar"
:uid="model.UID"
@close="hideInfo"
@close="hideSidebar"
@toggle-face-marker-mode="toggleFaceMarkerMode"
@toggle-face-marker-edit="toggleFaceMarkerEdit"
@eject-marker="onEjectFaceMarker"
@ -159,12 +159,12 @@ const appStorage = getAppStorage();
const appSessionStorage = getAppSessionStorage();
const viewportPadding = { top: 0, bottom: 0, left: 0, right: 0 };
// shouldShowInfo returns the persisted sidebar-visible flag.
const shouldShowInfo = () => {
return appStorage.getItem("lightbox.info") === "true";
// shouldShowSidebar returns the persisted sidebar visibility flag.
const shouldShowSidebar = () => {
return appStorage.getItem("lightbox.sidebar") === "true";
};
// shouldHideCaption returns the persisted Ctrl+H caption-hidden flag;
// shouldHideCaption returns the persisted Ctrl+H caption visibility flag;
// a missing key resolves to visible so first-time users see the caption.
const shouldHideCaption = () => {
return appStorage.getItem("lightbox.caption") === "false";
@ -182,11 +182,11 @@ export default {
return {
debug,
trace,
visible: false,
busy: false,
closing: false,
info: shouldShowInfo(),
hideCaption: shouldHideCaption() || shouldShowInfo(),
visible: false,
sidebarVisible: shouldShowSidebar(),
hideCaption: shouldHideCaption() || shouldShowSidebar(),
menuElement: null,
menuBgColor: "#252525",
menuVisible: false,
@ -351,8 +351,8 @@ export default {
this.closing = false;
this.visible = true;
this.wasFullscreen = $fullscreen.isEnabled();
this.info = shouldShowInfo();
this.hideCaption = shouldHideCaption() || this.info;
this.sidebarVisible = shouldShowSidebar();
this.hideCaption = shouldHideCaption() || this.sidebarVisible;
// Publish init event.
this.$event.publish("lightbox.init");
@ -364,7 +364,7 @@ export default {
this.resetFaceMarkers();
// Hide sidebar.
this.info = false;
this.sidebarVisible = false;
// Remove lightbox focus and hide lightbox.
if (this.visible) {
@ -420,10 +420,10 @@ export default {
return this.$refs.lightbox;
},
// Returns the metadata sidebar element.
getSidebarElement() {
// Returns the sidebar Vue component proxy.
getSidebar() {
if (!this.$refs.sidebar) {
this.log("sidebar element is not visible");
this.log("sidebar component is not visible");
return null;
}
@ -1309,7 +1309,7 @@ export default {
}),
});
// Add information toggle button.
// Add sidebar toggle control.
if (window.innerWidth > this.mobileBreakpoint) {
lightbox.pswp.ui.registerElement({
name: "sidebar-button",
@ -1325,7 +1325,7 @@ export default {
outlineID: "pswp__icn-info", // Add this to the <path> in the inner property.
size: 24, // Depends on the original SVG viewBox, e.g. use 24 for viewBox="0 0 24 24".
},
onClick: (ev) => this.onControlClick(ev, this.toggleInfo),
onClick: (ev) => this.onControlClick(ev, this.toggleSidebar),
});
}
@ -1529,7 +1529,7 @@ export default {
icon: "mdi-text-box-outline",
text: this.$gettext("Show Caption"),
shortcut: "Ctrl-H",
visible: !this.info && this.hideCaption,
visible: !this.sidebarVisible && this.hideCaption,
click: () => {
this.toggleCaption();
this.$refs.menu?.hide();
@ -1540,7 +1540,7 @@ export default {
icon: "mdi-text-box-remove-outline",
text: this.$gettext("Hide Caption"),
shortcut: "Ctrl-H",
visible: !this.info && !this.hideCaption,
visible: !this.sidebarVisible && !this.hideCaption,
click: () => {
this.toggleCaption();
this.$refs.menu?.hide();
@ -1747,8 +1747,8 @@ export default {
// still sees the dirty old photo. On cancel, revert via pswp.goTo().
if (this._suppressNavCheck) {
this._suppressNavCheck = false;
} else if (newIndex !== oldIndex && this.info && newIndex >= 0 && oldIndex >= 0) {
const sidebar = this.$refs.sidebarInfo;
} else if (newIndex !== oldIndex && this.sidebarVisible && newIndex >= 0 && oldIndex >= 0) {
const sidebar = this.getSidebar();
if (sidebar && typeof sidebar.hasPendingEdit === "function" && sidebar.hasPendingEdit()) {
const rollbackIndex = oldIndex;
this.$nextTick(() => {
@ -1784,7 +1784,7 @@ export default {
}
// Fetch full photo metadata for the sidebar if it is visible.
if (this.info) {
if (this.sidebarVisible) {
this.fetchPhoto(this.model.UID);
this.preloadNextPhoto();
}
@ -1834,7 +1834,7 @@ export default {
this.$nextTick(() => overlay.scheduleUpdate());
}
},
// Fully exits face-marker UI. Eye-toggle / Escape / hideInfo all
// Fully exits face-marker UI. Eye-toggle / Escape / hideSidebar all
// route through here.
exitFaceMarkerMode() {
this.faceMarkers.exit();
@ -1875,10 +1875,10 @@ export default {
},
// Asks the sidebar (if mounted) whether it has unsaved edits, returning
// a Promise that resolves true to proceed and false to cancel. Used by
// every gesture that would tear the sidebar down (hideInfo, slide nav,
// every gesture that would tear the sidebar down (hideSidebar, slide nav,
// close) so the user is prompted before their in-flight changes disappear.
confirmDiscardSidebar() {
const sidebar = this.$refs.sidebarInfo;
const sidebar = this.getSidebar();
if (sidebar && typeof sidebar.confirmDiscardPending === "function") {
return Promise.resolve(sidebar.confirmDiscardPending());
}
@ -2037,7 +2037,7 @@ export default {
// so sessions without library access don't issue prefetch GETs for
// long-form sidebar fields they aren't allowed to see anyway.
preloadNextPhoto() {
if (!this.info || !this.models.length || this.$config.deny("photos", "access_library")) {
if (!this.sidebarVisible || !this.models.length || this.$config.deny("photos", "access_library")) {
return;
}
Photo.prefetchAround(this.models, this.index, { before: 0, after: 1 });
@ -2477,7 +2477,7 @@ export default {
this.toggleCaption();
return true;
case "KeyI":
this.toggleInfo();
this.toggleSidebar();
return true;
case "KeyL":
this.onShowMenu();
@ -2499,7 +2499,7 @@ export default {
return;
}
if (this.info && (document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLTextAreaElement)) {
if (this.sidebarVisible && (document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLTextAreaElement)) {
return;
}
@ -2601,7 +2601,7 @@ export default {
// typed into sidebar inputs. `preventDefault` makes `_onKeyDown`
// bail before its switch statement.
onPswpKeyDown(ev) {
if (!ev || !this.info) {
if (!ev || !this.sidebarVisible) {
return;
}
const active = document.activeElement;
@ -2709,7 +2709,7 @@ export default {
// reads the flushed `.hide-caption` class see H8 in best-practices.
// No-op when the lightbox is hidden or the sidebar is open.
toggleCaption() {
if (!this.visible || this.info) {
if (!this.visible || this.sidebarVisible) {
return;
}
@ -2982,28 +2982,28 @@ export default {
}
}
},
toggleInfo() {
toggleSidebar() {
if (!this.visible) {
return;
}
if (this.info) {
this.hideInfo();
if (this.sidebarVisible) {
this.hideSidebar();
} else {
this.showInfo();
this.showSidebar();
}
},
// Shows the lightbox sidebar, if hidden.
showInfo() {
if (!this.visible || this.info) {
showSidebar() {
if (!this.visible || this.sidebarVisible) {
return;
}
this.info = true;
this.sidebarVisible = true;
// Sidebar renders the caption itself; suppress the overlay so it
// doesn't reserve viewport padding. hideInfo() restores the choice.
// doesn't reserve viewport padding. hideSidebar() restores the choice.
this.hideCaption = true;
appStorage.setItem("lightbox.info", `${this.info.toString()}`);
appStorage.setItem("lightbox.sidebar", `${this.sidebarVisible.toString()}`);
// Fetch full photo metadata when sidebar is opened.
this.fetchPhoto(this.model?.UID);
@ -3019,8 +3019,8 @@ export default {
// UI when active the eye and pencil controls live in the sidebar,
// so a closed sidebar would otherwise leave the overlay mounted with
// no UI to disable it (see P1-10).
async hideInfo() {
if (!this.visible || !this.info) {
async hideSidebar() {
if (!this.visible || !this.sidebarVisible) {
return;
}
@ -3029,14 +3029,14 @@ export default {
return;
}
this.info = false;
this.sidebarVisible = false;
// Restore the user's persisted Ctrl+H caption preference (#5580).
this.hideCaption = shouldHideCaption();
if (this.faceMarkers.active) {
this.exitFaceMarkerMode();
}
appStorage.setItem("lightbox.info", `${this.info.toString()}`);
appStorage.setItem("lightbox.sidebar", `${this.sidebarVisible.toString()}`);
// Resize and focus content element.
this.$nextTick(() => {

View file

@ -290,7 +290,7 @@ test.meta("testID", "settings-general-004").meta({ type: "short", mode: "auth" }
// that rewrote the sidebar gated both on $config.feature("people"),
// so this assertion fails if the feature flag no longer propagates.
await photoviewer.openPhotoViewer("nth", 0);
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
await t.expect(Selector(".p-lightbox-sidebar .text-subtitle-2").withText("People").exists).ok();
await t.expect(photoviewer.markersVisibilityToggle.exists).ok();
await photoviewer.triggerPhotoViewerAction("close-button");
@ -317,7 +317,7 @@ test.meta("testID", "settings-general-004").meta({ type: "short", mode: "auth" }
// People section and the face-marker toggle/add controls, even for
// an admin on a photo that has markers in the fixtures.
await photoviewer.openPhotoViewer("nth", 0);
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
await t.expect(Selector(".p-lightbox-sidebar .text-subtitle-2").withText("People").exists).notOk();
await t.expect(photoviewer.markersVisibilityToggle.exists).notOk();
await t.expect(photoviewer.markerAddButton.exists).notOk();
@ -458,7 +458,7 @@ test.meta("testID", "settings-general-006").meta({ type: "short", mode: "auth" }
// Baseline sidebar assertion: with Edit enabled, the lightbox
// sidebar exposes pencil icons for inline editing.
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
await t.expect(photoviewer.inlinePencils.exists).ok();
await photoviewer.triggerPhotoViewerAction("close-button");
@ -555,7 +555,7 @@ test.meta("testID", "settings-general-006").meta({ type: "short", mode: "auth" }
// With Edit disabled, the sidebar must drop every inline edit
// affordance: no pencil icons, no add-prompt placeholders, and no
// active inline edit inputs.
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
await t.expect(photoviewer.inlinePencils.exists).notOk();
await t.expect(photoviewer.inlineAddPrompt.exists).notOk();

View file

@ -207,7 +207,7 @@ test.meta("testID", "sharing-003").meta({ type: "short", mode: "auth" })("Common
await t.expect(toolbar.toolbarSecondTitle.withText("British Columbia").visible).ok();
await photoviewer.openPhotoViewer("nth", 0);
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
// Allow-list: file info and taken-at rows render for anonymous
// viewers so they can tell what they are looking at.

View file

@ -15,7 +15,7 @@ async function openSidebarOnFirstPhoto(t) {
await t.click(toolbar.cardsViewAction);
const uid = await photo.getNthPhotoUid("image", 0);
await photoviewer.openPhotoViewer("uid", uid);
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
return uid;
}
@ -41,7 +41,7 @@ test.meta("testID", "face-markers-002").meta({ mode: "public" })(
await t.click(toolbar.cardsViewAction);
const uid = await photo.getNthPhotoUid("image", 0);
await photoviewer.openPhotoViewer("uid", uid);
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
await t.expect(Selector("div.text-subtitle-2").withText("People").exists).ok();
await t.expect(photoviewer.markersVisibilityToggle.exists).ok();
await t.expect(photoviewer.markerAddButton.exists).ok();
@ -114,7 +114,7 @@ test.meta("testID", "face-markers-006").meta({ mode: "public" })("Common: Named
// named row we just assert the structural rule on the rendered DOM.
const uid = await photo.getNthPhotoUid("image", 0);
await photoviewer.openPhotoViewer("uid", uid);
await photoviewer.openInfoSidebar();
await photoviewer.openSidebar();
const namedRows = photoviewer.personRow.filter((node) => node.querySelector(".meta-marker-remove") === null);
const count = await namedRows.count;

View file

@ -19,9 +19,8 @@ export default class Page {
this.playButton = Selector('[class^="pswp__button pswp__button--slideshow-toggle pswp__"]', { timeout: 5000 });
this.favoriteOnIcon = Selector("button.action-favorite i.icon-favorite", { timeout: 5000 });
this.favoriteOffIcon = Selector("button.action-favorite i.icon-favorite-border", { timeout: 5000 });
// Sidebar info + face markers.
this.sidebar = Selector("div.p-lightbox__sidebar", { timeout: 15000 });
this.sidebarInfo = Selector("div.p-lightbox-sidebar", { timeout: 15000 });
// Sidebar + face markers.
this.sidebar = Selector("div.p-lightbox__sidebar div.p-lightbox-sidebar", { timeout: 15000 });
this.markersVisibilityToggle = Selector(".meta-markers-toggle", { timeout: 15000 });
this.markerAddButton = Selector(".meta-faces-edit", { timeout: 15000 });
this.markerRemoveButton = Selector(".meta-marker-remove", { timeout: 5000 });
@ -128,7 +127,7 @@ export default class Page {
await t.click(toolbar.cardsViewAction);
const uid = await photo.getNthPhotoUid("image", 0);
await this.openPhotoViewer("uid", uid);
await this.openInfoSidebar();
await this.openSidebar();
return uid;
}
@ -149,7 +148,7 @@ export default class Page {
}
}
async openInfoSidebar() {
async openSidebar() {
if (!(await this.sidebar.exists)) {
await t.click(Selector("button.pswp__button--info-button"));
}

View file

@ -33,7 +33,7 @@ const makeFaceMarkers = (overrides = {}) => ({
});
const storagePrefix = buildNamespace(clientConfig.storageNamespace);
const infoKey = `${storagePrefix}lightbox.info`;
const sidebarKey = `${storagePrefix}lightbox.sidebar`;
const mutedKey = `${storagePrefix}lightbox.muted`;
const captionKey = `${storagePrefix}lightbox.caption`;
@ -66,23 +66,23 @@ const allowAllConfig = { allow: () => true, deny: () => false };
describe("PLightbox (low-mock, jsdom-friendly)", () => {
beforeEach(() => {
localStorage.removeItem(infoKey);
localStorage.removeItem(sidebarKey);
localStorage.removeItem(captionKey);
sessionStorage.removeItem(mutedKey);
});
it("toggleInfo updates info and localStorage when visible", async () => {
it("toggleSidebar updates sidebar and localStorage when visible", async () => {
const wrapper = mountLightbox();
await wrapper.setData({ visible: true });
// Use exposed onShortCut to trigger info toggle (KeyI)
// Use exposed onShortCut to trigger sidebar toggle (KeyI)
await wrapper.vm.onShortCut({ code: "KeyI" });
await nextTick();
expect(localStorage.getItem(infoKey)).toBe("true");
expect(localStorage.getItem(sidebarKey)).toBe("true");
await wrapper.vm.onShortCut({ code: "KeyI" });
await nextTick();
expect(localStorage.getItem(infoKey)).toBe("false");
expect(localStorage.getItem(sidebarKey)).toBe("false");
});
it("toggleMute writes sessionStorage without requiring video or exposed state", async () => {
@ -110,15 +110,15 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
it("KeyI is ignored when dialog is not visible", async () => {
const wrapper = mountLightbox();
expect(localStorage.getItem(infoKey)).toBeNull();
expect(localStorage.getItem(sidebarKey)).toBeNull();
await wrapper.vm.onShortCut({ code: "KeyI" });
expect(localStorage.getItem(infoKey)).toBeNull();
expect(localStorage.getItem(sidebarKey)).toBeNull();
});
// Ctrl+H (#5580) toggles the PhotoSwipe Dynamic Caption overlay and
// persists the choice to localStorage. Default state is visible
// (hideCaption reads "lightbox.caption" with a strict !== "false"
// check, so missing-key resolves to true). Mirrors the toggleInfo
// check, so missing-key resolves to true). Mirrors the toggleSidebar
// pattern so future shortcut additions can copy the same shape.
it("toggleCaption updates hideCaption and localStorage when visible", async () => {
const wrapper = mountLightbox();
@ -223,9 +223,9 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
}
});
it("calls preventDefault when info is open and an INPUT is focused", () => {
it("calls preventDefault when sidebar is open and an INPUT is focused", () => {
const wrapper = mountLightbox();
wrapper.vm.info = true;
wrapper.vm.sidebarVisible = true;
const input = document.createElement("input");
scratch.appendChild(input);
input.focus();
@ -234,9 +234,9 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
expect(ev.preventDefault).toHaveBeenCalledTimes(1);
});
it("calls preventDefault when info is open and a TEXTAREA is focused", () => {
it("calls preventDefault when sidebar is open and a TEXTAREA is focused", () => {
const wrapper = mountLightbox();
wrapper.vm.info = true;
wrapper.vm.sidebarVisible = true;
const ta = document.createElement("textarea");
scratch.appendChild(ta);
ta.focus();
@ -247,11 +247,11 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
// Note: isContentEditable behavior isn't reliably simulated by jsdom, so
// the contenteditable branch of onPswpKeyDown is exercised in the browser
// ui-tester run. The INPUT / TEXTAREA / non-editable / no-info / no-event
// ui-tester run. The INPUT / TEXTAREA / non-editable / no-sidebar / no-event
// cases here cover the predicate's two-class boundary in unit tests.
it("does NOT call preventDefault when info is closed even with input focused", () => {
it("does NOT call preventDefault when sidebar is closed even with input focused", () => {
const wrapper = mountLightbox();
wrapper.vm.info = false;
wrapper.vm.sidebarVisible = false;
const input = document.createElement("input");
scratch.appendChild(input);
input.focus();
@ -262,7 +262,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
it("does NOT call preventDefault when focus is on a non-editable element", () => {
const wrapper = mountLightbox();
wrapper.vm.info = true;
wrapper.vm.sidebarVisible = true;
const span = document.createElement("span");
span.tabIndex = 0;
scratch.appendChild(span);
@ -274,7 +274,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
it("tolerates a missing event argument", () => {
const wrapper = mountLightbox();
wrapper.vm.info = true;
wrapper.vm.sidebarVisible = true;
expect(() => wrapper.vm.$options.methods.onPswpKeyDown.call(wrapper.vm, undefined)).not.toThrow();
});
});
@ -416,12 +416,12 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
// exits it. The eye and ✓ Done controls live in the sidebar, so a
// closed sidebar would otherwise leave the overlay mounted with no
// way to disable it.
it("hideInfo exits face-marker UI so the overlay tears down", async () => {
it("hideSidebar exits face-marker UI so the overlay tears down", async () => {
const wrapper = mountLightbox();
const exitFaceMarkerMode = vi.fn();
const ctx = {
visible: true,
info: true,
sidebarVisible: true,
faceMarkers: makeFaceMarkers({ active: true, isEdit: true, mode: FaceMarkerEdit }),
exitFaceMarkerMode,
confirmDiscardSidebar: () => Promise.resolve(true),
@ -429,20 +429,20 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
resize: vi.fn(),
focusContent: vi.fn(),
};
await wrapper.vm.$options.methods.hideInfo.call(ctx);
expect(ctx.info).toBe(false);
await wrapper.vm.$options.methods.hideSidebar.call(ctx);
expect(ctx.sidebarVisible).toBe(false);
expect(exitFaceMarkerMode).toHaveBeenCalledTimes(1);
});
// Guard: hideInfo does not exit face-marker UI if the user cancels
// Guard: hideSidebar does not exit face-marker UI if the user cancels
// out of the discard prompt. Sidebar (and overlay) stay open.
it("hideInfo keeps face-marker UI when confirmDiscardSidebar resolves false", async () => {
it("hideSidebar keeps face-marker UI when confirmDiscardSidebar resolves false", async () => {
const wrapper = mountLightbox();
const exitFaceMarkerMode = vi.fn();
const faceMarkers = makeFaceMarkers({ active: true, isEdit: true, mode: FaceMarkerEdit });
const ctx = {
visible: true,
info: true,
sidebarVisible: true,
faceMarkers,
exitFaceMarkerMode,
confirmDiscardSidebar: () => Promise.resolve(false),
@ -450,8 +450,8 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
resize: vi.fn(),
focusContent: vi.fn(),
};
await wrapper.vm.$options.methods.hideInfo.call(ctx);
expect(ctx.info).toBe(true);
await wrapper.vm.$options.methods.hideSidebar.call(ctx);
expect(ctx.sidebarVisible).toBe(true);
expect(faceMarkers.mode).toBe(FaceMarkerEdit);
expect(exitFaceMarkerMode).not.toHaveBeenCalled();
});
@ -706,7 +706,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
const toggleVideo = vi.fn();
const ctx = {
visible: true,
info: false,
sidebarVisible: false,
faceMarkers: makeFaceMarkers({ active: true, ...modeFlags }),
isShortcutDisabledInFaceMarkerMode: wrapper.vm.$options.methods.isShortcutDisabledInFaceMarkerMode,
$view: { isActive: () => true },
@ -737,17 +737,17 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
it("onShortCut still routes Escape + KeyI even when face-marker mode is active", () => {
const wrapper = mountLightbox();
const onEscapeKey = vi.fn();
const toggleInfo = vi.fn();
const toggleSidebar = vi.fn();
const ctx = {
faceMarkers: makeFaceMarkers({ active: true, isDisplay: true, mode: FaceMarkerDisplay }),
isShortcutDisabledInFaceMarkerMode: wrapper.vm.$options.methods.isShortcutDisabledInFaceMarkerMode,
onEscapeKey,
toggleInfo,
toggleSidebar,
};
wrapper.vm.$options.methods.onShortCut.call(ctx, { code: "Escape" });
wrapper.vm.$options.methods.onShortCut.call(ctx, { code: "KeyI" });
expect(onEscapeKey).toHaveBeenCalledTimes(1);
expect(toggleInfo).toHaveBeenCalledTimes(1);
expect(toggleSidebar).toHaveBeenCalledTimes(1);
});
});
});
@ -810,7 +810,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
const wrapper = mountLightbox();
const ctx = {
...wrapper.vm,
info: true,
sidebarVisible: true,
models: [{ UID: "uid-curr" }, { UID: "uid-next" }],
index: 0,
$config: { ...wrapper.vm.$config, deny: () => true, allow: () => false },
@ -827,7 +827,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
const wrapper = mountLightbox();
const ctx = {
...wrapper.vm,
info: false,
sidebarVisible: false,
models: [{ UID: "uid-curr" }, { UID: "uid-next" }],
index: 0,
};
@ -844,7 +844,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
const models = [{ UID: "uid-curr" }, { UID: "uid-next" }];
const ctx = {
...wrapper.vm,
info: true,
sidebarVisible: true,
models,
index: 0,
$config: allowAllConfig,
@ -993,7 +993,7 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
const models = [{ UID: "uid-curr" }, { UID: "uid-next" }];
const ctx = {
...wrapper.vm,
info: true,
sidebarVisible: true,
models,
index: 0,
$config: allowAllConfig,