Skip to content

[Chakra exit · Phase 2 · PR 16] frontend: the security ACLs tab on Registry components, plus a smoke spec - #2649

Merged
SpicyPete merged 4 commits into
masterfrom
chakra-exit/16-security-acls
Sep 11, 2026
Merged

[Chakra exit · Phase 2 · PR 16] frontend: the security ACLs tab on Registry components, plus a smoke spec#2649
SpicyPete merged 4 commits into
masterfrom
chakra-exit/16-security-acls

Conversation

@SpicyPete

Copy link
Copy Markdown
Contributor

Takes the last Chakra page file off the package: security/tabs/acls-tab.tsx. master goes 25 → 24, and once #2645 and #2647 merge that leaves five — all of them PR 13's own, which are deleted rather than migrated.

#2604 replaced the users, roles and permissions tabs with new designs and left ACLs on the old one. There is no acls-tab-new and the route is live at /security/acls (enterprise users-authorization.spec.ts navigates straight to it), so this is a parity swap, not throwaway work. Whether ACLs also gets the new security design is a separate product call — this only removes the dependency.

Reviewer's guide

Change What to look at
SearchFieldSearchInput The shared one, so the ACLs filter gains /-to-focus and Escape-to-clear like every other list.
DataTable → the Registry's Both sortable columns get a DataTableColumnHeader: the Registry sorts only through that component, where Chakra painted an affordance on every header, so a naive swap leaves sorting unreachable with nothing failing. The action column gets enableSorting: false, and the table passes enableHiding: false — there is no column-visibility toolbar to undo a hide with.
Pagination Legacy parity: 50 a page (DEFAULT_TABLE_PAGE_SIZE) with the pager only past that, matching transforms-list and connect/overview. Chakra's own default was defaultPageSize = 50 and it rendered the footer only when data.length > defaultPageSize.
The two size: props Dropped. The Registry DataTable never reads getSize(), so Number.POSITIVE_INFINITY and size: 60 were inert either way.
AclRowActions The row menu moves into its own component that owns its queries, which leaves the columns array closing over nothing but a stable state setter — so it is built once. DataTableColumnHeader is a dropdown trigger, and a re-created header tears an open sort menu down mid-interaction.

Also in here

The dead rs.mock('@redpanda-data/ui') in roles-tab.test.tsx is deleted — 156 lines stubbing ~30 components for a file whose tree no longer touches the package, since roles-tab.tsx is now just export { RolesTabNew as RolesTab } and roles-tab-new.tsx has zero Chakra imports. Both suites stay at 993 / 1427 with no failures, so those tests now exercise the real components instead of stub divs. This also clears one of PR 13's eleven bare-grep mop-up files, leaving ten.

Coverage

The ACLs table had no spec of its own — only an enterprise authorization test visits the route. acls-tab.spec.ts covers what the swap actually risked: the filter renders, both headers expose a sort trigger, and the trigger opens a menu rather than sorting on one click. Verified against a live container, and verified to fail when the DataTableColumnHeader is swapped back for a plain string header. A fresh cluster has no ACLs, so it asserts the chrome rather than rows.

Gates

type:check clean · lint:check 0 diagnostics on acls-tab.tsx (down from 2 on master — the Chakra import was itself a noRestrictedImports error, and the extraction removed an empty onClick), parsed with the colour codes stripped · 993 unit · 1427 integration, both identical to master · the new spec passes 2/2 in the console variant.

Flagged, not fixed

  • acl-delete-multi-match.spec.ts is test.skip end to end, both cases, pending UX-1216. A spec file is not evidence of coverage.
  • The three re-export shims (users-tab.tsx, roles-tab.tsx, permissions-list-tab.tsx) now only forward to their -new counterparts. Collapsing them so the routes import -new directly is Remove enableNewSecurityPage feature flag #2604 follow-up hygiene, not Chakra work.
  • permissions-list-tab.test.tsx:121 still mocks enableNewSecurityPage, a flag that no longer exists in source. Vestigial after Remove enableNewSecurityPage feature flag #2604.

🤖 Generated with Claude Code

…spec

#2604 replaced the users, roles and permissions tabs with new designs and
left the ACLs tab on the old one, so it kept the last Chakra import in
`pages/security`. There is no `acls-tab-new`, and the route is live at
`/security/acls`, so this is a parity swap rather than throwaway work:
Chakra's `SearchField` becomes the shared `SearchInput` and its
`DataTable` becomes the Registry one. Whether ACLs also gets the new
security design is a separate product call.

Both sortable columns get a `DataTableColumnHeader` — the Registry sorts
only through that component, where Chakra painted an affordance on every
header — and the table passes `enableHiding: false`, since there is no
column-visibility toolbar to undo a hide with. Pagination keeps legacy
parity at 50 a page with the pager only past that; the two `size:` props
are dropped because the Registry never reads them.

