Replace all native <select> elements in the admin panel with SearchableSelect for a consistent UI experience. This change enhances accessibility, supports RTL and dark mode, and improves the overall design by utilizing a common component. The updates include adjustments to state management and event handling to ensure seamless integration with existing functionality across various pages and components.

This commit is contained in:
hamed
2026-07-16 08:56:59 +03:30
parent 7b8a5c2775
commit b1b99903ec
21 changed files with 571 additions and 288 deletions
@@ -2,6 +2,16 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, fireEvent, waitFor } from '@testing-library/react';
import { renderWithProviders } from '../test/utils';
/** react-select (SearchableSelect) را با placeholder پیدا و گزینه را با متن انتخاب می‌کند. */
async function pickSelect(placeholder: string, optionLabel: string) {
const ph = await screen.findByText(placeholder);
const control = ph.closest('div[class*="control"]') as HTMLElement;
const input = control.querySelector('input') as HTMLInputElement;
fireEvent.focus(input);
fireEvent.keyDown(input, { key: 'ArrowDown' });
fireEvent.click(await screen.findByText(optionLabel));
}
vi.mock('../lib/api', () => ({
api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() },
ApiError: class extends Error {},
@@ -53,12 +63,9 @@ describe('NewAppointmentDrawer (اضافه کردن نوبت جدید)', () => {
fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی مراجعه کننده را وارد نمایید'), { target: { value: 'مریم خلیلی' } });
fireEvent.change(screen.getByPlaceholderText('شماره تماس مراجعه کننده را وارد نمایید'), { target: { value: '09136549874' } });
fireEvent.change(screen.getByPlaceholderText('کد ملی مراجعه کننده را وارد نمایید'), { target: { value: '1234567891' } });
await screen.findByRole('option', { name: 'زیبایی' });
fireEvent.change(screen.getByLabelText('بخش'), { target: { value: 'sec1' } });
await screen.findByRole('option', { name: 'لیزر توتال' });
fireEvent.change(screen.getByLabelText('سرویس'), { target: { value: 'it1' } });
await screen.findByRole('option', { name: 'سحر ایمانی' });
fireEvent.change(screen.getByLabelText('پرسنل'), { target: { value: 'st1' } });
await pickSelect('انتخاب بخش', 'زیبایی');
await pickSelect('انتخاب سرویس', 'لیزر توتال');
await pickSelect('انتخاب...', 'سحر ایمانی');
fireEvent.click(screen.getByRole('button', { name: 'ثبت نوبت' }));
await waitFor(() => expect(post).toHaveBeenCalledWith('/api/v1/my/appointment', expect.objectContaining({
@@ -112,9 +119,8 @@ describe('NewAppointmentDrawer (اضافه کردن نوبت جدید)', () => {
// حالت سرویس: منوی زمان‌دهیِ دستی نباید باشد
await waitFor(() => expect(screen.queryByLabelText('ساعت شروع')).toBeNull());
fireEvent.change(screen.getByLabelText('بخش'), { target: { value: 'sec1' } });
await screen.findByRole('option', { name: 'لیزر توتال' });
fireEvent.change(screen.getByLabelText('سرویس'), { target: { value: 'it1' } });
await pickSelect('انتخاب بخش', 'زیبایی');
await pickSelect('افزودن سرویس', 'لیزر توتال');
// زمانِ خالیِ پیشنهادی ظاهر می‌شود؛ انتخاب می‌کنیم
const slotBtn = await screen.findByRole('button', { name: '15:00' });