Repository: melt-ui/melt-ui
Pull request #408 (2023-08-18 11:27:34)

Link Preview
As discussed in #399 heres a list of things to do
- [x] Change HoverCard's name to LinkPreview
- [x] Document how to cancel focus behaviour, and modify previous callout that stated it didn't work on focus
- [x] Document that LinkPreview should only contain non-vital info
- [x] Fix the bug #398

let me know if you'd rather I do them in separate PRs or all in this one.


Patch:
diff --git a/.changeset/late-fishes-allow.md b/.changeset/late-fishes-allow.md
new file mode 100644
index 00000000..1da38082
--- /dev/null
+++ b/.changeset/late-fishes-allow.md
@@ -0,0 +1,5 @@
+---
+'@melt-ui/svelte': minor
+---
+
+Rename Hover Card to Link Preview
diff --git a/README.md b/README.md
index 887fbf8a..8348e51f 100644
--- a/README.md
+++ b/README.md
@@ -87,8 +87,8 @@ we'd love to have you there.
 | Dialog            | ✅     |
 | Dropdown Menu     | ✅     |
 | Dropzone          |        |
-| Hover Card        | ✅     |
 | Label             | ✅     |
+| Link Preview      | ✅     |
 | Menubar           | ✅     |
 | Navigation Menu   |        |
 | Pagination        | ✅     |
