diff --git a/frontend/packages/ux-editor/src/components/Elements/Subform/CreateSubformWrapper.tsx b/frontend/packages/ux-editor/src/components/Elements/Subform/CreateSubformWrapper.tsx index 0ce957979c2..44ff25007ef 100644 --- a/frontend/packages/ux-editor/src/components/Elements/Subform/CreateSubformWrapper.tsx +++ b/frontend/packages/ux-editor/src/components/Elements/Subform/CreateSubformWrapper.tsx @@ -1,12 +1,11 @@ import React, { useState } from 'react'; import { StudioButton, StudioPopover, StudioTextfield } from '@studio/components'; import { PlusIcon } from '@studio/icons'; -import { useAddLayoutSetMutation } from 'app-development/hooks/mutations/useAddLayoutSetMutation'; -import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; import { useTranslation } from 'react-i18next'; import { useValidateLayoutSetName } from 'app-shared/hooks/useValidateLayoutSetName'; import type { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; import classes from './CreateSubformWrapper.module.css'; +import { useCreateSubform } from '@altinn/ux-editor/hooks/useCreateSubform'; type CreateSubformWrapperProps = { layoutSets: LayoutSets; @@ -22,20 +21,11 @@ export const CreateSubformWrapper = ({ const [nameError, setNameError] = useState(''); const { t } = useTranslation(); const { validateLayoutSetName } = useValidateLayoutSetName(); - const { org, app } = useStudioEnvironmentParams(); - const { mutate: addLayoutSet } = useAddLayoutSetMutation(org, app); + const { createSubform } = useCreateSubform(); const onCreateConfirmClick = () => { setCreateNewOpen(false); - - addLayoutSet({ - layoutSetIdToUpdate: newSubformName, - layoutSetConfig: { - id: newSubformName, - type: 'subform', - }, - }); - onSubformCreated(newSubformName); + createSubform({ layoutSetName: newSubformName, onSubformCreated }); }; const onNameChange = (subformName: string) => { diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.test.tsx index 8a918996a9a..47bcb935ae0 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.test.tsx @@ -50,6 +50,27 @@ describe('CreateNewSubformLayoutSet ', () => { await waitFor(() => expect(onSubformCreatedMock).toHaveBeenCalledTimes(1)); expect(onSubformCreatedMock).toHaveBeenCalledWith('NewSubform'); }); + + it('disables the save button when input is invalid', async () => { + const user = userEvent.setup(); + renderCreateNewSubformLayoutSet(); + + const saveButton = screen.getByRole('button', { name: textMock('general.close') }); + expect(saveButton).toBeDisabled(); + + const input = screen.getByRole('textbox'); + + await user.type(input, 'æøå'); + expect(saveButton).toBeDisabled(); + + await user.clear(input); + await user.type(input, 'e re a'); + expect(saveButton).toBeDisabled(); + + await user.clear(input); + await user.type(input, 'NewSubform'); + expect(saveButton).not.toBeDisabled(); + }); }); const renderCreateNewSubformLayoutSet = ( @@ -60,7 +81,11 @@ const renderCreateNewSubformLayoutSet = ( queryClient.setQueryData([QueryKey.LayoutSets, org, app], layoutSetsMock); return renderWithProviders( - + , { queryClient }, ); diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.tsx index acaf206ce24..fd041cdd149 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/CreateNewSubformLayoutSet/CreateNewSubformLayoutSet.tsx @@ -2,39 +2,36 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { StudioButton, StudioCard, StudioTextfield } from '@studio/components'; import { ClipboardIcon, CheckmarkIcon } from '@studio/icons'; -import { useAddLayoutSetMutation } from 'app-development/hooks/mutations/useAddLayoutSetMutation'; -import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; import classes from './CreateNewSubformLayoutSet.module.css'; +import { useValidateLayoutSetName } from 'app-shared/hooks/useValidateLayoutSetName'; +import { useCreateSubform } from '@altinn/ux-editor/hooks/useCreateSubform'; +import type { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; type CreateNewSubformLayoutSetProps = { onSubformCreated: (layoutSetName: string) => void; + layoutSets: LayoutSets; }; export const CreateNewSubformLayoutSet = ({ onSubformCreated, + layoutSets, }: CreateNewSubformLayoutSetProps): React.ReactElement => { const { t } = useTranslation(); const [newSubform, setNewSubform] = useState(''); - const { org, app } = useStudioEnvironmentParams(); - const { mutate: addLayoutSet } = useAddLayoutSetMutation(org, app); - - const createNewSubform = () => { - if (!newSubform) return; - addLayoutSet({ - layoutSetIdToUpdate: newSubform, - layoutSetConfig: { - id: newSubform, - type: 'subform', - }, - }); - onSubformCreated(newSubform); - setNewSubform(''); - }; + const { validateLayoutSetName } = useValidateLayoutSetName(); + const { createSubform } = useCreateSubform(); + const [nameError, setNameError] = useState(''); function handleChange(e: React.ChangeEvent) { + const subformNameValidation = validateLayoutSetName(e.target.value, layoutSets); + setNameError(subformNameValidation); setNewSubform(e.target.value); } + function handleCreateSubform() { + createSubform({ layoutSetName: newSubform, onSubformCreated }); + } + return ( @@ -46,12 +43,14 @@ export const CreateNewSubformLayoutSet = ({ value={newSubform} size='sm' onChange={handleChange} + error={nameError} /> } - onClick={createNewSubform} + onClick={handleCreateSubform} title={t('general.close')} + disabled={!newSubform || !!nameError} variant='tertiary' color='success' /> diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/EditLayoutSet.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/EditLayoutSet.tsx index 8a8c957978e..9f1c6baae8e 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/EditLayoutSet.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSet/EditLayoutSet.tsx @@ -6,17 +6,20 @@ import { StudioParagraph, StudioProperty, StudioRecommendedNextAction } from '@s import { PlusIcon } from '@studio/icons'; import classes from './EditLayoutSet.module.css'; import { CreateNewSubformLayoutSet } from './CreateNewSubformLayoutSet'; +import type { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; type EditLayoutSetProps = { existingLayoutSetForSubform: string; onUpdateLayoutSet: (layoutSetId: string) => void; onSubformCreated: (layoutSetName: string) => void; + layoutSets: LayoutSets; }; export const EditLayoutSet = ({ existingLayoutSetForSubform, onUpdateLayoutSet, onSubformCreated, + layoutSets, }: EditLayoutSetProps): React.ReactElement => { const { t } = useTranslation(); const [isLayoutSetSelectorVisible, setIsLayoutSetSelectorVisible] = useState(false); @@ -62,7 +65,9 @@ export const EditLayoutSet = ({ onClick={handleClick} /> - {showCreateSubform && } + {showCreateSubform && ( + + )} ); } diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSetForSubform.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSetForSubform.tsx index 12954ba971e..70ee13ca973 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSetForSubform.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditLayoutSetForSubform/EditLayoutSetForSubform.tsx @@ -37,6 +37,7 @@ export const EditLayoutSetForSubform = ({ existingLayoutSetForSubform={component['layoutSet']} onUpdateLayoutSet={handleUpdatedLayoutSet} onSubformCreated={handleCreatedSubform} + layoutSets={layoutSets} /> ); }; diff --git a/frontend/packages/ux-editor/src/hooks/useCreateSubform.test.ts b/frontend/packages/ux-editor/src/hooks/useCreateSubform.test.ts new file mode 100644 index 00000000000..ece8a38b609 --- /dev/null +++ b/frontend/packages/ux-editor/src/hooks/useCreateSubform.test.ts @@ -0,0 +1,32 @@ +import { useCreateSubform } from './useCreateSubform'; +import { renderHook } from '@testing-library/react'; + +const addLayoutSetMock = jest.fn(); + +jest.mock('app-development/hooks/mutations/useAddLayoutSetMutation', () => ({ + useAddLayoutSetMutation: jest.fn(() => ({ + mutate: addLayoutSetMock, + })), +})); + +describe('useCreateSubform', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call addLayoutSet with correct parameters', () => { + const { createSubform } = renderHook(() => useCreateSubform()).result.current; + const subformName = 'underskjema'; + const onSubformCreated = jest.fn(); + + createSubform({ layoutSetName: subformName, onSubformCreated }); + + expect(addLayoutSetMock).toHaveBeenCalledWith({ + layoutSetIdToUpdate: subformName, + layoutSetConfig: { + id: subformName, + type: 'subform', + }, + }); + }); +}); diff --git a/frontend/packages/ux-editor/src/hooks/useCreateSubform.ts b/frontend/packages/ux-editor/src/hooks/useCreateSubform.ts new file mode 100644 index 00000000000..b96c3ca1aeb --- /dev/null +++ b/frontend/packages/ux-editor/src/hooks/useCreateSubform.ts @@ -0,0 +1,25 @@ +import { useAddLayoutSetMutation } from 'app-development/hooks/mutations/useAddLayoutSetMutation'; +import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; + +type CreateSubformProps = { + layoutSetName: string; + onSubformCreated: (layoutSetName: string) => void; +}; + +export const useCreateSubform = () => { + const { org, app } = useStudioEnvironmentParams(); + const { mutate: addLayoutSet } = useAddLayoutSetMutation(org, app); + + const createSubform = ({ layoutSetName, onSubformCreated }: CreateSubformProps) => { + addLayoutSet({ + layoutSetIdToUpdate: layoutSetName, + layoutSetConfig: { + id: layoutSetName, + type: 'subform', + }, + }); + onSubformCreated(layoutSetName); + }; + + return { createSubform }; +};