Skip to content

Commit

Permalink
Merge branch 'main' into chore/13697-resourceadm-use-new-studiopagehe…
Browse files Browse the repository at this point in the history
…ader-component-in-resourceadm
  • Loading branch information
mgunnerud authored Oct 4, 2024
2 parents edbe6a5 + 939464c commit 27d1c0f
Show file tree
Hide file tree
Showing 92 changed files with 1,544 additions and 268 deletions.
48 changes: 32 additions & 16 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ solution/platform:
- changed-files:
- any-glob-to-any-file: 'src/Altinn.Platform/**/*'

solution/app-backend:
- changed-files:
- any-glob-to-any-file: 'src/studio/AppTemplates/**/*'

solution/studio/designer:
- changed-files:
- any-glob-to-any-file: ['backend/**/*', 'frontend/**/*']
Expand All @@ -18,43 +14,55 @@ solution/studio/repos:
# Labels for areas
area/app-deploy:
- changed-files:
- any-glob-to-any-file: ['frontend/app-development/features/appPublish/**/*', 'frontend/app-development/sharedResources/appDeployment/**/*', 'backend/src/Designer/Controllers/DeploymentsController.cs', 'src/studio/AppTemplates/AspNet/deployment/**/*']
- any-glob-to-any-file: ['frontend/app-development/features/appPublish/**/*', 'frontend/app-development/sharedResources/appDeployment/**/*', 'backend/src/Designer/**/*Deployment*.cs', 'backend/src/Designer/**/*Release*.cs']

area/app-preview:
- changed-files:
- any-glob-to-any-file: ['frontend/app-preview/**/*', 'backend/src/Designer/Controllers/PreviewController.cs']

area/authorization:
- changed-files:
- any-glob-to-any-file: ['frontend/packages/policy-editor/**/*']

area/dashboard:
- changed-files:
- any-glob-to-any-file: 'frontend/dashboard/**/*'
- any-glob-to-any-file: ['frontend/dashboard/**/*']

area/data-modeling:
- changed-files:
- any-glob-to-any-file: ['frontend/packages/schema-editor/**/*', 'frontend/packages/schema-model/**/*', 'frontend/app-development/features/dataModelling/**/*', 'backend/src/DataModeling/**/*']

area/pdf:
area/overview:
- changed-files:
- any-glob-to-any-file: 'src/Altinn.Platform/Altinn.Platform.PDF/**/*'
- any-glob-to-any-file: ['frontend/app-development/features/overview/**/*']

area/process:
- changed-files:
- any-glob-to-any-file: ['frontend/app-development/features/processEditor/**/*', 'frontend/packages/process-editor/**/*', 'backend/src/Designer/**/*ProcessesModeling*.cs']

area/resource-registry:
- changed-files:
- any-glob-to-any-file: 'frontend/resourceAdm/**/*'

area/test:
area/settings:
- changed-files:
- any-glob-to-any-file: 'src/test/**/*'
- any-glob-to-any-file: 'frontend/app-development/layout/SettingsModalButton/**/*'

area/text-editor:
area/studio-root:
- changed-files:
- any-glob-to-any-file: ['frontend/packages/text-editor/**', 'frontend/app-development/features/textEditor/**/*']
- any-glob-to-any-file: 'frontend/studio-root/**/*'

area/process:
area/text-editor:
- changed-files:
- any-glob-to-any-file: ['frontend/app-development/features/processEditor/**/*', 'frontend/packages/process-editor/**/*', 'backend/src/Designer/**/*ProcessesModeling*.cs']
- any-glob-to-any-file: ['frontend/packages/text-editor/**', 'frontend/app-development/features/textEditor/**/*', 'backend/src/Designer/**/*Text*.cs',]

area/ui-editor:
- changed-files:
- any-glob-to-any-file: 'frontend/packages/ux-editor/**/*'

area/studio-root:
area/version-control:
- changed-files:
- any-glob-to-any-file: 'frontend/studio-root/**/*'
- any-glob-to-any-file: 'frontend/packages/shared/src/components/GiteaHeader/**/*'

## Other labels
kind/dependencies:
Expand All @@ -68,3 +76,11 @@ quality/testing:
skip-releasenotes:
- changed-files:
- any-glob-to-any-file: ['.github/**/*', '.husky/**/*', '.vscode/**/*', '.yarn/**/*', 'testdata/**/*', 'development/**/*', 'src/**/*']

