Skip to content

fix: standardize pagination UI and fix scrolling layout across table views - #4433

Merged
akshaydeo merged 1 commit into
devfrom
06-16-fix_workspace_model_pages_style_fixes
Jun 19, 2026
Merged

fix: standardize pagination UI and fix scrolling layout across table views#4433
akshaydeo merged 1 commit into
devfrom
06-16-fix_workspace_model_pages_style_fixes

Conversation

@impoiler

Copy link
Copy Markdown
Contributor

Summary

Standardizes the pagination UI across the Model Limits, Routing Rules, and Custom Pricing Overrides table views, and fixes a layout issue on the Model Limits page that caused it to overflow rather than fit within the viewport.

Changes

  • Replaced "Previous/Next" labeled outline buttons with icon-only ghost buttons (ChevronLeft/ChevronRight) and added aria-label attributes for accessibility
  • Added a "Page X of Y" indicator between the navigation buttons
  • Changed the entry count display from "Showing X-Y of Z" to "X-Y of Z entries" with locale-formatted numbers
  • Added a data-testid="pagination" attribute to the pagination container across all three views for consistency
  • Wrapped the Model Limits page in a full-height flex container to prevent overflow and enable proper scroll containment within the table
  • Made the table header sticky with a background so it remains visible while scrolling
  • Added an isLoading prop to ModelLimitsTable to suppress the empty state flash before the initial API response arrives, and to show a "Loading model limits..." message in the table body during load

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Navigate to the Model Limits, Routing Rules, and Custom Pricing Overrides pages with enough records to trigger pagination and verify:

  1. The pagination controls show icon-only previous/next buttons with a "Page X of Y" label between them.
  2. Entry counts are locale-formatted (e.g., 1,000).
  3. The Model Limits page does not overflow the viewport; the table scrolls internally with a sticky header.
  4. On initial page load, the empty state is not briefly flashed before data arrives; instead, "Loading model limits..." is shown in the table body.
cd ui
pnpm i || npm i
pnpm build || npm run build

Screenshots/Recordings

Before/after screenshots of the pagination controls and Model Limits layout recommended.

Breaking changes

  • No

Related issues

Security considerations

None.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@impoiler, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 11 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 35ee32ba-15da-45c8-bda4-87853528023b

📥 Commits

Reviewing files that changed from the base of the PR and between dc397f0 and 41c326c.

📒 Files selected for processing (8)
  • ui/app/workspace/custom-pricing/overrides/page.tsx
  • ui/app/workspace/custom-pricing/overrides/scopedPricingOverridesView.tsx
  • ui/app/workspace/model-limits/page.tsx
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/model-limits/views/modelLimitsView.tsx
  • ui/app/workspace/routing-rules/page.tsx
  • ui/app/workspace/routing-rules/views/routingRulesTable.tsx
  • ui/app/workspace/routing-rules/views/routingRulesView.tsx
📝 Walkthrough

Walkthrough

Three workspace pages (custom-pricing overrides, model-limits, routing-rules) receive consistent layout changes: page containers switch to full-viewport flex-column sizing; tables gain sticky headers, pinned right "Actions" columns styled with PIN_SHADOW_RIGHT, and group-hover row semantics; and all pagination bars are replaced with a compact icon-only ghost-button design. ModelLimitsTable also gains an isLoading prop to suppress premature empty-state rendering.

Changes

Table layout and pagination standardization

