Skip to content

Commit 81a7da4

Browse files
atomiksGitHub Actions
and
GitHub Actions
authored
fix(getContainingBlock): reorder isTopLayer check (floating-ui#2979)
Co-authored-by: GitHub Actions <github-actions@github.com>
1 parent 0d6c2d2 commit 81a7da4

File tree

5 files changed

+69
-13
lines changed

5 files changed

+69
-13
lines changed

.changeset/khaki-beds-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@floating-ui/utils': patch
3+
---
4+
5+
fix(getContainingBlock): reorder `isTopLayer` check. Fixes regression when a top layer element like `<dialog>` is a containing block (e.g. it has a `transform` style) and a floating element is being positioned inside of it.

packages/dom/test/functional/top-layer.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,14 @@ test('fixed inside dialog with outer containing block', async ({page}) => {
245245
`top-layer.dialog.outer.png`,
246246
);
247247
});
248+
249+
test('top layer containing block with inner floating element', async ({
250+
page,
251+
}) => {
252+
await page.goto('http://localhost:1234/top-layer');
253+
await click(page, '[data-testid="inner-true"]');
254+
255+
expect(await page.locator('#inner-dialog').screenshot()).toMatchSnapshot(
256+
`top-layer.dialog.inner.png`,
257+
);
258+
});
Loading

packages/dom/test/visual/spec/TopLayer.tsx

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,40 @@ function OuterTopLayer() {
3030
}, []);
3131

3232
return (
33-
<>
34-
<div style={{containerType: 'inline-size'}}>
35-
<dialog id="outer-dialog" ref={handleDialogRef}>
36-
<button ref={refs.setReference}>My button</button>
37-
<div ref={refs.setFloating} style={floatingStyles}>
38-
My tooltip
39-
</div>
40-
</dialog>
33+
<div style={{containerType: 'inline-size'}}>
34+
<dialog id="outer-dialog" ref={handleDialogRef}>
35+
<button ref={refs.setReference}>My button</button>
36+
<div ref={refs.setFloating} style={floatingStyles}>
37+
My tooltip
38+
</div>
39+
</dialog>
40+
</div>
41+
);
42+
}
43+
44+
function InnerTopLayer() {
45+
const {refs, floatingStyles} = useFloating({
46+
strategy: 'fixed',
47+
whileElementsMounted: autoUpdate,
48+
});
49+
50+
const handleDialogRef = useCallback((node: HTMLDialogElement | null) => {
51+
if (node) {
52+
node.showModal();
53+
}
54+
}, []);
55+
56+
return (
57+
<dialog
58+
id="inner-dialog"
59+
ref={handleDialogRef}
60+
style={{containerType: 'inline-size', width: 300, height: 300}}
61+
>
62+
<button ref={refs.setReference}>My button</button>
63+
<div ref={refs.setFloating} style={floatingStyles}>
64+
My tooltip
4165
</div>
42-
</>
66+
</dialog>
4367
);
4468
}
4569

@@ -174,6 +198,7 @@ export function TopLayer() {
174198
const [layoutStyles, setLayoutStyles] = useState(true);
175199
const [strategy, setStrategy] = useState<'absolute' | 'fixed'>('fixed');
176200
const [outer, setOuter] = useState(false);
201+
const [inner, setInner] = useState(false);
177202

178203
const {refs, floatingStyles, x, y} = useFloating({
179204
strategy,
@@ -222,6 +247,7 @@ export function TopLayer() {
222247
Top Layer
223248
</h1>
224249
{outer && <OuterTopLayer />}
250+
{inner && <InnerTopLayer />}
225251
<Stack {...stackProps}>
226252
<div
227253
className={classes}
@@ -406,6 +432,22 @@ export function TopLayer() {
406432
</button>
407433
))}
408434
</Controls>
435+
436+
<h2>inner</h2>
437+
<Controls>
438+
{BOOLS.map((bool) => (
439+
<button
440+
key={String(bool)}
441+
data-testid={`inner-${bool}`}
442+
onClick={() => setInner(bool)}
443+
style={{
444+
backgroundColor: bool === inner ? 'black' : '',
445+
}}
446+
>
447+
{String(bool)}
448+
</button>
449+
))}
450+
</Controls>
409451
</>
410452
);
411453
}

packages/utils/src/dom.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,10 @@ export function getContainingBlock(element: Element): HTMLElement | null {
9292
let currentNode: Node | null = getParentNode(element);
9393

9494
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
95-
if (isTopLayer(currentNode)) {
96-
return null;
97-
}
98-
9995
if (isContainingBlock(currentNode)) {
10096
return currentNode;
97+
} else if (isTopLayer(currentNode)) {
98+
return null;
10199
}
102100

103101
currentNode = getParentNode(currentNode);

0 commit comments

Comments
 (0)