backend:
- changed-files:
- any-glob-to-any-file: 'backend/**/*'

frontend:
- changed-files:
- any-glob-to-any-file: 'frontend/**/*'
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ COPY ./frontend/app-preview/package.json ./frontend/app-preview/
COPY ./frontend/dashboard/package.json ./frontend/dashboard/
COPY ./frontend/language/package.json ./frontend/language/
COPY ./frontend/libs/studio-components/package.json ./frontend/libs/studio-components/
COPY ./frontend/libs/studio-content-library/package.json ./frontend/libs/studio-content-library/
COPY ./frontend/libs/studio-hooks/package.json ./frontend/libs/studio-hooks/
COPY ./frontend/libs/studio-icons/package.json ./frontend/libs/studio-icons/
COPY ./frontend/libs/studio-pure-functions/package.json ./frontend/libs/studio-pure-functions/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { RepoNameInput } from '../RepoNameInput';
import { type User } from 'app-shared/types/Repository';
import { type Organization } from 'app-shared/types/Organization';
import { useSelectedContext } from '../../hooks/useSelectedContext';
import { SelectedContextType } from 'app-shared/navigation/main-header/Header';
import { SelectedContextType } from 'dashboard/context/HeaderContext';
import { type NewAppForm } from '../../types/NewAppForm';
import { useCreateAppFormValidation } from './hooks/useCreateAppFormValidation';
import { Link } from 'react-router-dom';
Expand Down
19 changes: 19 additions & 0 deletions frontend/dashboard/context/HeaderContext/HeaderContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createContext } from 'react';
import { type Organization } from 'app-shared/types/Organization';
import { type User } from 'app-shared/types/Repository';

export enum SelectedContextType {
All = 'all',
Self = 'self',
None = 'none',
}

export type HeaderContextType = {
selectableOrgs?: Organization[];
user: User;
};

export const HeaderContext = createContext<HeaderContextType>({
selectableOrgs: undefined,
user: undefined,
});
1 change: 1 addition & 0 deletions frontend/dashboard/context/HeaderContext/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { HeaderContext, type HeaderContextType, SelectedContextType } from './HeaderContext';
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
import { useSelectedContext } from '../useSelectedContext';
import type { NavigateFunction } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { SelectedContextType } from 'app-shared/navigation/main-header/Header';
import { SelectedContextType } from 'dashboard/context/HeaderContext';
import { typedSessionStorage } from '@studio/components/src/hooks/webStorage';
import { userHasAccessToSelectedContext } from 'dashboard/utils/userUtils';

Expand Down
1 change: 1 addition & 0 deletions frontend/dashboard/hooks/usePageHeaderTitle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { usePageHeaderTitle } from './usePageHeaderTitle';
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { usePageHeaderTitle } from './usePageHeaderTitle';
import { HeaderContext, type HeaderContextType } from 'dashboard/context/HeaderContext';
import { useSelectedContext } from 'dashboard/hooks/useSelectedContext';
import { headerContextValueMock } from 'dashboard/testing/headerContextMock';
import { SelectedContextType } from 'dashboard/context/HeaderContext';
import { mockOrg1 } from 'dashboard/testing/organizationMock';
import { renderHookWithProviders } from 'dashboard/testing/mocks';

jest.mock('dashboard/hooks/useSelectedContext');

const renderUsePageHeaderTitleHook = (headerContextValueProps: Partial<HeaderContextType> = {}) => {
return renderHookWithProviders(usePageHeaderTitle, {
externalWrapper: (children) => (
<HeaderContext.Provider value={{ ...headerContextValueMock, ...headerContextValueProps }}>
{children}
</HeaderContext.Provider>
),
});
};

describe('usePageHeaderTitle', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should return the organization name when a valid context is selected', () => {
(useSelectedContext as jest.Mock).mockReturnValue(mockOrg1.username);

const { result } = renderUsePageHeaderTitleHook();

expect(result.current).toBe(mockOrg1.full_name);
});

it('should return an empty string when selected context is All', () => {
(useSelectedContext as jest.Mock).mockReturnValue(SelectedContextType.All);

const { result } = renderUsePageHeaderTitleHook();

expect(result.current).toBe('');
});

it('should return an empty string when selected context is Self', () => {
(useSelectedContext as jest.Mock).mockReturnValue(SelectedContextType.Self);

const { result } = renderUsePageHeaderTitleHook();

expect(result.current).toBe('');
});
});
14 changes: 14 additions & 0 deletions frontend/dashboard/hooks/usePageHeaderTitle/usePageHeaderTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useContext } from 'react';
import { HeaderContext, SelectedContextType } from 'dashboard/context/HeaderContext';
import { getOrgNameByUsername } from 'dashboard/utils/userUtils';
import { useSelectedContext } from '../useSelectedContext';

export const usePageHeaderTitle = () => {
const selectedContext = useSelectedContext();
const { selectableOrgs } = useContext(HeaderContext);

if (selectedContext !== SelectedContextType.All && selectedContext !== SelectedContextType.Self) {
return getOrgNameByUsername(selectedContext, selectableOrgs);
}
return '';
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useProfileMenuTriggerButtonText } from './useProfileMenuTriggerButtonText';
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import { useProfileMenuTriggerButtonText } from './useProfileMenuTriggerButtonText';
import {
HeaderContext,
type HeaderContextType,
SelectedContextType,
} from 'dashboard/context/HeaderContext';
import { useSelectedContext } from '../useSelectedContext';
import { userMock } from 'dashboard/testing/userMock';
import { headerContextValueMock } from 'dashboard/testing/headerContextMock';
import { textMock } from '@studio/testing/mocks/i18nMock';
import { mockOrg1 } from 'dashboard/testing/organizationMock';
import { renderHookWithProviders } from 'dashboard/testing/mocks';

jest.mock('../useSelectedContext');

const renderUseProfileMenuTriggerButtonTextHook = (
headerContextValueProps: Partial<HeaderContextType> = {},
) => {
return renderHookWithProviders(useProfileMenuTriggerButtonText, {
externalWrapper: (children) => (
<HeaderContext.Provider value={{ ...headerContextValueMock, ...headerContextValueProps }}>
{children}
</HeaderContext.Provider>
),
});
};

describe('useProfileMenuTriggerButtonText', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should return the full name of the user when selected context is Self', () => {
(useSelectedContext as jest.Mock).mockReturnValue(SelectedContextType.Self);

const { result } = renderUseProfileMenuTriggerButtonTextHook();

expect(result.current).toBe(userMock.full_name);
});

it('should return the login name of the user when full_name is not available', () => {
(useSelectedContext as jest.Mock).mockReturnValue(SelectedContextType.Self);

const { result } = renderUseProfileMenuTriggerButtonTextHook({
user: { ...userMock, full_name: '' },
});

expect(result.current).toBe(userMock.login);
});

it('should return the organization and username when selected context is an organization', () => {
(useSelectedContext as jest.Mock).mockReturnValue(mockOrg1.username);
const { result } = renderUseProfileMenuTriggerButtonTextHook();

expect(result.current).toBe(
textMock('shared.header_user_for_org', { user: userMock.full_name, org: mockOrg1.full_name }),
);
});

it('should return the username when selected context is All', () => {
(useSelectedContext as jest.Mock).mockReturnValue(SelectedContextType.All);

const { result } = renderUseProfileMenuTriggerButtonTextHook();

expect(result.current).toBe(userMock.full_name);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useContext } from 'react';
import { HeaderContext, SelectedContextType } from 'dashboard/context/HeaderContext';
import { getOrgNameByUsername } from 'dashboard/utils/userUtils';
import { useTranslation } from 'react-i18next';
import { useSelectedContext } from '../useSelectedContext';

export const useProfileMenuTriggerButtonText = (): string => {
const { t } = useTranslation();
const { user, selectableOrgs } = useContext(HeaderContext);
const selectedContext = useSelectedContext();

const username = user.full_name || user.login;

if (selectedContext !== SelectedContextType.All && selectedContext !== SelectedContextType.Self) {
return t('shared.header_user_for_org', {
user: username,
org: getOrgNameByUsername(selectedContext, selectableOrgs),
});
}
return username;
};
1 change: 1 addition & 0 deletions frontend/dashboard/hooks/useRepoPath/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useRepoPath } from './useRepoPath';
53 changes: 53 additions & 0 deletions frontend/dashboard/hooks/useRepoPath/useRepoPath.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { useRepoPath } from './useRepoPath';
import { HeaderContext, type HeaderContextType } from 'dashboard/context/HeaderContext';
import { useSelectedContext } from 'dashboard/hooks/useSelectedContext';
import { headerContextValueMock } from 'dashboard/testing/headerContextMock';
import { repositoryOwnerPath, repositoryBasePath } from 'app-shared/api/paths';
import { mockOrg1 } from 'dashboard/testing/organizationMock';
import { userMock } from 'dashboard/testing/userMock';
import { renderHookWithProviders } from 'dashboard/testing/mocks';

