Skip to content

Commit e9d3ca1

Browse files
committed
Add slider double-click reset rule and update active sliders - PR_26159_050-slider-double-click-reset
1 parent 89037cd commit e9d3ca1

9 files changed

Lines changed: 159 additions & 16 deletions

File tree

admin/controls.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ <h3>Numeric Inputs</h3>
116116
<div class="control-row"><label for="number">Numeric Input - Number - textbox</label><input
117117
id="number" type="number" value="100" min="0" max="999"></div>
118118
<div class="control-row control-row--with-value"><label for="range">Numeric Input - Range - slider</label><input
119-
id="range" type="range" min="0" max="100" value="68"><output class="slider-current-value" for="range" data-slider-value-for="range">68</output></div>
119+
id="range" type="range" min="0" max="100" value="68" title="Double-click to reset to default 68." data-slider-default="68"><output class="slider-current-value" for="range" data-slider-value-for="range">68</output></div>
120120
</div>
121121
</article>
122122
<article class="control-card side-green">

assets/theme-v2/js/account-controls.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
return `${prefix}${slider.value}${unit}`;
2828
}
2929

30+
function sliderDefaultValue(slider) {
31+
return slider.dataset.sliderDefault || slider.defaultValue || slider.min || "0";
32+
}
33+
3034
document.querySelectorAll("[data-slider-value-for]").forEach((output) => {
3135
const slider = document.getElementById(output.dataset.sliderValueFor);
3236
if (!slider) {
@@ -41,6 +45,11 @@
4145

4246
slider.addEventListener("input", updateOutput);
4347
slider.addEventListener("change", updateOutput);
48+
slider.addEventListener("dblclick", function () {
49+
slider.value = sliderDefaultValue(slider);
50+
updateOutput();
51+
slider.dispatchEvent(new Event("input", { bubbles: true }));
52+
});
4453
updateOutput();
4554
});
4655
}());

docs_build/dev/PROJECT_INSTRUCTIONS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,24 @@ Rules:
132132
- Theme V2 controls
133133
- Future tools and pages
134134

135+
## SLIDER RESET BEHAVIOR REQUIREMENT
136+
137+
All user-adjustable sliders must support reset-to-default behavior.
138+
139+
Rules:
140+
- Double-clicking a slider resets it to its default value.
141+
- Reset must occur immediately.
142+
- Reset value must be visible through the live value display.
143+
- Users must not need a separate reset button for individual sliders.
144+
- Tool-specific Reset buttons may still exist for resetting multiple controls.
145+
- Slider tooltips/help text should identify the default value when practical.
146+
- Applies to:
147+
- Toolbox tools
148+
- Project Workspace controls
149+
- Account/Admin pages
150+
- Theme V2 controls
151+
- Future tools and pages
152+
135153
## RULE PRECEDENCE
136154

137155
Newer appended sections override earlier overlapping rules.

docs_build/dev/reports/coverage_changed_js_guardrail.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ Missing changed runtime JS files are WARN, not FAIL.
66
Source: Playwright/Chromium built-in V8 coverage from the active Playwright run.
77

88
Changed runtime JS files considered:
9-
(86%) toolbox/colors/colors.js - executed lines 2111/2111; executed functions 180/209
10-
(91%) assets/theme-v2/js/account-controls.js - executed lines 39/39; executed functions 10/11
9+
(86%) toolbox/colors/colors.js - executed lines 2127/2127; executed functions 182/211
10+
(92%) assets/theme-v2/js/account-controls.js - executed lines 47/47; executed functions 12/13
1111

1212
Guardrail warnings:
1313
(100%) none - no changed runtime JS coverage warnings

docs_build/dev/reports/playwright_v8_coverage_report.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,21 @@ Note: entry percentages use function coverage when available, otherwise line cov
1212
Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.
1313

