Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions assets/theme-v2/css/tables.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,44 @@ td {
text-transform: none
}

.data-table [data-idea-board-idea-cell] {
cursor: pointer
}

.idea-board-idea-label {
display: inline-flex;
align-items: center;
gap: .35em;
color: inherit;
font: inherit;
line-height: inherit;
vertical-align: baseline
}

.idea-board-idea-chevron {
display: inline-block;
width: 1em;
height: 1em;
flex: 0 0 1em;
background: currentColor;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: contain;
mask-size: contain
}

.idea-board-idea-chevron--down {
-webkit-mask-image: url("../images/gfs-chevron-down.svg");
mask-image: url("../images/gfs-chevron-down.svg")
}

.idea-board-idea-chevron--up {
-webkit-mask-image: url("../images/gfs-chevron-up.svg");
mask-image: url("../images/gfs-chevron-up.svg")
}

.tool-form-table {
table-layout: fixed;
width: 100%
Expand Down
36 changes: 31 additions & 5 deletions tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ function restoreEnvValue(key, value) {
process.env[key] = value;
}

async function expectIdeaChevron(page, ideaId, iconName) {
const metrics = await page.locator(`[data-idea-board-idea-row='${ideaId}'] th`).evaluate((cell, targetIdeaId) => {
const label = cell.querySelector(".idea-board-idea-label");
const icon = cell.querySelector(`[data-idea-board-chevron='${targetIdeaId}']`);
const cellStyles = getComputedStyle(cell);
const labelStyles = getComputedStyle(label);
const iconStyles = getComputedStyle(icon);
return {
iconName: icon.dataset.ideaBoardChevronIcon,
labelDisplay: labelStyles.display,
iconWidth: Number.parseFloat(iconStyles.width),
iconHeight: Number.parseFloat(iconStyles.height),
fontSize: Number.parseFloat(cellStyles.fontSize),
iconColor: iconStyles.backgroundColor,
textColor: cellStyles.color,
maskImage: iconStyles.getPropertyValue("-webkit-mask-image") || iconStyles.maskImage,
};
}, ideaId);
expect(metrics.iconName).toBe(iconName);
expect(metrics.labelDisplay).toBe("inline-flex");
expect(Math.abs(metrics.iconWidth - metrics.fontSize)).toBeLessThanOrEqual(1);
expect(Math.abs(metrics.iconHeight - metrics.fontSize)).toBeLessThanOrEqual(1);
expect(metrics.iconColor).toBe(metrics.textColor);
expect(metrics.maskImage).toContain(iconName);
}

test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page }) => {
const server = await startRepoServer();
const previousApiUrl = process.env.GAMEFOUNDRY_API_URL;
Expand Down Expand Up @@ -58,15 +84,15 @@ test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page
await expect(page.getByText("Selected")).toHaveCount(0);

await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] th")).toHaveText("Top Thoughts");
await expect(page.locator("[data-idea-board-chevron='top-thoughts']")).toHaveAttribute("src", /gfs-chevron-down\.svg$/);
await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-down.svg");
await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] td").nth(0)).toHaveText("Smartest person wins...");
await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] td").nth(1)).toHaveText("Exploring");
await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] td").nth(2)).toHaveText("2026-06-20");
await expect(page.locator("[data-idea-board-notes-count='top-thoughts']")).toHaveText("3 Notes");
await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] [data-idea-board-idea-action]")).toHaveText(["Edit", "Delete"]);

await expect(page.locator("[data-idea-board-idea-row='sky-orchard'] th")).toHaveText("Sky Orchard");
await expect(page.locator("[data-idea-board-chevron='sky-orchard']")).toHaveAttribute("src", /gfs-chevron-down\.svg$/);
await expectIdeaChevron(page, "sky-orchard", "gfs-chevron-down.svg");
await expect(page.locator("[data-idea-board-idea-row='sky-orchard'] td").nth(0)).toHaveText("Grow floating islands...");
await expect(page.locator("[data-idea-board-notes-count='sky-orchard']")).toHaveText("3 Notes");
await expect(page.locator("[data-idea-board-idea-row='clockwork-courier'] th")).toHaveText("Clockwork Courier");
Expand All @@ -77,7 +103,7 @@ test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page
await expect(page.locator("[data-idea-board-expanded-row]")).toHaveCount(0);
await page.locator("[data-idea-board-idea-cell='top-thoughts']").click();
await expect(page.locator("[data-idea-board-expanded-row='top-thoughts']")).toBeVisible();
await expect(page.locator("[data-idea-board-chevron='top-thoughts']")).toHaveAttribute("src", /gfs-chevron-up\.svg$/);
await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-up.svg");
await expect(page.locator("[data-idea-board-expanded-row='top-thoughts'] [data-idea-board-notes-header='top-thoughts']")).toHaveText("Notes");
await expect(page.locator("[data-idea-board-notes-table='top-thoughts'] th[scope='col']")).toHaveText(["Note", "Actions"]);
await expect(page.locator("[data-idea-board-notes-table] th[scope='col']", { hasText: "Type" })).toHaveCount(0);
Expand Down Expand Up @@ -113,12 +139,12 @@ test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page
await page.locator("[data-idea-board-idea-cell='sky-orchard']").click();
await expect(page.locator("[data-idea-board-expanded-row='top-thoughts']")).toHaveCount(0);
await expect(page.locator("[data-idea-board-expanded-row='sky-orchard']")).toBeVisible();
await expect(page.locator("[data-idea-board-chevron='sky-orchard']")).toHaveAttribute("src", /gfs-chevron-up\.svg$/);
await expectIdeaChevron(page, "sky-orchard", "gfs-chevron-up.svg");
await page.locator("[data-idea-board-notes-count='sky-orchard']").click();
await expect(page.locator("[data-idea-board-expanded-row='sky-orchard']")).toBeVisible();
await page.locator("[data-idea-board-idea-cell='sky-orchard']").click();
await expect(page.locator("[data-idea-board-expanded-row]")).toHaveCount(0);
await expect(page.locator("[data-idea-board-chevron='sky-orchard']")).toHaveAttribute("src", /gfs-chevron-down\.svg$/);
await expectIdeaChevron(page, "sky-orchard", "gfs-chevron-down.svg");

