@handled-ai/design-system 0.20.33 → 0.20.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/contextual-quick-action-launcher.d.ts +6 -0
- package/dist/components/contextual-quick-action-launcher.js +1 -0
- package/dist/components/contextual-quick-action-launcher.js.map +1 -1
- package/dist/components/pill.d.ts +1 -1
- package/dist/components/tabs.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/contextual-quick-action-launcher.test.tsx +14 -0
- package/src/components/contextual-quick-action-launcher.tsx +7 -0
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const badgeVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" |
|
|
6
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
8
8
|
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
9
9
|
asChild?: boolean;
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" |
|
|
6
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
@@ -8,6 +8,12 @@ interface ContextualQuickActionItem {
|
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
disabledReason?: string;
|
|
10
10
|
meta?: React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Optional stable test id stamped on the rendered menu item. Lets callers
|
|
13
|
+
* preserve an existing testid when an action moves into the launcher (e.g.
|
|
14
|
+
* the inbox `case-sync-button`).
|
|
15
|
+
*/
|
|
16
|
+
testId?: string;
|
|
11
17
|
}
|
|
12
18
|
type ContextualQuickActionLauncherVariant = "default" | "case-panel";
|
|
13
19
|
interface ContextualQuickActionContextLabelProps {
|
|
@@ -160,6 +160,7 @@ function ContextualQuickActionLauncher({
|
|
|
160
160
|
DropdownMenuItem,
|
|
161
161
|
{
|
|
162
162
|
disabled: item.disabled,
|
|
163
|
+
"data-testid": item.testId,
|
|
163
164
|
onSelect: (event) => handleSelect(item, event),
|
|
164
165
|
className: cn(
|
|
165
166
|
isCasePanel ? "grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100" : "flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/contextual-quick-action-launcher.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, Zap } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface ContextualQuickActionItem {\n id: string\n label: string\n description?: string\n icon?: React.ReactNode\n disabled?: boolean\n disabledReason?: string\n meta?: React.ReactNode\n}\n\nexport type ContextualQuickActionLauncherVariant = \"default\" | \"case-panel\"\n\nexport interface ContextualQuickActionContextLabelProps {\n contextLabel: string\n contextSecondary?: string\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n}\n\nexport interface ContextualQuickActionLauncherProps {\n contextLabel: string\n contextSecondary?: string\n items: ContextualQuickActionItem[]\n onSelect: (item: ContextualQuickActionItem) => void\n onBrowseAll?: () => void\n showHint?: boolean\n align?: \"start\" | \"end\"\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\nfunction ContextualQuickActionContextLabel({\n contextLabel,\n contextSecondary,\n variant = \"default\",\n className,\n}: ContextualQuickActionContextLabelProps) {\n const isCasePanel = variant === \"case-panel\"\n\n return (\n <div\n data-slot=\"contextual-quick-action-context-label\"\n data-variant={variant}\n className={cn(\n isCasePanel\n ? \"-mx-2 -mt-2 mb-1 flex items-center gap-1.5 whitespace-nowrap border-b px-3 py-2.5 text-[13px] text-muted-foreground\"\n : \"-mx-1 -mt-1 mb-1 flex items-center gap-1.5 border-b px-3 py-2 text-[11px] text-muted-foreground\",\n className\n )}\n >\n <Zap\n className={cn(isCasePanel ? \"h-3.5 w-3.5 shrink-0\" : \"h-3 w-3 shrink-0\")}\n strokeWidth={2.25}\n aria-hidden=\"true\"\n />\n <span>Acting on</span>\n <strong\n className={cn(\n isCasePanel\n ? \"max-w-[260px] truncate font-semibold text-foreground\"\n : \"max-w-[180px] truncate font-semibold text-foreground\"\n )}\n >\n {contextLabel}\n </strong>\n {contextSecondary ? (\n <span className=\"min-w-0 truncate text-muted-foreground\">\n · {contextSecondary}\n </span>\n ) : null}\n </div>\n )\n}\n\nfunction DefaultActionIcon() {\n return <Zap className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n}\n\nfunction ContextualQuickActionLauncher({\n contextLabel,\n contextSecondary,\n items,\n onSelect,\n onBrowseAll,\n showHint = false,\n align = \"start\",\n variant = \"default\",\n className,\n open,\n defaultOpen,\n onOpenChange,\n}: ContextualQuickActionLauncherProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false)\n const isCasePanel = variant === \"case-panel\"\n const isControlled = open !== undefined\n const isOpen = isControlled ? open : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(nextOpen)\n }\n onOpenChange?.(nextOpen)\n },\n [isControlled, onOpenChange]\n )\n\n const closeMenu = React.useCallback(() => {\n handleOpenChange(false)\n }, [handleOpenChange])\n\n const handleSelect = React.useCallback(\n (item: ContextualQuickActionItem, event?: Event) => {\n if (item.disabled) {\n event?.preventDefault()\n return\n }\n\n onSelect(item)\n closeMenu()\n },\n [closeMenu, onSelect]\n )\n\n const handleBrowseAll = React.useCallback(() => {\n onBrowseAll?.()\n closeMenu()\n }, [closeMenu, onBrowseAll])\n\n return (\n <div\n data-slot=\"contextual-quick-action-launcher\"\n className={cn(\"inline-flex items-center gap-2\", className)}\n >\n <DropdownMenu open={isOpen} onOpenChange={handleOpenChange}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"contextual-quick-action-trigger\"\n data-state={isOpen ? \"open\" : \"closed\"}\n className={cn(\n \"inline-flex h-8 items-center gap-2 rounded-lg border border-border bg-background py-1.5 pr-2.5 pl-2 text-xs font-medium text-foreground shadow-sm transition-colors hover:border-muted-foreground/40 hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n \"data-[state=open]:border-foreground data-[state=open]:bg-foreground data-[state=open]:text-background data-[state=open]:hover:bg-foreground\"\n )}\n >\n <span\n className={cn(\n \"inline-flex h-[18px] w-[18px] items-center justify-center rounded bg-foreground/[0.04]\",\n isOpen && \"bg-background/15\"\n )}\n >\n <Zap className=\"h-3 w-3\" strokeWidth={2} aria-hidden=\"true\" />\n </span>\n <span className=\"tracking-[-0.005em]\">Quick action</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" strokeWidth={2} aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n align={align}\n side=\"bottom\"\n sideOffset={6}\n className={cn(\n isCasePanel\n ? \"pointer-events-auto w-[432px] rounded-[13px] border border-border bg-popover p-2 text-[13px] shadow-[0_18px_44px_rgba(0,0,0,0.14),0_2px_10px_rgba(0,0,0,0.06)]\"\n : \"pointer-events-auto w-[308px] rounded-[10px] p-1.5 text-[12.5px] shadow-[0_12px_28px_rgba(0,0,0,0.12),0_2px_6px_rgba(0,0,0,0.04)]\"\n )}\n >\n <ContextualQuickActionContextLabel\n contextLabel={contextLabel}\n contextSecondary={contextSecondary}\n variant={variant}\n />\n\n {items.map((item) => (\n <DropdownMenuItem\n key={item.id}\n disabled={item.disabled}\n onSelect={(event) => handleSelect(item, event)}\n className={cn(\n isCasePanel\n ? \"grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\"\n : \"flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\",\n item.disabled && \"text-muted-foreground\"\n )}\n >\n <span\n data-slot=\"contextual-quick-action-item-icon\"\n className={cn(\n isCasePanel\n ? \"flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-[9px] border border-border/70 bg-muted/60 text-foreground [&_svg]:h-[18px] [&_svg]:w-[18px]\"\n : \"flex h-[26px] w-[26px] shrink-0 items-center justify-center rounded-md bg-secondary text-foreground\",\n item.disabled && \"opacity-60\"\n )}\n >\n {item.icon ?? <DefaultActionIcon />}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n isCasePanel\n ? \"block truncate text-[13.5px] font-semibold leading-tight text-current\"\n : \"block truncate text-[12.5px] font-medium leading-tight text-current\"\n )}\n >\n {item.label}\n </span>\n {item.description ? (\n <span\n className={cn(\n isCasePanel\n ? \"mt-1 block truncate text-[12px] leading-tight text-muted-foreground\"\n : \"mt-0.5 block truncate text-[11px] leading-tight text-muted-foreground\"\n )}\n >\n {item.description}\n </span>\n ) : null}\n </span>\n {item.disabled && item.disabledReason ? (\n <span\n data-slot=\"contextual-quick-action-item-disabled-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] italic text-muted-foreground/80\"\n )}\n >\n {item.disabledReason}\n </span>\n ) : item.meta ? (\n <span\n data-slot=\"contextual-quick-action-item-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] text-muted-foreground/80\"\n )}\n >\n {item.meta}\n </span>\n ) : null}\n </DropdownMenuItem>\n ))}\n\n <DropdownMenuSeparator className={cn(isCasePanel ? \"-mx-2 mt-1 mb-1\" : \"-mx-1.5 my-1\")} />\n <div\n className={cn(\n isCasePanel\n ? \"flex items-center justify-between whitespace-nowrap px-2.5 py-2 text-[13px] text-muted-foreground\"\n : \"flex items-center justify-between px-2 py-1.5 text-[11px] text-muted-foreground\"\n )}\n >\n <button\n type=\"button\"\n className={cn(\n isCasePanel\n ? \"inline-flex items-center gap-1 font-semibold text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n : \"inline-flex items-center gap-1 font-medium text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n )}\n onClick={handleBrowseAll}\n >\n Browse all actions\n </button>\n <span\n className={cn(\n isCasePanel\n ? \"inline-flex items-center rounded-[5px] border border-border bg-muted/50 px-1.5 py-0.5 font-mono text-[11px] font-medium leading-none text-muted-foreground\"\n : \"inline-flex items-center rounded border border-border px-1 py-0.5 text-[10px] font-medium leading-none text-muted-foreground\"\n )}\n >\n ⌘K\n </span>\n </div>\n </DropdownMenuContent>\n </DropdownMenu>\n\n {showHint ? (\n <span className=\"text-[11px] text-muted-foreground\">\n Or press{\" \"}\n <span className=\"inline-flex items-center rounded border border-border bg-muted/40 px-1 py-0.5 text-[10px] font-medium leading-none\">\n ⌘K\n </span>{\" \"}\n for all actions\n </span>\n ) : null}\n </div>\n )\n}\n\nexport { ContextualQuickActionContextLabel, ContextualQuickActionLauncher }\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/contextual-quick-action-launcher.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, Zap } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface ContextualQuickActionItem {\n id: string\n label: string\n description?: string\n icon?: React.ReactNode\n disabled?: boolean\n disabledReason?: string\n meta?: React.ReactNode\n /**\n * Optional stable test id stamped on the rendered menu item. Lets callers\n * preserve an existing testid when an action moves into the launcher (e.g.\n * the inbox `case-sync-button`).\n */\n testId?: string\n}\n\nexport type ContextualQuickActionLauncherVariant = \"default\" | \"case-panel\"\n\nexport interface ContextualQuickActionContextLabelProps {\n contextLabel: string\n contextSecondary?: string\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n}\n\nexport interface ContextualQuickActionLauncherProps {\n contextLabel: string\n contextSecondary?: string\n items: ContextualQuickActionItem[]\n onSelect: (item: ContextualQuickActionItem) => void\n onBrowseAll?: () => void\n showHint?: boolean\n align?: \"start\" | \"end\"\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\nfunction ContextualQuickActionContextLabel({\n contextLabel,\n contextSecondary,\n variant = \"default\",\n className,\n}: ContextualQuickActionContextLabelProps) {\n const isCasePanel = variant === \"case-panel\"\n\n return (\n <div\n data-slot=\"contextual-quick-action-context-label\"\n data-variant={variant}\n className={cn(\n isCasePanel\n ? \"-mx-2 -mt-2 mb-1 flex items-center gap-1.5 whitespace-nowrap border-b px-3 py-2.5 text-[13px] text-muted-foreground\"\n : \"-mx-1 -mt-1 mb-1 flex items-center gap-1.5 border-b px-3 py-2 text-[11px] text-muted-foreground\",\n className\n )}\n >\n <Zap\n className={cn(isCasePanel ? \"h-3.5 w-3.5 shrink-0\" : \"h-3 w-3 shrink-0\")}\n strokeWidth={2.25}\n aria-hidden=\"true\"\n />\n <span>Acting on</span>\n <strong\n className={cn(\n isCasePanel\n ? \"max-w-[260px] truncate font-semibold text-foreground\"\n : \"max-w-[180px] truncate font-semibold text-foreground\"\n )}\n >\n {contextLabel}\n </strong>\n {contextSecondary ? (\n <span className=\"min-w-0 truncate text-muted-foreground\">\n · {contextSecondary}\n </span>\n ) : null}\n </div>\n )\n}\n\nfunction DefaultActionIcon() {\n return <Zap className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n}\n\nfunction ContextualQuickActionLauncher({\n contextLabel,\n contextSecondary,\n items,\n onSelect,\n onBrowseAll,\n showHint = false,\n align = \"start\",\n variant = \"default\",\n className,\n open,\n defaultOpen,\n onOpenChange,\n}: ContextualQuickActionLauncherProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false)\n const isCasePanel = variant === \"case-panel\"\n const isControlled = open !== undefined\n const isOpen = isControlled ? open : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(nextOpen)\n }\n onOpenChange?.(nextOpen)\n },\n [isControlled, onOpenChange]\n )\n\n const closeMenu = React.useCallback(() => {\n handleOpenChange(false)\n }, [handleOpenChange])\n\n const handleSelect = React.useCallback(\n (item: ContextualQuickActionItem, event?: Event) => {\n if (item.disabled) {\n event?.preventDefault()\n return\n }\n\n onSelect(item)\n closeMenu()\n },\n [closeMenu, onSelect]\n )\n\n const handleBrowseAll = React.useCallback(() => {\n onBrowseAll?.()\n closeMenu()\n }, [closeMenu, onBrowseAll])\n\n return (\n <div\n data-slot=\"contextual-quick-action-launcher\"\n className={cn(\"inline-flex items-center gap-2\", className)}\n >\n <DropdownMenu open={isOpen} onOpenChange={handleOpenChange}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"contextual-quick-action-trigger\"\n data-state={isOpen ? \"open\" : \"closed\"}\n className={cn(\n \"inline-flex h-8 items-center gap-2 rounded-lg border border-border bg-background py-1.5 pr-2.5 pl-2 text-xs font-medium text-foreground shadow-sm transition-colors hover:border-muted-foreground/40 hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n \"data-[state=open]:border-foreground data-[state=open]:bg-foreground data-[state=open]:text-background data-[state=open]:hover:bg-foreground\"\n )}\n >\n <span\n className={cn(\n \"inline-flex h-[18px] w-[18px] items-center justify-center rounded bg-foreground/[0.04]\",\n isOpen && \"bg-background/15\"\n )}\n >\n <Zap className=\"h-3 w-3\" strokeWidth={2} aria-hidden=\"true\" />\n </span>\n <span className=\"tracking-[-0.005em]\">Quick action</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" strokeWidth={2} aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n align={align}\n side=\"bottom\"\n sideOffset={6}\n className={cn(\n isCasePanel\n ? \"pointer-events-auto w-[432px] rounded-[13px] border border-border bg-popover p-2 text-[13px] shadow-[0_18px_44px_rgba(0,0,0,0.14),0_2px_10px_rgba(0,0,0,0.06)]\"\n : \"pointer-events-auto w-[308px] rounded-[10px] p-1.5 text-[12.5px] shadow-[0_12px_28px_rgba(0,0,0,0.12),0_2px_6px_rgba(0,0,0,0.04)]\"\n )}\n >\n <ContextualQuickActionContextLabel\n contextLabel={contextLabel}\n contextSecondary={contextSecondary}\n variant={variant}\n />\n\n {items.map((item) => (\n <DropdownMenuItem\n key={item.id}\n disabled={item.disabled}\n data-testid={item.testId}\n onSelect={(event) => handleSelect(item, event)}\n className={cn(\n isCasePanel\n ? \"grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\"\n : \"flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\",\n item.disabled && \"text-muted-foreground\"\n )}\n >\n <span\n data-slot=\"contextual-quick-action-item-icon\"\n className={cn(\n isCasePanel\n ? \"flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-[9px] border border-border/70 bg-muted/60 text-foreground [&_svg]:h-[18px] [&_svg]:w-[18px]\"\n : \"flex h-[26px] w-[26px] shrink-0 items-center justify-center rounded-md bg-secondary text-foreground\",\n item.disabled && \"opacity-60\"\n )}\n >\n {item.icon ?? <DefaultActionIcon />}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n isCasePanel\n ? \"block truncate text-[13.5px] font-semibold leading-tight text-current\"\n : \"block truncate text-[12.5px] font-medium leading-tight text-current\"\n )}\n >\n {item.label}\n </span>\n {item.description ? (\n <span\n className={cn(\n isCasePanel\n ? \"mt-1 block truncate text-[12px] leading-tight text-muted-foreground\"\n : \"mt-0.5 block truncate text-[11px] leading-tight text-muted-foreground\"\n )}\n >\n {item.description}\n </span>\n ) : null}\n </span>\n {item.disabled && item.disabledReason ? (\n <span\n data-slot=\"contextual-quick-action-item-disabled-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] italic text-muted-foreground/80\"\n )}\n >\n {item.disabledReason}\n </span>\n ) : item.meta ? (\n <span\n data-slot=\"contextual-quick-action-item-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] text-muted-foreground/80\"\n )}\n >\n {item.meta}\n </span>\n ) : null}\n </DropdownMenuItem>\n ))}\n\n <DropdownMenuSeparator className={cn(isCasePanel ? \"-mx-2 mt-1 mb-1\" : \"-mx-1.5 my-1\")} />\n <div\n className={cn(\n isCasePanel\n ? \"flex items-center justify-between whitespace-nowrap px-2.5 py-2 text-[13px] text-muted-foreground\"\n : \"flex items-center justify-between px-2 py-1.5 text-[11px] text-muted-foreground\"\n )}\n >\n <button\n type=\"button\"\n className={cn(\n isCasePanel\n ? \"inline-flex items-center gap-1 font-semibold text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n : \"inline-flex items-center gap-1 font-medium text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n )}\n onClick={handleBrowseAll}\n >\n Browse all actions\n </button>\n <span\n className={cn(\n isCasePanel\n ? \"inline-flex items-center rounded-[5px] border border-border bg-muted/50 px-1.5 py-0.5 font-mono text-[11px] font-medium leading-none text-muted-foreground\"\n : \"inline-flex items-center rounded border border-border px-1 py-0.5 text-[10px] font-medium leading-none text-muted-foreground\"\n )}\n >\n ⌘K\n </span>\n </div>\n </DropdownMenuContent>\n </DropdownMenu>\n\n {showHint ? (\n <span className=\"text-[11px] text-muted-foreground\">\n Or press{\" \"}\n <span className=\"inline-flex items-center rounded border border-border bg-muted/40 px-1 py-0.5 text-[10px] font-medium leading-none\">\n ⌘K\n </span>{\" \"}\n for all actions\n </span>\n ) : null}\n </div>\n )\n}\n\nexport { ContextualQuickActionContextLabel, ContextualQuickActionLauncher }\n"],"mappings":"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","names":[]}
|
|
@@ -12,7 +12,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
12
12
|
*/
|
|
13
13
|
type PillStatus = "success" | "warning" | "error" | "neutral" | "info";
|
|
14
14
|
declare const pillVariants: (props?: ({
|
|
15
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "
|
|
15
|
+
variant?: "error" | "default" | "secondary" | "destructive" | "outline" | "ghost" | "neutral" | "info" | "success" | "warning" | null | undefined;
|
|
16
16
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
17
|
interface PillProps extends React.ComponentProps<"span">, VariantProps<typeof pillVariants> {
|
|
18
18
|
}
|
|
@@ -5,7 +5,7 @@ import { Tabs as Tabs$1 } from 'radix-ui';
|
|
|
5
5
|
|
|
6
6
|
declare function Tabs({ className, orientation, ...props }: React.ComponentProps<typeof Tabs$1.Root>): React.JSX.Element;
|
|
7
7
|
declare const tabsListVariants: (props?: ({
|
|
8
|
-
variant?: "
|
|
8
|
+
variant?: "line" | "default" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare function TabsList({ className, variant, ...props }: React.ComponentProps<typeof Tabs$1.List> & VariantProps<typeof tabsListVariants>): React.JSX.Element;
|
|
11
11
|
declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof Tabs$1.Trigger>): React.JSX.Element;
|
package/package.json
CHANGED
|
@@ -179,6 +179,20 @@ describe("ContextualQuickActionLauncher", () => {
|
|
|
179
179
|
expect(onOpenChange).toHaveBeenCalledWith(false)
|
|
180
180
|
})
|
|
181
181
|
|
|
182
|
+
it("stamps an optional per-item testId on the rendered menu item", () => {
|
|
183
|
+
render(
|
|
184
|
+
<ContextualQuickActionLauncher
|
|
185
|
+
contextLabel="Acme Corp"
|
|
186
|
+
items={[{ id: "sync-work-item", label: "Sync", testId: "case-sync-button" }]}
|
|
187
|
+
onSelect={() => {}}
|
|
188
|
+
open
|
|
189
|
+
/>,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
expect(screen.getByTestId("case-sync-button")).toBeTruthy()
|
|
193
|
+
expect(screen.getByTestId("case-sync-button").textContent).toContain("Sync")
|
|
194
|
+
})
|
|
195
|
+
|
|
182
196
|
it("renders optional hint outside the menu", () => {
|
|
183
197
|
render(
|
|
184
198
|
<ContextualQuickActionLauncher
|
|
@@ -20,6 +20,12 @@ export interface ContextualQuickActionItem {
|
|
|
20
20
|
disabled?: boolean
|
|
21
21
|
disabledReason?: string
|
|
22
22
|
meta?: React.ReactNode
|
|
23
|
+
/**
|
|
24
|
+
* Optional stable test id stamped on the rendered menu item. Lets callers
|
|
25
|
+
* preserve an existing testid when an action moves into the launcher (e.g.
|
|
26
|
+
* the inbox `case-sync-button`).
|
|
27
|
+
*/
|
|
28
|
+
testId?: string
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export type ContextualQuickActionLauncherVariant = "default" | "case-panel"
|
|
@@ -193,6 +199,7 @@ function ContextualQuickActionLauncher({
|
|
|
193
199
|
<DropdownMenuItem
|
|
194
200
|
key={item.id}
|
|
195
201
|
disabled={item.disabled}
|
|
202
|
+
data-testid={item.testId}
|
|
196
203
|
onSelect={(event) => handleSelect(item, event)}
|
|
197
204
|
className={cn(
|
|
198
205
|
isCasePanel
|