mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix(ui): keep segmented selections visible in forced colors (#127165)
* test(ui): cover segmented controls in forced colors Amp-Thread-ID: https://ampcode.com/threads/T-01a021f4-b547-7788-a916-d4a94cbd3e3b * fix(ui): preserve segmented selection in forced colors Amp-Thread-ID: https://ampcode.com/threads/T-01a021f4-b547-7788-a916-d4a94cbd3e3b * fix(ui): separate forced-color selection and focus Amp-Thread-ID: https://ampcode.com/threads/T-01a021f4-b547-7788-a916-d4a94cbd3e3b --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
committed by
GitHub
parent
cb563bb16d
commit
1dbcbb2fb0
@@ -108,6 +108,114 @@ async function captureBrowserSettingProof(
|
||||
}
|
||||
|
||||
suite.define(() => {
|
||||
it("keeps selected segmented options distinct in forced colors", async () => {
|
||||
const cases = [
|
||||
{
|
||||
colorScheme: "dark" as const,
|
||||
config: { ui: { prefs: { themeMode: "dark" } } },
|
||||
featureMethods: undefined,
|
||||
route: "settings/appearance",
|
||||
selected: "Dark",
|
||||
unselected: "Light",
|
||||
},
|
||||
{
|
||||
colorScheme: "light" as const,
|
||||
config: { update: { auto: { enabled: true }, channel: "beta" } },
|
||||
featureMethods: ["config.get", "config.set", "config.apply", "update.run"],
|
||||
route: "settings/updates",
|
||||
selected: "Beta",
|
||||
unselected: "Stable",
|
||||
},
|
||||
];
|
||||
|
||||
for (const scenario of cases) {
|
||||
await suite.withPage(
|
||||
{
|
||||
colorScheme: scenario.colorScheme,
|
||||
forcedColors: "active",
|
||||
locale: "en-US",
|
||||
serviceWorkers: "block",
|
||||
viewport: { height: 900, width: 1280 },
|
||||
},
|
||||
async ({ page }) => {
|
||||
await installMockGateway(page, {
|
||||
featureMethods: scenario.featureMethods,
|
||||
methodResponses: {
|
||||
"config.get": {
|
||||
config: scenario.config,
|
||||
hash: `forced-colors-${scenario.route}`,
|
||||
issues: [],
|
||||
raw: JSON.stringify(scenario.config),
|
||||
runtimeConfig: scenario.config,
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
operatorScopes: ["operator.read", "operator.admin"],
|
||||
});
|
||||
|
||||
const response = await page.goto(`${suite.server.baseUrl}${scenario.route}`);
|
||||
expect(response?.status()).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia("(forced-colors: active)").matches)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
const selected = page.getByRole("radio", { name: scenario.selected, exact: true });
|
||||
const unselected = page.getByRole("radio", { name: scenario.unselected, exact: true });
|
||||
await selected.waitFor();
|
||||
expect(await selected.getAttribute("aria-checked")).toBe("true");
|
||||
expect(await unselected.getAttribute("aria-checked")).toBe("false");
|
||||
if (captureUiProofEnabled) {
|
||||
await mkdir(uiProofArtifactDir, { recursive: true });
|
||||
await selected
|
||||
.locator(
|
||||
"xpath=ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' settings-row ')][1]",
|
||||
)
|
||||
.screenshot({
|
||||
animations: "disabled",
|
||||
path: path.join(
|
||||
uiProofArtifactDir,
|
||||
`segmented-forced-colors-${scenario.colorScheme}.png`,
|
||||
),
|
||||
});
|
||||
}
|
||||
expect(
|
||||
await selected.evaluate((element) => {
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
textDecorationLine: style.textDecorationLine,
|
||||
textDecorationThickness: style.textDecorationThickness,
|
||||
};
|
||||
}),
|
||||
).toEqual({ textDecorationLine: "underline", textDecorationThickness: "2px" });
|
||||
expect(
|
||||
await unselected.evaluate((element) => getComputedStyle(element).textDecorationLine),
|
||||
).toBe("none");
|
||||
|
||||
await selected.focus();
|
||||
expect(await selected.evaluate((element) => element.matches(":focus-visible"))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
await selected.evaluate((element) => {
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
outlineOffset: style.outlineOffset,
|
||||
outlineStyle: style.outlineStyle,
|
||||
outlineWidth: style.outlineWidth,
|
||||
textDecorationLine: style.textDecorationLine,
|
||||
};
|
||||
}),
|
||||
).toEqual({
|
||||
outlineOffset: "2px",
|
||||
outlineStyle: "solid",
|
||||
outlineWidth: "2px",
|
||||
textDecorationLine: "underline",
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps checked switches on the scene accent on the security page", async () => {
|
||||
await suite.withPage(
|
||||
{
|
||||
|
||||
@@ -90,6 +90,18 @@ wa-radio-group.settings-segmented::part(radios) {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.settings-segmented__btn--active {
|
||||
text-decoration: underline 2px;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.settings-segmented__btn:focus-visible {
|
||||
outline: 2px solid Highlight;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
input.settings-input,
|
||||
select.settings-select:not([multiple]) {
|
||||
height: var(--settings-control-height);
|
||||
|
||||
Reference in New Issue
Block a user