* feat: add keyboard shortcuts and tooltips to booking slideover buttons Co-Authored-By: peer@cal.com <peer@cal.com> * fix: remove duplicate useBookingLocation import and fix import ordering - Removed duplicate import of useBookingLocation from non-existent @calcom/web/modules/bookings/hooks/useBookingLocation path - Fixed import ordering to satisfy biome organizeImports rules - Removed unnecessary code comment - Original feature by @PeerRich via Devin AI Co-Authored-By: unknown <> * use same style arrows for both button and remove bydefault focus from them * fix join button tooltip hover * fix: disable keyboard shortcuts when overlays/dialogs are open on BookingDetailsSheet Co-Authored-By: unknown <> * fix: use focus-based detection instead of selector-based overlay detection for keyboard shortcuts Co-Authored-By: unknown <> * fix: allow keyboard shortcuts when focus is on sheet ancestors Co-Authored-By: unknown <> * fix: use capture phase for keyboard handler to prevent Enter from activating focused buttons Co-Authored-By: unknown <> * fix: allow shortcuts when focus is on page elements outside any Radix portal Co-Authored-By: unknown <> * fix: handle calendar event clicks in onInteractOutside to prevent sheet close/reopen Co-Authored-By: unknown <> * fix: stop arrow key propagation to prevent Radix dropdown from opening during booking navigation Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * fix: always stop arrow key propagation when sheet is active, even at first/last booking Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: extract keyboard handler into testable utility with tests Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * fix: resolve type errors in keyboard handler config and test mocks Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * test: add e2e tests for booking sheet keyboard shortcuts Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * fix: replace text= locators with data-testid selectors in e2e tests Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * made 3 fixes: Fix 1 — Stabilize handleNext/handlePrevious/handleClose with useCallback In BookingDetailsSheet.tsx, all three handler functions were plain arrow functions recreated on every render, causing the useEffect to tear down and re-attach the document keydown listener unnecessarily. Wrapped all three in useCallback with proper dependency arrays (the Zustand store functions they call). Fix 2 — data-booking-list-item verified (no change needed) Confirmed that data-booking-list-item is rendered on BookingListItem.tsx and data-booking-calendar-event is rendered on Event.tsx. The onInteractOutside handler in the final merged state correctly checks both selectors. No code change required. Fix 3 — Removed dead code from JoinMeetingButton Reverted JoinMeetingButton back to a plain function component: Removed forwardRef wrapping (no caller passes a ref) Removed showTooltip prop (unused — tooltip is handled by the parent BookingDetailsSheet) Removed ref prop from the inner Button Removed unused Tod forwardRef imports --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com> Co-authored-by: eunjae@cal.com <hey@eunjae.dev> Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
68 lines
1.7 KiB
TypeScript
68 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import type { BookingStatus } from "@calcom/prisma/enums";
|
|
import classNames from "@calcom/ui/classNames";
|
|
import { Button } from "@calcom/ui/components/button";
|
|
import { useJoinableLocation } from "./useJoinableLocation";
|
|
|
|
interface JoinMeetingButtonProps {
|
|
location: string | null;
|
|
metadata?: unknown;
|
|
bookingStatus: BookingStatus;
|
|
size?: "sm" | "base" | "lg";
|
|
color?: "primary" | "secondary" | "minimal" | "destructive";
|
|
className?: string;
|
|
onClick?: (e: React.MouseEvent) => void;
|
|
}
|
|
|
|
export function JoinMeetingButton({
|
|
location,
|
|
metadata,
|
|
bookingStatus,
|
|
size = "base",
|
|
color = "secondary",
|
|
className,
|
|
onClick,
|
|
}: JoinMeetingButtonProps) {
|
|
const { t } = useLocale();
|
|
const { isJoinable, locationToDisplay, provider } = useJoinableLocation({
|
|
location,
|
|
metadata,
|
|
bookingStatus,
|
|
t,
|
|
});
|
|
|
|
if (!isJoinable || !locationToDisplay) {
|
|
return null;
|
|
}
|
|
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
onClick?.(e);
|
|
};
|
|
|
|
return (
|
|
<Button
|
|
color={color}
|
|
size={size}
|
|
href={locationToDisplay}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className={classNames("flex items-center gap-2", className)}
|
|
onClick={handleClick}>
|
|
{provider?.iconUrl && (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
src={provider.iconUrl}
|
|
className="h-4 w-4 shrink-0 rounded-sm"
|
|
alt={`${provider.label} logo`}
|
|
/>
|
|
)}
|
|
{provider?.label
|
|
? t("join_event_location", { eventLocationType: provider.label })
|
|
: t("join_meeting")}
|
|
</Button>
|
|
);
|
|
}
|