diff --git a/client/src/pages/MobilePortalPage.tsx b/client/src/pages/MobilePortalPage.tsx
index b39a982b..a1f8c166 100644
--- a/client/src/pages/MobilePortalPage.tsx
+++ b/client/src/pages/MobilePortalPage.tsx
@@ -130,18 +130,25 @@ function ActionTile({ action }: { action: MobileAction }) {
to={action.to}
className={
isPrimary
- ? "flex min-h-24 flex-col justify-between rounded-lg border border-primary-100 bg-primary-50 p-3 text-primary shadow-sm"
+ ? "relative flex min-h-24 flex-col justify-between rounded-lg border border-primary-100 bg-primary-50 p-3 pr-14 text-primary shadow-sm"
: isWarning
- ? "flex min-h-24 flex-col justify-between rounded-lg border border-amber-200 bg-amber-50 p-3 text-amber-900"
- : "flex min-h-24 flex-col justify-between rounded-lg border border-zinc-200 bg-white p-3 text-zinc-900 shadow-sm"
+ ? "relative flex min-h-24 flex-col justify-between rounded-lg border border-amber-200 bg-amber-50 p-3 pr-14 text-amber-900"
+ : "relative flex min-h-24 flex-col justify-between rounded-lg border border-zinc-200 bg-white p-3 pr-14 text-zinc-900 shadow-sm"
}
data-testid={`mobile-action-${action.key}`}
>
-
-
{action.label}
-
{action.icon}
+
+
+ {action.label}
+
-
{action.meta}
+
+ {action.icon}
+
+
{action.meta}
);
}
diff --git a/client/tests/e2e/r5-real-device-feedback-mobile-ux.spec.ts b/client/tests/e2e/r5-real-device-feedback-mobile-ux.spec.ts
index b2adff94..2b673c3e 100644
--- a/client/tests/e2e/r5-real-device-feedback-mobile-ux.spec.ts
+++ b/client/tests/e2e/r5-real-device-feedback-mobile-ux.spec.ts
@@ -263,6 +263,25 @@ async function collectMobileMetrics(page: Page) {
document.querySelector("[data-testid='mobile-reimbursement-sticky-invoice-add']"),
document.querySelector("[data-testid='reimbursement-submit']"),
].filter(Boolean).map((element) => element!.getBoundingClientRect());
+ const actionLabels = Array.from(document.querySelectorAll("[data-testid^='mobile-action-'][data-testid$='-label']")).map((element) => {
+ const rect = element.getBoundingClientRect();
+ const style = window.getComputedStyle(element);
+ const key = element.getAttribute("data-testid")?.replace(/^mobile-action-/, "").replace(/-label$/, "") ?? "";
+ const icon = document.querySelector(`[data-testid='mobile-action-${key}-icon']`);
+ const iconRect = icon?.getBoundingClientRect();
+ return {
+ text: element.textContent?.trim() ?? "",
+ width: rect.width,
+ height: rect.height,
+ right: rect.right,
+ lineHeight: Number.parseFloat(style.lineHeight) || 24,
+ whiteSpace: style.whiteSpace,
+ iconLeft: iconRect?.left ?? null,
+ iconWidth: iconRect?.width ?? 0,
+ iconHeight: iconRect?.height ?? 0,
+ gapToIcon: iconRect ? iconRect.left - rect.right : null,
+ };
+ });
return {
width: window.innerWidth,
height: window.innerHeight,
@@ -278,6 +297,7 @@ async function collectMobileMetrics(page: Page) {
stickyButtons.length === 2 ? Math.abs(stickyButtons[0].top - stickyButtons[1].top) <= 2 : stickyButtons.length <= 1,
stickyButtonMinHeight: stickyButtons.length ? Math.min(...stickyButtons.map((rect) => rect.height)) : 0,
stickyButtonMinWidth: stickyButtons.length ? Math.min(...stickyButtons.map((rect) => rect.width)) : 0,
+ actionLabels,
text: document.body?.innerText ?? "",
};
});
@@ -314,6 +334,16 @@ test.describe("R5 真机反馈移动交互", () => {
expect(homeMetrics.bottomNavBottomGap).not.toBeNull();
expect(homeMetrics.bottomNavBottomGap!).toBeLessThanOrEqual(2);
expect(homeMetrics.scrollWidth).toBeLessThanOrEqual(homeMetrics.width + 2);
+ expect(homeMetrics.actionLabels.length).toBeGreaterThanOrEqual(4);
+ for (const label of homeMetrics.actionLabels) {
+ expect(label.whiteSpace, `${device.label} ${label.text} action label no wrap`).toBe("nowrap");
+ expect(label.height, `${device.label} ${label.text} action label single line`).toBeLessThanOrEqual(label.lineHeight + 2);
+ expect(label.width, `${device.label} ${label.text} action label readable width`).toBeGreaterThanOrEqual(40);
+ expect(label.iconWidth, `${device.label} ${label.text} action icon width`).toBeGreaterThanOrEqual(28);
+ expect(label.iconHeight, `${device.label} ${label.text} action icon height`).toBeGreaterThanOrEqual(28);
+ expect(label.gapToIcon, `${device.label} ${label.text} action label/icon spacing`).not.toBeNull();
+ expect(label.gapToIcon!, `${device.label} ${label.text} action label/icon spacing`).toBeGreaterThanOrEqual(6);
+ }
for (const item of homeMetrics.navItems) {
expect(item.visible, `${device.label} ${item.key} nav item visible`).toBe(true);
expect(item.width, `${device.label} ${item.key} nav item touch width`).toBeGreaterThanOrEqual(44);