Skip to content

Commit

Permalink
feat: added validation when creating new datamodel in subform (#14233)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamalAlabdullah authored Dec 12, 2024
1 parent fd1fcf5 commit db0fbcd
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,31 @@ describe('CreateNewSubformLayoutSet ', () => {
expect(saveButton).toBeDisabled();
});

it('Toggles the save button disabling based on data model input validation', async () => {
const user = userEvent.setup();
renderCreateNewSubformLayoutSet({});

const input = screen.getByRole('textbox');
await user.type(input, 'NewSubform');

const saveButton = screen.getByRole('button', { name: textMock('general.save') });

const displayDataModelInput = screen.getByRole('button', {
name: textMock('ux_editor.component_properties.subform.create_new_data_model'),
});
await user.click(displayDataModelInput);

const dataModelInput = screen.getByRole('textbox', {
name: textMock('ux_editor.component_properties.subform.create_new_data_model_label'),
});
await user.type(dataModelInput, 'æøå');
expect(saveButton).toBeDisabled();

await user.clear(dataModelInput);
await user.type(dataModelInput, 'datamodel');
expect(saveButton).not.toBeDisabled();
});

it('enables save button when both input and data model is valid', async () => {
const user = userEvent.setup();
renderCreateNewSubformLayoutSet({});
Expand Down Expand Up @@ -159,6 +184,31 @@ describe('CreateNewSubformLayoutSet ', () => {
await user.type(dataModelInput, 'datamodel');
expect(saveButton).not.toBeDisabled();
});

it('Should toggle ErrorMessage visibility based on input validity', async () => {
const user = userEvent.setup();
renderCreateNewSubformLayoutSet({});

const input = screen.getByRole('textbox');
await user.type(input, 'NewSubform');

const displayDataModelInput = screen.getByRole('button', {
name: textMock('ux_editor.component_properties.subform.create_new_data_model'),
});
await user.click(displayDataModelInput);

const dataModelInput = screen.getByRole('textbox', {
name: textMock('ux_editor.component_properties.subform.create_new_data_model_label'),
});

await user.type(dataModelInput, 'new');
const errorMessage = screen.getByText(textMock('schema_editor.error_reserved_keyword'));
expect(errorMessage).toBeInTheDocument();

await user.clear(dataModelInput);
await user.type(dataModelInput, 'datamodel');
expect(errorMessage).not.toBeInTheDocument();
});
});

type RenderCreateNewSubformLayoutSetProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { SubformDataModel } from './SubformDataModel';
import { CreateNewSubformButtons } from './CreateNewSubformButtons';
import { SubformInstructions } from './SubformInstructions';
import { useCreateSubform } from '@altinn/ux-editor/hooks/useCreateSubform';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import { useAppMetadataModelIdsQuery } from 'app-shared/hooks/queries/useAppMetadataModelIdsQuery';
import { useAppMetadataQuery } from 'app-shared/hooks/queries';
import { extractDataTypeNamesFromAppMetadata } from 'app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils';
import { useValidateSchemaName } from 'app-shared/hooks/useValidateSchemaName';

type CreateNewSubformSectionProps = {
layoutSets: LayoutSets;
Expand All @@ -33,26 +38,36 @@ export const CreateNewSubformSection = ({
}: CreateNewSubformSectionProps): React.ReactElement => {
const { t } = useTranslation();
const { validateLayoutSetName } = useValidateLayoutSetName();
const [nameError, setNameError] = useState<string>();
const [newDataModel, setNewDataModel] = useState<string>('');
const [newSubformNameError, setNewSubformNameError] = useState<string>();
const [selectedDataModel, setSelectedDataModel] = useState<string>('');
const [displayDataModelInput, setDisplayDataModelInput] = useState(false);
const { createSubform, isPendingNewSubformMutation } = useCreateSubform();
const [isNewDataModelFieldEmpty, setIsNewDataModelFieldEmpty] = useState(true);

const { org, app } = useStudioEnvironmentParams();
const { data: dataModelIds } = useAppMetadataModelIdsQuery(org, app, false);
const { data: appMetadata } = useAppMetadataQuery(org, app);
const dataTypeNames = extractDataTypeNamesFromAppMetadata(appMetadata);
const {
validateName,
nameError: dataModelNameError,
setNameError: setDataModelNameError,
} = useValidateSchemaName(dataModelIds, dataTypeNames);

const handleSubformName = (subformName: string) => {
const subformNameValidation = validateLayoutSetName(subformName, layoutSets);
setNameError(subformNameValidation);
setNewSubformNameError(subformNameValidation);
};

const handleCloseButton = () => {
if (displayDataModelInput) {
setNewDataModel('');
setDataModelNameError('');
setIsNewDataModelFieldEmpty(true);
setDisplayDataModelInput(false);
} else {
setShowCreateSubformCard(false);
}
};

const handleCreateSubformSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
const formData: FormData = new FormData(e.currentTarget);
Expand All @@ -68,8 +83,11 @@ export const CreateNewSubformSection = ({
});
};

const hasInvalidSubformName = nameError === undefined || Boolean(nameError);
const hasInvalidDataModel = displayDataModelInput ? !newDataModel : !selectedDataModel;
const hasInvalidSubformName = newSubformNameError === undefined || Boolean(newSubformNameError);
const hasInvalidDataModel = displayDataModelInput
? Boolean(dataModelNameError) || isNewDataModelFieldEmpty
: !selectedDataModel;
const disableSaveButton = hasInvalidSubformName || hasInvalidDataModel;

return (
<StudioRecommendedNextAction
Expand All @@ -95,17 +113,20 @@ export const CreateNewSubformSection = ({
size='sm'
disabled={isPendingNewSubformMutation}
onChange={(e) => handleSubformName(e.target.value)}
error={nameError}
error={newSubformNameError}
/>
<SubformDataModel
setDisplayDataModelInput={setDisplayDataModelInput}
setNewDataModel={setNewDataModel}
displayDataModelInput={displayDataModelInput}
setSelectedDataModel={setSelectedDataModel}
dataModelIds={dataModelIds}
validateName={validateName}
dataModelNameError={dataModelNameError}
setIsTextfieldEmpty={setIsNewDataModelFieldEmpty}
/>
<CreateNewSubformButtons
isPendingNewSubformMutation={isPendingNewSubformMutation}
disableSaveButton={hasInvalidSubformName || hasInvalidDataModel}
disableSaveButton={disableSaveButton}
displayCloseButton={hasSubforms || displayDataModelInput}
handleCloseButton={handleCloseButton}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { useAppMetadataModelIdsQuery } from 'app-shared/hooks/queries/useAppMeta

jest.mock('app-shared/hooks/queries/useAppMetadataModelIdsQuery');

const user = userEvent.setup();

const mockDataModelIds = ['dataModelId1', 'dataModelId2'];

(useAppMetadataModelIdsQuery as jest.Mock).mockReturnValue({ data: mockDataModelIds });
Expand Down Expand Up @@ -41,6 +39,7 @@ describe('SubformDataModel', () => {
});

it('Calls setDataModel when selecting an option', async () => {
const user = userEvent.setup();
const setSelectedDataModel = jest.fn();
renderSubformDataModelSelect({ setSelectedDataModel });

Expand All @@ -53,6 +52,7 @@ describe('SubformDataModel', () => {
});

it('Should call setDisplayDataModelInput true when clicking create new data model button', async () => {
const user = userEvent.setup();
const setDisplayDataModelInput = jest.fn();
renderSubformDataModelSelect({ setDisplayDataModelInput });
const displayDataModelInput = screen.getByRole('button', {
Expand All @@ -75,9 +75,12 @@ describe('SubformDataModel', () => {

const defaultProps: SubformDataModelProps = {
setDisplayDataModelInput: jest.fn(),
setNewDataModel: jest.fn(),
displayDataModelInput: false,
setSelectedDataModel: jest.fn(),
dataModelIds: mockDataModelIds,
validateName: jest.fn(),
dataModelNameError: '',
setIsTextfieldEmpty: jest.fn(),
};

const renderSubformDataModelSelect = (props: Partial<SubformDataModelProps> = {}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,32 @@ import React from 'react';
import { StudioTextfield, StudioNativeSelect, StudioProperty } from '@studio/components';
import { LinkIcon } from '@studio/icons';
import { useTranslation } from 'react-i18next';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import { useAppMetadataModelIdsQuery } from 'app-shared/hooks/queries/useAppMetadataModelIdsQuery';
import classes from './SubformDataModel.module.css';

export type SubformDataModelProps = {
setDisplayDataModelInput: (setDisplayDataModelInput: boolean) => void;
setNewDataModel: (dataModelId: string) => void;
displayDataModelInput: boolean;
setSelectedDataModel: (dataModelId: string) => void;
dataModelIds?: string[];
validateName: (name: string) => void;
dataModelNameError: string;
setIsTextfieldEmpty: (isEmpty: boolean) => void;
};

export const SubformDataModel = ({
setDisplayDataModelInput,
setSelectedDataModel,
setNewDataModel,
displayDataModelInput,
dataModelIds,
validateName,
dataModelNameError,
setIsTextfieldEmpty,
}: SubformDataModelProps): React.ReactElement => {
const { t } = useTranslation();
const { org, app } = useStudioEnvironmentParams();
const { data: dataModelIds } = useAppMetadataModelIdsQuery(org, app, false);

const handleDataModel = (dataModelId: string) => {
// TODO: https://github.com/Altinn/altinn-studio/issues/14184
setNewDataModel(dataModelId);
const handleNewDataModel = (dataModelId: string) => {
validateName(dataModelId);
setIsTextfieldEmpty(dataModelId === '');
};

const handleDisplayInput = () => {
Expand Down Expand Up @@ -59,7 +61,8 @@ export const SubformDataModel = ({
name='newSubformDataModel'
label={t('ux_editor.component_properties.subform.create_new_data_model_label')}
size='sm'
onChange={(e) => handleDataModel(e.target.value)}
onChange={(e) => handleNewDataModel(e.target.value)}
error={dataModelNameError}
/>
) : (
<StudioProperty.Button
Expand Down

0 comments on commit db0fbcd

Please sign in to comment.