Skip to content

ref(gsAdmin): add responsive CSS for narrow screens#117056

Merged
rahulchhabria merged 16 commits into
masterfrom
rahul/gs-admin-responsive-2-4
Jun 10, 2026
Merged

ref(gsAdmin): add responsive CSS for narrow screens#117056
rahulchhabria merged 16 commits into
masterfrom
rahul/gs-admin-responsive-2-4

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

What

Four responsive improvements to make _admin less broken at narrow viewport widths.

pageHeader.tsx — Add flex-wrap: wrap + gap to the outer PageHeader flex container and to Breadcrumbs. Prevents long breadcrumb trails and action buttons from colliding or escaping the viewport at narrow widths.

detailsContainer.tsx — Add a @media (max-width: 768px) breakpoint to collapse the fixed 1fr 1fr grid to a single column. Keeps label/value pairs readable on smaller screens.

resultGrid.tsx — Add flex-wrap: wrap to SortSearchForm and wrap ResultTable in a TableScrollWrapper with overflow-x: auto. Sort selector, search input, and table rows now scroll/wrap instead of overflowing when horizontal space is tight.

layout.tsx — Add a collapsible mobile sidebar with a hamburger toggle, slide-in transition, and dimmed overlay. Route changes (including browser back/forward) close the drawer automatically via useLocation. Body scroll is locked while the overlay is open.

Why

_admin has no responsive CSS in its core layout or shared components. These are pure CSS and minimal JS state changes; zero new network requests or non-trivial side effects.

Not included here

  • Table column hiding — more design judgment needed on which columns to deprioritize

Action taken on behalf of rahul.


View Session in Sentry

- PageHeader: flex-wrap on header and breadcrumbs so long trails
  and action buttons don't collide on narrow viewports
- DetailsContainer: collapse 2-col grid to 1-col below 700px
- SortSearchForm: flex-wrap so sort/search controls wrap before
  overflowing when the sidebar is open on a small screen

Refs: table overflow-x fix tracked separately in existing PR

Action taken on behalf of rahul.

---
[View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780797596.852199%22)

Co-authored-by: rahul <rahul.chhabria@sentry.io>
@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Jun 7, 2026
Add a collapsible sidebar drawer with a hamburger toggle for mobile
viewports (≤768px). On small screens the sidebar slides off-screen by
default and animates in when the hamburger button is tapped, with a
dark overlay backdrop that closes it on tap.

Add a sticky mobile top bar showing the hamburger and inline logo,
visible only on mobile. Wrap all ResultTable instances in an
overflow-x scroll container so tables scroll horizontally instead of
breaking page layout. Align the DetailsContainer two-to-one column
breakpoint to 768px to match the layout breakpoint.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread static/gsAdmin/views/layout.tsx
Comment thread static/gsAdmin/views/layout.tsx
- useLocation to detect route changes (back/forward nav) and close the drawer
- useEffect to toggle document.body.style.overflow when sidebar is open,
  preventing the page from scrolling behind the fixed overlay

Action taken on behalf of rahul.

---
[View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780839152.391199%22)

Co-authored-by: rahul <rahul.chhabria@sentry.io>
Comment thread static/gsAdmin/views/layout.tsx
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

📊 Type Coverage Diff

Metric Before After Delta
Coverage 93.67% 93.67% ±0%
Typed 132,574 132,639 🟢 +65
Untyped 8,960 8,963 🔴 +3
🔍 3 new type safety issues introduced

any-typed symbols (1 new)

File Line Detail
static/gsAdmin/views/layout.tsx 54 location (var)

Type assertions (as) (2 new)

File Line Detail
static/gsAdmin/components/resultGrid.tsx 469 as React.ReactElement<Record<string, unknown>>cell as React.ReactElement<Record<string, unknown>>
static/gsAdmin/views/layout.tsx 133 as HTMLElemente.target as HTMLElement

This is informational only and does not block the PR.

On narrow viewports (≤768px), result tables transform into stacked
cards instead of requiring horizontal scrolling. Each row becomes a
card with the primary cell (title/name) spanning full width at the
top, and remaining cells displayed as label+value pairs in a two-
column grid below it.