jest.mock('dashboard/hooks/useSelectedContext');

const renderUseRepoPathHook = (headerContextValueProps: Partial<HeaderContextType> = {}) => {
return renderHookWithProviders(useRepoPath, {
externalWrapper: (children) => (
<HeaderContext.Provider value={{ ...headerContextValueMock, ...headerContextValueProps }}>
{children}
</HeaderContext.Provider>
),
});
};

describe('useRepoPath', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should return the repository owner path when an organization is selected', () => {
(useSelectedContext as jest.Mock).mockReturnValue(mockOrg1.username);

const { result } = renderUseRepoPathHook();

expect(result.current).toBe(repositoryOwnerPath(mockOrg1.username));
});

it('should return the user login as the owner path when no organization is selected', () => {
(useSelectedContext as jest.Mock).mockReturnValue(null);

const { result } = renderUseRepoPathHook();

expect(result.current).toBe(repositoryOwnerPath(userMock.login));
});

it('should return the repository base path if neither organization nor user is available', () => {
(useSelectedContext as jest.Mock).mockReturnValue(null);

const { result } = renderUseRepoPathHook({
user: { ...userMock, login: '' },
});

expect(result.current).toBe(repositoryBasePath());
});
});
18 changes: 18 additions & 0 deletions frontend/dashboard/hooks/useRepoPath/useRepoPath.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useContext } from 'react';
import { repositoryBasePath, repositoryOwnerPath } from 'app-shared/api/paths';
import { getOrgUsernameByUsername } from 'dashboard/utils/userUtils';
import { useSelectedContext } from 'dashboard/hooks/useSelectedContext';
import { HeaderContext } from 'dashboard/context/HeaderContext';

export const useRepoPath = () => {
const { user, selectableOrgs } = useContext(HeaderContext);

const selectedContext = useSelectedContext();
const org = getOrgUsernameByUsername(selectedContext, selectableOrgs);

const owner = org || user?.login;
if (owner) {
return repositoryOwnerPath(owner);
}
return repositoryBasePath();
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useParams } from 'react-router-dom';
import { SelectedContextType } from 'app-shared/navigation/main-header/Header';
import { SelectedContextType } from 'dashboard/context/HeaderContext';

export const useSelectedContext = () => {
const { selectedContext = SelectedContextType.None } = useParams();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { textMock } from '@studio/testing/mocks/i18nMock';
import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext';
import { repository, user as userMock } from 'app-shared/mocks/mocks';
import { useParams } from 'react-router-dom';
import { SelectedContextType } from 'app-shared/navigation/main-header/Header';
import { SelectedContextType } from 'dashboard/context/HeaderContext';
import { DASHBOARD_ROOT_ROUTE } from 'app-shared/constants';

const orgMock: Organization = {
Expand Down
2 changes: 1 addition & 1 deletion frontend/dashboard/pages/CreateService/CreateService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { PackagesRouter } from 'app-shared/navigation/PackagesRouter';
import { type NewAppForm } from '../../types/NewAppForm';
import { DASHBOARD_ROOT_ROUTE } from 'app-shared/constants';
import { useSelectedContext } from '../../hooks/useSelectedContext';
import { SelectedContextType } from 'app-shared/navigation/main-header/Header';
import { SelectedContextType } from 'dashboard/context/HeaderContext';

const initialFormError: NewAppForm = {
org: '',
Expand Down
Loading

0 comments on commit 27d1c0f

Please sign in to comment.