mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
fix: shorten the calendar search placeholder when the field is narrow
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { render, screen, cleanup } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { createRef } from 'react';
|
||||
import { CalendarSearchField } from '../calendar-search';
|
||||
|
||||
// pim-21: at 150 % in German the search box read "Termine durchs". A field
|
||||
// too narrow for "Search events" now says "Search"; its name stays whole.
|
||||
|
||||
/** The field is `width` px wide inside; text measures 10px per character. */
|
||||
function stubWidth(width: number) {
|
||||
vi.stubGlobal('ResizeObserver', class implements ResizeObserver {
|
||||
constructor(private readonly callback: ResizeObserverCallback) {}
|
||||
observe(target: Element) {
|
||||
this.callback([{ target, contentRect: { width } } as unknown as ResizeObserverEntry], this);
|
||||
}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
});
|
||||
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({
|
||||
font: '',
|
||||
measureText: (text: string) => ({ width: text.length * 10 }),
|
||||
} as unknown as CanvasRenderingContext2D);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('CalendarSearchField placeholder', () => {
|
||||
it('says the short word in a narrow field', () => {
|
||||
// "search.title" is 12 characters: 120px does not fit in 96.
|
||||
stubWidth(96);
|
||||
render(<CalendarSearchField value="" onChange={vi.fn()} />);
|
||||
const input = screen.getByRole('searchbox');
|
||||
expect(input.getAttribute('placeholder')).toBe('search_short');
|
||||
expect(input.getAttribute('aria-label')).toBe('search.title');
|
||||
});
|
||||
|
||||
it('keeps the whole phrase where it fits', () => {
|
||||
stubWidth(200);
|
||||
render(<CalendarSearchField value="" onChange={vi.fn()} />);
|
||||
expect(screen.getByRole('searchbox').getAttribute('placeholder')).toBe('search.title');
|
||||
});
|
||||
|
||||
it('still hands the input to the page', () => {
|
||||
stubWidth(200);
|
||||
const ref = createRef<HTMLInputElement>();
|
||||
render(<CalendarSearchField ref={ref} value="" onChange={vi.fn()} />);
|
||||
expect(ref.current).toBe(screen.getByRole('searchbox'));
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { forwardRef, useMemo, useRef, type KeyboardEvent } from "react";
|
||||
import { forwardRef, useCallback, useMemo, useRef, type KeyboardEvent } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { format } from "date-fns";
|
||||
import { Search, MapPin, Repeat } from "@/components/icons";
|
||||
@@ -18,6 +18,7 @@ import type { Calendar, CalendarEvent } from "@/lib/jmap/types";
|
||||
import type { CalendarSearchResults as Results } from "@/hooks/use-calendar-event-search";
|
||||
import { getEventColor } from "./event-card";
|
||||
import { useCvdColor } from "@/hooks/use-cvd-color";
|
||||
import { useShortenedText } from "@/hooks/use-shortened-text";
|
||||
|
||||
const RESULT_SELECTOR = "[data-search-result]";
|
||||
|
||||
@@ -34,6 +35,15 @@ interface CalendarSearchFieldProps {
|
||||
export const CalendarSearchField = forwardRef<HTMLInputElement, CalendarSearchFieldProps>(
|
||||
function CalendarSearchField({ value, onChange, onDismiss, autoFocus, className }, ref) {
|
||||
const t = useTranslations("calendar");
|
||||
const tGrid = useTranslations("ux_pim_grid");
|
||||
// A narrow field says "Search" rather than cut "Search events" (pim-21:
|
||||
// "Termine durchs" at 150 % in German). The name stays the full one.
|
||||
const [measureRef, placeholder] = useShortenedText([t("search.title"), tGrid("search_short")]);
|
||||
const inputRef = useCallback((node: HTMLInputElement | null) => {
|
||||
measureRef(node);
|
||||
if (typeof ref === "function") ref(node);
|
||||
else if (ref) ref.current = node;
|
||||
}, [measureRef, ref]);
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Escape") {
|
||||
// Escape clears the search first, then leaves the field (R8).
|
||||
@@ -49,7 +59,7 @@ export const CalendarSearchField = forwardRef<HTMLInputElement, CalendarSearchFi
|
||||
return (
|
||||
<div role="search" className={className}>
|
||||
<ToolbarSearch
|
||||
ref={ref}
|
||||
ref={inputRef}
|
||||
value={value}
|
||||
autoFocus={autoFocus}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
@@ -57,7 +67,7 @@ export const CalendarSearchField = forwardRef<HTMLInputElement, CalendarSearchFi
|
||||
onClear={() => onChange("")}
|
||||
clearLabel={t("search.clear")}
|
||||
shortcut="/"
|
||||
placeholder={t("search.title")}
|
||||
placeholder={placeholder}
|
||||
aria-label={t("search.title")}
|
||||
autoComplete="off"
|
||||
data-calendar-search
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "المؤسسة"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "بحث",
|
||||
"reply": "رد",
|
||||
"reply_to_invitation": "الرد على الدعوة"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "حجز تقويمي",
|
||||
"description": "يضيف حدثًا مشغولًا لمدة غيابك، حتى لا يحجز أحد اجتماعات معك خلالها.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organització"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Cerca",
|
||||
"reply": "Respon",
|
||||
"reply_to_invitation": "Respon a la invitació"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Bloca el meu calendari",
|
||||
"description": "Afegeix un esdeveniment ocupat mentre sou fora, perquè ningú us convoqui a reunions aleshores.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organizace"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Hledat",
|
||||
"reply": "Odpovědět",
|
||||
"reply_to_invitation": "Odpovědět na pozvánku"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Zablokovat můj kalendář",
|
||||
"description": "Přidá obsazenou událost na dobu vaší nepřítomnosti, aby vám tehdy nikdo neplánoval schůzky.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisation"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Søg",
|
||||
"reply": "Svar",
|
||||
"reply_to_invitation": "Svar på invitationen"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Bloker min kalender",
|
||||
"description": "Tilføjer en optaget begivenhed, mens du er væk, så ingen booker møder med dig imens.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisation"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Suchen",
|
||||
"reply": "Antworten",
|
||||
"reply_to_invitation": "Einladung beantworten"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Meinen Kalender blockieren",
|
||||
"description": "Trägt für Ihre Abwesenheit einen belegten Termin ein, damit Ihnen niemand Besprechungen in dieser Zeit einträgt.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisation"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Search",
|
||||
"reply": "Reply",
|
||||
"reply_to_invitation": "Answer the invitation"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Block my calendar",
|
||||
"description": "Adds a busy event for the time you’re away, so nobody books meetings with you then.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organización"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Buscar",
|
||||
"reply": "Responder",
|
||||
"reply_to_invitation": "Responder a la invitación"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Bloquear mi calendario",
|
||||
"description": "Añade un evento ocupado mientras estás fuera, para que nadie te convoque a reuniones entonces.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisatsioon"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Otsi",
|
||||
"reply": "Vasta",
|
||||
"reply_to_invitation": "Vasta kutsele"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Blokeeri minu kalender",
|
||||
"description": "Lisab äraoleku ajaks hõivatud sündmuse, et keegi sel ajal sulle koosolekuid ei paneks.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "سازمان"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "جستجو",
|
||||
"reply": "پاسخ",
|
||||
"reply_to_invitation": "پاسخ به دعوت"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "مسدود کردن تقویم من",
|
||||
"description": "برای مدت غیبت شما یک رویداد مشغول اضافه میکند تا کسی در آن زمان جلسه نگذارد.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisation"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Rechercher",
|
||||
"reply": "Répondre",
|
||||
"reply_to_invitation": "Répondre à l’invitation"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Bloquer mon calendrier",
|
||||
"description": "Ajoute un événement occupé pendant votre absence, pour que personne ne vous invite à une réunion à ce moment-là.",
|
||||
|
||||
@@ -5551,6 +5551,11 @@
|
||||
"company_calendars": {
|
||||
"section": "ארגון"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "חיפוש",
|
||||
"reply": "מענה",
|
||||
"reply_to_invitation": "מענה להזמנה"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "חסום את היומן שלי",
|
||||
"description": "מוסיף אירוע תפוס לזמן ההיעדרות, כדי שאף אחד לא יקבע איתך פגישות אז.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Szervezet"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Keresés",
|
||||
"reply": "Válasz",
|
||||
"reply_to_invitation": "Válasz a meghívásra"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Naptáram foglalása",
|
||||
"description": "Foglalt eseményt tesz a távollét idejére, hogy addig senki ne szervezzen Önnel megbeszélést.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organizzazione"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Cerca",
|
||||
"reply": "Rispondi",
|
||||
"reply_to_invitation": "Rispondi all’invito"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Blocca il mio calendario",
|
||||
"description": "Aggiunge un evento occupato per il periodo di assenza, così nessuno ti fissa riunioni in quei giorni.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "組織"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "検索",
|
||||
"reply": "返信",
|
||||
"reply_to_invitation": "招待に返信"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "カレンダーをブロック",
|
||||
"description": "不在の期間に予定ありの予定を追加し、その間に会議を入れられないようにします。",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "조직"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "검색",
|
||||
"reply": "응답",
|
||||
"reply_to_invitation": "초대에 응답"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "내 캘린더 차단",
|
||||
"description": "부재 기간에 바쁨 일정을 추가해 그동안 회의가 잡히지 않게 합니다.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organizācija"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Meklēt",
|
||||
"reply": "Atbildēt",
|
||||
"reply_to_invitation": "Atbildēt uz ielūgumu"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Bloķēt manu kalendāru",
|
||||
"description": "Pievieno aizņemtu notikumu prombūtnes laikam, lai tad neviens neplānotu ar jums sapulces.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Байгууллага"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Хайх",
|
||||
"reply": "Хариулах",
|
||||
"reply_to_invitation": "Урилгад хариулах"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Миний хуанлийг хаах",
|
||||
"description": "Таныг байхгүй үед завгүй арга хэмжээ нэмж, тэр үед хэн ч уулзалт товлохгүй болгоно.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisasjon"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Søk",
|
||||
"reply": "Svar",
|
||||
"reply_to_invitation": "Svar på invitasjonen"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Blokker kalenderen min",
|
||||
"description": "Legger inn en opptatt-hendelse mens du er borte, så ingen booker møter med deg da.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organisatie"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Zoeken",
|
||||
"reply": "Reageren",
|
||||
"reply_to_invitation": "Op uitnodiging reageren"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Mijn agenda blokkeren",
|
||||
"description": "Zet een bezette afspraak in je agenda voor de tijd dat je weg bent, zodat niemand dan vergaderingen met je plant.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organizacja"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Szukaj",
|
||||
"reply": "Odpowiedz",
|
||||
"reply_to_invitation": "Odpowiedz na zaproszenie"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Zablokuj mój kalendarz",
|
||||
"description": "Dodaje zajęte wydarzenie na czas Twojej nieobecności, żeby nikt nie umawiał wtedy z Tobą spotkań.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organização"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Pesquisar",
|
||||
"reply": "Responder",
|
||||
"reply_to_invitation": "Responder ao convite"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Bloquear o meu calendário",
|
||||
"description": "Adiciona um evento ocupado durante a sua ausência, para que ninguém marque reuniões consigo nessa altura.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organizație"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Caută",
|
||||
"reply": "Răspunde",
|
||||
"reply_to_invitation": "Răspunde la invitație"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Blochează-mi calendarul",
|
||||
"description": "Adaugă un eveniment ocupat pe durata absenței, ca nimeni să nu vă programeze ședințe atunci.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Организация"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Поиск",
|
||||
"reply": "Ответить",
|
||||
"reply_to_invitation": "Ответить на приглашение"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Занять мой календарь",
|
||||
"description": "Добавляет событие «занят» на время отсутствия, чтобы вам не назначали встречи.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Organizácia"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Hľadať",
|
||||
"reply": "Odpovedať",
|
||||
"reply_to_invitation": "Odpovedať na pozvánku"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Zablokovať môj kalendár",
|
||||
"description": "Pridá obsadenú udalosť na čas vašej neprítomnosti, aby vám vtedy nikto neplánoval stretnutia.",
|
||||
|
||||
@@ -2736,6 +2736,11 @@
|
||||
"company_calendars": {
|
||||
"section": "องค์กร"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "ค้นหา",
|
||||
"reply": "ตอบกลับ",
|
||||
"reply_to_invitation": "ตอบกลับคำเชิญ"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "บล็อกปฏิทินของฉัน",
|
||||
"description": "เพิ่มกิจกรรมไม่ว่างในช่วงที่คุณไม่อยู่ เพื่อไม่ให้ใครนัดประชุมกับคุณในเวลานั้น",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Kuruluş"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Ara",
|
||||
"reply": "Yanıtla",
|
||||
"reply_to_invitation": "Davete yanıt ver"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Takvimimi kapat",
|
||||
"description": "Uzakta olduğunuz süre için meşgul bir etkinlik ekler, böylece o sırada kimse sizinle toplantı ayarlamaz.",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Організація"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Пошук",
|
||||
"reply": "Відповісти",
|
||||
"reply_to_invitation": "Відповісти на запрошення"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Заблокувати мій календар",
|
||||
"description": "Додає подію «зайнято» на час вашої відсутності, щоб вам тоді не призначали зустрічей.",
|
||||
|
||||
@@ -5153,6 +5153,11 @@
|
||||
"company_calendars": {
|
||||
"section": "Tổ chức"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "Tìm",
|
||||
"reply": "Trả lời",
|
||||
"reply_to_invitation": "Trả lời lời mời"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "Chặn lịch của tôi",
|
||||
"description": "Thêm một sự kiện bận trong thời gian bạn vắng mặt để không ai đặt cuộc họp với bạn lúc đó.",
|
||||
|
||||
@@ -5141,6 +5141,11 @@
|
||||
"company_calendars": {
|
||||
"section": "組織"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "搜尋",
|
||||
"reply": "回覆",
|
||||
"reply_to_invitation": "回覆邀請"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "佔用我的行事曆",
|
||||
"description": "在你外出期間新增一個忙碌活動,這樣別人不會在這段時間約你開會。",
|
||||
|
||||
@@ -5732,6 +5732,11 @@
|
||||
"company_calendars": {
|
||||
"section": "组织"
|
||||
},
|
||||
"ux_pim_grid": {
|
||||
"search_short": "搜索",
|
||||
"reply": "回复",
|
||||
"reply_to_invitation": "回复邀请"
|
||||
},
|
||||
"vacation_calendar": {
|
||||
"label": "占用我的日历",
|
||||
"description": "在你外出期间添加一个忙碌活动,这样别人不会在这段时间约你开会。",
|
||||
|
||||
Reference in New Issue
Block a user