Column labels are extracted from the <th> elements already passed to
ResultGrid and injected as data-label attributes onto each <td> via
cloneElement, so no changes are needed in individual list views. The
pattern applies automatically to all ResultGrid-based pages —
Broadcasts, Users, Customers, Promos, and others.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread static/gsAdmin/components/resultTable.tsx
Merge the collapsible sidebar from #117054 alongside the existing
mobile drawer. Desktop users can now collapse the sidebar via a
double-chevron button, reclaiming horizontal space on dense pages.
The preference persists to localStorage. The mobile hamburger drawer
is unaffected — it operates independently via the sidebarOpen state.

Co-Authored-By: Claude <noreply@anthropic.com>
The expand button was fixed-positioned with no corresponding padding
on the content area, causing it to overlap page headings. Introduce
--sidebarCollapsedWidth (48px) and use it for both the content
padding-left when collapsed and the button's reserved strip width.

Co-Authored-By: Claude <noreply@anthropic.com>
…trip

A floating fixed button created an awkward gap and overlapped content.
Replace it with a thin sidebar strip (same position/border as the full
sidebar) that holds the expand icon centered — the standard pattern for
collapsible sidebars.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread static/gsAdmin/components/resultTable.tsx
Comment thread static/gsAdmin/components/resultTable.tsx
…-typed filter param

- layout.tsx: add matchMedia listener to close mobile drawer when viewport
  widens past 768px, preventing body scroll lock from persisting on desktop
- resultTable.tsx: exclude colspan-only rows (loading/error/empty/expansion
  placeholders) from mobile card grid layout to avoid empty padded strips
- resultGrid.tsx: type extractColumnLabel filter param as unknown instead
  of implicit any to fix type coverage regression

Action taken on behalf of rahul.

---
[View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780860599.277959%22)

Co-authored-by: rahul <rahul.chhabria@sentry.io>
Add data-mobile-primary attribute support to ResultTable CSS so grids
with a control column (e.g. expand chevron) as the first column can
designate the actual primary/title cell. Uses :has() fallback to
td:first-of-type for tables that render without ResultGrid.

Update ResultGrid.renderResults() to inject data-mobile-primary="true"
on the first cell whose column label is non-empty, skipping unnamed
control columns like expand buttons.

Add data-label attributes to all non-primary td cells in views that use
ResultTable directly (userEmails, userEmailLog, billingPlans,
invoiceDetails) so mobile card layout shows column headings for
secondary cells.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread static/gsAdmin/components/resultGrid.tsx
… cells

cloneElement only reaches the top-level node returned by columnsForRow.
When a row returns a wrapper component (e.g. EditableOption in options.tsx)
that renders multiple <td> elements via Fragment, data-label and
data-mobile-primary land on the component prop and never reach the DOM
cells, leaving secondary column labels blank on mobile.

Switch to CSS custom properties (--cl-1, --cl-2, ...) set inline on <tr>
in renderResults(). CSS custom properties are inherited by all descendants
including ::before pseudo-elements, so they work regardless of how deeply
the <td> is nested inside a wrapper component.

Replace the single secondary-cell content: attr(data-label) rule with
per-nth-of-type rules using content: var(--cl-N, attr(data-label)).
The attr(data-label) fallback preserves correct behavior for tables
rendered without ResultGrid (userEmails, billingPlans, invoiceDetails)
where no CSS vars are set on <tr>.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment on lines +619 to +626
const TableScrollWrapper = styled('div')`
overflow-x: auto;
-webkit-overflow-scrolling: touch;

@media (max-width: 768px) {
overflow-x: visible;
}
`;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would be nice to use our Container component for this with it's media query capabilities

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done — replaced TableScrollWrapper with <Container overflowX={{'2xs': 'visible', sm: 'auto'}}> using the responsive prop.

Comment thread static/gsAdmin/views/layout.tsx Outdated
Comment on lines +335 to +339
const MainArea = styled('div')`
display: flex;
flex-direction: column;
min-width: 0;
`;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should be using <Flex>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done — replaced MainArea with an inline <Flex direction="column" minWidth={0}>.

Comment thread static/gsAdmin/views/layout.tsx Outdated
Comment on lines +313 to +318
const SidebarHeader = styled('div')`
display: flex;
align-items: center;
justify-content: space-between;
gap: ${p => p.theme.space.md};
`;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use <Flex>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done — replaced SidebarHeader with an inline <Flex align="center" justify="between" gap="md">.

