UNPKG

@aircall/blocks

Version:

Aircall Blocks — higher-level UI compositions built on @aircall/ds

179 lines (147 loc) 5.72 kB
--- name: aircall-blocks/migrate-dashboard/info-popup description: > Migrate @dashboard/library InfoPopup (and the aw-web shared info-popup) to @aircall/ds HoverCard. Load when a file imports InfoPopup, InfoPopupTrigger, or InfoPopupContent from @dashboard/library or from the local shared info-popup. type: sub-skill library: aircall-blocks requires: - aircall-blocks/setup - aircall-blocks/migrate-dashboard sources: - "aircall/hydra:packages/ds/src/components/hover-card.tsx" - "aircall/hydra:packages/ds/src/index.ts" --- This skill builds on aircall-blocks/migrate-dashboard. ## 1. Component mapping | `@dashboard/library` / shared | `@aircall/ds` | | --- | --- | | `InfoPopup` (root) | `HoverCard` | | `InfoPopupTrigger` | `HoverCardTrigger` | | `InfoPopupContent` | `HoverCardContent` | `HoverCard` is built on Base UI `PreviewCard`. The hover-grace period (cursor can move from trigger into content without the card closing) is handled natively — no manual timer cancellation needed. ## 2. Verified DS exports (`packages/ds/src/index.ts`) ``` HoverCard, HoverCardTrigger, HoverCardContent ``` ## 3. Imports ```tsx import { HoverCard, HoverCardContent, HoverCardTrigger } from '@aircall/ds'; ``` Icons (the trigger is usually an info icon): ```tsx import { Info } from '@aircall/react-icons'; ``` ## 4. Prop mapping ### Root (`InfoPopup``HoverCard`) | `InfoPopup` prop | `HoverCard` equivalent | Notes | | --- | --- | --- | | `side` | `<HoverCardContent side>` | Moved from root to content | | `align` | `<HoverCardContent align>` | Moved from root to content | | `mouseEnterDelay` | `<HoverCardTrigger delay>` | Default changes: 0 ms → 400 ms | | `mouseLeaveDelay` | `<HoverCardTrigger closeDelay>` | Default changes: 100 ms → 300 ms | | `open` | `<HoverCard open>` | Same | | `defaultOpen` | `<HoverCard defaultOpen>` | Same | | `onOpenChange` | `<HoverCard onOpenChange>` | Same | | `data-test` | standard `data-test` on trigger or content | Pass directly to the element | ### Trigger (`InfoPopupTrigger``HoverCardTrigger`) `InfoPopupTrigger` rendered children directly. `HoverCardTrigger` renders an `<a>` by default — swap it for any element via the `render` prop: ```tsx // info icon trigger <HoverCardTrigger render={<span className="inline-flex cursor-help" />}> <Info size={16} /> </HoverCardTrigger> // button trigger <HoverCardTrigger render={<Button variant="ghost" size="sm" />}> Contact insights <Info /> </HoverCardTrigger> ``` ### Content (`InfoPopupContent``HoverCardContent`) `InfoPopupContent` accepted Tractor `BoxProps` (`maxW`, `p`, `backgroundColor`, `borderRadius`, `boxShadow`). `HoverCardContent` is a plain `<div>` — use Tailwind: | Old Tractor prop | Tailwind equivalent | | --- | --- | | `maxW="280px"` | `className="max-w-[280px]"` | | `p="m"` | `className="p-4"` | | `backgroundColor="surface-default"` | default `bg-popover` (no change needed) | | `boxShadow={1}` | default `shadow-md` (no change needed) | | `borderRadius="sm"` | default `rounded-md` (no change needed) | ## 5. Before / After examples ### 5a. Basic icon trigger **Before (`@dashboard/library` / shared):** ```tsx import { InfoPopup, InfoPopupTrigger, InfoPopupContent } from '@/components/shared/info-popup'; import { Icon } from '@aircall/tractor'; import { InformationOutlined } from '@aircall/icons'; <InfoPopup side="top" align="center"> <InfoPopupTrigger> <Icon component={InformationOutlined} size={16} /> </InfoPopupTrigger> <InfoPopupContent> <p>Contextual help text.</p> </InfoPopupContent> </InfoPopup> ``` **After (`@aircall/ds`):** ```tsx import { HoverCard, HoverCardContent, HoverCardTrigger } from '@aircall/ds'; import { Info } from '@aircall/react-icons'; <HoverCard> <HoverCardTrigger render={<span className="inline-flex cursor-help" />}> <Info size={16} className="text-muted-foreground" /> </HoverCardTrigger> <HoverCardContent side="top" align="center"> <p>Contextual help text.</p> </HoverCardContent> </HoverCard> ``` ### 5b. With custom open delay (ContactInsightsWrapper pattern) **Before:** ```tsx <InfoPopup side="left" align="start" mouseEnterDelay={400}> <InfoPopupTrigger> <Flex gap="xxs" cursor="help"> <Typography variant="supportingSemiboldS">{title}</Typography> <Icon component={InformationOutlined} size={16} /> </Flex> </InfoPopupTrigger> <InfoPopupContent maxW="280px"> <Flex flexDirection="column" gap="xs"> <Typography variant="headingBoldXS">{heading}</Typography> <Typography variant="bodyRegularS">{body}</Typography> </Flex> </InfoPopupContent> </InfoPopup> ``` **After:** ```tsx <HoverCard> <HoverCardTrigger delay={400} closeDelay={100} render={<Button variant="ghost" size="sm" />} > {title} <Info /> </HoverCardTrigger> <HoverCardContent side="left" align="start" className="max-w-[280px]"> <div className="flex flex-col gap-2"> <p className="text-xs font-bold">{heading}</p> <p className="text-xs text-muted-foreground">{body}</p> </div> </HoverCardContent> </HoverCard> ``` ## 6. Key behaviour differences - **Hover grace** — moving the cursor from trigger into content keeps the card open natively. `InfoPopup` required manual timer cancellation. - **Focus triggers** — `HoverCard` also opens on keyboard focus (Tab); `InfoPopup` was hover-only. - **No provider needed** — neither `InfoPopup` nor `HoverCard` requires a provider. - **Default delays** — `HoverCardTrigger` defaults to `delay=400` / `closeDelay=300`. If you need the original `InfoPopup` behaviour (instant open, 100 ms close), pass `delay={0} closeDelay={100}` explicitly.