diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx
index 81a0b5d62f2..5d4c0b11845 100644
--- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx
+++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx
@@ -6,20 +6,37 @@ import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext';
import { act, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import { textMock } from '../../../../testing/mocks/i18nMock';
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
-import { datamodelNameMock } from 'app-shared/mocks/datamodelMetadataMocks';
+import {
+ createJsonMetadataMock,
+ createXsdMetadataMock,
+} from 'app-shared/mocks/datamodelMetadataMocks';
import userEvent from '@testing-library/user-event';
import { dataMock } from '@altinn/schema-editor/mockData';
import { AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS } from 'app-shared/constants';
import type { SchemaEditorAppProps } from '@altinn/schema-editor/SchemaEditorApp';
import { QueryKey } from 'app-shared/types/QueryKey';
import { createApiErrorMock } from 'app-shared/mocks/apiErrorMock';
+import { createJsonModelPathMock } from 'app-shared/mocks/modelPathMocks';
+import type {
+ DatamodelMetadataJson,
+ DatamodelMetadataXsd,
+} from 'app-shared/types/DatamodelMetadata';
+import { verifyNeverOccurs } from '../../../../testing/testUtils';
const user = userEvent.setup();
// Test data:
-const modelPath = datamodelNameMock;
+const model1Name = 'model1';
+const model2name = 'model2';
+const model1Path = createJsonModelPathMock(model1Name);
+const model2Path = createJsonModelPathMock(model2name);
+const model1MetadataJson: DatamodelMetadataJson = createJsonMetadataMock(model1Name);
+const model1MetadataXsd: DatamodelMetadataXsd = createXsdMetadataMock(model1Name);
+const model2MetadataJson: DatamodelMetadataJson = createJsonMetadataMock(model2name);
+const model2MetadataXsd: DatamodelMetadataXsd = createXsdMetadataMock(model2name);
+
const defaultProps: SelectedSchemaEditorProps = {
- modelPath,
+ modelPath: model1Path,
};
const org = 'org';
const app = 'app';
@@ -72,6 +89,7 @@ describe('SelectedSchemaEditor', () => {
const getDatamodel = jest.fn().mockImplementation(() => Promise.resolve(dataMock));
render({ getDatamodel, saveDatamodel });
+
await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading')));
const button = screen.getByTestId(saveButtonTestId);
@@ -80,7 +98,7 @@ describe('SelectedSchemaEditor', () => {
act(() => jest.advanceTimersByTime(AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS));
await waitFor(() => expect(saveDatamodel).toHaveBeenCalledTimes(1));
- expect(saveDatamodel).toHaveBeenCalledWith(org, app, modelPath, dataMock);
+ expect(saveDatamodel).toHaveBeenCalledWith(org, app, model1Path, dataMock);
});
it('Autosaves when changing between models that are not present in the cache', async () => {
@@ -92,22 +110,19 @@ describe('SelectedSchemaEditor', () => {
await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading')));
expect(saveDatamodel).not.toHaveBeenCalled();
- const updatedProps = {
- ...defaultProps,
- modelPath: 'newModel',
- };
+ const updatedProps = { ...defaultProps, modelPath: model2Path };
rerender();
jest.advanceTimersByTime(AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS);
await waitFor(() => expect(saveDatamodel).toHaveBeenCalledTimes(1));
- expect(saveDatamodel).toHaveBeenCalledWith(org, app, datamodelNameMock, dataMock);
+ expect(saveDatamodel).toHaveBeenCalledWith(org, app, model1Path, dataMock);
});
it('Autosaves when changing between models that are already present in the cache', async () => {
const saveDatamodel = jest.fn();
const queryClient = createQueryClientMock();
const newModelPath = 'newModel';
- queryClient.setQueryData([QueryKey.JsonSchema, org, app, datamodelNameMock], dataMock);
- queryClient.setQueryData([QueryKey.JsonSchema, org, app, newModelPath], dataMock);
+ queryClient.setQueryData([QueryKey.JsonSchema, org, app, model1Path], dataMock);
+ queryClient.setQueryData([QueryKey.JsonSchema, org, app, model1Path], dataMock);
const {
renderResult: { rerender },
} = render({ saveDatamodel }, queryClient);
@@ -120,7 +135,29 @@ describe('SelectedSchemaEditor', () => {
rerender();
jest.advanceTimersByTime(AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS);
await waitFor(() => expect(saveDatamodel).toHaveBeenCalledTimes(1));
- expect(saveDatamodel).toHaveBeenCalledWith(org, app, datamodelNameMock, dataMock);
+ expect(saveDatamodel).toHaveBeenCalledWith(org, app, model1Path, dataMock);
+ });
+
+ it('Does not save when model is deleted', async () => {
+ const saveDatamodel = jest.fn();
+ const queryClient = createQueryClientMock();
+
+ queryClient.setQueryData([QueryKey.JsonSchema, org, app, model1Path], dataMock);
+ queryClient.setQueryData([QueryKey.JsonSchema, org, app, model2Path], dataMock);
+ const {
+ renderResult: { rerender },
+ } = render({ saveDatamodel }, queryClient);
+ expect(saveDatamodel).not.toHaveBeenCalled();
+
+ const updatedProps = {
+ ...defaultProps,
+ modelPath: model2Path,
+ };
+ queryClient.setQueryData([QueryKey.DatamodelsJson, org, app], [model2MetadataJson]);
+ queryClient.setQueryData([QueryKey.DatamodelsXsd, org, app], [model2MetadataXsd]);
+ rerender();
+ jest.advanceTimersByTime(AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS);
+ await verifyNeverOccurs(() => expect(saveDatamodel).toHaveBeenCalled());
});
});
@@ -128,9 +165,18 @@ const render = (
queries: Partial = {},
queryClient = createQueryClientMock(),
props: Partial = {},
-) =>
- renderWithMockStore(
+) => {
+ queryClient.setQueryData(
+ [QueryKey.DatamodelsJson, org, app],
+ [model1MetadataJson, model2MetadataJson],
+ );
+ queryClient.setQueryData(
+ [QueryKey.DatamodelsXsd, org, app],
+ [model1MetadataXsd, model2MetadataXsd],
+ );
+ return renderWithMockStore(
{},
queries,
queryClient,
)();
+};
diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.tsx b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.tsx
index 3f58ee05bb8..3aedb20ac35 100644
--- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.tsx
+++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.tsx
@@ -8,8 +8,15 @@ import { useTranslation } from 'react-i18next';
import { AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS } from 'app-shared/constants';
import type { JsonSchema } from 'app-shared/types/JsonSchema';
import { useOnUnmount } from 'app-shared/hooks/useOnUnmount';
+import type {
+ DatamodelMetadataJson,
+ DatamodelMetadataXsd,
+} from 'app-shared/types/DatamodelMetadata';
+import { useQueryClient } from '@tanstack/react-query';
+import { QueryKey } from 'app-shared/types/QueryKey';
+import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
+import { mergeJsonAndXsdData } from 'app-development/utils/metadataUtils';
import { extractFilename, removeSchemaExtension } from 'app-shared/utils/filenameUtils';
-
export interface SelectedSchemaEditorProps {
modelPath: string;
}
@@ -46,7 +53,9 @@ interface SchemaEditorWithDebounceProps {
}
const SchemaEditorWithDebounce = ({ jsonSchema, modelPath }: SchemaEditorWithDebounceProps) => {
+ const { org, app } = useStudioUrlParams();
const { mutate } = useSchemaMutation();
+ const queryClient = useQueryClient();
const [model, setModel] = useState(jsonSchema);
const saveTimeoutRef = useRef>();
const updatedModel = useRef(jsonSchema);
@@ -68,9 +77,24 @@ const SchemaEditorWithDebounce = ({ jsonSchema, modelPath }: SchemaEditorWithDeb
[saveFunction],
);
+ const doesModelExist = useCallback(() => {
+ const jsonModels: DatamodelMetadataJson[] = queryClient.getQueryData([
+ QueryKey.DatamodelsJson,
+ org,
+ app,
+ ]);
+ const xsdModels: DatamodelMetadataXsd[] = queryClient.getQueryData([
+ QueryKey.DatamodelsXsd,
+ org,
+ app,
+ ]);
+ const metadataList = mergeJsonAndXsdData(jsonModels, xsdModels);
+ return metadataList.some((datamodel) => datamodel.repositoryRelativeUrl === modelPath);
+ }, [queryClient, org, app, modelPath]);
+
useOnUnmount(() => {
clearTimeout(saveTimeoutRef.current);
- saveFunction();
+ if (doesModelExist()) saveFunction();
});
return (
diff --git a/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.test.ts b/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.test.ts
new file mode 100644
index 00000000000..d61c5df3a03
--- /dev/null
+++ b/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.test.ts
@@ -0,0 +1,71 @@
+import { renderHookWithMockStore } from '../../test/mocks';
+import { useDeleteDatamodelMutation } from './useDeleteDatamodelMutation';
+import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext';
+import type { QueryClient } from '@tanstack/react-query';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import { waitFor } from '@testing-library/react';
+import { QueryKey } from 'app-shared/types/QueryKey';
+import { queriesMock } from 'app-shared/mocks/queriesMock';
+import { createJsonModelPathMock } from 'app-shared/mocks/modelPathMocks';
+import {
+ createJsonMetadataMock,
+ createXsdMetadataMock,
+} from 'app-shared/mocks/datamodelMetadataMocks';
+
+const modelName = 'modelName';
+const modelPath = createJsonModelPathMock(modelName);
+const org = 'org';
+const app = 'app';
+const modelMetadataJson = createJsonMetadataMock(modelName);
+const modelMetadataXsd = createXsdMetadataMock(modelName);
+
+describe('useDeleteDatamodelMutation', () => {
+ beforeEach(jest.clearAllMocks);
+
+ it('Calls deleteDatamodel with correct parameters', async () => {
+ const client = createQueryClientMock();
+ client.setQueryData([QueryKey.DatamodelsJson, org, app], [modelMetadataJson]);
+ client.setQueryData([QueryKey.DatamodelsXsd, org, app], [modelMetadataXsd]);
+ const {
+ renderHookResult: { result },
+ } = render({}, client);
+ expect(result.current).toBeDefined();
+ result.current.mutate(modelPath);
+ await waitFor(() => result.current.isSuccess);
+ expect(queriesMock.deleteDatamodel).toHaveBeenCalledTimes(1);
+ expect(queriesMock.deleteDatamodel).toHaveBeenCalledWith(org, app, modelPath);
+ });
+
+ it('Removes the metadata instances from the query cache', async () => {
+ const client = createQueryClientMock();
+ client.setQueryData([QueryKey.DatamodelsJson, org, app], [modelMetadataJson]);
+ client.setQueryData([QueryKey.DatamodelsXsd, org, app], [modelMetadataXsd]);
+ const {
+ renderHookResult: { result },
+ } = render({}, client);
+ result.current.mutate(modelPath);
+ await waitFor(() => result.current.isSuccess);
+ expect(client.getQueryData([QueryKey.DatamodelsJson, org, app])).toEqual([]);
+ expect(client.getQueryData([QueryKey.DatamodelsXsd, org, app])).toEqual([]);
+ });
+
+ it('Removes the schema queries from the query cache', async () => {
+ const client = createQueryClientMock();
+ client.setQueryData([QueryKey.DatamodelsJson, org, app], [modelMetadataJson]);
+ client.setQueryData([QueryKey.DatamodelsXsd, org, app], [modelMetadataXsd]);
+ const {
+ renderHookResult: { result },
+ } = render({}, client);
+ result.current.mutate(modelPath);
+ await waitFor(() => result.current.isSuccess);
+ expect(client.getQueryData([QueryKey.JsonSchema, org, app, modelPath])).toBeUndefined();
+ expect(
+ client.getQueryData([QueryKey.JsonSchema, org, app, modelMetadataXsd.repositoryRelativeUrl]),
+ ).toBeUndefined();
+ });
+});
+
+const render = (
+ queries: Partial = {},
+ queryClient: QueryClient = createQueryClientMock(),
+) => renderHookWithMockStore({}, queries, queryClient)(() => useDeleteDatamodelMutation());
diff --git a/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.ts b/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.ts
index f292aa4af31..dcf1838e8cf 100644
--- a/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.ts
+++ b/frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.ts
@@ -3,6 +3,7 @@ import { useServicesContext } from 'app-shared/contexts/ServicesContext';
import { QueryKey } from 'app-shared/types/QueryKey';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
import { isXsdFile } from 'app-shared/utils/filenameUtils';
+import type { DatamodelMetadata } from 'app-shared/types/DatamodelMetadata';
export const useDeleteDatamodelMutation = () => {
const { deleteDatamodel } = useServicesContext();
@@ -10,19 +11,33 @@ export const useDeleteDatamodelMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (modelPath: string) => {
- await deleteDatamodel(org, app, modelPath);
- const respectiveFileNameInXsdOrJson = isXsdFile(modelPath)
+ const jsonSchemaPath = isXsdFile(modelPath)
? modelPath.replace('.xsd', '.schema.json')
- : modelPath.replace('.schema.json', '.xsd');
- queryClient.setQueryData([QueryKey.JsonSchema, org, app, modelPath], undefined);
+ : modelPath;
+ const xsdPath = isXsdFile(modelPath) ? modelPath : modelPath.replace('.schema.json', '.xsd');
queryClient.setQueryData(
- [QueryKey.JsonSchema, org, app, respectiveFileNameInXsdOrJson],
- undefined,
+ [QueryKey.DatamodelsJson, org, app],
+ (oldData: DatamodelMetadata[]) => removeDatamodelFromList(oldData, jsonSchemaPath),
+ );
+ queryClient.setQueryData([QueryKey.DatamodelsXsd, org, app], (oldData: DatamodelMetadata[]) =>
+ removeDatamodelFromList(oldData, xsdPath),
);
- await Promise.all([
- queryClient.invalidateQueries({ queryKey: [QueryKey.DatamodelsJson, org, app] }),
- queryClient.invalidateQueries({ queryKey: [QueryKey.DatamodelsXsd, org, app] }),
- ]);
+ await deleteDatamodel(org, app, modelPath);
+ return { jsonSchemaPath, xsdPath };
+ },
+ onSuccess: ({ jsonSchemaPath, xsdPath }) => {
+ queryClient.removeQueries({
+ queryKey: [QueryKey.JsonSchema, org, app, jsonSchemaPath],
+ });
+ queryClient.removeQueries({
+ queryKey: [QueryKey.JsonSchema, org, app, xsdPath],
+ });
},
});
};
+
+export const removeDatamodelFromList = (
+ datamodels: DatamodelMetadata[],
+ relativeUrl: string,
+): DatamodelMetadata[] =>
+ datamodels.filter((datamodel) => datamodel.repositoryRelativeUrl !== relativeUrl);
diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json
index 8aac453738f..09346485b3b 100644
--- a/frontend/language/src/nb.json
+++ b/frontend/language/src/nb.json
@@ -1383,7 +1383,8 @@
"ux_editor.component_properties.action": "Aksjon",
"ux_editor.component_properties.align": "Plassering*",
"ux_editor.component_properties.attribution": "Opphav",
- "ux_editor.component_properties.autocomplete": "HTML autocomplete",
+ "ux_editor.component_properties.autocomplete": "HTML autofullfør",
+ "ux_editor.component_properties.autocomplete_default": "Standard",
"ux_editor.component_properties.breakAfter": "Sideskift ETTER komponenten",
"ux_editor.component_properties.breakBefore": "Sideskift FØR komponenten",
"ux_editor.component_properties.buttonStyle": "Knappens stil",
@@ -1446,7 +1447,9 @@
"ux_editor.component_title.AttachmentList": "Liste over vedlegg",
"ux_editor.component_title.Button": "Knapp",
"ux_editor.component_title.ButtonGroup": "Knappegruppe",
- "ux_editor.component_title.Checkboxes": "Avkrysningsboks",
+ "ux_editor.component_title.Checkboxes": "Avkrysningsbokser",
+ "ux_editor.component_title.Custom": "Egendefinert",
+ "ux_editor.component_title.CustomButton": "Egendefinert kanpp",
"ux_editor.component_title.Datepicker": "Dato",
"ux_editor.component_title.Dropdown": "Nedtrekksliste",
"ux_editor.component_title.FileUpload": "Vedlegg",
@@ -1460,6 +1463,7 @@
"ux_editor.component_title.InstanceInformation": "Informasjon om instans",
"ux_editor.component_title.InstantiationButton": "Instansieringsknapp",
"ux_editor.component_title.Likert": "Likert",
+ "ux_editor.component_title.LikertItem": "Likertelement",
"ux_editor.component_title.Link": "Lenke",
"ux_editor.component_title.List": "Liste",
"ux_editor.component_title.Map": "Stedfeste i kart",
@@ -1469,7 +1473,8 @@
"ux_editor.component_title.Panel": "Informativ melding",
"ux_editor.component_title.Paragraph": "Paragraf",
"ux_editor.component_title.PrintButton": "Utskriftsknapp",
- "ux_editor.component_title.RadioButtons": "Radioknapp",
+ "ux_editor.component_title.RadioButtons": "Radioknapper",
+ "ux_editor.component_title.RepeatingGroup": "Repeterende gruppe",
"ux_editor.component_title.Summary": "Oppsummering",
"ux_editor.component_title.TextArea": "Langt svar",
"ux_editor.component_unknown": "Ukjent komponent",
diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts
index 7520d22b93c..e4d77fdb33d 100644
--- a/frontend/packages/shared/src/api/mutations.ts
+++ b/frontend/packages/shared/src/api/mutations.ts
@@ -43,6 +43,7 @@ import type { CreateDeploymentPayload } from 'app-shared/types/api/CreateDeploym
import type { CreateReleasePayload } from 'app-shared/types/api/CreateReleasePayload';
import type { CreateRepoCommitPayload } from 'app-shared/types/api/CreateRepoCommitPayload';
import type { ExternalFormLayout } from 'app-shared/types/api/FormLayoutsResponse';
+import type { ExternalFormLayoutV3 } from 'app-shared/types/api/FormLayoutsResponseV3';
import type { LayoutSetConfig, LayoutSets } from 'app-shared/types/api/LayoutSetsResponse';
import type { ILayoutSettings, ITextResourcesObjectFormat } from 'app-shared/types/global';
import type { RuleConfig } from 'app-shared/types/RuleConfig';
@@ -83,6 +84,7 @@ export const pushRepoChanges = (org: string, app: string) => post(repoPushPath(o
export const resetRepoChanges = (org: string, app: string) => get(repoResetPath(org, app)); //Technically a mutation, but currently only implemented as a GET
export const saveDatamodel = (org: string, app: string, modelPath: string, payload: JsonSchema) => put(datamodelPath(org, app, modelPath, true), payload);
export const saveFormLayout = (org: string, app: string, layoutName: string, layoutSetName: string, payload: ExternalFormLayout) => post(formLayoutPath(org, app, layoutName, layoutSetName), payload);
+export const saveFormLayoutV3 = (org: string, app: string, layoutName: string, layoutSetName: string, payload: ExternalFormLayoutV3) => post(formLayoutPath(org, app, layoutName, layoutSetName), payload);
export const saveFormLayoutSettings = (org: string, app: string, layoutSetName: string, payload: ILayoutSettings) => post(layoutSettingsPath(org, app, layoutSetName), payload);
export const saveRuleConfig = (org: string, app: string, layoutSetName: string, payload: RuleConfig) => post(ruleConfigPath(org, app, layoutSetName), payload);
export const setStarredRepo = (org: string, app: string) => put(userStarredRepoPath(org, app), {});
diff --git a/frontend/packages/shared/src/api/queries.ts b/frontend/packages/shared/src/api/queries.ts
index 0b1dd6fc31f..bf94cbc6beb 100644
--- a/frontend/packages/shared/src/api/queries.ts
+++ b/frontend/packages/shared/src/api/queries.ts
@@ -71,6 +71,7 @@ import type { ApplicationMetadata } from 'app-shared/types/ApplicationMetadata';
import type { Altinn2LinkService } from 'app-shared/types/Altinn2LinkService';
import type { NewsList } from 'app-shared/types/api/NewsList';
import type { AppVersion } from 'app-shared/types/AppVersion';
+import type { FormLayoutsResponseV3 } from 'app-shared/types/api/FormLayoutsResponseV3';
export const getAppReleases = (owner: string, app: string) => get(releasesPath(owner, app, 'Descending'));
export const getAppVersion = (org: string, app: string) => get(appVersionPath(org, app));
@@ -87,6 +88,7 @@ export const getEnvironments = () => get(envConfigPath());
export const getExpressionSchema = () => get(expressionSchemaUrl());
export const getFormLayoutSettings = (owner: string, app: string, layoutSetName: string) => get(layoutSettingsPath(owner, app, layoutSetName));
export const getFormLayouts = (owner: string, app: string, layoutSetName: string) => get(formLayoutsPath(owner, app, layoutSetName));
+export const getFormLayoutsV3 = (owner: string, app: string, layoutSetName: string) => get(formLayoutsPath(owner, app, layoutSetName));
export const getFrontEndSettings = (owner: string, app: string) => get(frontEndSettingsPath(owner, app));
export const getInstanceIdForPreview = (owner: string, app: string) => get(instanceIdForPreviewPath(owner, app));
export const getLayoutSchema = () => get(layoutSchemaUrl());
diff --git a/frontend/packages/shared/src/mocks/datamodelMetadataMocks.ts b/frontend/packages/shared/src/mocks/datamodelMetadataMocks.ts
index 6fbae30be5f..22ff72523a3 100644
--- a/frontend/packages/shared/src/mocks/datamodelMetadataMocks.ts
+++ b/frontend/packages/shared/src/mocks/datamodelMetadataMocks.ts
@@ -2,6 +2,7 @@ import type {
DatamodelMetadataJson,
DatamodelMetadataXsd,
} from 'app-shared/types/DatamodelMetadata';
+import { createJsonModelPathMock, createXsdModelPathMock } from 'app-shared/mocks/modelPathMocks';
export const datamodelNameMock = 'model1';
const description = null;
@@ -15,18 +16,21 @@ const metadataMockBase = {
lastChanged,
};
-export const jsonMetadataMock: DatamodelMetadataJson = {
+export const createJsonMetadataMock = (modelName: string): DatamodelMetadataJson => ({
...metadataMockBase,
- fileName: `${datamodelNameMock}.schema.json`,
- filePath: `${directory}/${datamodelNameMock}.schema.json`,
+ fileName: `${modelName}.schema.json`,
+ filePath: `${directory}/${modelName}.schema.json`,
fileType: '.json',
- repositoryRelativeUrl: `/App/models/${datamodelNameMock}.schema.json`,
-};
+ repositoryRelativeUrl: createJsonModelPathMock(modelName),
+});
-export const xsdMetadataMock: DatamodelMetadataXsd = {
+export const createXsdMetadataMock = (modelName: string): DatamodelMetadataXsd => ({
...metadataMockBase,
- fileName: `${datamodelNameMock}.xsd`,
- filePath: `${directory}/${datamodelNameMock}.xsd`,
+ fileName: `${modelName}.xsd`,
+ filePath: `${directory}/${modelName}.xsd`,
fileType: '.xsd',
- repositoryRelativeUrl: `/App/models/${datamodelNameMock}.xsd`,
-};
+ repositoryRelativeUrl: createXsdModelPathMock(modelName),
+});
+
+export const jsonMetadataMock: DatamodelMetadataJson = createJsonMetadataMock(datamodelNameMock);
+export const xsdMetadataMock: DatamodelMetadataXsd = createXsdMetadataMock(datamodelNameMock);
diff --git a/frontend/packages/shared/src/mocks/modelPathMocks.ts b/frontend/packages/shared/src/mocks/modelPathMocks.ts
new file mode 100644
index 00000000000..b098721ac3d
--- /dev/null
+++ b/frontend/packages/shared/src/mocks/modelPathMocks.ts
@@ -0,0 +1,2 @@
+export const createJsonModelPathMock = (name: string): string => `/App/models/${name}.schema.json`;
+export const createXsdModelPathMock = (name: string): string => `/App/models/${name}.xsd`;
diff --git a/frontend/packages/shared/src/mocks/queriesMock.ts b/frontend/packages/shared/src/mocks/queriesMock.ts
index ebff491aa7a..18fdf00f43d 100644
--- a/frontend/packages/shared/src/mocks/queriesMock.ts
+++ b/frontend/packages/shared/src/mocks/queriesMock.ts
@@ -69,6 +69,7 @@ import {
user,
validation,
} from './mocks';
+import type { FormLayoutsResponseV3 } from 'app-shared/types/api/FormLayoutsResponseV3';
export const queriesMock: ServicesContextProps = {
// Queries
@@ -97,6 +98,7 @@ export const queriesMock: ServicesContextProps = {
getExpressionSchema: jest.fn().mockImplementation(() => Promise.resolve([])),
getFormLayoutSettings: jest.fn().mockImplementation(() => Promise.resolve({})),
getFormLayouts: jest.fn().mockImplementation(() => Promise.resolve({})),
+ getFormLayoutsV3: jest.fn().mockImplementation(() => Promise.resolve({})),
getFrontEndSettings: jest.fn().mockImplementation(() => Promise.resolve({})),
getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('')),
getLayoutSchema: jest.fn().mockImplementation(() => Promise.resolve([])),
@@ -188,6 +190,7 @@ export const queriesMock: ServicesContextProps = {
resetRepoChanges: jest.fn().mockImplementation(() => Promise.resolve()),
saveDatamodel: jest.fn().mockImplementation(() => Promise.resolve()),
saveFormLayout: jest.fn().mockImplementation(() => Promise.resolve()),
+ saveFormLayoutV3: jest.fn().mockImplementation(() => Promise.resolve()),
saveFormLayoutSettings: jest.fn().mockImplementation(() => Promise.resolve({})),
saveRuleConfig: jest.fn().mockImplementation(() => Promise.resolve(ruleConfig)),
setStarredRepo: jest.fn().mockImplementation(() => Promise.resolve()),
diff --git a/frontend/packages/shared/src/types/ActionButtonAction.ts b/frontend/packages/shared/src/types/ActionButtonAction.ts
new file mode 100644
index 00000000000..633bc7cd39f
--- /dev/null
+++ b/frontend/packages/shared/src/types/ActionButtonAction.ts
@@ -0,0 +1 @@
+export type ActionButtonAction = 'instantiate' | 'confirm' | 'sign' | 'reject';
diff --git a/frontend/packages/shared/src/types/ComponentSpecificConfig.ts b/frontend/packages/shared/src/types/ComponentSpecificConfig.ts
index e9b92febf4b..69315bc8a84 100644
--- a/frontend/packages/shared/src/types/ComponentSpecificConfig.ts
+++ b/frontend/packages/shared/src/types/ComponentSpecificConfig.ts
@@ -2,16 +2,36 @@ import type { ComponentType } from './ComponentType';
import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
import type { MapLayer } from 'app-shared/types/MapLayer';
import type { FormPanelVariant } from 'app-shared/types/FormPanelVariant';
+import type { HeadingLevel } from 'app-shared/types/HeadingLevel';
+import type { ActionButtonAction } from 'app-shared/types/ActionButtonAction';
+import type { GridRow } from 'app-shared/types/GridRow';
+import type { HTMLAutoCompleteValue } from 'app-shared/types/HTMLAutoCompleteValue';
-type Option = {
+type Option = {
label: string;
value: T;
+ description?: string;
+ helpText?: string;
};
-type OptionsComponentBase = {
+type SelectionComponent = {
+ optionsId?: string;
+ mapping?: KeyValuePairs;
+ queryParameters?: KeyValuePairs;
options?: Option[];
+ secure?: boolean;
+ sortOrder?: 'asc' | 'desc';
+ source?: {
+ group: string;
+ label: string;
+ value: string;
+ description?: string;
+ helpText?: string;
+ };
+};
+
+type SelectionComponentFull = SelectionComponent & {
preselectedOptionIndex?: number;
- optionsId?: string;
};
type FileUploadComponentBase = {
@@ -22,33 +42,124 @@ type FileUploadComponentBase = {
maxNumberOfAttachments: number;
minNumberOfAttachments: number;
validFileEndings?: string;
+ alertOnDelete?: boolean;
+};
+
+type FormComponentProps = {
+ readonly?: boolean;
+ required?: boolean;
+ showValidation?: AllowedValidationMasks;
+};
+
+type AllowedValidationMasks =
+ | 'Schema'
+ | 'Component'
+ | 'Expression'
+ | 'CustomBackend'
+ | 'Required'
+ | 'AllExceptRequired'
+ | 'All';
+
+type SummarizableComponentProps = {
+ renderAsSummary?: boolean;
+};
+
+type LabeledComponentProps = {
+ labelSettings?: LabelSettings;
+};
+
+type LabelSettings = {
+ optionalIndicator?: boolean;
+};
+
+type ButtonStyle = 'primary' | 'secondary';
+type LayoutStyle = 'column' | 'row' | 'table';
+
+type ClientActionId = 'nextPage' | 'previousPage' | 'navigateToPage';
+type ClientAction = {
+ id: T;
+ type: 'ClientAction';
+};
+type ServerAction = {
+ id: string;
+ type: 'ServerAction';
+};
+type CustomAction = ClientAction | ServerAction;
+
+type PageValidation = {
+ page: 'current' | 'currentAndPrevious' | 'all';
+ show: AllowedValidationMasks;
};
export type ComponentSpecificConfig = {
[ComponentType.Alert]: { severity: 'success' | 'info' | 'warning' | 'danger' };
- [ComponentType.Accordion]: {};
- [ComponentType.AccordionGroup]: {};
- [ComponentType.ActionButton]: {};
- [ComponentType.AddressComponent]: { simplified: boolean };
- [ComponentType.AttachmentList]: {};
- [ComponentType.Button]: { onClickAction: () => void };
- [ComponentType.ButtonGroup]: {};
- [ComponentType.Checkboxes]: OptionsComponentBase;
- [ComponentType.Custom]: { tagName: string; framework: string; [id: string]: any };
- [ComponentType.Datepicker]: { timeStamp: boolean };
- [ComponentType.Dropdown]: { optionsId: string };
- [ComponentType.FileUpload]: FileUploadComponentBase;
- [ComponentType.FileUploadWithTag]: FileUploadComponentBase & { optionsId: string };
- [ComponentType.Grid]: {};
- [ComponentType.Group]: {
- maxCount?: number;
- edit?: {
- multiPage?: boolean;
- mode?: string;
+ [ComponentType.Accordion]: { headingLevel?: HeadingLevel };
+ [ComponentType.AccordionGroup]: SummarizableComponentProps;
+ [ComponentType.ActionButton]: {
+ buttonStyle: ButtonStyle;
+ action: ActionButtonAction;
+ };
+ [ComponentType.AddressComponent]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps & {
+ simplified?: boolean;
+ saveWhileTyping?: number;
};
+ [ComponentType.AttachmentList]: { dataTypeIds?: string[] };
+ [ComponentType.Button]: {
+ mode?: 'submit' | 'save' | 'go-to-task' | 'instantiate';
+ taskId?: string;
+ mapping?: KeyValuePairs;
+ };
+ [ComponentType.ButtonGroup]: SummarizableComponentProps & LabeledComponentProps;
+ [ComponentType.Checkboxes]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps &
+ SelectionComponentFull & {
+ layout?: LayoutStyle;
+ alertOnChange?: boolean;
+ };
+ [ComponentType.Custom]: FormComponentProps &
+ SummarizableComponentProps & {
+ tagName: string;
+ [id: string]: any;
+ };
+ [ComponentType.CustomButton]: {
+ actions: CustomAction[];
+ buttonStyle: ButtonStyle;
+ };
+ [ComponentType.Datepicker]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps & {
+ minDate?: string;
+ maxDate?: string;
+ timeStamp?: boolean;
+ format?: string;
+ };
+ [ComponentType.Dropdown]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps &
+ SelectionComponentFull;
+ [ComponentType.FileUpload]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps &
+ FileUploadComponentBase;
+ [ComponentType.FileUploadWithTag]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps &
+ FileUploadComponentBase & { optionsId: string };
+ [ComponentType.Grid]: SummarizableComponentProps & LabeledComponentProps & { rows: GridRow[] };
+ [ComponentType.Group]: SummarizableComponentProps & {
+ groupingIndicator?: 'indented' | 'panel';
+ maxCount?: number;
};
[ComponentType.Header]: { size: string };
- [ComponentType.IFrame]: {};
+ [ComponentType.IFrame]: {
+ sandbox?: {
+ allowPopups?: boolean;
+ allowPopupsToEscapeSandbox?: boolean;
+ };
+ };
[ComponentType.Image]: {
image?: {
src?: KeyValuePairs;
@@ -56,30 +167,153 @@ export type ComponentSpecificConfig = {
width?: string;
};
};
- [ComponentType.Input]: { disabled?: boolean };
- [ComponentType.InstanceInformation]: {};
- [ComponentType.InstantiationButton]: {};
- [ComponentType.Likert]: {};
- [ComponentType.Link]: {};
- [ComponentType.List]: {};
- [ComponentType.Map]: {
- centerLocation: {
- latitude: number;
- longitude: number;
+ [ComponentType.Input]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps & {
+ saveWhileTyping?: number;
+ formatting?: {
+ currency?: string;
+ unit?: string;
+ position?: 'prefix' | 'suffix';
+ number?:
+ | {
+ format: string;
+ mask?: string | string[];
+ allowEmptyFormatting?: boolean;
+ patternChar?: string;
+ }
+ | {
+ thousandSeparator?: string;
+ decimalSeparator?: string;
+ allowedDecimalSeparators?: string[];
+ thousandsGroupStyle?: 'thousand' | 'lakh' | 'wan' | 'none';
+ decimalScale?: number;
+ fixedDecimalScale?: boolean;
+ allowNegative?: boolean;
+ allowLeadingZeros?: boolean;
+ suffix?: string;
+ prefix?: string;
+ };
+ align?: 'right' | 'center' | 'left';
+ };
+ variant?: 'text' | 'search';
+ autocomplete?: HTMLAutoCompleteValue;
+ maxLength?: number;
+ };
+ [ComponentType.InstanceInformation]: LabeledComponentProps & {
+ elements?: {
+ dateSent?: boolean;
+ sender?: boolean;
+ receiver?: boolean;
+ referenceNumber?: boolean;
+ }[];
+ };
+ [ComponentType.InstantiationButton]: { mapping?: KeyValuePairs };
+ [ComponentType.Likert]: FormComponentProps &
+ SummarizableComponentProps &
+ SelectionComponent & {
+ filter?: { key: 'start' | 'stop'; value: string | number };
};
- zoom: number;
- layers?: MapLayer[];
+ [ComponentType.LikertItem]: FormComponentProps &
+ SummarizableComponentProps &
+ SelectionComponentFull & {
+ layout?: LayoutStyle;
+ };
+ [ComponentType.Link]: {
+ style: 'primary' | 'secondary' | 'link';
+ openInNewTab?: boolean;
+ };
+ [ComponentType.List]: FormComponentProps &
+ SummarizableComponentProps & {
+ tableHeaders: KeyValuePairs;
+ sortableColumns?: string[];
+ pagination?: {
+ alternatives: number[];
+ default: number;
+ };
+ dataListId: string;
+ secure?: boolean;
+ mapping?: KeyValuePairs;
+ bindingToShowInSummary?: string;
+ tableHeadersMobile?: string[];
+ };
+ [ComponentType.Map]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps & {
+ centerLocation?: {
+ latitude: number;
+ longitude: number;
+ };
+ zoom?: number;
+ layers?: MapLayer[];
+ };
+ [ComponentType.MultipleSelect]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps &
+ SelectionComponentFull;
+ [ComponentType.NavigationBar]: {
+ compact?: boolean;
+ validateOnForward?: PageValidation;
+ validateOnBackward?: PageValidation;
+ };
+ [ComponentType.NavigationButtons]: {
+ showBackButton?: boolean;
+ validateOnNext?: PageValidation;
+ validateOnPrevious?: PageValidation;
};
- [ComponentType.MultipleSelect]: {};
- [ComponentType.NavigationBar]: {};
- [ComponentType.NavigationButtons]: { showSaveButton?: boolean; showPrev?: boolean };
[ComponentType.Panel]: {
- variant: FormPanelVariant;
- showIcon: boolean;
+ variant?: FormPanelVariant;
+ showIcon?: boolean;
};
[ComponentType.Paragraph]: {};
[ComponentType.PrintButton]: {};
- [ComponentType.RadioButtons]: OptionsComponentBase;
- [ComponentType.Summary]: {};
- [ComponentType.TextArea]: {};
+ [ComponentType.RadioButtons]: FormComponentProps &
+ SummarizableComponentProps &
+ SelectionComponentFull &
+ LabeledComponentProps & {
+ layout?: LayoutStyle;
+ alertOnChange?: boolean;
+ showAsCard?: boolean;
+ };
+ [ComponentType.RepeatingGroup]: SummarizableComponentProps & {
+ validateOnSaveRow?: AllowedValidationMasks;
+ edit?: {
+ mode?: string;
+ addButton?: boolean;
+ saveButton?: boolean;
+ deleteButton?: boolean;
+ editButton?: boolean;
+ multiPage?: boolean;
+ openByDefault?: boolean | 'first' | 'last';
+ alertOnDelete?: boolean;
+ saveAndNextButton?: boolean;
+ alwaysShowAddButton?: boolean;
+ };
+ maxCount?: number;
+ minCount?: number;
+ tableHeaders?: string[];
+ tableColumns?: KeyValuePairs;
+ hiddenRow?: boolean;
+ rowsBefore?: GridRow[];
+ rowsAfter?: GridRow[];
+ labelSettings?: LabelSettings;
+ };
+ [ComponentType.Summary]: SummarizableComponentProps & {
+ componentRef: string;
+ largeGroup?: boolean;
+ excludedChildren?: string[];
+ display?: {
+ hideChangeButton?: boolean;
+ hideValidationMessages?: boolean;
+ useComponentGrid?: boolean;
+ hideBottomBorder?: boolean;
+ };
+ };
+ [ComponentType.TextArea]: FormComponentProps &
+ SummarizableComponentProps &
+ LabeledComponentProps & {
+ saveWhileTyping?: number;
+ autocomplete?: HTMLAutoCompleteValue;
+ maxLength?: number;
+ };
}[T];
diff --git a/frontend/packages/shared/src/types/ComponentSpecificConfigV3.ts b/frontend/packages/shared/src/types/ComponentSpecificConfigV3.ts
new file mode 100644
index 00000000000..5346c9ee70b
--- /dev/null
+++ b/frontend/packages/shared/src/types/ComponentSpecificConfigV3.ts
@@ -0,0 +1,85 @@
+import type { ComponentTypeV3 } from './ComponentTypeV3';
+import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
+import type { MapLayer } from 'app-shared/types/MapLayer';
+import type { FormPanelVariant } from 'app-shared/types/FormPanelVariant';
+
+type Option = {
+ label: string;
+ value: T;
+};
+
+type OptionsComponentBase = {
+ options?: Option[];
+ preselectedOptionIndex?: number;
+ optionsId?: string;
+};
+
+type FileUploadComponentBase = {
+ description: string;
+ hasCustomFileEndings: boolean;
+ maxFileSizeInMB: number;
+ displayMode: string;
+ maxNumberOfAttachments: number;
+ minNumberOfAttachments: number;
+ validFileEndings?: string;
+};
+
+export type ComponentSpecificConfigV3 = {
+ [ComponentTypeV3.Alert]: { severity: 'success' | 'info' | 'warning' | 'danger' };
+ [ComponentTypeV3.Accordion]: {};
+ [ComponentTypeV3.AccordionGroup]: {};
+ [ComponentTypeV3.ActionButton]: {};
+ [ComponentTypeV3.AddressComponent]: { simplified: boolean };
+ [ComponentTypeV3.AttachmentList]: {};
+ [ComponentTypeV3.Button]: { onClickAction: () => void };
+ [ComponentTypeV3.ButtonGroup]: {};
+ [ComponentTypeV3.Checkboxes]: OptionsComponentBase;
+ [ComponentTypeV3.Custom]: { tagName: string; framework: string; [id: string]: any };
+ [ComponentTypeV3.Datepicker]: { timeStamp: boolean };
+ [ComponentTypeV3.Dropdown]: { optionsId: string };
+ [ComponentTypeV3.FileUpload]: FileUploadComponentBase;
+ [ComponentTypeV3.FileUploadWithTag]: FileUploadComponentBase & { optionsId: string };
+ [ComponentTypeV3.Grid]: {};
+ [ComponentTypeV3.Group]: {
+ maxCount?: number;
+ edit?: {
+ multiPage?: boolean;
+ mode?: string;
+ };
+ };
+ [ComponentTypeV3.Header]: { size: string };
+ [ComponentTypeV3.IFrame]: {};
+ [ComponentTypeV3.Image]: {
+ image?: {
+ src?: KeyValuePairs;
+ align?: string | null;
+ width?: string;
+ };
+ };
+ [ComponentTypeV3.Input]: { disabled?: boolean };
+ [ComponentTypeV3.InstanceInformation]: {};
+ [ComponentTypeV3.InstantiationButton]: {};
+ [ComponentTypeV3.Likert]: {};
+ [ComponentTypeV3.Link]: {};
+ [ComponentTypeV3.List]: {};
+ [ComponentTypeV3.Map]: {
+ centerLocation: {
+ latitude: number;
+ longitude: number;
+ };
+ zoom: number;
+ layers?: MapLayer[];
+ };
+ [ComponentTypeV3.MultipleSelect]: {};
+ [ComponentTypeV3.NavigationBar]: {};
+ [ComponentTypeV3.NavigationButtons]: { showSaveButton?: boolean; showPrev?: boolean };
+ [ComponentTypeV3.Panel]: {
+ variant: FormPanelVariant;
+ showIcon: boolean;
+ };
+ [ComponentTypeV3.Paragraph]: {};
+ [ComponentTypeV3.PrintButton]: {};
+ [ComponentTypeV3.RadioButtons]: OptionsComponentBase;
+ [ComponentTypeV3.Summary]: {};
+ [ComponentTypeV3.TextArea]: {};
+}[T];
diff --git a/frontend/packages/shared/src/types/ComponentType.ts b/frontend/packages/shared/src/types/ComponentType.ts
index a1855d47935..7a2615df25c 100644
--- a/frontend/packages/shared/src/types/ComponentType.ts
+++ b/frontend/packages/shared/src/types/ComponentType.ts
@@ -1,14 +1,15 @@
export enum ComponentType {
- Alert = 'Alert',
Accordion = 'Accordion',
AccordionGroup = 'AccordionGroup',
ActionButton = 'ActionButton',
AddressComponent = 'AddressComponent',
+ Alert = 'Alert',
AttachmentList = 'AttachmentList',
Button = 'Button',
ButtonGroup = 'ButtonGroup',
Checkboxes = 'Checkboxes',
Custom = 'Custom',
+ CustomButton = 'CustomButton',
Datepicker = 'Datepicker',
Dropdown = 'Dropdown',
FileUpload = 'FileUpload',
@@ -22,6 +23,7 @@ export enum ComponentType {
InstanceInformation = 'InstanceInformation',
InstantiationButton = 'InstantiationButton',
Likert = 'Likert',
+ LikertItem = 'LikertItem',
Link = 'Link',
List = 'List',
Map = 'Map',
@@ -32,6 +34,7 @@ export enum ComponentType {
Paragraph = 'Paragraph',
PrintButton = 'PrintButton',
RadioButtons = 'RadioButtons',
+ RepeatingGroup = 'RepeatingGroup',
Summary = 'Summary',
TextArea = 'TextArea',
}
diff --git a/frontend/packages/shared/src/types/ComponentTypeV3.ts b/frontend/packages/shared/src/types/ComponentTypeV3.ts
new file mode 100644
index 00000000000..ba9db55690a
--- /dev/null
+++ b/frontend/packages/shared/src/types/ComponentTypeV3.ts
@@ -0,0 +1,37 @@
+export enum ComponentTypeV3 {
+ Alert = 'Alert',
+ Accordion = 'Accordion',
+ AccordionGroup = 'AccordionGroup',
+ ActionButton = 'ActionButton',
+ AddressComponent = 'AddressComponent',
+ AttachmentList = 'AttachmentList',
+ Button = 'Button',
+ ButtonGroup = 'ButtonGroup',
+ Checkboxes = 'Checkboxes',
+ Custom = 'Custom',
+ Datepicker = 'Datepicker',
+ Dropdown = 'Dropdown',
+ FileUpload = 'FileUpload',
+ FileUploadWithTag = 'FileUploadWithTag',
+ Grid = 'Grid',
+ Group = 'Group',
+ Header = 'Header',
+ IFrame = 'IFrame',
+ Image = 'Image',
+ Input = 'Input',
+ InstanceInformation = 'InstanceInformation',
+ InstantiationButton = 'InstantiationButton',
+ Likert = 'Likert',
+ Link = 'Link',
+ List = 'List',
+ Map = 'Map',
+ MultipleSelect = 'MultipleSelect',
+ NavigationBar = 'NavigationBar',
+ NavigationButtons = 'NavigationButtons',
+ Panel = 'Panel',
+ Paragraph = 'Paragraph',
+ PrintButton = 'PrintButton',
+ RadioButtons = 'RadioButtons',
+ Summary = 'Summary',
+ TextArea = 'TextArea',
+}
diff --git a/frontend/packages/shared/src/types/FilterKeysOfType.ts b/frontend/packages/shared/src/types/FilterKeysOfType.ts
new file mode 100644
index 00000000000..adaa30b3e04
--- /dev/null
+++ b/frontend/packages/shared/src/types/FilterKeysOfType.ts
@@ -0,0 +1,5 @@
+import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
+
+export type FilterKeysOfType = {
+ [K in keyof ObjectType]: ObjectType[K] extends ValueType ? K : never;
+}[keyof ObjectType];
diff --git a/frontend/packages/shared/src/types/GridRow.ts b/frontend/packages/shared/src/types/GridRow.ts
new file mode 100644
index 00000000000..fee88d4a26b
--- /dev/null
+++ b/frontend/packages/shared/src/types/GridRow.ts
@@ -0,0 +1,33 @@
+export type GridRow = {
+ header?: boolean;
+ readonly?: boolean;
+ columnOptions?: ColumnOptions;
+ cells: GridCell[];
+};
+
+type ColumnOptions = {
+ width?: string;
+ alignText?: 'left' | 'center' | 'right';
+ textOverflow?: {
+ lineWrap?: boolean;
+ maxHeight?: number;
+ };
+};
+
+type GridCell = GridComponentRef | GridCellText | GridCellLabelFrom | null;
+
+type GridComponentRef = {
+ component?: string;
+ columnOptions?: ColumnOptions;
+};
+
+type GridCellText = ColumnOptions & {
+ text: string;
+ help?: string;
+ columnOptions?: ColumnOptions;
+};
+
+type GridCellLabelFrom = {
+ labelFrom: string;
+ columnOptions?: ColumnOptions;
+};
diff --git a/frontend/packages/shared/src/types/HTMLAutoCompleteValue.ts b/frontend/packages/shared/src/types/HTMLAutoCompleteValue.ts
new file mode 100644
index 00000000000..e900a89f4f4
--- /dev/null
+++ b/frontend/packages/shared/src/types/HTMLAutoCompleteValue.ts
@@ -0,0 +1,46 @@
+export enum HTMLAutoCompleteValue {
+ On = 'on',
+ Off = 'off',
+ Name = 'name',
+ HonorificPrefix = 'honorific-prefix',
+ GivenName = 'given-name',
+ AdditionalName = 'additional-name',
+ FamilyName = 'family-name',
+ HonorificSuffix = 'honorific-suffix',
+ Nickname = 'nickname',
+ Email = 'email',
+ Username = 'username',
+ NewPassword = 'new-password',
+ CurrentPassword = 'current-password',
+ OneTimeCode = 'one-time-code',
+ OrganizationTitle = 'organization-title',
+ Organization = 'organization',
+ StreetAddress = 'street-address',
+ AddressLine1 = 'address-line1',
+ AddressLine2 = 'address-line2',
+ AddressLine3 = 'address-line3',
+ AddressLevel4 = 'address-level4',
+ AddressLevel3 = 'address-level3',
+ AddressLevel2 = 'address-level2',
+ AddressLevel1 = 'address-level1',
+ Country = 'country',
+ CountryName = 'country-name',
+ PostalCode = 'postal-code',
+ CCName = 'cc-name',
+ CCGivenName = 'cc-given-name',
+ CCAdditionalName = 'cc-additional-name',
+ CCFamilyName = 'cc-family-name',
+ CCNumber = 'cc-number',
+ CCExp = 'cc-exp',
+ CCExpMonth = 'cc-exp-month',
+ CCExpYear = 'cc-exp-year',
+ CCCSC = 'cc-csc',
+ CCType = 'cc-type',
+ TransactionCurrency = 'transaction-currency',
+ TransactionAmount = 'transaction-amount',
+ Language = 'language',
+ BDay = 'bday',
+ BDayDay = 'bday-day',
+ BDayMonth = 'bday-month',
+ BDayYear = 'bday-year',
+}
diff --git a/frontend/packages/shared/src/types/HeadingLevel.ts b/frontend/packages/shared/src/types/HeadingLevel.ts
new file mode 100644
index 00000000000..796f8f7f1ef
--- /dev/null
+++ b/frontend/packages/shared/src/types/HeadingLevel.ts
@@ -0,0 +1 @@
+export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
diff --git a/frontend/packages/shared/src/types/api/FormLayoutsResponseV3.ts b/frontend/packages/shared/src/types/api/FormLayoutsResponseV3.ts
new file mode 100644
index 00000000000..96c4b7e372a
--- /dev/null
+++ b/frontend/packages/shared/src/types/api/FormLayoutsResponseV3.ts
@@ -0,0 +1,30 @@
+import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
+import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
+import type { ComponentSpecificConfigV3 } from 'app-shared/types/ComponentSpecificConfigV3';
+
+export type FormLayoutsResponseV3 = KeyValuePairs;
+
+export interface ExternalFormLayoutV3 {
+ $schema: string;
+ data: ExternalDataV3;
+ [key: string]: any;
+}
+
+export interface ExternalDataV3 {
+ layout: ExternalComponentV3[];
+ hidden?: boolean;
+ [key: string]: any;
+}
+
+type ExternalComponentBase = {
+ id: string;
+ type: T;
+ dataModelBindings?: KeyValuePairs;
+ textResourceBindings?: KeyValuePairs;
+ [key: string]: any;
+};
+
+export type ExternalComponentV3 = {
+ [componentType in ComponentTypeV3]: ExternalComponentBase &
+ ComponentSpecificConfigV3;
+}[T];
diff --git a/frontend/packages/shared/src/types/api/index.ts b/frontend/packages/shared/src/types/api/index.ts
index 1f4623948b8..170c07d386c 100644
--- a/frontend/packages/shared/src/types/api/index.ts
+++ b/frontend/packages/shared/src/types/api/index.ts
@@ -8,6 +8,7 @@ export * from './CreateReleasePayload';
export * from './CreateRepoCommitPayload';
export * from './DatamodelMetadataResponse';
export * from './FormLayoutsResponse';
+export * from './FormLayoutsResponseV3';
export * from './SearchRepoFilterParams';
export * from './SearchRepositoryResponse';
export * from './UpdateTextIdPayload';
diff --git a/frontend/packages/ux-editor-v3/src/components/Elements/ConfPageToolbar.tsx b/frontend/packages/ux-editor-v3/src/components/Elements/ConfPageToolbar.tsx
index b161961e62d..58017b8efe1 100644
--- a/frontend/packages/ux-editor-v3/src/components/Elements/ConfPageToolbar.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/Elements/ConfPageToolbar.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import type { ComponentType } from 'app-shared/types/ComponentType';
+import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import type { IToolbarElement } from '../../types/global';
import { InformationPanelComponent } from '../toolbar/InformationPanelComponent';
import { ToolbarItem } from './ToolbarItem';
@@ -10,10 +10,10 @@ import { useTranslation } from 'react-i18next';
export const ConfPageToolbar = () => {
const [anchorElement, setAnchorElement] = useState(null);
- const [compSelForInfoPanel, setCompSelForInfoPanel] = useState(null);
+ const [compSelForInfoPanel, setCompSelForInfoPanel] = useState(null);
const { t } = useTranslation();
const componentList: IToolbarElement[] = confOnScreenComponents.map(mapComponentToToolbarElement);
- const handleComponentInformationOpen = (component: ComponentType, event: any) => {
+ const handleComponentInformationOpen = (component: ComponentTypeV3, event: any) => {
setCompSelForInfoPanel(component);
setAnchorElement(event.currentTarget);
};
diff --git a/frontend/packages/ux-editor-v3/src/components/Elements/DefaultToolbar.tsx b/frontend/packages/ux-editor-v3/src/components/Elements/DefaultToolbar.tsx
index d0ca40d4079..20be2826d3f 100644
--- a/frontend/packages/ux-editor-v3/src/components/Elements/DefaultToolbar.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/Elements/DefaultToolbar.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import type { ComponentType } from 'app-shared/types/ComponentType';
+import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import type { IToolbarElement } from '../../types/global';
import { CollapsableMenus } from '../../types/global';
import { InformationPanelComponent } from '../toolbar/InformationPanelComponent';
@@ -16,7 +16,7 @@ import { getComponentTitleByComponentType } from '../../utils/language';
export function DefaultToolbar() {
const [compInfoPanelOpen, setCompInfoPanelOpen] = useState(false);
- const [compSelForInfoPanel, setCompSelForInfoPanel] = useState(null);
+ const [compSelForInfoPanel, setCompSelForInfoPanel] = useState(null);
const [anchorElement, setAnchorElement] = useState(null);
const { t } = useTranslation();
@@ -41,7 +41,7 @@ export function DefaultToolbar() {
// [CollapsableMenus.ThirdParty]: thirdPartyComponentList,
};
- const handleComponentInformationOpen = (component: ComponentType, event: any) => {
+ const handleComponentInformationOpen = (component: ComponentTypeV3, event: any) => {
setCompInfoPanelOpen(true);
setCompSelForInfoPanel(component);
setAnchorElement(event.currentTarget);
diff --git a/frontend/packages/ux-editor-v3/src/components/Elements/ToolbarItem.tsx b/frontend/packages/ux-editor-v3/src/components/Elements/ToolbarItem.tsx
index 083fa43d458..a8cf474ec2d 100644
--- a/frontend/packages/ux-editor-v3/src/components/Elements/ToolbarItem.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/Elements/ToolbarItem.tsx
@@ -1,14 +1,14 @@
import type { MouseEvent } from 'react';
import React from 'react';
import { ToolbarItemComponent } from '../toolbar/ToolbarItemComponent';
-import type { ComponentType } from 'app-shared/types/ComponentType';
+import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import { DragAndDropTree } from 'app-shared/components/DragAndDropTree';
interface IToolbarItemProps {
text: string;
notDraggable?: boolean;
- onClick: (type: ComponentType, event: MouseEvent) => void;
- componentType: ComponentType;
+ onClick: (type: ComponentTypeV3, event: MouseEvent) => void;
+ componentType: ComponentTypeV3;
icon?: React.ComponentType;
}
@@ -21,7 +21,7 @@ export const ToolbarItem = ({
}: IToolbarItemProps) => {
return (
-
notDraggable={notDraggable} payload={componentType}>
+ notDraggable={notDraggable} payload={componentType}>
{
test('should return input specific content when type input', async () => {
await render({
componentProps: {
- type: ComponentType.Input,
+ type: ComponentTypeV3.Input,
},
});
@@ -108,7 +108,7 @@ describe('EditFormComponent', () => {
test('should return header specific content when type header', async () => {
await render({
componentProps: {
- type: ComponentType.Header,
+ type: ComponentTypeV3.Header,
},
});
@@ -121,7 +121,7 @@ describe('EditFormComponent', () => {
test('should return file uploader specific content when type file uploader', async () => {
await render({
componentProps: {
- type: ComponentType.FileUpload,
+ type: ComponentTypeV3.FileUpload,
},
});
@@ -144,7 +144,7 @@ describe('EditFormComponent', () => {
const { allComponentProps } = await render({
componentProps: {
maxNumberOfAttachments: 3,
- type: ComponentType.FileUpload,
+ type: ComponentTypeV3.FileUpload,
},
handleComponentUpdate: handleUpdate,
});
@@ -164,7 +164,7 @@ describe('EditFormComponent', () => {
componentProps: {
required: true,
minNumberOfAttachments: 1,
- type: ComponentType.FileUpload,
+ type: ComponentTypeV3.FileUpload,
},
handleComponentUpdate: handleUpdate,
});
@@ -182,7 +182,7 @@ describe('EditFormComponent', () => {
test('should return button specific content when type button', async () => {
await render({
componentProps: {
- type: ComponentType.Button,
+ type: ComponentTypeV3.Button,
},
});
expect(await screen.findByTestId(buttonSpecificContentId)).toBeInTheDocument();
@@ -191,7 +191,7 @@ describe('EditFormComponent', () => {
test('should render Image component when component type is Image', async () => {
await render({
componentProps: {
- type: ComponentType.Image,
+ type: ComponentTypeV3.Image,
},
});
expect(await screen.findByTestId(imageSpecificContentId)).toBeInTheDocument();
@@ -200,7 +200,7 @@ describe('EditFormComponent', () => {
it('should not render Image component when component type is not Image', async () => {
await render({
componentProps: {
- type: ComponentType.Button,
+ type: ComponentTypeV3.Button,
},
});
expect(screen.queryByLabelText(srcValueLabel)).not.toBeInTheDocument();
@@ -209,7 +209,7 @@ describe('EditFormComponent', () => {
it('should notify users when the component is unrecognized and cannot be configured in Studio', async () => {
await render({
componentProps: {
- // Cast the type to avoid TypeScript error due to components that does not exists within ComponentType.
+ // Cast the type to avoid TypeScript error due to components that does not exists within ComponentTypeV3.
type: 'UnknownComponent' as unknown as any,
},
});
@@ -247,7 +247,7 @@ const render = async ({
textResourceBindings: {
title: 'title',
},
- type: ComponentType.Input,
+ type: ComponentTypeV3.Input,
id: 'test',
itemType: 'COMPONENT',
...componentProps,
diff --git a/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.test.tsx
index 4bfebc16946..1234d674c30 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.test.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.test.tsx
@@ -13,10 +13,10 @@ import {
import { useLayoutSchemaQuery } from '../../hooks/queries/useLayoutSchemaQuery';
import { container1IdMock, externalLayoutsMock, layoutMock } from '../../testing/layoutMock';
import { textMock } from '../../../../../testing/mocks/i18nMock';
-import type { FormLayoutsResponse } from 'app-shared/types/api';
+import type { FormLayoutsResponseV3 } from 'app-shared/types/api';
import type { ILayoutSettings } from 'app-shared/types/global';
import type { FormContainer } from '../../types/FormContainer';
-import { ComponentType } from 'app-shared/types/ComponentType';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
const user = userEvent.setup();
@@ -27,21 +27,21 @@ const selectedLayoutSet = 'test-layout-set';
const accordionContainer: FormContainer = {
id: 'accordionContainerId',
itemType: 'CONTAINER',
- type: ComponentType.Accordion,
+ type: ComponentTypeV3.Accordion,
pageIndex: 1,
propertyPath: 'definitions/accordionComponent',
};
const accordionGroupContainer: FormContainer = {
id: 'accordionGroupContainerId',
itemType: 'CONTAINER',
- type: ComponentType.AccordionGroup,
+ type: ComponentTypeV3.AccordionGroup,
pageIndex: 1,
propertyPath: 'definitions/accordionGroupComponent',
};
const buttonGroupContainer: FormContainer = {
id: 'buttonGroupContainerId',
itemType: 'CONTAINER',
- type: ComponentType.ButtonGroup,
+ type: ComponentTypeV3.ButtonGroup,
pageIndex: 1,
propertyPath: 'definitions/buttonGroupComponent',
};
@@ -120,15 +120,15 @@ describe('EditFormContainer', () => {
});
const waitForData = async () => {
- const getFormLayouts = jest
+ const getFormLayoutsV3 = jest
.fn()
- .mockImplementation(() => Promise.resolve(externalLayoutsMock));
+ .mockImplementation(() => Promise.resolve(externalLayoutsMock));
const getFormLayoutSettings = jest
.fn()
.mockImplementation(() => Promise.resolve(formLayoutSettingsMock));
const formLayoutsResult = renderHookWithMockStore(
{},
- { getFormLayouts },
+ { getFormLayoutsV3 },
)(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result;
const settingsResult = renderHookWithMockStore(
{},
diff --git a/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.tsx b/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.tsx
index 2b87df6ecdb..5260ee22fc6 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/EditFormContainer.tsx
@@ -27,7 +27,7 @@ import { FormField } from '../FormField';
import type { FormContainer } from '../../types/FormContainer';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
import { useAppContext } from '../../hooks/useAppContext';
-import { ComponentType } from 'app-shared/types/ComponentType';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
export interface IEditFormContainerProps {
editFormId: string;
@@ -138,7 +138,7 @@ export const EditFormContainer = ({
});
};
- return container.type === ComponentType.Group ? (
+ return container.type === ComponentTypeV3.Group ? (
{
const componentWithExpression: FormComponent = {
id: 'some-id',
- type: ComponentType.Input,
+ type: ComponentTypeV3.Input,
itemType: 'COMPONENT',
hidden: parsableExternalExpression,
};
diff --git a/frontend/packages/ux-editor-v3/src/components/config/Expressions/Expressions.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/Expressions/Expressions.test.tsx
index 4b8cfd58a3b..83af242230b 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/Expressions/Expressions.test.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/Expressions/Expressions.test.tsx
@@ -13,7 +13,7 @@ import { QueryKey } from 'app-shared/types/QueryKey';
import { Expressions } from './Expressions';
import { FormContext } from '../../../containers/FormContext';
import type { FormComponent } from '../../../types/FormComponent';
-import { ComponentType } from 'app-shared/types/ComponentType';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import { parsableExternalExpression } from '../../../testing/expressionMocks';
import type { FormContainer } from '../../../types/FormContainer';
@@ -25,7 +25,7 @@ const layouts: IFormLayouts = {
};
const componentWithExpression: FormComponent = {
id: 'some-id',
- type: ComponentType.Input,
+ type: ComponentTypeV3.Input,
itemType: 'COMPONENT',
hidden: parsableExternalExpression,
};
@@ -69,7 +69,7 @@ describe('Expressions', () => {
it('renders alert component when there are as many existing expressions as available properties to set expressions on for a regular component', () => {
const componentWithMultipleExpressions: FormComponent = {
id: 'some-id',
- type: ComponentType.Input,
+ type: ComponentTypeV3.Input,
itemType: 'COMPONENT',
hidden: parsableExternalExpression,
required: parsableExternalExpression,
@@ -89,7 +89,7 @@ describe('Expressions', () => {
const groupComponentWithAllBooleanFieldsAsExpressions: FormContainer = {
id: 'some-id',
itemType: 'CONTAINER',
- type: ComponentType.Group,
+ type: ComponentTypeV3.Group,
hidden: parsableExternalExpression,
required: parsableExternalExpression,
readOnly: parsableExternalExpression,
@@ -163,7 +163,7 @@ describe('Expressions', () => {
const component: FormContainer = {
id: 'some-id',
itemType: 'CONTAINER',
- type: ComponentType.Group,
+ type: ComponentTypeV3.Group,
edit: {
multiPage: true,
},
@@ -177,7 +177,7 @@ describe('Expressions', () => {
it('renders no existing expressions when component fields are boolean', () => {
const componentWithoutExpressions: FormComponent = {
id: 'some-id',
- type: ComponentType.Input,
+ type: ComponentTypeV3.Input,
itemType: 'COMPONENT',
hidden: true,
required: false,
diff --git a/frontend/packages/ux-editor-v3/src/components/config/componentConfig.tsx b/frontend/packages/ux-editor-v3/src/components/config/componentConfig.tsx
index b3c62acfa6a..d719bc4a3c8 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/componentConfig.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/componentConfig.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { ComponentType } from 'app-shared/types/ComponentType';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import { EditCodeList } from './editModal/EditCodeList';
import { EditDataModelBindings } from './editModal/EditDataModelBindings';
import { EditHeaderSize } from './editModal/EditHeaderSize';
@@ -51,39 +51,39 @@ interface IConfigComponents {
}
export const componentSpecificEditConfig: IComponentEditConfig = {
- [ComponentType.Header]: [EditSettings.Title, EditSettings.Size],
- [ComponentType.Input]: [...editBoilerPlate, EditSettings.AutoComplete],
- [ComponentType.TextArea]: [...editBoilerPlate, EditSettings.AutoComplete],
- [ComponentType.Datepicker]: [...editBoilerPlate],
- [ComponentType.Paragraph]: [EditSettings.Title],
- [ComponentType.AttachmentList]: [EditSettings.Title],
- [ComponentType.Checkboxes]: [
+ [ComponentTypeV3.Header]: [EditSettings.Title, EditSettings.Size],
+ [ComponentTypeV3.Input]: [...editBoilerPlate, EditSettings.AutoComplete],
+ [ComponentTypeV3.TextArea]: [...editBoilerPlate, EditSettings.AutoComplete],
+ [ComponentTypeV3.Datepicker]: [...editBoilerPlate],
+ [ComponentTypeV3.Paragraph]: [EditSettings.Title],
+ [ComponentTypeV3.AttachmentList]: [EditSettings.Title],
+ [ComponentTypeV3.Checkboxes]: [
...editBoilerPlate,
EditSettings.Options,
EditSettings.PreselectedIndex,
],
- [ComponentType.RadioButtons]: [
+ [ComponentTypeV3.RadioButtons]: [
...editBoilerPlate,
EditSettings.Options,
EditSettings.PreselectedIndex,
],
- [ComponentType.Dropdown]: [
+ [ComponentTypeV3.Dropdown]: [
...editBoilerPlate,
EditSettings.CodeList,
EditSettings.PreselectedIndex,
EditSettings.AutoComplete,
],
- [ComponentType.AddressComponent]: [EditSettings.Title, EditSettings.Help],
- [ComponentType.FileUpload]: [EditSettings.Title, EditSettings.Description, EditSettings.Help],
- [ComponentType.FileUploadWithTag]: [
+ [ComponentTypeV3.AddressComponent]: [EditSettings.Title, EditSettings.Help],
+ [ComponentTypeV3.FileUpload]: [EditSettings.Title, EditSettings.Description, EditSettings.Help],
+ [ComponentTypeV3.FileUploadWithTag]: [
EditSettings.Title,
EditSettings.Description,
EditSettings.Help,
EditSettings.TagTitle,
EditSettings.CodeList,
],
- [ComponentType.Panel]: [EditSettings.Title],
- [ComponentType.Map]: [...editBoilerPlate],
+ [ComponentTypeV3.Panel]: [EditSettings.Title],
+ [ComponentTypeV3.Map]: [...editBoilerPlate],
};
export const configComponents: IConfigComponents = {
diff --git a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Address/AddressComponent.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Address/AddressComponent.test.tsx
index 81ab88deac8..97d63d45bd1 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Address/AddressComponent.test.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Address/AddressComponent.test.tsx
@@ -3,14 +3,14 @@ import type { IGenericEditComponent } from '../../componentConfig';
import { renderWithMockStore, renderHookWithMockStore } from '../../../../testing/mocks';
import { useLayoutSchemaQuery } from '../../../../hooks/queries/useLayoutSchemaQuery';
import { AddressComponent } from './AddressComponent';
-import { ComponentType } from 'app-shared/types/ComponentType';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import type { FormAddressComponent } from '../../../../types/FormComponent';
import { waitFor, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// Test data:
const component: FormAddressComponent = {
- type: ComponentType.AddressComponent,
+ type: ComponentTypeV3.AddressComponent,
dataModelBindings: {
test: 'test',
},
diff --git a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.test.tsx
index b30eb756493..191363cc938 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.test.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.test.tsx
@@ -3,7 +3,7 @@ import { screen, waitFor } from '@testing-library/react';
import { renderWithMockStore, renderHookWithMockStore } from '../../../../testing/mocks';
import { useLayoutSchemaQuery } from '../../../../hooks/queries/useLayoutSchemaQuery';
import { ButtonComponent } from './ButtonComponent';
-import { ComponentType } from 'app-shared/types/ComponentType';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
import type { FormButtonComponent } from '../../../../types/FormComponent';
import type { IGenericEditComponent } from '../../componentConfig';
import { textMock } from '../../../../../../../testing/mocks/i18nMock';
@@ -12,7 +12,7 @@ import { textMock } from '../../../../../../../testing/mocks/i18nMock';
const component: FormButtonComponent = {
id: '1',
onClickAction: jest.fn(),
- type: ComponentType.Button,
+ type: ComponentTypeV3.Button,
itemType: 'COMPONENT',
dataModelBindings: {},
};
@@ -34,7 +34,7 @@ describe('ButtonComponent', () => {
await render({
component: {
...component,
- type: ComponentType.NavigationButtons,
+ type: ComponentTypeV3.NavigationButtons,
},
});
expect(
diff --git a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.tsx b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.tsx
index 920ff8cd359..fc8f3e96f4c 100644
--- a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.tsx
+++ b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/Button/ButtonComponent.tsx
@@ -3,16 +3,16 @@ import { Fieldset } from '@digdir/design-system-react';
import classes from './ButtonComponent.module.css';
import type { IGenericEditComponent } from '../../componentConfig';
import { EditSettings } from '../../componentConfig';
-import { ComponentType } from 'app-shared/types/ComponentType';
import { EditTextResourceBinding } from '../../editModal/EditTextResourceBinding';
import { EditTextResourceBindings } from '../../editModal/EditTextResourceBindings';
import { useTranslation } from 'react-i18next';
+import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3';
export const ButtonComponent = ({ component, handleComponentChange }: IGenericEditComponent) => {
const { t } = useTranslation();
return (