From 2b5c59d81e94c86c39aab160c650f837b8a93173 Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt Date: Mon, 18 Dec 2023 13:32:59 +0100 Subject: [PATCH 1/3] Implementing PagesRouter to navigate between packages --- .../app-development/layout/PageHeader.tsx | 10 +-- .../AppBarConfig/AppPreviewBarConfig.tsx | 13 ++- frontend/app-preview/src/enums/RoutePaths.ts | 3 - frontend/packages/shared/src/api/paths.js | 4 - .../src/components/altinnHeader/types.ts | 1 - .../PackagesRouter/PackagesRouter.test.ts | 89 +++++++++++++++++++ .../PackagesRouter/PackagesRouter.ts | 47 ++++++++++ .../src/navigation/PackagesRouter/index.ts | 1 + 8 files changed, 147 insertions(+), 21 deletions(-) delete mode 100644 frontend/app-preview/src/enums/RoutePaths.ts create mode 100644 frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts create mode 100644 frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts create mode 100644 frontend/packages/shared/src/navigation/PackagesRouter/index.ts diff --git a/frontend/app-development/layout/PageHeader.tsx b/frontend/app-development/layout/PageHeader.tsx index 8754f6398ff..fe2b65bce59 100644 --- a/frontend/app-development/layout/PageHeader.tsx +++ b/frontend/app-development/layout/PageHeader.tsx @@ -3,12 +3,12 @@ import { AltinnHeader } from 'app-shared/components/altinnHeader/AltinnHeader'; import { getFilteredTopBarMenu } from './AppBar/appBarConfig'; import { getRepositoryType } from 'app-shared/utils/repository'; import { useAppSelector } from 'app-development/hooks'; -import { previewPath, publishPath } from 'app-shared/api/paths'; import { AltinnButtonActionItem } from 'app-shared/components/altinnHeader/types'; import { GiteaHeader } from 'app-shared/components/GiteaHeader'; import { SettingsModalButton } from './SettingsModalButton'; import { TopBarMenu } from 'app-shared/enums/TopBarMenu'; import { User } from 'app-shared/types/User'; +import { PackagesRouter } from 'app-shared/navigation/PackagesRouter'; type SubMenuContentProps = { org: string; @@ -27,23 +27,23 @@ export const subMenuContent = ({ org, app }: SubMenuContentProps) => { }; export const buttonActions = (org: string, app: string): AltinnButtonActionItem[] => { + const packagesRouter = new PackagesRouter({ org, app }); + const actions: AltinnButtonActionItem[] = [ { title: 'top_menu.preview', - path: previewPath, menuKey: TopBarMenu.Preview, buttonVariant: 'secondary', buttonColor: 'inverted', headerButtonsClasses: undefined, - handleClick: () => (window.location.href = previewPath(org, app)), + handleClick: () => packagesRouter.navigateToPackage('preview'), }, { title: 'top_menu.deploy', - path: publishPath, menuKey: TopBarMenu.Deploy, buttonVariant: 'secondary', headerButtonsClasses: undefined, - handleClick: () => (window.location.href = publishPath(org, app)), + handleClick: () => packagesRouter.navigateToPackage('editorPublish'), }, ]; return actions; diff --git a/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx b/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx index 46f3f60d070..a94ab5dba22 100644 --- a/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx +++ b/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { RepositoryType } from 'app-shared/types/global'; import { TFunction } from 'i18next'; -import { editorPath } from 'app-shared/api/paths'; import { Button, Select, LegacyToggleButtonGroup } from '@digdir/design-system-react'; import { AltinnButtonActionItem } from 'app-shared/components/altinnHeader/types'; import classes from '../AppPreviewSubMenu.module.css'; @@ -12,7 +11,7 @@ import { useLayoutSetsQuery } from '../../../../packages/ux-editor/src/hooks/que import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams'; import { TopBarMenu } from 'app-shared/enums/TopBarMenu'; import { TopBarMenuItem } from 'app-shared/types/TopBarMenuItem'; -import { RoutePaths } from '../../enums/RoutePaths'; +import { PackagesRouter } from 'app-shared/navigation/PackagesRouter'; export interface AppPreviewMenuItem { key: string; @@ -102,18 +101,16 @@ export const appPreviewButtonActions = ( app: string, instanceId: string, ): AltinnButtonActionItem[] => { - const subUrl = `/${RoutePaths.UIEditor}?layout=`; + const packagesRouter = new PackagesRouter({ org, app }); + const subUrl = `?layout=${window.localStorage.getItem(instanceId)}`; + const action: AltinnButtonActionItem[] = [ { title: 'top_menu.preview_back_to_editing', - path: editorPath, menuKey: TopBarMenu.Preview, buttonVariant: 'secondary', headerButtonsClasses: classes.backToEditorBtn, - handleClick: () => - (window.location.href = `${editorPath(org, app)}${subUrl}${window.localStorage.getItem( - instanceId, - )}`), + handleClick: () => packagesRouter.navigateToPackage('editorUiEditor', subUrl), }, ]; return action; diff --git a/frontend/app-preview/src/enums/RoutePaths.ts b/frontend/app-preview/src/enums/RoutePaths.ts deleted file mode 100644 index 39afded89d6..00000000000 --- a/frontend/app-preview/src/enums/RoutePaths.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum RoutePaths { - UIEditor = 'ui-editor', -} diff --git a/frontend/packages/shared/src/api/paths.js b/frontend/packages/shared/src/api/paths.js index 6ce12adc4f2..519f75a36e4 100644 --- a/frontend/packages/shared/src/api/paths.js +++ b/frontend/packages/shared/src/api/paths.js @@ -61,13 +61,9 @@ export const datamodelMetadataPath = (org, app) => `${basePath}/${org}/${app}/mo export const orgsListPath = () => `${basePath}/orgs`; // Get // Preview -export const previewPath = (org, app) => `/preview/${org}/${app}`; export const instanceIdForPreviewPath = (org, app) => `${basePath}/${org}/${app}/mock-instance-id`; // Get export const previewPage = (org, app, selectedLayoutSet) => `/designer/html/preview.html?${s({ org, app, selectedLayoutSet })}`; -//Editor -export const editorPath = (org, app) => `/editor/${org}/${app}`; - // Preview - SignalR Hub export const previewSignalRHubSubPath = () => `/previewHub`; diff --git a/frontend/packages/shared/src/components/altinnHeader/types.ts b/frontend/packages/shared/src/components/altinnHeader/types.ts index b953b8ceeb1..5a5d0aa62a9 100644 --- a/frontend/packages/shared/src/components/altinnHeader/types.ts +++ b/frontend/packages/shared/src/components/altinnHeader/types.ts @@ -5,7 +5,6 @@ export type AltinnHeaderVariant = 'regular' | 'preview'; export interface AltinnButtonActionItem { title: string; menuKey: string; - path?: (org: string, app: string) => string; buttonVariant: ButtonProps['variant']; buttonColor?: ButtonProps['color']; headerButtonsClasses: any; diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts new file mode 100644 index 00000000000..4b2abb39407 --- /dev/null +++ b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts @@ -0,0 +1,89 @@ +import { PackagesRouter } from './PackagesRouter'; + +const mockOrg: string = 'org'; +const mockApp: string = 'app'; + +describe('PackagesRouter', () => { + describe('constructor', () => { + it('should initialize app and org properties', () => { + const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); + + expect(packagesRouter['app']).toEqual(mockApp); + expect(packagesRouter['org']).toEqual(mockOrg); + }); + + it('should default to empty strings if app and org are not provided', () => { + const routerWithoutParams = new PackagesRouter({}); + expect(routerWithoutParams['app']).toEqual(''); + expect(routerWithoutParams['org']).toEqual(''); + }); + }); + + describe('navigateToPackage', () => { + it('should navigate to the correct URL without subUrl', () => { + const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); + const expectedUrl = `/editor/${mockOrg}/${mockApp}/overview`; + + // Mock the window.location.assign method + const assignMock = jest.fn(); + Object.defineProperty(window, 'location', { + value: { assign: assignMock }, + writable: true, + }); + + packagesRouter.navigateToPackage('editorOverview'); + + expect(assignMock).toHaveBeenCalledWith(expectedUrl); + }); + + it('should navigate to the correct URL with subUrl', () => { + const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); + + const mockSubUrl = '?layout=123'; + const expectedUrl = `/editor/${mockOrg}/${mockApp}/ui-editor${mockSubUrl}`; + + const assignMock = jest.fn(); + Object.defineProperty(window, 'location', { + value: { assign: assignMock }, + writable: true, + }); + + packagesRouter.navigateToPackage('editorUiEditor', mockSubUrl); + + expect(assignMock).toHaveBeenCalledWith(expectedUrl); + }); + }); + + describe('getPackageNavigationUrl', () => { + it('should return the correct URL for a package route with placeholders', () => { + const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); + const expectedUrl = `/editor/${mockOrg}/${mockApp}/deploy`; + + const result = packagesRouter.getPackageNavigationUrl('editorPublish'); + + expect(result).toEqual(expectedUrl); + }); + + it('should return the correct URL for a package route without placeholders', () => { + const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); + const expectedUrl = '/dashboard'; + + const result = packagesRouter.getPackageNavigationUrl('dashboard'); + + expect(result).toEqual(expectedUrl); + }); + }); + + describe('replaceOrgAndApp', () => { + it('should replace {{org}} and {{app}} placeholders in the given URL', () => { + const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); + + const mockUrl = '/editor/{{org}}/{{app}}/overview'; + const expectedUrl = `/editor/${mockOrg}/${mockApp}/overview`; + + const result = packagesRouter['replaceOrgAndApp'](mockUrl); + + expect(result).toEqual(expectedUrl); + }); + }); +}); diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts new file mode 100644 index 00000000000..37596b345cf --- /dev/null +++ b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts @@ -0,0 +1,47 @@ +type ParamsOptions = { + org?: string; + app?: string; +}; + +type PackagesRoute = + | 'dashboard' + | 'editorOverview' + | 'editorUiEditor' + | 'preview' + | 'editorPublish'; + +const packagesRoutes = { + dashboard: '/dashboard', + editorOverview: '/editor/{{org}}/{{app}}/overview', + editorUiEditor: '/editor/{{org}}/{{app}}/ui-editor', + editorPublish: '/editor/{{org}}/{{app}}/deploy', + preview: '/preview/{{org}}/{{app}}', +}; + +export class PackagesRouter { + private app: string; + private org: string; + + constructor(private paramsOptions: ParamsOptions) { + this.app = this.paramsOptions.app ?? ''; + this.org = this.paramsOptions.org ?? ''; + } + + public navigateToPackage(packageRoute: PackagesRoute, subUrl?: string): void { + window.location.assign(`${this.getPackageNavigationUrl(packageRoute)}${subUrl ? subUrl : ''}`); + } + + public getPackageNavigationUrl(packageRoute: PackagesRoute): string { + const selectedPackageRoute = packagesRoutes[packageRoute]; + + if (selectedPackageRoute.includes('{{org}}') || selectedPackageRoute.includes('{{app}}')) { + return this.replaceOrgAndApp(selectedPackageRoute); + } + + return selectedPackageRoute; + } + + private replaceOrgAndApp(url: string): string { + return url.replace('{{org}}', this.org).replace('{{app}}', this.app); + } +} diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/index.ts b/frontend/packages/shared/src/navigation/PackagesRouter/index.ts new file mode 100644 index 00000000000..41021e23f52 --- /dev/null +++ b/frontend/packages/shared/src/navigation/PackagesRouter/index.ts @@ -0,0 +1 @@ +export { PackagesRouter } from './PackagesRouter'; From c6dc8dcfbb8367583ed8cf7b17aebdc45231258d Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt Date: Mon, 18 Dec 2023 18:57:51 +0100 Subject: [PATCH 2/3] fixing feedback from PR --- .../PackagesRouter/PackagesRouter.test.ts | 17 +++++------------ .../navigation/PackagesRouter/PackagesRouter.ts | 6 +++--- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts index 4b2abb39407..c6204cfd3a3 100644 --- a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts +++ b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts @@ -5,13 +5,6 @@ const mockApp: string = 'app'; describe('PackagesRouter', () => { describe('constructor', () => { - it('should initialize app and org properties', () => { - const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); - - expect(packagesRouter['app']).toEqual(mockApp); - expect(packagesRouter['org']).toEqual(mockOrg); - }); - it('should default to empty strings if app and org are not provided', () => { const routerWithoutParams = new PackagesRouter({}); expect(routerWithoutParams['app']).toEqual(''); @@ -20,7 +13,7 @@ describe('PackagesRouter', () => { }); describe('navigateToPackage', () => { - it('should navigate to the correct URL without subUrl', () => { + it('should navigate to the correct "editor/overview page when the location parameter is set to "editorOverview"', () => { const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); const expectedUrl = `/editor/${mockOrg}/${mockApp}/overview`; @@ -36,11 +29,11 @@ describe('PackagesRouter', () => { expect(assignMock).toHaveBeenCalledWith(expectedUrl); }); - it('should navigate to the correct URL with subUrl', () => { + it('should navigate to the correct URL and include queryParams', () => { const packagesRouter = new PackagesRouter({ org: mockOrg, app: mockApp }); - const mockSubUrl = '?layout=123'; - const expectedUrl = `/editor/${mockOrg}/${mockApp}/ui-editor${mockSubUrl}`; + const mockQueryParams = '?layout=123'; + const expectedUrl = `/editor/${mockOrg}/${mockApp}/ui-editor${mockQueryParams}`; const assignMock = jest.fn(); Object.defineProperty(window, 'location', { @@ -48,7 +41,7 @@ describe('PackagesRouter', () => { writable: true, }); - packagesRouter.navigateToPackage('editorUiEditor', mockSubUrl); + packagesRouter.navigateToPackage('editorUiEditor', mockQueryParams); expect(assignMock).toHaveBeenCalledWith(expectedUrl); }); diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts index 37596b345cf..9e2a1207761 100644 --- a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts +++ b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts @@ -10,7 +10,7 @@ type PackagesRoute = | 'preview' | 'editorPublish'; -const packagesRoutes = { +const packagesRoutes: Record = { dashboard: '/dashboard', editorOverview: '/editor/{{org}}/{{app}}/overview', editorUiEditor: '/editor/{{org}}/{{app}}/ui-editor', @@ -27,8 +27,8 @@ export class PackagesRouter { this.org = this.paramsOptions.org ?? ''; } - public navigateToPackage(packageRoute: PackagesRoute, subUrl?: string): void { - window.location.assign(`${this.getPackageNavigationUrl(packageRoute)}${subUrl ? subUrl : ''}`); + public navigateToPackage(packageRoute: PackagesRoute, queryParams?: string): void { + window.location.assign(`${this.getPackageNavigationUrl(packageRoute)}${queryParams ?? ''}`); } public getPackageNavigationUrl(packageRoute: PackagesRoute): string { From 84d230999e4fd64c69528df10aab77e32c3856be Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt Date: Tue, 19 Dec 2023 12:39:10 +0100 Subject: [PATCH 3/3] fixing feedback from PR --- .../src/components/AppBarConfig/AppPreviewBarConfig.tsx | 4 ++-- .../src/navigation/PackagesRouter/PackagesRouter.test.ts | 2 +- .../shared/src/navigation/PackagesRouter/PackagesRouter.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx b/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx index a94ab5dba22..a5579ddcdfa 100644 --- a/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx +++ b/frontend/app-preview/src/components/AppBarConfig/AppPreviewBarConfig.tsx @@ -102,7 +102,7 @@ export const appPreviewButtonActions = ( instanceId: string, ): AltinnButtonActionItem[] => { const packagesRouter = new PackagesRouter({ org, app }); - const subUrl = `?layout=${window.localStorage.getItem(instanceId)}`; + const queryParams = `?layout=${window.localStorage.getItem(instanceId)}`; const action: AltinnButtonActionItem[] = [ { @@ -110,7 +110,7 @@ export const appPreviewButtonActions = ( menuKey: TopBarMenu.Preview, buttonVariant: 'secondary', headerButtonsClasses: classes.backToEditorBtn, - handleClick: () => packagesRouter.navigateToPackage('editorUiEditor', subUrl), + handleClick: () => packagesRouter.navigateToPackage('editorUiEditor', queryParams), }, ]; return action; diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts index c6204cfd3a3..a7930f5a518 100644 --- a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts +++ b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.test.ts @@ -6,7 +6,7 @@ const mockApp: string = 'app'; describe('PackagesRouter', () => { describe('constructor', () => { it('should default to empty strings if app and org are not provided', () => { - const routerWithoutParams = new PackagesRouter({}); + const routerWithoutParams = new PackagesRouter(); expect(routerWithoutParams['app']).toEqual(''); expect(routerWithoutParams['org']).toEqual(''); }); diff --git a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts index 9e2a1207761..5f6515aa770 100644 --- a/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts +++ b/frontend/packages/shared/src/navigation/PackagesRouter/PackagesRouter.ts @@ -22,9 +22,9 @@ export class PackagesRouter { private app: string; private org: string; - constructor(private paramsOptions: ParamsOptions) { - this.app = this.paramsOptions.app ?? ''; - this.org = this.paramsOptions.org ?? ''; + constructor(private paramsOptions?: ParamsOptions) { + this.app = this.paramsOptions?.app ?? ''; + this.org = this.paramsOptions?.org ?? ''; } public navigateToPackage(packageRoute: PackagesRoute, queryParams?: string): void {