fix layout in spotlight (top inset)

This commit is contained in:
Timo K.
2026-09-09 19:20:31 +02:00
parent be5f3390f7
commit be4023dd66
2 changed files with 42 additions and 2 deletions
@@ -92,6 +92,32 @@ test("keeps its modals inside the container it was given", async ({ page }) => {
); );
}); });
test("keeps the call inside the container, wherever the host put it", async ({
page,
}) => {
const { username, roomId } = await createUserAndRoom("tileswithin");
const panes = await startHarness(page, username, roomId);
const pane = panes.first();
const container = pane.getByTestId("call-container");
// Large enough for the layout switch to be offered
await resizeContainer(container, { width: 900, height: 640 });
await pane.getByTestId("lobby_joinCall").click({ timeout: 60_000 });
await expect(pane.getByTestId("footer-container")).toBeVisible({
timeout: 60_000,
});
// The spotlight layout draws its tile in the fixed grid, which is positioned
// against Element Call's root rather than laid out in flow. The harness puts
// the container below a header of its own, so a grid offset measured from
// the top of the page instead of from the root would land the tile on top of
// the footer and out of the bottom of the container.
await pane.getByRole("radio", { name: "Spotlight" }).check();
const tile = pane.getByTestId("videoTile").first();
await expect(tile).toBeVisible({ timeout: 60_000 });
await expectWithin(tile, container);
});
test("leaves the host's own page unstyled", async ({ page }) => { test("leaves the host's own page unstyled", async ({ page }) => {
const { username, roomId } = await createUserAndRoom("hoststyles"); const { username, roomId } = await createUserAndRoom("hoststyles");
const panes = await startHarness(page, username, roomId); const panes = await startHarness(page, username, roomId);
+16 -2
View File
@@ -267,6 +267,20 @@ export const InCallView: FC<InCallViewProps> = ({
// Merge the refs so they can attach to the same element // Merge the refs so they can attach to the same element
const containerRef = useMergedRefs(containerRef1, containerRef2); const containerRef = useMergedRefs(containerRef1, containerRef2);
// The fixed grid is positioned against Element Call's root, so offsets
// handed to it have to be measured from there rather than from the
// viewport. Standalone the two are the same, the root being the page; for a
// component the root sits wherever the host put it, and measuring from the
// viewport would push the grid down by that much again. Taken at the same
// moment as `bounds`, so that the two agree however the host has scrolled.
const rootElement = useRootElement();
const rootTop = useMemo(
() => rootElement.getBoundingClientRect().top,
// eslint-disable-next-line react-hooks/exhaustive-deps
[rootElement, bounds],
);
const topWithinRoot = bounds.top - rootTop;
const { showControls, header: headerStyle } = useUrlParams(); const { showControls, header: headerStyle } = useUrlParams();
const muteAllAudio = useBehavior(muteAllAudio$); const muteAllAudio = useBehavior(muteAllAudio$);
@@ -564,7 +578,7 @@ export const InCallView: FC<InCallViewProps> = ({
className={styles.fixedGrid} className={styles.fixedGrid}
style={{ style={{
// If not edge-to-edge, consume the header insets right here. // If not edge-to-edge, consume the header insets right here.
insetBlockStart: edgeToEdge ? 0 : bounds.top + headerBounds.height, insetBlockStart: edgeToEdge ? 0 : topWithinRoot + headerBounds.height,
height: edgeToEdge ? "100%" : gridBounds.height, height: edgeToEdge ? "100%" : gridBounds.height,
// If edge-to-edge, compute new safe area insets that account for the // If edge-to-edge, compute new safe area insets that account for the
// header and footer, passing them down to the tiles. // header and footer, passing them down to the tiles.
@@ -575,7 +589,7 @@ export const InCallView: FC<InCallViewProps> = ({
// itself. Otherwise account for the safe area and header size // itself. Otherwise account for the safe area and header size
// as part of the InCallView. // as part of the InCallView.
headerStyle === HeaderStyle.AppBar headerStyle === HeaderStyle.AppBar
? `${bounds.top}px` ? `${topWithinRoot}px`
: `calc(env(safe-area-inset-top) + ${headerBounds.height}px)` : `calc(env(safe-area-inset-top) + ${headerBounds.height}px)`
: undefined, : undefined,
"--call-view-safe-area-inset-bottom": "--call-view-safe-area-inset-bottom":