HEL-373: 主题化 combobox 覆盖夜间下拉全状态,日期面板支持年月直选

原生 select 弹层无法可靠套黑金 token,改为可访问 listbox;日期标题增加年份/月份下拉,越月不提交非法日。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
总工
2026-09-02 11:07:16 +08:00
co-authored by Cursor multica-agent
parent 1061f125a8
commit 6eccbe9765
12 changed files with 1150 additions and 28 deletions
+37 -14
View File
@@ -75,7 +75,8 @@ class Hel351SourceContractTests(unittest.TestCase):
self.assertIn('id="credentialDialog"', html)
self.assertIn('id="cred-pass"', html)
self.assertIn('id="cred-copy-pass"', html)
self.assertIn("datepicker.js?v=1", html)
self.assertIn("select.js?v=1", html)
self.assertIn("datepicker.js?v=2", html)
self.assertNotIn("初始密码由管理员统一发放", html)
js = (WEB / "app.js").read_text(encoding="utf-8")
self.assertIn("function showOnceCredentials(", js)
@@ -135,15 +136,18 @@ class Hel351BrowserProbeTests(unittest.TestCase):
<button>重开审批</button>
</div>
<select class="select" id="probe-select">
<option value="">请选择公司</option>
<option>河南金牛农业科技发展有限公司</option>
<option>河南金牛煤业有限公司</option>
<option disabled>停用账户(不可选)</option>
</select>
<input class="input" id="probe-date" type="date" value="2026-09-01" />
<script src="{self.base}/select.js"></script>
<script src="{self.base}/datepicker.js"></script>
</body></html>""",
wait_until="domcontentloaded",
)
page.wait_for_function("() => window.JinniuDatePicker")
page.wait_for_function("() => window.JinniuDatePicker && window.JinniuSelect")
if zoom != 1:
page.evaluate(f"() => {{ document.body.style.zoom = '{zoom}'; }}")
@@ -152,25 +156,44 @@ class Hel351BrowserProbeTests(unittest.TestCase):
browser = p.chromium.launch(headless=True, args=["--no-sandbox"])
page = browser.new_page(viewport={"width": 1440, "height": 900})
self._open(page, night=True)
page.locator("#probe-select").locator("xpath=ancestor::div[contains(@class,'ds-combo')]").locator(".ds-combo-trigger").click()
page.wait_for_selector("#ds-listbox:not([hidden])")
colors = page.evaluate(
"""() => {
const sel = document.getElementById('probe-select');
const cs = getComputedStyle(sel);
const opt = getComputedStyle(sel.options[0]);
const trigger = document.querySelector('#probe-select').closest('.ds-combo').querySelector('.ds-combo-trigger');
const box = document.getElementById('ds-listbox');
const placeholder = box.querySelector('.is-placeholder');
const plain = [...box.querySelectorAll('.ds-listbox-option')].find((el) => !el.classList.contains('is-placeholder') && !el.classList.contains('is-disabled') && !el.classList.contains('is-selected'));
const disabled = box.querySelector('.is-disabled');
const tcs = getComputedStyle(trigger);
const bcs = getComputedStyle(box);
const pcs = placeholder && getComputedStyle(placeholder);
const ncs = plain && getComputedStyle(plain);
const dcs = disabled && getComputedStyle(disabled);
return {
scheme: getComputedStyle(document.documentElement).colorScheme,
bg: cs.backgroundColor,
color: cs.color,
optionBg: opt.backgroundColor,
optionColor: opt.color,
triggerBg: tcs.backgroundColor,
triggerColor: tcs.color,
boxBg: bcs.backgroundColor,
boxColor: bcs.color,
placeholderColor: pcs && pcs.color,
placeholderBg: pcs && pcs.backgroundColor,
plainColor: ncs && ncs.color,
plainBg: ncs && ncs.backgroundColor,
disabledColor: dcs && dcs.color,
};
}"""
)
browser.close()
self.assertEqual("dark", colors["scheme"])
self.assertNotEqual("rgb(255, 255, 255)", colors["bg"])
self.assertNotEqual("rgb(255, 255, 255)", colors["color"])
self.assertNotEqual("rgb(255, 255, 255)", colors["optionBg"])
white = "rgb(255, 255, 255)"
self.assertNotEqual(white, colors["triggerBg"])
self.assertNotEqual(white, colors["triggerColor"])
self.assertNotEqual(white, colors["boxBg"])
self.assertNotEqual(white, colors["boxColor"])
self.assertNotEqual(white, colors["placeholderBg"])
self.assertNotEqual(white, colors["placeholderColor"])
self.assertNotEqual(white, colors["plainBg"])
self.assertNotEqual(white, colors["plainColor"])
self.assertNotEqual(white, colors["disabledColor"])
def test_tabs_have_no_vertical_scrollbar_at_zoom(self) -> None:
with sync_playwright() as p: