From f72dadf8b46c51d73e8cb866d69f56220c8613de Mon Sep 17 00:00:00 2001 From: Tomas Engebretsen Date: Fri, 9 Feb 2024 09:17:51 +0100 Subject: [PATCH 1/2] Separate component types for versions 3 and 4 (#12276) --- frontend/language/src/nb.json | 11 +- frontend/packages/shared/src/api/mutations.ts | 2 + frontend/packages/shared/src/api/queries.ts | 2 + .../packages/shared/src/mocks/queriesMock.ts | 3 + .../shared/src/types/ActionButtonAction.ts | 1 + .../src/types/ComponentSpecificConfig.ts | 320 +++++++++++++++--- .../src/types/ComponentSpecificConfigV3.ts | 85 +++++ .../shared/src/types/ComponentType.ts | 5 +- .../shared/src/types/ComponentTypeV3.ts | 37 ++ .../shared/src/types/FilterKeysOfType.ts | 5 + frontend/packages/shared/src/types/GridRow.ts | 33 ++ .../shared/src/types/HTMLAutoCompleteValue.ts | 46 +++ .../packages/shared/src/types/HeadingLevel.ts | 1 + .../src/types/api/FormLayoutsResponseV3.ts | 30 ++ .../packages/shared/src/types/api/index.ts | 1 + .../components/Elements/ConfPageToolbar.tsx | 6 +- .../components/Elements/DefaultToolbar.tsx | 6 +- .../src/components/Elements/ToolbarItem.tsx | 8 +- .../config/EditFormComponent.test.tsx | 22 +- .../config/EditFormContainer.test.tsx | 16 +- .../components/config/EditFormContainer.tsx | 4 +- .../ExpressionContent.test.tsx | 4 +- .../config/Expressions/Expressions.test.tsx | 12 +- .../src/components/config/componentConfig.tsx | 30 +- .../Address/AddressComponent.test.tsx | 4 +- .../Button/ButtonComponent.test.tsx | 6 +- .../Button/ButtonComponent.tsx | 6 +- .../ComponentSpecificContent.tsx | 18 +- .../FileUpload/FileUploadComponent.test.tsx | 4 +- .../FileUpload/FileUploadComponent.tsx | 4 +- .../Image/ImageComponent.test.tsx | 4 +- .../Panel/PanelComponent.test.tsx | 6 +- .../editModal/EditAutoComplete.test.tsx | 4 +- .../config/editModal/EditCodeList.test.tsx | 4 +- .../editModal/EditDataModelBindings.test.tsx | 6 +- .../editModal/EditDataModelBindings.tsx | 4 +- .../config/editModal/EditHeaderSize.test.tsx | 6 +- .../config/editModal/EditNumberValue.test.tsx | 4 +- .../config/editModal/EditOptions.test.tsx | 4 +- .../config/editModal/EditPreselectedIndex.tsx | 12 +- .../config/editModal/EditStringValue.test.tsx | 6 +- .../EditTextResourceBinding.test.tsx | 4 +- .../EditTextResourceBindings.test.tsx | 4 +- .../toolbar/InformationPanelComponent.tsx | 4 +- .../toolbar/ToolbarItemComponent.tsx | 6 +- .../containers/DesignView/DesignView.test.tsx | 2 +- .../FormItemTitle/FormItemTitle.test.tsx | 4 +- .../FormItemTitle/useDeleteItem.test.ts | 4 +- .../FormTree/FormItem/useItemTitle.test.ts | 8 +- .../DesignView/FormTree/FormTree.test.tsx | 14 +- .../UnknownReferencedItem.test.tsx | 2 +- .../ReceiptContent/ReceiptContent.test.tsx | 4 +- .../src/containers/FormContext.test.tsx | 56 ++- .../src/containers/FormDesigner.tsx | 4 +- .../externalGroupComponentToInternal.test.ts | 6 +- .../internalGroupComponentToExternal.test.ts | 6 +- .../externalLayoutToInternal.test.ts | 6 +- .../externalLayoutToInternal.ts | 49 +-- .../formLayoutConverters.test.ts | 4 +- .../internalLayoutToExternal.test.ts | 4 +- .../internalLayoutToExternal.ts | 14 +- .../externalSimpleComponentToInternal.test.ts | 4 +- .../internalSimpleComponentToExternal.test.ts | 4 +- .../src/data/containerComponentTypes.ts | 4 +- .../src/data/formItemConfig.test.ts | 4 +- .../ux-editor-v3/src/data/formItemConfig.ts | 310 ++++++++--------- .../useAddFormContainerMutation.test.ts | 12 +- .../useAddItemToLayoutMutation.test.ts | 12 +- .../mutations/useAddItemToLayoutMutation.ts | 8 +- .../mutations/useAddLayoutMutation.test.ts | 20 +- .../hooks/mutations/useAddLayoutMutation.ts | 8 +- .../mutations/useAddWidgetMutation.test.ts | 6 +- .../useDeleteFormComponentMutation.test.ts | 4 +- .../useDeleteFormComponentMutation.ts | 6 +- .../useDeleteFormContainerMutation.test.ts | 12 +- .../mutations/useDeleteLayoutMutation.ts | 8 +- .../mutations/useFormLayoutMutation.test.tsx | 14 +- .../hooks/mutations/useFormLayoutMutation.ts | 8 +- .../useFormLayoutSettingsMutation.test.ts | 2 +- .../useUpdateFormComponentMutation.test.ts | 16 +- .../useUpdateFormComponentMutation.ts | 10 +- ...seUpdateFormComponentOrderMutation.test.ts | 12 +- .../useUpdateFormContainerMutation.test.ts | 16 +- .../src/hooks/queries/useFormLayoutsQuery.ts | 4 +- .../src/hooks/queries/useWidgetsQuery.test.ts | 6 +- .../hooks/useComponentErrorMessage.test.ts | 6 +- .../src/hooks/useComponentErrorMessage.ts | 6 +- .../src/hooks/useComponentTypeName.test.ts | 14 +- .../src/hooks/useComponentTypeName.ts | 6 +- .../src/hooks/useFormLayoutsSelector.test.ts | 4 +- .../src/hooks/useValidateComponent.test.ts | 20 +- .../src/hooks/useValidateComponent.ts | 6 +- .../src/selectors/formLayoutSelectors.test.ts | 20 +- .../src/testing/componentMocks.ts | 116 +++---- .../ux-editor-v3/src/testing/layoutMock.ts | 22 +- .../testing/layoutWithMultiPageGroupMocks.ts | 60 ++-- .../src/types/ContainerComponent.ts | 10 +- .../src/types/ExternalContainerComponent.ts | 4 +- .../src/types/ExternalSimpleComponent.ts | 4 +- .../ux-editor-v3/src/types/FormComponent.ts | 26 +- .../ux-editor-v3/src/types/FormItem.ts | 4 +- .../src/types/SimpleComponentType.ts | 4 +- .../packages/ux-editor-v3/src/types/global.ts | 6 +- .../ux-editor-v3/src/utils/component.test.ts | 24 +- .../ux-editor-v3/src/utils/component.ts | 4 +- .../src/utils/expressionsUtils.test.ts | 4 +- .../src/utils/expressionsUtils.ts | 4 +- .../src/utils/formLayoutUtils.test.tsx | 62 ++-- .../ux-editor-v3/src/utils/formLayoutUtils.ts | 14 +- .../src/utils/formLayoutsUtils.test.ts | 18 +- .../src/utils/formLayoutsUtils.ts | 10 +- .../src/utils/generateId.test.tsx | 14 +- .../ux-editor-v3/src/utils/generateId.ts | 4 +- .../ux-editor-v3/src/utils/language.test.ts | 16 +- .../ux-editor-v3/src/utils/language.ts | 16 +- .../components/Elements/DefaultToolbar.tsx | 10 - .../config/EditFormComponent.test.tsx | 11 +- .../components/config/EditFormComponent.tsx | 5 +- .../components/config/FormComponentConfig.tsx | 7 +- .../src/components/config/componentConfig.tsx | 8 +- .../Panel/PanelComponent.tsx | 6 +- .../editModal/EditAutoComplete.test.tsx | 132 +++----- .../config/editModal/EditAutoComplete.tsx | 179 +++------- .../config/editModal/EditBooleanValue.tsx | 5 +- .../editModal/EditGrid/EditGrid.test.tsx | 52 ++- .../config/editModal/EditGrid/EditGrid.tsx | 15 +- .../config/editModal/EditNumberValue.tsx | 27 +- .../config/editModal/EditOptions.test.tsx | 35 +- .../config/editModal/EditOptions.tsx | 15 +- .../config/editModal/EditStringValue.test.tsx | 35 +- .../config/editModal/EditStringValue.tsx | 5 +- .../externalContainerComponentToInternal.ts | 2 +- .../externalSimpleComponentToInternal.test.ts | 2 +- .../externalSimpleComponentToInternal.ts | 4 +- .../internalSimpleComponentToExternal.test.ts | 2 +- .../ux-editor/src/data/formItemConfig.test.ts | 31 +- .../ux-editor/src/data/formItemConfig.ts | 282 +++++---------- .../hooks/useComponentPropertyLabel.test.ts | 11 + .../src/hooks/useComponentPropertyLabel.ts | 13 + .../ux-editor/src/testing/componentMocks.ts | 4 +- .../ux-editor/src/testing/layoutMock.ts | 4 +- .../ux-editor/src/types/ContainerComponent.ts | 3 +- .../ux-editor/src/types/FormComponent.ts | 5 +- .../ux-editor/src/types/FormContainer.ts | 6 +- .../packages/ux-editor/src/types/FormItem.ts | 7 +- .../packages/ux-editor/src/utils/component.ts | 19 +- .../src/utils/formLayoutUtils.test.tsx | 27 +- .../ux-editor/src/utils/formLayoutUtils.ts | 9 +- .../packages/ux-editor/src/utils/language.ts | 8 - frontend/testing/mocks/i18nMock.ts | 8 +- 150 files changed, 1665 insertions(+), 1364 deletions(-) create mode 100644 frontend/packages/shared/src/types/ActionButtonAction.ts create mode 100644 frontend/packages/shared/src/types/ComponentSpecificConfigV3.ts create mode 100644 frontend/packages/shared/src/types/ComponentTypeV3.ts create mode 100644 frontend/packages/shared/src/types/FilterKeysOfType.ts create mode 100644 frontend/packages/shared/src/types/GridRow.ts create mode 100644 frontend/packages/shared/src/types/HTMLAutoCompleteValue.ts create mode 100644 frontend/packages/shared/src/types/HeadingLevel.ts create mode 100644 frontend/packages/shared/src/types/api/FormLayoutsResponseV3.ts create mode 100644 frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.test.ts create mode 100644 frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.ts diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 31f8694a6e5..9a7289fb6ee 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -1382,7 +1382,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", @@ -1445,7 +1446,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", @@ -1459,6 +1462,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", @@ -1468,7 +1472,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/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 (
- {component.type === ComponentType.Button && ( + {component.type === ComponentTypeV3.Button && ( )} - {component.type === ComponentType.NavigationButtons && ( + {component.type === ComponentTypeV3.NavigationButtons && ( ); - case ComponentType.AddressComponent: + case ComponentTypeV3.AddressComponent: return ( ); - case ComponentType.FileUpload: - case ComponentType.FileUploadWithTag: + case ComponentTypeV3.FileUpload: + case ComponentTypeV3.FileUploadWithTag: return ( ); - case ComponentType.Image: { + case ComponentTypeV3.Image: { return ( ; } - case ComponentType.Map: { + case ComponentTypeV3.Map: { return ; } default: { diff --git a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.test.tsx index 2cec2ce4faa..29333d9ebee 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.test.tsx @@ -3,7 +3,7 @@ import type { IGenericEditComponent } from '../../componentConfig'; import { renderWithMockStore, renderHookWithMockStore } from '../../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../../hooks/queries/useLayoutSchemaQuery'; import { FileUploadComponent } from './FileUploadComponent'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormFileUploaderComponent } from '../../../../types/FormComponent'; import { waitFor, screen, act } from '@testing-library/react'; import { textMock } from '../../../../../../../testing/mocks/i18nMock'; @@ -19,7 +19,7 @@ const component: FormFileUploaderComponent = { maxNumberOfAttachments: 1, minNumberOfAttachments: 0, onClickAction: jest.fn(), - type: ComponentType.FileUpload, + type: ComponentTypeV3.FileUpload, itemType: 'COMPONENT', dataModelBindings: {}, }; diff --git a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.tsx b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.tsx index d129c1ef2d5..1c54cfbba59 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/componentSpecificContent/FileUpload/FileUploadComponent.tsx @@ -3,12 +3,12 @@ import { Fieldset, LegacyTextField, Radio, Textfield } from '@digdir/design-syst import classes from './FileUploadComponent.module.css'; import { useText } from '../../../../hooks'; import type { IGenericEditComponent } from '../../componentConfig'; -import { ComponentType } from 'app-shared/types/ComponentType'; import type { FormFileUploaderComponent, FormFileUploaderWithTagComponent, } from '../../../../types/FormComponent'; import { FormField } from '../../../FormField'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; export const FileUploadComponent = ({ component, @@ -103,7 +103,7 @@ export const FileUploadComponent = ({ /> )} - {component.type === ComponentType.FileUpload && ( + {component.type === ComponentTypeV3.FileUpload && ( = { +const component: FormComponent = { id: '', itemType: 'COMPONENT', - type: ComponentType.Panel, + type: ComponentTypeV3.Panel, dataModelBindings: {}, variant: FormPanelVariant.Info, showIcon: false, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditAutoComplete.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditAutoComplete.test.tsx index 3d00a702fcd..8677d66d1d0 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditAutoComplete.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditAutoComplete.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { EditAutoComplete } from './EditAutoComplete'; import { act, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormComponent } from '../../../types/FormComponent'; import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; @@ -10,7 +10,7 @@ import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuer const componentMock: FormComponent = { id: 'random-id', autocomplete: '', - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', propertyPath: 'definitions/inputComponent', dataModelBindings: {}, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.test.tsx index 4c6b09eb2cb..ff581063d82 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { EditCodeList } from './EditCodeList'; import { screen, waitFor, act } from '@testing-library/react'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { renderWithMockStore, renderHookWithMockStore, @@ -78,7 +78,7 @@ const render = async ({ handleComponentChange={handleComponentChange} component={{ id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', - type: ComponentType.Dropdown, + type: ComponentTypeV3.Dropdown, textResourceBindings: { title: 'ServiceName', }, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.test.tsx index 787055a9633..50c6194f509 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.test.tsx @@ -5,7 +5,7 @@ import { appDataMock, textResourcesMock } from '../../../testing/stateMocks'; import type { IAppDataState } from '../../../features/appData/appDataReducers'; import { EditDataModelBindings } from './EditDataModelBindings'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import userEvent from '@testing-library/user-event'; import type { DatamodelMetadataResponse } from 'app-shared/types/api'; @@ -66,7 +66,7 @@ const render = async ({ dataModelBindings = {}, handleComponentChange = jest.fn( handleComponentChange={handleComponentChange} component={{ id: 'someComponentId', - type: ComponentType.Input, + type: ComponentTypeV3.Input, textResourceBindings: { title: 'ServiceName', }, @@ -248,7 +248,7 @@ describe('EditDataModelBindings', () => { handleComponentChange={jest.fn()} component={{ id: 'someComponentId', - type: ComponentType.Input, + type: ComponentTypeV3.Input, dataModelBindings: { simpleBinding: 'testModel.field2' }, itemType: 'COMPONENT', }} diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.tsx index 436dbbf1e17..76c599f1ed3 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditDataModelBindings.tsx @@ -1,6 +1,6 @@ import type { IGenericEditComponent } from '../componentConfig'; import { getMinOccursFromDataModel, getXsdDataTypeFromDataModel } from '../../../utils/datamodel'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import React, { useEffect, useState } from 'react'; import { useText } from '../../../hooks'; import { SelectDataModelComponent } from '../SelectDataModelComponent'; @@ -40,7 +40,7 @@ export const EditDataModelBindings = ({ }, required: getMinOccursFromDataModel(selectedDataModelElement, data) > 0, timeStamp: - component.type === ComponentType.Datepicker + component.type === ComponentTypeV3.Datepicker ? getXsdDataTypeFromDataModel(selectedDataModelElement, data) === 'DateTime' : undefined, }); diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditHeaderSize.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditHeaderSize.test.tsx index e038d03f87b..ca8d868ba97 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditHeaderSize.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditHeaderSize.test.tsx @@ -5,7 +5,7 @@ import { EditHeaderSize } from './EditHeaderSize'; import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; const getComboBox = () => screen.getByRole('combobox', { name: textMock('ux_editor.modal_header_type_helper') }); @@ -25,7 +25,7 @@ const render = async ({ size = undefined, handleComponentChange = jest.fn() } = handleComponentChange={handleComponentChange} component={{ id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', - type: ComponentType.Header, + type: ComponentTypeV3.Header, textResourceBindings: { title: 'ServiceName', }, @@ -105,7 +105,7 @@ describe('HeaderSizeSelect', () => { expect(handleComponentChange).toHaveBeenCalledWith({ id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', itemType: 'COMPONENT', - type: ComponentType.Header, + type: ComponentTypeV3.Header, textResourceBindings: { title: 'ServiceName', }, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditNumberValue.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditNumberValue.test.tsx index 8dad087ec97..b4c34e7e898 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditNumberValue.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditNumberValue.test.tsx @@ -5,7 +5,7 @@ import { EditNumberValue } from './EditNumberValue'; import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import userEvent from '@testing-library/user-event'; const waitForData = async () => { @@ -23,7 +23,7 @@ const render = async ({ maxLength = undefined, handleComponentChange = jest.fn() propertyKey='maxLength' component={{ id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', - type: ComponentType.Input, + type: ComponentTypeV3.Input, textResourceBindings: { title: 'ServiceName', }, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditOptions.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditOptions.test.tsx index db0cc70e49f..1f845d41f0a 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditOptions.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditOptions.test.tsx @@ -5,7 +5,7 @@ import { EditOptions } from './EditOptions'; import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormCheckboxesComponent, FormRadioButtonsComponent, @@ -19,7 +19,7 @@ const waitForData = async () => { const mockComponent: FormCheckboxesComponent | FormRadioButtonsComponent = { id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, textResourceBindings: { title: 'ServiceName', }, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditPreselectedIndex.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditPreselectedIndex.tsx index 7df5f6a9700..d956f10912c 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditPreselectedIndex.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditPreselectedIndex.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Textfield } from '@digdir/design-system-react'; import type { IGenericEditComponent } from '../componentConfig'; import { useText } from '../../../hooks'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormCheckboxesComponent, FormRadioButtonsComponent, @@ -19,13 +19,13 @@ export function EditPreselectedIndex({ component, handleComponentChange }: IGene }); }; - const mapComponentTypeToText = (componentType: ComponentType) => { + const mapComponentTypeToText = (componentType: ComponentTypeV3) => { switch (componentType) { - case ComponentType.Checkboxes: + case ComponentTypeV3.Checkboxes: return t('ux_editor.modal_check_box_set_preselected'); - case ComponentType.RadioButtons: + case ComponentTypeV3.RadioButtons: return t('ux_editor.modal_radio_button_set_preselected'); - case ComponentType.Dropdown: + case ComponentTypeV3.Dropdown: return t('ux_editor.component_dropdown_set_preselected'); default: return 'Unknown component'; @@ -35,7 +35,7 @@ export function EditPreselectedIndex({ component, handleComponentChange }: IGene return ( { await act(() => user.type(inputElement, 'new value')); expect(handleComponentChange).toHaveBeenCalledWith({ id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', - type: ComponentType.Input, + type: ComponentTypeV3.Input, textResourceBindings: { title: 'ServiceName', }, diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBinding.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBinding.test.tsx index e99bb5fa5bd..bc604076835 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBinding.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBinding.test.tsx @@ -11,7 +11,7 @@ import { import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import type { ITextResource } from 'app-shared/types/global'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ITextResourcesWithLanguage } from 'app-shared/types/global'; import { useTextResourcesQuery } from 'app-shared/hooks/queries/useTextResourcesQuery'; import type { FormComponent } from '../../../types/FormComponent'; @@ -28,7 +28,7 @@ describe('EditTextResourceBindings component', () => { textResourceBindings: { test: 'test-text', }, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }; diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBindings.test.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBindings.test.tsx index 91b847e864c..07c2083d70b 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBindings.test.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditTextResourceBindings.test.tsx @@ -10,7 +10,7 @@ import { import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import type { ITextResource } from 'app-shared/types/global'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ITextResourcesWithLanguage } from 'app-shared/types/global'; import { useTextResourcesQuery } from 'app-shared/hooks/queries/useTextResourcesQuery'; import type { FormComponent } from '../../../types/FormComponent'; @@ -26,7 +26,7 @@ describe('EditTextResourceBindings component', () => { textResourceBindings: { test: 'test-text', }, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }; diff --git a/frontend/packages/ux-editor-v3/src/components/toolbar/InformationPanelComponent.tsx b/frontend/packages/ux-editor-v3/src/components/toolbar/InformationPanelComponent.tsx index 96c7d28717a..2b59e7330f6 100644 --- a/frontend/packages/ux-editor-v3/src/components/toolbar/InformationPanelComponent.tsx +++ b/frontend/packages/ux-editor-v3/src/components/toolbar/InformationPanelComponent.tsx @@ -1,7 +1,7 @@ import React from 'react'; import classNames from 'classnames'; import classes from './InformationPanelComponent.module.css'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { Popover } from '@mui/material'; import { @@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next'; export interface IInformationPanelProvidedProps { anchorElement: any; - selectedComponent: ComponentType; + selectedComponent: ComponentTypeV3; informationPanelOpen: boolean; onClose: () => void; thirdPartyLibrary?: boolean; diff --git a/frontend/packages/ux-editor-v3/src/components/toolbar/ToolbarItemComponent.tsx b/frontend/packages/ux-editor-v3/src/components/toolbar/ToolbarItemComponent.tsx index 9a736de000b..30fc68aff40 100644 --- a/frontend/packages/ux-editor-v3/src/components/toolbar/ToolbarItemComponent.tsx +++ b/frontend/packages/ux-editor-v3/src/components/toolbar/ToolbarItemComponent.tsx @@ -5,11 +5,11 @@ import { StudioButton } from '@studio/components'; import { InformationIcon } from '@navikt/aksel-icons'; import { getComponentTitleByComponentType } from '../../utils/language'; import { useTranslation } from 'react-i18next'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; export interface IToolbarItemProvidedProps { - componentType: ComponentType; - onClick: (type: ComponentType, event: MouseEvent) => void; + componentType: ComponentTypeV3; + onClick: (type: ComponentTypeV3, event: MouseEvent) => void; thirdPartyLabel?: string; icon?: string | React.ComponentType; } diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/DesignView.test.tsx b/frontend/packages/ux-editor-v3/src/containers/DesignView/DesignView.test.tsx index 83102c5eff7..4004837259c 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/DesignView.test.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/DesignView.test.tsx @@ -75,7 +75,7 @@ describe('DesignView', () => { const addButton = screen.getByRole('button', { name: textMock('ux_editor.pages_add') }); await act(() => user.click(addButton)); - expect(queriesMock.saveFormLayout).toHaveBeenCalled(); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalled(); }); it('Displays the tree view version of the layout when the formTree feature flag is enabled', async () => { diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/FormItemTitle.test.tsx b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/FormItemTitle.test.tsx index 2170f2bcae9..64b44daf0f4 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/FormItemTitle.test.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/FormItemTitle.test.tsx @@ -3,12 +3,12 @@ import { renderWithMockStore } from '../../../../../testing/mocks'; import { FormItemTitle } from './FormItemTitle'; import type { FormComponent } from '../../../../../types/FormComponent'; import { componentMocks } from '../../../../../testing/componentMocks'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { screen } from '@testing-library/react'; import { textMock } from '../../../../../../../../testing/mocks/i18nMock'; // Test data: -const item: FormComponent = componentMocks[ComponentType.Input]; +const item: FormComponent = componentMocks[ComponentTypeV3.Input]; const label = 'Test label'; // Mocks: diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/useDeleteItem.test.ts b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/useDeleteItem.test.ts index b44c7b54fba..45a53b124f2 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/useDeleteItem.test.ts +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/FormItemTitle/useDeleteItem.test.ts @@ -3,7 +3,7 @@ import { useDeleteItem } from './useDeleteItem'; import type { FormComponent } from '../../../../../types/FormComponent'; import type { FormContainer } from '../../../../../types/FormContainer'; import { componentMocks } from '../../../../../testing/componentMocks'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { container1IdMock, layoutMock } from '../../../../../testing/layoutMock'; // Mocks: @@ -20,7 +20,7 @@ describe('useDeleteItem', () => { afterEach(jest.clearAllMocks); it('Calls component mutation when formItem is a component', () => { - const { result } = render(componentMocks[ComponentType.Input]); + const { result } = render(componentMocks[ComponentTypeV3.Input]); result.current(); expect(mockComponentMutation).toHaveBeenCalledTimes(1); expect(mockContainerMutation).not.toHaveBeenCalled(); diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/useItemTitle.test.ts b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/useItemTitle.test.ts index 1ad762a6f75..05e0c3405c5 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/useItemTitle.test.ts +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormItem/useItemTitle.test.ts @@ -3,7 +3,7 @@ import { renderHookWithMockStore } from '../../../../testing/mocks'; import { textMock } from '../../../../../../../testing/mocks/i18nMock'; import type { FormContainer } from '../../../../types/FormContainer'; import type { FormComponent } from '../../../../types/FormComponent'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryKey } from 'app-shared/types/QueryKey'; import type { ITextResource, ITextResources } from 'app-shared/types/global'; @@ -29,7 +29,7 @@ describe('useItemTitle', () => { it('Returns the correct title when the item is a container', () => { const id = '1'; - const container: FormContainer = { id, itemType: 'CONTAINER', type: ComponentType.Group }; + const container: FormContainer = { id, itemType: 'CONTAINER', type: ComponentTypeV3.Group }; const expectedResult = textMock(`ux_editor.component_title.${container.type}`); expect(result.current(container)).toBe(expectedResult); }); @@ -37,7 +37,7 @@ describe('useItemTitle', () => { it('Returns the component title when the item is a component with a given title', () => { const component: FormComponent = { id: 'a', - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', textResourceBindings: { title: titleKey }, }; @@ -45,7 +45,7 @@ describe('useItemTitle', () => { }); it('Returns the component type name when the item is a component without a given title', () => { - const type = ComponentType.Paragraph; + const type = ComponentTypeV3.Paragraph; const component: FormComponent = { id: 'a', type, diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormTree.test.tsx b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormTree.test.tsx index 69ee2e9465f..9c482e85c98 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormTree.test.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/FormTree.test.tsx @@ -9,7 +9,7 @@ import { QueryKey } from 'app-shared/types/QueryKey'; import type { ITextResources } from 'app-shared/types/global'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; import userEvent from '@testing-library/user-event'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { FormContext } from '../../FormContext'; import type { IInternalLayout } from '../../../types/global'; import type { FormComponent } from '../../../types/FormComponent'; @@ -29,32 +29,32 @@ const onMove = jest.fn(); const rootComponent: FormComponent = { id: 'rootComponent', itemType: 'COMPONENT', - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; const rootContainerWithChildren: FormContainer = { id: 'rootContainer1', itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const emptyRootContainer: FormContainer = { id: 'rootContainer2', itemType: 'CONTAINER', - type: ComponentType.ButtonGroup, + type: ComponentTypeV3.ButtonGroup, }; const subComponent: FormComponent = { id: 'subComponent', itemType: 'COMPONENT', - type: ComponentType.Input, + type: ComponentTypeV3.Input, }; const subContainer: FormContainer = { id: 'subContainer', itemType: 'CONTAINER', - type: ComponentType.Accordion, + type: ComponentTypeV3.Accordion, }; const subSubComponent: FormComponent = { id: 'subSubComponent', itemType: 'COMPONENT', - type: ComponentType.TextArea, + type: ComponentTypeV3.TextArea, }; const layoutMock: IInternalLayout = { components: { diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/UnknownReferencedItem/UnknownReferencedItem.test.tsx b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/UnknownReferencedItem/UnknownReferencedItem.test.tsx index 4d677904e9a..4442f26d2aa 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/UnknownReferencedItem/UnknownReferencedItem.test.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/FormTree/UnknownReferencedItem/UnknownReferencedItem.test.tsx @@ -44,7 +44,7 @@ describe('UnknownReferencedItem', () => { layout: layoutMock, }, queries: { - saveFormLayout: mockedSaveFormLayout, + saveFormLayoutV3: mockedSaveFormLayout, }, }); diff --git a/frontend/packages/ux-editor-v3/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx b/frontend/packages/ux-editor-v3/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx index b60151ebf02..374bb379b0f 100644 --- a/frontend/packages/ux-editor-v3/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx @@ -22,7 +22,7 @@ import { useFormLayoutsQuery } from '../../../hooks/queries/useFormLayoutsQuery' import { DragAndDrop } from 'app-shared/components/dragAndDrop'; import { FormContextProvider } from '../../FormContext'; import { BASE_CONTAINER_ID } from 'app-shared/constants'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; const mockOrg = 'org'; const mockApp = 'app'; @@ -35,7 +35,7 @@ const mockReceiptName: string = 'Kvittering'; const mockPageData: IInternalLayout = { components: { component1Mock, component2Mock }, - containers: { mockContainerId: { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group } }, + containers: { mockContainerId: { id: 'id', itemType: 'CONTAINER', type: ComponentTypeV3.Group } }, customDataProperties: {}, customRootProperties: {}, order: { mockContainerId: [] }, diff --git a/frontend/packages/ux-editor-v3/src/containers/FormContext.test.tsx b/frontend/packages/ux-editor-v3/src/containers/FormContext.test.tsx index a5fc68a961a..0da1465acc7 100644 --- a/frontend/packages/ux-editor-v3/src/containers/FormContext.test.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/FormContext.test.tsx @@ -9,7 +9,7 @@ import { useUpdateFormComponentMutation } from '../hooks/mutations/useUpdateForm import type { UseMutationResult } from '@tanstack/react-query'; import { renderWithMockStore } from '../testing/mocks'; import { AUTOSAVE_DEBOUNCE_INTERVAL_MILLISECONDS } from 'app-shared/constants'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormContainer } from '../types/FormContainer'; import type { FormComponent } from '../types/FormComponent'; import type { IAppState } from '../types/global'; @@ -57,7 +57,11 @@ describe('FormContext', () => { it('should update the form when calling handleUpdate', async () => { const user = userEvent.setup(); - const mockForm: FormContainer = { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group }; + const mockForm: FormContainer = { + id: 'id', + itemType: 'CONTAINER', + type: ComponentTypeV3.Group, + }; render(() => { const { form, handleUpdate } = React.useContext(FormContext); @@ -83,7 +87,11 @@ describe('FormContext', () => { it('should edit the form when calling handleEdit', async () => { const user = userEvent.setup(); - const mockForm: FormContainer = { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group }; + const mockForm: FormContainer = { + id: 'id', + itemType: 'CONTAINER', + type: ComponentTypeV3.Group, + }; const { store } = render(() => { const { formId, form, handleEdit } = React.useContext(FormContext); @@ -115,7 +123,11 @@ describe('FormContext', () => { it('should render id and itemType when calling handleEdit with truthy updatedForm', async () => { const user = userEvent.setup(); - const mockForm: FormContainer = { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group }; + const mockForm: FormContainer = { + id: 'id', + itemType: 'CONTAINER', + type: ComponentTypeV3.Group, + }; const { store } = render(() => { const { formId, form, handleEdit } = React.useContext(FormContext); return ( @@ -176,7 +188,11 @@ describe('FormContext', () => { it('should save the container when calling handleSave', async () => { const user = userEvent.setup(); - const mockForm: FormContainer = { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group }; + const mockForm: FormContainer = { + id: 'id', + itemType: 'CONTAINER', + type: ComponentTypeV3.Group, + }; render(() => { const { handleSave } = React.useContext(FormContext); @@ -191,7 +207,11 @@ describe('FormContext', () => { it('should save the container and its new id when calling handleSave', async () => { const user = userEvent.setup(); - const mockForm: FormContainer = { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group }; + const mockForm: FormContainer = { + id: 'id', + itemType: 'CONTAINER', + type: ComponentTypeV3.Group, + }; render(() => { const { formId, handleSave } = React.useContext(FormContext); @@ -214,7 +234,11 @@ describe('FormContext', () => { it('should save the container when calling debounceSave', async () => { const user = userEvent.setup(); - const mockForm: FormContainer = { id: 'id', itemType: 'CONTAINER', type: ComponentType.Group }; + const mockForm: FormContainer = { + id: 'id', + itemType: 'CONTAINER', + type: ComponentTypeV3.Group, + }; render(() => { const { debounceSave } = React.useContext(FormContext); @@ -231,7 +255,11 @@ describe('FormContext', () => { it('should save the component when calling handleSave', async () => { const user = userEvent.setup(); - const mockForm: FormComponent = { id: 'id', itemType: 'COMPONENT', type: ComponentType.Input }; + const mockForm: FormComponent = { + id: 'id', + itemType: 'COMPONENT', + type: ComponentTypeV3.Input, + }; render(() => { const { handleSave } = React.useContext(FormContext); @@ -246,7 +274,11 @@ describe('FormContext', () => { it('should save the component and its new id when calling handleSave', async () => { const user = userEvent.setup(); - const mockForm: FormComponent = { id: 'id', itemType: 'COMPONENT', type: ComponentType.Input }; + const mockForm: FormComponent = { + id: 'id', + itemType: 'COMPONENT', + type: ComponentTypeV3.Input, + }; render(() => { const { formId, handleSave } = React.useContext(FormContext); @@ -269,7 +301,11 @@ describe('FormContext', () => { it('should save the component when calling debounceSave', async () => { const user = userEvent.setup(); - const mockForm: FormComponent = { id: 'id', itemType: 'COMPONENT', type: ComponentType.Input }; + const mockForm: FormComponent = { + id: 'id', + itemType: 'COMPONENT', + type: ComponentTypeV3.Input, + }; render(() => { const { debounceSave } = React.useContext(FormContext); diff --git a/frontend/packages/ux-editor-v3/src/containers/FormDesigner.tsx b/frontend/packages/ux-editor-v3/src/containers/FormDesigner.tsx index 317d7f33057..c46b23c5f96 100644 --- a/frontend/packages/ux-editor-v3/src/containers/FormDesigner.tsx +++ b/frontend/packages/ux-editor-v3/src/containers/FormDesigner.tsx @@ -16,7 +16,7 @@ import { useRuleConfigQuery } from '../hooks/queries/useRuleConfigQuery'; import { useInstanceIdQuery } from 'app-shared/hooks/queries'; import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams'; import type { HandleAdd, HandleMove } from 'app-shared/types/dndTypes'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { generateComponentId } from '../utils/generateId'; import { addItemOfType, @@ -114,7 +114,7 @@ export const FormDesigner = ({ const triggerInvalidChildAlert = () => alert(t('schema_editor.invalid_child_error')); const layout = formLayouts[selectedLayout]; - const addItem: HandleAdd = (type, { parentId, index }) => { + const addItem: HandleAdd = (type, { parentId, index }) => { const newId = generateComponentId(type, formLayouts); if (!isComponentTypeValidChild(layout, parentId, type)) { diff --git a/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/externalGroupComponentToInternal.test.ts b/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/externalGroupComponentToInternal.test.ts index 1b1b4353e51..f7205141f8d 100644 --- a/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/externalGroupComponentToInternal.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/externalGroupComponentToInternal.test.ts @@ -1,6 +1,6 @@ import type { ExternalContainerComponent } from '../../types/ExternalContainerComponent'; import { externalContainerComponentToInternal } from './externalContainerComponentToInternal'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; // Test data: const id = '1'; @@ -13,7 +13,7 @@ describe('externalGroupComponentToInternal', () => { (pageIndex) => { const externalComponent: ExternalContainerComponent = { id, - type: ComponentType.Group, + type: ComponentTypeV3.Group, children, customProperty, }; @@ -21,7 +21,7 @@ describe('externalGroupComponentToInternal', () => { expect(result).toEqual({ id, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, pageIndex, propertyPath: 'definitions/groupComponent', customProperty, diff --git a/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/internalGroupComponentToExternal.test.ts b/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/internalGroupComponentToExternal.test.ts index cd81912dd8f..59f90ed0478 100644 --- a/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/internalGroupComponentToExternal.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/containerComponentConverters/internalGroupComponentToExternal.test.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { internalContainerComponentToExternal } from './internalContainerComponentToExternal'; import type { FormContainer } from '../../types/FormContainer'; @@ -12,7 +12,7 @@ describe('internalGroupComponentToExternal', () => { const internalGroupComponent: FormContainer = { id, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, pageIndex: null, customProperty, }; @@ -20,7 +20,7 @@ describe('internalGroupComponentToExternal', () => { expect(result).toEqual({ id, children, - type: ComponentType.Group, + type: ComponentTypeV3.Group, customProperty, }); }); diff --git a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.test.ts b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.test.ts index 021b2f66d14..049e9cd7b0e 100644 --- a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.test.ts @@ -4,7 +4,7 @@ import { internalLayoutWithMultiPageGroup, } from '../../testing/layoutWithMultiPageGroupMocks'; import { createEmptyLayout } from '../../utils/formLayoutUtils'; -import type { ExternalFormLayout } from 'app-shared/types/api'; +import type { ExternalFormLayoutV3 } from 'app-shared/types/api'; import type { IInternalLayout } from '../../types/global'; import { layoutSchemaUrl } from 'app-shared/cdn-paths'; @@ -22,7 +22,7 @@ describe('externalLayoutToInternal', () => { it('Returns an empty layout with custom properties when the "data" property is null', () => { const customProperty1 = 'test1'; const customProperty2 = 'test2'; - const externalLayout: ExternalFormLayout = { + const externalLayout: ExternalFormLayoutV3 = { $schema: layoutSchemaUrl(), data: null, customProperty1, @@ -44,7 +44,7 @@ describe('externalLayoutToInternal', () => { const rootCustomProperty2 = 'test2'; const dataCustomProperty1 = 'test3'; const dataCustomProperty2 = 'test4'; - const externalLayout: ExternalFormLayout = { + const externalLayout: ExternalFormLayoutV3 = { $schema: layoutSchemaUrl(), data: { layout: null, diff --git a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.ts b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.ts index e5cd80877f7..dda918ca59f 100644 --- a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.ts +++ b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/externalLayoutToInternal.ts @@ -1,4 +1,8 @@ -import type { ExternalComponent, ExternalData, ExternalFormLayout } from 'app-shared/types/api'; +import type { + ExternalComponentV3, + ExternalDataV3, + ExternalFormLayoutV3, +} from 'app-shared/types/api'; import type { IFormDesignerComponents, IFormDesignerContainers, @@ -24,11 +28,11 @@ import { import { containerComponentTypes } from '../../data/containerComponentTypes'; export const externalLayoutToInternal = ( - externalLayout: ExternalFormLayout | null, + externalLayout: ExternalFormLayoutV3 | null, ): IInternalLayout => externalLayout ? convertExternalLayout(externalLayout) : createEmptyLayout(); -const convertExternalLayout = (externalLayout: ExternalFormLayout): IInternalLayout => { +const convertExternalLayout = (externalLayout: ExternalFormLayoutV3): IInternalLayout => { const customRootProperties = getCustomRootProperties(externalLayout); const { data } = externalLayout; const convertedData: InternalLayoutData = data @@ -37,14 +41,14 @@ const convertExternalLayout = (externalLayout: ExternalFormLayout): IInternalLay return { ...convertedData, customRootProperties }; }; -const getCustomRootProperties = (externalLayout: ExternalFormLayout) => { +const getCustomRootProperties = (externalLayout: ExternalFormLayoutV3) => { const customProperties = { ...externalLayout }; delete customProperties.data; delete customProperties.$schema; return customProperties; }; -const convertExternalData = (externalData: ExternalData): InternalLayoutData => { +const convertExternalData = (externalData: ExternalDataV3): InternalLayoutData => { const customDataProperties = getCustomDataProperties(externalData); const { layout } = externalData; const convertedComponents: InternalLayoutComponents = layout @@ -53,14 +57,14 @@ const convertExternalData = (externalData: ExternalData): InternalLayoutData => return { ...convertedComponents, customDataProperties }; }; -const getCustomDataProperties = (externalData: ExternalData) => { +const getCustomDataProperties = (externalData: ExternalDataV3) => { const customProperties = { ...externalData }; delete customProperties.layout; return customProperties; }; const convertExternalComponentList = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], ): InternalLayoutComponents => ({ components: getInternalComponents(externalComponents), containers: getInternalContainers(externalComponents), @@ -68,7 +72,7 @@ const convertExternalComponentList = ( }); const getInternalComponents = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], ): IFormDesignerComponents => { const convert = (component: ExternalSimpleComponent) => convertSimpleComponent(externalComponents, component); @@ -77,7 +81,7 @@ const getInternalComponents = ( }; const getInternalContainers = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], ): IFormDesignerContainers => { const baseContainer: FormContainer = { id: BASE_CONTAINER_ID, @@ -91,36 +95,37 @@ const getInternalContainers = ( return mapByProperty(containers, 'id'); }; -const getConvertedContainers = (externalComponents: ExternalComponent[]): FormContainer[] => { +const getConvertedContainers = (externalComponents: ExternalComponentV3[]): FormContainer[] => { const convert = (component) => convertContainerComponent(externalComponents, component); return findContainerComponents(externalComponents).map(convert); }; -const getOrderOfComponents = (externalComponents: ExternalComponent[]): IFormLayoutOrder => ({ +const getOrderOfComponents = (externalComponents: ExternalComponentV3[]): IFormLayoutOrder => ({ [BASE_CONTAINER_ID]: findTopLevelComponentIds(externalComponents), ...getChildrenIdsOfAllContainers(externalComponents), }); const findContainerComponents = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], ): ExternalContainerComponent[] => externalComponents.filter(isContainer); -const isContainer = (component: ExternalComponent): component is ExternalContainerComponent => +const isContainer = (component: ExternalComponentV3): component is ExternalContainerComponent => containerComponentTypes.includes(component.type); -const findSimpleComponents = (externalComponents: ExternalComponent[]): ExternalSimpleComponent[] => - externalComponents.filter(isSimpleComponent); +const findSimpleComponents = ( + externalComponents: ExternalComponentV3[], +): ExternalSimpleComponent[] => externalComponents.filter(isSimpleComponent); -const isSimpleComponent = (component: ExternalComponent): component is ExternalSimpleComponent => +const isSimpleComponent = (component: ExternalComponentV3): component is ExternalSimpleComponent => !isContainer(component); -const findTopLevelComponentIds = (externalComponents: ExternalComponent[]) => +const findTopLevelComponentIds = (externalComponents: ExternalComponentV3[]) => externalComponents .filter((component) => findParent(externalComponents, component.id) === null) .map(({ id }) => id); const getChildrenIdsOfAllContainers = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], ): IFormLayoutOrder => { const entries: [string, string[]][] = findContainerComponents(externalComponents).map( (container) => [container.id, getChildIds(container)], @@ -129,7 +134,7 @@ const getChildrenIdsOfAllContainers = ( }; const convertSimpleComponent = ( - externalComponentList: ExternalComponent[], + externalComponentList: ExternalComponentV3[], externalComponent: ExternalSimpleComponent, ): FormComponent => { const pageIndex = findPageIndexOfComponent(externalComponentList, externalComponent.id); @@ -137,7 +142,7 @@ const convertSimpleComponent = ( }; const convertContainerComponent = ( - externalComponentList: ExternalComponent[], + externalComponentList: ExternalComponentV3[], externalComponent: ExternalContainerComponent, ): FormContainer => { const pageIndex = findPageIndexOfComponent(externalComponentList, externalComponent.id); @@ -145,7 +150,7 @@ const convertContainerComponent = ( }; const findParent = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], id: string, ): ExternalContainerComponent | null => findContainerComponents(externalComponents).find((container) => @@ -153,7 +158,7 @@ const findParent = ( ) ?? null; const findPageIndexOfComponent = ( - externalComponents: ExternalComponent[], + externalComponents: ExternalComponentV3[], id: string, ): number | null => { const parentContainer = findParent(externalComponents, id); diff --git a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/formLayoutConverters.test.ts b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/formLayoutConverters.test.ts index fe7224e77e6..8faa9d4687a 100644 --- a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/formLayoutConverters.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/formLayoutConverters.test.ts @@ -4,7 +4,7 @@ import { internalLayoutWithMultiPageGroup, } from '../../testing/layoutWithMultiPageGroupMocks'; import { internalLayoutToExternal } from './internalLayoutToExternal'; -import type { ExternalFormLayout } from 'app-shared/types/api'; +import type { ExternalFormLayoutV3 } from 'app-shared/types/api'; describe('formLayoutConverters', () => { test('Internal layout remains the same when converted to en external layout and back', () => { @@ -14,7 +14,7 @@ describe('formLayoutConverters', () => { }); test('External layout that is already converted once remains the same when converted to an internal layout and back', () => { - const convertToInternalAndBack = (layout: ExternalFormLayout) => + const convertToInternalAndBack = (layout: ExternalFormLayoutV3) => internalLayoutToExternal(externalLayoutToInternal(layout)); const convertedOnce = convertToInternalAndBack(externalLayoutWithMultiPageGroup); const convertedTwice = convertToInternalAndBack(convertedOnce); diff --git a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.test.ts b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.test.ts index 708fec50cb7..266785f4e6e 100644 --- a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.test.ts @@ -6,7 +6,7 @@ import { } from '../../testing/layoutWithMultiPageGroupMocks'; import { getComponentIdWithPageIndex, internalLayoutToExternal } from './internalLayoutToExternal'; import { BASE_CONTAINER_ID } from 'app-shared/constants'; -import type { ExternalComponent } from 'app-shared/types/api'; +import type { ExternalComponentV3 } from 'app-shared/types/api'; import { layoutMock } from '../../testing/layoutMock'; describe('internalLayoutToExternal', () => { @@ -20,7 +20,7 @@ describe('internalLayoutToExternal', () => { const findInternalComponent = (id) => internalLayoutWithMultiPageGroup.components[id] || internalLayoutWithMultiPageGroup.containers[id]; - const findExternalComponent = (id): ExternalComponent => + const findExternalComponent = (id): ExternalComponentV3 => layout.find((component) => component.id === id); it('Creates a list containing all components and containers', () => { diff --git a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.ts b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.ts index e9de76ddabe..5459dbf9f35 100644 --- a/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.ts +++ b/frontend/packages/ux-editor-v3/src/converters/formLayoutConverters/internalLayoutToExternal.ts @@ -1,5 +1,5 @@ import type { IInternalLayout } from '../../types/global'; -import type { ExternalComponent, ExternalFormLayout } from 'app-shared/types/api'; +import type { ExternalComponentV3, ExternalFormLayoutV3 } from 'app-shared/types/api'; import { layoutSchemaUrl } from 'app-shared/cdn-paths'; import type { ExternalContainerComponent } from '../../types/ExternalContainerComponent'; import { internalContainerComponentToExternal } from '../containerComponentConverters'; @@ -10,7 +10,9 @@ import { BASE_CONTAINER_ID } from 'app-shared/constants'; import type { CompareFunction } from 'app-shared/utils/compareFunctions'; import type { FormComponent } from '../../types/FormComponent'; -export const internalLayoutToExternal = (internalLayout: IInternalLayout): ExternalFormLayout => ({ +export const internalLayoutToExternal = ( + internalLayout: IInternalLayout, +): ExternalFormLayoutV3 => ({ $schema: layoutSchemaUrl(), data: { layout: generateExternalComponents(internalLayout), @@ -21,7 +23,7 @@ export const internalLayoutToExternal = (internalLayout: IInternalLayout): Exter export const generateExternalComponents = ( internalLayout: IInternalLayout, -): ExternalComponent[] => { +): ExternalComponentV3[] => { const groupComponents = getGroupComponents(internalLayout); const simpleComponents = getSimpleComponents(internalLayout); const allComponents = [...groupComponents, ...simpleComponents]; @@ -77,7 +79,7 @@ const getComponentById = ( ): FormComponent | FormContainer => internalLayout.components[componentId] || internalLayout.containers[componentId]; -const getSimpleComponents = (internalLayout: IInternalLayout): ExternalComponent[] => +const getSimpleComponents = (internalLayout: IInternalLayout): ExternalComponentV3[] => Object.values(internalLayout.components).map(internalSimpleComponentToExternal); /** @@ -90,8 +92,8 @@ const getAllComponentIdsInOrder = (internalLayout: IInternalLayout): string[] => }; const compareComponentsByPosition = - (idsInOrder: string[]): CompareFunction => - (componentA: ExternalComponent, componentB: ExternalComponent) => { + (idsInOrder: string[]): CompareFunction => + (componentA: ExternalComponentV3, componentB: ExternalComponentV3) => { const indexA = idsInOrder.indexOf(componentA.id); const indexB = idsInOrder.indexOf(componentB.id); return indexA - indexB; diff --git a/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.test.ts b/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.test.ts index bc4c0bbbbb6..aa34d27da76 100644 --- a/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.test.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ExternalSimpleComponent } from '../../types/ExternalSimpleComponent'; import { externalSimpleComponentToInternal } from './externalSimpleComponentToInternal'; import { formItemConfigs } from '../../data/formItemConfig'; @@ -6,7 +6,7 @@ import { formItemConfigs } from '../../data/formItemConfig'; // Test data: const id = '1'; const customProperty = 'test'; -const type: ComponentType = ComponentType.Input; +const type: ComponentTypeV3 = ComponentTypeV3.Input; const propertyPath = formItemConfigs[type].defaultProperties.propertyPath; describe('externalSimpleComponentToInternal', () => { diff --git a/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts b/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts index 8f36b5f03ed..b5ed0417559 100644 --- a/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts +++ b/frontend/packages/ux-editor-v3/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormComponent } from '../../types/FormComponent'; import { formItemConfigs } from '../../data/formItemConfig'; import { internalSimpleComponentToExternal } from './internalSimpleComponentToExternal'; @@ -6,7 +6,7 @@ import { internalSimpleComponentToExternal } from './internalSimpleComponentToEx // Test data: const id = '1'; const customProperty = 'test'; -const type: ComponentType = ComponentType.Input; +const type: ComponentTypeV3 = ComponentTypeV3.Input; const propertyPath = formItemConfigs[type].defaultProperties.propertyPath; describe('internalGroupComponentToExternal', () => { diff --git a/frontend/packages/ux-editor-v3/src/data/containerComponentTypes.ts b/frontend/packages/ux-editor-v3/src/data/containerComponentTypes.ts index 0d933791390..f57a6fc69a1 100644 --- a/frontend/packages/ux-editor-v3/src/data/containerComponentTypes.ts +++ b/frontend/packages/ux-editor-v3/src/data/containerComponentTypes.ts @@ -1,7 +1,7 @@ -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { formItemConfigs } from './formItemConfig'; import { LayoutItemType } from '../types/global'; -export const containerComponentTypes: ComponentType[] = Object.values(formItemConfigs) +export const containerComponentTypes: ComponentTypeV3[] = Object.values(formItemConfigs) .filter((comp) => comp.itemType === LayoutItemType.Container) .map((comp) => comp.name); diff --git a/frontend/packages/ux-editor-v3/src/data/formItemConfig.test.ts b/frontend/packages/ux-editor-v3/src/data/formItemConfig.test.ts index 1fa50010fda..4bce0b17098 100644 --- a/frontend/packages/ux-editor-v3/src/data/formItemConfig.test.ts +++ b/frontend/packages/ux-editor-v3/src/data/formItemConfig.test.ts @@ -1,9 +1,9 @@ import { formItemConfigs } from './formItemConfig'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; describe('formItemConfigs', () => { it('should have textResourceBindings and buttonStyle defined for ActionButton', () => { - const actionButtonConfig = formItemConfigs[ComponentType.ActionButton]; + const actionButtonConfig = formItemConfigs[ComponentTypeV3.ActionButton]; expect(actionButtonConfig).toBeDefined(); const { defaultProperties } = actionButtonConfig; diff --git a/frontend/packages/ux-editor-v3/src/data/formItemConfig.ts b/frontend/packages/ux-editor-v3/src/data/formItemConfig.ts index d61971c5c7b..7d147629dc1 100644 --- a/frontend/packages/ux-editor-v3/src/data/formItemConfig.ts +++ b/frontend/packages/ux-editor-v3/src/data/formItemConfig.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 as ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormItem } from '../types/FormItem'; import { FormPanelVariant } from 'app-shared/types/FormPanelVariant'; import type { RefAttributes, SVGProps } from 'react'; @@ -34,60 +34,60 @@ import { import type { ContainerComponentType } from '../types/ContainerComponent'; import { LayoutItemType } from '../types/global'; -export type FormItemConfig = { +export type FormItemConfig = { name: T; itemType: LayoutItemType; defaultProperties: FormItem; icon?: React.ComponentType & { title?: string; titleId?: string }> & RefAttributes; -} & (T extends ContainerComponentType ? { validChildTypes: ComponentType[] } : {}); +} & (T extends ContainerComponentType ? { validChildTypes: ComponentTypeV3[] } : {}); -export type FormItemConfigs = { [T in ComponentType]: FormItemConfig }; +export type FormItemConfigs = { [T in ComponentTypeV3]: FormItemConfig }; export const formItemConfigs: FormItemConfigs = { - [ComponentType.Alert]: { - name: ComponentType.Alert, + [ComponentTypeV3.Alert]: { + name: ComponentTypeV3.Alert, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Alert, + type: ComponentTypeV3.Alert, severity: 'info', propertyPath: 'definitions/alertComponent', }, icon: ExclamationmarkTriangleIcon, }, - [ComponentType.Accordion]: { - name: ComponentType.Accordion, + [ComponentTypeV3.Accordion]: { + name: ComponentTypeV3.Accordion, itemType: LayoutItemType.Container, defaultProperties: { id: '', itemType: 'CONTAINER', - type: ComponentType.Accordion, + type: ComponentTypeV3.Accordion, propertyPath: 'definitions/accordionComponent', }, icon: Accordion, - validChildTypes: [ComponentType.Paragraph], + validChildTypes: [ComponentTypeV3.Paragraph], }, - [ComponentType.AccordionGroup]: { - name: ComponentType.AccordionGroup, + [ComponentTypeV3.AccordionGroup]: { + name: ComponentTypeV3.AccordionGroup, itemType: LayoutItemType.Container, defaultProperties: { id: '', itemType: 'CONTAINER', - type: ComponentType.AccordionGroup, + type: ComponentTypeV3.AccordionGroup, propertyPath: 'definitions/accordionGroupComponent', }, icon: ChevronDownDoubleIcon, - validChildTypes: [ComponentType.Accordion], + validChildTypes: [ComponentTypeV3.Accordion], }, - [ComponentType.ActionButton]: { - name: ComponentType.ActionButton, + [ComponentTypeV3.ActionButton]: { + name: ComponentTypeV3.ActionButton, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.ActionButton, + type: ComponentTypeV3.ActionButton, textResourceBindings: { title: '', // To avoid undefined as text when previewing default component }, @@ -95,88 +95,88 @@ export const formItemConfigs: FormItemConfigs = { }, icon: FingerButtonIcon, }, - [ComponentType.AddressComponent]: { - name: ComponentType.AddressComponent, + [ComponentTypeV3.AddressComponent]: { + name: ComponentTypeV3.AddressComponent, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.AddressComponent, + type: ComponentTypeV3.AddressComponent, dataModelBindings: {}, simplified: true, propertyPath: 'definitions/addressComponent', }, icon: HouseIcon, }, - [ComponentType.AttachmentList]: { - name: ComponentType.AttachmentList, + [ComponentTypeV3.AttachmentList]: { + name: ComponentTypeV3.AttachmentList, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.AttachmentList, + type: ComponentTypeV3.AttachmentList, maxNumberOfAttachments: 1, minNumberOfAttachments: 0, propertyPath: 'definitions/attachmentListComponent', }, icon: PaperclipIcon, }, - [ComponentType.Button]: { - name: ComponentType.Button, + [ComponentTypeV3.Button]: { + name: ComponentTypeV3.Button, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Button, + type: ComponentTypeV3.Button, onClickAction: () => {}, propertyPath: 'definitions/actionButtonComponent', }, icon: FingerButtonIcon, }, - [ComponentType.ButtonGroup]: { - name: ComponentType.ButtonGroup, + [ComponentTypeV3.ButtonGroup]: { + name: ComponentTypeV3.ButtonGroup, itemType: LayoutItemType.Container, defaultProperties: { id: '', itemType: 'CONTAINER', - type: ComponentType.ButtonGroup, + type: ComponentTypeV3.ButtonGroup, propertyPath: 'definitions/buttonGroupComponent', }, icon: FingerButtonIcon, - validChildTypes: [ComponentType.Button], + validChildTypes: [ComponentTypeV3.Button], }, - [ComponentType.Checkboxes]: { - name: ComponentType.Checkboxes, + [ComponentTypeV3.Checkboxes]: { + name: ComponentTypeV3.Checkboxes, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, dataModelBindings: {}, required: true, propertyPath: 'definitions/radioAndCheckboxComponents', }, icon: Checkbox, }, - [ComponentType.Custom]: { - name: ComponentType.Custom, + [ComponentTypeV3.Custom]: { + name: ComponentTypeV3.Custom, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Custom, + type: ComponentTypeV3.Custom, tagName: 'tag', framework: 'framework', }, }, - [ComponentType.Datepicker]: { - name: ComponentType.Datepicker, + [ComponentTypeV3.Datepicker]: { + name: ComponentTypeV3.Datepicker, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', dataModelBindings: {}, - type: ComponentType.Datepicker, + type: ComponentTypeV3.Datepicker, minDate: '1900-01-01T12:00:00.000Z', maxDate: '2100-01-01T12:00:00.000Z', timeStamp: false, @@ -185,13 +185,13 @@ export const formItemConfigs: FormItemConfigs = { }, icon: CalendarIcon, }, - [ComponentType.Dropdown]: { - name: ComponentType.Dropdown, + [ComponentTypeV3.Dropdown]: { + name: ComponentTypeV3.Dropdown, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Dropdown, + type: ComponentTypeV3.Dropdown, dataModelBindings: {}, optionsId: '', required: true, @@ -199,13 +199,13 @@ export const formItemConfigs: FormItemConfigs = { }, icon: Select, }, - [ComponentType.FileUpload]: { - name: ComponentType.FileUpload, + [ComponentTypeV3.FileUpload]: { + name: ComponentTypeV3.FileUpload, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.FileUpload, + type: ComponentTypeV3.FileUpload, description: '', displayMode: 'list', hasCustomFileEndings: false, @@ -216,13 +216,13 @@ export const formItemConfigs: FormItemConfigs = { }, icon: PaperclipIcon, }, - [ComponentType.FileUploadWithTag]: { - name: ComponentType.FileUploadWithTag, + [ComponentTypeV3.FileUploadWithTag]: { + name: ComponentTypeV3.FileUploadWithTag, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.FileUploadWithTag, + type: ComponentTypeV3.FileUploadWithTag, description: '', displayMode: 'list', hasCustomFileEndings: false, @@ -234,60 +234,60 @@ export const formItemConfigs: FormItemConfigs = { }, icon: PaperclipIcon, }, - [ComponentType.Grid]: { - name: ComponentType.Grid, + [ComponentTypeV3.Grid]: { + name: ComponentTypeV3.Grid, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Grid, + type: ComponentTypeV3.Grid, propertyPath: 'definitions/gridComponent', rows: [], }, icon: TableIcon, }, - [ComponentType.Group]: { - name: ComponentType.Group, + [ComponentTypeV3.Group]: { + name: ComponentTypeV3.Group, itemType: LayoutItemType.Container, defaultProperties: { id: '', itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, propertyPath: 'definitions/groupComponent', }, icon: Group, - validChildTypes: Object.values(ComponentType), + validChildTypes: Object.values(ComponentTypeV3), }, - [ComponentType.Header]: { - name: ComponentType.Header, + [ComponentTypeV3.Header]: { + name: ComponentTypeV3.Header, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Header, + type: ComponentTypeV3.Header, size: 'L', propertyPath: 'definitions/headerComponent', }, icon: Title, }, - [ComponentType.IFrame]: { - name: ComponentType.IFrame, + [ComponentTypeV3.IFrame]: { + name: ComponentTypeV3.IFrame, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.IFrame, + type: ComponentTypeV3.IFrame, sandbox: {}, }, icon: PresentationIcon, }, - [ComponentType.Image]: { - name: ComponentType.Image, + [ComponentTypeV3.Image]: { + name: ComponentTypeV3.Image, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Image, + type: ComponentTypeV3.Image, image: { src: {}, width: '100%', @@ -297,80 +297,80 @@ export const formItemConfigs: FormItemConfigs = { }, icon: ImageIcon, }, - [ComponentType.Input]: { - name: ComponentType.Input, + [ComponentTypeV3.Input]: { + name: ComponentTypeV3.Input, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Input, + type: ComponentTypeV3.Input, dataModelBindings: {}, required: true, propertyPath: 'definitions/inputComponent', }, icon: ShortText, }, - [ComponentType.InstanceInformation]: { - name: ComponentType.InstanceInformation, + [ComponentTypeV3.InstanceInformation]: { + name: ComponentTypeV3.InstanceInformation, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.InstanceInformation, + type: ComponentTypeV3.InstanceInformation, propertyPath: 'definitions/instanceInformationComponent', }, icon: InformationSquareIcon, }, - [ComponentType.InstantiationButton]: { - name: ComponentType.InstantiationButton, + [ComponentTypeV3.InstantiationButton]: { + name: ComponentTypeV3.InstantiationButton, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.InstantiationButton, + type: ComponentTypeV3.InstantiationButton, }, icon: FingerButtonIcon, }, - [ComponentType.Likert]: { - name: ComponentType.Likert, + [ComponentTypeV3.Likert]: { + name: ComponentTypeV3.Likert, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Likert, + type: ComponentTypeV3.Likert, dataModelBindings: {}, propertyPath: 'definitions/radioAndCheckboxComponents', }, icon: Likert, }, - [ComponentType.Link]: { - name: ComponentType.Link, + [ComponentTypeV3.Link]: { + name: ComponentTypeV3.Link, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Link, + type: ComponentTypeV3.Link, }, icon: LinkIcon, }, - [ComponentType.List]: { - name: ComponentType.List, + [ComponentTypeV3.List]: { + name: ComponentTypeV3.List, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.List, + type: ComponentTypeV3.List, propertyPath: 'definitions/listComponent', }, icon: TasklistIcon, }, - [ComponentType.Map]: { - name: ComponentType.Map, + [ComponentTypeV3.Map]: { + name: ComponentTypeV3.Map, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Map, + type: ComponentTypeV3.Map, dataModelBindings: {}, centerLocation: { latitude: 0, @@ -382,13 +382,13 @@ export const formItemConfigs: FormItemConfigs = { }, icon: PinIcon, }, - [ComponentType.MultipleSelect]: { - name: ComponentType.MultipleSelect, + [ComponentTypeV3.MultipleSelect]: { + name: ComponentTypeV3.MultipleSelect, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.MultipleSelect, + type: ComponentTypeV3.MultipleSelect, dataModelBindings: {}, optionsId: '', required: true, @@ -396,93 +396,93 @@ export const formItemConfigs: FormItemConfigs = { }, icon: Select, }, - [ComponentType.NavigationBar]: { - name: ComponentType.NavigationBar, + [ComponentTypeV3.NavigationBar]: { + name: ComponentTypeV3.NavigationBar, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.NavigationBar, + type: ComponentTypeV3.NavigationBar, propertyPath: 'definitions/navigationBarComponent', }, icon: NavBar, }, - [ComponentType.NavigationButtons]: { - name: ComponentType.NavigationButtons, + [ComponentTypeV3.NavigationButtons]: { + name: ComponentTypeV3.NavigationButtons, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, onClickAction: () => {}, propertyPath: 'definitions/navigationButtonsComponent', }, icon: FingerButtonIcon, }, - [ComponentType.Panel]: { - name: ComponentType.Panel, + [ComponentTypeV3.Panel]: { + name: ComponentTypeV3.Panel, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Panel, + type: ComponentTypeV3.Panel, variant: FormPanelVariant.Info, showIcon: true, propertyPath: 'definitions/panelComponent', }, icon: FileTextIcon, }, - [ComponentType.Paragraph]: { - name: ComponentType.Paragraph, + [ComponentTypeV3.Paragraph]: { + name: ComponentTypeV3.Paragraph, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }, icon: Paragraph, }, - [ComponentType.PrintButton]: { - name: ComponentType.PrintButton, + [ComponentTypeV3.PrintButton]: { + name: ComponentTypeV3.PrintButton, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.PrintButton, + type: ComponentTypeV3.PrintButton, }, icon: FingerButtonIcon, }, - [ComponentType.RadioButtons]: { - name: ComponentType.RadioButtons, + [ComponentTypeV3.RadioButtons]: { + name: ComponentTypeV3.RadioButtons, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, dataModelBindings: {}, required: true, propertyPath: 'definitions/radioAndCheckboxComponents', }, icon: RadioButton, }, - [ComponentType.Summary]: { - name: ComponentType.Summary, + [ComponentTypeV3.Summary]: { + name: ComponentTypeV3.Summary, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.Summary, + type: ComponentTypeV3.Summary, propertyPath: 'definitions/summaryComponent', }, icon: FileTextIcon, }, - [ComponentType.TextArea]: { - name: ComponentType.TextArea, + [ComponentTypeV3.TextArea]: { + name: ComponentTypeV3.TextArea, itemType: LayoutItemType.Component, defaultProperties: { id: '', itemType: 'COMPONENT', - type: ComponentType.TextArea, + type: ComponentTypeV3.TextArea, dataModelBindings: {}, required: true, propertyPath: 'definitions/textAreaComponent', @@ -491,52 +491,52 @@ export const formItemConfigs: FormItemConfigs = { }, }; -export const advancedItems: FormItemConfigs[ComponentType][] = [ - formItemConfigs[ComponentType.AddressComponent], - formItemConfigs[ComponentType.AttachmentList], - formItemConfigs[ComponentType.Group], - formItemConfigs[ComponentType.Grid], - formItemConfigs[ComponentType.NavigationBar], - formItemConfigs[ComponentType.Map], - formItemConfigs[ComponentType.ButtonGroup], - formItemConfigs[ComponentType.Accordion], - formItemConfigs[ComponentType.AccordionGroup], - formItemConfigs[ComponentType.List], +export const advancedItems: FormItemConfigs[ComponentTypeV3][] = [ + formItemConfigs[ComponentTypeV3.AddressComponent], + formItemConfigs[ComponentTypeV3.AttachmentList], + formItemConfigs[ComponentTypeV3.Group], + formItemConfigs[ComponentTypeV3.Grid], + formItemConfigs[ComponentTypeV3.NavigationBar], + formItemConfigs[ComponentTypeV3.Map], + formItemConfigs[ComponentTypeV3.ButtonGroup], + formItemConfigs[ComponentTypeV3.Accordion], + formItemConfigs[ComponentTypeV3.AccordionGroup], + formItemConfigs[ComponentTypeV3.List], ]; -export const schemaComponents: FormItemConfigs[ComponentType][] = [ - formItemConfigs[ComponentType.Input], - formItemConfigs[ComponentType.TextArea], - formItemConfigs[ComponentType.Checkboxes], - formItemConfigs[ComponentType.RadioButtons], - formItemConfigs[ComponentType.Dropdown], - formItemConfigs[ComponentType.MultipleSelect], - formItemConfigs[ComponentType.Likert], - formItemConfigs[ComponentType.Datepicker], - formItemConfigs[ComponentType.FileUpload], - formItemConfigs[ComponentType.FileUploadWithTag], - formItemConfigs[ComponentType.Button], - formItemConfigs[ComponentType.NavigationButtons], - formItemConfigs[ComponentType.PrintButton], - formItemConfigs[ComponentType.InstantiationButton], - formItemConfigs[ComponentType.ActionButton], - formItemConfigs[ComponentType.Image], - formItemConfigs[ComponentType.Link], - formItemConfigs[ComponentType.IFrame], - formItemConfigs[ComponentType.InstanceInformation], - formItemConfigs[ComponentType.Summary], +export const schemaComponents: FormItemConfigs[ComponentTypeV3][] = [ + formItemConfigs[ComponentTypeV3.Input], + formItemConfigs[ComponentTypeV3.TextArea], + formItemConfigs[ComponentTypeV3.Checkboxes], + formItemConfigs[ComponentTypeV3.RadioButtons], + formItemConfigs[ComponentTypeV3.Dropdown], + formItemConfigs[ComponentTypeV3.MultipleSelect], + formItemConfigs[ComponentTypeV3.Likert], + formItemConfigs[ComponentTypeV3.Datepicker], + formItemConfigs[ComponentTypeV3.FileUpload], + formItemConfigs[ComponentTypeV3.FileUploadWithTag], + formItemConfigs[ComponentTypeV3.Button], + formItemConfigs[ComponentTypeV3.NavigationButtons], + formItemConfigs[ComponentTypeV3.PrintButton], + formItemConfigs[ComponentTypeV3.InstantiationButton], + formItemConfigs[ComponentTypeV3.ActionButton], + formItemConfigs[ComponentTypeV3.Image], + formItemConfigs[ComponentTypeV3.Link], + formItemConfigs[ComponentTypeV3.IFrame], + formItemConfigs[ComponentTypeV3.InstanceInformation], + formItemConfigs[ComponentTypeV3.Summary], ]; -export const textComponents: FormItemConfigs[ComponentType][] = [ - formItemConfigs[ComponentType.Header], - formItemConfigs[ComponentType.Paragraph], - formItemConfigs[ComponentType.Panel], - formItemConfigs[ComponentType.Alert], +export const textComponents: FormItemConfigs[ComponentTypeV3][] = [ + formItemConfigs[ComponentTypeV3.Header], + formItemConfigs[ComponentTypeV3.Paragraph], + formItemConfigs[ComponentTypeV3.Panel], + formItemConfigs[ComponentTypeV3.Alert], ]; -export const confOnScreenComponents: FormItemConfigs[ComponentType][] = [ - formItemConfigs[ComponentType.Header], - formItemConfigs[ComponentType.Paragraph], - formItemConfigs[ComponentType.AttachmentList], - formItemConfigs[ComponentType.Image], +export const confOnScreenComponents: FormItemConfigs[ComponentTypeV3][] = [ + formItemConfigs[ComponentTypeV3.Header], + formItemConfigs[ComponentTypeV3.Paragraph], + formItemConfigs[ComponentTypeV3.AttachmentList], + formItemConfigs[ComponentTypeV3.Image], ]; diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddFormContainerMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddFormContainerMutation.test.ts index 6bfc9dfa116..35f3596bb2a 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddFormContainerMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddFormContainerMutation.test.ts @@ -5,7 +5,7 @@ import { waitFor } from '@testing-library/react'; import type { AddFormContainerMutationArgs } from './useAddFormContainerMutation'; import { useAddFormContainerMutation } from './useAddFormContainerMutation'; import type { FormContainer } from '../../types/FormContainer'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { layout1NameMock } from '../../testing/layoutMock'; // Test data: @@ -16,7 +16,7 @@ const selectedLayoutSet = 'test-layout-set'; const container: FormContainer = { id, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const defaultArgs: AddFormContainerMutationArgs = { container, @@ -28,11 +28,11 @@ jest.mock('../../utils/generateId', () => ({ })); describe('useAddFormContainerMutation', () => { - it('Calls saveFormLayout with correct arguments and payload', async () => { + it('Calls saveFormLayoutV3 with correct arguments and payload', async () => { const { result } = await renderAddFormContainerMutation(); await result.current.mutateAsync(defaultArgs); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, @@ -42,7 +42,7 @@ describe('useAddFormContainerMutation', () => { layout: expect.arrayContaining([ { id, - type: ComponentType.Group, + type: ComponentTypeV3.Group, children: [], }, ]), diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.test.ts index d91aaf6d539..9a684e31e8d 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.test.ts @@ -5,7 +5,7 @@ import { appStateMock, formDesignerMock } from '../../testing/stateMocks'; import { waitFor } from '@testing-library/react'; import type { AddFormItemMutationArgs } from './useAddItemToLayoutMutation'; import { useAddItemToLayoutMutation } from './useAddItemToLayoutMutation'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ApplicationAttachmentMetadata } from 'app-shared/types/ApplicationAttachmentMetadata'; import type { IAppState } from '../../types/global'; import { externalLayoutsMock, layoutSetsMock } from '../../testing/layoutMock'; @@ -19,7 +19,7 @@ const selectedLayoutSet = 'test-layout-set'; const id = 'component-id'; const defaultArgs: AddFormItemMutationArgs = { - componentType: ComponentType.Paragraph, + componentType: ComponentTypeV3.Paragraph, newId: id, parentId: 'Container-1', index: 0, @@ -63,21 +63,21 @@ describe('useAddItemToLayoutMutation', () => { it('Adds attachment metadata when component type is fileUpload', async () => { const { result } = renderAddItemToLayoutMutation(selectedLayoutSet); - result.current.mutate({ ...defaultArgs, componentType: ComponentType.FileUpload }); + result.current.mutate({ ...defaultArgs, componentType: ComponentTypeV3.FileUpload }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledTimes(1); }); it('Adds attachment metadata when component type is fileUploadWithTag', async () => { const { result } = renderAddItemToLayoutMutation(selectedLayoutSet); - result.current.mutate({ ...defaultArgs, componentType: ComponentType.FileUploadWithTag }); + result.current.mutate({ ...defaultArgs, componentType: ComponentTypeV3.FileUploadWithTag }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledTimes(1); }); it('Adds correct taskId to attachment metadata when component type is fileUpload and selectedLayoutSet is test-layout-set-2', async () => { const { result } = renderAddItemToLayoutMutation('test-layout-set-2'); - result.current.mutate({ ...defaultArgs, componentType: ComponentType.FileUpload }); + result.current.mutate({ ...defaultArgs, componentType: ComponentTypeV3.FileUpload }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, { ...applicationAttachmentMetaDataMock, @@ -87,7 +87,7 @@ describe('useAddItemToLayoutMutation', () => { it('Adds Task_1 to attachment metadata when component type is fileUpload and selectedLayoutSet is undefined', async () => { const { result } = renderAddItemToLayoutMutation(undefined); - result.current.mutate({ ...defaultArgs, componentType: ComponentType.FileUpload }); + result.current.mutate({ ...defaultArgs, componentType: ComponentTypeV3.FileUpload }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, { ...applicationAttachmentMetaDataMock, diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.ts index 6273e55f3ca..dbfabdca486 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddItemToLayoutMutation.ts @@ -1,6 +1,6 @@ import { useSelectedFormLayoutWithName } from '../useFormLayoutsSelector'; import { useMutation } from '@tanstack/react-query'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { useFormLayoutMutation } from './useFormLayoutMutation'; import { useAddAppAttachmentMetadataMutation } from './useAddAppAttachmentMetadataMutation'; import type { FormFileUploaderComponent } from '../../types/FormComponent'; @@ -9,7 +9,7 @@ import { useLayoutSetsQuery } from '../queries/useLayoutSetsQuery'; import { TASKID_FOR_STATELESS_APPS } from 'app-shared/constants'; export interface AddFormItemMutationArgs { - componentType: ComponentType; + componentType: ComponentTypeV3; newId: string; parentId: string; index: number; @@ -29,8 +29,8 @@ export const useAddItemToLayoutMutation = (org: string, app: string, layoutSetNa return formLayoutsMutation.mutateAsync(updatedLayout).then(() => { if ( - componentType === ComponentType.FileUpload || - componentType === ComponentType.FileUploadWithTag + componentType === ComponentTypeV3.FileUpload || + componentType === ComponentTypeV3.FileUploadWithTag ) { const taskId = layoutSets ? layoutSets?.sets.find((set) => set.id === layoutSetName)?.tasks[0] diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.test.ts index 36c6d69268d..fd2aa3bf6ec 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.test.ts @@ -5,9 +5,9 @@ import { useAddLayoutMutation } from './useAddLayoutMutation'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { waitFor } from '@testing-library/react'; import { useFormLayoutSettingsQuery } from '../queries/useFormLayoutSettingsQuery'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { externalLayoutsMock } from '../../testing/layoutMock'; -import type { FormLayoutsResponse } from 'app-shared/types/api'; +import type { FormLayoutsResponseV3 } from 'app-shared/types/api'; import type { ILayoutSettings } from 'app-shared/types/global'; // Test data: @@ -20,7 +20,7 @@ const defaultArgs: AddLayoutMutationArgs = { layoutName }; describe('useAddLayoutMutation', () => { afterEach(jest.clearAllMocks); - it('Calls saveFormLayout with new layout', async () => { + it('Calls saveFormLayoutV3 with new layout', async () => { await renderAndWaitForData(); const addLayoutResult = renderHookWithMockStore()(() => @@ -31,7 +31,7 @@ describe('useAddLayoutMutation', () => { await waitFor(() => expect(addLayoutResult.current.isSuccess).toBe(true)); - expect(queriesMock.saveFormLayout).toHaveBeenLastCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenLastCalledWith( org, app, layoutName, @@ -39,14 +39,14 @@ describe('useAddLayoutMutation', () => { { $schema: 'https://altinncdn.no/schemas/json/layout/layout.schema.v1.json', data: { - layout: [expect.objectContaining({ type: ComponentType.NavigationButtons })], + layout: [expect.objectContaining({ type: ComponentTypeV3.NavigationButtons })], hidden: undefined, }, }, ); }); - it('Calls saveFormLayout with new layout for receiptPage', async () => { + it('Calls saveFormLayoutV3 with new layout for receiptPage', async () => { await renderAndWaitForData(); const addLayoutResult = renderHookWithMockStore()(() => @@ -60,7 +60,7 @@ describe('useAddLayoutMutation', () => { await waitFor(() => expect(addLayoutResult.current.isSuccess).toBe(true)); - expect(queriesMock.saveFormLayout).toHaveBeenLastCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenLastCalledWith( org, app, formLayoutSettingsMock.receiptLayoutName, @@ -77,15 +77,15 @@ describe('useAddLayoutMutation', () => { }); const renderAndWaitForData = 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/hooks/mutations/useAddLayoutMutation.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.ts index 1887cdd9a26..b89a87a20ee 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddLayoutMutation.ts @@ -5,7 +5,7 @@ import { FormLayoutActions } from '../../features/formDesigner/formLayout/formLa import { deepCopy } from 'app-shared/pure'; import { createEmptyLayout } from '../../utils/formLayoutUtils'; import type { IInternalLayout } from '../../types/global'; -import type { ExternalFormLayout } from 'app-shared/types/api/FormLayoutsResponse'; +import type { ExternalFormLayoutV3 } from 'app-shared/types/api/FormLayoutsResponseV3'; import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { QueryKey } from 'app-shared/types/QueryKey'; import { useFormLayoutSettingsMutation } from './useFormLayoutSettingsMutation'; @@ -20,7 +20,7 @@ export interface AddLayoutMutationArgs { } export const useAddLayoutMutation = (org: string, app: string, layoutSetName: string) => { - const { saveFormLayout } = useServicesContext(); + const { saveFormLayoutV3 } = useServicesContext(); const formLayoutsQuery = useFormLayoutsQuery(org, app, layoutSetName); const formLayoutSettingsQuery = useFormLayoutSettingsQuery(org, app, layoutSetName); const formLayoutSettingsMutation = useFormLayoutSettingsMutation(org, app, layoutSetName); @@ -28,8 +28,8 @@ export const useAddLayoutMutation = (org: string, app: string, layoutSetName: st const queryClient = useQueryClient(); const save = async (updatedLayoutName: string, updatedLayout: IInternalLayout) => { - const convertedLayout: ExternalFormLayout = internalLayoutToExternal(updatedLayout); - return await saveFormLayout(org, app, updatedLayoutName, layoutSetName, convertedLayout); + const convertedLayout: ExternalFormLayoutV3 = internalLayoutToExternal(updatedLayout); + return await saveFormLayoutV3(org, app, updatedLayoutName, layoutSetName, convertedLayout); }; return useMutation({ diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddWidgetMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddWidgetMutation.test.ts index 9e3e0a3310e..e220fa56e6d 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddWidgetMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useAddWidgetMutation.test.ts @@ -5,7 +5,7 @@ import { waitFor } from '@testing-library/react'; import type { AddWidgetMutationArgs } from './useAddWidgetMutation'; import { useAddWidgetMutation } from './useAddWidgetMutation'; import type { IWidget, IWidgetTexts } from '../../types/global'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ITextResource } from 'app-shared/types/global'; import { useTextResourcesQuery } from 'app-shared/hooks/queries/useTextResourcesQuery'; @@ -13,7 +13,7 @@ import { useTextResourcesQuery } from 'app-shared/hooks/queries/useTextResources const org = 'org'; const app = 'app'; const selectedLayoutSet = 'test-layout-set'; -const displayName = ComponentType.TextArea; +const displayName = ComponentTypeV3.TextArea; const language = 'nb'; const textId = 'testid'; const textValue = 'testvalue'; @@ -32,7 +32,7 @@ describe('useAddWidgetMutation', () => { it('Saves layout', async () => { const { result } = await renderAddWidgetMutation(); await result.current.mutateAsync(defaultArgs); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); }); it('Saves text resources', async () => { diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.test.ts index 684f1675368..5b4a52fb821 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.test.ts @@ -14,8 +14,8 @@ describe('useDeleteFormComponentMutation', () => { it('Should save layout without deleted component', async () => { const { result } = await renderDeleteFormComponentsMutation(); await result.current.mutateAsync(component2IdMock); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.ts index 40607d92cbc..ff347c5cd68 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormComponentMutation.ts @@ -1,6 +1,6 @@ import { useSelectedFormLayoutWithName } from '../useFormLayoutsSelector'; import { useMutation } from '@tanstack/react-query'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { useFormLayoutMutation } from './useFormLayoutMutation'; import { useDeleteAppAttachmentMetadataMutation } from './useDeleteAppAttachmentMetadataMutation'; import { removeComponent } from '../../utils/formLayoutUtils'; @@ -14,8 +14,8 @@ export const useDeleteFormComponentMutation = (org: string, app: string, layoutS const component = layout.components[id]; const updatedLayout = removeComponent(layout, id); if ( - component?.type === ComponentType.FileUpload || - component?.type === ComponentType.FileUploadWithTag + component?.type === ComponentTypeV3.FileUpload || + component?.type === ComponentTypeV3.FileUploadWithTag ) { await deleteAppAttachmentMetadataMutation.mutateAsync(id); } diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormContainerMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormContainerMutation.test.ts index e70ef1ec139..439fe260205 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormContainerMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteFormContainerMutation.test.ts @@ -4,7 +4,7 @@ import { waitFor } from '@testing-library/react'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { useDeleteFormContainerMutation } from './useDeleteFormContainerMutation'; import { container1IdMock, externalLayoutsMock, layout1NameMock } from '../../testing/layoutMock'; -import type { FormLayoutsResponse } from 'app-shared/types/api'; +import type { FormLayoutsResponseV3 } from 'app-shared/types/api'; // Test data: const org = 'org'; @@ -16,8 +16,8 @@ describe('useDeleteFormContainerMutation', () => { it('Should save layout without deleted container', async () => { const { result } = await renderDeleteFormContainerMutation(); await result.current.mutateAsync(id); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, @@ -32,12 +32,12 @@ describe('useDeleteFormContainerMutation', () => { }); const renderDeleteFormContainerMutation = async () => { - const getFormLayouts = jest + const getFormLayoutsV3 = jest .fn() - .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); const formLayoutsResult = renderHookWithMockStore( {}, - { getFormLayouts }, + { getFormLayoutsV3 }, )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); return renderHookWithMockStore()(() => diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteLayoutMutation.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteLayoutMutation.ts index e4a5542add8..8a6e97bda74 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteLayoutMutation.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useDeleteLayoutMutation.ts @@ -10,14 +10,14 @@ import type { ILayoutSettings } from 'app-shared/types/global'; import { useFormLayoutSettingsMutation } from './useFormLayoutSettingsMutation'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { addOrRemoveNavigationButtons } from '../../utils/formLayoutsUtils'; -import type { ExternalFormLayout } from 'app-shared/types/api/FormLayoutsResponse'; +import type { ExternalFormLayoutV3 } from 'app-shared/types/api/FormLayoutsResponseV3'; import { useAddLayoutMutation } from './useAddLayoutMutation'; import { useText } from '../useText'; import { selectedLayoutNameSelector } from '../../selectors/formLayoutSelectors'; import { internalLayoutToExternal } from '../../converters/formLayoutConverters'; export const useDeleteLayoutMutation = (org: string, app: string, layoutSetName: string) => { - const { deleteFormLayout, saveFormLayout } = useServicesContext(); + const { deleteFormLayout, saveFormLayoutV3 } = useServicesContext(); const { data: formLayouts } = useFormLayoutsQuery(org, app, layoutSetName); const { data: formLayoutSettings } = useFormLayoutSettingsQuery(org, app, layoutSetName); @@ -30,8 +30,8 @@ export const useDeleteLayoutMutation = (org: string, app: string, layoutSetName: const queryClient = useQueryClient(); const saveLayout = async (updatedLayoutName: string, updatedLayout: IInternalLayout) => { - const convertedLayout: ExternalFormLayout = internalLayoutToExternal(updatedLayout); - return await saveFormLayout(org, app, updatedLayoutName, layoutSetName, convertedLayout); + const convertedLayout: ExternalFormLayoutV3 = internalLayoutToExternal(updatedLayout); + return await saveFormLayoutV3(org, app, updatedLayoutName, layoutSetName, convertedLayout); }; return useMutation({ diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.test.tsx b/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.test.tsx index f9505367ca5..b152d147743 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.test.tsx +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.test.tsx @@ -3,7 +3,7 @@ import { queryClientMock } from 'app-shared/mocks/queryClientMock'; import { renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutMutation } from './useFormLayoutMutation'; import type { IInternalLayout } from '../../types/global'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { baseContainerIdMock } from '../../testing/layoutMock'; import type { AppContextProps } from '../../AppContext'; import type { RefObject } from 'react'; @@ -15,7 +15,7 @@ const app = 'app'; const layoutName = 'layoutName'; const selectedLayoutSet = 'test-layout-set'; const componentId = 'component1'; -const componentType = ComponentType.TextArea; +const componentType = ComponentTypeV3.TextArea; const baseContainerId = baseContainerIdMock; const containerId = 'container1'; const newLayout: IInternalLayout = { @@ -39,7 +39,7 @@ const newLayout: IInternalLayout = { id: containerId, itemType: 'CONTAINER', pageIndex: null, - type: ComponentType.Group, + type: ComponentTypeV3.Group, }, }, order: { @@ -53,10 +53,10 @@ const newLayout: IInternalLayout = { describe('useFormLayoutMutation', () => { afterEach(jest.clearAllMocks); - it('Calls saveFormLayout with correct arguments and payload', async () => { + it('Calls saveFormLayoutV3 with correct arguments and payload', async () => { await renderAndMutate(newLayout); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layoutName, @@ -66,7 +66,7 @@ describe('useFormLayoutMutation', () => { layout: [ { id: containerId, - type: ComponentType.Group, + type: ComponentTypeV3.Group, children: [componentId], }, { diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.ts index c358219a101..fce1cdac5b5 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutMutation.ts @@ -3,7 +3,7 @@ import type { IFormLayouts, IInternalLayout } from '../../types/global'; import { QueryKey } from 'app-shared/types/QueryKey'; import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { usePreviewConnection } from 'app-shared/providers/PreviewConnectionContext'; -import type { ExternalFormLayout } from 'app-shared/types/api/FormLayoutsResponse'; +import type { ExternalFormLayoutV3 } from 'app-shared/types/api/FormLayoutsResponseV3'; import { useAppContext } from '../useAppContext'; import { internalLayoutToExternal } from '../../converters/formLayoutConverters'; @@ -14,14 +14,14 @@ export const useFormLayoutMutation = ( layoutSetName: string, ) => { const previewConnection = usePreviewConnection(); - const { saveFormLayout } = useServicesContext(); + const { saveFormLayoutV3 } = useServicesContext(); const queryClient = useQueryClient(); const { previewIframeRef } = useAppContext(); return useMutation({ mutationFn: (layout: IInternalLayout) => { - const convertedLayout: ExternalFormLayout = internalLayoutToExternal(layout); - return saveFormLayout(org, app, layoutName, layoutSetName, convertedLayout).then( + const convertedLayout: ExternalFormLayoutV3 = internalLayoutToExternal(layout); + return saveFormLayoutV3(org, app, layoutName, layoutSetName, convertedLayout).then( () => layout, ); }, diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts index 5e72e54af57..e65bf648ca5 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts @@ -9,7 +9,7 @@ const app = 'app'; const selectedLayoutSet = 'test-layout-set'; describe('useFormLayoutSettingsMutation', () => { - it('Calls saveFormLayoutSettings with correct arguments and payload', async () => { + it('Calls saveFormLayoutV3Settings with correct arguments and payload', async () => { const settingsResult = renderHookWithMockStore()(() => useFormLayoutSettingsMutation(org, app, selectedLayoutSet), ).renderHookResult.result; diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.test.ts index a6000d59f1f..92cb3906bea 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.test.ts @@ -1,7 +1,7 @@ import { queriesMock } from 'app-shared/mocks/queriesMock'; import { queryClientMock } from 'app-shared/mocks/queryClientMock'; import { renderHookWithMockStore } from '../../testing/mocks'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { UpdateFormComponentMutationArgs } from './useUpdateFormComponentMutation'; import { useUpdateFormComponentMutation } from './useUpdateFormComponentMutation'; import { component1IdMock, externalLayoutsMock, layout1NameMock } from '../../testing/layoutMock'; @@ -22,12 +22,12 @@ const app = 'app'; const selectedLayoutName = 'Side1'; const selectedLayoutSet = 'test-layout-set'; const id = component1IdMock; -const type = ComponentType.TextArea; +const type = ComponentTypeV3.TextArea; const dataModelBindings: IDataModelBindings = {}; const updatedComponent: FormComponent = { id, itemType: 'COMPONENT', - type: ComponentType.TextArea, + type: ComponentTypeV3.TextArea, dataModelBindings, }; const defaultArgs: UpdateFormComponentMutationArgs = { id, updatedComponent }; @@ -44,8 +44,8 @@ describe('useUpdateFormComponentMutation', () => { await updateFormComponentResult.current.mutateAsync(defaultArgs); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, @@ -88,7 +88,7 @@ describe('useUpdateFormComponentMutation', () => { maxFileSizeInMB: 100, maxNumberOfAttachments: 2, minNumberOfAttachments: 1, - type: ComponentType.FileUpload, + type: ComponentTypeV3.FileUpload, }; const args: UpdateFormComponentMutationArgs = { ...defaultArgs, @@ -104,7 +104,7 @@ describe('useUpdateFormComponentMutation', () => { useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet), ).renderHookResult.result; - for (const componentType of [ComponentType.RadioButtons, ComponentType.Checkboxes]) { + for (const componentType of [ComponentTypeV3.RadioButtons, ComponentTypeV3.Checkboxes]) { for (const optionKind of ['options', 'optionsId']) { const optionsProp = optionKind === 'options' ? { options: [] } : { optionsId: 'test' }; const newComponent = { @@ -118,7 +118,7 @@ describe('useUpdateFormComponentMutation', () => { updatedComponent: newComponent, }; await updateFormComponentResult.current.mutateAsync(args); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.ts index dc1abfa1045..16aed0be720 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentMutation.ts @@ -1,6 +1,6 @@ import type { IInternalLayout } from '../../types/global'; import { useMutation } from '@tanstack/react-query'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { useAddAppAttachmentMetadataMutation } from './useAddAppAttachmentMetadataMutation'; import { useDeleteAppAttachmentMetadataMutation } from './useDeleteAppAttachmentMetadataMutation'; import { useUpdateAppAttachmentMetadataMutation } from './useUpdateAppAttachmentMetadataMutation'; @@ -54,8 +54,8 @@ export const useUpdateFormComponentMutation = ( parentContainerOrder[containerIndex] = newId; } else { if ( - components[id]?.type === ComponentType.RadioButtons || - components[id]?.type === ComponentType.Checkboxes + components[id]?.type === ComponentTypeV3.RadioButtons || + components[id]?.type === ComponentTypeV3.Checkboxes ) { delete components[id].options; delete components[id].optionsId; @@ -66,8 +66,8 @@ export const useUpdateFormComponentMutation = ( return saveLayout(updatedLayout) .then(async (data) => { if ( - updatedComponent.type === ComponentType.FileUpload || - updatedComponent.type === ComponentType.FileUploadWithTag + updatedComponent.type === ComponentTypeV3.FileUpload || + updatedComponent.type === ComponentTypeV3.FileUploadWithTag ) { // Todo: Consider handling this in the backend const taskId = layoutSets diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts index 928e9222801..0e8fc546228 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts @@ -12,7 +12,7 @@ import { layout1NameMock, layoutMock, } from '../../testing/layoutMock'; -import type { FormLayoutsResponse } from 'app-shared/types/api'; +import type { FormLayoutsResponseV3 } from 'app-shared/types/api'; // Test data: const org = 'org'; @@ -35,8 +35,8 @@ describe('useUpdateFormComponentOrderMutation', () => { }; await componentOrderResult.current.mutateAsync(newOrder); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, @@ -55,12 +55,12 @@ describe('useUpdateFormComponentOrderMutation', () => { }); const renderAndWaitForData = async () => { - const getFormLayouts = jest + const getFormLayoutsV3 = jest .fn() - .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); const formLayoutsResult = renderHookWithMockStore( {}, - { getFormLayouts }, + { getFormLayoutsV3 }, )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); }; diff --git a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormContainerMutation.test.ts b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormContainerMutation.test.ts index c66130944d2..69ebc56cd59 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormContainerMutation.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/mutations/useUpdateFormContainerMutation.test.ts @@ -13,9 +13,9 @@ import { layout1NameMock, } from '../../testing/layoutMock'; import { ruleConfig as ruleConfigMock } from '../../testing/ruleConfigMock'; -import type { FormLayoutsResponse } from 'app-shared/types/api'; +import type { FormLayoutsResponseV3 } from 'app-shared/types/api'; import type { RuleConfig } from 'app-shared/types/RuleConfig'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; // Test data: const org = 'org'; @@ -26,7 +26,7 @@ const maxCount = 2; const updatedContainer: FormContainer = { id: 'newId', itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, maxCount, }; const id = container1IdMock; @@ -42,8 +42,8 @@ describe('useUpdateFormContainerMutation', () => { await updateFormContainerResult.current.mutateAsync(mutationArgs); - expect(queriesMock.saveFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledTimes(1); + expect(queriesMock.saveFormLayoutV3).toHaveBeenCalledWith( org, app, layout1NameMock, @@ -64,15 +64,15 @@ describe('useUpdateFormContainerMutation', () => { }); const renderAndWaitForData = async () => { - const getFormLayouts = jest + const getFormLayoutsV3 = jest .fn() - .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); const getRuleConfig = jest .fn() .mockImplementation(() => Promise.resolve(ruleConfigMock)); const formLayoutsResult = renderHookWithMockStore( {}, - { getFormLayouts }, + { getFormLayoutsV3 }, )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; const ruleConfigResult = renderHookWithMockStore( {}, diff --git a/frontend/packages/ux-editor-v3/src/hooks/queries/useFormLayoutsQuery.ts b/frontend/packages/ux-editor-v3/src/hooks/queries/useFormLayoutsQuery.ts index 18c130ed242..98c43436a80 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/queries/useFormLayoutsQuery.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/queries/useFormLayoutsQuery.ts @@ -12,12 +12,12 @@ export const useFormLayoutsQuery = ( app: string, layoutSetName: string, ): UseQueryResult => { - const { getFormLayouts } = useServicesContext(); + const { getFormLayoutsV3 } = useServicesContext(); const dispatch = useDispatch(); return useQuery({ queryKey: [QueryKey.FormLayouts, org, app, layoutSetName], queryFn: () => - getFormLayouts(org, app, layoutSetName).then((formLayouts) => { + getFormLayoutsV3(org, app, layoutSetName).then((formLayouts) => { const { convertedLayouts, invalidLayouts } = convertExternalLayoutsToInternalFormat(formLayouts); dispatch(FormLayoutActions.setInvalidLayouts(invalidLayouts)); diff --git a/frontend/packages/ux-editor-v3/src/hooks/queries/useWidgetsQuery.test.ts b/frontend/packages/ux-editor-v3/src/hooks/queries/useWidgetsQuery.test.ts index addac8dd39d..577191abcd1 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/queries/useWidgetsQuery.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/queries/useWidgetsQuery.test.ts @@ -3,7 +3,7 @@ import { waitFor } from '@testing-library/react'; import { useWidgetsQuery } from './useWidgetsQuery'; import type { WidgetSettingsResponse } from 'app-shared/types/widgetTypes'; import type { IWidget } from '../../types/global'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; // Test data: const org = 'org'; @@ -11,8 +11,8 @@ const app = 'app'; const widgetUrl1 = 'url1'; const widgetUrl2 = 'url2'; const widgetUrls = [widgetUrl1, widgetUrl2]; -const widget1Name = ComponentType.Header; -const widget2Name = ComponentType.Paragraph; +const widget1Name = ComponentTypeV3.Header; +const widget2Name = ComponentTypeV3.Paragraph; const widget1: IWidget = { displayName: widget1Name, components: [], texts: [] }; const widget2: IWidget = { displayName: widget2Name, components: [], texts: [] }; diff --git a/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.test.ts b/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.test.ts index 94b1b5a54b9..6a483489787 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.test.ts @@ -1,13 +1,13 @@ import { renderHookWithMockStore } from '../testing/mocks'; import { useComponentErrorMessage } from './useComponentErrorMessage'; import type { FormComponent } from '../types/FormComponent'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { textMock } from '../../../../testing/mocks/i18nMock'; describe('useComponentErrorMessage', () => { it('Returns an error message from the translation file if the component is invalid', () => { const invalidComponent: FormComponent = { - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, options: [ { label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, @@ -24,7 +24,7 @@ describe('useComponentErrorMessage', () => { it('Returns null if the component is valid', () => { const validComponent: FormComponent = { - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, options: [ { label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, diff --git a/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.ts b/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.ts index 8f7c542a85c..8392484d297 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useComponentErrorMessage.ts @@ -1,7 +1,7 @@ import type { FormComponent } from '../types/FormComponent'; import { useTranslation } from 'react-i18next'; import { useValidateComponent } from './useValidateComponent'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; /** * Returns an error message for the given component, or null if the component is valid. @@ -13,9 +13,9 @@ export const useComponentErrorMessage = (component: FormComponent): string | nul const { isValid, error } = useValidateComponent(component); if (isValid) return null; switch (component.type) { - case ComponentType.Checkboxes: + case ComponentTypeV3.Checkboxes: return t(`ux_editor.checkboxes_error_${error}`); - case ComponentType.RadioButtons: + case ComponentTypeV3.RadioButtons: return t(`ux_editor.radios_error_${error}`); default: return null; diff --git a/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.test.ts b/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.test.ts index 16ce4bf1652..9ee804f09a4 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.test.ts @@ -1,5 +1,5 @@ import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { renderHook } from '@testing-library/react'; import { useComponentTypeName } from './useComponentTypeName'; @@ -7,8 +7,8 @@ import { useComponentTypeName } from './useComponentTypeName'; const inputText = 'input'; const paragraphText = 'paragraph'; const texts: KeyValuePairs = { - [`ux_editor.component_title.${ComponentType.Input}`]: inputText, - [`ux_editor.component_title.${ComponentType.Paragraph}`]: paragraphText, + [`ux_editor.component_title.${ComponentTypeV3.Input}`]: inputText, + [`ux_editor.component_title.${ComponentTypeV3.Paragraph}`]: paragraphText, }; // Mocks: @@ -23,12 +23,12 @@ describe('useComponentTypeName', () => { const { result } = renderHook(useComponentTypeName); it('Returns the correct text if it exists', () => { - expect(result.current(ComponentType.Input)).toBe(inputText); - expect(result.current(ComponentType.Paragraph)).toBe(paragraphText); + expect(result.current(ComponentTypeV3.Input)).toBe(inputText); + expect(result.current(ComponentTypeV3.Paragraph)).toBe(paragraphText); }); it('Returns the component type if the text does not exist', () => { - expect(result.current(ComponentType.Header)).toBe(ComponentType.Header); - expect(result.current(ComponentType.Checkboxes)).toBe(ComponentType.Checkboxes); + expect(result.current(ComponentTypeV3.Header)).toBe(ComponentTypeV3.Header); + expect(result.current(ComponentTypeV3.Checkboxes)).toBe(ComponentTypeV3.Checkboxes); }); }); diff --git a/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.ts b/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.ts index e88ce6a788c..d1aa744274e 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useComponentTypeName.ts @@ -1,11 +1,11 @@ import { useTranslation } from 'react-i18next'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { useCallback } from 'react'; -export function useComponentTypeName(): (type: ComponentType) => string { +export function useComponentTypeName(): (type: ComponentTypeV3) => string { const { t } = useTranslation(); return useCallback( - (type: ComponentType) => { + (type: ComponentTypeV3) => { const key = `ux_editor.component_title.${type}`; const text = t(key); return text === key ? type : text; diff --git a/frontend/packages/ux-editor-v3/src/hooks/useFormLayoutsSelector.test.ts b/frontend/packages/ux-editor-v3/src/hooks/useFormLayoutsSelector.test.ts index 54320207490..aa2c323e8a9 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useFormLayoutsSelector.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useFormLayoutsSelector.test.ts @@ -18,10 +18,10 @@ const selectedLayoutName = 'Side1'; const selectedLayoutSet = 'test-layout-set'; const render = async (callback: () => IFormLayouts | IInternalLayout | IInternalLayoutWithName) => { - const getFormLayouts = jest.fn().mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const getFormLayoutsV3 = jest.fn().mockImplementation(() => Promise.resolve(externalLayoutsMock)); const formLayoutsResult = renderHookWithMockStore( {}, - { getFormLayouts }, + { getFormLayoutsV3 }, )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); diff --git a/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.test.ts b/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.test.ts index 9e615e24730..ca0c0eed02b 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.test.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.test.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { ErrorCode, useValidateComponent } from './useValidateComponent'; import type { FormCheckboxesComponent, @@ -11,7 +11,7 @@ describe('useValidateComponent', () => { describe('Checkboxes', () => { it('Returns ErrorCode.NoOptions if there are no options', () => { const component: FormCheckboxesComponent = { - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, itemType: 'COMPONENT', id: 'test', optionsId: '', @@ -26,7 +26,7 @@ describe('useValidateComponent', () => { it('Returns ErrorCode.DuplicateValues if there are duplicate values', () => { const component: FormCheckboxesComponent = { - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, itemType: 'COMPONENT', id: 'test', optionsId: '', @@ -44,7 +44,7 @@ describe('useValidateComponent', () => { it('Returns { isValid: true } if optionsId is filled in', () => { const component: FormCheckboxesComponent = { - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, itemType: 'COMPONENT', id: 'test', optionsId: optionListIdsMock[0], @@ -56,7 +56,7 @@ describe('useValidateComponent', () => { it('Returns { isValid: true } if there are no errors', () => { const component: FormCheckboxesComponent = { - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, itemType: 'COMPONENT', id: 'test', optionsId: '', @@ -73,7 +73,7 @@ describe('useValidateComponent', () => { describe('RadioButtons', () => { it('Returns ErrorCode.NoOptions if there are no options', () => { const component: FormRadioButtonsComponent = { - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, itemType: 'COMPONENT', id: 'test', optionsId: '', @@ -88,7 +88,7 @@ describe('useValidateComponent', () => { it('Returns ErrorCode.DuplicateValues if there are duplicate values', () => { const component: FormRadioButtonsComponent = { - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, itemType: 'COMPONENT', id: 'test', optionsId: '', @@ -106,7 +106,7 @@ describe('useValidateComponent', () => { it('Returns { isValid: true } if optionsId is filled in', () => { const component: FormRadioButtonsComponent = { - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, itemType: 'COMPONENT', id: 'test', optionsId: optionListIdsMock[1], @@ -118,7 +118,7 @@ describe('useValidateComponent', () => { it('Returns { isValid: true } if there are no errors', () => { const component: FormRadioButtonsComponent = { - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, itemType: 'COMPONENT', id: 'test', optionsId: '', @@ -134,7 +134,7 @@ describe('useValidateComponent', () => { it('Returns { isValid: true } by default', () => { const component: FormComponent = { - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', id: 'test', dataModelBindings: {}, diff --git a/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.ts b/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.ts index 70c3004741b..93bf1458ad3 100644 --- a/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.ts +++ b/frontend/packages/ux-editor-v3/src/hooks/useValidateComponent.ts @@ -1,5 +1,5 @@ import { areItemsUnique } from 'app-shared/utils/arrayUtils'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormCheckboxesComponent, FormComponent, @@ -52,8 +52,8 @@ export const useValidateComponent = (component: FormComponent): ComponentValidat const { data: optionListIds } = useOptionListIdsQuery(org, app); switch (component.type) { - case ComponentType.Checkboxes: - case ComponentType.RadioButtons: + case ComponentTypeV3.Checkboxes: + case ComponentTypeV3.RadioButtons: return validateOptionGroup(component, optionListIds); default: return { isValid: true }; diff --git a/frontend/packages/ux-editor-v3/src/selectors/formLayoutSelectors.test.ts b/frontend/packages/ux-editor-v3/src/selectors/formLayoutSelectors.test.ts index e33808318ba..0a09c8646d4 100644 --- a/frontend/packages/ux-editor-v3/src/selectors/formLayoutSelectors.test.ts +++ b/frontend/packages/ux-editor-v3/src/selectors/formLayoutSelectors.test.ts @@ -6,7 +6,7 @@ import { getFullLayoutOrder, selectedLayoutNameSelector, } from './formLayoutSelectors'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; // Test data: const layout1Name = 'Side1'; @@ -41,37 +41,37 @@ const formLayoutsData: IFormLayouts = { id: container0Id, index: 0, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }, [container1Id]: { id: container1Id, index: 1, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }, }, components: { [component0AId]: { id: component0AId, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, [component0BId]: { id: component0BId, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, [component1AId]: { id: component1AId, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, [component1BId]: { id: component1BId, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, @@ -89,19 +89,19 @@ const formLayoutsData: IFormLayouts = { id: container2Id, index: 0, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }, }, components: { [component2AId]: { id: component2AId, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, [component2BId]: { id: component2BId, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, diff --git a/frontend/packages/ux-editor-v3/src/testing/componentMocks.ts b/frontend/packages/ux-editor-v3/src/testing/componentMocks.ts index e7b989e057c..788399964b2 100644 --- a/frontend/packages/ux-editor-v3/src/testing/componentMocks.ts +++ b/frontend/packages/ux-editor-v3/src/testing/componentMocks.ts @@ -1,5 +1,5 @@ import type { FormComponent, FormComponentBase } from '../types/FormComponent'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { FormPanelVariant } from 'app-shared/types/FormPanelVariant'; const commonProps: Pick = { @@ -7,9 +7,9 @@ const commonProps: Pick = { +const checkboxesComponent: FormComponent = { ...commonProps, - type: ComponentType.Checkboxes, + type: ComponentTypeV3.Checkboxes, options: [ { label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, @@ -17,9 +17,9 @@ const checkboxesComponent: FormComponent = { ], optionsId: '', }; -const radiosComponent: FormComponent = { +const radiosComponent: FormComponent = { ...commonProps, - type: ComponentType.RadioButtons, + type: ComponentTypeV3.RadioButtons, options: [ { label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, @@ -27,40 +27,40 @@ const radiosComponent: FormComponent = { ], optionsId: '', }; -const inputComponent: FormComponent = { +const inputComponent: FormComponent = { ...commonProps, - type: ComponentType.Input, + type: ComponentTypeV3.Input, }; -const headerComponent: FormComponent = { +const headerComponent: FormComponent = { ...commonProps, - type: ComponentType.Header, + type: ComponentTypeV3.Header, size: 'medium', }; -const paragraphComponent: FormComponent = { +const paragraphComponent: FormComponent = { ...commonProps, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; -const imageComponent: FormComponent = { +const imageComponent: FormComponent = { ...commonProps, - type: ComponentType.Image, + type: ComponentTypeV3.Image, }; -const datePickerComponent: FormComponent = { +const datePickerComponent: FormComponent = { ...commonProps, - type: ComponentType.Datepicker, + type: ComponentTypeV3.Datepicker, timeStamp: true, }; -const dropdownComponent: FormComponent = { +const dropdownComponent: FormComponent = { ...commonProps, - type: ComponentType.Dropdown, + type: ComponentTypeV3.Dropdown, optionsId: '', }; -const textareaComponent: FormComponent = { +const textareaComponent: FormComponent = { ...commonProps, - type: ComponentType.TextArea, + type: ComponentTypeV3.TextArea, }; -const fileUploaderComponent: FormComponent = { +const fileUploaderComponent: FormComponent = { ...commonProps, - type: ComponentType.FileUpload, + type: ComponentTypeV3.FileUpload, description: 'test', displayMode: 'list', hasCustomFileEndings: false, @@ -68,9 +68,9 @@ const fileUploaderComponent: FormComponent = { maxNumberOfAttachments: 1, minNumberOfAttachments: 1, }; -const fileUploaderWithTagComponent: FormComponent = { +const fileUploaderWithTagComponent: FormComponent = { ...commonProps, - type: ComponentType.FileUploadWithTag, + type: ComponentTypeV3.FileUploadWithTag, description: 'test', displayMode: 'list', hasCustomFileEndings: false, @@ -79,43 +79,43 @@ const fileUploaderWithTagComponent: FormComponent = { +const buttonComponent: FormComponent = { ...commonProps, - type: ComponentType.Button, + type: ComponentTypeV3.Button, onClickAction: jest.fn(), }; -const addressComponent: FormComponent = { +const addressComponent: FormComponent = { ...commonProps, - type: ComponentType.AddressComponent, + type: ComponentTypeV3.AddressComponent, simplified: true, }; -const groupComponent: FormComponent = { +const groupComponent: FormComponent = { ...commonProps, - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; -const navigationBarComponent: FormComponent = { +const navigationBarComponent: FormComponent = { ...commonProps, - type: ComponentType.NavigationBar, + type: ComponentTypeV3.NavigationBar, }; -const attachmentListComponent: FormComponent = { +const attachmentListComponent: FormComponent = { ...commonProps, - type: ComponentType.AttachmentList, + type: ComponentTypeV3.AttachmentList, }; -const thirdPartyComponent: FormComponent = { +const thirdPartyComponent: FormComponent = { ...commonProps, - type: ComponentType.Custom, + type: ComponentTypeV3.Custom, tagName: 'test', framework: 'test', }; -const panelComponent: FormComponent = { +const panelComponent: FormComponent = { ...commonProps, - type: ComponentType.Panel, + type: ComponentTypeV3.Panel, variant: FormPanelVariant.Info, showIcon: true, }; -const mapComponent: FormComponent = { +const mapComponent: FormComponent = { ...commonProps, - type: ComponentType.Map, + type: ComponentTypeV3.Map, centerLocation: { latitude: 0, longitude: 0, @@ -124,23 +124,23 @@ const mapComponent: FormComponent = { }; export const componentMocks = { - [ComponentType.AddressComponent]: addressComponent, - [ComponentType.AttachmentList]: attachmentListComponent, - [ComponentType.Button]: buttonComponent, - [ComponentType.Checkboxes]: checkboxesComponent, - [ComponentType.Datepicker]: datePickerComponent, - [ComponentType.Dropdown]: dropdownComponent, - [ComponentType.FileUploadWithTag]: fileUploaderWithTagComponent, - [ComponentType.FileUpload]: fileUploaderComponent, - [ComponentType.Group]: groupComponent, - [ComponentType.Header]: headerComponent, - [ComponentType.Image]: imageComponent, - [ComponentType.Input]: inputComponent, - [ComponentType.Map]: mapComponent, - [ComponentType.NavigationBar]: navigationBarComponent, - [ComponentType.Panel]: panelComponent, - [ComponentType.Paragraph]: paragraphComponent, - [ComponentType.RadioButtons]: radiosComponent, - [ComponentType.TextArea]: textareaComponent, - [ComponentType.Custom]: thirdPartyComponent, + [ComponentTypeV3.AddressComponent]: addressComponent, + [ComponentTypeV3.AttachmentList]: attachmentListComponent, + [ComponentTypeV3.Button]: buttonComponent, + [ComponentTypeV3.Checkboxes]: checkboxesComponent, + [ComponentTypeV3.Datepicker]: datePickerComponent, + [ComponentTypeV3.Dropdown]: dropdownComponent, + [ComponentTypeV3.FileUploadWithTag]: fileUploaderWithTagComponent, + [ComponentTypeV3.FileUpload]: fileUploaderComponent, + [ComponentTypeV3.Group]: groupComponent, + [ComponentTypeV3.Header]: headerComponent, + [ComponentTypeV3.Image]: imageComponent, + [ComponentTypeV3.Input]: inputComponent, + [ComponentTypeV3.Map]: mapComponent, + [ComponentTypeV3.NavigationBar]: navigationBarComponent, + [ComponentTypeV3.Panel]: panelComponent, + [ComponentTypeV3.Paragraph]: paragraphComponent, + [ComponentTypeV3.RadioButtons]: radiosComponent, + [ComponentTypeV3.TextArea]: textareaComponent, + [ComponentTypeV3.Custom]: thirdPartyComponent, }; diff --git a/frontend/packages/ux-editor-v3/src/testing/layoutMock.ts b/frontend/packages/ux-editor-v3/src/testing/layoutMock.ts index de115de25ff..6b6c8db6801 100644 --- a/frontend/packages/ux-editor-v3/src/testing/layoutMock.ts +++ b/frontend/packages/ux-editor-v3/src/testing/layoutMock.ts @@ -1,20 +1,20 @@ import { BASE_CONTAINER_ID } from 'app-shared/constants'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { IInternalLayout } from '../types/global'; import type { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; import type { FormComponent } from '../types/FormComponent'; import type { - ExternalFormLayout, - FormLayoutsResponse, -} from 'app-shared/types/api/FormLayoutsResponse'; + ExternalFormLayoutV3, + FormLayoutsResponseV3, +} from 'app-shared/types/api/FormLayoutsResponseV3'; export const layout1NameMock = 'Side1'; export const layout2NameMock = 'Side2'; export const baseContainerIdMock = BASE_CONTAINER_ID; export const component1IdMock = 'Component-1'; -export const component1TypeMock = ComponentType.Input; +export const component1TypeMock = ComponentTypeV3.Input; export const component1Mock: FormComponent = { id: component1IdMock, type: component1TypeMock, @@ -23,7 +23,7 @@ export const component1Mock: FormComponent = { pageIndex: null, }; export const component2IdMock = 'Component-2'; -export const component2TypeMock = ComponentType.Paragraph; +export const component2TypeMock = ComponentTypeV3.Paragraph; export const component2Mock: FormComponent = { id: component2IdMock, type: component2TypeMock, @@ -55,7 +55,7 @@ export const layoutMock: IInternalLayout = { [container1IdMock]: { id: container1IdMock, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, pageIndex: null, propertyPath: 'definitions/groupComponent', }, @@ -68,13 +68,13 @@ export const layoutMock: IInternalLayout = { customDataProperties: customDataPropertiesMock, }; -export const layout1Mock: ExternalFormLayout = { +export const layout1Mock: ExternalFormLayoutV3 = { $schema: 'https://altinncdn.no/schemas/json/layout/layout.schema.v1.json', data: { layout: [ { id: container1IdMock, - type: ComponentType.Group, + type: ComponentTypeV3.Group, children: [component1IdMock, component2IdMock], }, { @@ -90,13 +90,13 @@ export const layout1Mock: ExternalFormLayout = { }, ...customRootPropertiesMock, }; -const layout2Mock: ExternalFormLayout = { +const layout2Mock: ExternalFormLayoutV3 = { $schema: 'https://altinncdn.no/schemas/json/layout/layout.schema.v1.json', data: { layout: [], }, }; -export const externalLayoutsMock: FormLayoutsResponse = { +export const externalLayoutsMock: FormLayoutsResponseV3 = { [layout1NameMock]: layout1Mock, [layout2NameMock]: layout2Mock, }; diff --git a/frontend/packages/ux-editor-v3/src/testing/layoutWithMultiPageGroupMocks.ts b/frontend/packages/ux-editor-v3/src/testing/layoutWithMultiPageGroupMocks.ts index 26ae08bf4e8..7db2d56f782 100644 --- a/frontend/packages/ux-editor-v3/src/testing/layoutWithMultiPageGroupMocks.ts +++ b/frontend/packages/ux-editor-v3/src/testing/layoutWithMultiPageGroupMocks.ts @@ -1,5 +1,5 @@ -import type { ExternalComponent, ExternalFormLayout } from 'app-shared/types/api'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import type { ExternalComponentV3, ExternalFormLayoutV3 } from 'app-shared/types/api'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { IInternalLayout } from '../types/global'; import type { FormComponent } from '../types/FormComponent'; import type { FormContainer } from '../types/FormContainer'; @@ -16,43 +16,43 @@ export const component3_1_2Id = 'component3_1_2'; export const component3_1_3Id = 'component3_1_3'; export const component3_1_4Id = 'component3_1_4'; -const externalComponent1: ExternalComponent = { +const externalComponent1: ExternalComponentV3 = { id: component1Id, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; const internalComponent1: FormComponent = { id: component1Id, itemType: 'COMPONENT', pageIndex: null, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; -const externalComponent2: ExternalComponent = { +const externalComponent2: ExternalComponentV3 = { id: component2Id, - type: ComponentType.Input, + type: ComponentTypeV3.Input, }; const internalComponent2: FormComponent = { id: component2Id, itemType: 'COMPONENT', pageIndex: null, propertyPath: 'definitions/inputComponent', - type: ComponentType.Input, + type: ComponentTypeV3.Input, }; -const externalComponent3: ExternalComponent = { +const externalComponent3: ExternalComponentV3 = { id: component3Id, - type: ComponentType.Group, + type: ComponentTypeV3.Group, children: [component3_1Id, component3_2Id], }; const internalComponent3: FormContainer = { id: component3Id, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, pageIndex: null, propertyPath: 'definitions/groupComponent', }; -const externalComponent3_1: ExternalComponent = { +const externalComponent3_1: ExternalComponentV3 = { id: component3_1Id, children: [ '0:' + component3_1_1Id, @@ -61,76 +61,76 @@ const externalComponent3_1: ExternalComponent = { '1:' + component3_1_4Id, ], edit: { multiPage: true }, - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const internalComponent3_1: FormContainer = { edit: { multiPage: true }, id: component3_1Id, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, pageIndex: null, propertyPath: 'definitions/groupComponent', }; -const externalComponent3_1_1: ExternalComponent = { +const externalComponent3_1_1: ExternalComponentV3 = { id: component3_1_1Id, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; const internalComponent3_1_1: FormComponent = { id: component3_1_1Id, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', pageIndex: 0, }; -const externalComponent3_1_2: ExternalComponent = { +const externalComponent3_1_2: ExternalComponentV3 = { id: component3_1_2Id, - type: ComponentType.ButtonGroup, + type: ComponentTypeV3.ButtonGroup, }; const internalComponent3_1_2: FormContainer = { id: component3_1_2Id, itemType: 'CONTAINER', - type: ComponentType.ButtonGroup, + type: ComponentTypeV3.ButtonGroup, pageIndex: 0, propertyPath: 'definitions/buttonGroupComponent', }; -const externalComponent3_1_3: ExternalComponent = { +const externalComponent3_1_3: ExternalComponentV3 = { id: component3_1_3Id, - type: ComponentType.Accordion, + type: ComponentTypeV3.Accordion, children: [], }; const internalComponent3_1_3: FormContainer = { id: component3_1_3Id, itemType: 'CONTAINER', - type: ComponentType.Accordion, + type: ComponentTypeV3.Accordion, pageIndex: 1, propertyPath: 'definitions/accordionComponent', }; -const externalComponent3_1_4: ExternalComponent = { +const externalComponent3_1_4: ExternalComponentV3 = { id: component3_1_4Id, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; const internalComponent3_1_4: FormComponent = { id: component3_1_4Id, itemType: 'COMPONENT', pageIndex: 1, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; -const externalComponent3_2: ExternalComponent = { +const externalComponent3_2: ExternalComponentV3 = { id: component3_2Id, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, }; const internalComponent3_2: FormComponent = { id: component3_2Id, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', pageIndex: null, }; -export const externalLayoutWithMultiPageGroup: ExternalFormLayout = { +export const externalLayoutWithMultiPageGroup: ExternalFormLayoutV3 = { $schema: 'https://altinncdn.no/schemas/json/layout/layout.schema.v1.json', data: { layout: [ diff --git a/frontend/packages/ux-editor-v3/src/types/ContainerComponent.ts b/frontend/packages/ux-editor-v3/src/types/ContainerComponent.ts index f95b9f50392..034e4357c4b 100644 --- a/frontend/packages/ux-editor-v3/src/types/ContainerComponent.ts +++ b/frontend/packages/ux-editor-v3/src/types/ContainerComponent.ts @@ -1,7 +1,7 @@ -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; export type ContainerComponentType = - | ComponentType.Accordion - | ComponentType.AccordionGroup - | ComponentType.ButtonGroup - | ComponentType.Group; + | ComponentTypeV3.Accordion + | ComponentTypeV3.AccordionGroup + | ComponentTypeV3.ButtonGroup + | ComponentTypeV3.Group; diff --git a/frontend/packages/ux-editor-v3/src/types/ExternalContainerComponent.ts b/frontend/packages/ux-editor-v3/src/types/ExternalContainerComponent.ts index 436db6f695e..6b49477fffe 100644 --- a/frontend/packages/ux-editor-v3/src/types/ExternalContainerComponent.ts +++ b/frontend/packages/ux-editor-v3/src/types/ExternalContainerComponent.ts @@ -1,4 +1,4 @@ -import type { ExternalComponent } from 'app-shared/types/api'; +import type { ExternalComponentV3 } from 'app-shared/types/api'; import type { ContainerComponentType } from './ContainerComponent'; -export type ExternalContainerComponent = ExternalComponent; +export type ExternalContainerComponent = ExternalComponentV3; diff --git a/frontend/packages/ux-editor-v3/src/types/ExternalSimpleComponent.ts b/frontend/packages/ux-editor-v3/src/types/ExternalSimpleComponent.ts index 691a9e8dd21..dd55c38c2a6 100644 --- a/frontend/packages/ux-editor-v3/src/types/ExternalSimpleComponent.ts +++ b/frontend/packages/ux-editor-v3/src/types/ExternalSimpleComponent.ts @@ -1,5 +1,5 @@ -import type { ExternalComponent } from 'app-shared/types/api'; +import type { ExternalComponentV3 } from 'app-shared/types/api'; import type { SimpleComponentType } from './SimpleComponentType'; export type ExternalSimpleComponent = - ExternalComponent; + ExternalComponentV3; diff --git a/frontend/packages/ux-editor-v3/src/types/FormComponent.ts b/frontend/packages/ux-editor-v3/src/types/FormComponent.ts index fe7e2bcdd36..5cf738a9b37 100644 --- a/frontend/packages/ux-editor-v3/src/types/FormComponent.ts +++ b/frontend/packages/ux-editor-v3/src/types/FormComponent.ts @@ -1,9 +1,9 @@ -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { IDataModelBindings, ITextResourceBindings, IOption } from './global'; -import type { ComponentSpecificConfig } from 'app-shared/types/ComponentSpecificConfig'; +import type { ComponentSpecificConfigV3 } from 'app-shared/types/ComponentSpecificConfigV3'; import type { FormComponent } from '../components/FormComponent'; -export interface FormComponentBase { +export interface FormComponentBase { id: string; component?: string; itemType: 'COMPONENT'; @@ -29,17 +29,17 @@ export interface FormComponentBase { propertyPath?: string; } -export type FormImageComponent = FormComponent; -export type FormCheckboxesComponent = FormComponent; -export type FormRadioButtonsComponent = FormComponent; -export type FormFileUploaderComponent = FormComponent; -export type FormFileUploaderWithTagComponent = FormComponent; +export type FormImageComponent = FormComponent; +export type FormCheckboxesComponent = FormComponent; +export type FormRadioButtonsComponent = FormComponent; +export type FormFileUploaderComponent = FormComponent; +export type FormFileUploaderWithTagComponent = FormComponent; export type FormButtonComponent = FormComponent< - ComponentType.Button | ComponentType.NavigationButtons + ComponentTypeV3.Button | ComponentTypeV3.NavigationButtons >; -export type FormAddressComponent = FormComponent; +export type FormAddressComponent = FormComponent; -export type FormComponent = { - [componentType in ComponentType]: FormComponentBase & - ComponentSpecificConfig; +export type FormComponent = { + [componentType in ComponentTypeV3]: FormComponentBase & + ComponentSpecificConfigV3; }[T]; diff --git a/frontend/packages/ux-editor-v3/src/types/FormItem.ts b/frontend/packages/ux-editor-v3/src/types/FormItem.ts index 4340a4018f6..698c4c9acba 100644 --- a/frontend/packages/ux-editor-v3/src/types/FormItem.ts +++ b/frontend/packages/ux-editor-v3/src/types/FormItem.ts @@ -1,8 +1,8 @@ import type { FormComponent } from './FormComponent'; import type { FormContainer } from './FormContainer'; import type { ContainerComponentType } from './ContainerComponent'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; -export type FormItem = T extends ContainerComponentType +export type FormItem = T extends ContainerComponentType ? FormContainer : FormComponent; diff --git a/frontend/packages/ux-editor-v3/src/types/SimpleComponentType.ts b/frontend/packages/ux-editor-v3/src/types/SimpleComponentType.ts index 951d63e792a..49dcaaa3e5a 100644 --- a/frontend/packages/ux-editor-v3/src/types/SimpleComponentType.ts +++ b/frontend/packages/ux-editor-v3/src/types/SimpleComponentType.ts @@ -1,4 +1,4 @@ -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ContainerComponentType } from './ContainerComponent'; -export type SimpleComponentType = Exclude; +export type SimpleComponentType = Exclude; diff --git a/frontend/packages/ux-editor-v3/src/types/global.ts b/frontend/packages/ux-editor-v3/src/types/global.ts index 5d4c444c2a1..afca514f1d7 100644 --- a/frontend/packages/ux-editor-v3/src/types/global.ts +++ b/frontend/packages/ux-editor-v3/src/types/global.ts @@ -1,6 +1,6 @@ import type { IAppDataState } from '../features/appData/appDataReducers'; import type { IFormDesignerState } from '../features/formDesigner/formDesignerReducer'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ITextResource, ITextResources } from 'app-shared/types/global'; import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; import type { FormComponent } from './FormComponent'; @@ -53,7 +53,7 @@ export interface IRuleModelFieldElement { export interface IWidget { components: any[]; texts: IWidgetTexts[]; - displayName: ComponentType; + displayName: ComponentTypeV3; } export interface IWidgetTexts { @@ -64,7 +64,7 @@ export interface IWidgetTexts { export interface IToolbarElement { label: string; icon?: React.ComponentType; - type: ComponentType; + type: ComponentTypeV3; } export enum CollapsableMenus { diff --git a/frontend/packages/ux-editor-v3/src/utils/component.test.ts b/frontend/packages/ux-editor-v3/src/utils/component.test.ts index 344e0671fa7..026d499b0a8 100644 --- a/frontend/packages/ux-editor-v3/src/utils/component.test.ts +++ b/frontend/packages/ux-editor-v3/src/utils/component.test.ts @@ -8,7 +8,7 @@ import { isPropertyTypeSupported, setComponentProperty, } from './component'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormCheckboxesComponent, FormComponent, @@ -31,7 +31,7 @@ describe('Component utils', () => { [bindingKeyToKeep]: resourceKeyToKeep, [bindingKeyToChange]: resourceKeyToChange, }, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }; @@ -54,7 +54,7 @@ describe('Component utils', () => { textResourceBindings: { title: titleResourceKey, }, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }; @@ -73,7 +73,7 @@ describe('Component utils', () => { textResourceBindings: { description: descriptionResourceKey, }, - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }; @@ -85,9 +85,9 @@ describe('Component utils', () => { }); describe('addOptionToComponent', () => { - it.each([ComponentType.Checkboxes, ComponentType.RadioButtons] as ( - | ComponentType.Checkboxes - | ComponentType.RadioButtons + it.each([ComponentTypeV3.Checkboxes, ComponentTypeV3.RadioButtons] as ( + | ComponentTypeV3.Checkboxes + | ComponentTypeV3.RadioButtons )[])('Adds option to %s component', (componentType) => { const component: FormCheckboxesComponent | FormRadioButtonsComponent = { id: 'test', @@ -114,9 +114,9 @@ describe('Component utils', () => { }); describe('changeComponentOptionLabel', () => { - it.each([ComponentType.Checkboxes, ComponentType.RadioButtons] as ( - | ComponentType.Checkboxes - | ComponentType.RadioButtons + it.each([ComponentTypeV3.Checkboxes, ComponentTypeV3.RadioButtons] as ( + | ComponentTypeV3.Checkboxes + | ComponentTypeV3.RadioButtons )[])('Changes label of option with given value on %s component', (componentType) => { const valueOfWhichLabelShouldChange = 'testValue2'; const component: FormCheckboxesComponent | FormRadioButtonsComponent = { @@ -151,7 +151,7 @@ describe('Component utils', () => { }); describe('generateFormItem', () => { - it.each(Object.values(ComponentType).filter((v) => !containerComponentTypes.includes(v)))( + it.each(Object.values(ComponentTypeV3).filter((v) => !containerComponentTypes.includes(v)))( 'Generates component of type %s with given ID', (componentType) => { const id = 'testId'; @@ -186,7 +186,7 @@ describe('Component utils', () => { it('Sets given property on given component', () => { const component: FormComponent = { id: 'test', - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }; diff --git a/frontend/packages/ux-editor-v3/src/utils/component.ts b/frontend/packages/ux-editor-v3/src/utils/component.ts index c3e9c9edead..ca7616491d9 100644 --- a/frontend/packages/ux-editor-v3/src/utils/component.ts +++ b/frontend/packages/ux-editor-v3/src/utils/component.ts @@ -5,7 +5,7 @@ import type { FormComponent, FormRadioButtonsComponent, } from '../types/FormComponent'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { formItemConfigs } from '../data/formItemConfig'; import type { FormItem } from '../types/FormItem'; import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; @@ -90,7 +90,7 @@ export const generateRandomOption = (): IOption => ({ label: '', value: generate * @param id The id of the component to generate. * @returns A component of the given type. */ -export const generateFormItem = (type: T, id: string): FormItem => { +export const generateFormItem = (type: T, id: string): FormItem => { const { defaultProperties } = formItemConfigs[type]; return { ...defaultProperties, id }; }; diff --git a/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.test.ts b/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.test.ts index 950eb679d1e..ea6b876271b 100644 --- a/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.test.ts +++ b/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.test.ts @@ -52,7 +52,7 @@ import { import { deepCopy } from 'app-shared/pure'; import { textMock } from '../../../../testing/mocks/i18nMock'; import type { FormContainer } from '../types/FormContainer'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; describe('expressionsUtils', () => { describe('convertSubExpression', () => { @@ -392,7 +392,7 @@ describe('expressionsUtils', () => { const groupComponentWithAllBooleanFieldsAsExpressions: FormContainer = { id: 'some-id', itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, hidden: parsableExternalExpression, required: parsableExternalExpression, readOnly: parsableExternalExpression, diff --git a/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.ts b/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.ts index 4c16178ab33..db0b203769d 100644 --- a/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.ts +++ b/frontend/packages/ux-editor-v3/src/utils/expressionsUtils.ts @@ -19,7 +19,7 @@ import type { FormComponent } from '../types/FormComponent'; import type { LegacySingleSelectOption } from '@digdir/design-system-react'; import type { FormContainer } from '../types/FormContainer'; import type { UseText } from '../hooks'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; export const convertInternalExpressionToExternal = (expression: Expression): any => { if (complexExpressionIsSet(expression.complexExpression)) { @@ -428,7 +428,7 @@ export const getExternalExpressionOnComponentProperty = ( property: ExpressionProperty, ): any => { let value = form[property]; - if (form.type === ComponentType.Group && property.includes('edit')) { + if (form.type === ComponentTypeV3.Group && property.includes('edit')) { const editPropertyForGroup = property.split('edit.')[1]; value = form['edit'][editPropertyForGroup]; } diff --git a/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.test.tsx b/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.test.tsx index b7a2ef764c7..85516c14828 100644 --- a/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.test.tsx +++ b/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.test.tsx @@ -19,7 +19,7 @@ import { isComponentTypeValidChild, validateDepth, } from './formLayoutUtils'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { IInternalLayout } from '../types/global'; import { BASE_CONTAINER_ID } from 'app-shared/constants'; import { customDataPropertiesMock, customRootPropertiesMock } from '../testing/layoutMock'; @@ -41,13 +41,13 @@ const baseContainer: FormContainer = { id: BASE_CONTAINER_ID, index: 0, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const customProperty = 'some-custom-property'; const headerId = '46882e2b-8097-4170-ad4c-32cdc156634e'; -const headerComponent: FormComponent = { +const headerComponent: FormComponent = { id: headerId, - type: ComponentType.Header, + type: ComponentTypeV3.Header, itemType: 'COMPONENT', textResourceBindings: { title: 'ServiceName', @@ -56,9 +56,9 @@ const headerComponent: FormComponent = { size: 'L', }; const paragraphId = 'ede0b05d-2c53-4feb-bdd4-4c61b89bd729'; -const paragraphComponent: FormComponent = { +const paragraphComponent: FormComponent = { id: paragraphId, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', textResourceBindings: { title: 'ServiceName', @@ -71,12 +71,12 @@ const groupContainer: FormContainer = { dataModelBindings: {}, id: groupId, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const paragraphInGroupId = 'group-paragraph'; -const paragraphInGroupComponent: FormComponent = { +const paragraphInGroupComponent: FormComponent = { id: paragraphInGroupId, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', textResourceBindings: { title: 'ServiceName', @@ -88,19 +88,19 @@ const groupInGroupContainer: FormContainer = { dataModelBindings: {}, id: groupInGroupId, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const buttonGroupId = 'button-group-container'; const buttonGroupContainer: FormContainer = { dataModelBindings: {}, id: buttonGroupId, itemType: 'CONTAINER', - type: ComponentType.ButtonGroup, + type: ComponentTypeV3.ButtonGroup, }; const paragraphInGroupInGroupId = 'group-child-paragraph'; -const paragraphInGroupInGroupComponent: FormComponent = { +const paragraphInGroupInGroupComponent: FormComponent = { id: paragraphInGroupInGroupId, - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', textResourceBindings: { title: 'ServiceName', @@ -134,11 +134,11 @@ describe('formLayoutUtils', () => { describe('hasNavigationButtons', () => { it('Returns true if navigation buttons are present', () => { const navigationButtonsId = 'navigationButtons'; - const navigationButtonsComponent: FormComponent = { + const navigationButtonsComponent: FormComponent = { id: navigationButtonsId, itemType: 'COMPONENT', onClickAction: jest.fn(), - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, dataModelBindings: {}, }; const layout: IInternalLayout = { @@ -181,9 +181,9 @@ describe('formLayoutUtils', () => { }); describe('addComponent', () => { - const newComponent: FormComponent = { + const newComponent: FormComponent = { id: 'newComponent', - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, itemType: 'COMPONENT', dataModelBindings: {}, }; @@ -231,7 +231,7 @@ describe('formLayoutUtils', () => { describe('addContainer', () => { const id = 'testId'; - const newContainer: FormContainer = { id, itemType: 'CONTAINER', type: ComponentType.Group }; + const newContainer: FormContainer = { id, itemType: 'CONTAINER', type: ComponentTypeV3.Group }; it('Adds container to the end of the base container by default', () => { const layout = addContainer(mockInternal, newContainer, id); @@ -299,7 +299,7 @@ describe('formLayoutUtils', () => { describe('removeComponentsByType', () => { it('Removes components of the given type from the layout', () => { - const layout = removeComponentsByType(mockInternal, ComponentType.Paragraph); + const layout = removeComponentsByType(mockInternal, ComponentTypeV3.Paragraph); expect(layout.components[paragraphId]).toBeUndefined(); expect(layout.components[paragraphInGroupId]).toBeUndefined(); expect(layout.components[paragraphInGroupInGroupId]).toBeUndefined(); @@ -318,7 +318,7 @@ describe('formLayoutUtils', () => { const id = 'navigationButtons'; const layout = addNavigationButtons(mockInternal, id); expect(layout.components[id]).toBeDefined(); - expect(layout.components[id].type).toEqual(ComponentType.NavigationButtons); + expect(layout.components[id].type).toEqual(ComponentTypeV3.NavigationButtons); expect(layout.components[id].textResourceBindings).toEqual({ next: undefined, back: undefined, @@ -379,7 +379,7 @@ describe('formLayoutUtils', () => { }); describe('addItemOfType', () => { - it.each(Object.values(ComponentType).filter((v) => !containerComponentTypes.includes(v)))( + it.each(Object.values(ComponentTypeV3).filter((v) => !containerComponentTypes.includes(v)))( 'Adds a new component to the layout when the given type is %s', (componentType) => { const id = 'newItemId'; @@ -438,7 +438,7 @@ describe('formLayoutUtils', () => { id, itemType: 'CONTAINER', pageIndex: null, - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const layout: IInternalLayout = addContainer(createEmptyLayout(), container, id); expect(getDepth(layout)).toBe(1); @@ -450,12 +450,12 @@ describe('formLayoutUtils', () => { id, itemType: 'CONTAINER', pageIndex: null, - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; const containerId = 'sometestgroup'; const component: FormComponent = { itemType: 'COMPONENT', - type: ComponentType.Paragraph, + type: ComponentTypeV3.Paragraph, id: 'sometestcomponent', }; let layout: IInternalLayout = createEmptyLayout(); @@ -473,7 +473,7 @@ describe('formLayoutUtils', () => { const container: FormContainer = { id: groupInGroupId, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; layout = addContainer(layout, container, 'groupingroupingroup', groupInGroupId); expect(getDepth(layout)).toBe(3); @@ -490,7 +490,7 @@ describe('formLayoutUtils', () => { const container: FormContainer = { id: groupInGroupId, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }; layout = addContainer(layout, container, 'groupingroupingroup', groupInGroupId); expect(validateDepth(layout)).toBe(false); @@ -499,21 +499,21 @@ describe('formLayoutUtils', () => { describe('isComponentTypeValidChild', () => { it('Returns true if the child is valid to given container', () => { - expect(isComponentTypeValidChild(mockInternal, buttonGroupId, ComponentType.Button)).toBe( + expect(isComponentTypeValidChild(mockInternal, buttonGroupId, ComponentTypeV3.Button)).toBe( true, ); }); it('Returns true if the component is not dropped inside a container', () => { expect( - isComponentTypeValidChild(mockInternal, BASE_CONTAINER_ID, ComponentType.ButtonGroup), + isComponentTypeValidChild(mockInternal, BASE_CONTAINER_ID, ComponentTypeV3.ButtonGroup), ).toBe(true); }); it('Returns false if the child is invalid for the given container', () => { - expect(isComponentTypeValidChild(mockInternal, buttonGroupId, ComponentType.Paragraph)).toBe( - false, - ); + expect( + isComponentTypeValidChild(mockInternal, buttonGroupId, ComponentTypeV3.Paragraph), + ).toBe(false); }); }); diff --git a/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.ts b/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.ts index 3583deed216..662ce70c471 100644 --- a/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.ts +++ b/frontend/packages/ux-editor-v3/src/utils/formLayoutUtils.ts @@ -9,7 +9,7 @@ import type { import { BASE_CONTAINER_ID, MAX_NESTED_GROUP_LEVEL } from 'app-shared/constants'; import { deepCopy } from 'app-shared/pure'; import { insertArrayElementAtPos, removeItemByValue } from 'app-shared/utils/arrayUtils'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { FormComponent } from '../types/FormComponent'; import { generateFormItem } from './component'; import type { FormItemConfigs } from '../data/formItemConfig'; @@ -18,7 +18,7 @@ import type { FormContainer } from '../types/FormContainer'; import type { FormItem } from '../types/FormItem'; import * as formItemUtils from './formItemUtils'; -export const mapComponentToToolbarElement = ( +export const mapComponentToToolbarElement = ( c: FormItemConfigs[T], ): IToolbarElement => ({ label: c.name, @@ -46,7 +46,7 @@ export const hasNavigationButtons = (layout: IInternalLayout): boolean => { const { components } = layout; return Object.values(components) .map(({ type }) => type) - .includes(ComponentType.NavigationButtons); + .includes(ComponentTypeV3.NavigationButtons); }; /** @@ -216,7 +216,7 @@ export const removeComponent = (layout: IInternalLayout, componentId: string): I */ export const removeComponentsByType = ( layout: IInternalLayout, - componentType: ComponentType, + componentType: ComponentTypeV3, ): IInternalLayout => { let newLayout = layout; Object.keys(layout.components) @@ -240,7 +240,7 @@ export const addNavigationButtons = (layout: IInternalLayout, id: string): IInte onClickAction: () => {}, showBackButton: true, textResourceBindings: { next: undefined, back: undefined }, - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, }; return addComponent(layout, navigationButtons); }; @@ -318,7 +318,7 @@ const findItem = (layout: IInternalLayout, id: string): FormComponent | FormCont * @param position The desired index of the component within its container. Set it to a negative value to add it at the end. Defaults to -1. * @returns The new layout. */ -export const addItemOfType = ( +export const addItemOfType = ( layout: IInternalLayout, componentType: T, id: string, @@ -383,7 +383,7 @@ export const validateDepth = (layout: IInternalLayout): boolean => export const isComponentTypeValidChild = ( layout: IInternalLayout, parentId: string, - componentType: ComponentType, + componentType: ComponentTypeV3, ): boolean => { if (parentId === BASE_CONTAINER_ID) return true; const parent = getItem(layout, parentId); diff --git a/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.test.ts b/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.test.ts index ba8c20e87b0..9712aece6e4 100644 --- a/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.test.ts +++ b/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.test.ts @@ -4,7 +4,7 @@ import { convertExternalLayoutsToInternalFormat, firstAvailableLayout, } from './formLayoutsUtils'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { createEmptyLayout } from './formLayoutUtils'; import { BASE_CONTAINER_ID, DEFAULT_SELECTED_LAYOUT_NAME } from 'app-shared/constants'; import { externalLayoutsMock, layout1NameMock, layout2NameMock } from '../testing/layoutMock'; @@ -24,9 +24,9 @@ describe('formLayoutsUtils', () => { const layout1Components = Object.values(updatedLayouts[layout1Id].components); const layout2Components = Object.values(updatedLayouts[layout2Id].components); expect(layout1Components.length).toBe(1); - expect(layout1Components[0].type).toBe(ComponentType.NavigationButtons); + expect(layout1Components[0].type).toBe(ComponentTypeV3.NavigationButtons); expect(layout2Components.length).toBe(1); - expect(layout2Components[0].type).toBe(ComponentType.NavigationButtons); + expect(layout2Components[0].type).toBe(ComponentTypeV3.NavigationButtons); expect(callback).toHaveBeenCalledTimes(2); expect(callback).toHaveBeenCalledWith(layout1Id, updatedLayouts[layout1Id]); expect(callback).toHaveBeenCalledWith(layout2Id, updatedLayouts[layout2Id]); @@ -63,7 +63,7 @@ describe('formLayoutsUtils', () => { id: navButtonsId, itemType: 'COMPONENT', onClickAction: jest.fn(), - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, dataModelBindings: {}, }; const layouts: IFormLayouts = { @@ -116,9 +116,9 @@ describe('formLayoutsUtils', () => { const layout2Components = Object.values(updatedLayouts[layout2id].components); const layoutReceiptComponents = Object.values(updatedLayouts[layoutReceiptId].components); expect(layout1Components.length).toBe(1); - expect(layout1Components[0].type).toBe(ComponentType.NavigationButtons); + expect(layout1Components[0].type).toBe(ComponentTypeV3.NavigationButtons); expect(layout2Components.length).toBe(1); - expect(layout2Components[0].type).toBe(ComponentType.NavigationButtons); + expect(layout2Components[0].type).toBe(ComponentTypeV3.NavigationButtons); expect(layoutReceiptComponents.length).toBe(0); expect(callback).toHaveBeenCalledTimes(2); expect(callback).toHaveBeenCalledWith(layout1id, updatedLayouts[layout1id]); @@ -133,7 +133,7 @@ describe('formLayoutsUtils', () => { id: navButtonsId, itemType: 'COMPONENT', onClickAction: jest.fn(), - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, dataModelBindings: {}, }; const layouts: IFormLayouts = { @@ -167,7 +167,7 @@ describe('formLayoutsUtils', () => { id: navButtonsId, itemType: 'COMPONENT', onClickAction: jest.fn(), - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, dataModelBindings: {}, }; const layouts: IFormLayouts = { @@ -207,7 +207,7 @@ describe('formLayoutsUtils', () => { id: navButtonsId, itemType: 'COMPONENT', onClickAction: jest.fn(), - type: ComponentType.NavigationButtons, + type: ComponentTypeV3.NavigationButtons, dataModelBindings: {}, }; const layouts: IFormLayouts = { diff --git a/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.ts b/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.ts index 4712da7e09c..95d85090ad1 100644 --- a/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.ts +++ b/frontend/packages/ux-editor-v3/src/utils/formLayoutsUtils.ts @@ -5,11 +5,11 @@ import { hasNavigationButtons, removeComponentsByType, } from './formLayoutUtils'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { generateComponentId } from './generateId'; import { deepCopy } from 'app-shared/pure'; import { DEFAULT_SELECTED_LAYOUT_NAME } from 'app-shared/constants'; -import type { FormLayoutsResponse } from 'app-shared/types/api/FormLayoutsResponse'; +import type { FormLayoutsResponseV3 } from 'app-shared/types/api/FormLayoutsResponseV3'; import { ArrayUtils } from '@studio/pure-functions'; import { externalLayoutToInternal } from '../converters/formLayoutConverters'; @@ -42,7 +42,7 @@ export const addOrRemoveNavigationButtons = async ( if (layoutsThatShouldHaveNavigationButtons.length === 1) { // There is only one layout const name = layoutsThatShouldHaveNavigationButtons[0]; - const layout = removeComponentsByType(layouts[name], ComponentType.NavigationButtons); + const layout = removeComponentsByType(layouts[name], ComponentTypeV3.NavigationButtons); layouts[name] !== layout && layoutsToUpdate.push(name); allLayouts[name] = layout; } else { @@ -50,7 +50,7 @@ export const addOrRemoveNavigationButtons = async ( for (const name of layoutsThatShouldHaveNavigationButtons) { const layout = layouts[name]; if (!hasNavigationButtons(layout)) { - const navButtonsId = generateComponentId(ComponentType.NavigationButtons, layouts); + const navButtonsId = generateComponentId(ComponentTypeV3.NavigationButtons, layouts); allLayouts[name] = addNavigationButtons(layout, navButtonsId); layoutsToUpdate.push(name); } @@ -73,7 +73,7 @@ interface AllLayouts { * @returns A list of layouts in internal format and a list of layouts with an invalid format. */ export const convertExternalLayoutsToInternalFormat = ( - layouts: FormLayoutsResponse, + layouts: FormLayoutsResponseV3, ): AllLayouts => { const convertedLayouts: IFormLayouts = {}; const invalidLayouts: string[] = []; diff --git a/frontend/packages/ux-editor-v3/src/utils/generateId.test.tsx b/frontend/packages/ux-editor-v3/src/utils/generateId.test.tsx index ecd061420c2..2174401b995 100644 --- a/frontend/packages/ux-editor-v3/src/utils/generateId.test.tsx +++ b/frontend/packages/ux-editor-v3/src/utils/generateId.test.tsx @@ -1,6 +1,6 @@ import type { IFormLayouts } from '../types/global'; import { generateComponentId, generateTextResourceId } from './generateId'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; describe('generateComponentId', () => { const layouts: IFormLayouts = { @@ -10,13 +10,13 @@ describe('generateComponentId', () => { id: 'container1', index: 0, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }, }, components: { 'Input-1bd34': { id: 'Input-1bd34', - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, @@ -33,13 +33,13 @@ describe('generateComponentId', () => { id: 'container2', index: 0, itemType: 'CONTAINER', - type: ComponentType.Group, + type: ComponentTypeV3.Group, }, }, components: { 'Input-abfr34': { id: 'Input-abfr34', - type: ComponentType.Input, + type: ComponentTypeV3.Input, itemType: 'COMPONENT', dataModelBindings: {}, }, @@ -52,14 +52,14 @@ describe('generateComponentId', () => { }, }; it('should generate unique component id within provided layouts', () => { - const newId = generateComponentId(ComponentType.Input, layouts); + const newId = generateComponentId(ComponentTypeV3.Input, layouts); expect(newId.startsWith('Input')).toBeTruthy(); expect(layouts.layout1.components[newId]).toBeUndefined(); expect(layouts.layout2.components[newId]).toBeUndefined(); }); it('should generate unique component id for group component', () => { - const newId = generateComponentId(ComponentType.Group, layouts); + const newId = generateComponentId(ComponentTypeV3.Group, layouts); expect(newId.startsWith('Group')).toBeTruthy(); expect(layouts.layout1.containers[newId]).toBeUndefined(); expect(layouts.layout2.containers[newId]).toBeUndefined(); diff --git a/frontend/packages/ux-editor-v3/src/utils/generateId.ts b/frontend/packages/ux-editor-v3/src/utils/generateId.ts index a1f549d36ca..d9c6413bca7 100644 --- a/frontend/packages/ux-editor-v3/src/utils/generateId.ts +++ b/frontend/packages/ux-editor-v3/src/utils/generateId.ts @@ -1,6 +1,6 @@ import type { IFormLayouts } from '../types/global'; import { generateRandomId } from 'app-shared/utils/generateRandomId'; -import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import type { ContainerComponentType } from '../types/ContainerComponent'; import { containerComponentTypes } from '../data/containerComponentTypes'; @@ -12,7 +12,7 @@ export const generateTextResourceId = ( return `${layoutName}.${componentId}.${textKey}`; }; -export const generateComponentId = (componentType: ComponentType, layouts: IFormLayouts) => { +export const generateComponentId = (componentType: ComponentTypeV3, layouts: IFormLayouts) => { const layoutNames = Object.keys(layouts); let existsInLayout = true; let componentId = ''; diff --git a/frontend/packages/ux-editor-v3/src/utils/language.test.ts b/frontend/packages/ux-editor-v3/src/utils/language.test.ts index a0b40eecad3..d2c47bfe539 100644 --- a/frontend/packages/ux-editor-v3/src/utils/language.test.ts +++ b/frontend/packages/ux-editor-v3/src/utils/language.test.ts @@ -3,7 +3,7 @@ import { getComponentTitleByComponentType, getTextResource, } from './language'; -import { ComponentType } from 'app-shared/types/ComponentType'; +import { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; import { mockUseTranslation } from '../../../../testing/mocks/i18nMock'; describe('Designer > utils/language', () => { @@ -11,14 +11,14 @@ describe('Designer > utils/language', () => { const componentHelpTexts = { 'ux_editor.component_help_text.default': 'Default help text', }; - Object.values(ComponentType).forEach((componentType) => { + Object.values(ComponentTypeV3).forEach((componentType) => { componentHelpTexts[`ux_editor.component_help_text.${componentType}`] = `Help text for ${componentType}`; }); const { t } = mockUseTranslation(componentHelpTexts); it('should return specific help text when component type is known', () => { - Object.values(ComponentType).forEach((componentType) => { + Object.values(ComponentTypeV3).forEach((componentType) => { expect(getComponentHelperTextByComponentType(componentType, t)).toBe( `Help text for ${componentType}`, ); @@ -32,14 +32,14 @@ describe('Designer > utils/language', () => { describe('getComponentTitleByComponentType', () => { const componentTitleTexts = {}; - Object.values(ComponentType).forEach((componentType) => { + Object.values(ComponentTypeV3).forEach((componentType) => { componentTitleTexts[`ux_editor.component_title.${componentType}`] = `Title text for ${componentType}`; }); it('should return specific title text it exists', () => { const { t } = mockUseTranslation(componentTitleTexts); - Object.values(ComponentType).forEach((componentType) => { + Object.values(ComponentTypeV3).forEach((componentType) => { expect(getComponentTitleByComponentType(componentType, t)).toBe( `Title text for ${componentType}`, ); @@ -48,9 +48,11 @@ describe('Designer > utils/language', () => { it('should return component type when title text does not exist', () => { const { t } = mockUseTranslation({ - [`ux_editor.component_title.${ComponentType.Accordion}`]: `Title text for ${ComponentType.Accordion}`, + [`ux_editor.component_title.${ComponentTypeV3.Accordion}`]: `Title text for ${ComponentTypeV3.Accordion}`, }); - expect(getComponentTitleByComponentType(ComponentType.Input, t)).toBe(ComponentType.Input); + expect(getComponentTitleByComponentType(ComponentTypeV3.Input, t)).toBe( + ComponentTypeV3.Input, + ); }); }); diff --git a/frontend/packages/ux-editor-v3/src/utils/language.ts b/frontend/packages/ux-editor-v3/src/utils/language.ts index 0fc641e9ac7..c9d94ed9b18 100644 --- a/frontend/packages/ux-editor-v3/src/utils/language.ts +++ b/frontend/packages/ux-editor-v3/src/utils/language.ts @@ -1,9 +1,9 @@ import type { ITextResource } from 'app-shared/types/global'; import { CollapsableMenus } from '../types/global'; -import type { ComponentType } from 'app-shared/types/ComponentType'; import type i18next from 'i18next'; import type { UseText } from '../hooks'; import type { TranslationKey } from 'app-shared/types/language'; +import type { ComponentTypeV3 } from 'app-shared/types/ComponentTypeV3'; /** * Get the help text for a given component type @@ -12,7 +12,7 @@ import type { TranslationKey } from 'app-shared/types/language'; * @returns The help text for the component, or the default help text if none is found */ export function getComponentHelperTextByComponentType( - type: ComponentType, + type: ComponentTypeV3, t: typeof i18next.t, ): string { const text = t(`ux_editor.component_help_text.${type}`); @@ -27,7 +27,10 @@ export function getComponentHelperTextByComponentType( * @param t The translation function * @returns The title text for the component, or the type if none is found */ -export function getComponentTitleByComponentType(type: ComponentType, t: typeof i18next.t): string { +export function getComponentTitleByComponentType( + type: ComponentTypeV3, + t: typeof i18next.t, +): string { const text = t(`ux_editor.component_title.${type}`); return text !== `ux_editor.component_title.${type}` ? text : type; } @@ -43,13 +46,6 @@ export function getCollapsableMenuTitleByType(menu: CollapsableMenus, t: typeof case CollapsableMenus.AdvancedComponents: { return t('ux_editor.collapsable_text_advanced_components'); } - // TODO : Uncomment when we have widgets components - // case CollapsableMenus.Widgets: { - // return t('ux_editor.collapsable_text_widgets'); - // } - // case CollapsableMenus.ThirdParty: { - // return language['ux_editor.collapsable_text_thirdparty_components']; - // } default: { return ''; } diff --git a/frontend/packages/ux-editor/src/components/Elements/DefaultToolbar.tsx b/frontend/packages/ux-editor/src/components/Elements/DefaultToolbar.tsx index d0ca40d4079..f2c066e6808 100644 --- a/frontend/packages/ux-editor/src/components/Elements/DefaultToolbar.tsx +++ b/frontend/packages/ux-editor/src/components/Elements/DefaultToolbar.tsx @@ -20,25 +20,15 @@ export function DefaultToolbar() { const [anchorElement, setAnchorElement] = useState(null); const { t } = useTranslation(); - // TODO: Uncomment when widgets are implemented - // const { org, app } = useParams(); - // const { data: widgetsList } = useWidgetsQuery(org, app); const componentList: IToolbarElement[] = schemaComponents.map(mapComponentToToolbarElement); const textComponentList: IToolbarElement[] = textComponents.map(mapComponentToToolbarElement); const advancedComponentsList: IToolbarElement[] = advancedItems.map(mapComponentToToolbarElement); - // TODO: Uncomment when widgets are implemented - // const widgetComponentsList: IToolbarElement[] = widgetsList.map( - // (widget) => mapWidgetToToolbarElement(widget, t) - // ); const allComponentLists: KeyValuePairs = { [CollapsableMenus.Components]: componentList, [CollapsableMenus.Texts]: textComponentList, [CollapsableMenus.AdvancedComponents]: advancedComponentsList, - // TODO: Uncomment when widgets are implemented - // [CollapsableMenus.Widgets]: widgetComponentsList, - // [CollapsableMenus.ThirdParty]: thirdPartyComponentList, }; const handleComponentInformationOpen = (component: ComponentType, event: any) => { diff --git a/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx b/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx index 969a11680a1..5530ff4bbbc 100644 --- a/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx @@ -14,6 +14,8 @@ const user = userEvent.setup(); // Test data: const srcValueLabel = 'Source'; +const autocompleteLabel = 'Autocomplete'; +const datamodelFieldLinkLabel = 'Add data model field'; const texts = { 'general.label': '', 'general.value': '', @@ -24,6 +26,8 @@ const texts = { 'ux_editor.modal_properties_image_placement_label': 'Placement', 'ux_editor.modal_properties_image_alt_text_label': 'Alt text', 'ux_editor.modal_properties_image_width_label': 'Width', + 'ux_editor.component_properties.autocomplete': autocompleteLabel, + 'ux_editor.modal_properties_data_model_link': datamodelFieldLinkLabel, }; // Mocks: @@ -95,14 +99,13 @@ describe('EditFormComponent', () => { 'ux_editor.modal_configure_read_only': 'checkbox', }; - const linkIcon = screen.getByText(/ux_editor.modal_properties_data_model_link/i); - await act(() => user.click(linkIcon)); + const datamodelFieldButton = screen.getByRole('button', { name: datamodelFieldLinkLabel }); + await act(() => user.click(datamodelFieldButton)); Object.keys(labels).map(async (label) => expect(await screen.findByRole(labels[label], { name: label })), ); - expect(screen.getByRole('combobox')); - expect(screen.getByLabelText('Autocomplete (WCAG)')); + expect(screen.getByRole('combobox', { name: autocompleteLabel })); }); test('should return header specific content when type header', async () => { diff --git a/frontend/packages/ux-editor/src/components/config/EditFormComponent.tsx b/frontend/packages/ux-editor/src/components/config/EditFormComponent.tsx index 8dd32ed7128..f226660884f 100644 --- a/frontend/packages/ux-editor/src/components/config/EditFormComponent.tsx +++ b/frontend/packages/ux-editor/src/components/config/EditFormComponent.tsx @@ -40,6 +40,7 @@ export const EditFormComponent = ({ const [showComponentConfigBeta, setShowComponentConfigBeta] = React.useState( shouldDisplayFeature('componentConfigBeta'), ); + const formItemConfig = formItemConfigs[component.type]; useLayoutSchemaQuery(); // Ensure we load the layout schemas so that component schemas can be loaded const { data: schema, isPending } = useComponentSchemaQuery(component.type); @@ -72,7 +73,7 @@ export const EditFormComponent = ({ } }; - const isUnknownInternalComponent: boolean = !formItemConfigs[component.type]; + const isUnknownInternalComponent: boolean = !formItemConfig; if (isUnknownInternalComponent) { return ; } @@ -83,7 +84,7 @@ export const EditFormComponent = ({ id={component.id} value={showComponentConfigBeta || false} onChange={toggleShowBetaFunc} - propertyPath={component.propertyPath} + propertyPath={formItemConfig.propertyPath} componentType={component.type} helpText={t('ux_editor.edit_component.show_beta_func_help_text')} renderField={({ fieldProps }) => ( diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index f7570be49b2..0cc044a0fcc 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -11,9 +11,9 @@ import { EditOptions } from './editModal/EditOptions'; import { EditStringValue } from './editModal/EditStringValue'; import { useSelector } from 'react-redux'; import { useText } from '../../hooks'; -import { getComponentPropertyLabel } from '../../utils/language'; import { getUnsupportedPropertyTypes } from '../../utils/component'; import { EditGrid } from './editModal/EditGrid'; +import { useComponentPropertyLabel } from '../../hooks/useComponentPropertyLabel'; export interface IEditFormComponentProps { editFormId: string; @@ -34,6 +34,7 @@ export const FormComponentConfig = ({ }: FormComponentConfigProps) => { const selectedLayout = useSelector(selectedLayoutNameSelector); const t = useText(); + const componentPropertyLabel = useComponentPropertyLabel(); if (!schema?.properties) return null; @@ -86,7 +87,7 @@ export const FormComponentConfig = ({ {t('top_menu.datamodel')} - {Object.keys(dataModelBindings?.properties).map((propertyKey: any) => { + {Object.keys(dataModelBindings?.properties).map((propertyKey: string) => { return ( - {getComponentPropertyLabel(propertyKey, t)} + {componentPropertyLabel(propertyKey)} {rest[propertyKey]?.description && ( {rest[propertyKey].description} diff --git a/frontend/packages/ux-editor/src/components/config/componentConfig.tsx b/frontend/packages/ux-editor/src/components/config/componentConfig.tsx index b3c62acfa6a..4d856cec498 100644 --- a/frontend/packages/ux-editor/src/components/config/componentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/componentConfig.tsx @@ -9,12 +9,12 @@ import { EditReadOnly } from './editModal/EditReadOnly'; import { EditRequired } from './editModal/EditRequired'; import { EditAutoComplete } from './editModal/EditAutoComplete'; import { EditTextResourceBinding } from './editModal/EditTextResourceBinding'; -import type { FormComponent } from '../../types/FormComponent'; +import type { FormItem } from '../../types/FormItem'; -export interface IGenericEditComponent { +export interface IGenericEditComponent { editFormId?: string; - component: T; - handleComponentChange: (component: T) => void; + component: FormItem; + handleComponentChange: (component: FormItem) => void; layoutName?: string; } diff --git a/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.tsx b/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.tsx index 8ed86599a5d..af37e3498ea 100644 --- a/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.tsx +++ b/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.tsx @@ -5,8 +5,12 @@ import { useText } from '../../../../hooks'; import { EditTextResourceBinding } from '../../editModal/EditTextResourceBinding'; import { FormPanelVariant } from 'app-shared/types/FormPanelVariant'; import { FormField } from '../../../FormField'; +import type { ComponentType } from 'app-shared/types/ComponentType'; -export const PanelComponent = ({ component, handleComponentChange }: IGenericEditComponent) => { +export const PanelComponent = ({ + component, + handleComponentChange, +}: IGenericEditComponent) => { const t = useText(); const handleShowIconClick = (showIcon: boolean) => { diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.test.tsx index 3d00a702fcd..ba7b2e7f718 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.test.tsx @@ -1,107 +1,67 @@ import React from 'react'; import { EditAutoComplete } from './EditAutoComplete'; -import { act, screen, waitFor } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ComponentType } from 'app-shared/types/ComponentType'; import type { FormComponent } from '../../../types/FormComponent'; -import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; -import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; +import type { FormItem } from '../../../types/FormItem'; +import { HTMLAutoCompleteValue } from 'app-shared/types/HTMLAutoCompleteValue'; -const componentMock: FormComponent = { +const componentMock: FormComponent = { id: 'random-id', - autocomplete: '', + autocomplete: undefined, type: ComponentType.Input, itemType: 'COMPONENT', propertyPath: 'definitions/inputComponent', dataModelBindings: {}, }; -const waitForData = async () => { - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) - .renderHookResult.result; - await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); -}; - -export const render = async ( - handleComponentChangeMock: any = jest.fn(), - component: FormComponent = componentMock, -) => { - await waitForData(); - return renderWithMockStore()( +export const renderEditAutocomplete = ( + handleComponentChangeMock: (component: FormItem) => void = jest.fn(), + component: FormItem = componentMock, +) => + render( , ); -}; - -test('should render first 6 suggestions on search field focused', async () => { - await render(); - const user = userEvent.setup(); - - const inputField = screen.getByRole('textbox'); - expect(inputField).toBeInTheDocument(); - - await act(() => user.click(inputField)); - - expect(await screen.findByRole('dialog')).toBeInTheDocument(); - expect(screen.getAllByRole('option')).toHaveLength(6); -}); - -test('should filter options while typing in search field', async () => { - await render(); - const user = userEvent.setup(); - - await act(() => user.type(screen.getByRole('textbox'), 'of')); - - await waitFor(() => expect(screen.getByRole('textbox')).toHaveValue('of')); - - expect(screen.getByRole('option', { name: 'off' })).toBeInTheDocument(); - expect(screen.queryByRole('option', { name: 'given-name' })).not.toBeInTheDocument(); -}); -test('should set the chosen options within the search field', async () => { - await render(); - const user = userEvent.setup(); - - const searchField = screen.getByRole('textbox'); - - await act(() => user.type(searchField, 'of')); - await waitFor(() => expect(searchField).toHaveValue('of')); - await act(() => user.click(screen.getByRole('option', { name: 'off' }))); - - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); - await waitFor(() => expect(searchField).toHaveValue('off')); -}); - -test('should toggle autocomplete-popup based onFocus and onBlur', async () => { - await render(); - const user = userEvent.setup(); - await act(() => user.click(screen.getByRole('textbox'))); - - expect(await screen.findByRole('dialog')).toBeInTheDocument(); - - await act(() => user.tab()); - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); -}); - -test('should call handleComponentChangeMock callback ', async () => { - const handleComponentChangeMock = jest.fn(); - await render(handleComponentChangeMock); - - const user = userEvent.setup(); +describe('EditAutoComplete', () => { + it('Renders with the "default" option as selected by default', () => { + renderEditAutocomplete(); + const combobox = screen.getByRole('combobox'); + expect(combobox).toHaveValue(''); + }); - const inputField = screen.getByRole('textbox'); - expect(inputField).toBeInTheDocument(); + it('Updates the component with the chosen value', async () => { + const user = userEvent.setup(); + const handleComponentChange = jest.fn(); + renderEditAutocomplete(handleComponentChange); + const combobox = screen.getByRole('combobox'); + await act(() => user.selectOptions(combobox, 'name')); + expect(handleComponentChange).toHaveBeenCalledTimes(1); + expect(handleComponentChange).toHaveBeenCalledWith({ + ...componentMock, + autocomplete: 'name', + }); + }); - await act(() => user.click(inputField)); - await screen.findByRole('dialog'); + it('Renders with the given value', () => { + const autocomplete = HTMLAutoCompleteValue.Name; + renderEditAutocomplete(jest.fn(), { ...componentMock, autocomplete }); + const combobox = screen.getByRole('combobox'); + expect(combobox).toHaveValue(autocomplete); + }); - await act(() => user.click(screen.getByRole('option', { name: 'on' }))); - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); - expect(handleComponentChangeMock).toHaveBeenCalledWith({ - autocomplete: 'on', - dataModelBindings: {}, - id: 'random-id', - itemType: 'COMPONENT', - propertyPath: 'definitions/inputComponent', - type: 'Input', + it('Sets autocomplete to undefined when the "default" option is selected', async () => { + const autocomplete = HTMLAutoCompleteValue.Name; + const handleComponentChange = jest.fn(); + const user = userEvent.setup(); + renderEditAutocomplete(handleComponentChange, { ...componentMock, autocomplete }); + const combobox = screen.getByRole('combobox'); + await act(() => user.selectOptions(combobox, '')); + expect(handleComponentChange).toHaveBeenCalledTimes(1); + expect(handleComponentChange).toHaveBeenLastCalledWith({ + ...componentMock, + autocomplete: undefined, + }); }); }); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.tsx index 9d37015ce42..5f1e7576240 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditAutoComplete.tsx @@ -1,148 +1,45 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import type { ChangeEvent } from 'react'; +import React from 'react'; import type { IGenericEditComponent } from '../componentConfig'; -import { LegacyTextField, LegacyPopover } from '@digdir/design-system-react'; -import { stringToArray, arrayToString } from '../../../utils/stringUtils'; -import { replaceLastItem } from 'app-shared/utils/arrayUtils'; -import { FormField } from '../../FormField'; -import { StudioButton } from '@studio/components'; - -const getLastWord = (value: string) => value.split(' ').pop(); -const stdAutocompleteOpts = [ - 'on', - 'off', - 'name', - 'honorific-prefix', - 'given-name', - 'additional-name', - 'family-name', - 'honorific-suffix', - 'nickname', - 'email', - 'username', - 'new-password', - 'current-password', - 'one-time-code', - 'organization-title', - 'organization', - 'street-address', - 'address-line1', - 'address-line2', - 'address-line3', - 'address-level4', - 'address-level3', - 'address-level2', - 'address-level1', - 'country', - 'country-name', - 'postal-code', - 'cc-name', - 'cc-given-name', - 'cc-additional-name', - 'cc-family-name', - 'cc-number', - 'cc-exp', - 'cc-exp-month', - 'cc-exp-year', - 'cc-csc', - 'cc-type', - 'transaction-currency', - 'transaction-amount', - 'language', - 'bday', - 'bday-day', - 'bday-month', - 'bday-year', - 'sex', - 'tel', - 'tel-country-code', - 'tel-national', - 'tel-area-code', - 'tel-local', - 'tel-extension', - 'url', - 'photo', -]; - -export const EditAutoComplete = ({ component, handleComponentChange }: IGenericEditComponent) => { - const [searchFieldFocused, setSearchFieldFocused] = useState(false); - const initialAutocompleteText = component?.autocomplete || ''; - const [autocompleteText, setAutocompleteText] = useState(initialAutocompleteText); - - useEffect(() => { - setAutocompleteText(initialAutocompleteText); - }, [initialAutocompleteText, component.id]); - - const autoCompleteOptions = useMemo((): string[] => { - const lastWord = getLastWord(autocompleteText); - return stdAutocompleteOpts.filter((alternative) => alternative.includes(lastWord))?.slice(0, 6); - }, [autocompleteText]); - - const buildNewText = (word: string): string => { - const wordParts = stringToArray(autocompleteText, ' '); - const newWordParts = replaceLastItem(wordParts, word); - return arrayToString(newWordParts); - }; - - const handleWordClick = (word: string): void => { - const autocomplete = buildNewText(word); - setAutocompleteText(autocomplete); - handleComponentChange({ +import { NativeSelect } from '@digdir/design-system-react'; +import { HTMLAutoCompleteValue } from 'app-shared/types/HTMLAutoCompleteValue'; +import { useTranslation } from 'react-i18next'; +import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { FormItem } from '../../../types/FormItem'; + +const htmlAutoCompleteValues: HTMLAutoCompleteValue[] = Object.values(HTMLAutoCompleteValue); +const isHTMLAutoCompleteValue = (value: string): value is HTMLAutoCompleteValue => + htmlAutoCompleteValues.includes(value as HTMLAutoCompleteValue); +const options: (HTMLAutoCompleteValue | '')[] = ['', ...htmlAutoCompleteValues]; + +export const EditAutoComplete = ({ + component, + handleComponentChange, +}: IGenericEditComponent) => { + const { t } = useTranslation(); + + const handleChange = (event: ChangeEvent): void => { + const { value } = event.target; + const newValue = isHTMLAutoCompleteValue(value) ? value : undefined; + const updatedComponent: FormItem = { ...component, - autocomplete, - }); - setSearchFieldFocused(false); - }; - - const handleChange = (value: string): void => { - if (!searchFieldFocused) setSearchFieldFocused(true); - setAutocompleteText(value); + autocomplete: newValue, + }; + handleComponentChange(updatedComponent); }; return ( -
- ( - setSearchFieldFocused(true)} - onBlur={(): void => { - if (searchFieldFocused) setSearchFieldFocused(false); - }} - onChange={(event) => { - const { value } = event.target; - handleChange(value); - fieldProps.onChange(value); - }} - /> - )} - /> - 0} - placement='bottom-start' - arrow={false} - trigger={
} - > - {autoCompleteOptions.map( - (option): JSX.Element => ( - handleWordClick(option)} - > - {option} - - ), - )} - -
+ + {options.map((value: HTMLAutoCompleteValue) => ( + + ))} + ); }; diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx index 98fbc37816c..195cb35407d 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx @@ -3,7 +3,7 @@ import { Switch } from '@digdir/design-system-react'; import type { IGenericEditComponent } from '../componentConfig'; import { useText } from '../../../hooks'; import { FormField } from '../../FormField'; -import { getComponentPropertyLabel } from '../../../utils/language'; +import { useComponentPropertyLabel } from '../../../hooks/useComponentPropertyLabel'; export interface EditBooleanValueProps extends IGenericEditComponent { propertyKey: string; @@ -17,6 +17,7 @@ export const EditBooleanValue = ({ helpText, }: EditBooleanValueProps) => { const t = useText(); + const componentPropertyLabel = useComponentPropertyLabel(); const handleChange = () => { handleComponentChange({ @@ -51,7 +52,7 @@ export const EditBooleanValue = ({ id={`${propertyKey}-checkbox-${component.id}`} disabled={isValueExpression(fieldProps.value)} > - {getComponentPropertyLabel(propertyKey, t)} + {componentPropertyLabel(propertyKey)} ); }} diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.test.tsx index ae847a2826f..7d59345283a 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.test.tsx @@ -1,32 +1,20 @@ import React from 'react'; -import { act, fireEvent, screen, waitFor } from '@testing-library/react'; +import { act, fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { EditGrid } from './EditGrid'; -import { renderWithMockStore, renderHookWithMockStore } from '../../../../testing/mocks'; -import { useLayoutSchemaQuery } from '../../../../hooks/queries/useLayoutSchemaQuery'; import { textMock } from '../../../../../../../testing/mocks/i18nMock'; import { component1Mock } from '../../../../testing/layoutMock'; +import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { FormItem } from '../../../../types/FormItem'; -const waitForData = async () => { - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) - .renderHookResult.result; - await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); -}; - -const render = async ({ grid = undefined, handleComponentChange = jest.fn() } = {}) => { - await waitForData(); - - return renderWithMockStore()( - , - ); +const renderEditGrid = ({ grid = undefined, handleComponentChange = jest.fn() } = {}) => { + const component: FormItem = { ...component1Mock, grid }; + return render(); }; describe('EditGrid', () => { - it('should show grid value 4 on slider for mobile tab when xs: "4" is set on grid on component', async () => { - await render({ grid: { xs: 4 } }); + it('should show grid value 4 on slider for mobile tab when xs: "4" is set on grid on component', () => { + renderEditGrid({ grid: { xs: 4 } }); const mobileTab = screen.getByRole('tab', { name: textMock('ux_editor.modal_properties_grid_size_xs'), @@ -40,7 +28,7 @@ describe('EditGrid', () => { it('should show slider value equals to grid xs: "2" when switching tab from laptop with grid md: "6"', async () => { const user = userEvent.setup(); - await render({ grid: { xs: 2, md: 6 } }); + renderEditGrid({ grid: { xs: 2, md: 6 } }); const sliderMobile = screen.getByRole('slider'); expect(sliderMobile).toHaveValue('2'); @@ -57,8 +45,8 @@ describe('EditGrid', () => { expect(sliderLaptop).toHaveValue('6'); }); - it('should show mobile tab as selected when by default', async () => { - await render({ grid: { xs: 3, md: 4 } }); + it('should show mobile tab as selected when by default', () => { + renderEditGrid({ grid: { xs: 3, md: 4 } }); const laptopTab = screen.getByRole('tab', { name: textMock('ux_editor.modal_properties_grid_size_md'), @@ -76,7 +64,7 @@ describe('EditGrid', () => { it('should call handleComponentChange with grid: xs: 12 when switch is disabled', async () => { const user = userEvent.setup(); const handleComponentChange = jest.fn(); - await render({ handleComponentChange }); + renderEditGrid({ handleComponentChange }); const lockIcon = screen.getByRole('img', { name: 'lockIcon' }); expect(lockIcon).toBeInTheDocument(); @@ -96,9 +84,9 @@ describe('EditGrid', () => { }); }); - it('should call handleComponentChange with grid: xs: 3 when slider is changed', async () => { + it('should call handleComponentChange with grid: xs: 3 when slider is changed', () => { const handleComponentChange = jest.fn(); - await render({ grid: { xs: 12 }, handleComponentChange }); + renderEditGrid({ grid: { xs: 12 }, handleComponentChange }); const slider = screen.getByRole('slider'); @@ -112,9 +100,9 @@ describe('EditGrid', () => { }); }); - it('should call handleComponentChange with new value for xs, but remain original value for md, when slider is changed to "4" for mobile', async () => { + it('should call handleComponentChange with new value for xs, but remain original value for md, when slider is changed to "4" for mobile', () => { const handleComponentChange = jest.fn(); - await render({ + renderEditGrid({ grid: { xs: 6, md: 3 }, handleComponentChange, }); @@ -135,7 +123,7 @@ describe('EditGrid', () => { it('should call handleComponentChange with original value for md and no value for xs when useDefaultSwitch is enabled', async () => { const user = userEvent.setup(); const handleComponentChange = jest.fn(); - await render({ + renderEditGrid({ grid: { xs: 3, md: 3 }, handleComponentChange, }); @@ -154,7 +142,7 @@ describe('EditGrid', () => { it('should call handleComponentChange with original value for xs and no value for md when useDefaultSwitch is enabled for laptop tab', async () => { const user = userEvent.setup(); const handleComponentChange = jest.fn(); - await render({ + renderEditGrid({ grid: { xs: 3, md: 3 }, handleComponentChange, }); @@ -179,7 +167,7 @@ describe('EditGrid', () => { it('should call handleComponentChange with original values for innerGrid and labelGrid when useDefaultSwitch is enabled', async () => { const user = userEvent.setup(); const handleComponentChange = jest.fn(); - await render({ + renderEditGrid({ grid: { innerGrid: { xs: 3, md: 3 }, labelGrid: { xs: 6 }, @@ -204,7 +192,7 @@ describe('EditGrid', () => { it('should call handleComponentChange with no grid-property when useDefaultSwitch is disabled', async () => { const user = userEvent.setup(); const handleComponentChange = jest.fn(); - await render({ + renderEditGrid({ grid: { xs: 3 }, handleComponentChange, }); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.tsx index 94b76e0dfa4..495694b4868 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditGrid/EditGrid.tsx @@ -5,14 +5,18 @@ import { Tabs } from '@digdir/design-system-react'; import classes from './EditGrid.module.css'; import { EditGridForGivenViewSize } from './EditGridForGivenViewSize'; import { LaptopIcon, MobileIcon, MobileSmallIcon, MonitorIcon, TabletIcon } from '@studio/icons'; -import type { FormComponent } from '../../../../types/FormComponent'; import { deepCopy } from 'app-shared/pure'; import { ViewSize } from './types/ViewSize'; import type { GridSizes } from './types/GridSizes'; import { useTranslation } from 'react-i18next'; +import type { FormItem } from '../../../../types/FormItem'; +import type { ComponentType } from 'app-shared/types/ComponentType'; -const setGridOnComponent = (gridValues: GridSizes, component: FormComponent) => { - const newComponent = deepCopy(component); +const setGridOnComponent = ( + gridValues: GridSizes, + component: FormItem, +): FormItem => { + const newComponent: FormItem = deepCopy(component); newComponent.grid = { ...newComponent.grid, ...gridValues }; if ( Object.keys(newComponent.grid).length === 0 || @@ -23,7 +27,10 @@ const setGridOnComponent = (gridValues: GridSizes, component: FormComponent) => return newComponent; }; -export const EditGrid = ({ handleComponentChange, component }: IGenericEditComponent) => { +export const EditGrid = ({ + handleComponentChange, + component, +}: IGenericEditComponent) => { const [gridValues, setGridValues] = useState(component.grid ?? {}); const [selectedViewSizeForGridProp, setSelectedViewSizeForGridProp] = useState( ViewSize.Xs, diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditNumberValue.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditNumberValue.tsx index 7bdb9527b96..f5e18e107aa 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditNumberValue.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditNumberValue.tsx @@ -1,26 +1,31 @@ import React from 'react'; import type { IGenericEditComponent } from '../componentConfig'; -import { useTranslation } from 'react-i18next'; import { FormField } from '../../FormField'; -import { getComponentPropertyLabel } from '../../../utils/language'; import { setComponentProperty } from '../../../utils/component'; import { StudioDecimalInput } from '@studio/components'; +import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { FormItem } from '../../../types/FormItem'; +import type { FilterKeysOfType } from 'app-shared/types/FilterKeysOfType'; +import { useComponentPropertyLabel } from '../../../hooks/useComponentPropertyLabel'; +import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; -export interface EditNumberValueProps extends IGenericEditComponent { - propertyKey: string; +type NumberKeys = FilterKeysOfType; + +export interface EditNumberValueProps>> + extends IGenericEditComponent { + propertyKey: K; helpText?: string; } -export const EditNumberValue = ({ +export const EditNumberValue = >>({ component, handleComponentChange, propertyKey, helpText, -}: EditNumberValueProps) => { - const { t } = useTranslation(); - const handleValueChange = (newValue: number) => { - handleComponentChange(setComponentProperty(component, propertyKey, newValue)); - }; +}: EditNumberValueProps) => { + const componentPropertyLabel = useComponentPropertyLabel(); + const handleValueChange = (newValue: number) => + handleComponentChange(setComponentProperty(component, propertyKey, newValue)); return ( )} /> diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.test.tsx index db0cc70e49f..172b3ddb0f3 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.test.tsx @@ -1,23 +1,13 @@ import React from 'react'; -import { screen, waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { EditOptions } from './EditOptions'; -import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; -import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; +import { renderWithMockStore } from '../../../testing/mocks'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; import { ComponentType } from 'app-shared/types/ComponentType'; -import type { - FormCheckboxesComponent, - FormRadioButtonsComponent, -} from '../../../types/FormComponent'; +import type { FormRadioButtonsComponent } from '../../../types/FormComponent'; -const waitForData = async () => { - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) - .renderHookResult.result; - await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); -}; - -const mockComponent: FormCheckboxesComponent | FormRadioButtonsComponent = { +const mockComponent: FormRadioButtonsComponent = { id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', type: ComponentType.RadioButtons, textResourceBindings: { @@ -28,29 +18,26 @@ const mockComponent: FormCheckboxesComponent | FormRadioButtonsComponent = { dataModelBindings: {}, }; -const render = async ({ component = mockComponent, handleComponentChange = jest.fn() } = {}) => { - await waitForData(); - - return renderWithMockStore()( +const renderEditOptions = ({ component = mockComponent, handleComponentChange = jest.fn() } = {}) => + renderWithMockStore()( , ); -}; describe('EditOptions', () => { - it('should render', async () => { - await render(); + it('should render', () => { + renderEditOptions(); expect( screen.getByText(textMock('ux_editor.modal_properties_add_radio_button_options')), ).toBeInTheDocument(); }); it('should show code list input by default when neither options nor optionId are set', async () => { - await render(); + renderEditOptions(); expect(screen.getByText(textMock('ux_editor.modal_add_options_codelist'))).toBeInTheDocument(); }); it('should show manual input when component has options defined', async () => { - await render({ + renderEditOptions({ component: { ...mockComponent, options: [{ label: 'option1', value: 'option1' }], @@ -60,7 +47,7 @@ describe('EditOptions', () => { }); it('should show code list input when component has optionsId defined', async () => { - await render({ + renderEditOptions({ component: { ...mockComponent, optionsId: 'optionsId', diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.tsx index 53d77899c08..f95120d0fd0 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions.tsx @@ -8,16 +8,15 @@ import { PlusIcon, XMarkIcon } from '@navikt/aksel-icons'; import { TextResource } from '../../TextResource'; import { useText, useComponentErrorMessage } from '../../../hooks'; import { addOptionToComponent, generateRandomOption } from '../../../utils/component'; -import type { - FormCheckboxesComponent, - FormRadioButtonsComponent, -} from '../../../types/FormComponent'; import { ErrorMessage } from '@digdir/design-system-react'; import { FormField } from '../../FormField'; import { StudioButton } from '@studio/components'; +import type { ComponentType } from 'app-shared/types/ComponentType'; -export interface ISelectionEditComponentProvidedProps - extends IGenericEditComponent { +type SelectionComponentType = ComponentType.Checkboxes | ComponentType.RadioButtons; + +export interface ISelectionEditComponentProvidedProps + extends IGenericEditComponent { renderOptions?: { onlyCodeListOptions?: boolean; }; @@ -36,11 +35,11 @@ const getSelectedOptionsType = (codeListId: string, options: IOption[]): Selecte return SelectedOptionsType.CodeList; }; -export function EditOptions({ +export function EditOptions({ editFormId, component, handleComponentChange, -}: ISelectionEditComponentProvidedProps) { +}: ISelectionEditComponentProvidedProps) { const previousEditFormId = useRef(editFormId); const initialSelectedOptionType = getSelectedOptionsType(component.optionsId, component.options); const [selectedOptionsType, setSelectedOptionsType] = useState(initialSelectedOptionType); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.test.tsx index f39a6f11791..7ef95c0fdd0 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.test.tsx @@ -1,30 +1,17 @@ import React from 'react'; -import { screen, waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { EditStringValue } from './EditStringValue'; -import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; -import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; -import { mockUseTranslation } from '../../../../../../testing/mocks/i18nMock'; +import { renderWithMockStore } from '../../../testing/mocks'; +import { textMock } from '../../../../../../testing/mocks/i18nMock'; import { ComponentType } from 'app-shared/types/ComponentType'; import userEvent from '@testing-library/user-event'; import { act } from 'react-dom/test-utils'; -jest.mock('react-i18next', () => ({ - useTranslation: () => mockUseTranslation({}), -})); - const user = userEvent.setup(); -const waitForData = async () => { - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) - .renderHookResult.result; - await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); -}; - -const render = async ({ maxLength = undefined, handleComponentChange = jest.fn() } = {}) => { - await waitForData(); - - return renderWithMockStore()( +const renderEditStringValue = ({ maxLength = undefined, handleComponentChange = jest.fn() } = {}) => + renderWithMockStore()( , ); -}; + describe('EditStringValue', () => { - it('should render', async () => { + it('should render', () => { const handleComponentChange = jest.fn(); - await render({ handleComponentChange }); + renderEditStringValue({ handleComponentChange }); }); it(' Ensure that the onChange handler is called with the correct arguments', async () => { const handleComponentChange = jest.fn(); - await render({ handleComponentChange }); - const inputElement = screen.getByLabelText('maxLength'); + renderEditStringValue({ handleComponentChange }); + const inputElement = screen.getByLabelText( + textMock('ux_editor.component_properties.maxLength'), + ); await act(() => user.type(inputElement, 'new value')); expect(handleComponentChange).toHaveBeenCalledWith({ id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96', diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx index 7e5346f4c2f..6afe9ebf795 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx @@ -3,7 +3,7 @@ import type { IGenericEditComponent } from '../componentConfig'; import { useTranslation } from 'react-i18next'; import { FormField } from '../../FormField'; import { LegacySelect, Textfield } from '@digdir/design-system-react'; -import { getComponentPropertyLabel } from '../../../utils/language'; +import { useComponentPropertyLabel } from '../../../hooks/useComponentPropertyLabel'; export interface EditStringValueProps extends IGenericEditComponent { propertyKey: string; @@ -21,6 +21,7 @@ export const EditStringValue = ({ multiple, }: EditStringValueProps) => { const { t } = useTranslation(); + const componentPropertyLabel = useComponentPropertyLabel(); const handleValueChange = (newValue: string) => { handleComponentChange({ @@ -32,7 +33,7 @@ export const EditStringValue = ({ return ( { it.each([null, 0, 1, 2])( diff --git a/frontend/packages/ux-editor/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.ts b/frontend/packages/ux-editor/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.ts index daf0481a9b8..7ba94381c2d 100644 --- a/frontend/packages/ux-editor/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.ts +++ b/frontend/packages/ux-editor/src/converters/simpleComponentConverters/externalSimpleComponentToInternal.ts @@ -6,8 +6,8 @@ export const externalSimpleComponentToInternal = ( externalComponent: ExternalSimpleComponent, pageIndex: number | null, ): FormComponent => { - const defaultProperties = formItemConfigs[externalComponent.type]?.defaultProperties; - const propertyPath = defaultProperties?.propertyPath; + const formItemConfig = formItemConfigs[externalComponent.type]; + const propertyPath = formItemConfig?.propertyPath; return { ...(propertyPath ? { propertyPath } : {}), ...externalComponent, diff --git a/frontend/packages/ux-editor/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts b/frontend/packages/ux-editor/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts index 8f36b5f03ed..6e811723049 100644 --- a/frontend/packages/ux-editor/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts +++ b/frontend/packages/ux-editor/src/converters/simpleComponentConverters/internalSimpleComponentToExternal.test.ts @@ -7,7 +7,7 @@ import { internalSimpleComponentToExternal } from './internalSimpleComponentToEx const id = '1'; const customProperty = 'test'; const type: ComponentType = ComponentType.Input; -const propertyPath = formItemConfigs[type].defaultProperties.propertyPath; +const propertyPath = formItemConfigs[type].propertyPath; describe('internalGroupComponentToExternal', () => { it('Correctly converts an internal simple component', () => { diff --git a/frontend/packages/ux-editor/src/data/formItemConfig.test.ts b/frontend/packages/ux-editor/src/data/formItemConfig.test.ts index 1fa50010fda..af6efff1da8 100644 --- a/frontend/packages/ux-editor/src/data/formItemConfig.test.ts +++ b/frontend/packages/ux-editor/src/data/formItemConfig.test.ts @@ -1,15 +1,24 @@ -import { formItemConfigs } from './formItemConfig'; +import { + advancedItems, + confOnScreenComponents, + schemaComponents, + textComponents, +} from './formItemConfig'; import { ComponentType } from 'app-shared/types/ComponentType'; -describe('formItemConfigs', () => { - it('should have textResourceBindings and buttonStyle defined for ActionButton', () => { - const actionButtonConfig = formItemConfigs[ComponentType.ActionButton]; - expect(actionButtonConfig).toBeDefined(); +describe('formItemConfig', () => { + const allAvailableLists = [ + schemaComponents, + advancedItems, + textComponents, + confOnScreenComponents, + ]; + const allAvailableComponents = allAvailableLists.flat(); - const { defaultProperties } = actionButtonConfig; - expect(defaultProperties).toBeDefined(); - expect(defaultProperties.textResourceBindings).toBeDefined(); - expect(defaultProperties.textResourceBindings.title).toBeDefined(); - expect(defaultProperties.buttonStyle).toBeDefined(); - }); + it.each(Object.values(ComponentType))( + '%s is available through one of the visible lists', + (componentType) => { + expect(allAvailableComponents.map(({ name }) => name)).toContain(componentType); + }, + ); }); diff --git a/frontend/packages/ux-editor/src/data/formItemConfig.ts b/frontend/packages/ux-editor/src/data/formItemConfig.ts index d61971c5c7b..ef9677b5fa7 100644 --- a/frontend/packages/ux-editor/src/data/formItemConfig.ts +++ b/frontend/packages/ux-editor/src/data/formItemConfig.ts @@ -1,14 +1,13 @@ import { ComponentType } from 'app-shared/types/ComponentType'; -import type { FormItem } from '../types/FormItem'; import { FormPanelVariant } from 'app-shared/types/FormPanelVariant'; import type { RefAttributes, SVGProps } from 'react'; import type React from 'react'; -import ActionButtonSchema from '../testing/schemas/json/component/ActionButton.schema.v1.json'; import { Accordion, CalendarIcon, Checkbox, ChevronDownDoubleIcon, + ElementIcon, ExclamationmarkTriangleIcon, FileTextIcon, FingerButtonIcon, @@ -33,13 +32,15 @@ import { } from '@studio/icons'; import type { ContainerComponentType } from '../types/ContainerComponent'; import { LayoutItemType } from '../types/global'; +import type { ComponentSpecificConfig } from 'app-shared/types/ComponentSpecificConfig'; export type FormItemConfig = { name: T; - itemType: LayoutItemType; - defaultProperties: FormItem; + itemType: T extends ContainerComponentType ? LayoutItemType.Container : LayoutItemType.Component; + defaultProperties: ComponentSpecificConfig; icon?: React.ComponentType & { title?: string; titleId?: string }> & RefAttributes; + propertyPath?: string; } & (T extends ContainerComponentType ? { validChildTypes: ComponentType[] } : {}); export type FormItemConfigs = { [T in ComponentType]: FormItemConfig }; @@ -49,35 +50,24 @@ export const formItemConfigs: FormItemConfigs = { name: ComponentType.Alert, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Alert, severity: 'info', - propertyPath: 'definitions/alertComponent', }, + propertyPath: 'definitions/alertComponent', icon: ExclamationmarkTriangleIcon, }, [ComponentType.Accordion]: { name: ComponentType.Accordion, itemType: LayoutItemType.Container, - defaultProperties: { - id: '', - itemType: 'CONTAINER', - type: ComponentType.Accordion, - propertyPath: 'definitions/accordionComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/accordionComponent', icon: Accordion, validChildTypes: [ComponentType.Paragraph], }, [ComponentType.AccordionGroup]: { name: ComponentType.AccordionGroup, itemType: LayoutItemType.Container, - defaultProperties: { - id: '', - itemType: 'CONTAINER', - type: ComponentType.AccordionGroup, - propertyPath: 'definitions/accordionGroupComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/accordionGroupComponent', icon: ChevronDownDoubleIcon, validChildTypes: [ComponentType.Accordion], }, @@ -85,13 +75,8 @@ export const formItemConfigs: FormItemConfigs = { name: ComponentType.ActionButton, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.ActionButton, - textResourceBindings: { - title: '', // To avoid undefined as text when previewing default component - }, - buttonStyle: ActionButtonSchema.properties.buttonStyle.enum[0], // To avoid rendering error in app-frontend when previewing default component + buttonStyle: 'primary', + action: 'instantiate', }, icon: FingerButtonIcon, }, @@ -99,130 +84,97 @@ export const formItemConfigs: FormItemConfigs = { name: ComponentType.AddressComponent, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.AddressComponent, - dataModelBindings: {}, simplified: true, - propertyPath: 'definitions/addressComponent', + saveWhileTyping: 400, }, + propertyPath: 'definitions/addressComponent', icon: HouseIcon, }, [ComponentType.AttachmentList]: { name: ComponentType.AttachmentList, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.AttachmentList, - maxNumberOfAttachments: 1, - minNumberOfAttachments: 0, - propertyPath: 'definitions/attachmentListComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/attachmentListComponent', icon: PaperclipIcon, }, [ComponentType.Button]: { name: ComponentType.Button, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Button, - onClickAction: () => {}, - propertyPath: 'definitions/actionButtonComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/actionButtonComponent', icon: FingerButtonIcon, }, [ComponentType.ButtonGroup]: { name: ComponentType.ButtonGroup, itemType: LayoutItemType.Container, - defaultProperties: { - id: '', - itemType: 'CONTAINER', - type: ComponentType.ButtonGroup, - propertyPath: 'definitions/buttonGroupComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/buttonGroupComponent', icon: FingerButtonIcon, validChildTypes: [ComponentType.Button], }, [ComponentType.Checkboxes]: { name: ComponentType.Checkboxes, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Checkboxes, - dataModelBindings: {}, - required: true, - propertyPath: 'definitions/radioAndCheckboxComponents', - }, + defaultProperties: {}, + propertyPath: 'definitions/radioAndCheckboxComponents', icon: Checkbox, }, [ComponentType.Custom]: { name: ComponentType.Custom, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Custom, tagName: 'tag', framework: 'framework', }, + icon: ElementIcon, + }, + [ComponentType.CustomButton]: { + name: ComponentType.CustomButton, + itemType: LayoutItemType.Component, + defaultProperties: { + actions: [], + buttonStyle: 'primary', + }, + icon: FingerButtonIcon, }, [ComponentType.Datepicker]: { name: ComponentType.Datepicker, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - dataModelBindings: {}, - type: ComponentType.Datepicker, minDate: '1900-01-01T12:00:00.000Z', maxDate: '2100-01-01T12:00:00.000Z', timeStamp: false, - required: true, - propertyPath: 'definitions/datepickerComponent', }, + propertyPath: 'definitions/datepickerComponent', icon: CalendarIcon, }, [ComponentType.Dropdown]: { name: ComponentType.Dropdown, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Dropdown, - dataModelBindings: {}, optionsId: '', - required: true, - propertyPath: 'definitions/selectionComponents', }, + propertyPath: 'definitions/selectionComponents', icon: Select, }, [ComponentType.FileUpload]: { name: ComponentType.FileUpload, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.FileUpload, description: '', displayMode: 'list', hasCustomFileEndings: false, maxFileSizeInMB: 25, maxNumberOfAttachments: 1, minNumberOfAttachments: 1, - propertyPath: 'definitions/fileUploadComponent', }, + propertyPath: 'definitions/fileUploadComponent', icon: PaperclipIcon, }, [ComponentType.FileUploadWithTag]: { name: ComponentType.FileUploadWithTag, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.FileUploadWithTag, description: '', displayMode: 'list', hasCustomFileEndings: false, @@ -230,31 +182,24 @@ export const formItemConfigs: FormItemConfigs = { maxNumberOfAttachments: 1, minNumberOfAttachments: 1, optionsId: '', - propertyPath: 'definitions/fileUploadWithTagComponent', }, + propertyPath: 'definitions/fileUploadWithTagComponent', icon: PaperclipIcon, }, [ComponentType.Grid]: { name: ComponentType.Grid, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Grid, - propertyPath: 'definitions/gridComponent', rows: [], }, + propertyPath: 'definitions/gridComponent', icon: TableIcon, }, [ComponentType.Group]: { name: ComponentType.Group, itemType: LayoutItemType.Container, - defaultProperties: { - id: '', - itemType: 'CONTAINER', - type: ComponentType.Group, - propertyPath: 'definitions/groupComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/groupComponent', icon: Group, validChildTypes: Object.values(ComponentType), }, @@ -262,21 +207,15 @@ export const formItemConfigs: FormItemConfigs = { name: ComponentType.Header, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Header, size: 'L', - propertyPath: 'definitions/headerComponent', }, + propertyPath: 'definitions/headerComponent', icon: Title, }, [ComponentType.IFrame]: { name: ComponentType.IFrame, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.IFrame, sandbox: {}, }, icon: PresentationIcon, @@ -285,71 +224,54 @@ export const formItemConfigs: FormItemConfigs = { name: ComponentType.Image, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Image, image: { src: {}, width: '100%', align: 'center', }, - propertyPath: 'definitions/imageComponent', }, + propertyPath: 'definitions/imageComponent', icon: ImageIcon, }, [ComponentType.Input]: { name: ComponentType.Input, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Input, - dataModelBindings: {}, - required: true, - propertyPath: 'definitions/inputComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/inputComponent', icon: ShortText, }, [ComponentType.InstanceInformation]: { name: ComponentType.InstanceInformation, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.InstanceInformation, - propertyPath: 'definitions/instanceInformationComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/instanceInformationComponent', icon: InformationSquareIcon, }, [ComponentType.InstantiationButton]: { name: ComponentType.InstantiationButton, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.InstantiationButton, - }, + defaultProperties: {}, icon: FingerButtonIcon, }, [ComponentType.Likert]: { name: ComponentType.Likert, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Likert, - dataModelBindings: {}, - propertyPath: 'definitions/radioAndCheckboxComponents', - }, + defaultProperties: {}, + propertyPath: 'definitions/radioAndCheckboxComponents', + icon: Likert, + }, + [ComponentType.LikertItem]: { + name: ComponentType.LikertItem, + itemType: LayoutItemType.Component, + defaultProperties: {}, + propertyPath: 'definitions/radioAndCheckboxComponents', icon: Likert, }, [ComponentType.Link]: { name: ComponentType.Link, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Link, + style: 'link', }, icon: LinkIcon, }, @@ -357,136 +279,98 @@ export const formItemConfigs: FormItemConfigs = { name: ComponentType.List, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.List, - propertyPath: 'definitions/listComponent', + tableHeaders: {}, + dataListId: '', }, + propertyPath: 'definitions/listComponent', icon: TasklistIcon, }, [ComponentType.Map]: { name: ComponentType.Map, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Map, - dataModelBindings: {}, centerLocation: { latitude: 0, longitude: 0, }, zoom: 1, - required: true, - propertyPath: 'definitions/mapComponent', }, + propertyPath: 'definitions/mapComponent', icon: PinIcon, }, [ComponentType.MultipleSelect]: { name: ComponentType.MultipleSelect, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.MultipleSelect, - dataModelBindings: {}, optionsId: '', - required: true, - propertyPath: 'definitions/selectionComponents', }, + propertyPath: 'definitions/selectionComponents', icon: Select, }, [ComponentType.NavigationBar]: { name: ComponentType.NavigationBar, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.NavigationBar, - propertyPath: 'definitions/navigationBarComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/navigationBarComponent', icon: NavBar, }, [ComponentType.NavigationButtons]: { name: ComponentType.NavigationButtons, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.NavigationButtons, - onClickAction: () => {}, - propertyPath: 'definitions/navigationButtonsComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/navigationButtonsComponent', icon: FingerButtonIcon, }, [ComponentType.Panel]: { name: ComponentType.Panel, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Panel, variant: FormPanelVariant.Info, showIcon: true, - propertyPath: 'definitions/panelComponent', }, + propertyPath: 'definitions/panelComponent', icon: FileTextIcon, }, [ComponentType.Paragraph]: { name: ComponentType.Paragraph, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Paragraph, - }, + defaultProperties: {}, icon: Paragraph, }, [ComponentType.PrintButton]: { name: ComponentType.PrintButton, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.PrintButton, - }, + defaultProperties: {}, icon: FingerButtonIcon, }, [ComponentType.RadioButtons]: { name: ComponentType.RadioButtons, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.RadioButtons, - dataModelBindings: {}, - required: true, - propertyPath: 'definitions/radioAndCheckboxComponents', - }, + defaultProperties: {}, + propertyPath: 'definitions/radioAndCheckboxComponents', icon: RadioButton, }, + [ComponentType.RepeatingGroup]: { + name: ComponentType.RepeatingGroup, + itemType: LayoutItemType.Container, + defaultProperties: {}, + icon: Group, + validChildTypes: Object.values(ComponentType), + }, [ComponentType.Summary]: { name: ComponentType.Summary, itemType: LayoutItemType.Component, defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.Summary, - propertyPath: 'definitions/summaryComponent', + componentRef: '', }, + propertyPath: 'definitions/summaryComponent', icon: FileTextIcon, }, [ComponentType.TextArea]: { name: ComponentType.TextArea, itemType: LayoutItemType.Component, - defaultProperties: { - id: '', - itemType: 'COMPONENT', - type: ComponentType.TextArea, - dataModelBindings: {}, - required: true, - propertyPath: 'definitions/textAreaComponent', - }, + defaultProperties: {}, + propertyPath: 'definitions/textAreaComponent', icon: LongText, }, }; @@ -502,6 +386,8 @@ export const advancedItems: FormItemConfigs[ComponentType][] = [ formItemConfigs[ComponentType.Accordion], formItemConfigs[ComponentType.AccordionGroup], formItemConfigs[ComponentType.List], + formItemConfigs[ComponentType.Custom], + formItemConfigs[ComponentType.RepeatingGroup], ]; export const schemaComponents: FormItemConfigs[ComponentType][] = [ @@ -512,10 +398,12 @@ export const schemaComponents: FormItemConfigs[ComponentType][] = [ formItemConfigs[ComponentType.Dropdown], formItemConfigs[ComponentType.MultipleSelect], formItemConfigs[ComponentType.Likert], + formItemConfigs[ComponentType.LikertItem], formItemConfigs[ComponentType.Datepicker], formItemConfigs[ComponentType.FileUpload], formItemConfigs[ComponentType.FileUploadWithTag], formItemConfigs[ComponentType.Button], + formItemConfigs[ComponentType.CustomButton], formItemConfigs[ComponentType.NavigationButtons], formItemConfigs[ComponentType.PrintButton], formItemConfigs[ComponentType.InstantiationButton], diff --git a/frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.test.ts b/frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.test.ts new file mode 100644 index 00000000000..f06f6893b7f --- /dev/null +++ b/frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.test.ts @@ -0,0 +1,11 @@ +import { renderHook } from '@testing-library/react'; +import { useComponentPropertyLabel } from './useComponentPropertyLabel'; +import { textMock } from '../../../../testing/mocks/i18nMock'; + +describe('useComponentPropertyLabel', () => { + it('Returns a function that returns the property name', () => { + const result = renderHook(() => useComponentPropertyLabel()).result.current; + const propertyLabel = result('testProperty'); + expect(propertyLabel).toEqual(textMock('ux_editor.component_properties.testProperty')); + }); +}); diff --git a/frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.ts b/frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.ts new file mode 100644 index 00000000000..e8f00f9c7b0 --- /dev/null +++ b/frontend/packages/ux-editor/src/hooks/useComponentPropertyLabel.ts @@ -0,0 +1,13 @@ +import { useTranslation } from 'react-i18next'; +import { useCallback } from 'react'; + +export const useComponentPropertyLabel = () => { + const { t } = useTranslation(); + return useCallback( + (propertyKey: string) => { + const translationKey: string = `ux_editor.component_properties.${propertyKey}`; + return t([translationKey, propertyKey]); + }, + [t], + ); +}; diff --git a/frontend/packages/ux-editor/src/testing/componentMocks.ts b/frontend/packages/ux-editor/src/testing/componentMocks.ts index e7b989e057c..744cb02e11f 100644 --- a/frontend/packages/ux-editor/src/testing/componentMocks.ts +++ b/frontend/packages/ux-editor/src/testing/componentMocks.ts @@ -1,6 +1,7 @@ import type { FormComponent, FormComponentBase } from '../types/FormComponent'; import { ComponentType } from 'app-shared/types/ComponentType'; import { FormPanelVariant } from 'app-shared/types/FormPanelVariant'; +import type { FormContainer } from '../types/FormContainer'; const commonProps: Pick = { id: 'test', @@ -89,8 +90,9 @@ const addressComponent: FormComponent = { type: ComponentType.AddressComponent, simplified: true, }; -const groupComponent: FormComponent = { +const groupComponent: FormContainer = { ...commonProps, + itemType: 'CONTAINER', type: ComponentType.Group, }; const navigationBarComponent: FormComponent = { diff --git a/frontend/packages/ux-editor/src/testing/layoutMock.ts b/frontend/packages/ux-editor/src/testing/layoutMock.ts index de115de25ff..cd412a6030d 100644 --- a/frontend/packages/ux-editor/src/testing/layoutMock.ts +++ b/frontend/packages/ux-editor/src/testing/layoutMock.ts @@ -15,7 +15,7 @@ export const layout2NameMock = 'Side2'; export const baseContainerIdMock = BASE_CONTAINER_ID; export const component1IdMock = 'Component-1'; export const component1TypeMock = ComponentType.Input; -export const component1Mock: FormComponent = { +export const component1Mock: FormComponent = { id: component1IdMock, type: component1TypeMock, itemType: 'COMPONENT', @@ -24,7 +24,7 @@ export const component1Mock: FormComponent = { }; export const component2IdMock = 'Component-2'; export const component2TypeMock = ComponentType.Paragraph; -export const component2Mock: FormComponent = { +export const component2Mock: FormComponent = { id: component2IdMock, type: component2TypeMock, itemType: 'COMPONENT', diff --git a/frontend/packages/ux-editor/src/types/ContainerComponent.ts b/frontend/packages/ux-editor/src/types/ContainerComponent.ts index f95b9f50392..811146a6e7e 100644 --- a/frontend/packages/ux-editor/src/types/ContainerComponent.ts +++ b/frontend/packages/ux-editor/src/types/ContainerComponent.ts @@ -4,4 +4,5 @@ export type ContainerComponentType = | ComponentType.Accordion | ComponentType.AccordionGroup | ComponentType.ButtonGroup - | ComponentType.Group; + | ComponentType.Group + | ComponentType.RepeatingGroup; diff --git a/frontend/packages/ux-editor/src/types/FormComponent.ts b/frontend/packages/ux-editor/src/types/FormComponent.ts index fe7e2bcdd36..1563e2e4102 100644 --- a/frontend/packages/ux-editor/src/types/FormComponent.ts +++ b/frontend/packages/ux-editor/src/types/FormComponent.ts @@ -2,6 +2,8 @@ import type { ComponentType } from 'app-shared/types/ComponentType'; import type { IDataModelBindings, ITextResourceBindings, IOption } from './global'; import type { ComponentSpecificConfig } from 'app-shared/types/ComponentSpecificConfig'; import type { FormComponent } from '../components/FormComponent'; +import type { SimpleComponentType } from './SimpleComponentType'; +import type { GridSizes } from '../components/config/editModal/EditGrid/types/GridSizes'; export interface FormComponentBase { id: string; @@ -25,6 +27,7 @@ export interface FormComponentBase { required?: boolean | any; hidden?: boolean | any; readOnly?: boolean | any; + grid?: GridSizes; [id: string]: any; propertyPath?: string; } @@ -39,7 +42,7 @@ export type FormButtonComponent = FormComponent< >; export type FormAddressComponent = FormComponent; -export type FormComponent = { +export type FormComponent = { [componentType in ComponentType]: FormComponentBase & ComponentSpecificConfig; }[T]; diff --git a/frontend/packages/ux-editor/src/types/FormContainer.ts b/frontend/packages/ux-editor/src/types/FormContainer.ts index fab4b61700e..cb49389c6d1 100644 --- a/frontend/packages/ux-editor/src/types/FormContainer.ts +++ b/frontend/packages/ux-editor/src/types/FormContainer.ts @@ -1,18 +1,20 @@ import type { IDataModelBindings, ITextResourceBindings } from './global'; import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; import type { ContainerComponentType } from './ContainerComponent'; +import type { GridSizes } from '../components/config/editModal/EditGrid/types/GridSizes'; -export interface FormContainer { +export interface FormContainer { dataModelBindings?: IDataModelBindings; id: string; index?: number; itemType: 'CONTAINER'; - type: ContainerComponentType; + type: T; maxCount?: number; pageIndex?: number; tableHeaders?: string[]; textResourceBindings?: ITextResourceBindings; propertyPath?: string; edit?: KeyValuePairs; + grid?: GridSizes; [id: string]: any; } diff --git a/frontend/packages/ux-editor/src/types/FormItem.ts b/frontend/packages/ux-editor/src/types/FormItem.ts index 4340a4018f6..1a524e8bde5 100644 --- a/frontend/packages/ux-editor/src/types/FormItem.ts +++ b/frontend/packages/ux-editor/src/types/FormItem.ts @@ -2,7 +2,10 @@ import type { FormComponent } from './FormComponent'; import type { FormContainer } from './FormContainer'; import type { ContainerComponentType } from './ContainerComponent'; import type { ComponentType } from 'app-shared/types/ComponentType'; +import type { SimpleComponentType } from './SimpleComponentType'; export type FormItem = T extends ContainerComponentType - ? FormContainer - : FormComponent; + ? FormContainer + : T extends SimpleComponentType + ? FormComponent + : never; diff --git a/frontend/packages/ux-editor/src/utils/component.ts b/frontend/packages/ux-editor/src/utils/component.ts index c3e9c9edead..06c2fde79cc 100644 --- a/frontend/packages/ux-editor/src/utils/component.ts +++ b/frontend/packages/ux-editor/src/utils/component.ts @@ -9,6 +9,7 @@ import type { ComponentType } from 'app-shared/types/ComponentType'; import { formItemConfigs } from '../data/formItemConfig'; import type { FormItem } from '../types/FormItem'; import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; +import type { FilterKeysOfType } from 'app-shared/types/FilterKeysOfType'; export function getTextResourceByAddressKey(key: AddressKeys, t: (key: string) => string): string { switch (key) { @@ -91,8 +92,8 @@ export const generateRandomOption = (): IOption => ({ label: '', value: generate * @returns A component of the given type. */ export const generateFormItem = (type: T, id: string): FormItem => { - const { defaultProperties } = formItemConfigs[type]; - return { ...defaultProperties, id }; + const { defaultProperties, itemType } = formItemConfigs[type]; + return { ...defaultProperties, id, type, itemType } as FormItem; }; /** @@ -102,11 +103,15 @@ export const generateFormItem = (type: T, id: string): * @param value The value to set the property to. * @returns The component with updated property. */ -export const setComponentProperty = ( - component: FormComponent, - propertyKey: string, - value: any, -): FormComponent => ({ +export const setComponentProperty = < + T extends ComponentType, + V, + K extends FilterKeysOfType, V>, +>( + component: FormItem, + propertyKey: K, + value: V, +): FormItem => ({ ...component, [propertyKey]: value, }); diff --git a/frontend/packages/ux-editor/src/utils/formLayoutUtils.test.tsx b/frontend/packages/ux-editor/src/utils/formLayoutUtils.test.tsx index b7a2ef764c7..9cc77d90d35 100644 --- a/frontend/packages/ux-editor/src/utils/formLayoutUtils.test.tsx +++ b/frontend/packages/ux-editor/src/utils/formLayoutUtils.test.tsx @@ -37,7 +37,7 @@ import { import { containerComponentTypes } from '../data/containerComponentTypes'; // Test data: -const baseContainer: FormContainer = { +const baseContainer: FormContainer = { id: BASE_CONTAINER_ID, index: 0, itemType: 'CONTAINER', @@ -67,7 +67,7 @@ const paragraphComponent: FormComponent = { customProperty, }; const groupId = 'group-container'; -const groupContainer: FormContainer = { +const groupContainer: FormContainer = { dataModelBindings: {}, id: groupId, itemType: 'CONTAINER', @@ -84,14 +84,14 @@ const paragraphInGroupComponent: FormComponent = { dataModelBindings: {}, }; const groupInGroupId = 'group-child-container'; -const groupInGroupContainer: FormContainer = { +const groupInGroupContainer: FormContainer = { dataModelBindings: {}, id: groupInGroupId, itemType: 'CONTAINER', type: ComponentType.Group, }; const buttonGroupId = 'button-group-container'; -const buttonGroupContainer: FormContainer = { +const buttonGroupContainer: FormContainer = { dataModelBindings: {}, id: buttonGroupId, itemType: 'CONTAINER', @@ -231,7 +231,11 @@ describe('formLayoutUtils', () => { describe('addContainer', () => { const id = 'testId'; - const newContainer: FormContainer = { id, itemType: 'CONTAINER', type: ComponentType.Group }; + const newContainer: FormContainer = { + id, + itemType: 'CONTAINER', + type: ComponentType.Group, + }; it('Adds container to the end of the base container by default', () => { const layout = addContainer(mockInternal, newContainer, id); @@ -280,7 +284,10 @@ describe('formLayoutUtils', () => { describe('updateContainer', () => { const containerId = groupId; const newContainerId = groupId + '-new'; - const updatedContainer: FormContainer = { ...groupContainer, id: newContainerId }; + const updatedContainer: FormContainer = { + ...groupContainer, + id: newContainerId, + }; it('Updates container based on the given container id', () => { const layout = updateContainer(mockInternal, updatedContainer, containerId); @@ -434,7 +441,7 @@ describe('formLayoutUtils', () => { it('Returns 1 if there is a group', () => { const id = 'test'; - const container: FormContainer = { + const container: FormContainer = { id, itemType: 'CONTAINER', pageIndex: null, @@ -446,7 +453,7 @@ describe('formLayoutUtils', () => { it('Returns 1 if there is a group with components only', () => { const id = 'test'; - const container: FormContainer = { + const container: FormContainer = { id, itemType: 'CONTAINER', pageIndex: null, @@ -470,7 +477,7 @@ describe('formLayoutUtils', () => { it('Returns 3 if there is a group within a group within a group', () => { let layout = deepCopy(mockInternal); - const container: FormContainer = { + const container: FormContainer = { id: groupInGroupId, itemType: 'CONTAINER', type: ComponentType.Group, @@ -487,7 +494,7 @@ describe('formLayoutUtils', () => { it('Returns false if the depth is invalid', () => { let layout = deepCopy(mockInternal); - const container: FormContainer = { + const container: FormContainer = { id: groupInGroupId, itemType: 'CONTAINER', type: ComponentType.Group, diff --git a/frontend/packages/ux-editor/src/utils/formLayoutUtils.ts b/frontend/packages/ux-editor/src/utils/formLayoutUtils.ts index 3583deed216..0578264f503 100644 --- a/frontend/packages/ux-editor/src/utils/formLayoutUtils.ts +++ b/frontend/packages/ux-editor/src/utils/formLayoutUtils.ts @@ -17,6 +17,7 @@ import { formItemConfigs } from '../data/formItemConfig'; import type { FormContainer } from '../types/FormContainer'; import type { FormItem } from '../types/FormItem'; import * as formItemUtils from './formItemUtils'; +import type { ContainerComponentType } from '../types/ContainerComponent'; export const mapComponentToToolbarElement = ( c: FormItemConfigs[T], @@ -128,9 +129,9 @@ const findPositionOfPreviousComponent = ( * @param position The desired index of the container within its parent container. Set it to a negative value to add it at the end. Defaults to -1. * @returns The new layout. */ -export const addContainer = ( +export const addContainer = ( layout: IInternalLayout, - container: FormContainer, + container: FormContainer, id: string, parentId: string = BASE_CONTAINER_ID, position: number = -1, @@ -151,9 +152,9 @@ export const addContainer = ( * @param containerId The current id of the updated container. * @returns The new layout. */ -export const updateContainer = ( +export const updateContainer = ( layout: IInternalLayout, - updatedContainer: FormContainer, + updatedContainer: FormContainer, containerId: string, ): IInternalLayout => { const oldLayout: IInternalLayout = deepCopy(layout); diff --git a/frontend/packages/ux-editor/src/utils/language.ts b/frontend/packages/ux-editor/src/utils/language.ts index 0fc641e9ac7..b16f626176f 100644 --- a/frontend/packages/ux-editor/src/utils/language.ts +++ b/frontend/packages/ux-editor/src/utils/language.ts @@ -2,8 +2,6 @@ import type { ITextResource } from 'app-shared/types/global'; import { CollapsableMenus } from '../types/global'; import type { ComponentType } from 'app-shared/types/ComponentType'; import type i18next from 'i18next'; -import type { UseText } from '../hooks'; -import type { TranslationKey } from 'app-shared/types/language'; /** * Get the help text for a given component type @@ -68,9 +66,3 @@ export function getTextResource(resourceKey: string, textResources: ITextResourc const textResource = textResources.find((resource) => resource.id === resourceKey); return textResource?.value; } - -export const getComponentPropertyLabel = (propertyKey: string, t: UseText): string => { - const translationKey: string = `ux_editor.component_properties.${propertyKey}`; - const translation = t(translationKey as TranslationKey); - return translation === translationKey ? propertyKey : translation; -}; diff --git a/frontend/testing/mocks/i18nMock.ts b/frontend/testing/mocks/i18nMock.ts index 33946279a18..d5bddbe1f81 100644 --- a/frontend/testing/mocks/i18nMock.ts +++ b/frontend/testing/mocks/i18nMock.ts @@ -5,7 +5,9 @@ export const mockUseTranslation = (texts: { [key: string]: string } = {}) => ({ t: ((key: string) => texts[key] ?? key) as typeof i18next.t, }); -export const textMock = ((key: string, variables?: KeyValuePairs) => - variables +export const textMock = ((keys: string | string[], variables?: KeyValuePairs) => { + const key = Array.isArray(keys) ? keys[0] : keys; + return variables ? '[mockedText(' + key + ', ' + JSON.stringify(variables) + ')]' - : '[mockedText(' + key + ')]') as typeof i18next.t; + : '[mockedText(' + key + ')]'; +}) as typeof i18next.t; From 97fef7614a689d3aecb2c6a65b2ad99e0888fd63 Mon Sep 17 00:00:00 2001 From: JamalAlabdullah <90609090+JamalAlabdullah@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:33:27 +0100 Subject: [PATCH 2/2] Bug/12035 not possible to delete a data model in studio (#12115) * fixed removing datamodell and old datamodell --- .../SelectedSchemaEditor.test.tsx | 74 +++++++++++++++---- .../SelectedSchemaEditor.tsx | 28 ++++++- .../useDeleteDatamodelMutation.test.ts | 71 ++++++++++++++++++ .../mutations/useDeleteDatamodelMutation.ts | 35 ++++++--- .../src/mocks/datamodelMetadataMocks.ts | 24 +++--- .../shared/src/mocks/modelPathMocks.ts | 2 + frontend/testing/testUtils.ts | 3 + 7 files changed, 201 insertions(+), 36 deletions(-) create mode 100644 frontend/app-development/hooks/mutations/useDeleteDatamodelMutation.test.ts create mode 100644 frontend/packages/shared/src/mocks/modelPathMocks.ts 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/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/testing/testUtils.ts b/frontend/testing/testUtils.ts index 0f2c2e5cdbb..61363927f41 100644 --- a/frontend/testing/testUtils.ts +++ b/frontend/testing/testUtils.ts @@ -1,3 +1,4 @@ +import { waitFor } from '@testing-library/react'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; export const TEST_DOMAIN = 'http://localhost'; @@ -8,3 +9,5 @@ export const setWindowLocationForTests = (org: string, app: string) => { `${TEST_DOMAIN}${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, ) as unknown as Location; }; + +export const verifyNeverOccurs = (fn: () => void) => expect(waitFor(fn)).rejects.toThrow();