mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(ui): align menu selection checks to the right (#122158)
Aligns selector menu checks to the trailing rail so icons and labels remain stable across Control UI menus and New Session place selection.
Refs #122156.
Prepared head SHA: b1b60d1e15
Co-authored-by: Vyctor H. Brzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
Co-authored-by: Shakker <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
This commit is contained in:
committed by
GitHub
parent
5c81ed5883
commit
24bbb416b5
@@ -0,0 +1,80 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import "../styles.css";
|
||||
import "../styles/cron.css";
|
||||
import "../styles/usage.css";
|
||||
import "./web-awesome.ts";
|
||||
|
||||
const hasBrowserLayout = !navigator.userAgent.toLowerCase().includes("jsdom");
|
||||
|
||||
type DropdownItemElement = HTMLElement & {
|
||||
checked: boolean;
|
||||
type: "checkbox" | "normal";
|
||||
updateComplete: Promise<boolean>;
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
document.body.replaceChildren();
|
||||
});
|
||||
|
||||
function createMenuItem(label: string, checkbox: boolean, checked = false) {
|
||||
const item = document.createElement("wa-dropdown-item") as DropdownItemElement;
|
||||
item.type = checkbox ? "checkbox" : "normal";
|
||||
item.checked = checked;
|
||||
item.setAttribute("checkbox-adjacent", "");
|
||||
const icon = document.createElement("span");
|
||||
icon.slot = "icon";
|
||||
icon.textContent = "#";
|
||||
item.append(icon, label);
|
||||
return item;
|
||||
}
|
||||
|
||||
function partBounds(item: DropdownItemElement, part: string): DOMRect {
|
||||
const element = item.shadowRoot?.querySelector<HTMLElement>(`[part="${part}"]`);
|
||||
expect(element, `${part} part should exist`).not.toBeNull();
|
||||
return element!.getBoundingClientRect();
|
||||
}
|
||||
|
||||
describe.skipIf(!hasBrowserLayout)("menu selection check alignment", () => {
|
||||
it("keeps the icon column flush left and selection checks in the trailing rail", async () => {
|
||||
const menu = document.createElement("div");
|
||||
menu.style.width = "240px";
|
||||
const selected = createMenuItem("Selected", true, true);
|
||||
const unselected = createMenuItem("Unselected", true);
|
||||
const command = createMenuItem("Command", false);
|
||||
const usageSelected = createMenuItem("Usage selected", true, true);
|
||||
selected.className = "cron-filter-dropdown__option";
|
||||
unselected.className = "cron-filter-dropdown__option";
|
||||
command.className = "cron-filter-dropdown__option";
|
||||
usageSelected.className = "usage-filter-option";
|
||||
menu.append(selected, unselected, command, usageSelected);
|
||||
document.body.append(menu);
|
||||
await Promise.all([
|
||||
selected.updateComplete,
|
||||
unselected.updateComplete,
|
||||
command.updateComplete,
|
||||
usageSelected.updateComplete,
|
||||
]);
|
||||
|
||||
expect(selected.hasAttribute("checkbox-adjacent")).toBe(true);
|
||||
expect(unselected.hasAttribute("checkbox-adjacent")).toBe(true);
|
||||
expect(command.hasAttribute("checkbox-adjacent")).toBe(true);
|
||||
|
||||
const selectedCheck = partBounds(selected, "checkmark");
|
||||
const selectedLabel = partBounds(selected, "label");
|
||||
expect(selectedCheck.width).toBeGreaterThan(0);
|
||||
expect(selectedLabel.width).toBeGreaterThan(0);
|
||||
expect(selectedCheck.left).toBeGreaterThanOrEqual(selectedLabel.right);
|
||||
expect(Math.abs(selectedCheck.top - selectedLabel.top)).toBeLessThanOrEqual(2);
|
||||
|
||||
const unselectedCheck = partBounds(unselected, "checkmark");
|
||||
const unselectedLabel = partBounds(unselected, "label");
|
||||
expect(unselectedCheck.left).toBeGreaterThanOrEqual(unselectedLabel.right);
|
||||
|
||||
const usageCheck = partBounds(usageSelected, "checkmark");
|
||||
const usageLabel = partBounds(usageSelected, "label");
|
||||
expect(usageCheck.left).toBeGreaterThanOrEqual(usageLabel.right);
|
||||
|
||||
const iconStarts = [selected, unselected, command].map((item) => partBounds(item, "icon").left);
|
||||
expect(Math.max(...iconStarts) - Math.min(...iconStarts)).toBeLessThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
@@ -37,14 +37,14 @@ export function renderSessionMenuItem(params: SessionMenuItemOptions, submitting
|
||||
?disabled=${submitting || (params.disabled ?? false)}
|
||||
@click=${params.onSelect}
|
||||
>
|
||||
<span class="session-menu__check" aria-hidden="true"
|
||||
>${params.checked ? icons.check : nothing}</span
|
||||
>
|
||||
${params.icon
|
||||
? html`<span class="session-menu__icon" aria-hidden="true">${params.icon}</span>`
|
||||
: nothing}
|
||||
<span class="session-menu__text">${params.label}</span>
|
||||
${params.sub ? html`<span class="session-menu__sub">${params.sub}</span>` : nothing}
|
||||
<span class="session-menu__check" aria-hidden="true"
|
||||
>${params.checked ? icons.check : nothing}</span
|
||||
>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -398,7 +398,6 @@ export function renderPlaceSelect(params: {
|
||||
?disabled=${params.submitting || params.pendingCloud || !params.browseAvailable}
|
||||
@click=${() => params.onBrowse(browseTarget)}
|
||||
>
|
||||
<span class="session-menu__check" aria-hidden="true"></span>
|
||||
<span class="session-menu__text">${t("newSession.browse")}</span>
|
||||
<span class="new-session-page__menu-chevron" aria-hidden="true"
|
||||
>${icons.chevronRight}</span
|
||||
|
||||
@@ -799,6 +799,21 @@ wa-dropdown-item[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Keep Web Awesome's checkbox rail trailing so it cannot shift menu icons and labels. */
|
||||
wa-dropdown-item[checkbox-adjacent] {
|
||||
padding-inline-start: 1em;
|
||||
}
|
||||
|
||||
wa-dropdown-item[type="checkbox"]::part(checkmark) {
|
||||
order: 1;
|
||||
margin-inline: 0.75em 0;
|
||||
}
|
||||
|
||||
wa-dropdown-item[type="checkbox"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
textarea,
|
||||
|
||||
@@ -2512,8 +2512,8 @@ wa-dropdown.sidebar-customize-menu::part(menu) {
|
||||
}
|
||||
|
||||
.sidebar-customize-menu__group-title {
|
||||
margin: 6px 4px 2px;
|
||||
padding: 7px 6px 4px;
|
||||
margin: 6px 0 2px;
|
||||
padding: 7px 8px 4px;
|
||||
border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
|
||||
color: var(--muted);
|
||||
font-size: calc(10px * var(--control-ui-text-scale));
|
||||
@@ -2569,20 +2569,6 @@ wa-dropdown-item.sidebar-customize-menu__item {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Pin-editor rows: wa-dropdown flags every row [checkbox-adjacent] when the menu
|
||||
holds checkbox items and tucks the shadow checkmark into a 2em start inset via
|
||||
a -1.5em margin. Our compact `padding: 0 8px` above removes that inset, so
|
||||
rebuild the check column here or the checkmark lands outside the row edge. */
|
||||
.sidebar-pin-editor-menu .sidebar-customize-menu__item[checkbox-adjacent] {
|
||||
padding-inline-start: 30px; /* 8px edge + 14px check column + 8px gutter */
|
||||
}
|
||||
|
||||
.sidebar-pin-editor-menu .sidebar-customize-menu__item::part(checkmark) {
|
||||
width: 14px;
|
||||
min-width: 14px;
|
||||
margin-inline: -22px 8px; /* pull back into the padding; the end margin spaces the icon */
|
||||
}
|
||||
|
||||
/* The agent switcher marks its selection in the trailing details rail
|
||||
(.session-menu__check), so the native leading checkmark is layout-only: it
|
||||
reserved a column that pushed avatar rows out of the command rows' gutter. */
|
||||
|
||||
Reference in New Issue
Block a user