Skip to content
This repository was archived by the owner on Oct 14, 2025. It is now read-only.

Commit 37718b4

Browse files
committed
focus manager tests wip
1 parent 5b19001 commit 37718b4

21 files changed

+855
-87
lines changed

packages/floating-ui-svelte/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@testing-library/svelte": "^5.2.6",
3939
"@testing-library/user-event": "^14.5.2",
4040
"csstype": "^3.1.3",
41+
"resize-observer-polyfill": "^1.5.1",
4142
"svelte": "^5.17.3",
4243
"typescript": "^5.7.2",
4344
"vite": "^6.0.6",

packages/floating-ui-svelte/src/components/floating-focus-manager/floating-focus-manager.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
);
187187
188188
const inertSupported = supportsInert();
189+
console.log("INERT SUPPORTED", inertSupported);
189190
const guards = $derived(inertSupported ? _guards : true);
190191
const useInert = $derived(
191192
!guards || (inertSupported && outsideElementsInert)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script lang="ts">
2+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
3+
import { useFloating } from "../../../../src/index.js";
4+
5+
let {
6+
outsideElementsInert = false,
7+
modal = true,
8+
}: { outsideElementsInert?: boolean; modal?: boolean } = $props();
9+
10+
let open = $state(false);
11+
const floating = useFloating({
12+
open: () => open,
13+
onOpenChange: (v) => {
14+
open = v;
15+
},
16+
});
17+
</script>
18+
19+
<!-- svelte-ignore a11y_role_has_required_aria_props -->
20+
<input
21+
role="combobox"
22+
data-testid="reference"
23+
bind:this={floating.reference}
24+
onfocus={() => (open = true)} />
25+
<button data-testid="btn-1">btn1</button>
26+
<button data-testid="btn-2">btn2</button>
27+
28+
{#if open}
29+
<FloatingFocusManager
30+
context={floating.context}
31+
{modal}
32+
order={["reference"]}
33+
{outsideElementsInert}>
34+
<div
35+
role="listbox"
36+
bind:this={floating.floating}
37+
data-testid="floating">
38+
</div>
39+
</FloatingFocusManager>
40+
{/if}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<script lang="ts">
2+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
3+
import FloatingPortal from "../../../../src/components/floating-portal/floating-portal.svelte";
4+
import {
5+
useClick,
6+
useFloating,
7+
useInteractions,
8+
} from "../../../../src/index.js";
9+
10+
let { modal = true }: { modal: boolean } = $props();
11+
12+
let open = $state(false);
13+
let removed = $state(false);
14+
15+
const floating = useFloating({
16+
open: () => open,
17+
onOpenChange: (v) => {
18+
open = v;
19+
},
20+
});
21+
22+
const ints = useInteractions([useClick(floating.context)]);
23+
</script>
24+
25+
{#if !removed}
26+
<button
27+
bind:this={floating.reference}
28+
{...ints.getReferenceProps()}
29+
data-testid="reference">reference</button>
30+
{/if}
31+
{#if open}
32+
<FloatingPortal>
33+
<FloatingFocusManager context={floating.context} {modal}>
34+
<div bind:this={floating.floating} {...ints.getFloatingProps()}>
35+
<button
36+
data-testid="remove"
37+
onclick={() => {
38+
removed = true;
39+
open = false;
40+
}}>
41+
remove
42+
</button>
43+
</div>
44+
</FloatingFocusManager>
45+
</FloatingPortal>
46+
{/if}
47+
<button data-testid="fallback">fallback</button>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script lang="ts">
2+
import Dialog from "./dialog.svelte";
3+
</script>
4+
5+
<Dialog>
6+
{#snippet reference(ref, props)}
7+
<div
8+
{...props}
9+
bind:this={ref.current}
10+
data-testid="non-focusable-reference">
11+
<button data-testid="open-dialog"> open dialog </button>
12+
</div>
13+
{/snippet}
14+
15+
{#snippet content(handleClose)}
16+
<button onclick={handleClose} data-testid="close-dialog">close</button>
17+
{/snippet}
18+
</Dialog>

packages/floating-ui-svelte/test/components/floating-focus-manager/dialog.svelte renamed to packages/floating-ui-svelte/test/components/floating-focus-manager/components/dialog.svelte

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<script lang="ts">
22
import type { Snippet } from "svelte";
3-
import FloatingNode from "../../../src/components/floating-tree/floating-node.svelte";
3+
import FloatingNode from "../../../../src/components/floating-tree/floating-node.svelte";
44
import {
55
useClick,
66
useDismiss,
77
useFloating,
88
useFloatingNodeId,
99
useInteractions,
1010
type Boxed,
11-
} from "../../../src/index.js";
12-
import { box } from "../../../src/internal/box.svelte.js";
13-
import FloatingPortal from "../../../src/components/floating-portal/floating-portal.svelte";
14-
import FloatingFocusManager from "../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
11+
} from "../../../../src/index.js";
12+
import { box } from "../../../../src/internal/box.svelte.js";
13+
import FloatingPortal from "../../../../src/components/floating-portal/floating-portal.svelte";
14+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
1515
1616
let {
1717
open: passedOpen = false,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
3+
import { useFloating } from "../../../../src/index.js";
4+
5+
const floating = useFloating({ open: true });
6+
</script>
7+
8+
<button data-testid="reference" bind:this={floating.reference}
9+
>reference</button>
10+
<FloatingFocusManager context={floating.context} modal={true}>
11+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
12+
<div bind:this={floating.floating} data-testid="floating" tabindex={-1}>
13+
</div>
14+
</FloatingFocusManager>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script lang="ts">
2+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
3+
import {
4+
useClick,
5+
useDismiss,
6+
useFloating,
7+
useInteractions,
8+
} from "../../../../src/index.js";
9+
10+
let open = $state(false);
11+
12+
const f = useFloating({
13+
open: () => open,
14+
onOpenChange: (v) => {
15+
open = v;
16+
},
17+
});
18+
19+
const ints = useInteractions([useClick(f.context), useDismiss(f.context)]);
20+
</script>
21+
22+
<button
23+
data-testid="reference"
24+
bind:this={f.reference}
25+
{...ints.getReferenceProps()}>reference</button>
26+
<FloatingFocusManager context={f.context} disabled={!open} modal={false}>
27+
<div
28+
bind:this={f.floating}
29+
data-testid="floating"
30+
{...ints.getFloatingProps()}>
31+
<button data-testid="child">child</button>
32+
</div>
33+
</FloatingFocusManager>
34+
<button data-testid="after">after</button>

packages/floating-ui-svelte/test/components/floating-focus-manager/main.svelte renamed to packages/floating-ui-svelte/test/components/floating-focus-manager/components/main.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
2-
import type { FloatingFocusManagerProps } from "../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
3-
import FloatingFocusManager from "../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
4-
import { useFloating } from "../../../src/index.js";
2+
import type { FloatingFocusManagerProps } from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
3+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
4+
import { useFloating } from "../../../../src/index.js";
55
66
let {
77
renderInput = false,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts">
2+
import type { Snippet } from "svelte";
3+
import {
4+
useClick,
5+
useDismiss,
6+
useFloating,
7+
useFloatingNodeId,
8+
useInteractions,
9+
} from "../../../../src/index.js";
10+
import FloatingNode from "../../../../src/components/floating-tree/floating-node.svelte";
11+
import { box } from "../../../../src/internal/box.svelte.js";
12+
import FloatingPortal from "../../../../src/components/floating-portal/floating-portal.svelte";
13+
import FloatingFocusManager from "../../../../src/components/floating-focus-manager/floating-focus-manager.svelte";
14+
15+
let {
16+
open: controlledOpen,
17+
modal = true,
18+
reference,
19+
sideChildren,
20+
content,
21+
}: {
22+
open?: boolean;
23+
modal?: boolean;
24+
content?: Snippet<[() => void]>;
25+
reference?: Snippet<
26+
[{ current: Element | null }, Record<string, unknown>]
27+
>;
28+
sideChildren?: Snippet;
29+
} = $props();
30+
31+
let internalOpen = $state(false);
32+
const nodeId = useFloatingNodeId();
33+
const open = $derived(
34+
controlledOpen !== undefined ? controlledOpen : internalOpen
35+
);
36+
37+
const ref = box<Element | null>(null);
38+
39+
const floating = useFloating({
40+
reference: () => ref.current,
41+
onReferenceChange: (v) => {
42+
ref.current = v;
43+
},
44+
open: () => open,
45+
onOpenChange: (v) => {
46+
internalOpen = v;
47+
},
48+
nodeId: () => nodeId,
49+
});
50+
51+
const ints = useInteractions([
52+
useClick(floating.context),
53+
useDismiss(floating.context, { bubbles: false }),
54+
]);
55+
</script>
56+
57+
<FloatingNode id={nodeId}>
58+
{@render reference?.(ref, ints.getReferenceProps())}
59+
{#if open}
60+
<FloatingPortal>
61+
<FloatingFocusManager context={floating.context} {modal}>
62+
<div bind:this={floating.floating} {...ints.getFloatingProps()}>
63+
{@render content?.(() => (controlledOpen = false))}
64+
</div>
65+
</FloatingFocusManager>
66+
</FloatingPortal>
67+
{/if}
68+
{@render sideChildren?.()}
69+
</FloatingNode>

0 commit comments

Comments
 (0)