Skip to content

Commit d816830

Browse files
author
DavidQ
committed
Reverse shape list display order and align front back controls with visual stacking - PR_26133_051-shape-order-ui-reverse-sort-and-action-order
1 parent 9e89e6e commit d816830

4 files changed

Lines changed: 52 additions & 28 deletions

File tree

docs/dev/reports/playwright_v8_coverage_report.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# PR_26133_050 Playwright V8 Coverage Report
1+
# PR_26133_051 Playwright V8 Coverage Report
22

3-
Task: PR_26133_050-object-state-shape-grouping-and-order-controls
3+
Task: PR_26133_051-shape-order-ui-reverse-sort-and-action-order
44
Date: 2026-05-15
55

66
## Result
@@ -25,7 +25,7 @@ PASS - Coverage reporting was generated during `npm run test:workspace-v2`.
2525

2626
```text
2727
(83%) tools/object-vector-studio-v2/js/bootstrap.js - executed lines 105/105; executed functions 5/6
28-
(94%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 5070/5070; executed functions 532/563
28+
(95%) tools/object-vector-studio-v2/js/ToolStarterApp.js - executed lines 5073/5073; executed functions 533/564
2929
```
3030

3131
## Guardrail
@@ -36,4 +36,4 @@ PASS - Coverage reporting was generated during `npm run test:workspace-v2`.
3636

3737
## PR-Specific Note
3838

39-
The Workspace V2 run exercised Object Vector Studio V2 object header counts, state help text, shape grouping selection, group indicators, z-order controls, state/frame visibility overrides, base shape deletion cleanup, Object Vector schema validation, and Asteroids runtime object-vector rendering.
39+
The Workspace V2 run exercised Object Vector Studio V2 shape-list rendering, reversed visual shape ordering, selected shape z-order actions, preserved render-order behavior, Object Vector schema validation, and Asteroids runtime object-vector rendering.
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# PR_26133_050 Workspace V2 Playwright Results
1+
# PR_26133_051 Workspace V2 Playwright Results
22

3-
Task: PR_26133_050-object-state-shape-grouping-and-order-controls
3+
Task: PR_26133_051-shape-order-ui-reverse-sort-and-action-order
44
Date: 2026-05-15
55

66
## Result
@@ -14,15 +14,15 @@ PASS - `npm run test:workspace-v2` completed successfully.
1414

1515
## PR-Specific Coverage
1616

17-
- Verified Objects header includes object count, selected object state count, and selected object shape count.
18-
- Verified state `?` help lists the descriptions for every state available in the dropdown.
19-
- Verified selected shape order controls use the restored z-order glyphs and perform forward/back/front/back order changes.
20-
- Verified grouped shapes select together, render group indicators, use a deterministic group color, and ungroup cleanly.
21-
- Verified state/frame visibility toggles write frame overrides without deleting the base object shape.
22-
- Verified explicit shape delete remains a base-shape delete and cleans dependent frame override indexes.
17+
- Verified Object Vector Studio V2 keeps underlying render/order semantics unchanged: lower order remains back and higher order remains front.
18+
- Verified the object tile shape list displays in reverse UI order, with the front-most rendered shape at the top and the back-most shape at the bottom.
19+
- Verified shape stacking controls render in the requested visual order: Send To Back, Move Backward, Move Forward, Bring To Front.
20+
- Verified the reordered controls keep the existing send/backward/forward/front icon mappings.
21+
- Verified Bring To Front moves the selected shape to the top of the displayed list.
22+
- Verified Send To Back moves the selected shape to the bottom of the displayed list.
2323

2424
## Additional Validation
2525

