Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { DataModelBindings } from './DataModelBindings';
import { FormItemContext } from '../../containers/FormItemContext';
Expand Down Expand Up @@ -292,6 +292,9 @@ describe('DataModelBindings', () => {
const dataModelFieldSelector = screen.getByRole('combobox', {
name: textMock('ux_editor.modal_properties_data_model_field_binding'),
});
await waitFor(() => {
expect(formItemContextProviderMock.handleUpdate).toHaveBeenCalled();
});
const option = screen.getByRole('option', { name: defaultModel });
await user.selectOptions(dataModelFieldSelector, option);

Expand All @@ -300,8 +303,8 @@ describe('DataModelBindings', () => {
});
await user.click(saveButton);

expect(formItemContextProviderMock.handleUpdate).toHaveBeenCalledTimes(1);
expect(formItemContextProviderMock.debounceSave).toHaveBeenCalledTimes(1);
expect(formItemContextProviderMock.handleUpdate).toHaveBeenCalledTimes(2);
expect(formItemContextProviderMock.debounceSave).toHaveBeenCalledTimes(2);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
import { componentMocks } from '@altinn/ux-editor/testing/componentMocks';
import { ComponentType } from 'app-shared/types/ComponentType';
import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext';
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import { textMock } from '@studio/testing/mocks/i18nMock';
import userEvent from '@testing-library/user-event';
import { layoutSet1NameMock } from '../../../../../testing/layoutSetsMock';
Expand Down Expand Up @@ -147,7 +147,7 @@ describe('EditBinding', () => {
expect(dataModelFieldSelector).toBeInTheDocument();
});

it('should display default data model and "choose datafield" when no bindings', async () => {
it('should display default data model and auto-select first field when no bindings', async () => {
renderEditBinding({
editBindingProps: {
...defaultEditBinding,
Expand All @@ -170,13 +170,14 @@ describe('EditBinding', () => {
name: textMock('ux_editor.modal_properties_data_model_binding'),
});
expect(dataModelSelector).toHaveValue(defaultDataModel);

const chooseDataFieldOption: HTMLOptionElement = screen.getByRole('option', {
name: textMock('ux_editor.modal_properties_data_model_field_choose'),
await screen.findByRole('combobox', {
name: textMock('ux_editor.modal_properties_data_model_field_binding'),
});

expect(chooseDataFieldOption).toHaveValue('');
expect(chooseDataFieldOption.selected).toBe(true);
const dataFieldSelector = screen.getByRole('combobox', {
name: textMock('ux_editor.modal_properties_data_model_field_binding'),
});
expect(dataFieldSelector).toHaveValue(defaultDataModelField);
});

it('should render error message when data model is not valid', async () => {
Expand Down Expand Up @@ -314,6 +315,9 @@ describe('EditBinding', () => {
const dataFieldSelector = screen.getByRole('combobox', {
name: textMock('ux_editor.modal_properties_data_model_field_binding'),
});
await waitFor(() => {
expect(dataFieldSelector).toHaveValue(defaultDataModelField);
});
const secondDataFieldOption = screen.getByRole('option', { name: secondDataModelField });
await user.selectOptions(dataFieldSelector, secondDataFieldOption);

Expand All @@ -323,8 +327,8 @@ describe('EditBinding', () => {

await user.click(saveButton);

expect(handleComponentChange).toHaveBeenCalledTimes(1);
expect(handleComponentChange).toHaveBeenCalledWith(
expect(handleComponentChange).toHaveBeenCalledTimes(2);
expect(handleComponentChange).toHaveBeenLastCalledWith(
expect.objectContaining({
dataModelBindings: expect.objectContaining({
[defaultEditBinding.bindingKey]: expect.objectContaining({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import React, { useCallback, useEffect } from 'react';
import type { FormItem } from '../../../../../types/FormItem';
import { SelectDataModelBinding } from './SelectDataModelBinding';
import { SelectDataFieldBinding } from './SelectDataFieldBinding';
import {
getDataModelFields,
getMaxOccursFromDataModelFields,
getMinOccursFromDataModelFields,
getXsdDataTypeFromDataModelFields,
Expand Down Expand Up @@ -40,46 +41,88 @@ export const EditBinding = ({
const { t } = useTranslation();
const { updateLayoutsForPreview } = useAppContext();
const { layoutSet } = useUxEditorParams();
const { dataModelMetadata, isLoadingDataModels } = useValidDataModels(
const { dataModelMetadata, isLoadingDataModels, selectedDataModel } = useValidDataModels(
internalBindingFormat?.dataType,
);

const handleBindingChange = (updatedBinding?: ExplicitDataModelBinding) => {
const selectedDataFieldElement = updatedBinding?.field;
const saveBinding = useCallback(
(updatedBinding?: ExplicitDataModelBinding) => {
const selectedDataFieldElement = updatedBinding?.field;
const value =
updatedBinding ??
formItemConfigs[component.type]?.defaultProperties?.['dataModelBindings']?.[bindingKey];
const dataModelBindings = { ...component.dataModelBindings };
if (value === undefined || value === null) {
delete dataModelBindings[bindingKey];
} else {
dataModelBindings[bindingKey] = value;
}
handleComponentChange(
{
...component,
dataModelBindings: Object.keys(dataModelBindings).length ? dataModelBindings : undefined,
required: getMinOccursFromDataModelFields(selectedDataFieldElement, dataModelMetadata),
timeStamp: getXsdDataTypeFromDataModelFields(
component.type,
selectedDataFieldElement,
dataModelMetadata,
),
maxCount: getMaxOccursFromDataModelFields(
component.type,
selectedDataFieldElement,
dataModelMetadata,
),
} as FormItem,
{
onSuccess: async () => {
await updateLayoutsForPreview(layoutSet, true);
},
},
);
},
[
component,
bindingKey,
dataModelMetadata,
handleComponentChange,
layoutSet,
updateLayoutsForPreview,
],
);

const value =
updatedBinding ??
formItemConfigs[component.type]?.defaultProperties?.['dataModelBindings']?.[bindingKey];
const handleAutoSave = useCallback(() => {
const dataModelFields = getDataModelFields({
componentType: component.type,
bindingKey,
dataModelMetadata,
});
const firstField = dataModelFields[0];
const dataTypeToUse = binding?.dataType || selectedDataModel;

const dataModelBindings = { ...component.dataModelBindings };
if (value === undefined || value === null) {
delete dataModelBindings[bindingKey];
} else {
dataModelBindings[bindingKey] = value;
if (dataTypeToUse && dataModelMetadata && !binding?.field && firstField?.value) {
const autoBinding = {
field: firstField.value,
dataType: dataTypeToUse,
};
setBinding(autoBinding);
saveBinding(autoBinding);
}
}, [
selectedDataModel,
dataModelMetadata,
binding?.field,
binding?.dataType,
component.type,
bindingKey,
saveBinding,
]);

handleComponentChange(
{
...component,
dataModelBindings: Object.keys(dataModelBindings).length ? dataModelBindings : undefined,
required: getMinOccursFromDataModelFields(selectedDataFieldElement, dataModelMetadata),
timeStamp: getXsdDataTypeFromDataModelFields(
component.type,
selectedDataFieldElement,
dataModelMetadata,
),
maxCount: getMaxOccursFromDataModelFields(
component.type,
selectedDataFieldElement,
dataModelMetadata,
),
} as FormItem,
{
onSuccess: async () => {
await updateLayoutsForPreview(layoutSet, true);
},
},
);
useEffect(() => {
handleAutoSave();
}, [handleAutoSave]);

const handleBindingChange = (updatedBinding?: ExplicitDataModelBinding) => {
saveBinding(updatedBinding);
onSetDataModelSelectVisible(false);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export const SelectDataFieldBinding = ({
);
const componentPropertyHelpText = useComponentPropertyHelpText();

const selectedDataField =
currentDataModelField || (dataModelFields.length > 0 ? dataModelFields[0]?.value : '');

const handleDataModelFieldChange = (updatedDataModelField: string) => {
const updatedDataModelBinding = {
field: updatedDataModelField,
Expand All @@ -58,7 +61,7 @@ export const SelectDataFieldBinding = ({
<FormField
id={`selectDataModelField-${bindingKey}`}
onChange={handleDataModelFieldChange}
value={isBindingError ? '' : currentDataModelField}
value={isBindingError ? '' : selectedDataField}
propertyPath={propertyPath}
helpText={componentPropertyHelpText(`data_model_bindings.${bindingKey}`)}
label={t('ux_editor.modal_properties_data_model_field_binding')}
Expand Down
24 changes: 12 additions & 12 deletions src/Designer/frontend/testing/playwright/pages/UiEditorPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,11 @@ export class UiEditorPage extends BasePage {
}

public async clickOnDataModelFieldPropertyOption(option: string): Promise<void> {
await this.page
.getByRole('combobox', {
name: this.textMock('ux_editor.modal_properties_data_model_field_binding'),
})
.selectOption(option);
const combobox = this.page.getByRole('combobox', {
name: this.textMock('ux_editor.modal_properties_data_model_field_binding'),
});
await combobox.selectOption(option);
await expect(combobox).toHaveValue(option);
}

public async clickOnDataModelPropertyOption(option: string): Promise<void> {
Expand All @@ -207,20 +207,20 @@ export class UiEditorPage extends BasePage {
}

public async clickOnSaveDataModel(): Promise<void> {
await this.page
.getByRole('button', {
name: this.textMock('general.save'),
exact: true,
})
.click();
const saveButton = this.page.getByRole('button', {
name: this.textMock('general.save'),
exact: true,
});
await expect(saveButton).toBeEnabled();
await saveButton.click();
}

public async waitForDataModelToBeSelected(): Promise<void> {
const saveButton = this.page.getByRole('button', {
name: this.textMock('ux_editor.input_popover_save_button'),
});

await expect(saveButton).toBeHidden();
await expect(saveButton).toBeDisabled();
}

public async waitForTreeItemToGetNewLabel(label: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ test('That it is possible to navigate back to ui-editor page and add the data mo
await uiEditorPage.verifyThatThereAreOptionsInTheDataModelFieldList();

await uiEditorPage.clickOnDataModelFieldPropertyOption(dataModelBindingName);
await uiEditorPage.clickOnSaveDataModel();
await uiEditorPage.waitForDataModelToBeSelected();
});

Expand Down Expand Up @@ -189,7 +188,6 @@ test('That it is possible to navigate back to ui-editor page and add the newly a
await uiEditorPage.clickOnDataModelPropertyOption(newDataModel);
await uiEditorPage.clickOnDataModelFieldBindingCombobox();
await uiEditorPage.clickOnDataModelFieldPropertyOption(dataModelBindingName);
await uiEditorPage.clickOnSaveDataModel();
await uiEditorPage.waitForDataModelToBeSelected();
});

Expand Down
Loading