From 3a948c5f58acf99a9fc84804469f18631d9dff29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Mon, 5 Oct 2026 14:03:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD(=E5=85=85=E5=80=BC=E5=BC=B9?= =?UTF-8?q?=E5=B1=82):=20=E4=BC=9A=E5=91=98=E5=91=A8=E6=9C=9F=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E6=94=AF=E6=8C=81=E9=94=AE=E7=9B=98=E6=96=B9=E5=90=91?= =?UTF-8?q?=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 月付/年付 radiogroup 原先无 roving tabindex,方向键无法移动选项 - 复用 resolveRovingIndex 与 ref,方向键同步移动选中与焦点;锁定时不响应方向键 - 补一条周期切换键盘导航测试 --- .../index.test.tsx | 46 +++++++++++++++++++ .../PlatformProfileRechargeModal/index.tsx | 28 +++++++++++ 2 files changed, 74 insertions(+) diff --git a/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx b/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx index 16ee9f5d6..0de7d8fd2 100644 --- a/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx +++ b/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx @@ -197,6 +197,52 @@ describe('PlatformProfileRechargeModal', () => { ); }); + test('周期切换按钮支持方向键与 roving tabindex', async () => { + const user = userEvent.setup(); + + render( + , + ); + + const group = screen.getByRole('radiogroup', { name: '付费周期' }); + const monthly = within(group).getByRole('radio', { name: '月付' }); + const yearly = within(group).getByRole('radio', { name: '年付' }); + expect(monthly.getAttribute('tabindex')).toBe('0'); + expect(yearly.getAttribute('tabindex')).toBe('-1'); + + monthly.focus(); + await user.keyboard('{ArrowRight}'); + + expect(yearly.getAttribute('aria-checked')).toBe('true'); + expect(monthly.getAttribute('aria-checked')).toBe('false'); + expect(yearly.getAttribute('tabindex')).toBe('0'); + expect(monthly.getAttribute('tabindex')).toBe('-1'); + expect(document.activeElement).toBe(yearly); + }); + test('requests the server quote and shows the upgrade amount', async () => { const user = userEvent.setup(); const onRequestMembershipQuote = vi.fn(); diff --git a/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx b/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx index 62a948a47..10a92df03 100644 --- a/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx +++ b/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx @@ -641,6 +641,8 @@ function PlatformMembershipCycleSwitch({ { id: 'monthly', label: '月付' }, { id: 'yearly', label: '年付' }, ]; + // 与泥点档位一致:roving tabindex + 方向键移动焦点,保证键盘可切换周期。 + const optionRefs = useRef>({}); return (
@@ -648,15 +650,41 @@ function PlatformMembershipCycleSwitch({ role="radiogroup" aria-label="付费周期" className="platform-subpanel flex gap-1 rounded-full p-1" + onKeyDown={(event) => { + if (locked) { + return; + } + const currentIndex = options.findIndex( + (option) => option.id === cycleKind, + ); + const nextIndex = resolveRovingIndex( + event.key, + currentIndex, + options.length, + ); + if (nextIndex === null) { + return; + } + event.preventDefault(); + const nextOption = options[nextIndex]; + if (nextOption) { + onChange(nextOption.id); + optionRefs.current[nextOption.id]?.focus(); + } + }} > {options.map((option) => { const selected = option.id === cycleKind; return (