fix(mobile): enforce strict >= 44x44px touch targets on topbar, drawer close, and form builder controls

This commit is contained in:
ochenstarik 2026-09-11 10:25:20 +07:00
parent 42d48618ec
commit 63b12b92ec
5 changed files with 21 additions and 16 deletions

View file

@ -1,9 +1,8 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => { test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto('/'); await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded');
}); });
test('1. First open immediately displays all 18 modules with "Все модули" active by default', async ({ page }) => { test('1. First open immediately displays all 18 modules with "Все модули" active by default', async ({ page }) => {

View file

@ -2,8 +2,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () => { test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto('/'); await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded');
}); });
test('1. Mobile viewport (< 768px) automatically displays bottom navigation', async ({ page, viewport }) => { test('1. Mobile viewport (< 768px) automatically displays bottom navigation', async ({ page, viewport }) => {
@ -88,6 +87,12 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () =
// Close drawer // Close drawer
const closeBtn = page.locator('[data-testid="close-mobile-menu"]'); const closeBtn = page.locator('[data-testid="close-mobile-menu"]');
const closeBox = await closeBtn.boundingBox();
expect(closeBox).not.toBeNull();
if (closeBox) {
expect(closeBox.height).toBeGreaterThanOrEqual(44);
expect(closeBox.width).toBeGreaterThanOrEqual(44);
}
await closeBtn.click(); await closeBtn.click();
await expect(drawer).not.toBeVisible(); await expect(drawer).not.toBeVisible();
}); });
@ -139,11 +144,12 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () =
if (await moveDownBtn.isVisible()) { if (await moveDownBtn.isVisible()) {
await expect(moveDownBtn).toBeVisible(); await expect(moveDownBtn).toBeVisible();
// Verify button has accessible touch target // Verify button has accessible touch target >= 44x44px
const box = await moveDownBtn.boundingBox(); const box = await moveDownBtn.boundingBox();
expect(box).not.toBeNull(); expect(box).not.toBeNull();
if (box) { if (box) {
expect(box.height).toBeGreaterThanOrEqual(28); expect(box.height).toBeGreaterThanOrEqual(44);
expect(box.width).toBeGreaterThanOrEqual(44);
} }
} }
}); });

View file

@ -97,7 +97,7 @@ export const MobileMenuDrawer: React.FC<MobileMenuDrawerProps> = ({ isOpen, onCl
<button <button
onClick={onClose} onClick={onClose}
className="w-10 h-10 rounded-xl bg-krona-workspace border border-krona-border text-krona-secondary hover:text-krona-primary flex items-center justify-center active:scale-95 transition-all" className="w-11 h-11 min-w-[44px] min-h-[44px] rounded-xl bg-krona-workspace border border-krona-border text-krona-secondary hover:text-krona-primary flex items-center justify-center active:scale-95 transition-all"
aria-label="Закрыть меню" aria-label="Закрыть меню"
data-testid="close-mobile-menu" data-testid="close-mobile-menu"
> >

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { useDemoStore } from '../../mock/store'; import { useDemoStore } from '../../mock/store';
import { BUSINESS_PROFILES } from '../../core/profiles/profiles'; import { BUSINESS_PROFILES } from '../../core/profiles/profiles';
@ -96,7 +96,7 @@ export const Topbar: React.FC = () => {
{/* AI Trigger */} {/* AI Trigger */}
<button <button
onClick={() => setIsAiDrawerOpen(true)} onClick={() => setIsAiDrawerOpen(true)}
className="w-9 h-9 rounded-xl bg-krona-workspace text-krona-gold hover:text-krona-gold-text border border-krona-border flex items-center justify-center active:scale-95 transition-all" className="w-11 h-11 min-w-[44px] min-h-[44px] rounded-xl bg-krona-workspace text-krona-gold hover:text-krona-gold-text border border-krona-border flex items-center justify-center active:scale-95 transition-all"
title="Krona AI" title="Krona AI"
aria-label="Krona AI" aria-label="Krona AI"
> >
@ -106,7 +106,7 @@ export const Topbar: React.FC = () => {
{/* Notifications Trigger */} {/* Notifications Trigger */}
<button <button
onClick={() => setIsNotificationsOpen(true)} onClick={() => setIsNotificationsOpen(true)}
className="relative w-9 h-9 rounded-xl bg-krona-workspace text-krona-secondary hover:text-krona-primary border border-krona-border flex items-center justify-center active:scale-95 transition-all" className="relative w-11 h-11 min-w-[44px] min-h-[44px] rounded-xl bg-krona-workspace text-krona-secondary hover:text-krona-primary border border-krona-border flex items-center justify-center active:scale-95 transition-all"
title="Уведомления" title="Уведомления"
aria-label="Уведомления" aria-label="Уведомления"
data-testid="mobile-notifications-btn" data-testid="mobile-notifications-btn"
@ -120,7 +120,7 @@ export const Topbar: React.FC = () => {
{/* Profile / Menu Drawer Trigger */} {/* Profile / Menu Drawer Trigger */}
<button <button
onClick={() => setIsMobileDrawerOpen(true)} onClick={() => setIsMobileDrawerOpen(true)}
className="w-9 h-9 rounded-xl bg-krona-workspace text-krona-primary border border-krona-border flex items-center justify-center active:scale-95 transition-all" className="w-11 h-11 min-w-[44px] min-h-[44px] rounded-xl bg-krona-workspace text-krona-primary border border-krona-border flex items-center justify-center active:scale-95 transition-all"
title="Меню управления" title="Меню управления"
aria-label="Открыть меню управления" aria-label="Открыть меню управления"
data-testid="mobile-menu-btn" data-testid="mobile-menu-btn"

View file

@ -640,11 +640,11 @@ export const FormBuilder: React.FC = () => {
handleMoveField(section.id, fIdx, Math.max(0, fIdx - 1)); handleMoveField(section.id, fIdx, Math.max(0, fIdx - 1));
}} }}
disabled={fIdx === 0} disabled={fIdx === 0}
className="p-1.5 min-w-[32px] min-h-[32px] flex items-center justify-center rounded hover:bg-krona-surface hover:text-krona-primary disabled:opacity-20 transition-colors" className="p-1.5 min-w-[44px] min-h-[44px] flex items-center justify-center rounded hover:bg-krona-surface hover:text-krona-primary disabled:opacity-20 transition-colors"
title="Переместить вверх / влево" title="Переместить вверх / влево"
aria-label="Переместить поле выше" aria-label="Переместить поле выше"
> >
<MoveUp className="w-3.5 h-3.5" /> <MoveUp className="w-4 h-4" />
</button> </button>
<button <button
type="button" type="button"
@ -658,11 +658,11 @@ export const FormBuilder: React.FC = () => {
); );
}} }}
disabled={fIdx === section.fieldKeys.length - 1} disabled={fIdx === section.fieldKeys.length - 1}
className="p-1.5 min-w-[32px] min-h-[32px] flex items-center justify-center rounded hover:bg-krona-surface hover:text-krona-primary disabled:opacity-20 transition-colors" className="p-1.5 min-w-[44px] min-h-[44px] flex items-center justify-center rounded hover:bg-krona-surface hover:text-krona-primary disabled:opacity-20 transition-colors"
title="Переместить вниз / вправо" title="Переместить вниз / вправо"
aria-label="Переместить поле ниже" aria-label="Переместить поле ниже"
> >
<MoveDown className="w-3.5 h-3.5" /> <MoveDown className="w-4 h-4" />
</button> </button>
</div> </div>