1414
Exercised tool entry points detected:
15-
(85%) Toolbox Index - exercised 3 runtime JS files
15+
(86%) Toolbox Index - exercised 3 runtime JS files
1616
(0%) Tool Template V2 - not exercised by this Playwright run
17-
(76%) Theme V2 Shared JS - exercised 3 runtime JS files
17+
(77%) Theme V2 Shared JS - exercised 3 runtime JS files
1818

1919
Changed runtime JS files covered:
20-
(86%) toolbox/colors/colors.js - executed lines 2111/2111; executed functions 180/209
21-
(91%) assets/theme-v2/js/account-controls.js - executed lines 39/39; executed functions 10/11
20+
(86%) toolbox/colors/colors.js - executed lines 2127/2127; executed functions 182/211
21+
(92%) assets/theme-v2/js/account-controls.js - executed lines 47/47; executed functions 12/13
2222

2323
Files with executed line/function counts where available:
2424
(58%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 11/19
2525
(64%) assets/theme-v2/js/tool-display-mode.js - executed lines 201/201; executed functions 9/14
2626
(76%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 525/525; executed functions 35/46
2727
(78%) toolbox/tool-registry-api-client.js - executed lines 148/148; executed functions 21/27
28-
(86%) toolbox/colors/colors.js - executed lines 2111/2111; executed functions 180/209
29-
(91%) assets/theme-v2/js/account-controls.js - executed lines 39/39; executed functions 10/11
28+
(86%) toolbox/colors/colors.js - executed lines 2127/2127; executed functions 182/211
29+
(92%) assets/theme-v2/js/account-controls.js - executed lines 47/47; executed functions 12/13
3030
(100%) toolbox/colors/palette-api-client.js - executed lines 19/19; executed functions 4/4
3131

3232
Uncovered or low-coverage changed JS files:
@@ -36,4 +36,4 @@ Changed JS files considered:
3636
(0%) tests/helpers/playwrightV8CoverageReporter.mjs - changed JS file not collected as browser runtime coverage
3737
(0%) tests/playwright/tools/PaletteToolMockRepository.spec.mjs - changed JS file not collected as browser runtime coverage
3838
(86%) toolbox/colors/colors.js - changed JS file with browser V8 coverage
39-
(91%) assets/theme-v2/js/account-controls.js - changed JS file with browser V8 coverage
39+
(92%) assets/theme-v2/js/account-controls.js - changed JS file with browser V8 coverage
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# PR_26159_050-slider-double-click-reset
2+
3+
Status: PASS
4+
5+
## Branch Guard
6+
7+
| Requirement | Status | Evidence |
8+
|---|---|---|
9+
| Current branch must be `main` before BUILD work | PASS | `git branch --show-current` returned `main`. |
10+
11+
## Requirement Checklist
12+
13+
| Requirement | Status | Evidence |
14+
|---|---|---|
15+
| Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` first | PASS | Read before branch validation and implementation. |
16+
| Add `SLIDER RESET BEHAVIOR REQUIREMENT` to slider governance | PASS | `docs_build/dev/PROJECT_INSTRUCTIONS.md` now includes the reset behavior section after slider value visibility. |
17+
| Find all active sliders in the current repo | PASS | Active range inputs are the four Colors generator sliders and the Admin Controls range demo; deprecated archive/start_of_day paths were excluded. |
18+
| Double-clicking a slider resets it to its default value | PASS | Colors double-click handler resets each generator slider to `PALETTE_GENERATOR_DEFAULTS`; Theme V2 generic handler resets Admin Controls range to `data-slider-default` / declared default. |
19+
| Reset occurs immediately | PASS | Playwright dispatches `dblclick` and immediately verifies slider values. |
20+
| Reset value is visible through the live value display | PASS | Playwright verifies visible outputs after every double-click reset. |
21+
| Users do not need a separate reset button for individual sliders | PASS | Individual sliders support double-click reset; Colors multi-control Reset button remains for group reset. |
22+
| Slider help text identifies default when practical | PASS | Active sliders include `title` text such as `Double-click to reset to default 40%.` |
23+
| Preserve existing functionality | PASS | Targeted Palette Tool lane passed existing palette, picker, pin, duplicate, tag, and validation behaviors. |
24+
| Do not add inline script/style/event handlers | PASS | Static scan found no inline `<script>`, `<style>`, `oninput`, `onchange`, `onclick`, or `ondblclick` in changed active HTML. |
25+
| Validate no console errors | PASS | Targeted Playwright helper collected no repo-owned console errors, page errors, or failed requests for covered pages. |
26+
| Produce required reports and ZIP | PASS | Review artifacts and `tmp/PR_26159_050-slider-double-click-reset_delta.zip` produced. |
27+
28+
## Active Slider Inventory
29+
30+
| Slider | File | Default | Reset Implementation | Validation |
31+
|---|---|---:|---|---|
32+
| Contrast | `toolbox/colors/index.html` | `40` | `toolbox/colors/colors.js` double-click -> `PALETTE_GENERATOR_DEFAULTS.contrast` | PASS: Playwright changes to `80`, double-clicks, verifies `40` and `40%`. |
33+
| Saturation | `toolbox/colors/index.html` | `100` | `toolbox/colors/colors.js` double-click -> `PALETTE_GENERATOR_DEFAULTS.saturation` | PASS: Playwright changes to `20`, double-clicks, verifies `100` and `100%`. |
34+
| Hue Shift | `toolbox/colors/index.html` | `0` | `toolbox/colors/colors.js` double-click -> `PALETTE_GENERATOR_DEFAULTS.hueShift` | PASS: Playwright changes to `45`, double-clicks, verifies `0` and ``. |
35+
| Step Range | `toolbox/colors/index.html` | `50` | `toolbox/colors/colors.js` double-click -> `PALETTE_GENERATOR_DEFAULTS.stepRange` | PASS: Playwright changes to `100`, double-clicks, verifies `50` and `50%`. |
36+
| Numeric Input - Range demo | `admin/controls.html` | `68` | `assets/theme-v2/js/account-controls.js` double-click -> `data-slider-default` / `defaultValue` | PASS: Playwright changes to `42`, double-clicks, verifies `68`. |
37+
38+
Intentionally skipped: none. CSS selectors such as `input[type="range"]` are styling rules, not user-adjustable controls.
39+
40+
## Validation
41+
42+
| Command / Lane | Result | Notes |
43+
|---|---|---|
44+
| `node --check toolbox/colors/colors.js` | PASS | Colors runtime JS syntax. |
45+
| `node --check assets/theme-v2/js/account-controls.js` | PASS | Theme V2/Admin Controls external JS syntax. |
46+
| `node --check tests/playwright/tools/PaletteToolMockRepository.spec.mjs` | PASS | Targeted Playwright test syntax. |
47+
| `rg --pcre2 ... toolbox/colors/index.html admin/controls.html` | PASS | No inline scripts, styles, or inline event handlers in changed active HTML. |
48+
| `npx playwright test tests/playwright/tools/PaletteToolMockRepository.spec.mjs` | PASS | 9 passed; covers all active slider double-click resets and visible value updates. |
49+
| `git diff --check` | PASS | No whitespace errors. |
50+
51+
## Playwright Impact
52+
53+
Playwright impacted: Yes.
54+
55+
Validated behavior:
56+
- Colors Contrast, Saturation, Hue Shift, and Step Range reset on double-click.
57+
- Reset values are visible immediately in the persistent slider value displays.
58+
- Admin Controls range demo resets on double-click.
59+
- Existing Colors picker, duplicate, pin, tag, add/update/clear, and validation behaviors remain passing.
60+
- Covered pages report no console errors.
61+
62+
## Coverage
63+
64+
`docs_build/dev/reports/playwright_v8_coverage_report.txt` reports:
65+
- `(86%) toolbox/colors/colors.js`
66+
- `(92%) assets/theme-v2/js/account-controls.js`
67+
68+
No low-coverage changed runtime JS warnings were reported.
69+
70+
## Skipped Lanes
71+
72+
Full samples validation: SKIP. This PR changes slider governance and UI control behavior only; no sample loader/framework or sample JSON changed.
73+
74+
Broader workspace/tool suites: SKIP. The targeted Palette Tool lane covers the changed Colors runtime and the Admin Controls slider demo. No shared persistence, engine, sample, or cross-tool launch behavior changed.

tests/playwright/tools/PaletteToolMockRepository.spec.mjs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ test("Palette Tool renders curated swatch selector controls and live preview", a
506506
await expect(page.locator("[data-palette-generator-saturation-value]")).toHaveText("100%");
507507
await expect(page.locator("[data-palette-generator-hue-shift-value]")).toHaveText("0°");
508508
await expect(page.locator("[data-palette-generator-step-range-value]")).toHaveText("50%");
509+
await expect(page.locator("[data-palette-generator-contrast]")).toHaveAttribute("title", /default 40%/);
510+
await expect(page.locator("[data-palette-generator-saturation]")).toHaveAttribute("title", /default 100%/);
511+
await expect(page.locator("[data-palette-generator-hue-shift]")).toHaveAttribute("title", /default 0/);
512+
await expect(page.locator("[data-palette-generator-step-range]")).toHaveAttribute("title", /default 50%/);
509513
await expect(page.locator("[data-palette-generator-step-range]")).toHaveAttribute("type", "range");
510514
await expect(page.locator("[data-palette-generator-generate]")).toHaveCount(0);
511515
await expect(page.locator("[data-palette-show-duplicates]")).toBeChecked();
@@ -712,8 +716,7 @@ test("Palette Tool renders curated swatch selector controls and live preview", a
712716
expect(wideStepRangeSpread.bottomLightness).toBeLessThan(0.02);
713717
expect(wideStepRangeSpread.center).toBe(defaultStepRangeSpread.center);
714718
await page.locator("[data-palette-generator-step-range]").evaluate((control) => {
715-
control.value = "50";
716-
control.dispatchEvent(new Event("input", { bubbles: true }));
719+
control.dispatchEvent(new MouseEvent("dblclick", { bubbles: true }));
717720
});
718721
await expect(page.locator("[data-palette-generator-step-range-value]")).toHaveText("50%");
719722
const restoredStepRangeSpread = await columnLightnessSpread(page, stepRangeEvidenceColumn);
@@ -732,6 +735,11 @@ test("Palette Tool renders curated swatch selector controls and live preview", a
732735
await expect(page.locator("[data-palette-generator-hue-shift]")).toHaveValue("45");
733736
await expect(page.locator("[data-palette-generator-hue-shift-value]")).toHaveText("+45°");
734737
expect(await firstSwatch.getAttribute("data-palette-generator-color")).not.toBe(initialColor);
738+
await page.locator("[data-palette-generator-hue-shift]").evaluate((control) => {
739+
control.dispatchEvent(new MouseEvent("dblclick", { bubbles: true }));
740+
});
741+
await expect(page.locator("[data-palette-generator-hue-shift]")).toHaveValue("0");
742+
await expect(page.locator("[data-palette-generator-hue-shift-value]")).toHaveText("0°");
735743

736744
await page.locator("[data-palette-theme-collection]").selectOption("ROYGBIV");
737745
await expect(page.locator("[data-palette-generator-type] option")).toHaveText(["ROYGBIV"]);
@@ -849,6 +857,16 @@ test("Palette Tool renders curated swatch selector controls and live preview", a
849857
await expect(page.locator("[data-palette-generator-saturation]")).toHaveValue("20");
850858
await expect(page.locator("[data-palette-generator-contrast-value]")).toHaveText("80%");
851859
await expect(page.locator("[data-palette-generator-saturation-value]")).toHaveText("20%");
860+
await page.locator("[data-palette-generator-contrast]").evaluate((control) => {
861+
control.dispatchEvent(new MouseEvent("dblclick", { bubbles: true }));
862+
});
863+
await page.locator("[data-palette-generator-saturation]").evaluate((control) => {
864+
control.dispatchEvent(new MouseEvent("dblclick", { bubbles: true }));
865+
});
866+
await expect(page.locator("[data-palette-generator-contrast]")).toHaveValue("40");
867+
await expect(page.locator("[data-palette-generator-saturation]")).toHaveValue("100");
868+
await expect(page.locator("[data-palette-generator-contrast-value]")).toHaveText("40%");
869+
await expect(page.locator("[data-palette-generator-saturation-value]")).toHaveText("100%");
852870
await page.locator("[data-palette-generator-reset]").click();
853871
await expect(page.locator("[data-palette-generator-contrast]")).toHaveValue("40");
854872
await expect(page.locator("[data-palette-generator-saturation]")).toHaveValue("100");
@@ -1432,12 +1450,19 @@ test("Theme V2 Admin Controls range slider displays a live persistent value", as
14321450
try {
14331451
await expect(page.getByRole("heading", { name: "Controls.", level: 1 })).toBeVisible();
14341452
await expect(page.locator("#range")).toHaveValue("68");
1453+
await expect(page.locator("#range")).toHaveAttribute("data-slider-default", "68");
1454+
await expect(page.locator("#range")).toHaveAttribute("title", "Double-click to reset to default 68.");
14351455
await expect(page.locator("[data-slider-value-for='range']")).toHaveText("68");
14361456
await page.locator("#range").evaluate((control) => {
14371457
control.value = "42";
14381458
control.dispatchEvent(new Event("input", { bubbles: true }));
14391459
});
14401460
await expect(page.locator("[data-slider-value-for='range']")).toHaveText("42");
1461+
await page.locator("#range").evaluate((control) => {
1462+
control.dispatchEvent(new MouseEvent("dblclick", { bubbles: true }));
1463+
});
1464+
await expect(page.locator("#range")).toHaveValue("68");
1465+
await expect(page.locator("[data-slider-value-for='range']")).toHaveText("68");
14411466
await expect(page.locator("[oninput], [onchange], [onclick]")).toHaveCount(0);
14421467
expectNoPageFailures(failures);
14431468
} finally {

toolbox/colors/colors.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,22 @@ function resetPaletteGeneratorControls() {
13051305
renderPaletteGeneratorPreview("Palette generator controls reset.");
13061306
}
13071307

1308+
function resetPaletteGeneratorSlider(control) {
1309+
if (!control) {
1310+
return;
1311+
}
1312+
if (control === elements.generatorContrast) {
1313+
control.value = String(PALETTE_GENERATOR_DEFAULTS.contrast);
1314+
} else if (control === elements.generatorSaturation) {
1315+
control.value = String(PALETTE_GENERATOR_DEFAULTS.saturation);
1316+
} else if (control === elements.generatorHueShift) {
1317+
control.value = String(PALETTE_GENERATOR_DEFAULTS.hueShift);
1318+
} else if (control === elements.generatorStepRange) {
1319+
control.value = String(PALETTE_GENERATOR_DEFAULTS.stepRange);
1320+
}
1321+
renderPaletteGeneratorPreview(`${control.labels?.[0]?.textContent || "Slider"} reset to default.`);
1322+
}
1323+
13081324
function setSelectValueByTextOrValue(select, preferredValue, preferredText) {
13091325
if (!select) {
13101326
return false;
@@ -2174,6 +2190,7 @@ elements.generatorColors?.addEventListener("input", () => renderPaletteGenerator
21742190
elements.generatorStepRange
21752191
].forEach((control) => {
21762192
control?.addEventListener("input", () => renderPaletteGeneratorPreview());
2193+
control?.addEventListener("dblclick", () => resetPaletteGeneratorSlider(control));
21772194
});
21782195

21792196
elements.generatorReset?.addEventListener("click", resetPaletteGeneratorControls);

0 commit comments

Comments
 (0)