From d44d9fbbeb55bfd4a4667b55d1a23c89024779b7 Mon Sep 17 00:00:00 2001 From: Gold856 <117957790+Gold856@users.noreply.github.com> Date: Sun, 7 Dec 2025 12:49:14 -0500 Subject: [PATCH] Prevent slider from going past bounds (#2222) ## Description #1900 updated how the value was handled in pv-slider, and unintentionally removed bounds protection. This restores bounds protection. Unfortunately, there is an edge case that might be rather difficult to solve. If the slider is already at the min/max, you can enter a number through the text field, and while the value won't actually update, the text field keeps the entered value, likely because the model value didn't change, and therefore, a rerender isn't triggered. However, this is an edge case that I doubt many people will actually encounter, so we should still ship this. Fixes #2221. ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2025.3.2 - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [x] If this PR addresses a bug, a regression test for it is added --- .../src/components/common/pv-slider.vue | 8 +++- photon-client/tests/input.spec.ts | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 photon-client/tests/input.spec.ts diff --git a/photon-client/src/components/common/pv-slider.vue b/photon-client/src/components/common/pv-slider.vue index e4aaf0387..4503f8cc4 100644 --- a/photon-client/src/components/common/pv-slider.vue +++ b/photon-client/src/components/common/pv-slider.vue @@ -27,7 +27,13 @@ function debounce(func: (...args: any[]) => void, wait: number) { } const debouncedEmit = debounce((v: number) => { - emit("update:modelValue", v); + if (v < props.min) { + emit("update:modelValue", props.min); + } else if (v > props.max) { + emit("update:modelValue", props.max); + } else { + emit("update:modelValue", v); + } }, 20); const localValue = computed({ diff --git a/photon-client/tests/input.spec.ts b/photon-client/tests/input.spec.ts new file mode 100644 index 000000000..acef2800e --- /dev/null +++ b/photon-client/tests/input.spec.ts @@ -0,0 +1,39 @@ +import { expect } from "@playwright/test"; +import { test } from "./fixtures.ts"; + +test("Camera Gain Slider won't go past max or min", async ({ page }) => { + await page.goto("http://localhost:5800/#/dashboard"); + await page.locator("div").filter({ hasText: "Set up some cameras to get started!" }).nth(2).press("Escape"); + + // Fill in Camera Gain text field with 1000 + await page.locator("#input-v-44").fill("1000"); + await page.locator("#input-v-44").press("Enter"); + await expect(page.locator("#input-v-44")).toHaveValue("100"); + + // Try using buttons to go past the max + await page.getByRole("button", { name: "appended action" }).nth(2).click(); + await expect(page.locator("#input-v-44")).toHaveValue("100"); + + // Make sure the value is actually properly limited, not just visually + await page.getByRole("button", { name: "prepended action" }).nth(2).click(); + await expect(page.locator("#input-v-44")).toHaveValue("99"); + + await page.locator("#input-v-44").fill("-10"); + await page.locator("#input-v-44").press("Enter"); + await expect(page.locator("#input-v-44")).toHaveValue("0"); + + await page.getByRole("button", { name: "prepended action" }).nth(2).click(); + await expect(page.locator("#input-v-44")).toHaveValue("0"); + + // Make sure the value is actually properly limited, not just visually + await page.getByRole("button", { name: "appended action" }).nth(2).click(); + await expect(page.locator("#input-v-44")).toHaveValue("1"); + + // Make sure that the guard actually prevents value setting, instead of just reverting the value + // This can be ensured by making sure the Camera Gain field doesn't disappear (disappears when the value is -1) + await page.getByRole("button", { name: "prepended action" }).nth(2).click(); + await page.getByRole("button", { name: "prepended action" }).nth(2).click(); + await expect(page.locator("#input-v-44")).toHaveValue("0"); + + await expect(page.getByText("Camera Gain", { exact: true })).toBeVisible(); +});