Layer / File(s) Summary
Page container layout
ui/app/workspace/custom-pricing/overrides/page.tsx, ui/app/workspace/model-limits/page.tsx, ui/app/workspace/routing-rules/page.tsx
Top-level wrapper div on all three pages changes from mx-auto max-w-7xl to a full-width flex-column layout with h-[calc(100dvh-1rem)], overflow-hidden, and p-4. ModelLimitsPage gains a wrapping div it previously lacked.
isLoading prop threading (ModelLimits)
ui/app/workspace/model-limits/views/modelLimitsTable.tsx, ui/app/workspace/model-limits/views/modelLimitsView.tsx
ModelLimitsTableProps adds optional isLoading?: boolean; the empty-state guard adds !isLoading to prevent a flash during initial fetch; ModelLimitsView destructures isLoading from useGetModelConfigsQuery and passes it into ModelLimitsTable while removing its outer wrapper div.
Sticky header + pinned right Actions column
ui/app/workspace/custom-pricing/overrides/scopedPricingOverridesView.tsx, ui/app/workspace/model-limits/views/modelLimitsTable.tsx, ui/app/workspace/routing-rules/views/routingRulesTable.tsx
All three table components import PIN_SHADOW_RIGHT and apply it to the Actions header and cell. Table containers gain overflow-bounded wrappers with a scrollable containerClassName; header rows become sticky; row elements add group for coordinated hover; the Actions TableCell is now sticky-right with theme-aware backgrounds and preserved click-propagation blocking.
Compact pagination UI
ui/app/workspace/custom-pricing/overrides/scopedPricingOverridesView.tsx, ui/app/workspace/model-limits/views/modelLimitsTable.tsx, ui/app/workspace/routing-rules/views/routingRulesTable.tsx
Pagination bars across all three tables replace "Showing…" text and outline Previous/Next text buttons with a text-xs flex row showing entry range, total, and "Page X of Y", with icon-only ghost chevron buttons and added data-testid/aria-label attributes. Offset math and disabled logic are unchanged.
View layout wrapper updates
ui/app/workspace/routing-rules/views/routingRulesView.tsx, ui/app/workspace/model-limits/views/modelLimitsView.tsx
routingRulesView outer container changes from space-y-4 to flex flex-col overflow-y-auto with updated header spacing; modelLimitsView removes its wrapper div around ModelLimitsTable; both files have minor import reordering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • maximhq/bifrost#3483: Modifies modelLimitsTable.tsx's Actions cell UI by replacing inline edit/delete buttons with a dropdown menu — the same cell this PR makes sticky and pinned.
  • maximhq/bifrost#3484: Replaces inline edit/delete buttons in routingRulesTable.tsx with a dropdown, directly in the same Actions column this PR converts to sticky/pinned.
  • maximhq/bifrost#4046: Applies the same no-padding-parent and h-[calc(100dvh-1rem)] page container standardization to other workspace page wrappers.

Suggested reviewers

  • akshaydeo
  • danpiths

Poem

🐇 Hop, hop, the table rows now stick,
Pinned shadows right, pagination slick!
No max-width walls to crowd the view,
Full-viewport flex — the layout grew.
Ghost buttons chevron, left and right,
A compact bar, a cleaner sight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: standardizing pagination UI and fixing scrolling layout across three table views, which aligns with the actual code modifications.
Description check ✅ Passed The description is well-structured, follows the template, includes all major sections (Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, Security considerations, and Checklist), and clearly documents the changes and testing approach.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-16-fix_workspace_model_pages_style_fixes

Comment @coderabbitai help to get the list of available commands and usage tips.

@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch 4 times, most recently from 6321ee6 to 4976ecd Compare June 16, 2026 11:57
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 08c9151 to 71fd592 Compare June 16, 2026 11:57
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 71fd592 to fe98418 Compare June 16, 2026 12:18
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 4976ecd to 5e8194b Compare June 16, 2026 12:18
@impoiler impoiler self-assigned this Jun 16, 2026
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 5e8194b to aaea614 Compare June 17, 2026 14:04
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 1f9a2bc to 3a465f2 Compare June 18, 2026 06:38
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from aaea614 to 2f1aba0 Compare June 18, 2026 06:38
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 3a465f2 to a49acb3 Compare June 18, 2026 07:12
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 2f1aba0 to 3e70c05 Compare June 18, 2026 07:12
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 3e70c05 to 83783a5 Compare June 18, 2026 08:37
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from a49acb3 to 634983f Compare June 18, 2026 08:37
@impoiler
impoiler marked this pull request as ready for review June 18, 2026 08:51
@coderabbitai
coderabbitai Bot requested a review from akshaydeo June 18, 2026 08:52
@greptile-apps

greptile-apps Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Pure UI refactor — pagination controls, layout shell, and loading state — with no logic or data-flow changes outside of the new isLoading prop; all existing data-testid attributes used by E2E tests are preserved.

All three pages use an established pattern already present in Virtual Keys and Model Catalog. The isLoading guard in ModelLimitsTable is correctly wired and handles the edge cases (active filters, subsequent page loads). No E2E test selectors are broken: per-button testids (e.g. routing-rules-pagination-prev-btn) are unchanged, and the new data-testid="pagination" container is additive.

No files require special attention.

Important Files Changed

Filename Overview
ui/app/workspace/model-limits/page.tsx Wraps the view in a full-height flex container with no-padding-parent; removes the old inner max-w-7xl wrapper in favour of a viewport-height constrained shell.
ui/app/workspace/model-limits/views/modelLimitsView.tsx Extracts isLoading from useGetModelConfigsQuery and passes it down to ModelLimitsTable to suppress the empty-state flash on initial load.
ui/app/workspace/model-limits/views/modelLimitsTable.tsx Adds isLoading prop; guards the true-empty-state early return; shows 'Loading model limits...' in the table body during fetch; standardises pagination UI (icon-only ghost buttons, Page X of Y, locale-formatted counts, sticky header, PIN_SHADOW_RIGHT on pinned action column).
ui/app/workspace/routing-rules/page.tsx Applies the same full-height no-padding-parent shell as the other updated pages; removes max-w-7xl.
ui/app/workspace/routing-rules/views/routingRulesTable.tsx Pagination UI standardised (ghost icon buttons, Page X of Y, data-testid="pagination", locale-formatted counts); sticky header and PIN_SHADOW_RIGHT on pinned action column; group class added to rows for hover-state pinned cell background.
ui/app/workspace/routing-rules/views/routingRulesView.tsx Root div changed from space-y-4 to flex flex-col overflow-y-auto; import order normalised; no logic changes.
ui/app/workspace/custom-pricing/overrides/page.tsx Same full-height no-padding-parent shell applied; max-w-7xl removed for full-width table layout.
ui/app/workspace/custom-pricing/overrides/scopedPricingOverridesView.tsx Pagination UI standardised; sticky header; PIN_SHADOW_RIGHT applied to actions column; group class added for hover-state pinned cell; existing isLoading skeleton retained.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["page.tsx\n(no-padding-parent shell\nh-[calc(100dvh-1rem)] flex-col)"]
    A --> B["*View.tsx\n(flex flex-col overflow-y-auto)"]
    B --> C["Header + Toolbar divs\n(static, mb-4 spacing)"]
    B --> D["Border div\n(overflow-hidden rounded-sm border mb-2)"]
    D --> E["Table containerClassName=h-full overflow-auto\n(scroll surface)"]
    E --> F["TableHeader sticky top-0 bg-muted z-10\n(sticky within scroll surface)"]
    E --> G["TableBody rows\n(scrolls under sticky header)"]
    B --> H["Pagination div shrink-0\n(data-testid=pagination)\nIcon-only ghost buttons · Page X of Y · N-M of Z entries"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["page.tsx\n(no-padding-parent shell\nh-[calc(100dvh-1rem)] flex-col)"]
    A --> B["*View.tsx\n(flex flex-col overflow-y-auto)"]
    B --> C["Header + Toolbar divs\n(static, mb-4 spacing)"]
    B --> D["Border div\n(overflow-hidden rounded-sm border mb-2)"]
    D --> E["Table containerClassName=h-full overflow-auto\n(scroll surface)"]
    E --> F["TableHeader sticky top-0 bg-muted z-10\n(sticky within scroll surface)"]
    E --> G["TableBody rows\n(scrolls under sticky header)"]
    B --> H["Pagination div shrink-0\n(data-testid=pagination)\nIcon-only ghost buttons · Page X of Y · N-M of Z entries"]
Loading

Reviews (6): Last reviewed commit: "fix: workspace/model pages style fixes" | Re-trigger Greptile

Comment thread ui/app/workspace/model-limits/views/modelLimitsTable.tsx
Comment thread ui/app/workspace/routing-rules/views/routingRulesTable.tsx

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/app/workspace/model-limits/views/modelLimitsTable.tsx`:
- Line 540: The data-testid attribute on the pagination container div (with
className "flex shrink-0 items-center justify-between text-xs") currently uses
the simple value "pagination", which does not follow the repository's required
three-part naming convention of entity-element-qualifier. Update the data-testid
to follow this convention by identifying the appropriate entity (such as the
model limits table context), the element being tested (pagination), and a
qualifier if needed to make it more explicit and consistent with other test
selectors in the codebase.
- Around line 319-320: The TableHead element for the Actions column on line 319
has no accessible label, making it unclear to screen readers. Add a screen
reader only label inside this TableHead to identify it as the Actions column.
Use the sr-only CSS class to visually hide the label while keeping it available
to assistive technology. The label should be descriptive and indicate that this
column contains actions for the table rows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: add48785-b495-477f-9ff5-1fbfb7e91ed2

📥 Commits

Reviewing files that changed from the base of the PR and between 634983f and 83783a5.

📒 Files selected for processing (8)
  • ui/app/workspace/custom-pricing/overrides/page.tsx
  • ui/app/workspace/custom-pricing/overrides/scopedPricingOverridesView.tsx
  • ui/app/workspace/model-limits/page.tsx
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/model-limits/views/modelLimitsView.tsx
  • ui/app/workspace/routing-rules/page.tsx
  • ui/app/workspace/routing-rules/views/routingRulesTable.tsx
  • ui/app/workspace/routing-rules/views/routingRulesView.tsx

Comment thread ui/app/workspace/model-limits/views/modelLimitsTable.tsx
Comment thread ui/app/workspace/model-limits/views/modelLimitsTable.tsx
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 18, 2026
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 83783a5 to c44293f Compare June 18, 2026 16:57
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch 2 times, most recently from 100ef8c to 46651bd Compare June 18, 2026 17:11
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from c44293f to 2ace9ff Compare June 18, 2026 17:11
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 46651bd to 707a206 Compare June 18, 2026 17:39
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 2ace9ff to 06256a8 Compare June 18, 2026 17:39
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 707a206 to b591b18 Compare June 19, 2026 06:43
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 06256a8 to dc397f0 Compare June 19, 2026 06:43
@coderabbitai
coderabbitai Bot requested a review from danpiths June 19, 2026 06:44
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from b591b18 to 41d26da Compare June 19, 2026 06:46
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from dc397f0 to 3aaef6b Compare June 19, 2026 06:46
@impoiler
impoiler force-pushed the 06-16-fix_workspace_model_pages_style_fixes branch from 3aaef6b to 41c326c Compare June 19, 2026 06:51
@impoiler
impoiler force-pushed the 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search branch from 41d26da to 18b205d Compare June 19, 2026 06:51

akshaydeo commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Merge activity

  • Jun 19, 6:55 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 19, 7:01 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo changed the base branch from 06-16-fix_do_not_clear_selection_on_virtual_keys_table_on_filter_or_search to graphite-base/4433 June 19, 2026 07:00
@akshaydeo
akshaydeo changed the base branch from graphite-base/4433 to dev June 19, 2026 07:01
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review June 19, 2026 07:01

The base branch was changed.

@akshaydeo
akshaydeo merged commit 22c24e7 into dev Jun 19, 2026
11 checks passed
@akshaydeo
akshaydeo deleted the 06-16-fix_workspace_model_pages_style_fixes branch June 19, 2026 07:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants