-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(resource-adm): use new StudioPageHeader component in resourc…
…eadm (#13699)
- Loading branch information
Showing
10 changed files
with
204 additions
and
24 deletions.
There are no files selected for viewing
84 changes: 84 additions & 0 deletions
84
frontend/resourceadm/components/ResourceAdmHeader/ResourceAdmHeader.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,84 @@ | ||
import React from 'react'; | ||
import { MemoryRouter } from 'react-router-dom'; | ||
import { render, screen } from '@testing-library/react'; | ||
import { textMock } from '@studio/testing/mocks/i18nMock'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { queriesMock } from 'app-shared/mocks/queriesMock'; | ||
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; | ||
import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; | ||
import { ResourceAdmHeader } from './ResourceAdmHeader'; | ||
|
||
const mainOrganization = { | ||
avatar_url: '', | ||
id: 1, | ||
username: 'ttd', | ||
full_name: 'Testdepartementet', | ||
}; | ||
const otherOrganization = { | ||
avatar_url: '', | ||
id: 2, | ||
username: 'skd', | ||
full_name: 'Skatteetaten', | ||
}; | ||
const organizations = [mainOrganization, otherOrganization]; | ||
|
||
const testUser = { | ||
avatar_url: '', | ||
email: 'test@test.no', | ||
full_name: 'Test Testersen', | ||
id: 11, | ||
login: 'test', | ||
userType: 1, | ||
}; | ||
|
||
const resourceId = 'res-id'; | ||
|
||
const navigateMock = jest.fn(); | ||
jest.mock('react-router-dom', () => ({ | ||
...jest.requireActual('react-router-dom'), | ||
useNavigate: () => navigateMock, | ||
useParams: () => ({ | ||
org: mainOrganization.username, | ||
resourceId: resourceId, | ||
}), | ||
})); | ||
|
||
describe('ResourceAdmHeader', () => { | ||
afterEach(jest.clearAllMocks); | ||
|
||
it('should show org name and resource id in header', () => { | ||
renderResourceAdmHeader(); | ||
|
||
expect(screen.getByText(`${mainOrganization.full_name} / ${resourceId}`)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should navigate to new org when another org is chosen in menu', async () => { | ||
const user = userEvent.setup(); | ||
renderResourceAdmHeader(); | ||
|
||
const menuTrigger = screen.getByRole('button', { | ||
name: textMock('shared.header_user_for_org', { | ||
user: testUser.full_name, | ||
org: mainOrganization.full_name, | ||
}), | ||
}); | ||
await user.click(menuTrigger); | ||
|
||
const otherOrgButton = screen.getByRole('menuitemradio', { | ||
name: otherOrganization.full_name, | ||
}); | ||
await user.click(otherOrgButton); | ||
|
||
expect(navigateMock).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
const renderResourceAdmHeader = () => { | ||
return render( | ||
<MemoryRouter> | ||
<ServicesContextProvider {...queriesMock} client={createQueryClientMock()}> | ||
<ResourceAdmHeader organizations={organizations} user={testUser} /> | ||
</ServicesContextProvider> | ||
</MemoryRouter>, | ||
); | ||
}; |
98 changes: 98 additions & 0 deletions
98
frontend/resourceadm/components/ResourceAdmHeader/ResourceAdmHeader.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,98 @@ | ||
import React from 'react'; | ||
import { useNavigate } from 'react-router-dom'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { | ||
StudioAvatar, | ||
StudioPageHeader, | ||
type StudioProfileMenuGroup, | ||
useMediaQuery, | ||
type StudioProfileMenuItem, | ||
} from '@studio/components'; | ||
import { getOrgNameByUsername } from '../../utils/userUtils'; | ||
import { type Organization } from 'app-shared/types/Organization'; | ||
import { MEDIA_QUERY_MAX_WIDTH } from 'app-shared/constants'; | ||
import { useLogoutMutation } from 'app-shared/hooks/mutations/useLogoutMutation'; | ||
import type { User } from 'app-shared/types/Repository'; | ||
import { useUrlParams } from '../../hooks/useUrlParams'; | ||
import { getAppName } from '../../utils/stringUtils'; | ||
|
||
interface ResourceAdmHeaderProps { | ||
organizations: Organization[]; | ||
user: User; | ||
} | ||
|
||
export const ResourceAdmHeader = ({ organizations, user }: ResourceAdmHeaderProps) => { | ||
const { org, resourceId } = useUrlParams(); | ||
const resourcePath = resourceId ? ` / ${resourceId}` : ''; | ||
const pageHeaderTitle: string = `${getOrgNameByUsername(org, organizations)}${resourcePath}`; | ||
|
||
return ( | ||
<StudioPageHeader> | ||
<StudioPageHeader.Main> | ||
<StudioPageHeader.Left title={pageHeaderTitle} showTitle /> | ||
<StudioPageHeader.Right> | ||
<DashboardHeaderMenu organizations={organizations} user={user} /> | ||
</StudioPageHeader.Right> | ||
</StudioPageHeader.Main> | ||
</StudioPageHeader> | ||
); | ||
}; | ||
|
||
const DashboardHeaderMenu = ({ organizations, user }: ResourceAdmHeaderProps) => { | ||
const { t } = useTranslation(); | ||
const showButtonText = !useMediaQuery(MEDIA_QUERY_MAX_WIDTH); | ||
const { org, app } = useUrlParams(); | ||
const { mutate: logout } = useLogoutMutation(); | ||
const navigate = useNavigate(); | ||
const selectableOrgs = organizations; | ||
|
||
const triggerButtonText = t('shared.header_user_for_org', { | ||
user: user?.full_name || user?.login, | ||
org: getOrgNameByUsername(org, selectableOrgs), | ||
}); | ||
const repoPath = `/repos/${org}/${app}`; | ||
|
||
const handleSetSelectedContext = (context: string) => { | ||
navigate(`/${context}/${getAppName(context)}${location.search}`); | ||
}; | ||
|
||
const selectableOrgMenuItems: StudioProfileMenuItem[] = selectableOrgs.map( | ||
(selectableOrg: Organization) => ({ | ||
action: { type: 'button', onClick: () => handleSetSelectedContext(selectableOrg.username) }, | ||
itemName: selectableOrg?.full_name || selectableOrg.username, | ||
isActive: org === selectableOrg.username, | ||
}), | ||
); | ||
|
||
const giteaMenuItem: StudioProfileMenuItem = { | ||
action: { type: 'link', href: repoPath }, | ||
itemName: t('shared.header_go_to_gitea'), | ||
}; | ||
|
||
const logOutMenuItem: StudioProfileMenuItem = { | ||
action: { type: 'button', onClick: logout }, | ||
itemName: t('shared.header_logout'), | ||
}; | ||
|
||
const profileMenuGroups: StudioProfileMenuGroup[] = [ | ||
{ items: selectableOrgMenuItems }, | ||
{ items: [giteaMenuItem, logOutMenuItem] }, | ||
]; | ||
|
||
return ( | ||
<StudioPageHeader.ProfileMenu | ||
triggerButtonText={showButtonText && triggerButtonText} | ||
ariaLabelTriggerButton={triggerButtonText} | ||
color='dark' | ||
variant='regular' | ||
profileImage={ | ||
<StudioAvatar | ||
src={user?.avatar_url} | ||
alt={t('general.profile_icon')} | ||
title={t('shared.header_profile_icon_text')} | ||
/> | ||
} | ||
profileMenuGroups={profileMenuGroups} | ||
/> | ||
); | ||
}; |
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 @@ | ||
export { ResourceAdmHeader } from './ResourceAdmHeader'; |
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
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
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
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 |
---|---|---|
|
@@ -4,4 +4,5 @@ export { | |
isSePrefix, | ||
stringNumberToAriaLabel, | ||
isOrgNrString, | ||
getAppName, | ||
} from './stringUtils'; |
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
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 |
---|---|---|
@@ -1 +1 @@ | ||
export { userHasAccessToOrganization } from './userUtils'; | ||
export { userHasAccessToOrganization, getOrgNameByUsername } from './userUtils'; |
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