-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
upcoming: [M3-7429] - Add Parent/Proxy 'Switch Account' button and dr…
…awer to user profile dropdown menu (#10031) * Add switch account button and basic drawer * List child accounts in drawer * Use render function * Correct comments so as not to confuse myself * Correct the button text * Clean up and make styling more closely match mocks * Make a component out of Switch Account button; add icon * More styling and clean up * Added changeset: Add parent/proxy 'Switch Account' button and drawer to user profile dropdown menu * Clean up and add WIP unit tests * Clean up and fix z-index so user menu is focused on drawer close * Add tests for SwitchAccountDrawer * Address feedback: use startIcon
- Loading branch information
Showing
8 changed files
with
384 additions
and
78 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
packages/manager/.changeset/pr-10031-upcoming-features-1704467384095.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@linode/manager": Upcoming Features | ||
--- | ||
|
||
Add parent/proxy 'Switch Account' button and drawer to user profile dropdown menu ([#10031](https://github.com/linode/manager/pull/10031)) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions
12
packages/manager/src/features/Account/SwitchAccountButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import * as React from 'react'; | ||
|
||
import SwapIcon from 'src/assets/icons/swapSmall.svg'; | ||
import { Button, ButtonProps } from 'src/components/Button/Button'; | ||
|
||
export const SwitchAccountButton = (props: ButtonProps) => { | ||
return ( | ||
<Button startIcon={<SwapIcon />} {...props}> | ||
Switch Account | ||
</Button> | ||
); | ||
}; |
89 changes: 89 additions & 0 deletions
89
packages/manager/src/features/Account/SwitchAccountDrawer.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { fireEvent, within } from '@testing-library/react'; | ||
import * as React from 'react'; | ||
|
||
import { accountFactory } from 'src/factories/account'; | ||
import { accountUserFactory } from 'src/factories/accountUsers'; | ||
import { makeResourcePage } from 'src/mocks/serverHandlers'; | ||
import { rest, server } from 'src/mocks/testServer'; | ||
import { renderWithTheme } from 'src/utilities/testHelpers'; | ||
|
||
import { SwitchAccountDrawer } from './SwitchAccountDrawer'; | ||
|
||
const props = { | ||
onClose: vi.fn(), | ||
open: true, | ||
username: 'mock-user', | ||
}; | ||
|
||
describe('SwitchAccountDrawer', () => { | ||
it('should have a title', () => { | ||
const { getByText } = renderWithTheme(<SwitchAccountDrawer {...props} />); | ||
expect(getByText('Switch Account')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should display helper text about the accounts', () => { | ||
const { getByText } = renderWithTheme(<SwitchAccountDrawer {...props} />); | ||
expect( | ||
getByText( | ||
'Select an account to view and manage its settings and configurations', | ||
{ exact: false } | ||
) | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it('should include a link to switch back to the parent account if the active user is a proxy user', async () => { | ||
server.use( | ||
rest.get('*/account/users/*', (req, res, ctx) => { | ||
return res(ctx.json(accountUserFactory.build({ user_type: 'proxy' }))); | ||
}) | ||
); | ||
|
||
const { findByLabelText, getByText } = renderWithTheme( | ||
<SwitchAccountDrawer {...props} /> | ||
); | ||
|
||
expect( | ||
getByText( | ||
'Select an account to view and manage its settings and configurations', | ||
{ exact: false } | ||
) | ||
).toBeInTheDocument(); | ||
expect(await findByLabelText('parent-account-link')).toHaveTextContent( | ||
'switch back to your account' | ||
); | ||
}); | ||
|
||
it('should display a list of child accounts', async () => { | ||
server.use( | ||
rest.get('*/account/child-accounts', (req, res, ctx) => { | ||
return res( | ||
ctx.json( | ||
makeResourcePage( | ||
accountFactory.buildList(5, { company: 'Child Co.' }) | ||
) | ||
) | ||
); | ||
}) | ||
); | ||
|
||
const { findByTestId } = renderWithTheme( | ||
<SwitchAccountDrawer {...props} /> | ||
); | ||
|
||
const childAccounts = await findByTestId('child-account-list'); | ||
expect( | ||
within(childAccounts).getAllByText('Child Co.', { exact: false }) | ||
).toHaveLength(5); | ||
}); | ||
|
||
it('should close when the close icon is clicked', () => { | ||
const { getByLabelText } = renderWithTheme( | ||
<SwitchAccountDrawer {...props} /> | ||
); | ||
|
||
const closeIconButton = getByLabelText('Close drawer'); | ||
fireEvent.click(closeIconButton); | ||
|
||
expect(props.onClose).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
104 changes: 104 additions & 0 deletions
104
packages/manager/src/features/Account/SwitchAccountDrawer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { Typography, styled } from '@mui/material'; | ||
import { AxiosHeaders } from 'axios'; | ||
import React from 'react'; | ||
|
||
import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; | ||
import { CircleProgress } from 'src/components/CircleProgress'; | ||
import { Drawer } from 'src/components/Drawer'; | ||
import { Notice } from 'src/components/Notice/Notice'; | ||
import { Stack } from 'src/components/Stack'; | ||
import { useFlags } from 'src/hooks/useFlags'; | ||
import { useChildAccounts } from 'src/queries/account'; | ||
import { useAccountUser } from 'src/queries/accountUsers'; | ||
import { useProfile } from 'src/queries/profile'; | ||
import { authentication } from 'src/utilities/storage'; | ||
|
||
interface Props { | ||
onClose: () => void; | ||
open: boolean; | ||
username: string; | ||
} | ||
|
||
export const SwitchAccountDrawer = (props: Props) => { | ||
const { onClose, open } = props; | ||
|
||
const flags = useFlags(); | ||
|
||
const handleClose = () => { | ||
onClose(); | ||
}; | ||
|
||
const { data: profile } = useProfile(); | ||
const { data: user } = useAccountUser(profile?.username ?? ''); | ||
|
||
// From proxy accounts, make a request on behalf of the parent account to fetch child accounts. | ||
const headers = | ||
flags.parentChildAccountAccess && user?.user_type === 'proxy' | ||
? new AxiosHeaders({ Authorization: authentication.token.get() }) // TODO: Parent/Child - M3-7430: replace this token with the parent token in local storage. | ||
: undefined; | ||
const { data: childAccounts, error, isLoading } = useChildAccounts({ | ||
headers, | ||
}); | ||
|
||
const renderChildAccounts = React.useCallback(() => { | ||
if (isLoading) { | ||
return <CircleProgress mini />; | ||
} | ||
|
||
if (childAccounts?.results === 0) { | ||
return <Notice variant="info">There are no child accounts.</Notice>; | ||
} | ||
|
||
if (error) { | ||
return ( | ||
<Notice variant="error"> | ||
There was an error loading child accounts. | ||
</Notice> | ||
); | ||
} | ||
|
||
return childAccounts?.data.map((childAccount, idx) => ( | ||
<StyledChildAccountLinkButton | ||
onClick={() => { | ||
// TODO: Parent/Child - M3-7430 | ||
// handleAccountSwitch(); | ||
}} | ||
key={`child-account-link-button-${idx}`} | ||
> | ||
{childAccount.company} | ||
</StyledChildAccountLinkButton> | ||
)); | ||
}, [childAccounts, error, isLoading]); | ||
|
||
return ( | ||
<Drawer onClose={handleClose} open={open} title="Switch Account"> | ||
<StyledTypography> | ||
Select an account to view and manage its settings and configurations | ||
{user?.user_type === 'proxy' && ( | ||
<> | ||
{' '} | ||
or {/* TODO: Parent/Child - M3-7430 */} | ||
<StyledLinkButton | ||
aria-label="parent-account-link" | ||
onClick={() => null} | ||
> | ||
switch back to your account | ||
</StyledLinkButton> | ||
</> | ||
)} | ||
. | ||
</StyledTypography> | ||
<Stack alignItems={'flex-start'} data-testid="child-account-list"> | ||
{renderChildAccounts()} | ||
</Stack> | ||
</Drawer> | ||
); | ||
}; | ||
|
||
const StyledTypography = styled(Typography)(({ theme }) => ({ | ||
margin: `${theme.spacing(3)} 0`, | ||
})); | ||
|
||
const StyledChildAccountLinkButton = styled(StyledLinkButton)(({ theme }) => ({ | ||
marginBottom: theme.spacing(2), | ||
})); |
Oops, something went wrong.