Skip to content

Commit 526ec3a

Browse files
gpascucciclaude
andcommitted
fix(auth): stop the dev "view as" banner hiding under the header and firing on no-op overrides
Two local-dev fixes for the "View as (dev)" role override: - The banner rendered between the fixed brand header and <Content>, with no top offset, so the fixed header painted over it. Move it inside <Content> so it inherits the `.cds--header ~ .cds--content` offset. - The banner fired on ANY stored override, including one equal to the role you already hold — e.g. a stale "view as ILCR_SUBMITTER" replayed from localStorage for a real submitter, which changes nothing. Show it only when the override actually differs from the real role set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d900036 commit 526ec3a

3 files changed

Lines changed: 31 additions & 6 deletions

File tree

frontend/src/components/DevRoleBanner.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@ describe('DevRoleBanner', () => {
3232
expect(screen.queryByText(/viewing as/)).toBeNull()
3333
})
3434

35+
it('renders nothing when the override matches the real role (a no-op override)', () => {
36+
render(
37+
<AuthContext
38+
value={authValue({
39+
override: 'ILCR_SUBMITTER',
40+
realRoles: ['ILCR_SUBMITTER'],
41+
setOverride: () => undefined,
42+
})}
43+
>
44+
<DevRoleBanner />
45+
</AuthContext>,
46+
)
47+
expect(screen.queryByText(/viewing as/)).toBeNull()
48+
})
49+
3550
it('renders nothing when devRoleSwitch is absent (deployed builds)', () => {
3651
render(
3752
<AuthContext value={authValue(undefined)}>

frontend/src/components/DevRoleBanner.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,25 @@ import useAuth from '@/context/auth/useAuth'
99
export default function DevRoleBanner() {
1010
const { devRoleSwitch } = useAuth()
1111

12-
if (!devRoleSwitch?.override) {
12+
// Show only when the override actually changes the effective role. An override equal to the role
13+
// you already hold (e.g. a stale "view as ILCR_SUBMITTER" replayed for a real submitter) is a
14+
// no-op, so warning about it is just noise.
15+
const override = devRoleSwitch?.override
16+
const realRoles = devRoleSwitch?.realRoles ?? []
17+
const isNoOp = realRoles.length === 1 && realRoles[0] === override
18+
if (!override || isNoOp) {
1319
return null
1420
}
1521

16-
const realRoles = devRoleSwitch.realRoles.join(' + ') || 'no ILCR role'
22+
const realLabel = realRoles.join(' + ') || 'no ILCR role'
1723
return (
1824
<InlineNotification
1925
kind="warning"
2026
lowContrast
2127
hideCloseButton
2228
className="dev-role-banner"
23-
title={`Dev override — viewing as ${devRoleSwitch.override}`}
24-
subtitle={`Frontend only: the backend still enforces your real role (${realRoles}), so admin APIs may return 403.`}
29+
title={`Dev override — viewing as ${override}`}
30+
subtitle={`Frontend only: the backend still enforces your real role (${realLabel}), so admin APIs may return 403.`}
2531
/>
2632
)
2733
}

frontend/src/components/Layout/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ type Props = {
1616
const Layout: FC<Props> = ({ children }) => (
1717
<LayoutProvider>
1818
<HeaderContainer render={() => <LayoutHeader />} />
19-
<DevRoleBanner />
20-
<Content className="app-content">{children}</Content>
19+
{/* Inside Content so it inherits the fixed-header top offset (.cds--header ~ .cds--content);
20+
rendered at the top of the flow above the fixed brand header would hide it. */}
21+
<Content className="app-content">
22+
<DevRoleBanner />
23+
{children}
24+
</Content>
2125
<Footer />
2226
</LayoutProvider>
2327
)

0 commit comments

Comments
 (0)