The row-actions menu moves into its own component that owns its queries.
That leaves the columns array closing over nothing but a stable state
setter, so it is built once: `DataTableColumnHeader` is a dropdown
trigger, and a re-created header tears an open sort menu down.

Also drops the dead `rs.mock('@redpanda-data/ui')` from
`roles-tab.test.tsx` — 156 lines stubbing a package that file's tree no
longer touches, since `roles-tab.tsx` is now a re-export of the
Chakra-free `roles-tab-new`. Both suites are unchanged at 993/1427, so
those tests now exercise the real components.

The new Playwright spec covers what the swap risked: the filter renders,
both headers expose a sort trigger, and the trigger opens a menu.
Verified against a live container, and it fails if the
`DataTableColumnHeader` is swapped back for a plain string header.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Clean — no registry drift, off-token colours, or ad-hoc classes

App: frontend · Scope: diff vs origin/master · Files: 5

Count
⚠️ Outdated registry components 0
🛠 Locally-modified components 0
❓ Unknown to registry 0
🎨 Off-token palette colours 0
🔢 Ad-hoc utility classes 0

Generated by lookout audit-changes.

@SpicyPete
SpicyPete requested a review from a team September 11, 2026 14:46
@SpicyPete SpicyPete self-assigned this Sep 11, 2026
@SpicyPete
SpicyPete requested review from Mateoc, c-julin, datamali and weeco and removed request for a team September 11, 2026 14:47
The Playwright spec covers the table chrome, but a fresh cluster has no
ACLs, so it never clicks a row menu — which is the part of this swap that
moved most, into its own component. Five tests over the real components:
the rows and their hosts render, a Group principal is badged, the menu
opens with all three delete options, and the user-delete options are
enabled only for a principal that actually has a SASL account.

That last pair is the `canDeleteUser` logic the extraction restructured,
and the menu opening at all confirms the trigger still works after the
empty `onClick` came off its render slot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SpicyPete and others added 2 commits September 11, 2026 08:21
Six from a review of the swap, two of them mine and shipping-visible:

- the 300px width was on `className`, which lands on the `<input>`, not
  `containerClassName`, which sizes the positioned wrapper. `InputEnd` is
  absolute against that wrapper, so the "Clear search" X rendered at the
  far right of the page as soon as anyone typed. Every other call site
  uses `containerClassName`.
- `AclRowActions` owned its own queries, and `useListUsersQuery` wraps
  `useInfiniteQueryWithAllPages`, which auto-fetches every page from an
  effect — so each row ran its own `fetchNextPage()` loop. The queries
  move back to the parent and reach the row through context, which is
  also what the stable-columns goal actually needed:
  `useInvalidateUsersCache` returns a new function every render, so a
  `useMemo` dep list could never have been stable. `columns` is now a
  module constant, closing over nothing.

Four behaviour findings, all pre-existing in the Chakra original and
folded in because the extraction put them in reach:

- `canDeleteUser` matched only on name, so a Group principal sharing a
  name with a SASL user offered — and performed — a delete of that
  unrelated account. Now requires `principalType === 'User'`.
- in "Delete (User and ACLs)" a failed ACL delete fell through to the
  account delete, orphaning the ACLs it had just failed to remove, and
  its `onFailure` was overwritten by the second leg's. It now returns.
- the Principal sort key was the `User:`-prefixed `principal` while the
  cell renders `principalName`, so the newly-reachable sort ordered the
  column differently from what is on screen. Now an `accessorFn`.
- no `getRowId`, so rows were index-keyed: a refetch that reordered the
  list retargeted an open action menu at a different principal.

Also drops the dead `deleteButton` class, whose SCSS is nested under
`.stringList .reorderableList` and includes an `opacity: 0` that would
hide the button if anyone ever unscoped it.

Two more tests: a Group row never offers the user deletes even when a
user shares its name, and the Principal sort matches the rendered name.
Both fail if their fix is reverted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…type

Comments to the constraint; the reasoning is in the PR. The context and
table-options notes and both spec preambles were carrying paragraphs.

Two code cleanups: `SimpleAcl` — whose own comment already says it is
"used by ACLs tab and Permissions List tab" — is now exported and
consumed, instead of the page hand-copying four of its five fields where
the two could drift apart. And the ten-line `DeleteACLsRequest` literal
buried mid-object is now a named `allAclsFor(principal, host)`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
deleteRoleMutationMock: rs.fn().mockResolvedValue(undefined),
}));

rs.mock('@redpanda-data/ui', () => {

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.

i presume chakra is gone from the roles-tab then?

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.

ah. it just reexports ./roles-tab-new' 😑

@SpicyPete
SpicyPete merged commit 761148d into master Sep 11, 2026
21 checks passed
@SpicyPete
SpicyPete deleted the chakra-exit/16-security-acls branch September 11, 2026 18:32
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