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
4 changes: 4 additions & 0 deletions docs/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,10 @@ function App() {
Use explicit header, action-row, or chrome modes only when the layout should not
adapt.
</li>
<li>
Floating chrome joins its title tab to the frame rail. Stacked chrome keeps a
full-width title bar inside the outer frame.
</li>
<li>Menu popups clamp to the available viewport at narrow widths.</li>
</ul>
</Guidance>
Expand Down
13 changes: 11 additions & 2 deletions docs/src/window-regression-fixtures.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,17 @@ export function WindowRegressionFixtures() {
</Window>
</RegressionCase>

<RegressionCase width={520} state="inactive" label="Inactive frame and dense action rail">
<Window title="Inactive settings" active={false} data-regression-frame="window">
<RegressionCase
width={520}
state="inactive"
label="Inactive floating frame and dense action rail"
>
<Window
title="Inactive settings"
active={false}
chrome="floating"
data-regression-frame="window"
>
<RegressionContent headerLayout="stacked" actionLayout="inline" />
<Window.StatusBar data-regression-contained>Changes saved</Window.StatusBar>
</Window>
Expand Down
12 changes: 8 additions & 4 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1139,16 +1139,18 @@
}

.greyui-window-frame {
--greyui-window-shadow: 3px 3px 6px rgb(0 0 0 / 22%);

position: relative;
display: grid;
isolation: isolate;
min-width: 0;
max-width: 100%;
box-sizing: border-box;
padding: var(--greyui-tab-height) 3px 3px;
padding: calc(var(--greyui-tab-height) + 3px) 3px 3px;
border: 0;
background: transparent;
box-shadow: 3px 3px 6px rgb(0 0 0 / 22%);
box-shadow: none;
}

.greyui-window-frame::before {
Expand All @@ -1160,13 +1162,15 @@
background: var(--greyui-panel);
box-shadow:
inset 1px 1px 0 var(--greyui-border-light),
inset -1px -1px 0 var(--greyui-border-dark);
inset -1px -1px 0 var(--greyui-border-dark),
var(--greyui-window-shadow);
pointer-events: none;
}

.greyui-window[data-active="false"] .greyui-window-frame {
--greyui-window-shadow: 2px 2px 4px rgb(0 0 0 / 18%);

opacity: 0.94;
box-shadow: 2px 2px 4px rgb(0 0 0 / 18%);
}

.greyui-window[data-collapsed="true"] .greyui-window-frame {
Expand Down
42 changes: 42 additions & 0 deletions tests/browser/docs-geometry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,52 @@ for (const viewport of viewports) {
"position",
"absolute",
);
await expectFloatingFrameRail(suite, 520);
await expectFloatingFrameRail(suite, 640);
await expectFloatingFrameRail(suite, 820);
await expectStackedTitleJoin(suite, 280);
await expectStackedTitleJoin(suite, 760);
}
});
}

async function expectFloatingFrameRail(suite: Locator, width: number) {
const geometry = await windowGeometry(suite, width);

expect(geometry.framePaddingTop).toBe(geometry.tabHeight + 3);
expect(geometry.bodyTop - geometry.tabBottom).toBeCloseTo(3, 5);
}

async function expectStackedTitleJoin(suite: Locator, width: number) {
const geometry = await windowGeometry(suite, width);

expect(geometry.bodyTop - geometry.tabBottom).toBeCloseTo(0, 5);
}

async function windowGeometry(suite: Locator, width: number) {
return suite.locator(`[data-regression-width='${width}']`).evaluate((fixture) => {
const frame = fixture.querySelector<HTMLElement>(".greyui-window-frame");
const tab = fixture.querySelector<HTMLElement>(".greyui-window-tab");
const body = fixture.querySelector<HTMLElement>(".greyui-window-body");
if (frame === null || tab === null || body === null) {
throw new Error(
`Missing Window geometry for ${fixture.dataset.regressionWidth ?? "unknown"}`,
);
}

const frameStyle = getComputedStyle(frame);
const tabRect = tab.getBoundingClientRect();
const bodyRect = body.getBoundingClientRect();

return {
bodyTop: bodyRect.top,
framePaddingTop: Number.parseFloat(frameStyle.paddingTop),
tabBottom: tabRect.bottom,
tabHeight: tabRect.height,
};
});
}

test("edge menu and form popups remain inside the viewport", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/");
Expand Down
8 changes: 7 additions & 1 deletion tests/styles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ describe("default theme contrast", () => {
css.indexOf(".greyui-window-frame {"),
css.indexOf(".greyui-window-frame::before"),
);
const frameSurfaceRule = css.slice(
css.indexOf(".greyui-window-frame::before"),
css.indexOf('.greyui-window[data-active="false"] .greyui-window-frame'),
);
const bodyRule = css.slice(
css.indexOf(".greyui-window-body {"),
css.indexOf(".greyui-window-content {"),
);

expect(rootRule).not.toContain("box-shadow:");
expect(frameRule).toContain("box-shadow:");
expect(frameRule).toContain("padding: calc(var(--greyui-tab-height) + 3px) 3px 3px;");
expect(frameRule).toContain("box-shadow: none;");
expect(frameSurfaceRule).toContain("var(--greyui-window-shadow)");
expect(css).toMatch(/\.greyui-window-frame::before\s*\{[\s\S]*?border:/);
expect(bodyRule).not.toContain("border:");
expect(bodyRule).not.toContain("box-shadow:");
Expand Down
Loading