26-
- Focused Object Vector slices passed before the full run:
27-
`shows Object Vector Studio V2 layout shell`, `edits Object Vector Studio V2 preview shapes`, and `supports Object Vector Studio V2 animation states`.
26+
- Focused Object Vector Studio V2 slice passed before the full run:
27+
`npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --grep "Object Vector Studio V2"` completed with 12 passed, 0 failed.
2828
- `git diff --check` passed.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,8 +2084,11 @@ test.describe("Workspace Manager V2 bootstrap", () => {
20842084
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-index='1']")).toHaveClass(/is-selected/);
20852085
await expect(page.locator("[data-palette-color='#ffffff']")).toHaveClass(/is-selected/);
20862086
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1']")).toHaveClass(/is-selected/);
2087-
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-tile-shape-index]")).toHaveCount(2);
2088-
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-tile-shape-index] .object-vector-studio-v2__shape-select-label")).toHaveText(["0. Rectangle", "1. Circle"]);
2087+
const objectOneShapeButtons = page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-tile-shape-index]");
2088+
await expect(objectOneShapeButtons).toHaveCount(2);
2089+
await expect(objectOneShapeButtons.first()).toHaveAttribute("data-object-tile-shape-index", "1");
2090+
await expect(objectOneShapeButtons.last()).toHaveAttribute("data-object-tile-shape-index", "0");
2091+
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-tile-shape-index] .object-vector-studio-v2__shape-select-label")).toHaveText(["1. Circle", "0. Rectangle"]);
20892092
await expect(page.locator(".object-vector-studio-v2__object-tile-shape-row.is-selected [data-object-tile-shape-index='1']")).toHaveCount(1);
20902093
const shapeTileActionLayout = await page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] .object-vector-studio-v2__object-tile-shape-row").first().evaluate((row) => {
20912094
const visibility = row.querySelector("[data-shape-visibility-index]");
@@ -2467,12 +2470,13 @@ test.describe("Workspace Manager V2 bootstrap", () => {
24672470

24682471
const selectedShapeActions = page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] .object-vector-studio-v2__object-tile-shapes .object-vector-studio-v2__shape-list-actions");
24692472
await expect(selectedShapeActions.locator("[data-shape-list-action]")).toHaveCount(6);
2470-
await expect(selectedShapeActions.locator("[data-shape-list-action] .object-vector-studio-v2__z-label")).toHaveText(["Bring Forward", "Send Backward", "Bring To Front", "Send To Back", "Group", "Ungroup"]);
2473+
await expect(selectedShapeActions.locator("[data-shape-list-action] .object-vector-studio-v2__z-label")).toHaveText(["Send To Back", "Move Backward", "Move Forward", "Bring To Front", "Group", "Ungroup"]);
24712474
const selectedShapeActionLayout = await selectedShapeActions.evaluate((actions) => {
24722475
const shapeList = actions.closest(".object-vector-studio-v2__object-tile-shapes");
24732476
const buttons = Array.from(actions.querySelectorAll("[data-shape-list-action]"));
24742477
const buttonRects = buttons.map((button) => button.getBoundingClientRect());
24752478
return {
2479+
iconKeys: buttons.map((button) => button.querySelector("[data-ovs-icon]")?.dataset.ovsIconKey),
24762480
iconOnly: actions.classList.contains("is-icon-only"),
24772481
labelsHidden: buttons.every((button) => {
24782482
const label = button.querySelector(".object-vector-studio-v2__z-label").getBoundingClientRect();
@@ -2482,14 +2486,31 @@ test.describe("Workspace Manager V2 bootstrap", () => {
24822486
squareButtons: buttonRects.every((rect) => Math.round(rect.width) === Math.round(rect.height))
24832487
};
24842488
});
2485-
expect(selectedShapeActionLayout).toEqual({ iconOnly: true, labelsHidden: true, parentIsShapeList: true, squareButtons: true });
2489+
expect(selectedShapeActionLayout).toEqual({
2490+
iconKeys: ["sendBack", "sendBackward", "bringForward", "bringFront", "group", "ungroup"],
2491+
iconOnly: true,
2492+
labelsHidden: true,
2493+
parentIsShapeList: true,
2494+
squareButtons: true
2495+
});
24862496

2487-
await selectedShapeActions.locator("[data-shape-list-action='bring-forward']").click();
2488-
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"order": 2');
2489-
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape row 0 z-order forward\./);
2497+
await selectedShapeActions.locator("[data-shape-list-action='bring-to-front']").click();
2498+
await expect(objectOneShapeButtons.first()).toHaveAttribute("data-object-tile-shape-index", "1");
2499+
await expect(objectOneShapeButtons.first()).toHaveAttribute("aria-pressed", "true");
2500+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape row 0 z-order front\./);
24902501
await selectedShapeActions.locator("[data-shape-list-action='send-to-back']").click();
2491-
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"order": 1');
2502+
await expect(objectOneShapeButtons.first()).toHaveAttribute("data-object-tile-shape-index", "1");
2503+
await expect(objectOneShapeButtons.last()).toHaveAttribute("data-object-tile-shape-index", "0");
2504+
await expect(objectOneShapeButtons.last()).toHaveAttribute("aria-pressed", "true");
24922505
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape row 1 z-order back\./);
2506+
await selectedShapeActions.locator("[data-shape-list-action='move-forward']").click();
2507+
await expect(objectOneShapeButtons.first()).toHaveAttribute("data-object-tile-shape-index", "1");
2508+
await expect(objectOneShapeButtons.first()).toHaveAttribute("aria-pressed", "true");
2509+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape row 0 z-order forward\./);
2510+
await selectedShapeActions.locator("[data-shape-list-action='move-backward']").click();
2511+
await expect(objectOneShapeButtons.last()).toHaveAttribute("data-object-tile-shape-index", "0");
2512+
await expect(objectOneShapeButtons.last()).toHaveAttribute("aria-pressed", "true");
2513+
await expect(page.locator("#statusLog")).toHaveValue(/OK Shape row 1 z-order backward\./);
24932514

24942515
await page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-tile-shape-index='0']").click();
24952516
await expect(selectedShapeActions.locator("[data-shape-list-action='group']")).toBeDisabled();
@@ -7480,7 +7501,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
74807501
await smallUfoTile.scrollIntoViewIfNeeded();
74817502
await smallUfoTile.click();
74827503
await expect(smallUfoTile).toContainText("object > asteroids > Small UFO");
7483-
await expect(smallUfoTile.locator("[data-object-tile-shape-index] .object-vector-studio-v2__shape-select-label")).toHaveText(["0. Ellipse", "1. Ellipse"]);
7504+
await expect(smallUfoTile.locator("[data-object-tile-shape-index] .object-vector-studio-v2__shape-select-label")).toHaveText(["1. Ellipse", "0. Ellipse"]);
74847505
await page.locator('button[aria-controls="objectVectorStudioV2DependencyGraphContent"]').click();
74857506
await expect(page.locator("#objectVectorStudioV2DependencyGraph")).toContainText("Object tags:");
74867507
await expect(page.locator("#objectVectorStudioV2DependencyGraph")).toContainText("object.asteroids.ship: Asteroids Ship");

