-
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.
- Loading branch information
Showing
437 changed files
with
33,585 additions
and
3 deletions.
There are no files selected for viewing
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,61 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import { routerRoutes } from './routes'; | ||
import { RoutePaths } from '../enums/RoutePaths'; | ||
import React from 'react'; | ||
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; | ||
import { QueryKey } from 'app-shared/types/QueryKey'; | ||
import { AppVersion } from 'app-shared/types/AppVersion'; | ||
import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; | ||
import { queriesMock } from 'app-shared/mocks/queriesMock'; | ||
|
||
// Mocks: | ||
jest.mock('../../packages/ux-editor-v3/src/SubApp', () => ({ | ||
SubApp: () => <div data-testid='version 3' />, | ||
})); | ||
jest.mock('../../packages/ux-editor/src/SubApp', () => ({ | ||
SubApp: () => <div data-testid='latest version' />, | ||
})); | ||
|
||
// Test data | ||
const org = 'org'; | ||
const app = 'app'; | ||
|
||
describe('routes', () => { | ||
describe(RoutePaths.UIEditor, () => { | ||
type FrontendVersion = null | '3.0.0' | '4.0.0'; | ||
type PackageVersion = 'version 3' | 'latest version'; | ||
type TestCase = [PackageVersion, FrontendVersion]; | ||
|
||
const testCases: TestCase[] = [ | ||
['version 3', null], | ||
['version 3', '3.0.0'], | ||
['latest version', '4.0.0'], | ||
]; | ||
|
||
it.each(testCases)( | ||
'Renders the %s schema editor page when the app frontend version is %s', | ||
(expectedPackage, frontendVersion) => { | ||
renderUiEditor(frontendVersion); | ||
expect(screen.getByTestId(expectedPackage)).toBeInTheDocument(); | ||
}, | ||
); | ||
|
||
const renderUiEditor = (frontendVersion: string | null) => | ||
renderSubapp(RoutePaths.UIEditor, frontendVersion); | ||
}); | ||
}); | ||
|
||
const renderSubapp = (path: RoutePathsm, frontendVersion: string = null) => { | ||
const Subapp = routerRoutes.find((route) => route.path === path)!.subapp; | ||
const appVersion: AppVersion = { | ||
frontendVersion, | ||
backendVersion: '7.0.0', | ||
}; | ||
const queryClient = createQueryClientMock(); | ||
queryClient.setQueryData([QueryKey.AppVersion, org, app], appVersion); | ||
return render( | ||
<ServicesContextProvider {...queriesMock} client={queryClient}> | ||
<Subapp /> | ||
</ServicesContextProvider>, | ||
); | ||
}; |
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
# Tjeneste 3.0 react POC |
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 @@ | ||
module.exports = require('../../jest.config'); |
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,36 @@ | ||
{ | ||
"name": "ux-editor-v3", | ||
"description": "", | ||
"version": "1.0.1", | ||
"author": "Altinn", | ||
"dependencies": { | ||
"@mui/material": "5.15.5", | ||
"@reduxjs/toolkit": "1.9.7", | ||
"@studio/icons": "workspace:^", | ||
"axios": "1.6.5", | ||
"classnames": "2.5.1", | ||
"react": "18.2.0", | ||
"react-dnd": "16.0.1", | ||
"react-dnd-html5-backend": "16.0.1", | ||
"react-dom": "18.2.0", | ||
"react-modal": "3.16.1", | ||
"react-redux": "8.1.3", | ||
"react-select": "5.8.0", | ||
"redux": "4.2.1", | ||
"reselect": "4.1.8", | ||
"typescript": "5.3.3", | ||
"uuid": "9.0.1" | ||
}, | ||
"devDependencies": { | ||
"@redux-devtools/extension": "3.0.0", | ||
"jest": "29.7.0" | ||
}, | ||
"license": "3-Clause BSD", | ||
"main": "index.js", | ||
"peerDependencies": { | ||
"webpack": "5.89.0" | ||
}, | ||
"scripts": { | ||
"test": "jest --maxWorkers=50%" | ||
} | ||
} |
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,106 @@ | ||
import React from 'react'; | ||
import { screen, waitFor } from '@testing-library/react'; | ||
import { formLayoutSettingsMock, renderWithProviders } from './testing/mocks'; | ||
import { App } from './App'; | ||
import { textMock } from '../../../testing/mocks/i18nMock'; | ||
import { typedLocalStorage } from 'app-shared/utils/webStorage'; | ||
import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; | ||
import { appStateMock } from './testing/stateMocks'; | ||
import type { AppContextProps } from './AppContext'; | ||
import ruleHandlerMock from './testing/ruleHandlerMock'; | ||
import { layoutSetsMock } from './testing/layoutMock'; | ||
|
||
const { selectedLayoutSet } = appStateMock.formDesigner.layout; | ||
|
||
const renderApp = ( | ||
queries: Partial<ServicesContextProps> = {}, | ||
appContextProps: Partial<AppContextProps> = {}, | ||
) => { | ||
return renderWithProviders(<App />, { | ||
queries, | ||
appContextProps, | ||
}); | ||
}; | ||
|
||
describe('App', () => { | ||
it('should render the spinner', () => { | ||
renderApp({}, { selectedLayoutSet }); | ||
expect(screen.getByText(textMock('general.loading'))).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render the component', async () => { | ||
const mockQueries: Partial<ServicesContextProps> = { | ||
getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')), | ||
getRuleModel: jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)), | ||
getLayoutSets: jest.fn().mockImplementation(() => Promise.resolve(layoutSetsMock)), | ||
getFormLayoutSettings: jest | ||
.fn() | ||
.mockImplementation(() => Promise.resolve(formLayoutSettingsMock)), | ||
}; | ||
renderApp(mockQueries, { selectedLayoutSet }); | ||
await waitFor(() => | ||
expect(screen.queryByText(textMock('general.loading'))).not.toBeInTheDocument(), | ||
); | ||
}); | ||
|
||
it('Removes the preview layout set from local storage if it does not exist', async () => { | ||
const removeSelectedLayoutSetMock = jest.fn(); | ||
const layoutSetThatDoesNotExist = 'layout-set-that-does-not-exist'; | ||
const mockQueries: Partial<ServicesContextProps> = { | ||
getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')), | ||
getRuleModel: jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)), | ||
getLayoutSets: jest.fn().mockImplementation(() => Promise.resolve(layoutSetsMock)), | ||
getFormLayoutSettings: jest | ||
.fn() | ||
.mockImplementation(() => Promise.resolve(formLayoutSettingsMock)), | ||
}; | ||
renderApp(mockQueries, { | ||
selectedLayoutSet: layoutSetThatDoesNotExist, | ||
removeSelectedLayoutSet: removeSelectedLayoutSetMock, | ||
}); | ||
await waitFor(() => | ||
expect(screen.queryByText(textMock('general.loading'))).not.toBeInTheDocument(), | ||
); | ||
expect(removeSelectedLayoutSetMock).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('Does not remove the preview layout set from local storage if it exists', async () => { | ||
const removeSelectedLayoutSetMock = jest.fn(); | ||
const mockQueries: Partial<ServicesContextProps> = { | ||
getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')), | ||
getRuleModel: jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)), | ||
getLayoutSets: jest.fn().mockImplementation(() => Promise.resolve(layoutSetsMock)), | ||
getFormLayoutSettings: jest | ||
.fn() | ||
.mockImplementation(() => Promise.resolve(formLayoutSettingsMock)), | ||
}; | ||
jest.spyOn(typedLocalStorage, 'getItem').mockReturnValue(selectedLayoutSet); | ||
renderApp(mockQueries, { | ||
selectedLayoutSet, | ||
removeSelectedLayoutSet: removeSelectedLayoutSetMock, | ||
}); | ||
await waitFor(() => | ||
expect(screen.queryByText(textMock('general.loading'))).not.toBeInTheDocument(), | ||
); | ||
expect(removeSelectedLayoutSetMock).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('Renders the unsupported version message if the version is not supported', async () => { | ||
renderApp( | ||
{ | ||
getAppVersion: jest | ||
.fn() | ||
.mockImplementation(() => | ||
Promise.resolve({ backendVersion: '7.15.1', frontendVersion: '4.0.0-rc1' }), | ||
), | ||
}, | ||
{ selectedLayoutSet }, | ||
); | ||
|
||
expect( | ||
await screen.findByText( | ||
textMock('ux_editor.unsupported_version_message_title', { version: 'V4' }), | ||
), | ||
).toBeInTheDocument(); | ||
}); | ||
}); |
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 React, { useEffect } from 'react'; | ||
import { useSelector } from 'react-redux'; | ||
import { FormDesigner } from './containers/FormDesigner'; | ||
import { useText } from './hooks'; | ||
import { StudioPageSpinner } from '@studio/components'; | ||
import { ErrorPage } from './components/ErrorPage'; | ||
import { useDatamodelMetadataQuery } from './hooks/queries/useDatamodelMetadataQuery'; | ||
import { selectedLayoutNameSelector } from './selectors/formLayoutSelectors'; | ||
import { useWidgetsQuery } from './hooks/queries/useWidgetsQuery'; | ||
import { useTextResourcesQuery } from 'app-shared/hooks/queries/useTextResourcesQuery'; | ||
import { useLayoutSetsQuery } from './hooks/queries/useLayoutSetsQuery'; | ||
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams'; | ||
import { useAppContext } from './hooks/useAppContext'; | ||
import { FormContextProvider } from './containers/FormContext'; | ||
import { shouldDisplayFeature } from 'app-shared/utils/featureToggleUtils'; | ||
import { UnsupportedVersionMessage } from './components/UnsupportedVersionMessage'; | ||
import { useAppVersionQuery } from 'app-shared/hooks/queries/useAppVersionQuery'; | ||
|
||
/** | ||
* This is the main React component responsible for controlling | ||
* the mode of the application and loading initial data for the | ||
* application | ||
*/ | ||
|
||
export function App() { | ||
const t = useText(); | ||
const { org, app } = useStudioUrlParams(); | ||
const selectedLayout = useSelector(selectedLayoutNameSelector); | ||
const { selectedLayoutSet, setSelectedLayoutSet, removeSelectedLayoutSet } = useAppContext(); | ||
const { data: layoutSets, isSuccess: areLayoutSetsFetched } = useLayoutSetsQuery(org, app); | ||
const { isSuccess: areWidgetsFetched, isError: widgetFetchedError } = useWidgetsQuery(org, app); | ||
const { isSuccess: isDatamodelFetched, isError: dataModelFetchedError } = | ||
useDatamodelMetadataQuery(org, app); | ||
const { isSuccess: areTextResourcesFetched } = useTextResourcesQuery(org, app); | ||
const { data: appVersion } = useAppVersionQuery(org, app); | ||
|
||
useEffect(() => { | ||
if ( | ||
areLayoutSetsFetched && | ||
selectedLayoutSet && | ||
(!layoutSets || !layoutSets.sets.map((set) => set.id).includes(selectedLayoutSet)) | ||
) | ||
removeSelectedLayoutSet(); | ||
}, [ | ||
areLayoutSetsFetched, | ||
layoutSets, | ||
selectedLayoutSet, | ||
setSelectedLayoutSet, | ||
removeSelectedLayoutSet, | ||
]); | ||
|
||
const componentIsReady = areWidgetsFetched && isDatamodelFetched && areTextResourcesFetched; | ||
|
||
const componentHasError = dataModelFetchedError || widgetFetchedError; | ||
|
||
const mapErrorToDisplayError = (): { title: string; message: string } => { | ||
const defaultTitle = t('general.fetch_error_title'); | ||
const defaultMessage = t('general.fetch_error_message'); | ||
|
||
const createErrorMessage = (resource: string): { title: string; message: string } => ({ | ||
title: `${defaultTitle} ${resource}`, | ||
message: defaultMessage, | ||
}); | ||
|
||
if (dataModelFetchedError) { | ||
return createErrorMessage(t('general.dataModel')); | ||
} | ||
if (widgetFetchedError) { | ||
return createErrorMessage(t('general.widget')); | ||
} | ||
|
||
return createErrorMessage(t('general.unknown_error')); | ||
}; | ||
|
||
useEffect(() => { | ||
if (selectedLayoutSet === null && layoutSets) { | ||
// Only set layout set if layout sets exists and there is no layout set selected yet | ||
setSelectedLayoutSet(layoutSets.sets[0].id); | ||
} | ||
}, [setSelectedLayoutSet, selectedLayoutSet, layoutSets, app]); | ||
|
||
if ( | ||
appVersion?.frontendVersion?.startsWith('4') && | ||
!shouldDisplayFeature('shouldOverrideAppFrontendCheck') | ||
) { | ||
return ( | ||
<UnsupportedVersionMessage version='V4' closestSupportedVersion='V3' category='too-new' /> | ||
); | ||
} | ||
|
||
if (componentHasError) { | ||
const mappedError = mapErrorToDisplayError(); | ||
return <ErrorPage title={mappedError.title} message={mappedError.message} />; | ||
} | ||
|
||
if (componentIsReady) { | ||
return ( | ||
<FormContextProvider> | ||
<FormDesigner selectedLayout={selectedLayout} selectedLayoutSet={selectedLayoutSet} /> | ||
</FormContextProvider> | ||
); | ||
} | ||
return <StudioPageSpinner />; | ||
} |
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,11 @@ | ||
import type { RefObject } from 'react'; | ||
import { createContext } from 'react'; | ||
|
||
export interface AppContextProps { | ||
previewIframeRef: RefObject<HTMLIFrameElement>; | ||
selectedLayoutSet: string; | ||
setSelectedLayoutSet: (layoutSet: string) => void; | ||
removeSelectedLayoutSet: () => void; | ||
} | ||
|
||
export const AppContext = createContext<AppContextProps>(null); |
Oops, something went wrong.