await page.locator("[data-idea-board-add-idea]").click();
const ideaInputRow = page.locator("[data-idea-board-idea-input-row]").last();
Expand Down
30 changes: 28 additions & 2 deletions tests/playwright/tools/ToolboxRoutePages.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,32 @@ function restoreEnvValue(key, value) {
process.env[key] = value;
}

async function expectIdeaChevron(page, ideaId, iconName) {
const metrics = await page.locator(`[data-idea-board-idea-row='${ideaId}'] th`).evaluate((cell, targetIdeaId) => {
const label = cell.querySelector(".idea-board-idea-label");
const icon = cell.querySelector(`[data-idea-board-chevron='${targetIdeaId}']`);
const cellStyles = getComputedStyle(cell);
const labelStyles = getComputedStyle(label);
const iconStyles = getComputedStyle(icon);
return {
iconName: icon.dataset.ideaBoardChevronIcon,
labelDisplay: labelStyles.display,
iconWidth: Number.parseFloat(iconStyles.width),
iconHeight: Number.parseFloat(iconStyles.height),
fontSize: Number.parseFloat(cellStyles.fontSize),
iconColor: iconStyles.backgroundColor,
textColor: cellStyles.color,
maskImage: iconStyles.getPropertyValue("-webkit-mask-image") || iconStyles.maskImage,
};
}, ideaId);
expect(metrics.iconName).toBe(iconName);
expect(metrics.labelDisplay).toBe("inline-flex");
expect(Math.abs(metrics.iconWidth - metrics.fontSize)).toBeLessThanOrEqual(1);
expect(Math.abs(metrics.iconHeight - metrics.fontSize)).toBeLessThanOrEqual(1);
expect(metrics.iconColor).toBe(metrics.textColor);
expect(metrics.maskImage).toContain(iconName);
}

test("tools route aliases render toolbox tool pages", async ({ page }) => {
const server = await startRepoServer();
const failedRequests = [];
Expand Down Expand Up @@ -226,13 +252,13 @@ test("Idea Board launches from Toolbox with accordion table notes model", async
await expect(page.locator("[data-idea-board-notes-count='top-thoughts']")).toHaveText("3 Notes");
await expect(page.locator("[data-idea-board-notes-count='sky-orchard']")).toHaveText("3 Notes");
await expect(page.locator("[data-idea-board-notes-count='clockwork-courier']")).toHaveText("0 Notes");
await expect(page.locator("[data-idea-board-chevron='top-thoughts']")).toHaveAttribute("src", /gfs-chevron-down\.svg$/);
await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-down.svg");
await expect(page.locator("[data-idea-board-status]")).toHaveText("Idea Board table edits are in-page only. No project records, auth, AI, or database behavior is connected.");
await page.locator("[data-idea-board-notes-count='top-thoughts']").click();
await expect(page.locator("[data-idea-board-expanded-row]")).toHaveCount(0);
await page.locator("[data-idea-board-idea-cell='top-thoughts']").click();
await expect(page.locator("[data-idea-board-expanded-row='top-thoughts']")).toBeVisible();
await expect(page.locator("[data-idea-board-chevron='top-thoughts']")).toHaveAttribute("src", /gfs-chevron-up\.svg$/);
await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-up.svg");
await expect(page.locator("[data-idea-board-notes-header='top-thoughts']")).toHaveText("Notes");
await expect(page.locator("[data-idea-board-notes-table='top-thoughts'] th[scope='col']")).toHaveText(["Note", "Actions"]);
await expect(page.getByText("Notes for Sky Orchard")).toHaveCount(0);
Expand Down
14 changes: 10 additions & 4 deletions toolbox/idea-board/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,19 @@ function renderIdeaRow(tbody, record) {
idea.setAttribute("aria-expanded", String(expanded));
idea.setAttribute("role", "button");
idea.setAttribute("aria-label", `${expanded ? "Collapse" : "Expand"} notes for ${record.idea}`);
const ideaLabel = document.createElement("span");
ideaLabel.className = "idea-board-idea-label";
const ideaText = document.createElement("span");
ideaText.className = "idea-board-idea-label__text";
ideaText.textContent = record.idea;
const chevron = document.createElement("img");
chevron.alt = "";
const chevron = document.createElement("span");
const chevronIcon = expanded ? "gfs-chevron-up.svg" : "gfs-chevron-down.svg";
chevron.className = `idea-board-idea-chevron idea-board-idea-chevron--${expanded ? "up" : "down"}`;
chevron.setAttribute("aria-hidden", "true");
chevron.dataset.ideaBoardChevron = record.ideaId;
chevron.src = expanded ? "assets/theme-v2/images/gfs-chevron-up.svg" : "assets/theme-v2/images/gfs-chevron-down.svg";
idea.append(ideaText, " ", chevron);
chevron.dataset.ideaBoardChevronIcon = chevronIcon;
ideaLabel.append(ideaText, chevron);
idea.append(ideaLabel);
row.append(idea);
row.append(cell(record.pitch));
row.append(cell(record.status));
Expand Down
Loading