diff --git a/src/docs/content/builders/hover-card.md b/src/docs/content/builders/hover-card.md
deleted file mode 100644
index 6349576f..00000000
--- a/src/docs/content/builders/hover-card.md
+++ /dev/null
@@ -1,66 +0,0 @@
----
-title: Hover Card
-description: Enable sighted users to preview content behind a link.
----
-
-<script>
-    import { KbdTable, APIReference, Callout } from '$docs/components'
-    export let schemas
-</script>
-
-<Callout type="warning">
-
-⚠️ The Hover Card is only intended to be used with a mouse or other pointing device and doesn't
-respond to keyboard or touch events. On touch devices, the link will be followed immediately.
-
-</Callout>
-
-## Anatomy
-
-- **Trigger**: The element that opens the hovercard on hover.
-- **Content**: The element containing the content for the hovercard.
-- **Arrow**: An optional arrow component
-
-## Usage
-
-Create a hovercard using the `createHoverCard` builder function.
-
-```svelte {3-5}
-<script lang="ts">
-	import { createHoverCard, melt } from '@melt-ui/svelte'
-	const {
-		elements: { trigger, content, arrow }
-	} = createHoverCard()
-</script>
-```
-
-Then you can use the `trigger`, `content`, and `arrow` to construct a hovercard. A high level
-example of how to structure the hovercard is shown below.
-
-```svelte
-<script lang="ts">
-	import { createHoverCard, melt } from '@melt-ui/svelte'
-	const {
-		elements: { trigger, content, arrow }
-	} = createHoverCard()
-</script>
-
-<button use:melt={$trigger}>Hover Me</button>
-
-<div use:melt={$content}>
-	<div>I am content inside the hover card</div>
-	<div use:melt={$arrow} />
-</div>
-```
-
-It's also possible to use Svelte Transitions, as demonstrated in the example at the top of this
-page.
-
-## API Reference
-
-<APIReference {schemas} />
-
-## Accessibility
-
-The hover card is only intended to be used with a mouse or other pointing device and doesn't respond
-to keyboard events.
diff --git a/src/docs/content/builders/link-preview.md b/src/docs/content/builders/link-preview.md
new file mode 100644
index 00000000..ea3c325d
--- /dev/null
+++ b/src/docs/content/builders/link-preview.md
@@ -0,0 +1,90 @@
+---
+title: Link Preview
+description: Enable sighted users to preview content behind a link.
+---
+
+<script>
+    import { KbdTable, APIReference, Callout } from '$docs/components'
+    export let schemas
+</script>
+
+<Callout type="warning">
+
+⚠️ The Link Preview is only intended to be used with a mouse or other pointing device. It doesn't
+respond to touch events, and the preview content cannot be accessed via the keyboard. On touch
+devices, the link will be followed immediately. As it is not accessible to all users, the preview
+should not contain vital information.
+
+</Callout>
+
+## Anatomy
+
+- **Trigger**: The element that opens the link preview on hover.
+- **Content**: The element containing the content for the link preview.
+- **Arrow**: An optional arrow component
+
+## Usage
+
+Create a link preview using the `createLinkPreview` builder function.
+
+```svelte {3-5}
+<script lang="ts">
+	import { createLinkPreview, melt } from '@melt-ui/svelte'
+	const {
+		elements: { trigger, content, arrow }
+	} = createLinkPreview()
+</script>
+```
+
+Then you can use the `trigger`, `content`, and `arrow` to construct a link preview. A high level
+example of how to structure the link preview is shown below.
+
+```svelte
+<script lang="ts">
+	import { createLinkPreview, melt } from '@melt-ui/svelte'
+	const {
+		elements: { trigger, content, arrow }
+	} = createLinkPreview()
+</script>
+
+<button use:melt={$trigger}>Hover Me</button>
+
+<div use:melt={$content}>
+	<div>I am content inside the link preview</div>
+	<div use:melt={$arrow} />
+</div>
+```
+
+It's also possible to use Svelte Transitions, as demonstrated in the example at the top of this
+page.
+
+### Focus handling
+
+By default the link preview will be shown when focused by the keyboard (or more specifically if
+`:focus-visible` would apply), and hide on blur.
+
+You can disable this functionality by listening to and cancelling the custom `m-focus` and `m-blur`
+events.
+
+```svelte {3-8}
+<button
+	use:melt={$trigger}
+	on:m-focus={(e) => {
+		e.preventDefault()
+	}}
+	on:m-blur={(e) => {
+		e.preventDefault()
+	}}>
+	Hover Me
+</button>
+```
+
+## API Reference
+
+<APIReference {schemas} />
+
+## Accessibility
+
+The link preview is only intended to be used with a mouse or other pointing device and the preview
+content cannot be accessed via the keyboard and is not exposed to screen readers. As a result the
+preview should contain content that is otherwise not visible elswhere.
diff --git a/src/docs/data/builders/index.ts b/src/docs/data/builders/index.ts
index 37a4dd57..3411a0c1 100644
--- a/src/docs/data/builders/index.ts
+++ b/src/docs/data/builders/index.ts
@@ -7,8 +7,8 @@ import { comboboxData } from './combobox.js';
 import { contextMenuData } from './context-menu.js';
 import { dialogData } from './dialog.js';
 import { dropdownMenuData } from './dropdown-menu.js';
-import { hoverCardData } from './hover-card.js';
 import { labelData } from './label.js';
+import { linkPreviewData } from './link-preview.js';
 import { menubarData } from './menubar.js';
 import { paginationData } from './pagination.js';
 import { pinInputData } from './pin-input.js';
