Frontend: upstreamable: Add placeholder buttons to prevent layout shift during project loading#393
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes visible layout shifts and button flickering on the project details page that occur while async operations (namespace loading, RBAC checks, custom delete button resolution) resolve. It adds placeholder buttons that maintain the layout area from the initial render, matching the visual behavior of static components on the page.
Changes:
- Added
authResolvedstate toProjectDeleteButtonto show a placeholder button while RBAC permissions are pending, replacing it seamlessly with the real button once auth resolves. - Updated
ProjectDetails.tsxto initializeDeleteButtonasnullwhen a custom delete button is being resolved, rendering a placeholderActionButtonin its place. - Fixed a pre-existing bug where
isEnabledreturningfalsenever showed the fallbackProjectDeleteButton.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
frontend/src/components/project/ProjectDeleteButton.tsx |
Adds namespace-loading and RBAC-pending placeholders to prevent layout shift; introduces authResolved state wired to AuthVisible's onAuthResult/onError callbacks |
frontend/src/components/project/ProjectDetails.tsx |
Initializes DeleteButton as null during async custom-button resolution, renders a placeholder ActionButton; also fixes the isEnabled = false fallback to correctly display ProjectDeleteButton |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
illume
left a comment
There was a problem hiding this comment.
Please check the open review comments?
For the commit it could have projects: added.
It's not upstreamable? (I'm not sure... but looks like it could be?) if so please add the upstreamable to the commit/PR title?
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| {!authResolved && ( | ||
| <ActionButton | ||
| description={t('Delete project')} | ||
| buttonStyle={buttonStyle} | ||
| onClick={() => {}} | ||
| icon="mdi:delete" | ||
| /> | ||
| )} | ||
| <AuthVisible | ||
| item={projectNamespaces[0]} | ||
| authVerb="update" | ||
| onAuthResult={() => setAuthResolved(true)} | ||
| onError={() => setAuthResolved(true)} | ||
| > | ||
| {authResolved && ( | ||
| <> | ||
| <ActionButton | ||
| description={t('Delete project')} | ||
| buttonStyle={buttonStyle} | ||
| onClick={() => setOpenDialog(true)} | ||
| icon="mdi:delete" | ||
| /> | ||
| <ProjectDeleteDialog | ||
| open={openDialog} | ||
| project={project} | ||
| onClose={() => setOpenDialog(false)} | ||
| namespaces={projectNamespaces} | ||
| /> | ||
| </> | ||
| )} | ||
| </AuthVisible> |
There was a problem hiding this comment.
With the current authResolved gating, users who are not authorized will briefly see the placeholder button and then it will disappear once onAuthResult fires with allowed: false, causing a layout shift (and a short-lived “Delete” affordance). If the goal is to avoid layout shifts in all cases, consider reserving the space even after auth resolves but is denied (e.g., keep a disabled button with visibility: hidden/aria-hidden, or a fixed-size container) rather than removing it entirely.
There was a problem hiding this comment.
The placeholder button exists to prevent layout shift for authorized users (the common case). Making it disabled would cause a visible white -> grey -> white flicker as it transitions to the real button.
For unauthorized users, there would be a brief flash of the placeholder before it disappears, but this comment suggests an empty placeholder for that case - which means these 2 goals are contradictory. Since we check auth first before making the decision of 'showing' it or not, we can't know which placeholder type to use before the auth is done.
Current approach leans toward the most common case so we see zero layout shift.
But this is something that might be a design decision to discuss. (delete button is right aligned)
…layout shift during project loading
514c808 to
82dbdc5
Compare
Description
Currently, the project delete button pops in only after async operations resolve (namespace loading, RBAC checks, project type detection), causing a visible layout shift on the project details page.
For custom project types (AKS projects for ex.), there's an additional visual glitch where the default delete button renders first, disappears, then the custom one appears, resulting in a flash/additional layout shift.
These issues typically occur within the first second, but are noticeable at longer load times and are visually inconsistent with most of the statically loaded components on the page.
This PR adds placeholder buttons so the delete button area is standardized from the initial render, which visually aligns with the behavior of most other static components on the project details page.
Note: This PR is related but not exclusive to #392. Additional fixes for the
AKSProjectDeleteButtonreside thereType of Change
Changes Made
authResolvedstate that is utilized to shows a placeholder button while namespaces load and RBAC permissions resolve.DeleteButtonstate to initialize asnullwhen a custom delete button is being resolved.Testing
Test Cases
Steps to reproduce