mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
fix: hide the date arrows in Tasks and let n and the create button make a task there
This commit is contained in:
@@ -148,3 +148,31 @@ describe('CalendarApp in the command palette', () => {
|
||||
expect(useCalendarStore.getState().viewMode).toBe('day');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CalendarApp in the Tasks view', () => {
|
||||
beforeEach(() => setUp([makeEvent()], 'tasks'));
|
||||
|
||||
it('has no date navigation', () => {
|
||||
renderApp();
|
||||
// The one left is the mini calendar's month arrow.
|
||||
expect(screen.queryAllByRole('button', { name: 'nav_prev' })).toHaveLength(1);
|
||||
expect(screen.queryAllByRole('button', { name: 'nav_next' })).toHaveLength(1);
|
||||
expect(screen.queryByRole('button', { name: 'views.today' })).toBeNull();
|
||||
});
|
||||
|
||||
it('creates a task with n, not an event', () => {
|
||||
renderApp();
|
||||
fireEvent.keyDown(document.body, { key: 'n', code: 'KeyN' });
|
||||
expect(screen.getByRole('heading', { name: 'tasks.create' })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('heading', { name: 'events.create' })).toBeNull();
|
||||
});
|
||||
|
||||
it('creates a task with the phone create button', () => {
|
||||
media.mobile = true;
|
||||
media.desktop = false;
|
||||
renderApp();
|
||||
// The floating button, after the drawer's.
|
||||
fireEvent.click(screen.getAllByRole('button', { name: 'events.new_task' }).at(-1)!);
|
||||
expect(screen.getByRole('dialog', { name: 'tasks.create' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1071,6 +1071,14 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
|
||||
|
||||
const closeQuickCreate = useCallback(() => setQuickDraft(null), []);
|
||||
|
||||
// The create button and `n` make what the view shows: a task in Tasks.
|
||||
const inTasksView = normalizedViewMode === "tasks";
|
||||
const openCreateForView = useCallback(() => {
|
||||
if (inTasksView) openCreateTaskModal();
|
||||
else openCreateModal();
|
||||
}, [inTasksView, openCreateTaskModal, openCreateModal]);
|
||||
const createLabel = inTasksView ? t("events.new_task") : t("events.create");
|
||||
|
||||
const handleQuickCreateMoreOptions = useCallback(() => {
|
||||
if (!quickDraft) return;
|
||||
const draft = quickDraft;
|
||||
@@ -1351,12 +1359,12 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
|
||||
case "day": setViewMode("day"); break;
|
||||
case "agenda": setViewMode("agenda"); break;
|
||||
case "tasks": if (enableCalendarTasks) setViewMode("tasks"); break;
|
||||
case "new": openCreateModal(); break;
|
||||
case "new": openCreateForView(); break;
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handleKey);
|
||||
return () => window.removeEventListener("keydown", handleKey);
|
||||
}, [navigatePrev, navigateNext, goToToday, setViewMode, openCreateModal, showEventModal, showTaskModal, detailPinned, enableCalendarTasks]);
|
||||
}, [navigatePrev, navigateNext, goToToday, setViewMode, openCreateForView, showEventModal, showTaskModal, detailPinned, enableCalendarTasks]);
|
||||
|
||||
// The command palette's Calendar group, while this calendar is the one in
|
||||
// front (in the Pro shell: its focused tab).
|
||||
@@ -1695,7 +1703,8 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
|
||||
onNext={navigateNext}
|
||||
onToday={goToToday}
|
||||
onViewModeChange={(mode) => { setMobileReturnToMonth(false); setViewMode(mode); }}
|
||||
onCreateEvent={() => openCreateModal()}
|
||||
onCreateEvent={openCreateForView}
|
||||
createLabel={createLabel}
|
||||
onImport={() => setShowImportModal(true)}
|
||||
onSubscribe={() => setShowSubscriptionModal(true)}
|
||||
onRefresh={() => { void refreshCalendar(); }}
|
||||
@@ -1775,7 +1784,8 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
|
||||
style={isNarrow ? undefined : { width: `${calSidebarWidth}px` }}
|
||||
>
|
||||
<CalendarSidebarHeader
|
||||
onCreateEvent={() => { setNarrowSidebarOpen(false); openCreateModal(); }}
|
||||
onCreateEvent={() => { setNarrowSidebarOpen(false); openCreateForView(); }}
|
||||
createLabel={createLabel}
|
||||
tourTarget={!isNarrow}
|
||||
/>
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden py-3">
|
||||
@@ -1939,9 +1949,9 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps =
|
||||
{/* Floating Create Event Button (mobile) */}
|
||||
{isMobile && (
|
||||
<Button
|
||||
onClick={() => openCreateModal()}
|
||||
onClick={openCreateForView}
|
||||
className="absolute bottom-4 right-4 z-40 h-14 w-14 rounded-full shadow-lg"
|
||||
aria-label={t("events.create")}
|
||||
aria-label={createLabel}
|
||||
>
|
||||
<Plus className="h-6 w-6" />
|
||||
</Button>
|
||||
|
||||
@@ -6,6 +6,8 @@ import { Plus } from "@/components/icons";
|
||||
|
||||
interface CalendarSidebarHeaderProps {
|
||||
onCreateEvent: () => void;
|
||||
/** The create button's name: what it makes in the current view. */
|
||||
createLabel?: string;
|
||||
/** Mark the button for the guided tour; off while the toolbar's own "+" is the visible one. */
|
||||
tourTarget?: boolean;
|
||||
}
|
||||
@@ -14,8 +16,9 @@ interface CalendarSidebarHeaderProps {
|
||||
* Title row at the top of the calendar sidebar, the same shape as the
|
||||
* contacts sidebar header: the app name on the left and "+" on the right.
|
||||
*/
|
||||
export function CalendarSidebarHeader({ onCreateEvent, tourTarget = true }: CalendarSidebarHeaderProps) {
|
||||
export function CalendarSidebarHeader({ onCreateEvent, createLabel, tourTarget = true }: CalendarSidebarHeaderProps) {
|
||||
const t = useTranslations("calendar");
|
||||
const label = createLabel ?? t("events.create");
|
||||
|
||||
return (
|
||||
// Same h-14 box as the mail sidebar's account switcher, so the apps line up;
|
||||
@@ -27,8 +30,8 @@ export function CalendarSidebarHeader({ onCreateEvent, tourTarget = true }: Cale
|
||||
variant="ghost"
|
||||
onClick={onCreateEvent}
|
||||
className="h-8 w-8 flex-shrink-0"
|
||||
aria-label={t("events.create")}
|
||||
title={t("events.create")}
|
||||
aria-label={label}
|
||||
title={label}
|
||||
data-tour={tourTarget ? "create-event-button" : undefined}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
|
||||
@@ -23,6 +23,8 @@ interface CalendarToolbarProps {
|
||||
onToday: () => void;
|
||||
onViewModeChange: (mode: CalendarViewMode) => void;
|
||||
onCreateEvent: () => void;
|
||||
/** The create button's name: what it makes in the current view. */
|
||||
createLabel?: string;
|
||||
onImport?: () => void;
|
||||
onSubscribe?: () => void;
|
||||
/** Reload calendars, events and subscriptions. */
|
||||
@@ -63,6 +65,7 @@ export function CalendarToolbar({
|
||||
onToday,
|
||||
onViewModeChange,
|
||||
onCreateEvent,
|
||||
createLabel,
|
||||
onImport,
|
||||
onSubscribe,
|
||||
onRefresh,
|
||||
@@ -140,6 +143,9 @@ export function CalendarToolbar({
|
||||
}
|
||||
};
|
||||
|
||||
// Tasks have no dates to step through.
|
||||
const hasDateNav = viewMode !== "tasks";
|
||||
|
||||
// The ⋮ menu: what the calendar as a whole offers.
|
||||
const {
|
||||
contextMenu: moreMenu,
|
||||
@@ -220,18 +226,24 @@ export function CalendarToolbar({
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
<button onClick={onPrev} className="p-1.5 rounded-md hover:bg-muted transition-colors touch-manipulation" aria-label={t("nav_prev")}>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
{hasDateNav && (
|
||||
<button onClick={onPrev} className="p-1.5 rounded-md hover:bg-muted transition-colors touch-manipulation" aria-label={t("nav_prev")}>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
<span className="text-sm font-semibold text-center flex-1 select-none truncate">
|
||||
{getDateLabel()}
|
||||
</span>
|
||||
<button onClick={onNext} className="p-1.5 rounded-md hover:bg-muted transition-colors touch-manipulation" aria-label={t("nav_next")}>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
<Button variant="ghost" size="sm" onClick={onToday} className="touch-manipulation text-xs h-7 px-2 ms-0.5">
|
||||
{t("views.today")}
|
||||
</Button>
|
||||
{hasDateNav && (
|
||||
<>
|
||||
<button onClick={onNext} className="p-1.5 rounded-md hover:bg-muted transition-colors touch-manipulation" aria-label={t("nav_next")}>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
<Button variant="ghost" size="sm" onClick={onToday} className="touch-manipulation text-xs h-7 px-2 ms-0.5">
|
||||
{t("views.today")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Row 2: View switcher pills + calendar toggle */}
|
||||
@@ -373,40 +385,44 @@ export function CalendarToolbar({
|
||||
size="icon"
|
||||
onClick={onCreateEvent}
|
||||
className="h-8 w-8"
|
||||
aria-label={t("events.create")}
|
||||
title={t("events.create")}
|
||||
aria-label={createLabel ?? t("events.create")}
|
||||
title={createLabel ?? t("events.create")}
|
||||
data-tour="create-event-button"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onToday}
|
||||
className="h-8 me-1"
|
||||
title={formatFullDate(displayNow())}
|
||||
>
|
||||
{t("views.today")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={onPrev}
|
||||
aria-label={t("nav_prev")}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 rtl:rotate-180" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={onNext}
|
||||
aria-label={t("nav_next")}
|
||||
>
|
||||
<ChevronRight className="w-4 h-4 rtl:rotate-180" />
|
||||
</Button>
|
||||
{hasDateNav && (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onToday}
|
||||
className="h-8 me-1"
|
||||
title={formatFullDate(displayNow())}
|
||||
>
|
||||
{t("views.today")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={onPrev}
|
||||
aria-label={t("nav_prev")}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 rtl:rotate-180" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={onNext}
|
||||
aria-label={t("nav_next")}
|
||||
>
|
||||
<ChevronRight className="w-4 h-4 rtl:rotate-180" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<h1 className="ms-2 text-base font-semibold truncate select-none">
|
||||
{getDateLabel()}
|
||||
</h1>
|
||||
|
||||
Reference in New Issue
Block a user