Skip to content

User-controlled refresh for read-only lists, stop auto-refetch on tab refocus - #1911

Open
RotemCDos wants to merge 11 commits into
kafbat:mainfrom
IDFCTS:feature/controllable-table-refresh
Open

User-controlled refresh for read-only lists, stop auto-refetch on tab refocus#1911
RotemCDos wants to merge 11 commits into
kafbat:mainfrom
IDFCTS:feature/controllable-table-refresh

Conversation

@RotemCDos

@RotemCDos RotemCDos commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Closes #1009, closes #4027, closes #472

  • Breaking change? (if so, please describe the impact and migration path for existing application instances)

Not a breaking change. It does change a default behavior: read-only list tables no longer refetch automatically when the browser tab regains focus. No config/migration is required — users refresh on demand via the new control, or opt into an auto-refresh interval.

What changes did you make? (Give an overview)

Read-only list tables used to refetch on their own — most noticeably every time you switched away from the tab and came back (TanStack Query's default refetchOnWindowFocus), which #1009 reported as random, unsolicited reloads. At the same time #4027/#472 asked for a manual refresh button and an opt-in auto-refresh. This PR resolves both by putting refreshing under the user's control:

  • Stop involuntary refetching (FE: UX: Do not auto refresh r/o data #1009): set refetchOnWindowFocus: false and refetchOnReconnect: false on the global QueryClient (App.tsx). refetchOnMount is intentionally left at its default (true) so navigating to a page still loads fresh data.
  • Reusable refresh control (#4027, UX: Add auto/manual refresh for pages #472): a Grafana-style split button — the left segment triggers a manual refresh (spinner while fetching); the right segment shows the active auto-refresh interval and opens a menu (Off / 2s / 5s / 10s / 15s). Added to the Topics, Schema Registry, Kafka Connect connectors, and Consumer Groups list pages. Consumer Groups' existing rate dropdown is migrated onto it.
  • Supporting pieces: a useRefreshRate(storageKey) hook that turns the persisted rate into a refetchInterval; list query hooks now accept that interval; each page's loader guard was changed from isLoading || isRefetching to isLoading so auto/manual refresh no longer blanks the whole table (data stays put via placeholderData).

Is there anything you'd like reviewers to focus on?

  • The refetchOnWindowFocus/refetchOnReconnect: false change in App.tsx is app-wide by design, not just the list pages. Detail/metrics pages that previously freshened on tab-refocus now update on remount or via their own polling (e.g. the consumer-lag pollers set their own refetchInterval). Keeping refetchOnMount: true was a deliberate choice.
  • Topics splits its list query (useTopics) into a child component, so the control drives refresh via useIsFetching / queryClient.refetchQueries scoped with type: 'active' on the topicKeys.all prefix — this targets only the mounted list query, not cached topic-detail queries.

How Has This Been Tested? (put an "x" (case-sensitive!) next to an item)

  • No need to
  • Manually (please, describe, if necessary)
  • Unit checks
  • Integration checks
  • Covered by existing automation

Manually: ran the frontend dev server against the documentation/compose/kafbat-ui.yaml stack and verified on all four pages that the manual button refetches (spinner, no full-page blank), the interval selector auto-polls and "Off" stops it, and returning to the tab no longer triggers a refetch. Unit: added/updated Jest + RTL tests for the split button (incl. menu selection persisting the interval), useRefreshRate, the global query defaults (asserting refetchOnMount is preserved), and each page's control; all suites pass locally.

Checklist (put an "x" (case-sensitive!) next to all the items, otherwise the build will fail)

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (e.g. ENVIRONMENT VARIABLES)
  • My changes generate no new warnings (e.g. Sonar is happy)
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged

Check out Contributing and Code of Conduct

A picture of a cute animal (not mandatory but encouraged)

Summary by CodeRabbit

  • New Features

    • Added a shared split Refresh + auto-refresh interval control to Connectors, Consumer Groups, Schemas, and Topics lists (persisted locally).
    • Intervals support Off, 2, 5, 10, 15 seconds with clear loading/spinner feedback.
  • Improvements

    • Updated query behavior for more reliable offline-first operation by reducing automatic refetching.
    • Refined list loading UI so full-page loaders appear on initial load only (not during background refresh).
  • Tests

    • Expanded coverage for refresh clicks, interval persistence, and updated loading states.

RotemCDos and others added 9 commits July 13, 2026 10:17
…val menu)

Replaces the separate Refresh button + rate dropdown with a single joined
control: the left segment triggers a manual refresh (spinner while fetching),
the right segment shows the active auto-refresh interval and opens a menu to
change it. Interval still persists under the same storage key, so useRefreshRate
and the query polling are unaffected. RefreshRateSelect remains for its other
consumers (consumer-group lag pollers).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@RotemCDos
RotemCDos requested a review from a team as a code owner July 13, 2026 08:32
@kapybro kapybro Bot added status/triage/manual Manual triage in progress and removed status/triage/manual Manual triage in progress labels Jul 13, 2026
@kapybro

kapybro Bot commented Jul 13, 2026

Copy link
Copy Markdown

AI Summary

Read-only list tables previously auto-refetched whenever the browser tab regained focus, causing unwanted reloads. This change disables involuntary refetching globally by setting refetchOnWindowFocus: false and refetchOnReconnect: false, while adding a split-button control that lets users manually refresh lists or select an auto-refresh interval (Off, 2s, 5s, 10s, 15s). The control was implemented across Topics, Schema Registry, Kafka Connect connectors, and Consumer Groups pages.

@kapybro kapybro Bot changed the title FE: Add user-controlled refresh to read-only lists & stop auto-refetch on tab refocus User-controlled refresh for read-only lists, stop auto-refetch on tab refocus Jul 13, 2026
@kapybro kapybro Bot added area/connect Kafka Connect, its connectors area/consumers area/sr Schema Registry area/topics area/ux User experiense issues scope/frontend Related to frontend changes type/feature A brand new feature labels Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0b08603c-c760-40a0-9434-96cbc3e3acbb

📥 Commits

Reviewing files that changed from the base of the PR and between b7a21df and 0b58591.

📒 Files selected for processing (3)
  • frontend/src/components/Topics/List/ListPage.tsx
  • frontend/src/components/Topics/List/TopicTable.tsx
  • frontend/src/components/common/RefreshRateSelect/RefreshRateSelect.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/components/common/RefreshRateSelect/RefreshRateSelect.tsx
  • frontend/src/components/Topics/List/TopicTable.tsx
  • frontend/src/components/Topics/List/ListPage.tsx

📝 Walkthrough

Walkthrough

Adds a reusable TableRefresh control with persisted polling intervals, integrates it across connector, consumer-group, schema, and topic lists, and updates React Query defaults to disable involuntary refetching. Tests cover configuration, refresh interactions, polling conversion, and persistence.

Changes

Refresh polling flow

Layer / File(s) Summary
React Query default configuration
frontend/src/components/App.tsx, frontend/src/components/__tests__/queryClientConfig.spec.ts
Exports typed defaults using offlineFirst, disables focus and reconnect refetching, and preserves mutation error handling with configuration tests.
Shared refresh control and rate contract
frontend/src/components/common/RefreshRateSelect/..., frontend/src/components/common/TableRefresh/*, frontend/src/components/common/Icons/RefreshIcon.tsx, frontend/src/lib/hooks/useRefreshRate.ts, frontend/src/lib/hooks/__tests__/useRefreshRate.spec.ts
Adds refresh storage-key typing, a split manual/automatic refresh control, persisted interval handling, shared styling, an icon, and focused tests.
List-page polling and manual refresh
frontend/src/components/Connect/List/*, frontend/src/components/ConsumerGroups/*, frontend/src/components/Schemas/List/*, frontend/src/components/Topics/List/*, frontend/src/lib/hooks/api/topics.ts
Wires refresh intervals and manual refetch controls into list pages, disables background refetching, separates initial loading from fetching, and extends useTopics with query options. Supporting tests cover the new controls.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant TableRefresh
  participant LocalStorage
  participant useRefreshRate
  participant ReactQuery
  Operator->>TableRefresh: Select auto-refresh interval
  TableRefresh->>LocalStorage: Store rate in seconds
  useRefreshRate->>LocalStorage: Read stored rate
  useRefreshRate->>ReactQuery: Configure refetchInterval
  Operator->>TableRefresh: Click Refresh
  TableRefresh->>ReactQuery: Trigger refetch
Loading

Possibly related PRs

Poem

I’m a rabbit with a refresh in my paw,
Polling the tables by interval law.
Click once, and the rows hop anew,
Local storage remembers too.
Fresh little lists, behaving right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: user-controlled refresh for list pages and disabling automatic refetch on focus/reconnect.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 1

🧹 Nitpick comments (2)
frontend/src/components/common/Icons/RefreshIcon.tsx (1)

5-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add aria-hidden="true" to the decorative SVG.

The icon is used inside a button with aria-label="Refresh", making the SVG purely decorative. Adding aria-hidden="true" prevents redundant screen reader announcements.

♿ Proposed fix
     <svg
       xmlns="http://www.w3.org/2000/svg"
       viewBox="0 0 512 512"
       width="14"
       height="14"
       fill="currentColor"
+      aria-hidden="true"
+      focusable="false"
     >
🤖 Prompt for 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.

In `@frontend/src/components/common/Icons/RefreshIcon.tsx` around lines 5 - 14,
Add aria-hidden="true" to the decorative SVG element in RefreshIcon, preserving
the existing button aria-label and icon markup.
frontend/src/lib/hooks/api/topics.ts (1)

66-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider omitting placeholderData from queryOptions to prevent accidental override.

...queryOptions is spread after placeholderData: (previousData) => previousData, so a caller could override it and break the "keep existing data visible during refetches" guarantee. The current caller only passes refetchInterval/refetchIntervalInBackground, so this isn't an active bug, but adding 'placeholderData' to the Omit list would make the contract safer.

♻️ Suggested fix
 export function useTopics(
   props: GetTopicsRequest,
   queryOptions?: Omit<
     UseQueryOptions<TopicsResponse, ServerResponse>,
-    'queryKey' | 'queryFn'
+    'queryKey' | 'queryFn' | 'placeholderData'
   >
 ) {
🤖 Prompt for 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.

In `@frontend/src/lib/hooks/api/topics.ts` around lines 66 - 80, Update the
useTopics queryOptions type to omit placeholderData alongside queryKey and
queryFn, preventing callers from overriding the hook’s existing-data behavior
while preserving the current option spread and refetch configuration support.
🤖 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 `@frontend/src/components/common/TableRefresh/TableRefresh.tsx`:
- Around line 51-58: Update the interval toggle in the TableRefresh Dropdown so
Dropdown’s open state is passed to DropdownArrowIcon via isOpen, and ensure the
button’s accessible name includes the current interval label instead of being
hidden by the static aria-label. Preserve the existing rate display and toggle
behavior.

---

Nitpick comments:
In `@frontend/src/components/common/Icons/RefreshIcon.tsx`:
- Around line 5-14: Add aria-hidden="true" to the decorative SVG element in
RefreshIcon, preserving the existing button aria-label and icon markup.

In `@frontend/src/lib/hooks/api/topics.ts`:
- Around line 66-80: Update the useTopics queryOptions type to omit
placeholderData alongside queryKey and queryFn, preventing callers from
overriding the hook’s existing-data behavior while preserving the current option
spread and refetch configuration support.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: efc15020-b723-4c85-b241-c490ce611722

📥 Commits

Reviewing files that changed from the base of the PR and between bc2d7ca and 9511d11.

📒 Files selected for processing (19)
  • frontend/src/components/App.tsx
  • frontend/src/components/Connect/List/ListPage.tsx
  • frontend/src/components/Connect/List/__tests__/ListPage.spec.tsx
  • frontend/src/components/ConsumerGroups/List.tsx
  • frontend/src/components/ConsumerGroups/__test__/List.spec.tsx
  • frontend/src/components/Schemas/List/List.tsx
  • frontend/src/components/Schemas/List/__test__/List.spec.tsx
  • frontend/src/components/Topics/List/ListPage.tsx
  • frontend/src/components/Topics/List/TopicTable.tsx
  • frontend/src/components/Topics/List/__tests__/ListPage.spec.tsx
  • frontend/src/components/__tests__/queryClientConfig.spec.ts
  • frontend/src/components/common/Icons/RefreshIcon.tsx
  • frontend/src/components/common/RefreshRateSelect/RefreshRateSelect.tsx
  • frontend/src/components/common/TableRefresh/TableRefresh.styled.ts
  • frontend/src/components/common/TableRefresh/TableRefresh.tsx
  • frontend/src/components/common/TableRefresh/__tests__/TableRefresh.spec.tsx
  • frontend/src/lib/hooks/__tests__/useRefreshRate.spec.ts
  • frontend/src/lib/hooks/api/topics.ts
  • frontend/src/lib/hooks/useRefreshRate.ts

Comment on lines +51 to +58
<Dropdown
aria-label="Auto refresh interval"
openBtnEl={
<S.IntervalButton type="button">
{rate > 0 && <S.Rate>{current?.label}</S.Rate>}
<DropdownArrowIcon isOpen={false} />
</S.IntervalButton>
}

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the Dropdown component to verify openBtnEl rendering behavior.

# Find the Dropdown component implementation
ast-grep outline frontend/src/components/common/Dropdown --items all --type function,component --match 'Dropdown'

# Then read the relevant rendering logic
# Look for how openBtnEl is rendered: is it wrapped in a <button>? 
# Is isOpen injected via cloneElement? Is aria-label passed to openBtnEl?

Repository: kafbat/kafka-ui

Length of output: 200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the Dropdown implementation and related props/usages.
fd -a 'Dropdown*' frontend/src/components/common
rg -n "openBtnEl|isOpen|aria-label|cloneElement|<button|DropdownArrowIcon" frontend/src/components/common/Dropdown frontend/src/components/common/TableRefresh -S

# Map the Dropdown component structure before reading it.
ast-grep outline frontend/src/components/common/Dropdown --match 'Dropdown' --view expanded || true

# Read the likely implementation files if present.
for f in \
  frontend/src/components/common/Dropdown/Dropdown.tsx \
  frontend/src/components/common/Dropdown/index.ts \
  frontend/src/components/common/Dropdown/DropdownButton.tsx
do
  [ -f "$f" ] && { echo "===== $f ====="; cat -n "$f"; }
done

Repository: kafbat/kafka-ui

Length of output: 5568


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n frontend/src/components/common/Icons/DropdownArrowIcon.tsx
echo "===== TableRefresh ====="
cat -n frontend/src/components/common/TableRefresh/TableRefresh.tsx

Repository: kafbat/kafka-ui

Length of output: 4128


Pass the open state into the interval toggle

  • Dropdown clones openBtnEl into a <span>, so nested buttons aren’t an issue.
  • The arrow still never receives isOpen, and the cloned aria-label hides the current interval from the button’s accessible name. Include the rate in the label or use aria-labelledby.
🤖 Prompt for 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.

In `@frontend/src/components/common/TableRefresh/TableRefresh.tsx` around lines 51
- 58, Update the interval toggle in the TableRefresh Dropdown so Dropdown’s open
state is passed to DropdownArrowIcon via isOpen, and ensure the button’s
accessible name includes the current interval label instead of being hidden by
the static aria-label. Preserve the existing rate display and toggle behavior.

- RefreshIcon: mark the decorative SVG aria-hidden/focusable=false (icon sits
  in a button that already has an accessible name)
- TableRefresh: make the interval toggle's accessible name convey the active
  interval (e.g. 'Auto refresh interval: every 5s') instead of a static label
  that hid the current value from screen readers
- useTopics: omit placeholderData from the accepted queryOptions so callers
  can't override the keep-previous-data behavior

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@RotemCDos

Copy link
Copy Markdown
Contributor Author

Some images from the change:
image
image
image

Please note that the refresh picker of the consumer groups is also changed to better match the rest of the pickers

@RotemCDos

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

The Topics list reused 'topics-refresh-rate', which the topic-detail
Consumer Groups sub-tab (TopicConsumerGroups) already uses for its
lag-polling interval. Sharing the key made the two unrelated controls
overwrite each other's value. Use a distinct 'topics-list-refresh-rate'
key for the list page.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/connect Kafka Connect, its connectors area/consumers area/sr Schema Registry area/topics area/ux User experiense issues scope/frontend Related to frontend changes type/feature A brand new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FE: UX: Do not auto refresh r/o data UX: Add auto/manual refresh for pages

1 participant