Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,23 @@ describe("ReAuthBannerBar", () => {
expect(bar.style.transform).toBe("translate(-50%, -50%)");
expect(bar.style.zIndex).toBe("200");
});

it("caps its width instead of fixing it, so a narrow viewport cannot clip its controls", () => {
renderWithMantine(
<ReAuthBannerBar data-testid="bar">contents</ReAuthBannerBar>,
);
const bar = screen.getByTestId("bar");
// Centered by `left: 50%` plus a -50% translate, so a width wider than the
// viewport would overflow both edges and clip the close button on one side
// and "Authorize again" on the other (#2218).
expect(bar.style.width).toBe("calc(100vw - 2rem)");
expect(bar.style.maxWidth).toBe("calc(26.25rem * var(--mantine-scale))");
});

it("takes its centering offset from the Paper theme rather than inline styles", () => {
renderWithMantine(
<ReAuthBannerBar data-testid="bar">contents</ReAuthBannerBar>,
);
expect(screen.getByTestId("bar")).toHaveAttribute("data-variant", "reauth");
});
});
19 changes: 14 additions & 5 deletions clients/web/src/components/groups/ReAuthBanner/ReAuthBannerBar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Paper } from "@mantine/core";

// The re-auth popup. A `Paper` so every static style is a prop; the stacking
// order goes through `styles.root` since Mantine has no `z` prop.
// order and the centering offset go through the `reauth` variant in
// `theme/Paper.ts`, since Mantine exposes neither `transform` nor `zIndex` as a
// style prop and flat CSS belongs in the theme layer rather than in inline
// `styles` (#2218).
//
// Floats rather than spanning the top as a sticky full-bleed bar. The bar cost
// the whole view a band of vertical space for what is a notification about one
Expand All @@ -24,15 +27,21 @@ import { Paper } from "@mantine/core";
// "Authorize again" also clears the stale OAuth state, which a plain reconnect
// does not do. So it floats above the page and leaves it usable.
//
// `transform` goes through `styles.root` for the same reason `zIndex` does:
// Mantine exposes neither as a style prop.
// The width is CAPPED at 420, not fixed at it. Because the banner is centered
// by `left: 50%` plus a -50% translate, a fixed width wider than the viewport
// overflows BOTH edges equally — clipping the close button on one side and
// "Authorize again" on the other, which are the only two controls it has. That
// is reachable on a narrow desktop window, not just a phone, since the element
// is positioned against the viewport rather than a panel. `maw` caps it while
// `w` keeps a 1rem gutter on each side so the shadow and radius still read.
export const ReAuthBannerBar = Paper.withProps({
variant: "reauth",
pos: "fixed",
top: "50%",
left: "50%",
w: 420,
w: "calc(100vw - 2rem)",
maw: 420,
bg: "var(--mantine-color-body)",
shadow: "xl",
radius: "md",
styles: { root: { transform: "translate(-50%, -50%)", zIndex: 200 } },
});
11 changes: 11 additions & 0 deletions clients/web/src/theme/Paper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ export const ThemePaper = Paper.extend({
},
};
}
if (props.variant === "reauth") {
return {
root: {
// Centers the fixed-position re-auth banner against the viewport.
// Neither property is available as a Mantine style prop, so the
// theme layer is where they belong (#2218).
transform: "translate(-50%, -50%)",
zIndex: 200,
},
};
}
if (props.variant === "panel") {
return {
root: {
Expand Down