Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
import { toast } from "sonner";
import { useRouter } from "next/navigation";

import { cn } from "@/lib/utils";

import { Menu, MenuItem, MenuSeparator } from "@veriworkly/ui";

import type { DocumentLibraryItem } from "@/features/documents/services/document-library";
Expand All @@ -26,6 +28,8 @@ export interface DocumentActionsMenuProps {
onRenameAction: (doc: DocumentLibraryItem) => void;
onSyncNowAction: (id: string) => void;
onSyncDetailsAction: (id: string) => void;
className?: string;
triggerClassName?: string;
}

export function DocumentActionsMenu({
Expand All @@ -36,32 +40,44 @@ export function DocumentActionsMenu({
onRenameAction,
onSyncNowAction,
onSyncDetailsAction,
className,
triggerClassName,
}: DocumentActionsMenuProps) {
const router = useRouter();
const editorPath = getDocumentEditorPath(doc.type, doc.id);

return (
<Menu
size="sm"
panelClassName="z-50"
trigger={({ open, toggle, menuId }) => (
<button
type="button"
aria-expanded={open}
aria-haspopup="menu"
aria-controls={open ? menuId : undefined}
aria-label={`Open actions for ${doc.title}`}
className="border-border bg-card/90 text-foreground hover:bg-background flex h-9 w-9 items-center justify-center rounded-xl border shadow-sm backdrop-blur"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
toggle();
}}
>
<MoreHorizontal className="h-4 w-4" />
</button>
)}
>
<div className={className}>
<Menu
size="sm"
panelClassName="z-50"
trigger={({ open, toggle, menuId }) => (
<div
className={cn(
"transition-opacity duration-200",
open
? "opacity-100 pointer-events-auto"
: triggerClassName
)}
>
<button
type="button"
aria-expanded={open}
aria-haspopup="menu"
aria-controls={open ? menuId : undefined}
aria-label={`Open actions for ${doc.title}`}
className="border-border bg-card/90 text-foreground hover:bg-background focus-visible:ring-2 focus-visible:ring-accent focus-visible:outline-none flex h-9 w-9 items-center justify-center rounded-xl border shadow-sm backdrop-blur"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
toggle();
}}
>
<MoreHorizontal className="h-4 w-4" />
</button>
</div>
)}
>
{({ close }) => (
<>
<MenuItem
Expand Down Expand Up @@ -147,5 +163,6 @@ export function DocumentActionsMenu({
</>
)}
</Menu>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function DocumentPreviewCard({
<DocumentThumbnailPreview doc={doc} />
</div>

<div className="bg-background/90 border-border absolute right-0 bottom-0 left-0 z-10 min-w-0 translate-y-[calc(100%-44px)] border-t p-3 backdrop-blur-md transition-transform duration-300 ease-in-out group-hover:translate-y-0">
<div className="bg-background/90 border-border absolute right-0 bottom-0 left-0 z-10 min-w-0 translate-y-0 md:translate-y-[calc(100%-44px)] border-t p-3 backdrop-blur-md transition-transform duration-300 ease-in-out md:group-hover:translate-y-0 md:group-focus-within:translate-y-0">
<div className="flex h-5 items-center justify-between gap-2">
<h2 className="text-foreground truncate pr-2 text-sm font-bold">{doc.title}</h2>

Expand Down Expand Up @@ -82,20 +82,20 @@ export function DocumentPreviewCard({
<Link
href={editorPath}
aria-label={`Open ${doc.title}`}
className="absolute inset-0 z-20 cursor-pointer"
className="absolute inset-0 z-20 cursor-pointer focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 rounded-xl outline-none"
/>

<div className="absolute top-2 right-2 z-30 opacity-0 transition-opacity duration-200 group-hover:opacity-100">
<DocumentActionsMenu
doc={doc}
syncing={syncing}
onShareAction={onShareAction}
onRenameAction={onRenameAction}
onDeleteAction={onDeleteAction}
onSyncNowAction={onSyncNowAction}
onSyncDetailsAction={onSyncDetailsAction}
/>
</div>
<DocumentActionsMenu
doc={doc}
syncing={syncing}
onShareAction={onShareAction}
onRenameAction={onRenameAction}
onDeleteAction={onDeleteAction}
onSyncNowAction={onSyncNowAction}
onSyncDetailsAction={onSyncDetailsAction}
className="absolute top-2 right-2 z-30"
triggerClassName="opacity-100 pointer-events-auto md:opacity-0 md:pointer-events-none transition-opacity duration-200 md:group-hover:opacity-100 md:group-hover:pointer-events-auto md:group-focus-within:opacity-100 md:group-focus-within:pointer-events-auto"
/>
</article>
);
}
Expand Down
12 changes: 10 additions & 2 deletions apps/studio/tests/contracts/template-render.contract.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";

import type { TemplateRenderProps } from "@/types/template";

import { defaultResume } from "@/features/resume/constants/default-resume";
import { loadTemplateComponentById, templateRegistry } from "@/templates";

Expand Down Expand Up @@ -35,9 +37,15 @@ describe("template render contract", () => {
for (const template of templateRegistry) {
const TemplateComponent = loadTemplateComponentById(template.id);

expect(() => renderToStaticMarkup(<TemplateComponent {...({} as any)} />)).not.toThrow();
expect(() =>
renderToStaticMarkup(<TemplateComponent {...({} as unknown as TemplateRenderProps)} />),
).not.toThrow();

expect(() => renderToStaticMarkup(<TemplateComponent resume={null as any} />)).not.toThrow();
expect(() =>
renderToStaticMarkup(
<TemplateComponent resume={null as unknown as TemplateRenderProps["resume"]} />,
),
).not.toThrow();
}
});

Expand Down
Loading