From 25c85296df1b2c07205d5b907db20285d090b7d7 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Tue, 21 Dec 2021 08:20:53 +0100 Subject: [PATCH 1/5] rename isSimpleComponent -> componentRendersItsOwnValidationErrors Explains better how the function is used. Also fix some minor type issues. New interface IComponentProps Previously all components had their own interface. It is better when all share the common props from GenericComponent ``` export interface IComponentProps extends IGenericComponentProps { handleDataChange: (value: string, key?:string) => void, handleFocusUpdate: (componentId: string, step?: number) => void, getTextResource: (key: string) => React.ReactNode, getTextResourceAsString: (key: string) => string, formData: any, isValid: boolean, language: any, shouldFocus: boolean, text: React.ReactNode, label: ()=>JSX.Element, legend: ()=>JSX.Element, }; ``` Make formData type stable and use .simpleBinding everywhere Use React.ReactNode as type for getTextResource Apply formatting corrections from code review Co-authored-by: Steffen Lorang Ekeberg Use a proper key for AltinnMobileTable Rename componentRendersItsOwnValidationErrors => componentValidationsHandledByGenericComponent Fix missing `?.simpleBinding` in Components Remove ITextAreaComponentProps to fix @typescript-eslint/no-empty-interface --- .../components/GenericComponent.test.tsx | 4 +- .../advanced/AddressComponent.test.tsx | 11 +++- .../base/AttachmentListComponent.test.tsx | 2 +- .../components/base/ButtonComponent.test.tsx | 3 + .../base/DropdownComponent.test.tsx | 2 + .../base/FileUploadComponent.test.tsx | 5 +- .../components/base/HeaderComponent.test.tsx | 3 +- .../components/base/InputComponent.test.tsx | 7 ++- .../base/ParagraphComponent.test.tsx | 21 ++++--- .../base/TextAreaComponent.test.tsx | 12 ++-- .../presentation/NavigationButton.test.tsx | 4 ++ .../form/data/submitFormDataSagas.test.ts | 12 ++-- .../utils/conditionalRendering.test.ts | 2 +- .../utils/formComponentUtils.test.ts | 14 ++--- .../__tests__/utils/rules.test.tsx | 4 +- .../__tests__/utils/validation.test.ts | 4 +- .../src/components/GenericComponent.tsx | 31 ++++------ .../components/advanced/AddressComponent.tsx | 16 +---- .../base/AttachmentListComponent.tsx | 5 +- .../src/components/base/ButtonComponent.tsx | 8 +-- .../base/CheckboxesContainerComponent.tsx | 23 +++----- .../components/base/DatepickerComponent.tsx | 17 ++---- .../src/components/base/DropdownComponent.tsx | 13 ++--- .../components/base/FileUploadComponent.tsx | 9 +-- .../src/components/base/HeaderComponent.tsx | 12 +--- .../src/components/base/ImageComponent.tsx | 10 +--- .../src/components/base/InputComponent.tsx | 16 ++--- .../base/InstantiationButtonComponent.tsx | 6 +- .../components/base/ParagraphComponent.tsx | 12 +--- .../base/RadioButtonsContainerComponent.tsx | 20 ++----- .../src/components/base/TextAreaComponent.tsx | 17 ++---- .../src/components/index.ts | 18 +++++- .../presentation/NavigationButtons.tsx | 6 +- .../features/form/components/Description.tsx | 2 +- .../form/components/HelpTextContainer.tsx | 2 +- .../form/components/HelpTextPopover.tsx | 2 +- .../src/features/form/components/Legend.tsx | 6 +- .../form/containers/RepeatingGroupTable.tsx | 1 + .../src/features/form/data/formDataReducer.ts | 2 +- .../src/features/form/layout/index.ts | 58 +------------------ .../form/rules/check/checkRulesSagas.ts | 6 +- .../containers/InstanceSelection.tsx | 2 + .../src/utils/formComponentUtils.ts | 24 ++++---- .../src/utils/formLayout.ts | 2 +- .../src/utils/rules/index.ts | 4 +- .../src/utils/validation.ts | 2 +- .../molecules/AltinnMobileTableItem.tsx | 5 +- 47 files changed, 185 insertions(+), 282 deletions(-) diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/GenericComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/GenericComponent.test.tsx index 95288bd1c6f..fdfb7011da0 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/GenericComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/GenericComponent.test.tsx @@ -51,9 +51,7 @@ describe('>>> components/GenericComponent.tsx', () => { const formData = getFormDataStateMock({ formData: { - mockId: { - mockDataBinding: 'value', - }, + mockDataBinding: 'value', }, }); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/advanced/AddressComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/advanced/AddressComponent.test.tsx index ed1fad1d7ed..2421089e5d8 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/advanced/AddressComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/advanced/AddressComponent.test.tsx @@ -2,12 +2,13 @@ import { mount, shallow } from 'enzyme'; import 'jest'; import * as React from 'react'; +import { IComponentProps } from 'src/components'; import { AddressComponent } from '../../../src/components/advanced/AddressComponent'; describe('components > advanced > AddressComponent', () => { const mockId = 'mock-id'; const mockFormData = { address: 'adresse 1' }; - const mockHandleDataChange = (data: any) => ''; + const mockHandleDataChange = (data: string) => ''; const mockGetTextResource = (resourceKey: string) => 'test'; const mockIsValid = true; const mockSimplified = true; @@ -48,6 +49,7 @@ describe('components > advanced > AddressComponent', () => { required={mockRequired} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />, ); expect(shallowAddressComponent.find('input').length).toBe(3); @@ -68,6 +70,7 @@ describe('components > advanced > AddressComponent', () => { required={mockRequired} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />, ); expect(shallowAddressComponent.find('.address-component-small-inputs').hasClass('disabled')).toBe(false); @@ -88,6 +91,7 @@ describe('components > advanced > AddressComponent', () => { required={mockRequired} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />, ); shallowAddressComponent.find('input').forEach((node: any) => { @@ -110,6 +114,7 @@ describe('components > advanced > AddressComponent', () => { required={mockRequired} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />, ); shallowAddressComponent.find('input').forEach((node: any) => { @@ -133,6 +138,7 @@ describe('components > advanced > AddressComponent', () => { required={false} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />, ); expect(shallowAddressComponent.find('span.label-optional')).toHaveLength(2); @@ -154,6 +160,7 @@ describe('components > advanced > AddressComponent', () => { labelSettings={{ optionalIndicator: false }} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />, ); expect(shallowAddressComponent.find('.label-optional')).toHaveLength(0); @@ -175,6 +182,7 @@ describe('components > advanced > AddressComponent', () => { required={false} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />); const input = component.find(`#address_zip_code_${mockId}`); input.simulate('change', { target: { value: '123'}}) @@ -198,6 +206,7 @@ describe('components > advanced > AddressComponent', () => { required={false} language={mockLanguage} textResourceBindings={mocktextResourceBindings} + {...({} as IComponentProps)} />); const input = component.find(`#address_zip_code_${mockId}`); input.simulate('change', { target: { value: ''}}) diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/AttachmentListComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/AttachmentListComponent.test.tsx index 7df25274e9a..a5b87c481ad 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/AttachmentListComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/AttachmentListComponent.test.tsx @@ -71,7 +71,7 @@ describe('>>> components/base/FileUploadComponent.tsx', () => { id: mockId, text: 'Attachments', dataTypeIds: ['test-data-type-1'], - }; + } as IAttachmentListProps; return render( diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ButtonComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ButtonComponent.test.tsx index 1291c547248..fc1fd168dff 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ButtonComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ButtonComponent.test.tsx @@ -5,6 +5,7 @@ import configureStore from 'redux-mock-store'; import { mount } from 'enzyme'; import { Provider } from 'react-redux'; import { ButtonComponent } from '../../../src/components/base/ButtonComponent'; +import { IComponentProps } from 'src/components'; describe('components/base/ButtonComponent.tsx', () => { let mockId: string; @@ -61,6 +62,7 @@ describe('components/base/ButtonComponent.tsx', () => { disabled={mockDisabled} formDataCount={formDataCount} language={mockLanguage} + {...({} as IComponentProps)} /> ); @@ -78,6 +80,7 @@ describe('components/base/ButtonComponent.tsx', () => { disabled={mockDisabled} formDataCount={formDataCount} language={mockLanguage} + {...({} as IComponentProps)} /> ); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/DropdownComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/DropdownComponent.test.tsx index dbb4028f5f1..c095301d8ad 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/DropdownComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/DropdownComponent.test.tsx @@ -4,6 +4,7 @@ import configureStore from 'redux-mock-store'; import { Provider } from 'react-redux'; import { render } from '@testing-library/react'; import DropdownComponent from '../../../src/components/base/DropdownComponent'; +import { IComponentProps } from 'src/components'; describe('>>> components/base/DropdownComponent.tsx --- Snapshot', () => { let mockId: string; @@ -47,6 +48,7 @@ describe('>>> components/base/DropdownComponent.tsx --- Snapshot', () => { optionsId={mockOptionsId} isValid={mockIsValid} readOnly={false} + {...({} as IComponentProps)} /> , ); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/FileUploadComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/FileUploadComponent.test.tsx index d53d3a46cc5..53da5ddcf70 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/FileUploadComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/FileUploadComponent.test.tsx @@ -9,6 +9,7 @@ import { render } from '@testing-library/react'; import { bytesInOneMB, FileUploadComponent, IFileUploadProps } from '../../../src/components/base/FileUploadComponent'; import { getFileEnding, removeFileEnding } from '../../../src/utils/attachment'; import { getFileUploadComponentValidations } from '../../../src/utils/formComponentUtils'; +import { IComponentProps } from 'src/components'; describe('>>> components/base/FileUploadComponent.tsx', () => { @@ -79,6 +80,7 @@ describe('>>> components/base/FileUploadComponent.tsx', () => { maxNumberOfAttachments={mockMaxNumberOfAttachments} minNumberOfAttachments={mockMinNumberOfAttachments} readOnly={mockReadOnly} + {...({} as IComponentProps)} /> ); @@ -207,6 +209,7 @@ describe('>>> components/base/FileUploadComponent.tsx', () => { maxNumberOfAttachments={mockMaxNumberOfAttachments} minNumberOfAttachments={mockMinNumberOfAttachments} readOnly={mockReadOnly} + {...({} as IComponentProps)} /> ); @@ -272,7 +275,7 @@ describe('>>> components/base/FileUploadComponent.tsx', () => { minNumberOfAttachments: mockMinNumberOfAttachments, isValid: mockIsValid, readOnly: mockReadOnly, - }; + } as IFileUploadProps; return render( diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/HeaderComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/HeaderComponent.test.tsx index 632afc63cfe..df803b397e7 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/HeaderComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/HeaderComponent.test.tsx @@ -7,6 +7,7 @@ import { } from '@testing-library/react'; import { HeaderComponent } from '../../../src/components/base/HeaderComponent'; +import { IComponentProps } from 'src/components'; const render = (props = {}) => { const allProps = { @@ -16,7 +17,7 @@ const render = (props = {}) => { language: {}, textResourceBindings: {}, ...props, - }; + } as IComponentProps; rtlRender(); }; diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/InputComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/InputComponent.test.tsx index c13b0210abb..8656a70d5ec 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/InputComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/InputComponent.test.tsx @@ -4,6 +4,7 @@ import 'jest'; import * as React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { InputComponent, IInputProps } from '../../../src/components/base/InputComponent'; +import { IComponentProps } from 'src/components'; describe('components/base/InputComponent.tsx', () => { let mockId: string; @@ -35,7 +36,7 @@ describe('components/base/InputComponent.tsx', () => { }); test('components/base/InputComponent.tsx -- should have correct value with specified form data', async () => { - const customProps = { formData: 'Test123' }; + const customProps:Partial = { formData: {simpleBinding:'Test123'} }; const { findByTestId } = renderInputComponent(customProps); const inputComponent: any = await findByTestId(mockId); @@ -69,7 +70,7 @@ describe('components/base/InputComponent.tsx', () => { prefix: '$', }, }, - formData: '1234', + formData: {simpleBinding:'1234'}, }); const inputComponent: any = await findByTestId(`${mockId}-formatted-number`); expect(inputComponent.value).toEqual('$1,234'); @@ -83,7 +84,7 @@ describe('components/base/InputComponent.tsx', () => { isValid: mockIsValid, readOnly: mockReadOnly, required: mockRequired, - }; + } as unknown as IInputProps; return render(); } diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ParagraphComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ParagraphComponent.test.tsx index fced72e20cd..a5d11ae7c36 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ParagraphComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/ParagraphComponent.test.tsx @@ -3,20 +3,16 @@ import { shallow } from 'enzyme'; import 'jest'; import * as React from 'react'; import * as renderer from 'react-test-renderer'; +import { IComponentProps } from 'src/components'; import { ParagraphComponent } from '../../../src/components/base/ParagraphComponent'; import { ITextResourceBindings } from '../../../src/features/form/layout'; describe('>>> components/base/ParagraphComponent.tsx --- Snapshot', () => { - let mockId: string; - let mockText: string; - let mockGetTextResource: (key: string) => string; - let mockLanguage: any; - let mockTextResourceBindings: ITextResourceBindings; - mockId = 'mock-id'; - mockText = 'Here goes a paragraph'; - mockGetTextResource = (key: string) => key; - mockLanguage = {}; - mockTextResourceBindings = { + const mockId = 'mock-id'; + const mockText = 'Here goes a paragraph'; + const mockGetTextResource = (key: string) => key; + const mockLanguage: any = {}; + const mockTextResourceBindings: ITextResourceBindings = { tile: mockText, } @@ -28,6 +24,7 @@ describe('>>> components/base/ParagraphComponent.tsx --- Snapshot', () => { language={mockLanguage} getTextResource={mockGetTextResource} textResourceBindings={mockTextResourceBindings} + {...({} as IComponentProps)} />, ); expect(rendered).toMatchSnapshot(); @@ -40,6 +37,7 @@ describe('>>> components/base/ParagraphComponent.tsx --- Snapshot', () => { language={mockLanguage} getTextResource={mockGetTextResource} textResourceBindings={mockTextResourceBindings} + {...({} as IComponentProps)} />, ); @@ -53,7 +51,8 @@ describe('>>> components/base/ParagraphComponent.tsx --- Snapshot', () => { text={mockText} language={mockLanguage} getTextResource={mockGetTextResource} - textResourceBindings={{ help: 'this is the help text'}} + textResourceBindings={{ help: 'this is the help text' }} + {...({} as IComponentProps)} />, ); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/TextAreaComponent.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/TextAreaComponent.test.tsx index 1fe8af75d62..a5f4fa7a287 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/TextAreaComponent.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/base/TextAreaComponent.test.tsx @@ -4,8 +4,9 @@ import * as React from 'react'; import * as renderer from 'react-test-renderer'; import { mount } from 'enzyme'; -import { TextAreaComponent, ITextAreaComponentProps } from '../../../src/components/base/TextAreaComponent'; +import { TextAreaComponent } from '../../../src/components/base/TextAreaComponent'; import { render, fireEvent } from '@testing-library/react'; +import { IComponentProps } from 'src/components'; describe('>>> components/base/TextAreaComponent.tsx', () => { let mockId: string; @@ -30,6 +31,7 @@ describe('>>> components/base/TextAreaComponent.tsx', () => { handleDataChange={mockHandleDataChange} isValid={mockIsValid} readOnly={mockReadOnly} + {...({} as IComponentProps)} />, ); expect(rendered).toMatchSnapshot(); @@ -52,6 +54,7 @@ describe('>>> components/base/TextAreaComponent.tsx', () => { handleDataChange={mockHandleDataChange} isValid={mockIsValid} readOnly={mockReadOnly} + {...({} as IComponentProps)} />, ); expect(wrapper.find('textarea').hasClass('disabled')).toBe(false); @@ -66,20 +69,21 @@ describe('>>> components/base/TextAreaComponent.tsx', () => { handleDataChange={mockHandleDataChange} isValid={mockIsValid} readOnly={true} + {...({} as IComponentProps)} />, ); expect(wrapper.find('textarea').hasClass('disabled')).toBe(true); expect(wrapper.find('textarea').prop('readOnly')).toBe(true); }); - function renderTextAreaComponent(props: Partial = {}) { - const defaultProps: ITextAreaComponentProps = { + function renderTextAreaComponent(props: Partial = {}) { + const defaultProps: IComponentProps = { id: mockId, formData: mockFormData, handleDataChange: mockHandleDataChange, isValid: mockIsValid, readOnly: mockReadOnly, - }; + } as IComponentProps; return render(); } diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/presentation/NavigationButton.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/presentation/NavigationButton.test.tsx index 1893c542676..f52a5fb5f57 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/presentation/NavigationButton.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/components/presentation/NavigationButton.test.tsx @@ -7,6 +7,7 @@ import { render, screen } from '@testing-library/react'; import configureStore from 'redux-mock-store'; import { getFormLayoutStateMock, getInitialStateMock } from '../../../__mocks__/mocks'; import { NavigationButtons } from '../../../src/components/presentation/NavigationButtons'; +import { IComponentProps } from 'src/components'; describe('>>> components/presentation/NavigationButton.tsx', () => { let mockStore; @@ -92,6 +93,7 @@ describe('>>> components/presentation/NavigationButton.tsx', () => { id='nav-button-1' showBackButton={false} textResourceBindings={null} + {...({} as IComponentProps)} /> , ); @@ -107,6 +109,7 @@ describe('>>> components/presentation/NavigationButton.tsx', () => { id='nav-button-1' showBackButton={true} textResourceBindings={null} + {...({} as IComponentProps)} /> , ); @@ -136,6 +139,7 @@ describe('>>> components/presentation/NavigationButton.tsx', () => { id='nav-button-2' showBackButton={true} textResourceBindings={null} + {...({} as IComponentProps)} /> , ); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/features/form/data/submitFormDataSagas.test.ts b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/features/form/data/submitFormDataSagas.test.ts index dae07fa94cb..246eef79069 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/features/form/data/submitFormDataSagas.test.ts +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/features/form/data/submitFormDataSagas.test.ts @@ -53,7 +53,7 @@ describe('submitFormDataSagas', () => { formData: getFormDataStateMock({ formData: { field1: 'value1', - field2: 123, + field2: '123', }, }), }; @@ -89,9 +89,7 @@ describe('submitFormDataSagas', () => { }, formData: { ...stateMock.formData, - formData: { - formData, - }, + formData: formData, }, formLayout: { ...stateMock.formLayout, @@ -125,11 +123,13 @@ describe('submitFormDataSagas', () => { }], ]) .call(saveStatelessData, state, model) - .put(FormDataActions.fetchFormDataFulfilled({ formData: + .put(FormDataActions.fetchFormDataFulfilled({ + formData: { ...formData, 'group.field1': 'value1', - } })) + } + })) .call(FormDynamicsActions.checkIfConditionalRulesShouldRun) .put(FormDataActions.submitFormDataFulfilled()) .run(); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/conditionalRendering.test.ts b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/conditionalRendering.test.ts index 7c6a78ff138..a8e62255f6b 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/conditionalRendering.test.ts +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/conditionalRendering.test.ts @@ -156,7 +156,7 @@ describe('>>> utils/conditionalRendering.ts', () => { }; const formData = { - 'mockGroup[0].mockField': 8, + 'mockGroup[0].mockField': '8', }; const result = runConditionalRenderingRules(showRules, formData, repeatingGroups); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/formComponentUtils.test.ts b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/formComponentUtils.test.ts index 6af2fda3a90..bde6b60f5dd 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/formComponentUtils.test.ts +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/formComponentUtils.test.ts @@ -1,4 +1,4 @@ -import { getDisplayFormData, getFormDataForComponentInRepeatingGroup, isSimpleComponent } from '../../src/utils/formComponentUtils'; +import { getDisplayFormData, getFormDataForComponentInRepeatingGroup, componentValidationsHandledByGenericComponent } from '../../src/utils/formComponentUtils'; import { IOptions, ITextResource } from '../../src/types'; import { IFormData } from '../../src/features/form/data/formDataReducer'; import { ILayoutComponent, ISelectionComponentProps } from '../../src/features/form/layout'; @@ -85,33 +85,33 @@ describe('>>> utils/formComponentUtils.ts', () => { expect(result).toEqual('RepValue1, RepValue2, RepValue3'); }); - it('+++ isSimpleComponent should return false when dataModelBinding is undefined', () => { + it('+++ componentValidationsHandledByGenericComponent should return false when dataModelBinding is undefined', () => { const genericComponent = { type: 'FileUpload', } - const result = isSimpleComponent(undefined, genericComponent.type); + const result = componentValidationsHandledByGenericComponent(undefined, genericComponent.type); expect(result).toEqual(false); }); - it('+++ isSimpleComponent should return false when component type is Datepicker', () => { + it('+++ componentValidationsHandledByGenericComponent should return false when component type is Datepicker', () => { const genericComponent = { type: 'Datepicker', dataModelBindings: { simpleBinding: 'group.superdate', }, } - const result = isSimpleComponent(genericComponent.dataModelBindings, genericComponent.type); + const result = componentValidationsHandledByGenericComponent(genericComponent.dataModelBindings, genericComponent.type); expect(result).toEqual(false); }); - it('+++ isSimpleComponent should return true when component type is Input', () => { + it('+++ componentValidationsHandledByGenericComponent should return true when component type is Input', () => { const genericComponent = { type: 'Input', dataModelBindings: { simpleBinding: 'group.secretnumber', }, } - const result = isSimpleComponent(genericComponent.dataModelBindings, genericComponent.type); + const result = componentValidationsHandledByGenericComponent(genericComponent.dataModelBindings, genericComponent.type); expect(result).toEqual(true); }); }); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/rules.test.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/rules.test.tsx index ec35849f1c6..045efb7f1ac 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/rules.test.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/rules.test.tsx @@ -1,12 +1,12 @@ /* eslint-disable no-param-reassign */ /* eslint-disable no-undef */ import 'jest'; -import { IFormData } from '../../src/features/form/data/formDataReducer'; +import { IFormDataState } from '../../src/features/form/data/formDataReducer'; import { checkIfRuleShouldRun, getRuleModelFields } from '../../src/utils/rules'; describe('>>> features/rules checkIfRuleShouldRun', () => { let mockRuleConnectionState: any; - let mockFormDataState: IFormData; + let mockFormDataState: Partial; let mockFormLayoutState: any; let mockLastUpdatedDataBinding: string; let mockRuleHandlerHelper; diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/validation.test.ts b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/validation.test.ts index caeff3553d4..1f38dd19bfe 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/validation.test.ts +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/__tests__/utils/validation.test.ts @@ -21,8 +21,8 @@ describe('utils > validation', () => { let mockLayoutState: any; let mockJsonSchema: any; let mockInvalidTypes: any; - let mockFormData: IFormData; - let mockValidFormData: IFormData; + let mockFormData: any; + let mockValidFormData: any; let mockFormValidationResult: any; let mockLanguage: any; let mockFormAttachments: any; diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/GenericComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/GenericComponent.tsx index a87ac941df7..714e78fd6fd 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/GenericComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/GenericComponent.tsx @@ -3,14 +3,13 @@ import { shallowEqual } from 'react-redux'; import { getTextResourceByKey } from 'altinn-shared/utils'; import { ILabelSettings, - ITextResource, Triggers, - IComponentValidations, + IComponentValidations } from 'src/types'; import { Grid, makeStyles } from '@material-ui/core'; import classNames from 'classnames'; -import components from '.'; +import components, { IComponentProps } from '.'; import FormDataActions from '../features/form/data/formDataActions'; import { IDataModelBindings, @@ -26,7 +25,7 @@ import Legend from '../features/form/components/Legend'; import { renderValidationMessagesForComponent } from '../utils/render'; import { getFormDataForComponent, - isSimpleComponent, + componentValidationsHandledByGenericComponent, componentHasValidationMessages, getTextResource, isComponentValid, @@ -88,7 +87,6 @@ export function GenericComponent(props: IGenericComponentProps) { const classes = useStyles(props); const GetHiddenSelector = makeGetHidden(); const GetFocusSelector = makeGetFocus(); - const [isSimple, setIsSimple] = React.useState(true); const [hasValidationMessages, setHasValidationMessages] = React.useState(false); @@ -97,7 +95,7 @@ export function GenericComponent(props: IGenericComponentProps) { getFormDataForComponent(state.formData.formData, props.dataModelBindings), shallowEqual, ); - const currentView: string = useAppSelector( + const currentView = useAppSelector( state => state.formLayout.uiConfig.currentView, ); const isValid = useAppSelector(state => @@ -106,8 +104,8 @@ export function GenericComponent(props: IGenericComponentProps) { ), ); const language = useAppSelector(state => state.language.language); - const textResources: ITextResource[] = useAppSelector(state => state.textResources.resources); - const texts: any = useAppSelector(state => + const textResources = useAppSelector(state => state.textResources.resources); + const texts = useAppSelector(state => selectComponentTexts( state.textResources.resources, props.textResourceBindings, @@ -121,10 +119,6 @@ export function GenericComponent(props: IGenericComponentProps) { shallowEqual, ); - React.useEffect(() => { - setIsSimple(isSimpleComponent(props.dataModelBindings, props.type)); - }, []); - React.useEffect(() => { setHasValidationMessages( componentHasValidationMessages(componentValidations), @@ -135,7 +129,7 @@ export function GenericComponent(props: IGenericComponentProps) { return null; } - const handleDataUpdate = (value: any, key = 'simpleBinding') => { + const handleDataUpdate = (value: string, key = 'simpleBinding') => { if (!props.dataModelBindings || !props.dataModelBindings[key]) { return; } @@ -144,12 +138,7 @@ export function GenericComponent(props: IGenericComponentProps) { return; } - if (formData instanceof Object) { - if (formData[key] && formData[key] === value) { - // data unchanged, do nothing - return; - } - } else if (formData && formData === value) { + if (formData[key] === value) { // data unchanged, do nothing return; } @@ -272,7 +261,7 @@ export function GenericComponent(props: IGenericComponentProps) { return getTextResourceByKey(key, textResources); }; - const componentProps = { + const componentProps:IComponentProps = { handleDataChange: handleDataUpdate, handleFocusUpdate, getTextResource: getTextResourceWrapper, @@ -347,7 +336,7 @@ export function GenericComponent(props: IGenericComponentProps) { > - {isSimple && + {componentValidationsHandledByGenericComponent(props.dataModelBindings, props.type) && hasValidationMessages && renderValidationMessagesForComponent( componentValidations?.simpleBinding, diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/advanced/AddressComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/advanced/AddressComponent.tsx index ecf78335491..bb1efd78d5f 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/advanced/AddressComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/advanced/AddressComponent.tsx @@ -6,27 +6,15 @@ import axios from 'axios'; import * as React from 'react'; import { getLanguageFromKey, get } from 'altinn-shared/utils'; import { IComponentValidations, ILabelSettings } from 'src/types'; -import { IDataModelBindings, ITextResourceBindings } from '../../features/form/layout'; import '../../styles/AddressComponent.css'; import '../../styles/shared.css'; import { renderValidationMessagesForComponent } from '../../utils/render'; import classNames from 'classnames'; -import { ILanguage } from 'altinn-shared/types'; +import { IComponentProps } from '..'; -export interface IAddressComponentProps { - id: string; - formData: { [id: string]: string }; - handleDataChange: (value: any, key: string) => void; - getTextResource: (key: string) => string; - isValid?: boolean; +export interface IAddressComponentProps extends IComponentProps { simplified: boolean; - dataModelBindings: IDataModelBindings; - readOnly: boolean; - required: boolean; labelSettings?: ILabelSettings; - language: ILanguage; - textResourceBindings: ITextResourceBindings; - componentValidations?: IComponentValidations; } interface IAddressValidationErrors { diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/AttachmentListComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/AttachmentListComponent.tsx index 4401f134dca..46b79279483 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/AttachmentListComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/AttachmentListComponent.tsx @@ -3,10 +3,9 @@ import { AltinnAttachment } from 'altinn-shared/components'; import { mapInstanceAttachments } from 'altinn-shared/utils'; import { Grid, Typography } from '@material-ui/core'; import { useAppSelector } from 'src/common/hooks'; +import { IComponentProps } from '..'; -export interface IAttachmentListProps { - id: string; - text: string; +export interface IAttachmentListProps extends IComponentProps { dataTypeIds?: string[]; } diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/ButtonComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/ButtonComponent.tsx index 7f50c7d5720..037f36996fb 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/ButtonComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/ButtonComponent.tsx @@ -5,15 +5,11 @@ import { AltinnLoader } from 'altinn-shared/components'; import { IAltinnWindow } from '../../types'; import FormDataActions from '../../features/form/data/formDataActions'; import { useAppDispatch, useAppSelector } from 'src/common/hooks'; -import { ILanguage } from 'altinn-shared/types'; +import { IComponentProps } from '..'; -export interface IButtonProvidedProps { - id: string; - text: string; +export interface IButtonProvidedProps extends IComponentProps { disabled: boolean; - handleDataChange: (value: any) => void; formDataCount: number; - language: ILanguage; } const buttonStyle = { diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx index e633e08cf63..343da0a605b 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx @@ -7,22 +7,13 @@ import { AltinnAppTheme } from 'altinn-shared/theme'; import classNames from 'classnames'; import { renderValidationMessagesForComponent } from '../../utils/render'; import { useAppSelector } from 'src/common/hooks'; +import { IComponentProps } from '..'; -export interface ICheckboxContainerProps { - id: string; - formData: any; - handleDataChange: (value: any) => void; - handleFocusUpdate: (value: any) => void; - isValid: boolean; +export interface ICheckboxContainerProps extends IComponentProps { validationMessages: any; options: any[]; optionsId: string; - preselectedOptionIndex: number; - readOnly: boolean; - shouldFocus: boolean; - legend: () => JSX.Element; - getTextResource: (key: string) => JSX.Element; - getTextResourceAsString: (key: string) => string; + preselectedOptionIndex?: number; } export interface IStyledCheckboxProps extends CheckboxProps { @@ -99,11 +90,11 @@ export const CheckboxContainerComponent = (props: ICheckboxContainerProps) => { React.useEffect(() => { returnState(); - }, [props.formData]); + }, [props.formData?.simpleBinding]); const returnState = () => { if ( - !props.formData && + !props.formData?.simpleBinding && props.preselectedOptionIndex >= 0 && options && props.preselectedOptionIndex < options.length @@ -114,7 +105,7 @@ export const CheckboxContainerComponent = (props: ICheckboxContainerProps) => { props.handleDataChange(preSelected[props.preselectedOptionIndex]); setSelected(preSelected); } else { - setSelected(props.formData ? props.formData.toString().split(',') : []); + setSelected(props.formData?.simpleBinding ? props.formData.simpleBinding.split(',') : []); } }; @@ -133,7 +124,7 @@ export const CheckboxContainerComponent = (props: ICheckboxContainerProps) => { }; const handleOnBlur = () => { - props.handleDataChange(props.formData); + props.handleDataChange(props.formData?.simpleBinding ?? ''); }; const selectedHasValues = (select: string[]): boolean => { diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DatepickerComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DatepickerComponent.tsx index d7c15a48a12..72e2a29c31a 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DatepickerComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DatepickerComponent.tsx @@ -17,28 +17,19 @@ import MomentUtils from '@date-io/moment'; import { getLanguageFromKey } from 'altinn-shared/utils'; import { AltinnAppTheme } from 'altinn-shared/theme'; import { - IComponentValidations, IComponentBindingValidation, DateFlags, } from 'src/types'; import { getFlagBasedDate, getISOString } from '../../utils/dateHelpers'; import { renderValidationMessagesForComponent } from '../../utils/render'; import { validateDatepickerFormData } from '../../utils/validation'; -import { ILanguage } from 'altinn-shared/types'; +import { IComponentProps } from '..'; -export interface IDatePickerProps { - id: string; - readOnly: boolean; - required: boolean; - formData: any; - handleDataChange: (value: any) => void; - isValid?: boolean; +export interface IDatePickerProps extends IComponentProps { timeStamp?: boolean; format: string; minDate: string; maxDate: string; - language: ILanguage; - componentValidations: IComponentValidations; } const useStyles = makeStyles({ @@ -152,10 +143,10 @@ function DatepickerComponent(props: IDatePickerProps) { }; React.useEffect(() => { - const dateValue = props.formData ? moment(props.formData) : null; + const dateValue = props.formData?.simpleBinding ? moment(props.formData.simpleBinding) : null; setDate(dateValue); setValidationMessages(getValidationMessages()); - }, [props.formData]); + }, [props.formData?.simpleBinding]); React.useEffect(() => { setValidationMessages(getValidationMessages()); diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DropdownComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DropdownComponent.tsx index 15c88c1a791..75dd6207303 100644 --- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DropdownComponent.tsx +++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/DropdownComponent.tsx @@ -5,15 +5,10 @@ import classNames from 'classnames'; import { makeStyles } from '@material-ui/core'; import { AltinnAppTheme } from 'altinn-shared/theme'; import { useAppSelector } from 'src/common/hooks'; +import { IComponentProps } from '..'; -export interface IDropdownProps { - formData: string; - getTextResourceAsString: (resourceKey: string) => string; - handleDataChange: (value: string) => void; - id: string; - isValid?: boolean; +export interface IDropdownProps extends IComponentProps { optionsId: string; - readOnly: boolean; preselectedOptionIndex?: number; } @@ -46,7 +41,7 @@ function DropdownComponent(props: IDropdownProps) { const returnState = () => { if ( - !props.formData && + !props.formData?.simpleBinding && props.preselectedOptionIndex >= 0 && options && props.preselectedOptionIndex < options.length @@ -66,7 +61,7 @@ function DropdownComponent(props: IDropdownProps) { return (