Comment thread static/gsAdmin/views/layout.tsx Outdated
Comment on lines 266 to 283
const CollapsedSidebar = styled('section')<{isCollapsed: boolean}>`
display: none;

@media (min-width: 769px) {
display: ${p => (p.isCollapsed ? 'flex' : 'none')};
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: var(--sidebarCollapsedWidth);
flex-direction: column;
align-items: center;
padding-top: ${p => p.theme.space['2xl']};
background: ${p => p.theme.tokens.background.primary};
border-right: 1px solid ${p => p.theme.tokens.border.primary};
z-index: 100;
}
`;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can also be using Flex here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done — converted CollapsedSidebar to styled(Flex) and moved flex-direction: column / align-items: center to direction="column" align="center" props.

Comment thread static/gsAdmin/views/layout.tsx Outdated
</SidebarHeader>
<Navigation>
<NavLink to="/_admin/" index>
<NavLink to="/_admin/" index onClick={closeSidebar}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I might prefer putting the onClick handler on the Navigation element instead and in the onClick check if a anchor was the target so we can close the sidebar, instead of duplicating the onClick to every nav link

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done — moved the handler to <Navigation onClick> with an (e.target as HTMLElement).closest('a') check, and removed the per-link onClick props.

…andler

Replace hand-rolled styled flex wrappers (SidebarHeader, MainArea) with
inline Flex components, convert CollapsedSidebar to extend styled(Flex) so
flex props are expressed via component props rather than CSS. Replace
TableScrollWrapper with Container using responsive overflowX prop.

Consolidate the per-NavLink closeSidebar onClick into a single delegated
handler on Navigation that checks for an anchor target.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread static/gsAdmin/components/resultGrid.tsx Outdated
…rapper

The scraps sm breakpoint is 800px, but the admin layout shifts to desktop
at 769px. Using the responsive Container prop introduced a 769–799px window
where wide tables could overflow with no horizontal scroll container.

Switch to styled(Container) with an explicit max-width: 768px media query
to match the admin layout breakpoint exactly.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread static/gsAdmin/components/resultGrid.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d2bc9b3. Configure here.

Comment thread static/gsAdmin/views/layout.tsx
…ent inert

Sidebar was hiding with transform only, keeping its links in the keyboard
tab sequence when off-screen. Add visibility: hidden with a 0.2s delay so
it disappears from the tab order after the slide animation completes.

Also set inert on the main content area when the mobile drawer is open, so
keyboard focus cannot reach page content behind the overlay.

Co-Authored-By: Claude <noreply@anthropic.com>
@evanpurkhiser evanpurkhiser added the Trigger: getsentry tests Once code is reviewed: apply label to PR to trigger getsentry tests label Jun 10, 2026
@rahulchhabria rahulchhabria merged commit 0d07c50 into master Jun 10, 2026
84 of 85 checks passed
@rahulchhabria rahulchhabria deleted the rahul/gs-admin-responsive-2-4 branch June 10, 2026 15:17
amy-chen23 pushed a commit that referenced this pull request Jun 10, 2026
## What

Four responsive improvements to make `_admin` less broken at narrow
viewport widths.

**`pageHeader.tsx`** — Add `flex-wrap: wrap` + `gap` to the outer
`PageHeader` flex container and to `Breadcrumbs`. Prevents long
breadcrumb trails and action buttons from colliding or escaping the
viewport at narrow widths.

**`detailsContainer.tsx`** — Add a `@media (max-width: 768px)`
breakpoint to collapse the fixed `1fr 1fr` grid to a single column.
Keeps label/value pairs readable on smaller screens.

**`resultGrid.tsx`** — Add `flex-wrap: wrap` to `SortSearchForm` and
wrap `ResultTable` in a `TableScrollWrapper` with `overflow-x: auto`.
Sort selector, search input, and table rows now scroll/wrap instead of
overflowing when horizontal space is tight.

**`layout.tsx`** — Add a collapsible mobile sidebar with a hamburger
toggle, slide-in transition, and dimmed overlay. Route changes
(including browser back/forward) close the drawer automatically via
`useLocation`. Body scroll is locked while the overlay is open.

## Why

`_admin` has no responsive CSS in its core layout or shared components.
These are pure CSS and minimal JS state changes; zero new network
requests or non-trivial side effects.

## Not included here

- Table column hiding — more design judgment needed on which columns to
deprioritize

Action taken on behalf of rahul.

---
[View Session in
Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780839152.391199%22)

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: rahul <rahul.chhabria@sentry.io>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components Trigger: getsentry tests Once code is reviewed: apply label to PR to trigger getsentry tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants