feat(consumers): migrate consumer groups to UI Registry - #2545
Conversation
Migrate the consumer groups list, detail page, and the edit/delete offset modals from legacy @redpanda-data/ui to the Registry components. List (group-list.tsx): - TanStack useReactTable + Registry Table inside ListLayout - URL-persisted page/pageSize/sort/search (nuqs), sortable columns, pagination, and a State faceted filter (all states always available) - New useLegacyListConsumerGroupsFullQuery hook (Connect/REST) returning enriched GroupDescription (lagSum/isInUse/perms) Detail (group-details.tsx): - Registry Tabs (Topics/ACL, tab persisted in URL), Card stats bar, Accordion per topic, and a sortable+paginated PartitionTable - Disabled Edit/Delete buttons now show an explanatory tooltip - Shared ConsumerGroupStateCell (icon + state) reused by list and detail Modals (modals.tsx): - DeleteOffsetsModal -> Registry AlertDialog - EditOffsetsModal -> Registry Dialog/Select/RadioGroup/Input/Accordion + useReactTable preview tables; toasts moved to sonner - KowlTimePicker kept (no Registry datetime picker yet) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🚨 Registry drift detectedApp:
Components needing attention
Refresh command: bunx shadcn@latest add @redpanda/stat --overwriteGenerated by lookout audit-changes. |
base-ui SelectValue renders the raw selected value, so the "All
Topics/Partitions" sentinel ('__all__') and the strategy keys
(endOffset, ...) leaked into the trigger. Map value -> label via a
SelectValue render function, matching the add-acl-dialog pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Empty groups have no protocol/protocolType, which rendered blank stat values, and the legacy ProtocolType helper duplicated protocolType under a "Protocol" label. Show the actual group.protocol field (matching the list column) and group.protocolType with an em-dash fallback, and drop the redundant ProtocolType component. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Coordinator ID copy button used size="icon" (size-9, 36px), which was much taller than the stat label text; the flex row stretched every stat to that height, inflating the card and leaving gaps under the other labels. Shrink the copy button to size-5 and add items-start so the row hugs its natural content height. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The migrated partition action buttons use the registry (base-ui) tooltip,
which renders a `[data-slot="tooltip-content"]` popup without
role="tooltip" (the old Chakra tooltip had it). Match on the slot + text
instead of getByRole('tooltip'). No app code changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Local e2e runs remap every host port via E2E_PORTS_OVERRIDE, but the enterprise config hardcoded shadowBackendURL to http://localhost:3101, so the shadowlink tests hit ERR_CONNECTION_REFUSED (the dest backend was on a random port). Resolve the shadow backend port from the override (falling back to 3101 for CI/static-port runs). No app code changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move DisabledReasonButton out of group-details.tsx into src/components/ui/ so it can be reused outside consumer group details. Behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two layout fixes surfaced by the group list page: - group-list rendered its table inside ListLayoutContent (flex-1), which grew to fill the min-h-screen layout and pushed the pagination bar to the bottom of the viewport. Render <Table> directly like the other list pages so pagination sits under the last row. - ListLayoutPagination did not stretch its child, so DataTablePagination collapsed to content width and its internal justify-between clustered the controls on the left. Add [&>*]:w-full so the controls span the row (also fixes topics/quotas/users/roles list pages). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y drift The pagination full-width fix edited the vendored list-layout registry component in-place, which the UI audit flags as locally-modified drift. Revert list-layout to its released bytes and apply [&>*]:w-full via className at the ListLayoutPagination call site instead — same render, no registry drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Console's dialog and tabs were pinned to registry 2.3.0's predecessor
(2.2.0), which the frontend UI audit flags as outdated once the consumer
pages use them. Sync both to the 2.3.0 release:
- dialog: scroll-shadow refactor — useScrollShadow now takes the container
ref and returns axis-relative { start, end } edges (was sentinel refs +
{ top, bottom }).
- use-scroll-shadow: rewritten to the 2.3.0 geometry-based API (scroll +
ResizeObserver/MutationObserver, horizontal orientation support). Only
caller in console is DialogBody; rp-connect only mentions it in a comment.
- tabs: add ScrollableTabsList, pulling in the new drag-scroll-area dep.
- drag-scroll-area: new component from 2.3.0.
Audit now reports dialog + tabs up-to-date at 2.3.0; 0 drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // Registry (base-ui) tooltip renders a `[data-slot="tooltip-content"]` popup | ||
| // without role="tooltip", so match on the slot + text instead of the role. |
There was a problem hiding this comment.
sounds like we should fix this so we can have simpler test assertions to write
Base UI's tooltip popup sets no ARIA role, so the disabled-action tooltip was only reachable via a [data-slot] selector. Set role="tooltip" at the call site (keeping the registry component free of drift) so it is exposed as a tooltip to assistive tech and to getByRole in tests. Derive the button variant/size props from the registry ButtonProps so they cannot drift from the Button they are forwarded to. Drop the per-assertion timeouts in the e2e spec: the Playwright config already sets a 60s expect timeout, so they were only shortening the default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tooltip reads "No committed offsets" (plural). Switching the assertion from a substring filter to toHaveText made the exact text matter, so the singular string no longer matched and the test timed out. Pull the string into a constant so it stays tied to group-details.tsx. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopt the registry Stat component for the group list and detail stats bars, replacing the hand-rolled label/value stacks. Swap both search fields to registry Input + InputStart/InputEnd so the padding and icon positioning come from the component instead of ad-hoc absolute positioning, and associate the "topics with lag" checkbox with a registry Label via htmlFor. Drop the activeClassName="after:bg-foreground" override on the tabs so the underline uses the registry's intended after:bg-selected, and remove the hover:bg-transparent override on the empty-state row: TableRow has no hover background, only data-[state=selected], which this row never gets. Hoist the column meta type out of render into a shared module, derived from the registry's tableHeadVariants so it cannot drift, and compose the disabled trigger in DisabledReasonButton from buttonVariants rather than hardcoding h-8 w-8. stat.tsx is vendored from ui-registry v2.3.0 to match the tagged release the audit classifies against. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er-groups-to-ui-registry # Conflicts: # frontend/src/routes/groups/$groupId.tsx
| @@ -0,0 +1,171 @@ | |||
| import { cva, type VariantProps } from 'class-variance-authority'; | |||
There was a problem hiding this comment.
Minor: Registry has a newer version of stat, lets re-pull this in
|
This PR is becoming a bit stale, can it be rebased + fix the lint errors and I'll take another look at it |
The merge from master clobbered GroupList's return JSX with a leftover fragment from master's old GroupId helper (undefined protocol/groupIdEl refs), and an earlier "remove console ai features" cleanup deleted the whole react-query/api/consumer-group.tsx file instead of just the AI-only export, taking useLegacyListConsumerGroupsFullQuery with it. Restore the pre-merge return statement (ListLayout/Stat/filters/table/ pagination) and re-add useLegacyListConsumerGroupsFullQuery.
SpicyPete
left a comment
There was a problem hiding this comment.
Looking great overall, just a few findings from Claude which are probably worth either validating / double checking, or fixing.
I would also run another local review or two and fixing the issues, it's finding a few smaller cleanup details and issues which I'm not including below
- The list can serve up to 5-minute-stale data in Cloud, including groups you just deleted — react-query/api/consumer-group.tsx:16
The list moved from api.refreshConsumerGroups(true) (always forced on mount) to useLegacyListConsumerGroupsFullQuery with no staleTime override, so it inherits the client default: 3s standalone (query-client.ts:12), but 5 minutes in embedded/Cloud mode (federation/console-app.tsx:173 → LONG_LIVED_CACHE_STALE_TIME). modals.tsx:927 still calls api.refreshConsumerGroups(true) after a delete, but nothing invalidates ['consumer-groups', 'full']. Delete a group → historyReplace('/groups') (modals.tsx:917) → in Cloud the deleted group is still listed until the user hits Refresh.
Fix: queryClient.invalidateQueries({ queryKey: ['consumer-groups'] }) in the delete/edit handlers, or staleTime: 0 on the hook.
- consumerGroupStateNames is exported but never used, and the UI shows raw state strings instead — components/ui/consumer-group/consumer-group-state-cell.tsx:28
…tate labels - Delete/offset-delete only refreshed the legacy mobx store, leaving the react-query consumer-groups list stale (up to 5min in Cloud) after a delete - ConsumerGroupStateCell rendered the raw backend state string instead of the already-defined human-readable label
Summary
Migrates the consumer groups feature (list, detail, and the edit/delete offset modals) from the legacy
@redpanda-data/uicomponents to the Registry components — matching the already-migrated Topics/Security pages.List (
group-list.tsx)useReactTable+ RegistryTableinsideListLayoutnuqs), sortable columns, paginationuseLegacyListConsumerGroupsFullQueryhook returning enrichedGroupDescription(lagSum/isInUse/perms)Detail (
group-details.tsx)Tabs(Topics / ACL, active tab persisted in the URL),Cardstats bar,Accordionper topic, sortable + paginatedPartitionTableConsumerGroupStateCell(icon + state) reused by both list and detailModals (
modals.tsx)DeleteOffsetsModal→ RegistryAlertDialogEditOffsetsModal→ RegistryDialog/Select/RadioGroup/Input/Accordion+useReactTablepreview tables; toasts moved tosonner.KowlTimePickerkept (no Registry datetime picker yet).modals.tsxno longer imports@redpanda-data/uiat all.Test plan
bun run type:check— passbun run lint— passbun run test:integration— all consumer-group tests pass (group-details2/2); full suite green apart from one unrelated flakyobservability-pagetest that passes in isolation🤖 Generated with Claude Code