Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
02c5df1
test(web-admin): add a mobile overflow smoke suite
dfliess Jul 13, 2026
604c03e
fix(web-admin): cap fixed widths and fit admin tables on narrow viewp…
dfliess Jul 13, 2026
42e5f75
fix(web-common): let the shared chrome wrap on narrow viewports
dfliess Jul 13, 2026
49a6b6a
fix(web-admin): make the project shell fit phones
dfliess Jul 13, 2026
146472d
fix(explore): stack the explore dashboard on phones
dfliess Jul 13, 2026
4907e21
fix(canvas): let canvas rows shrink below 420px
dfliess Jul 13, 2026
5a1bdbd
fix(web-admin): fit embed shells and markdown on narrow viewports
dfliess Jul 13, 2026
1596433
feat(web-common): make the fullpage chat and project home fit phones
dfliess Jul 13, 2026
7a8b7e8
feat(web-admin): switch Rill Cloud to the real device viewport
dfliess Jul 13, 2026
18f9e6e
fix(chat): make markdown tables content-width on purpose
dfliess Sep 21, 2026
9515885
fix(web-admin): stop capping the viewport zoom
dfliess Sep 21, 2026
f208cf6
fix(explore): opt into the phone layout instead of keying it on width
dfliess Sep 21, 2026
3f9b743
fix(web-admin): drop the inert scroll wrapper around the resources table
dfliess Sep 21, 2026
8466cce
fix(web-admin): drop the inert scroll wrappers around the org tables
dfliess Sep 21, 2026
a650d97
test(web-admin): keep the mobile smoke suite out of the desktop e2e p…
dfliess Sep 21, 2026
9962b3b
fix(web-admin): don't mount the editor and pivot behind their phone n…
dfliess Sep 21, 2026
c387b18
test(web-admin): don't flag overflow a scroll or clip container alrea…
dfliess Sep 21, 2026
3c6e720
fix(time-controls): hide the SuperPill scrollbar
dfliess Sep 21, 2026
495017a
fix(canvas): keep the KPI number on one line beside its sparkline
dfliess Oct 16, 2026
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
15 changes: 14 additions & 1 deletion web-admin/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,24 @@ const config: PlaywrightTestConfig = {
{
name: "e2e",
dependencies: process.env.E2E_NO_SETUP_OR_TEARDOWN ? [] : ["setup"],
testIgnore: "/setup",
// The mobile smoke suite runs only in the `mobile` project, at a phone viewport.
testIgnore: ["/setup", "mobile-smoke.spec.ts"],
use: {
storageState: ADMIN_STORAGE_STATE,
},
},
{
// Responsive smoke checks run on an emulated phone against the same
// seeded stack. Limited to mobile-smoke so the desktop suites aren't
// re-run here.
name: "mobile",
dependencies: process.env.E2E_NO_SETUP_OR_TEARDOWN ? [] : ["setup"],
testMatch: "mobile-smoke.spec.ts",
Comment on lines +46 to +51

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The e2e project declares no testMatch, so Playwright's default **/*.@(spec|test).?(c|m)[jt]s?(x) applies and its testIgnore: "/setup" does not exclude the new file (checked against createFileMatcher in playwright/lib/util.js at the installed 1.59.1). Every test in mobile-smoke.spec.ts therefore runs twice: once here at iPhone 13, and once under e2e at Desktop Chrome's 1280×720, where the iPhone emulation is discarded and the suite starts asserting zero horizontal overflow at desktop width across four routes — a guarantee nothing else in the repo makes, and the opposite of what this comment claims. The e2e project needs a testIgnore for mobile-smoke.spec.ts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with a testIgnore on the e2e project, so the suite only runs in mobile. 70e6731

use: {
...devices["iPhone 13"],
storageState: ADMIN_STORAGE_STATE,
},
},
],
};

Expand Down
8 changes: 3 additions & 5 deletions web-admin/src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@
crossorigin
/>
<meta charset="utf-8" />
<!-- Set the mobile viewport width -->
<meta
name="viewport"
content="width=1024, initial-scale=1.0, user-scalable=yes"
/>
<!-- Use the device's real width. The zoom is not capped; the chat composer
stays at 16px on phones so iOS doesn't auto-zoom into it. -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Set the og image for thumbnail previews -->
<meta
property="og:image"
Expand Down
6 changes: 6 additions & 0 deletions web-admin/src/components/nav/Tab.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
position.set(left);
}

// When the tab row overflows (narrow viewports), keep the selected tab
// visible; "nearest" makes this a no-op when it already is.
$: if (selected && element) {
element.scrollIntoView({ block: "nearest", inline: "nearest" });
}

function measure() {
if (!element) return;
left = element.offsetLeft;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="bg-black bg-opacity-25 w-full h-full">
<div
class="bg-surface-subtle text-center mx-auto my-40 w-[600px] rounded p-12"
class="bg-surface-subtle text-center mx-auto my-40 w-full sm:w-[600px] rounded p-12"
>
<div
class="flex flex-col items-center justify-center gap-y-4 max-w-[400px] mx-auto"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
<div {...props} class="hidden"></div>
{/snippet}
</Dialog.Trigger>
<Dialog.Content class="w-[520px]" noClose>
<Dialog.Content class="w-[min(520px,calc(100vw-2rem))]" noClose>
<Dialog.Header>
<Dialog.Title>{m.billing_change_billing_contact()}</Dialog.Title>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
<div {...props} class="hidden"></div>
{/snippet}
</AlertDialogTrigger>
<AlertDialogContent class="flex flex-row gap-x-2 min-w-[600px]">
<AlertDialogContent
class="flex flex-row gap-x-2 min-w-[min(600px,calc(100vw-2rem))]"
>
<Champagne size="150px" className="min-w-[150px]" />
<div class="flex flex-col gap-x-2">
<AlertDialogHeader>
Expand Down
2 changes: 1 addition & 1 deletion web-admin/src/features/bookmarks/Bookmarks.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@
The "bookmark current view" action and search stay pinned; only the lists scroll.
-->
<DropdownMenuContent
class="flex flex-col w-[450px] max-h-[min(600px,var(--bits-floating-available-height))] overflow-hidden p-0"
class="flex flex-col w-[min(450px,calc(100vw-2rem))] max-h-[min(600px,var(--bits-floating-available-height))] overflow-hidden p-0"
>
<div class="flex-none p-1.5">
<DropdownMenuItem onclick={() => (showDialog = true)}>
Expand Down
4 changes: 3 additions & 1 deletion web-admin/src/features/branches/BranchesSection.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,9 @@
<Tooltip location="bottom" distance={8}>
<span class="readonly-badge">{m.branch_read_only()}</span>
<TooltipContent slot="tooltip-content">
<div class="text-xs max-w-[360px] flex flex-col gap-y-1">
<div
class="text-xs max-w-[min(360px,calc(100vw-2rem))] flex flex-col gap-y-1"
>
<span>{m.branch_not_editable()}</span>
<span>
{m.branch_recreate_with()}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<script lang="ts">
import * as Popover from "@rilldata/web-common/components/popover";
import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte";
import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte";
import { createRuntimeServiceGetExplore } from "@rilldata/web-common/runtime-client";
import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2";
import { timeAgo } from "@rilldata/web-common/lib/time/relative-time";
import { m } from "@rilldata/web-common/lib/i18n/gen/messages";
import { Clock } from "lucide-svelte";

export let dashboard: string;

Expand All @@ -27,8 +29,23 @@
</script>

{#if data}
<!-- On phones the relative text costs a full header row, so it collapses to
a clock icon; hover tooltips never fire on touch, so the icon opens a
popover on tap instead. -->
<Popover.Root>
<Popover.Trigger
class="sm:hidden p-1 -m-1 flex items-center text-fg-secondary"
aria-label={m.dashboard_last_refreshed_ago({ time: timeAgo(data) })}
>
<Clock size="14px" />
</Popover.Trigger>
<Popover.Content class="flex w-fit flex-col gap-y-0.5 text-xs" padding="2">
<span>{m.dashboard_last_refreshed_ago({ time: timeAgo(data) })}</span>
<span class="text-fg-secondary">{data.toLocaleString()}</span>
</Popover.Content>
</Popover.Root>
<Tooltip distance={8}>
<div class="text-[11px] text-fg-secondary">
<div class="hidden sm:block text-[11px] text-fg-secondary">
{m.dashboard_last_refreshed_ago({ time: timeAgo(data) })}
</div>
<TooltipContent slot="tooltip-content">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import type { PdfExportRunOptions } from "@rilldata/web-common/features/exports/pdf/types";
import ScheduledReportDialog from "@rilldata/web-common/features/scheduled-reports/ScheduledReportDialog.svelte";
import { m } from "@rilldata/web-common/lib/i18n/gen/messages";
import { Share } from "lucide-svelte";
import { readable } from "svelte/store";
import { getDashboardResourceFromPage } from "@rilldata/web-common/features/dashboards/nav-utils.ts";
import { page } from "$app/stores";
Expand Down Expand Up @@ -77,16 +78,24 @@
<PopoverTrigger>
{#snippet child({ props })}
<Tooltip distance={8} suppress={isOpen}>
<Button {...props} type="secondary" selected={isOpen}
>{m.avatar_share()}</Button
<!-- On phones the label costs too much header width, so the button
collapses to its icon; the aria-label keeps the accessible name. -->
<Button
{...props}
type="secondary"
selected={isOpen}
label={m.avatar_share()}
>
<Share size="16px" class="sm:hidden" />
<span class="hidden sm:inline">{m.avatar_share()}</span>
</Button>
<TooltipContent slot="tooltip-content"
>{m.avatar_share_dashboard()}</TooltipContent
>
</Tooltip>
{/snippet}
</PopoverTrigger>
<PopoverContent align="end" class="w-[402px] p-0">
<PopoverContent align="end" class="w-[min(402px,calc(100vw-2rem))] p-0">
<Tabs>
<TabsList>
<TabsTrigger value="tab1">{m.avatar_copy_url()}</TabsTrigger>
Expand Down
2 changes: 1 addition & 1 deletion web-admin/src/features/embeds/EmbedHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
{#if $isErrorStoreEmpty && shouldRender}
<div class="flex items-center w-full">
{#if navigationBarEnabled}
<nav class="flex-1">
<nav class="flex-1 min-w-0">
<ol class="flex items-center pl-4">
{#if !onProjectPage}
<div class="flex gap-x-2">
Expand Down
2 changes: 1 addition & 1 deletion web-admin/src/features/organizations/OrgHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<Breadcrumbs {pathParts} {currentPath} />
{/if}

<div class="flex gap-x-2 items-center ml-auto">
<div class="flex gap-x-2 items-center ml-auto shrink-0">
{#if $user.isSuccess}
{#if $user.data?.user}
<AvatarButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
<PopoverContent
align="start"
side="bottom"
class="flex flex-col gap-y-2 w-[400px] p-4"
class="flex flex-col gap-y-2 w-[min(400px,calc(100vw-2rem))] p-4"
>
<div class="text-base font-medium">
{m.settings_upload_org_image_title({ label })}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
{/snippet}
</Dialog.Trigger>
<Dialog.Content
class="translate-y-[-200px] md:w-[425px] w-[425px]"
class="translate-y-[-200px] w-[min(425px,calc(100vw-2rem))]"
onInteractOutside={(e) => {
e.preventDefault();
open = false;
Expand Down
97 changes: 76 additions & 21 deletions web-admin/src/features/projects/ProjectTabs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { removeBranchFromPath } from "@rilldata/web-admin/features/branches/branch-utils";
import { featureFlags } from "@rilldata/web-common/features/feature-flags";
import { m } from "@rilldata/web-common/lib/i18n/gen/messages";
import { onMount } from "svelte";
import { type V1ProjectPermissions } from "../../client";

export let projectPermissions: V1ProjectPermissions;
Expand Down Expand Up @@ -69,6 +70,28 @@

$: selectedIndex = tabs?.findLastIndex((t) => isSelected(t.route, pathname));

let scroller: HTMLDivElement;
let navElement: HTMLElement;
let canScrollLeft = false;
let canScrollRight = false;

function updateFades() {
if (!scroller) return;
canScrollLeft = scroller.scrollLeft > 0;
canScrollRight =
scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth - 1;
}

onMount(() => {
updateFades();
// Observe the nav too: tabs mount/unmount with permissions and feature
// flags, which changes scrollWidth without resizing the scroller itself.
const observer = new ResizeObserver(updateFades);
observer.observe(scroller);
observer.observe(navElement);
return () => observer.disconnect();
});

function isSelected(tabRoute: string, currentPathname: string) {
// Strip @branch from both sides so comparison works regardless of branch
const normalizedTab = removeBranchFromPath(tabRoute);
Expand All @@ -86,31 +109,63 @@
}
</script>

<div class="bg-surface-base">
<nav>
{#each tabs as tab, i (tab.route)}
{#if tab.hasPermission}
<Tab
route={tab.route}
label={tab.label}
selected={selectedIndex === i}
{organization}
{project}
/>
{/if}
{/each}
</nav>

{#if $width}
<span style:width="{$width}px" style:transform="translateX({$position}px) "
></span>
{/if}
<div class="tabs-container bg-surface-base">
<!-- On narrow viewports the tab row scrolls horizontally; the selection
indicator scrolls with it since both share the scroller. The edge
fades signal the hidden overflow on either side. -->
<div class="tabs-scroller" bind:this={scroller} on:scroll={updateFades}>
<nav bind:this={navElement}>
{#each tabs as tab, i (tab.route)}
{#if tab.hasPermission}
<Tab
route={tab.route}
label={tab.label}
selected={selectedIndex === i}
{organization}
{project}
/>
{/if}
{/each}
</nav>

{#if $width}
<span
style:width="{$width}px"
style:transform="translateX({$position}px) "
></span>
{/if}
</div>

<div class="fade left" class:visible={canScrollLeft}></div>
<div class="fade right" class:visible={canScrollRight}></div>
</div>

<style lang="postcss">
div {
@apply border-b pt-1;
.tabs-container {
@apply relative border-b;
}

.tabs-scroller {
@apply pt-1;
@apply gap-y-[3px] flex flex-col;
@apply overflow-x-auto;
}

.fade {
@apply absolute inset-y-0 w-8 pointer-events-none;
@apply opacity-0 transition-opacity duration-200;
}

.fade.visible {
@apply opacity-100;
}

.fade.left {
@apply left-0 bg-gradient-to-r from-surface-base to-transparent;
}

.fade.right {
@apply right-0 bg-gradient-to-l from-surface-base to-transparent;
}

nav {
Expand Down
7 changes: 6 additions & 1 deletion web-admin/src/features/projects/SlimProjectHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@
<HeaderLogo href={rillLogoHref} logoUrl={organizationLogoUrl} />
<Breadcrumbs {pathParts} {currentPath} />

<div class="flex gap-x-2 items-center ml-auto">
<!-- max-w-full bounds the cluster so its flex-wrap can engage on narrow
viewports (a bare shrink-0 leaves the width unbounded and the row
never wraps); from sm up it keeps to one row as before. -->
<div
class="flex flex-wrap justify-end gap-y-1 gap-x-2 items-center ml-auto max-w-full sm:shrink-0"
>
{#if $user.isSuccess}
{#if $user.data?.user}
<AvatarButton />
Expand Down
10 changes: 9 additions & 1 deletion web-admin/src/features/projects/header/ProjectHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,15 @@
</Breadcrumbs>
{/if}

<div class="flex gap-x-2 items-center ml-auto">
<!-- max-w-full bounds the cluster so its flex-wrap can engage on narrow
viewports (a bare shrink-0 leaves the width unbounded and the row
never wraps); from sm up it keeps to one row as before. grow works
around WebKit sizing a wrapping flex item below its single-line
max-content (the row wrapped despite free space); with justify-end
the extra width is invisible. -->
<div
class="flex flex-wrap justify-end gap-y-1 gap-x-2 items-center ml-auto grow max-w-full sm:shrink-0"
>
{#if editContext}
{#if editSessionDeploymentId && editSessionJwt}
<EditSessionViewAs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@
{/snippet}
</Popover.Trigger>

<Popover.Content class="w-[380px]" align="end" sideOffset={8}>
<Popover.Content
class="w-[min(380px,calc(100vw-2rem))]"
align="end"
sideOffset={8}
>
<div class="flex flex-col gap-y-3">
<span class="text-sm text-fg-secondary">
{m.status_clone_description()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@
];
</script>

<VirtualizedTable
tableId="external-tables-table"
data={tables}
{columns}
columnLayout="minmax(80px, 0.5fr) minmax(150px, 2fr) minmax(100px, 1fr)"
/>
<div class="overflow-x-auto">
<VirtualizedTable
tableId="external-tables-table"
data={tables}
{columns}
columnLayout="minmax(80px, 0.5fr) minmax(150px, 2fr) minmax(100px, 1fr)"
/>
</div>
Loading
Loading