@@ -37,8 +37,8 @@ export const builderList = [
 	'context-menu',
 	'dialog',
 	'dropdown-menu',
-	'hover-card',
 	'label',
+	'link-preview',
 	'menubar',
 	'pagination',
 	'pin-input',
@@ -82,8 +82,8 @@ export const data: Builders = {
 	'context-menu': contextMenuData,
 	dialog: dialogData,
 	'dropdown-menu': dropdownMenuData,
-	'hover-card': hoverCardData,
 	label: labelData,
+	'link-preview': linkPreviewData,
 	menubar: menubarData,
 	pagination: paginationData,
 	'pin-input': pinInputData,
diff --git a/src/docs/data/builders/hover-card.ts b/src/docs/data/builders/link-preview.ts
similarity index 63%
rename from src/docs/data/builders/hover-card.ts
rename to src/docs/data/builders/link-preview.ts
index b38744d1..7a15b010 100644
--- a/src/docs/data/builders/hover-card.ts
+++ b/src/docs/data/builders/link-preview.ts
@@ -1,6 +1,6 @@
 import { ATTRS, PROPS } from '$docs/constants.js';
 import { builderSchema, elementSchema } from '$docs/utils/index.js';
-import { hoverCardEvents } from '$lib/builders/hover-card/events.js';
+import { linkPreviewEvents } from '$lib/builders/link-preview/events.js';
 import type { BuilderData } from './index.js';
 import { getMenuArrowSchema } from './menu.js';
 
@@ -12,13 +12,13 @@ const OPTION_PROPS = [
 		name: 'openDelay',
 		type: 'number',
 		default: '700',
-		description: 'The delay in milliseconds before the hover card opens.',
+		description: 'The delay in milliseconds before the link preview opens.',
 	},
 	{
 		name: 'closeDelay',
 		type: 'number',
 		default: '300',
-		description: 'The delay in milliseconds before the hover card closes.',
+		description: 'The delay in milliseconds before the link preview closes.',
 	},
 	PROPS.POSITIONING({ default: "placement: 'bottom'" }),
 	PROPS.ARROW_SIZE,
@@ -28,63 +28,63 @@ const OPTION_PROPS = [
 	PROPS.PORTAL,
 ];
 
-const BUILDER_NAME = 'hover card';
+const BUILDER_NAME = 'link preview';
 
 const builder = builderSchema(BUILDER_NAME, {
-	title: 'createHoverCard',
+	title: 'createLinkPreview',
 	props: [...OPTION_PROPS, PROPS.DEFAULT_OPEN, PROPS.OPEN, PROPS.ON_OPEN_CHANGE],
 	elements: [
 		{
 			name: 'trigger',
-			description: 'The builder store used to create the hover card trigger.',
+			description: 'The builder store used to create the link preview trigger.',
 		},
 		{
 			name: 'content',
-			description: 'The builder store used to create the hover card content.',
+			description: 'The builder store used to create the link preview content.',
 		},
 		{
 			name: 'arrow',
-			description: 'The builder store used to create the hover card arrow.',
+			description: 'The builder store used to create the link preview arrow.',
 		},
 	],
 	states: [
 		{
 			name: 'open',
 			type: 'Writable<boolean>',
-			description: 'A writable store with the open state of the hover card.',
+			description: 'A writable store with the open state of the link preview.',
 		},
 	],
 	options: OPTION_PROPS,
 });
 
 const trigger = elementSchema('trigger', {
-	description: 'The hover card trigger element.',
+	description: 'The link preview trigger element.',
 	dataAttributes: [
 		{
 			name: 'data-state',
 			value: ATTRS.OPEN_CLOSED,
 		},
 		{
-			name: 'data-melt-hover-card-trigger',
+			name: 'data-melt-link-preview-trigger',
 			value: ATTRS.MELT('trigger'),
 		},
 	],
-	events: hoverCardEvents['trigger'],
+	events: linkPreviewEvents['trigger'],
 });
 
 const content = elementSchema('content', {
-	description: 'The content displayed in the hovercard',
+	description: 'The content displayed in the linkpreview',
 	dataAttributes: [
 		{
 			name: 'data-state',
 			value: ATTRS.OPEN_CLOSED,
 		},
 		{
-			name: 'data-melt-hover-card-content',
+			name: 'data-melt-link-preview-content',
 			value: ATTRS.MELT('content'),
 		},
 	],
-	events: hoverCardEvents['content'],
+	events: linkPreviewEvents['content'],
 });
 
 const arrow = getMenuArrowSchema(BUILDER_NAME);
@@ -98,7 +98,7 @@ const features = [
 	'Positioning and alignment customization',
 ];
 
-export const hoverCardData: BuilderData = {
+export const linkPreviewData: BuilderData = {
 	schemas,
 	features,
 };
diff --git a/src/docs/previews/hover-card/main/tailwind/index.svelte b/src/docs/previews/link-preview/main/tailwind/index.svelte
similarity index 95%
rename from src/docs/previews/hover-card/main/tailwind/index.svelte
rename to src/docs/previews/link-preview/main/tailwind/index.svelte
index ed10cb8d..6846274a 100644
--- a/src/docs/previews/hover-card/main/tailwind/index.svelte
+++ b/src/docs/previews/link-preview/main/tailwind/index.svelte
@@ -1,11 +1,11 @@
 <script lang="ts">
-	import { createHoverCard, melt } from '$lib/index.js';
+	import { createLinkPreview, melt } from '$lib/index.js';
 	import { fly } from 'svelte/transition';
 
 	const {
 		elements: { trigger, content, arrow },
 		states: { open },
-	} = createHoverCard({
+	} = createLinkPreview({
 		forceVisible: true,
 	});
 </script>
diff --git a/src/lib/builders/hover-card/events.ts b/src/lib/builders/hover-card/events.ts
deleted file mode 100644
index ee671938..00000000
--- a/src/lib/builders/hover-card/events.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import type { GroupedEvents, MeltComponentEvents } from '$lib/internal/types.js';
-
-export const hoverCardEvents = {
-	trigger: ['pointerenter', 'pointerleave', 'focus', 'blur', 'touchstart'] as const,
-	content: ['pointerdown', 'pointerenter', 'pointerleave', 'focusout'] as const,
-};
-
-export type HoverCardEvents = GroupedEvents<typeof hoverCardEvents>;
-export type HoverCardComponentEvents = MeltComponentEvents<HoverCardEvents>;
diff --git a/src/lib/builders/index.ts b/src/lib/builders/index.ts
index 2712f460..0c392ce2 100644
--- a/src/lib/builders/index.ts
+++ b/src/lib/builders/index.ts
@@ -6,7 +6,7 @@ export * from './combobox/index.js';
 export * from './context-menu/index.js';
 export * from './dialog/index.js';
 export * from './dropdown-menu/index.js';
-export * from './hover-card/index.js';
+export * from './link-preview/index.js';
 export * from './label/index.js';
 export * from './menubar/index.js';
 export * from './pagination/index.js';
diff --git a/src/lib/builders/hover-card/create.ts b/src/lib/builders/link-preview/create.ts
similarity index 90%
rename from src/lib/builders/hover-card/create.ts
rename to src/lib/builders/link-preview/create.ts
index 28c6e16b..dec94f7b 100644
--- a/src/lib/builders/hover-card/create.ts
+++ b/src/lib/builders/link-preview/create.ts
@@ -10,6 +10,8 @@ import {
 	getPortalDestination,
 	getTabbableNodes,
 	isBrowser,
+	isElement,
+	isFocusVisible,
 	isHTMLElement,
 	isTouch,
 	noop,
@@ -21,11 +23,11 @@ import {
 import type { MeltActionReturn } from '$lib/internal/types.js';
 import { onMount, tick } from 'svelte';
 import { derived, get, writable, type Readable } from 'svelte/store';
-import type { HoverCardEvents } from './events.js';
-import type { CreateHoverCardProps } from './types.js';
+import type { LinkPreviewEvents } from './events.js';
+import type { CreateLinkPreviewProps } from './types.js';
 
-type HoverCardParts = 'trigger' | 'content' | 'arrow';
-const { name } = createElHelpers<HoverCardParts>('hover-card');
+type LinkPreviewParts = 'trigger' | 'content' | 'arrow';
+const { name } = createElHelpers<LinkPreviewParts>('hover-card');
 
 const defaults = {
 	defaultOpen: false,
@@ -39,10 +41,10 @@ const defaults = {
 	forceVisible: false,
 	portal: 'body',
 	closeOnEscape: true,
-} satisfies CreateHoverCardProps;
+} satisfies CreateLinkPreviewProps;
 
-export function createHoverCard(props: CreateHoverCardProps = {}) {
-	const withDefaults = { ...defaults, ...props } satisfies CreateHoverCardProps;
+export function createLinkPreview(props: CreateLinkPreviewProps = {}) {
+	const withDefaults = { ...defaults, ...props } satisfies CreateLinkPreviewProps;
 
 	const openWritable = withDefaults.open ?? writable(withDefaults.defaultOpen);
 	const open = overridable(openWritable, withDefaults?.onOpenChange);
@@ -114,7 +116,7 @@ export function createHoverCard(props: CreateHoverCardProps = {}) {
 				id: ids.trigger,
 			};
 		},
-		action: (node: HTMLElement): MeltActionReturn<HoverCardEvents['trigger']> => {
+		action: (node: HTMLElement): MeltActionReturn<LinkPreviewEvents['trigger']> => {
 			const unsub = executeCallbacks(
 				addMeltEventListener(node, 'pointerenter', (e) => {
 					if (isTouch(e)) return;
@@ -124,16 +126,11 @@ export function createHoverCard(props: CreateHoverCardProps = {}) {
 					if (isTouch(e)) return;
 					get(handleClose)();
 				}),
-				addMeltEventListener(node, 'focus', () => get(handleOpen)()),
-
-				addMeltEventListener(node, 'blur', () => get(handleClose)()),
-				addMeltEventListener(node, 'touchstart', (e) => {
-					// prevent focus on touch devices
-					e.preventDefault();
-					const currentTarget = e.currentTarget;
-					if (!isHTMLElement(currentTarget)) return;
-					currentTarget.click();
-				})
+				addMeltEventListener(node, 'focus', (e) => {
+					if (!isElement(e.currentTarget) || !isFocusVisible(e.currentTarget)) return;
+					get(handleOpen)();
+				}),
+				addMeltEventListener(node, 'blur', () => get(handleClose)())
 			);
 
 			return {
@@ -160,7 +157,7 @@ export function createHoverCard(props: CreateHoverCardProps = {}) {
 				'data-portal': $portal ? '' : undefined,
 			};
 		},
-		action: (node: HTMLElement): MeltActionReturn<HoverCardEvents['content']> => {
+		action: (node: HTMLElement): MeltActionReturn<LinkPreviewEvents['content']> => {
 			let unsub = noop;
 
 			const unsubTimers = () => {
diff --git a/src/lib/builders/link-preview/events.ts b/src/lib/builders/link-preview/events.ts
new file mode 100644
index 00000000..cb636d21
--- /dev/null
+++ b/src/lib/builders/link-preview/events.ts
@@ -0,0 +1,9 @@
+import type { GroupedEvents, MeltComponentEvents } from '$lib/internal/types.js';
+
+export const linkPreviewEvents = {
+	trigger: ['pointerenter', 'pointerleave', 'focus', 'blur'] as const,
+	content: ['pointerdown', 'pointerenter', 'pointerleave', 'focusout'] as const,
+};
+
+export type LinkPreviewEvents = GroupedEvents<typeof linkPreviewEvents>;
+export type LinkPreviewComponentEvents = MeltComponentEvents<LinkPreviewEvents>;
diff --git a/src/lib/builders/hover-card/index.ts b/src/lib/builders/link-preview/index.ts
similarity index 100%
rename from src/lib/builders/hover-card/index.ts
rename to src/lib/builders/link-preview/index.ts
diff --git a/src/lib/builders/hover-card/types.ts b/src/lib/builders/link-preview/types.ts
similarity index 67%
rename from src/lib/builders/hover-card/types.ts
rename to src/lib/builders/link-preview/types.ts
index 6cb79f0e..789c7de3 100644
--- a/src/lib/builders/hover-card/types.ts
+++ b/src/lib/builders/link-preview/types.ts
@@ -1,10 +1,10 @@
 import type { FloatingConfig } from '$lib/internal/actions/index.js';
 import type { BuilderReturn } from '$lib/internal/types.js';
 import type { Writable } from 'svelte/store';
-import type { createHoverCard } from './create.js';
+import type { createLinkPreview } from './create.js';
 import type { ChangeFn } from '$lib/internal/helpers/index.js';
-export type { HoverCardComponentEvents } from './events.js';
-export type CreateHoverCardProps = {
+export type { LinkPreviewComponentEvents } from './events.js';
+export type CreateLinkPreviewProps = {
 	/**
 	 * Options for positioning the popover menu.
 	 *
@@ -13,14 +13,14 @@ export type CreateHoverCardProps = {
 	positioning?: FloatingConfig;
 
 	/**
-	 * Whether or not the hovercard is open by default.
+	 * Whether or not the linkpreview is open by default.
 	 *
 	 * @default false
 	 */
 	defaultOpen?: boolean;
 
 	/**
-	 * A controlled open state store for the hovercard. If provided, the
+	 * A controlled open state store for the linkpreview. If provided, the
 	 * value of this store will override the `defaultOpen` prop.
 	 *
 	 * @see https://melt-ui.com/docs/controlled#bring-your-own-store
@@ -35,7 +35,7 @@ export type CreateHoverCardProps = {
 	onOpenChange?: ChangeFn<boolean>;
 
 	/**
-	 * The delay in milliseconds to hover before opening the hovercard
+	 * The delay in milliseconds to hover before opening the linkpreview
 	 *
 	 * @default 700
 	 */
@@ -43,14 +43,14 @@ export type CreateHoverCardProps = {
 
 	/**
 	 * The delay in milliseconds after the pointer leaves the
-	 * hovercard before closing it.
+	 * linkpreview before closing it.
 	 *
 	 * @default 300
 	 */
 	closeDelay?: number;
 
 	/**
-	 * Whether or not to close the hovercard when the pointer is clicked
+	 * Whether or not to close the linkpreview when the pointer is clicked
 	 * outside of it.
 	 *
 	 * @default true
@@ -58,7 +58,7 @@ export type CreateHoverCardProps = {
 	closeOnOutsideClick?: boolean;
 
 	/**
-	 * Whether or not to close the hovercard when the escape key is pressed
+	 * Whether or not to close the linkpreview when the escape key is pressed
 	 * while it is open.
 	 *
 	 * @default true
@@ -90,7 +90,7 @@ export type CreateHoverCardProps = {
 	portal?: HTMLElement | string | null;
 };
 
-export type HoverCard = BuilderReturn<typeof createHoverCard>;
-export type HoverCardElements = HoverCard['elements'];
-export type HoverCardOptions = HoverCard['options'];
-export type HoverCardStates = HoverCard['states'];
+export type LinkPreview = BuilderReturn<typeof createLinkPreview>;
+export type LinkPreviewElements = LinkPreview['elements'];
+export type LinkPreviewOptions = LinkPreview['options'];
+export type LinkPreviewStates = LinkPreview['states'];
diff --git a/src/lib/internal/helpers/is.ts b/src/lib/internal/helpers/is.ts
index 14aeea71..0ba61fc4 100644
--- a/src/lib/internal/helpers/is.ts
+++ b/src/lib/internal/helpers/is.ts
@@ -2,6 +2,10 @@ export const isBrowser = typeof document !== 'undefined';
 // eslint-disable-next-line @typescript-eslint/ban-types
 export const isFunction = (v: unknown): v is Function => typeof v === 'function';
 
+export function isElement(element: unknown): element is Element {
+	return element instanceof Element;
+}
+
 export function isHTMLElement(element: unknown): element is HTMLElement {
 	return element instanceof HTMLElement;
 }
@@ -29,3 +33,7 @@ export function isTouch(event: PointerEvent): boolean {
 export function isLeftClick(event: PointerEvent | MouseEvent): boolean {
 	return event.button === 0 && event.ctrlKey === false && event.metaKey === false;
 }
+
+export function isFocusVisible(element: Element): boolean {
+	return element.matches(':focus-visible');
+}


Test patch:
diff --git a/src/tests/hover-card/HoverCard.spec.ts b/src/tests/link-preview/LinkPreview.spec.ts
similarity index 84%
rename from src/tests/hover-card/HoverCard.spec.ts
rename to src/tests/link-preview/LinkPreview.spec.ts
index c15d92de..cbb6c746 100644
--- a/src/tests/hover-card/HoverCard.spec.ts
+++ b/src/tests/link-preview/LinkPreview.spec.ts
@@ -1,18 +1,18 @@
 import { render, waitFor } from '@testing-library/svelte';
 import { axe } from 'jest-axe';
-import HoverCardTest from './HoverCardTest.svelte';
+import LinkPreviewTest from './LinkPreviewTest.svelte';
 import userEvent from '@testing-library/user-event';
 import { kbd } from '$lib/internal/helpers/index.js';
 
-describe('HoverCard (Default)', () => {
+describe('LinkPreview (Default)', () => {
 	test('No accessibility violations', async () => {
-		const { container } = await render(HoverCardTest);
+		const { container } = await render(LinkPreviewTest);
 
 		expect(await axe(container)).toHaveNoViolations();
 	});
 
 	test('Opens on hover', async () => {
-		const { getByTestId } = await render(HoverCardTest);
+		const { getByTestId } = await render(LinkPreviewTest);
 		const user = userEvent.setup();
 		const trigger = getByTestId('trigger');
 		const content = getByTestId('content');
@@ -26,7 +26,7 @@ describe('HoverCard (Default)', () => {
 	});
 
 	test('Closes on escape', async () => {
-		const { getByTestId } = await render(HoverCardTest);
+		const { getByTestId } = await render(LinkPreviewTest);
 		const user = userEvent.setup();
 		const trigger = getByTestId('trigger');
 		const content = getByTestId('content');
@@ -42,7 +42,7 @@ describe('HoverCard (Default)', () => {
 	});
 
 	test('Closes when pointer moves outside', async () => {
-		const { getByTestId } = await render(HoverCardTest);
+		const { getByTestId } = await render(LinkPreviewTest);
 		const user = userEvent.setup();
 		const trigger = getByTestId('trigger');
 		const content = getByTestId('content');
diff --git a/src/tests/hover-card/HoverCardTest.svelte b/src/tests/link-preview/LinkPreviewTest.svelte
similarity index 95%
rename from src/tests/hover-card/HoverCardTest.svelte
rename to src/tests/link-preview/LinkPreviewTest.svelte
index 1089d252..6341958a 100644
--- a/src/tests/hover-card/HoverCardTest.svelte
+++ b/src/tests/link-preview/LinkPreviewTest.svelte
@@ -1,9 +1,9 @@
 <script lang="ts">
-	import { createHoverCard, melt } from '$lib/index.js';
+	import { createLinkPreview, melt } from '$lib/index.js';
 
 	const {
 		elements: { trigger, content, arrow },
-	} = createHoverCard({
+	} = createLinkPreview({
 		openDelay: 0,
 	});
 </script>
diff --git a/src/tests/popover/Popover.spec.ts b/src/tests/popover/Popover.spec.ts
index d028168c..8bd759af 100644
--- a/src/tests/popover/Popover.spec.ts
+++ b/src/tests/popover/Popover.spec.ts
@@ -4,7 +4,7 @@ import PopoverTest from './PopoverTest.svelte';
 import userEvent from '@testing-library/user-event';
 import { kbd } from '$lib/internal/helpers/index.js';
 
-describe('HoverCard (Default)', () => {
+describe('Popover (Default)', () => {
 	test('No accessibility violations', async () => {
 		const { container } = await render(PopoverTest);