tools/object-vector-studio-v2/js/ToolStarterApp.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,10 @@ export class ToolStarterApp {
14101410
createObjectTileShapeList(object) {
14111411
const list = document.createElement("div");
14121412
list.className = "object-vector-studio-v2__object-tile-shapes";
1413-
sortedShapes(object).forEach((shape, shapeIndex) => {
1413+
const displayedShapes = sortedShapes(object)
1414+
.map((shape, shapeIndex) => ({ shape, shapeIndex }))
1415+
.reverse();
1416+
displayedShapes.forEach(({ shape, shapeIndex }) => {
14141417
const displayShape = object.id === this.selectedObjectId ? this.effectiveShape(shape, shapeIndex) : shape;
14151418
const row = document.createElement("div");
14161419
row.className = "object-vector-studio-v2__object-tile-shape-row";
@@ -1497,10 +1500,10 @@ export class ToolStarterApp {
14971500
const isLocked = this.isObjectLocked(object.id);
14981501
const noShapeReason = "Disabled until a schema-valid shape is selected.";
14991502
[
1500-
{ action: "forward", iconClass: "bring-forward", iconKey: "bringForward", label: "Bring Forward", title: "Move selected shape up one row / bring forward." },
1501-
{ action: "backward", iconClass: "send-backward", iconKey: "sendBackward", label: "Send Backward", title: "Move selected shape down one row / send backward." },
1502-
{ action: "front", iconClass: "bring-front", iconKey: "bringFront", label: "Bring To Front", title: "Bring selected shape to the front." },
1503-
{ action: "back", iconClass: "send-back", iconKey: "sendBack", label: "Send To Back", title: "Send selected shape to the back." }
1503+
{ action: "back", iconClass: "send-back", iconKey: "sendBack", label: "Send To Back", title: "Send selected shape to the back." },
1504+
{ action: "backward", iconClass: "send-backward", iconKey: "sendBackward", label: "Move Backward", title: "Move selected shape backward one row." },
1505+
{ action: "forward", iconClass: "bring-forward", iconKey: "bringForward", label: "Move Forward", title: "Move selected shape forward one row." },
1506+
{ action: "front", iconClass: "bring-front", iconKey: "bringFront", label: "Bring To Front", title: "Bring selected shape to the front." }
15041507
].forEach((definition) => {
15051508
const button = this.createShapeActionButton(definition.label, definition.iconClass, definition.iconKey, definition.title, () => this.changeSelectedShapeOrder(definition.action));
15061509
this.setControlDisabled(button, !shape || isLocked, isLocked ? `Disabled because ${object.name} is locked for this runtime session.` : noShapeReason, definition.title);

0 commit comments

Comments
 (0)