From 8464610746c724976505389bfab5d61ed7db865b Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Thu, 29 Dec 2022 15:00:39 +0100 Subject: [PATCH 1/9] Cleaning up typings, making sure all components can be rendered only with PropsFromGenericComponent, and making sure typescript knows that: - Summary components cannot be inside Group components (now using ComponentInGroup for this type) - Summary components cannot be rendered directly using GenericComponent.tsx (now using RenderableGenericComponent for this) - Lots more various cleanup to make typings work --- src/features/form/containers/Form.tsx | 4 +- .../form/containers/GroupContainer.tsx | 7 +-- .../form/containers/RepeatingGroupTable.tsx | 10 ++-- .../RepeatingGroupsEditContainer.test.tsx | 8 +++- .../RepeatingGroupsEditContainer.tsx | 6 +-- .../containers/RepeatingGroupsTable.test.tsx | 22 ++++++--- src/features/form/containers/formUtils.ts | 4 +- src/layout/Button/ButtonComponent.tsx | 15 +++--- src/layout/Button/GoToTaskButton.test.tsx | 29 ++++++++---- src/layout/Button/GoToTaskButton.tsx | 8 ++-- src/layout/Button/getComponentFromMode.ts | 10 +++- src/layout/Button/types.d.ts | 12 ++++- .../CheckboxesContainerComponent.test.tsx | 1 - .../CheckboxesContainerComponent.tsx | 47 ++++++++----------- src/layout/GenericComponent.tsx | 13 +++-- .../InstantiationButton.test.tsx | 13 ++--- .../InstantiationButton.tsx | 5 +- src/layout/InstantiationButton/types.d.ts | 1 + .../Likert/GroupContainerLikertTestUtils.tsx | 10 ++-- .../Likert/RepeatingGroupsLikertContainer.tsx | 16 +++++-- ...t.tsx => MultipleSelectComponent.test.tsx} | 6 +-- ...Select.tsx => MultipleSelectComponent.tsx} | 2 +- ...st.tsx => NavigationBarComponent.test.tsx} | 6 +-- ...tionBar.tsx => NavigationBarComponent.tsx} | 2 +- ...sx => NavigationButtonsComponent.test.tsx} | 10 ++-- ...ons.tsx => NavigationButtonsComponent.tsx} | 2 +- src/layout/layout.d.ts | 2 + src/utils/formLayout.ts | 10 ++-- src/utils/layout/index.tsx | 29 ++++++------ src/utils/validation/validation.ts | 6 ++- 30 files changed, 180 insertions(+), 136 deletions(-) rename src/layout/MultipleSelect/{MultipleSelect.test.tsx => MultipleSelectComponent.test.tsx} (92%) rename src/layout/MultipleSelect/{MultipleSelect.tsx => MultipleSelectComponent.tsx} (98%) rename src/layout/NavigationBar/{NavigationBar.test.tsx => NavigationBarComponent.test.tsx} (97%) rename src/layout/NavigationBar/{NavigationBar.tsx => NavigationBarComponent.tsx} (98%) rename src/layout/NavigationButtons/{NavigationButtons.test.tsx => NavigationButtonsComponent.test.tsx} (93%) rename src/layout/NavigationButtons/{NavigationButtons.tsx => NavigationButtonsComponent.tsx} (98%) diff --git a/src/features/form/containers/Form.tsx b/src/features/form/containers/Form.tsx index 5031903421..3f148d8949 100644 --- a/src/features/form/containers/Form.tsx +++ b/src/features/form/containers/Form.tsx @@ -15,7 +15,7 @@ import { extractBottomButtons, hasRequiredFields, topLevelComponents } from 'src import { renderGenericComponent } from 'src/utils/layout'; import { getFormHasErrors, missingFieldsInLayoutValidations } from 'src/utils/validation'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ILayout, ILayoutComponent, RenderableGenericComponent } from 'src/layout/layout'; export function renderLayoutComponent( layoutComponent: ILayoutComponent | ILayoutGroup, @@ -44,7 +44,7 @@ export function renderLayoutComponent( } } -function GenericComponent(component: ILayoutComponent, layout: ILayout) { +function GenericComponent(component: RenderableGenericComponent, layout: ILayout) { return renderGenericComponent({ component, layout }); } diff --git a/src/features/form/containers/GroupContainer.tsx b/src/features/form/containers/GroupContainer.tsx index 3514b3462a..99f80adbc4 100644 --- a/src/features/form/containers/GroupContainer.tsx +++ b/src/features/form/containers/GroupContainer.tsx @@ -18,12 +18,13 @@ import { createRepeatingGroupComponents, getRepeatingGroupFilteredIndices } from import { getHiddenFieldsForGroup } from 'src/utils/layout'; import { renderValidationMessagesForComponent } from 'src/utils/render'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayoutComponent, ILayoutComponentOrGroup } from 'src/layout/layout'; +import type { ComponentInGroup, ILayoutComponent } from 'src/layout/layout'; import type { IRuntimeState } from 'src/types'; + export interface IGroupProps { id: string; container: ILayoutGroup; - components: ILayoutComponentOrGroup[]; + components: ComponentInGroup[]; triggers?: Triggers[]; } @@ -193,7 +194,7 @@ export function GroupContainer({ id, container, components }: IGroupProps): JSX. <> c[0] as ILayoutComponent)} + repeatingGroupDeepCopyComponents={repeatingGroupDeepCopyComponents.map((c) => c[0])} textResources={textResources} container={container} /> diff --git a/src/features/form/containers/RepeatingGroupTable.tsx b/src/features/form/containers/RepeatingGroupTable.tsx index de09adfbc6..50dfb9fccd 100644 --- a/src/features/form/containers/RepeatingGroupTable.tsx +++ b/src/features/form/containers/RepeatingGroupTable.tsx @@ -23,7 +23,7 @@ import { componentHasValidations, repeatingGroupHasValidations } from 'src/utils import type { IFormData } from 'src/features/form/data'; import type { ILayoutGroup } from 'src/layout/Group/types'; import type { ILayoutCompInput } from 'src/layout/Input/types'; -import type { ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup, ILayout, ILayoutComponent } from 'src/layout/layout'; import type { IAttachments } from 'src/shared/resources/attachments'; import type { IOptions, IRepeatingGroups, ITextResource, ITextResourceBindings, IValidations } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -31,10 +31,10 @@ import type { ILanguage } from 'src/types/shared'; export interface IRepeatingGroupTableProps { id: string; container: ILayoutGroup; - components: (ILayoutComponent | ILayoutGroup)[]; + components: ComponentInGroup[]; repeatingGroupIndex: number; repeatingGroups: IRepeatingGroups | null; - repeatingGroupDeepCopyComponents: (ILayoutComponent | ILayoutGroup)[][]; + repeatingGroupDeepCopyComponents: ComponentInGroup[][]; hiddenFields: string[]; formData: IFormData; attachments: IAttachments; @@ -267,7 +267,9 @@ export function RepeatingGroupTable({ } const childGroupIndex = repeatingGroups[childGroup.id]?.index; - const childGroupComponents = layout.filter((childElement) => childGroup.children?.indexOf(childElement.id) > -1); + const childGroupComponents = layout.filter( + (childElement) => childGroup.children?.indexOf(childElement.id) > -1, + ) as ComponentInGroup[]; const childRenderComponents = setupGroupComponents( childGroupComponents, childGroup.dataModelBindings?.group, diff --git a/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx b/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx index e390199cfd..abc6a6c1ff 100644 --- a/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx +++ b/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx @@ -85,8 +85,12 @@ describe('RepeatingGroupsEditContainer', () => { const layout: ILayout = [multiPageGroup, ...components]; const repeatingGroupIndex = 3; - const repeatingGroupDeepCopyComponents: Array> = - createRepeatingGroupComponents(multiPageGroup, components, repeatingGroupIndex, textResources); + const repeatingGroupDeepCopyComponents = createRepeatingGroupComponents( + multiPageGroup, + components, + repeatingGroupIndex, + textResources, + ); it('calls setEditIndex when save and open next is pressed when edit.saveAndNextButton is true', async () => { const setEditIndex = jest.fn(); diff --git a/src/features/form/containers/RepeatingGroupsEditContainer.tsx b/src/features/form/containers/RepeatingGroupsEditContainer.tsx index 5dd4e8d029..b53c7b7f3d 100644 --- a/src/features/form/containers/RepeatingGroupsEditContainer.tsx +++ b/src/features/form/containers/RepeatingGroupsEditContainer.tsx @@ -11,7 +11,7 @@ import theme from 'src/theme/altinnStudioTheme'; import { renderGenericComponent } from 'src/utils/layout'; import { getLanguageFromKey, getTextResourceByKey } from 'src/utils/sharedUtils'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup, ILayout } from 'src/layout/layout'; import type { ITextResource } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -19,7 +19,7 @@ export interface IRepeatingGroupsEditContainer { id: string; className?: string; container: ILayoutGroup; - repeatingGroupDeepCopyComponents: (ILayoutComponent | ILayoutGroup)[][]; + repeatingGroupDeepCopyComponents: ComponentInGroup[][]; language: ILanguage; textResources: ITextResource[]; layout: ILayout | null; @@ -168,7 +168,7 @@ export function RepeatingGroupsEditContainer({ item={true} spacing={3} > - {repeatingGroupDeepCopyComponents[editIndex]?.map((component: ILayoutComponent) => { + {repeatingGroupDeepCopyComponents[editIndex]?.map((component) => { if ( group.edit?.multiPage && typeof multiPageIndex === 'number' && diff --git a/src/features/form/containers/RepeatingGroupsTable.test.tsx b/src/features/form/containers/RepeatingGroupsTable.test.tsx index 874032affd..276ac27c7f 100644 --- a/src/features/form/containers/RepeatingGroupsTable.test.tsx +++ b/src/features/form/containers/RepeatingGroupsTable.test.tsx @@ -12,7 +12,7 @@ import type { IRepeatingGroupTableProps } from 'src/features/form/containers/Rep import type { IFormData } from 'src/features/form/data'; import type { ILayoutState } from 'src/features/form/layout/formLayoutSlice'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayoutComponent, ISelectionComponentProps } from 'src/layout/layout'; +import type { ComponentInGroup, ILayoutComponent, ISelectionComponentProps } from 'src/layout/layout'; import type { IAttachments } from 'src/shared/resources/attachments'; import type { IOption, ITextResource } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -21,7 +21,7 @@ import type { ILanguage } from 'src/types/shared'; const user = userEvent.setup(); -const getLayout = (group: ILayoutGroup, components: ILayoutComponent[]) => { +const getLayout = (group: ILayoutGroup, components: (ILayoutComponent | ComponentInGroup)[]) => { const layout: ILayoutState = { layouts: { FormLayout: [group, ...components], @@ -65,7 +65,7 @@ describe('RepeatingGroupTable', () => { const textResources: ITextResource[] = [{ id: 'option.label', value: 'Value to be shown' }]; const attachments: IAttachments = {}; const options: IOption[] = [{ value: 'option.value', label: 'option.label' }]; - const components: ILayoutComponent[] = [ + const components: ComponentInGroup[] = [ { id: 'field1', type: 'Input', @@ -127,8 +127,12 @@ describe('RepeatingGroupTable', () => { }; const repeatingGroupIndex = 3; - const repeatingGroupDeepCopyComponents: Array> = - createRepeatingGroupComponents(group, components, repeatingGroupIndex, textResources); + const repeatingGroupDeepCopyComponents = createRepeatingGroupComponents( + group, + components, + repeatingGroupIndex, + textResources, + ); it('should render table header when table has entries', () => { const container = render(); @@ -150,8 +154,12 @@ describe('RepeatingGroupTable', () => { edit: { alertOnDelete: true }, }); const layout: ILayoutState = getLayout(group, components); - const repeatingGroupDeepCopyComponents: Array> = - createRepeatingGroupComponents(group, components, repeatingGroupIndex, textResources); + const repeatingGroupDeepCopyComponents = createRepeatingGroupComponents( + group, + components, + repeatingGroupIndex, + textResources, + ); if (!layout.layouts) { return; diff --git a/src/features/form/containers/formUtils.ts b/src/features/form/containers/formUtils.ts index ab56be692a..f71600227e 100644 --- a/src/features/form/containers/formUtils.ts +++ b/src/features/form/containers/formUtils.ts @@ -1,5 +1,5 @@ import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponentOrGroup } from 'src/layout/layout'; +import type { ComponentInGroup, ILayout } from 'src/layout/layout'; export const mapGroupComponents = ({ children, edit }: ILayoutGroup, layout: ILayout | undefined | null) => children @@ -7,4 +7,4 @@ export const mapGroupComponents = ({ children, edit }: ILayoutGroup, layout: ILa const childId = (edit?.multiPage && child.split(':')[1]) || child; return layout && layout.find((c) => c.id === childId); }) - .filter((c) => c !== undefined && c !== null) as ILayoutComponentOrGroup[]; + .filter((c) => c !== undefined && c !== null) as ComponentInGroup[]; diff --git a/src/layout/Button/ButtonComponent.tsx b/src/layout/Button/ButtonComponent.tsx index 399705f027..2aaf664583 100644 --- a/src/layout/Button/ButtonComponent.tsx +++ b/src/layout/Button/ButtonComponent.tsx @@ -8,17 +8,10 @@ import { SaveButton } from 'src/layout/Button/SaveButton'; import { SubmitButton } from 'src/layout/Button/SubmitButton'; import { ProcessActions } from 'src/shared/resources/process/processSlice'; import { getLanguageFromKey } from 'src/utils/sharedUtils'; -import type { IComponentProps } from 'src/layout'; -import type { ButtonMode } from 'src/layout/Button/getComponentFromMode'; -import type { ILayoutCompButton } from 'src/layout/Button/types'; +import type { PropsFromGenericComponent } from 'src/layout'; import type { IAltinnWindow } from 'src/types'; -export interface IButtonProvidedProps extends IComponentProps, ILayoutCompButton { - disabled: boolean; - id: string; - mode?: ButtonMode; - taskId?: string; -} +export type IButtonProvidedProps = PropsFromGenericComponent<'Button'>; export const ButtonComponent = ({ mode, ...props }: IButtonProvidedProps) => { const dispatch = useAppDispatch(); @@ -29,6 +22,10 @@ export const ButtonComponent = ({ mode, ...props }: IButtonProvidedProps) => { const currentTaskType = useAppSelector((state) => state.instanceData.instance?.process.currentTask?.altinnTaskType); if (mode && !(mode === 'save' || mode === 'submit')) { const GenericButton = getComponentFromMode(mode); + if (!GenericButton) { + return null; + } + return (
diff --git a/src/layout/Button/GoToTaskButton.test.tsx b/src/layout/Button/GoToTaskButton.test.tsx index 8a2dfb77cd..1987f34f8d 100644 --- a/src/layout/Button/GoToTaskButton.test.tsx +++ b/src/layout/Button/GoToTaskButton.test.tsx @@ -6,23 +6,32 @@ import userEvent from '@testing-library/user-event'; import { getInitialStateMock } from 'src/__mocks__/mocks'; import { GoToTaskButton } from 'src/layout/Button/GoToTaskButton'; import { setupStore } from 'src/store'; -import { renderWithProviders } from 'src/testUtils'; -import type { Props as GoToTaskButtonProps } from 'src/layout/Button/GoToTaskButton'; +import { mockComponentProps, renderWithProviders } from 'src/testUtils'; +import type { IButtonProvidedProps } from 'src/layout/Button/ButtonComponent'; -const render = ({ props = {}, dispatch = jest.fn() } = {}) => { - const allProps = { - id: 'go-to-task-button', - ...props, - } as GoToTaskButtonProps; +interface RenderProps { + props: Partial; + dispatch: (...args: any[]) => any; +} + +const render = ({ props, dispatch }: RenderProps) => { const stateMock = getInitialStateMock(); stateMock.process.availableNextTasks = ['a', 'b']; const store = setupStore(stateMock); store.dispatch = dispatch; - renderWithProviders(Go to task, { - store, - }); + renderWithProviders( + + Go to task + , + { + store, + }, + ); }; describe('GoToTaskButton', () => { diff --git a/src/layout/Button/GoToTaskButton.tsx b/src/layout/Button/GoToTaskButton.tsx index f489e96977..89e8328190 100644 --- a/src/layout/Button/GoToTaskButton.tsx +++ b/src/layout/Button/GoToTaskButton.tsx @@ -6,14 +6,12 @@ import { useAppDispatch, useAppSelector } from 'src/common/hooks'; import { WrappedButton } from 'src/layout/Button/WrappedButton'; import { ProcessActions } from 'src/shared/resources/process/processSlice'; import { ProcessTaskType } from 'src/types'; -import type { ButtonProps } from 'src/layout/Button/WrappedButton'; +import type { IButtonProvidedProps } from 'src/layout/Button/ButtonComponent'; -export type Props = Omit & { taskId: string }; - -export const GoToTaskButton = ({ children, taskId, ...props }: Props) => { +export const GoToTaskButton = ({ children, taskId, ...props }: React.PropsWithChildren) => { const dispatch = useAppDispatch(); const availableProcessTasks = useAppSelector((state) => state.process.availableNextTasks); - const canGoToTask = availableProcessTasks && availableProcessTasks.includes(taskId); + const canGoToTask = availableProcessTasks && availableProcessTasks.includes(taskId || ''); const navigateToTask = () => { if (canGoToTask) { dispatch( diff --git a/src/layout/Button/getComponentFromMode.ts b/src/layout/Button/getComponentFromMode.ts index 20ca1c67c7..277ce03384 100644 --- a/src/layout/Button/getComponentFromMode.ts +++ b/src/layout/Button/getComponentFromMode.ts @@ -1,11 +1,17 @@ +import type React from 'react'; + import { GoToTaskButton } from 'src/layout/Button/GoToTaskButton'; import { InstantiationButton } from 'src/layout/InstantiationButton/InstantiationButton'; -export type ButtonMode = 'submit' | 'save' | 'go-to-task' | 'instantiate'; +import type { IButtonProvidedProps } from 'src/layout/Button/ButtonComponent'; +import type { ButtonMode } from 'src/layout/Button/types'; -const buttons = { +const buttons: { [key in ButtonMode]: React.FC> | null } = { + save: null, + submit: null, 'go-to-task': GoToTaskButton, instantiate: InstantiationButton, }; + export const getComponentFromMode = (mode: ButtonMode) => { return buttons[mode]; }; diff --git a/src/layout/Button/types.d.ts b/src/layout/Button/types.d.ts index 44f9eb4d62..cd8fd62e8c 100644 --- a/src/layout/Button/types.d.ts +++ b/src/layout/Button/types.d.ts @@ -1,3 +1,13 @@ import type { ILayoutCompBase } from 'src/layout/layout'; +import type { IMapping } from 'src/types'; -export type ILayoutCompButton = ILayoutCompBase<'Button'>; +export type ButtonMode = 'submit' | 'save' | 'go-to-task' | 'instantiate'; + +export interface ILayoutCompButton extends ILayoutCompBase<'Button'> { + mode?: ButtonMode; + + taskId?: string; // Required for go-to-task + + busyWithId?: string; + mapping?: IMapping; +} diff --git a/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx b/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx index f114ebe9bf..f0cc491dc6 100644 --- a/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx +++ b/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx @@ -35,7 +35,6 @@ const render = (props: Partial = {}, customState: Prelo options: [], optionsId: 'countries', preselectedOptionIndex: undefined, - validationMessages: {}, legend: () => legend, handleDataChange: jest.fn(), getTextResource: (value) => value, diff --git a/src/layout/Checkboxes/CheckboxesContainerComponent.tsx b/src/layout/Checkboxes/CheckboxesContainerComponent.tsx index 7e05b8a20a..e45eb03e8b 100644 --- a/src/layout/Checkboxes/CheckboxesContainerComponent.tsx +++ b/src/layout/Checkboxes/CheckboxesContainerComponent.tsx @@ -13,13 +13,10 @@ import { useDelayedSavedState } from 'src/components/hooks/useDelayedSavedState' import { AltinnSpinner } from 'src/components/shared'; import { shouldUseRowLayout } from 'src/utils/layout'; import { getOptionLookupKey } from 'src/utils/options'; -import { renderValidationMessagesForComponent } from 'src/utils/render'; import type { PropsFromGenericComponent } from 'src/layout'; -import type { IComponentValidations, IOption } from 'src/types'; +import type { IOption } from 'src/types'; -export interface ICheckboxContainerProps extends PropsFromGenericComponent<'Checkboxes'> { - validationMessages: IComponentValidations; -} +export type ICheckboxContainerProps = PropsFromGenericComponent<'Checkboxes'>; interface IStyledCheckboxProps extends CheckboxProps { label: string; @@ -92,7 +89,6 @@ export const CheckboxContainerComponent = ({ readOnly, getTextResourceAsString, getTextResource, - validationMessages, mapping, source, }: ICheckboxContainerProps) => { @@ -177,28 +173,23 @@ export const CheckboxContainerComponent = ({ ) : ( <> {calculatedOptions.map((option, index) => ( - - - } - label={getTextResource(option.label)} - /> - {validationMessages && - isOptionSelected(option.value) && - renderValidationMessagesForComponent(validationMessages.simpleBinding, id)} - + + } + label={getTextResource(option.label)} + /> ))} )} diff --git a/src/layout/GenericComponent.tsx b/src/layout/GenericComponent.tsx index c0a483945c..53ce8b521c 100644 --- a/src/layout/GenericComponent.tsx +++ b/src/layout/GenericComponent.tsx @@ -29,12 +29,13 @@ import type { ExprResolved } from 'src/features/expressions/types'; import type { ISingleFieldValidation } from 'src/features/form/data/formDataTypes'; import type { IComponentProps, IFormComponentContext, PropsFromGenericComponent } from 'src/layout/index'; import type { - ComponentExceptGroup, + ComponentExceptGroupAndSummary, ComponentTypes, IGridStyling, ILayoutCompBase, ILayoutComponent, } from 'src/layout/layout'; +import type { LayoutComponent } from 'src/layout/LayoutComponent'; import type { IComponentValidations, ILabelSettings } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -97,7 +98,9 @@ const useStyles = makeStyles((theme) => ({ }, })); -export function GenericComponent(_props: IActualGenericComponentProps) { +export function GenericComponent( + _props: IActualGenericComponentProps, +) { const props = useExpressionsForComponent(_props as ILayoutComponent) as ExprResolved< IActualGenericComponentProps > & { @@ -220,8 +223,8 @@ export function GenericComponent(_props: IAct passThroughProps.componentValidations = internalComponentValidations; } - const RenderComponent = components[props.type as keyof typeof components]; - if (!RenderComponent) { + const layoutComponent = components[props.type as keyof typeof components]; + if (!layoutComponent) { return (
Unknown component type: {props.type} @@ -231,6 +234,8 @@ export function GenericComponent(_props: IAct ); } + const RenderComponent = layoutComponent.render as LayoutComponent['render']; + const RenderLabel = () => { return ( { - const allProps = { - id: 'instantiate-button', - ...props, - } as InstantiationButtonProps; +const render = () => { const stateMock = getInitialStateMock(); const store = setupStore(stateMock); @@ -26,7 +21,7 @@ const render = ({ props = {} }) => { Instantiate} + element={Instantiate} /> { describe('InstantiationButton', () => { it('should show button and it should be possible to click and start loading', async () => { mockAxios.reset(); - const dispatch = render({}); + const dispatch = render(); expect(screen.getByText('Instantiate')).toBeInTheDocument(); expect(dispatch).toHaveBeenCalledTimes(0); diff --git a/src/layout/InstantiationButton/InstantiationButton.tsx b/src/layout/InstantiationButton/InstantiationButton.tsx index 69e6901f97..03cb02d9e5 100644 --- a/src/layout/InstantiationButton/InstantiationButton.tsx +++ b/src/layout/InstantiationButton/InstantiationButton.tsx @@ -8,12 +8,11 @@ import { useInstantiateWithPrefillMutation } from 'src/services/InstancesApi'; import { AttachmentActions } from 'src/shared/resources/attachments/attachmentSlice'; import { InstanceDataActions } from 'src/shared/resources/instanceData/instanceDataSlice'; import { mapFormData } from 'src/utils/databindings'; -import type { ButtonProps } from 'src/layout/Button/WrappedButton'; import type { IInstantiationButtonComponentProps } from 'src/layout/InstantiationButton/InstantiationButtonComponent'; -export type InstantiationButtonProps = Omit & Omit; +type Props = Omit, 'text'>; -export const InstantiationButton = ({ children, ...props }: InstantiationButtonProps) => { +export const InstantiationButton = ({ children, ...props }: Props) => { const dispatch = useAppDispatch(); const [instantiateWithPrefill, { isSuccess, data, isLoading, isError }] = useInstantiateWithPrefillMutation(); const formData = useAppSelector((state) => state.formData.formData); diff --git a/src/layout/InstantiationButton/types.d.ts b/src/layout/InstantiationButton/types.d.ts index 56e30892a5..96762ce18c 100644 --- a/src/layout/InstantiationButton/types.d.ts +++ b/src/layout/InstantiationButton/types.d.ts @@ -3,4 +3,5 @@ import type { IMapping } from 'src/types'; export interface ILayoutCompInstantiationButton extends ILayoutCompBase<'InstantiationButton'> { mapping?: IMapping; + busyWithId?: string; } diff --git a/src/layout/Likert/GroupContainerLikertTestUtils.tsx b/src/layout/Likert/GroupContainerLikertTestUtils.tsx index 37c8ae3d30..cfda8b29f7 100644 --- a/src/layout/Likert/GroupContainerLikertTestUtils.tsx +++ b/src/layout/Likert/GroupContainerLikertTestUtils.tsx @@ -13,7 +13,7 @@ import type { IUpdateFormData } from 'src/features/form/data/formDataTypes'; import type { ILayoutState } from 'src/features/form/layout/formLayoutSlice'; import type { IValidationState } from 'src/features/form/validation/validationSlice'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup, ILayoutComponent } from 'src/layout/layout'; import type { ILayoutCompLikert } from 'src/layout/Likert/types'; import type { ITextResourcesState } from 'src/shared/resources/textResources'; import type { ILayoutValidations, ITextResource } from 'src/types'; @@ -109,7 +109,11 @@ export const createFormDataUpdateAction = (index: number, optionValue: string): }; }; -const createLayout = (container: ILayoutGroup, components: ILayoutComponent[], groupIndex: number): ILayoutState => { +const createLayout = ( + container: ILayoutGroup, + components: (ILayoutComponent | ComponentInGroup)[], + groupIndex: number, +): ILayoutState => { return { error: null, layoutsets: null, @@ -210,7 +214,7 @@ export const render = ({ }: Partial = {}) => { const mockRadioButton = createRadioButton(radioButtonProps); const mockLikertContainer = createLikertContainer(likertContainerProps); - const components: ILayoutComponent[] = [mockRadioButton]; + const components: ComponentInGroup[] = [mockRadioButton]; const mockData: IFormDataState = { formData: generateMockFormData(mockQuestions), error: null, diff --git a/src/layout/Likert/RepeatingGroupsLikertContainer.tsx b/src/layout/Likert/RepeatingGroupsLikertContainer.tsx index c7cbf099ec..84ebba5fff 100644 --- a/src/layout/Likert/RepeatingGroupsLikertContainer.tsx +++ b/src/layout/Likert/RepeatingGroupsLikertContainer.tsx @@ -13,13 +13,13 @@ import { LayoutStyle } from 'src/types'; import { getTextResource } from 'src/utils/formComponentUtils'; import { getOptionLookupKey } from 'src/utils/options'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup } from 'src/layout/layout'; import type { IRadioButtonsContainerProps } from 'src/layout/RadioButtons/RadioButtonsContainerComponent'; import type { ITextResource } from 'src/types'; type RepeatingGroupsLikertContainerProps = { id: string; - repeatingGroupDeepCopyComponents: ILayoutComponent[]; + repeatingGroupDeepCopyComponents: ComponentInGroup[]; textResources: ITextResource[]; container: ILayoutGroup; }; @@ -93,10 +93,15 @@ export const RepeatingGroupsLikertContainer = ({ aria-describedby={(description && descriptionId) || undefined} > {repeatingGroupDeepCopyComponents.map((comp) => { + if (comp.type === 'Group') { + console.warn('Unexpected group inside likert container', comp); + return; + } + return ( ); })} @@ -143,6 +148,11 @@ export const RepeatingGroupsLikertContainer = ({ padding={'dense'} > {repeatingGroupDeepCopyComponents.map((comp) => { + if (comp.type === 'Group') { + console.warn('Unexpected group inside likert container', comp); + return; + } + return ( = {}, customState: Preloade return renderWithProviders( <> - + , { preloadedState: { diff --git a/src/layout/MultipleSelect/MultipleSelect.tsx b/src/layout/MultipleSelect/MultipleSelectComponent.tsx similarity index 98% rename from src/layout/MultipleSelect/MultipleSelect.tsx rename to src/layout/MultipleSelect/MultipleSelectComponent.tsx index 5e1caf973b..3f920d7aa5 100644 --- a/src/layout/MultipleSelect/MultipleSelect.tsx +++ b/src/layout/MultipleSelect/MultipleSelectComponent.tsx @@ -15,7 +15,7 @@ const invalidBorderColor = '#D5203B !important'; export type IMultipleSelectProps = PropsFromGenericComponent<'MultipleSelect'>; -export function MultipleSelect({ +export function MultipleSelectComponent({ options, optionsId, mapping, diff --git a/src/layout/NavigationBar/NavigationBar.test.tsx b/src/layout/NavigationBar/NavigationBarComponent.test.tsx similarity index 97% rename from src/layout/NavigationBar/NavigationBar.test.tsx rename to src/layout/NavigationBar/NavigationBarComponent.test.tsx index 28f6414783..ab513c9dfc 100644 --- a/src/layout/NavigationBar/NavigationBar.test.tsx +++ b/src/layout/NavigationBar/NavigationBarComponent.test.tsx @@ -4,10 +4,10 @@ import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { FormLayoutActions } from 'src/features/form/layout/formLayoutSlice'; -import { NavigationBar } from 'src/layout/NavigationBar/NavigationBar'; +import { NavigationBarComponent } from 'src/layout/NavigationBar/NavigationBarComponent'; import { setupStore } from 'src/store'; import { mockMediaQuery, renderWithProviders } from 'src/testUtils'; -import type { INavigationBar } from 'src/layout/NavigationBar/NavigationBar'; +import type { INavigationBar } from 'src/layout/NavigationBar/NavigationBarComponent'; const { setScreenWidth } = mockMediaQuery(600); @@ -114,7 +114,7 @@ const render = ({ props = {}, dispatch = jest.fn() } = {}) => { store.dispatch = dispatch; - renderWithProviders(, { + renderWithProviders(, { store, }); }; diff --git a/src/layout/NavigationBar/NavigationBar.tsx b/src/layout/NavigationBar/NavigationBarComponent.tsx similarity index 98% rename from src/layout/NavigationBar/NavigationBar.tsx rename to src/layout/NavigationBar/NavigationBarComponent.tsx index f15eccf30b..7339b17760 100644 --- a/src/layout/NavigationBar/NavigationBar.tsx +++ b/src/layout/NavigationBar/NavigationBarComponent.tsx @@ -97,7 +97,7 @@ const NavigationButton = React.forwardRef( NavigationButton.displayName = 'NavigationButton'; -export const NavigationBar = ({ triggers }: INavigationBar) => { +export const NavigationBarComponent = ({ triggers }: INavigationBar) => { const classes = useStyles(); const dispatch = useAppDispatch(); const pageIds = useAppSelector(selectLayoutOrder); diff --git a/src/layout/NavigationButtons/NavigationButtons.test.tsx b/src/layout/NavigationButtons/NavigationButtonsComponent.test.tsx similarity index 93% rename from src/layout/NavigationButtons/NavigationButtons.test.tsx rename to src/layout/NavigationButtons/NavigationButtonsComponent.test.tsx index 3dac24abe9..b1c5ee2f53 100644 --- a/src/layout/NavigationButtons/NavigationButtons.test.tsx +++ b/src/layout/NavigationButtons/NavigationButtonsComponent.test.tsx @@ -5,8 +5,8 @@ import { render, screen } from '@testing-library/react'; import configureStore from 'redux-mock-store'; import { getFormLayoutStateMock, getInitialStateMock } from 'src/__mocks__/mocks'; -import { NavigationButtons } from 'src/layout/NavigationButtons/NavigationButtons'; -import type { INavigationButtons } from 'src/layout/NavigationButtons/NavigationButtons'; +import { NavigationButtonsComponent } from 'src/layout/NavigationButtons/NavigationButtonsComponent'; +import type { INavigationButtons } from 'src/layout/NavigationButtons/NavigationButtonsComponent'; describe('NavigationButton', () => { let mockStore; @@ -92,7 +92,7 @@ describe('NavigationButton', () => { test('renders default NavigationButtons component', () => { render( - { test('renders NavigationButtons component without back button if there is no previous page', () => { render( - { const store = createStoreNew(initialState); render( - ; -export function NavigationButtons(props: INavigationButtons) { +export function NavigationButtonsComponent(props: INavigationButtons) { const dispatch = useAppDispatch(); const refPrev = React.useRef(); diff --git a/src/layout/layout.d.ts b/src/layout/layout.d.ts index f3e037dc29..1e34a92d3a 100644 --- a/src/layout/layout.d.ts +++ b/src/layout/layout.d.ts @@ -128,6 +128,8 @@ type AllComponents = Map[ComponentTypes]; export type ComponentExceptGroup = Exclude; export type ComponentExceptGroupAndSummary = Exclude; +export type RenderableGenericComponent = ILayoutComponent; +export type ComponentInGroup = RenderableGenericComponent | ILayoutGroup; /** * This type can be used to reference the layout declaration for a component. You can either use it to specify diff --git a/src/utils/formLayout.ts b/src/utils/formLayout.ts index 83138108bc..eaaaf4809f 100644 --- a/src/utils/formLayout.ts +++ b/src/utils/formLayout.ts @@ -1,7 +1,7 @@ import { INDEX_KEY_INDICATOR_REGEX } from 'src/utils/databindings'; import type { IFormData } from 'src/features/form/data'; import type { IGroupEditProperties, IGroupFilter, ILayoutGroup } from 'src/layout/Group/types'; -import type { ComponentTypes, ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup, ComponentTypes, ILayout, ILayoutComponent } from 'src/layout/layout'; import type { IAttachmentState } from 'src/shared/resources/attachments'; import type { IFileUploadersWithTag, @@ -273,8 +273,8 @@ export function createRepeatingGroupComponents( repeatingGroupIndex: number, textResources: ITextResource[], hiddenFields?: string[], -): Array> { - const componentArray: Array> = []; +): ComponentInGroup[][] { + const componentArray: ComponentInGroup[][] = []; const { startIndex, stopIndex } = getRepeatingGroupStartStopIndex(repeatingGroupIndex, container.edit); for (let index = startIndex; index <= stopIndex; index++) { componentArray.push( @@ -305,7 +305,7 @@ export function createRepeatingGroupComponentsForIndex({ index, hiddenFields, }: ICreateRepeatingGroupComponentsForIndexProps) { - return renderComponents.map((component: ILayoutComponent | ILayoutGroup) => { + return renderComponents.map((component: ComponentInGroup) => { if (component.type === 'Group' && component.panel?.groupReference) { // Do not treat as a regular group child as this is merely an option // to add elements for another group from this group context @@ -344,7 +344,7 @@ export function createRepeatingGroupComponentsForIndex({ baseComponentId: componentDeepCopy.baseComponentId || componentDeepCopy.id, hidden, mapping, - }; + } as ComponentInGroup; }); } diff --git a/src/utils/layout/index.tsx b/src/utils/layout/index.tsx index d350654bb0..a029a4cffe 100644 --- a/src/utils/layout/index.tsx +++ b/src/utils/layout/index.tsx @@ -6,7 +6,14 @@ import { PanelGroupContainer } from 'src/layout/Panel/PanelGroupContainer'; import { LayoutStyle } from 'src/types'; import { setMappingForRepeatingGroupComponent } from 'src/utils/formLayout'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponent, ILayoutComponentOrGroup, ILayouts } from 'src/layout/layout'; +import type { + ComponentInGroup, + ILayout, + ILayoutComponent, + ILayoutComponentOrGroup, + ILayouts, + RenderableGenericComponent, +} from 'src/layout/layout'; import type { ILayoutSet, ILayoutSets } from 'src/types'; import type { IInstance } from 'src/types/shared'; @@ -59,7 +66,7 @@ export function matchLayoutComponent(providedId: string, componentId: string) { } interface RenderGenericComponentProps { - component: ILayoutComponentOrGroup; + component: RenderableGenericComponent | ILayoutGroup; layout?: ILayout | null; index?: number; } @@ -82,7 +89,7 @@ export function renderLayoutGroup(layoutGroup: ILayoutGroup, layout?: ILayout, i .map((child) => { return layout?.find((c) => c.id === child); }) - .filter((item) => item !== undefined) as ILayoutComponentOrGroup[]; + .filter((item) => item !== undefined) as ComponentInGroup[]; const panel = layoutGroup.panel; if (panel) { @@ -99,13 +106,7 @@ export function renderLayoutGroup(layoutGroup: ILayoutGroup, layout?: ILayout, i const repeating = layoutGroup.maxCount && layoutGroup.maxCount > 1; if (!repeating) { // If not repeating, treat as regular components - return ( - <> - {deepCopyComponents.map((component: ILayoutComponent) => { - return renderGenericComponent({ component, layout }); - })} - - ); + return <>{deepCopyComponents.map((component) => renderGenericComponent({ component, layout }))}; } return ( @@ -119,11 +120,11 @@ export function renderLayoutGroup(layoutGroup: ILayoutGroup, layout?: ILayout, i } export function setupGroupComponents( - components: (ILayoutComponent | ILayoutGroup)[], + components: ComponentInGroup[], groupDataModelBinding: string | undefined, index: number | undefined, -): (ILayoutGroup | ILayoutComponent)[] { - return components.map((component: ILayoutComponent | ILayoutGroup) => { +): ComponentInGroup[] { + return components.map((component) => { if (component.type === 'Group' && component.panel?.groupReference) { // Do not treat as a regular group child as this is merely an option to add elements for another group from this group context return component; @@ -133,7 +134,7 @@ export function setupGroupComponents( return component; } - const componentDeepCopy: ILayoutComponent = JSON.parse(JSON.stringify(component)); + const componentDeepCopy: ComponentInGroup = JSON.parse(JSON.stringify(component)); const dataModelBindings = { ...componentDeepCopy.dataModelBindings }; Object.keys(dataModelBindings).forEach((key) => { const originalGroupBinding = groupDataModelBinding.replace(`[${index}]`, ''); diff --git a/src/utils/validation/validation.ts b/src/utils/validation/validation.ts index b0683ad3b1..14051d237e 100644 --- a/src/utils/validation/validation.ts +++ b/src/utils/validation/validation.ts @@ -21,7 +21,7 @@ import { getLanguageFromKey, getParsedLanguageFromKey, getTextResourceByKey } fr import type { IFormData } from 'src/features/form/data'; import type { ILayoutCompDatepicker } from 'src/layout/Datepicker/types'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { IDataModelBindings, ILayout, ILayoutComponent, ILayouts } from 'src/layout/layout'; +import type { ComponentInGroup, IDataModelBindings, ILayout, ILayoutComponent, ILayouts } from 'src/layout/layout'; import type { IAttachment, IAttachments } from 'src/shared/resources/attachments'; import type { IComponentBindingValidation, @@ -1158,7 +1158,9 @@ export function repeatingGroupHasValidations( return false; } const childGroupIndex = repeatingGroups[element.id]?.index; - const childGroupComponents = layout.filter((childElement) => element.children?.indexOf(childElement.id) > -1); + const childGroupComponents = layout.filter( + (childElement) => element.children?.indexOf(childElement.id) > -1, + ) as ComponentInGroup[]; const renderComponents = setupGroupComponents(childGroupComponents, element.dataModelBindings?.group, index); const deepCopyComponents = createRepeatingGroupComponents( element, From 3ac05f974cbddf75f62319479092c6cf7986cd7c Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Thu, 29 Dec 2022 15:01:34 +0100 Subject: [PATCH 2/9] Adding bare-bones class all layout components should inherit from --- src/layout/LayoutComponent.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/layout/LayoutComponent.ts diff --git a/src/layout/LayoutComponent.ts b/src/layout/LayoutComponent.ts new file mode 100644 index 0000000000..db77705338 --- /dev/null +++ b/src/layout/LayoutComponent.ts @@ -0,0 +1,6 @@ +import type { PropsFromGenericComponent } from 'src/layout/index'; +import type { ComponentExceptGroupAndSummary } from 'src/layout/layout'; + +export abstract class LayoutComponent { + public abstract render(props: PropsFromGenericComponent): JSX.Element | null; +} From c3ac915c7f92a1a47d8457e19e4f7fde6d3d751f Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Thu, 29 Dec 2022 15:02:05 +0100 Subject: [PATCH 3/9] Implementing class for layout components --- src/layout/Address/index.tsx | 11 +++ src/layout/AttachmentList/index.tsx | 11 +++ src/layout/Button/index.tsx | 11 +++ src/layout/Checkboxes/index.tsx | 11 +++ src/layout/Custom/index.tsx | 11 +++ src/layout/Datepicker/index.tsx | 11 +++ src/layout/Dropdown/index.tsx | 11 +++ src/layout/FileUpload/index.tsx | 11 +++ src/layout/FileUploadWithTag/index.tsx | 11 +++ src/layout/Header/index.tsx | 11 +++ src/layout/Image/index.tsx | 11 +++ src/layout/Input/index.tsx | 11 +++ src/layout/InstanceInformation/index.tsx | 11 +++ src/layout/InstantiationButton/index.tsx | 11 +++ src/layout/Likert/index.tsx | 11 +++ src/layout/List/index.tsx | 11 +++ src/layout/Map/index.tsx | 11 +++ src/layout/MultipleSelect/index.tsx | 11 +++ src/layout/NavigationBar/index.tsx | 11 +++ src/layout/NavigationButtons/index.tsx | 11 +++ src/layout/Panel/index.tsx | 11 +++ src/layout/Paragraph/index.tsx | 11 +++ src/layout/PrintButton/index.tsx | 10 +++ src/layout/RadioButtons/index.tsx | 11 +++ src/layout/TextArea/index.tsx | 11 +++ src/layout/index.ts | 103 ++++++++++++----------- 26 files changed, 326 insertions(+), 51 deletions(-) create mode 100644 src/layout/Address/index.tsx create mode 100644 src/layout/AttachmentList/index.tsx create mode 100644 src/layout/Button/index.tsx create mode 100644 src/layout/Checkboxes/index.tsx create mode 100644 src/layout/Custom/index.tsx create mode 100644 src/layout/Datepicker/index.tsx create mode 100644 src/layout/Dropdown/index.tsx create mode 100644 src/layout/FileUpload/index.tsx create mode 100644 src/layout/FileUploadWithTag/index.tsx create mode 100644 src/layout/Header/index.tsx create mode 100644 src/layout/Image/index.tsx create mode 100644 src/layout/Input/index.tsx create mode 100644 src/layout/InstanceInformation/index.tsx create mode 100644 src/layout/InstantiationButton/index.tsx create mode 100644 src/layout/Likert/index.tsx create mode 100644 src/layout/List/index.tsx create mode 100644 src/layout/Map/index.tsx create mode 100644 src/layout/MultipleSelect/index.tsx create mode 100644 src/layout/NavigationBar/index.tsx create mode 100644 src/layout/NavigationButtons/index.tsx create mode 100644 src/layout/Panel/index.tsx create mode 100644 src/layout/Paragraph/index.tsx create mode 100644 src/layout/PrintButton/index.tsx create mode 100644 src/layout/RadioButtons/index.tsx create mode 100644 src/layout/TextArea/index.tsx diff --git a/src/layout/Address/index.tsx b/src/layout/Address/index.tsx new file mode 100644 index 0000000000..a341cc7eac --- /dev/null +++ b/src/layout/Address/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { AddressComponent } from 'src/layout/Address/AddressComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Address extends LayoutComponent<'AddressComponent'> { + public render(props: PropsFromGenericComponent<'AddressComponent'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/AttachmentList/index.tsx b/src/layout/AttachmentList/index.tsx new file mode 100644 index 0000000000..21a3fa2fca --- /dev/null +++ b/src/layout/AttachmentList/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { AttachmentListComponent } from 'src/layout/AttachmentList/AttachmentListComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class AttachmentList extends LayoutComponent<'AttachmentList'> { + public render(props: PropsFromGenericComponent<'AttachmentList'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Button/index.tsx b/src/layout/Button/index.tsx new file mode 100644 index 0000000000..8db1fbd581 --- /dev/null +++ b/src/layout/Button/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { ButtonComponent } from 'src/layout/Button/ButtonComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Button extends LayoutComponent<'Button'> { + public render(props: PropsFromGenericComponent<'Button'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Checkboxes/index.tsx b/src/layout/Checkboxes/index.tsx new file mode 100644 index 0000000000..5c23f9fbae --- /dev/null +++ b/src/layout/Checkboxes/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { CheckboxContainerComponent } from 'src/layout/Checkboxes/CheckboxesContainerComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Checkboxes extends LayoutComponent<'Checkboxes'> { + public render(props: PropsFromGenericComponent<'Checkboxes'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Custom/index.tsx b/src/layout/Custom/index.tsx new file mode 100644 index 0000000000..90d844526d --- /dev/null +++ b/src/layout/Custom/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { CustomWebComponent } from 'src/layout/Custom/CustomWebComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Custom extends LayoutComponent<'Custom'> { + public render(props: PropsFromGenericComponent<'Custom'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Datepicker/index.tsx b/src/layout/Datepicker/index.tsx new file mode 100644 index 0000000000..e6dbf8652f --- /dev/null +++ b/src/layout/Datepicker/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { DatepickerComponent } from 'src/layout/Datepicker/DatepickerComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Datepicker extends LayoutComponent<'Datepicker'> { + public render(props: PropsFromGenericComponent<'Datepicker'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Dropdown/index.tsx b/src/layout/Dropdown/index.tsx new file mode 100644 index 0000000000..92b89161dc --- /dev/null +++ b/src/layout/Dropdown/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { DropdownComponent } from 'src/layout/Dropdown/DropdownComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Dropdown extends LayoutComponent<'Dropdown'> { + public render(props: PropsFromGenericComponent<'Dropdown'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/FileUpload/index.tsx b/src/layout/FileUpload/index.tsx new file mode 100644 index 0000000000..040315db3f --- /dev/null +++ b/src/layout/FileUpload/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { FileUploadComponent } from 'src/layout/FileUpload/FileUploadComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class FileUpload extends LayoutComponent<'FileUpload'> { + public render(props: PropsFromGenericComponent<'FileUpload'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/FileUploadWithTag/index.tsx b/src/layout/FileUploadWithTag/index.tsx new file mode 100644 index 0000000000..a09fb35689 --- /dev/null +++ b/src/layout/FileUploadWithTag/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { FileUploadWithTagComponent } from 'src/layout/FileUploadWithTag/FileUploadWithTagComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class FileUploadWithTag extends LayoutComponent<'FileUploadWithTag'> { + public render(props: PropsFromGenericComponent<'FileUploadWithTag'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Header/index.tsx b/src/layout/Header/index.tsx new file mode 100644 index 0000000000..7e832b186b --- /dev/null +++ b/src/layout/Header/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { HeaderComponent } from 'src/layout/Header/HeaderComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Header extends LayoutComponent<'Header'> { + public render(props: PropsFromGenericComponent<'Header'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Image/index.tsx b/src/layout/Image/index.tsx new file mode 100644 index 0000000000..ddc72c00fd --- /dev/null +++ b/src/layout/Image/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { ImageComponent } from 'src/layout/Image/ImageComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Image extends LayoutComponent<'Image'> { + public render(props: PropsFromGenericComponent<'Image'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Input/index.tsx b/src/layout/Input/index.tsx new file mode 100644 index 0000000000..ce2327902b --- /dev/null +++ b/src/layout/Input/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { InputComponent } from 'src/layout/Input/InputComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Input extends LayoutComponent<'Input'> { + public render(props: PropsFromGenericComponent<'Input'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/InstanceInformation/index.tsx b/src/layout/InstanceInformation/index.tsx new file mode 100644 index 0000000000..54913f1bfb --- /dev/null +++ b/src/layout/InstanceInformation/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { InstanceInformationComponent } from 'src/layout/InstanceInformation/InstanceInformationComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class InstanceInformation extends LayoutComponent<'InstanceInformation'> { + public render(props: PropsFromGenericComponent<'InstanceInformation'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/InstantiationButton/index.tsx b/src/layout/InstantiationButton/index.tsx new file mode 100644 index 0000000000..baf4e0d3f1 --- /dev/null +++ b/src/layout/InstantiationButton/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { InstantiationButtonComponent } from 'src/layout/InstantiationButton/InstantiationButtonComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class InstantiationButton extends LayoutComponent<'InstantiationButton'> { + public render(props: PropsFromGenericComponent<'InstantiationButton'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Likert/index.tsx b/src/layout/Likert/index.tsx new file mode 100644 index 0000000000..00200a3ab5 --- /dev/null +++ b/src/layout/Likert/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { LikertComponent } from 'src/layout/Likert/LikertComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Likert extends LayoutComponent<'Likert'> { + public render(props: PropsFromGenericComponent<'Likert'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/List/index.tsx b/src/layout/List/index.tsx new file mode 100644 index 0000000000..7b294a3565 --- /dev/null +++ b/src/layout/List/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { ListComponent } from 'src/layout/List/ListComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class List extends LayoutComponent<'List'> { + public render(props: PropsFromGenericComponent<'List'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Map/index.tsx b/src/layout/Map/index.tsx new file mode 100644 index 0000000000..492e360cdc --- /dev/null +++ b/src/layout/Map/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { MapComponent } from 'src/layout/Map/MapComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Map extends LayoutComponent<'Map'> { + public render(props: PropsFromGenericComponent<'Map'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/MultipleSelect/index.tsx b/src/layout/MultipleSelect/index.tsx new file mode 100644 index 0000000000..423fa034e8 --- /dev/null +++ b/src/layout/MultipleSelect/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { MultipleSelectComponent } from 'src/layout/MultipleSelect/MultipleSelectComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class MultipleSelect extends LayoutComponent<'MultipleSelect'> { + public render(props: PropsFromGenericComponent<'MultipleSelect'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/NavigationBar/index.tsx b/src/layout/NavigationBar/index.tsx new file mode 100644 index 0000000000..39f4f2e160 --- /dev/null +++ b/src/layout/NavigationBar/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { NavigationBarComponent } from 'src/layout/NavigationBar/NavigationBarComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class NavigationBar extends LayoutComponent<'NavigationBar'> { + public render(props: PropsFromGenericComponent<'NavigationBar'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/NavigationButtons/index.tsx b/src/layout/NavigationButtons/index.tsx new file mode 100644 index 0000000000..edffda6562 --- /dev/null +++ b/src/layout/NavigationButtons/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { NavigationButtonsComponent } from 'src/layout/NavigationButtons/NavigationButtonsComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class NavigationButtons extends LayoutComponent<'NavigationButtons'> { + public render(props: PropsFromGenericComponent<'NavigationButtons'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Panel/index.tsx b/src/layout/Panel/index.tsx new file mode 100644 index 0000000000..baa2acb2c5 --- /dev/null +++ b/src/layout/Panel/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { PanelComponent } from 'src/layout/Panel/PanelComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Panel extends LayoutComponent<'Panel'> { + public render(props: PropsFromGenericComponent<'Panel'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Paragraph/index.tsx b/src/layout/Paragraph/index.tsx new file mode 100644 index 0000000000..37dc271ce4 --- /dev/null +++ b/src/layout/Paragraph/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { ParagraphComponent } from 'src/layout/Paragraph/ParagraphComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Paragraph extends LayoutComponent<'Paragraph'> { + public render(props: PropsFromGenericComponent<'Paragraph'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/PrintButton/index.tsx b/src/layout/PrintButton/index.tsx new file mode 100644 index 0000000000..493f52edc8 --- /dev/null +++ b/src/layout/PrintButton/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { PrintButtonComponent } from 'src/layout/PrintButton/PrintButtonComponent'; + +export class PrintButton extends LayoutComponent<'PrintButton'> { + public render(): JSX.Element | null { + return ; + } +} diff --git a/src/layout/RadioButtons/index.tsx b/src/layout/RadioButtons/index.tsx new file mode 100644 index 0000000000..b2f94a4154 --- /dev/null +++ b/src/layout/RadioButtons/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { RadioButtonContainerComponent } from 'src/layout/RadioButtons/RadioButtonsContainerComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class RadioButtons extends LayoutComponent<'RadioButtons'> { + public render(props: PropsFromGenericComponent<'RadioButtons'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/TextArea/index.tsx b/src/layout/TextArea/index.tsx new file mode 100644 index 0000000000..7953c55904 --- /dev/null +++ b/src/layout/TextArea/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import { TextAreaComponent } from 'src/layout/TextArea/TextAreaComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class TextArea extends LayoutComponent<'TextArea'> { + public render(props: PropsFromGenericComponent<'TextArea'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/index.ts b/src/layout/index.ts index ac7deb15f3..5d2fa1ba03 100644 --- a/src/layout/index.ts +++ b/src/layout/index.ts @@ -1,65 +1,66 @@ import { createContext } from 'react'; import type React from 'react'; -import { AddressComponent as Address } from 'src/layout/Address/AddressComponent'; -import { AttachmentListComponent } from 'src/layout/AttachmentList/AttachmentListComponent'; -import { ButtonComponent } from 'src/layout/Button/ButtonComponent'; -import { CheckboxContainerComponent } from 'src/layout/Checkboxes/CheckboxesContainerComponent'; -import { CustomWebComponent } from 'src/layout/Custom/CustomWebComponent'; -import { DatepickerComponent } from 'src/layout/Datepicker/DatepickerComponent'; -import { DropdownComponent } from 'src/layout/Dropdown/DropdownComponent'; -import { FileUploadComponent } from 'src/layout/FileUpload/FileUploadComponent'; -import { FileUploadWithTagComponent } from 'src/layout/FileUploadWithTag/FileUploadWithTagComponent'; -import { HeaderComponent } from 'src/layout/Header/HeaderComponent'; -import { ImageComponent } from 'src/layout/Image/ImageComponent'; -import { InputComponent } from 'src/layout/Input/InputComponent'; -import { InstanceInformationComponent } from 'src/layout/InstanceInformation/InstanceInformationComponent'; -import { InstantiationButtonComponent } from 'src/layout/InstantiationButton/InstantiationButtonComponent'; -import { LikertComponent } from 'src/layout/Likert/LikertComponent'; -import { ListComponent } from 'src/layout/List/ListComponent'; -import { MapComponent } from 'src/layout/Map/MapComponent'; -import { MultipleSelect } from 'src/layout/MultipleSelect/MultipleSelect'; -import { NavigationBar as NavigationBarComponent } from 'src/layout/NavigationBar/NavigationBar'; -import { NavigationButtons as NavigationButtonsComponent } from 'src/layout/NavigationButtons/NavigationButtons'; -import { PanelComponent } from 'src/layout/Panel/PanelComponent'; -import { ParagraphComponent } from 'src/layout/Paragraph/ParagraphComponent'; -import { PrintButtonComponent } from 'src/layout/PrintButton/PrintButtonComponent'; -import { RadioButtonContainerComponent } from 'src/layout/RadioButtons/RadioButtonsContainerComponent'; -import { TextAreaComponent } from 'src/layout/TextArea/TextAreaComponent'; +import { Address } from 'src/layout/Address/index'; +import { AttachmentList } from 'src/layout/AttachmentList/index'; +import { Button } from 'src/layout/Button/index'; +import { Checkboxes } from 'src/layout/Checkboxes/index'; +import { Custom } from 'src/layout/Custom/index'; +import { Datepicker } from 'src/layout/Datepicker/index'; +import { Dropdown } from 'src/layout/Dropdown/index'; +import { FileUpload } from 'src/layout/FileUpload/index'; +import { FileUploadWithTag } from 'src/layout/FileUploadWithTag/index'; +import { Header } from 'src/layout/Header/index'; +import { Image } from 'src/layout/Image/index'; +import { Input } from 'src/layout/Input/index'; +import { InstanceInformation } from 'src/layout/InstanceInformation/index'; +import { InstantiationButton } from 'src/layout/InstantiationButton/index'; +import { Likert } from 'src/layout/Likert/index'; +import { List } from 'src/layout/List/index'; +import { Map } from 'src/layout/Map/index'; +import { MultipleSelect } from 'src/layout/MultipleSelect/index'; +import { NavigationBar } from 'src/layout/NavigationBar/index'; +import { NavigationButtons } from 'src/layout/NavigationButtons/index'; +import { Panel } from 'src/layout/Panel/index'; +import { Paragraph } from 'src/layout/Paragraph/index'; +import { PrintButton } from 'src/layout/PrintButton/index'; +import { RadioButtons } from 'src/layout/RadioButtons/index'; +import { TextArea } from 'src/layout/TextArea/index'; import type { ExprResolved } from 'src/features/expressions/types'; import type { IGenericComponentProps } from 'src/layout/GenericComponent'; import type { ComponentExceptGroup, ComponentExceptGroupAndSummary, IGrid, ILayoutComponent } from 'src/layout/layout'; +import type { LayoutComponent } from 'src/layout/LayoutComponent'; import type { ILanguage } from 'src/types/shared'; import type { IComponentFormData } from 'src/utils/formComponentUtils'; const components: { - [Type in ComponentExceptGroupAndSummary]: (props: any) => JSX.Element | null; + [Type in ComponentExceptGroupAndSummary]: LayoutComponent; } = { - AddressComponent: Address, - AttachmentList: AttachmentListComponent, - Button: ButtonComponent, - Checkboxes: CheckboxContainerComponent, - Custom: CustomWebComponent, - Datepicker: DatepickerComponent, - Dropdown: DropdownComponent, - FileUpload: FileUploadComponent, - FileUploadWithTag: FileUploadWithTagComponent, - Header: HeaderComponent, - Image: ImageComponent, - Input: InputComponent, - InstanceInformation: InstanceInformationComponent, - InstantiationButton: InstantiationButtonComponent, - Likert: LikertComponent, - Map: MapComponent, - MultipleSelect: MultipleSelect, - NavigationBar: NavigationBarComponent, - NavigationButtons: NavigationButtonsComponent, - Panel: PanelComponent, - Paragraph: ParagraphComponent, - PrintButton: PrintButtonComponent, - RadioButtons: RadioButtonContainerComponent, - TextArea: TextAreaComponent, - List: ListComponent, + AddressComponent: new Address(), + AttachmentList: new AttachmentList(), + Button: new Button(), + Checkboxes: new Checkboxes(), + Custom: new Custom(), + Datepicker: new Datepicker(), + Dropdown: new Dropdown(), + FileUpload: new FileUpload(), + FileUploadWithTag: new FileUploadWithTag(), + Header: new Header(), + Image: new Image(), + Input: new Input(), + InstanceInformation: new InstanceInformation(), + InstantiationButton: new InstantiationButton(), + Likert: new Likert(), + Map: new Map(), + MultipleSelect: new MultipleSelect(), + NavigationBar: new NavigationBar(), + NavigationButtons: new NavigationButtons(), + Panel: new Panel(), + Paragraph: new Paragraph(), + PrintButton: new PrintButton(), + RadioButtons: new RadioButtons(), + TextArea: new TextArea(), + List: new List(), }; export interface IComponentProps extends IGenericComponentProps { From 472c0a304d3b81d70af7c470f1512231a9020b75 Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Thu, 29 Dec 2022 15:09:24 +0100 Subject: [PATCH 4/9] Implementing an example of how this architecture can move component-specific logic to where the code should belong (in the layout component folder) --- src/layout/GenericComponent.tsx | 10 +++++++--- src/layout/LayoutComponent.ts | 11 +++++++++++ src/layout/Likert/index.tsx | 5 +++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/layout/GenericComponent.tsx b/src/layout/GenericComponent.tsx index 53ce8b521c..279687d57a 100644 --- a/src/layout/GenericComponent.tsx +++ b/src/layout/GenericComponent.tsx @@ -223,7 +223,7 @@ export function GenericComponent( passThroughProps.componentValidations = internalComponentValidations; } - const layoutComponent = components[props.type as keyof typeof components]; + const layoutComponent = components[props.type as keyof typeof components] as LayoutComponent; if (!layoutComponent) { return (
@@ -319,8 +319,12 @@ export function GenericComponent( const showValidationMessages = componentValidationsHandledByGenericComponent(props.dataModelBindings, props.type) && hasValidationMessages; - if (props.type === 'Likert' && props.layout === LayoutStyle.Table) { - return ; + if (layoutComponent.directRender(componentProps)) { + return ( + + + + ); } return ( diff --git a/src/layout/LayoutComponent.ts b/src/layout/LayoutComponent.ts index db77705338..0fb834bc46 100644 --- a/src/layout/LayoutComponent.ts +++ b/src/layout/LayoutComponent.ts @@ -2,5 +2,16 @@ import type { PropsFromGenericComponent } from 'src/layout/index'; import type { ComponentExceptGroupAndSummary } from 'src/layout/layout'; export abstract class LayoutComponent { + /** + * Given properties from GenericComponent, render this layout component + */ public abstract render(props: PropsFromGenericComponent): JSX.Element | null; + + /** + * Direct render? Override this and return true if you want GenericComponent to omit rendering grid, + * validation messages, etc. + */ + public directRender(_props: PropsFromGenericComponent): boolean { + return false; + } } diff --git a/src/layout/Likert/index.tsx b/src/layout/Likert/index.tsx index 00200a3ab5..a2e6e503d7 100644 --- a/src/layout/Likert/index.tsx +++ b/src/layout/Likert/index.tsx @@ -2,10 +2,15 @@ import React from 'react'; import { LayoutComponent } from 'src/layout/LayoutComponent'; import { LikertComponent } from 'src/layout/Likert/LikertComponent'; +import { LayoutStyle } from 'src/types'; import type { PropsFromGenericComponent } from 'src/layout'; export class Likert extends LayoutComponent<'Likert'> { public render(props: PropsFromGenericComponent<'Likert'>): JSX.Element | null { return ; } + + public directRender(props: PropsFromGenericComponent<'Likert'>): boolean { + return props.layout === LayoutStyle.Table; + } } From 15fcc0d60a811522ad778feae9f3a95a9c3e195f Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Fri, 30 Dec 2022 11:25:14 +0100 Subject: [PATCH 5/9] Adding function to indicate if a component should render with label or not. Cleaning up some splat-properties and passing componentValidations to everyone (each component can decide on their own if they want to use the validations). --- src/features/form/components/Legend.tsx | 2 +- src/layout/Address/index.tsx | 6 ++- src/layout/AttachmentList/index.tsx | 6 ++- src/layout/Button/index.tsx | 6 ++- src/layout/Checkboxes/index.tsx | 6 ++- src/layout/Custom/index.tsx | 6 ++- src/layout/Datepicker/index.tsx | 2 +- src/layout/Dropdown/index.tsx | 2 +- src/layout/FileUpload/index.tsx | 2 +- src/layout/FileUploadWithTag/index.tsx | 2 +- src/layout/GenericComponent.tsx | 51 ++++-------------------- src/layout/Header/index.tsx | 6 ++- src/layout/Image/index.tsx | 6 ++- src/layout/Input/index.tsx | 2 +- src/layout/InstanceInformation/index.tsx | 2 +- src/layout/InstantiationButton/index.tsx | 6 ++- src/layout/LayoutComponent.ts | 11 ++++- src/layout/Likert/index.tsx | 8 +++- src/layout/List/index.tsx | 2 +- src/layout/Map/index.tsx | 2 +- src/layout/MultipleSelect/index.tsx | 2 +- src/layout/NavigationBar/index.tsx | 6 ++- src/layout/NavigationButtons/index.tsx | 6 ++- src/layout/Panel/index.tsx | 6 ++- src/layout/Paragraph/index.tsx | 6 ++- src/layout/PrintButton/index.tsx | 2 +- src/layout/RadioButtons/index.tsx | 6 ++- src/layout/TextArea/index.tsx | 2 +- src/layout/index.ts | 2 + 29 files changed, 102 insertions(+), 72 deletions(-) diff --git a/src/features/form/components/Legend.tsx b/src/features/form/components/Legend.tsx index aac420dea6..c6130edc68 100644 --- a/src/features/form/components/Legend.tsx +++ b/src/features/form/components/Legend.tsx @@ -59,7 +59,7 @@ export default function Legend(props: IFormLegendProps) { {props.descriptionText && ( )} diff --git a/src/layout/Address/index.tsx b/src/layout/Address/index.tsx index a341cc7eac..a2e1aaf4d7 100644 --- a/src/layout/Address/index.tsx +++ b/src/layout/Address/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Address extends LayoutComponent<'AddressComponent'> { - public render(props: PropsFromGenericComponent<'AddressComponent'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'AddressComponent'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/AttachmentList/index.tsx b/src/layout/AttachmentList/index.tsx index 21a3fa2fca..fc92bd5835 100644 --- a/src/layout/AttachmentList/index.tsx +++ b/src/layout/AttachmentList/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class AttachmentList extends LayoutComponent<'AttachmentList'> { - public render(props: PropsFromGenericComponent<'AttachmentList'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'AttachmentList'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Button/index.tsx b/src/layout/Button/index.tsx index 8db1fbd581..725afb6fdd 100644 --- a/src/layout/Button/index.tsx +++ b/src/layout/Button/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Button extends LayoutComponent<'Button'> { - public render(props: PropsFromGenericComponent<'Button'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Button'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Checkboxes/index.tsx b/src/layout/Checkboxes/index.tsx index 5c23f9fbae..56126d2046 100644 --- a/src/layout/Checkboxes/index.tsx +++ b/src/layout/Checkboxes/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Checkboxes extends LayoutComponent<'Checkboxes'> { - public render(props: PropsFromGenericComponent<'Checkboxes'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Checkboxes'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Custom/index.tsx b/src/layout/Custom/index.tsx index 90d844526d..f6e2f6702c 100644 --- a/src/layout/Custom/index.tsx +++ b/src/layout/Custom/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Custom extends LayoutComponent<'Custom'> { - public render(props: PropsFromGenericComponent<'Custom'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Custom'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Datepicker/index.tsx b/src/layout/Datepicker/index.tsx index e6dbf8652f..2100aedf74 100644 --- a/src/layout/Datepicker/index.tsx +++ b/src/layout/Datepicker/index.tsx @@ -5,7 +5,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Datepicker extends LayoutComponent<'Datepicker'> { - public render(props: PropsFromGenericComponent<'Datepicker'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Datepicker'>): JSX.Element | null { return ; } } diff --git a/src/layout/Dropdown/index.tsx b/src/layout/Dropdown/index.tsx index 92b89161dc..e54e7fb842 100644 --- a/src/layout/Dropdown/index.tsx +++ b/src/layout/Dropdown/index.tsx @@ -5,7 +5,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Dropdown extends LayoutComponent<'Dropdown'> { - public render(props: PropsFromGenericComponent<'Dropdown'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Dropdown'>): JSX.Element | null { return ; } } diff --git a/src/layout/FileUpload/index.tsx b/src/layout/FileUpload/index.tsx index 040315db3f..4c4c14acc5 100644 --- a/src/layout/FileUpload/index.tsx +++ b/src/layout/FileUpload/index.tsx @@ -5,7 +5,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class FileUpload extends LayoutComponent<'FileUpload'> { - public render(props: PropsFromGenericComponent<'FileUpload'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'FileUpload'>): JSX.Element | null { return ; } } diff --git a/src/layout/FileUploadWithTag/index.tsx b/src/layout/FileUploadWithTag/index.tsx index a09fb35689..6b29ecf2d9 100644 --- a/src/layout/FileUploadWithTag/index.tsx +++ b/src/layout/FileUploadWithTag/index.tsx @@ -5,7 +5,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class FileUploadWithTag extends LayoutComponent<'FileUploadWithTag'> { - public render(props: PropsFromGenericComponent<'FileUploadWithTag'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'FileUploadWithTag'>): JSX.Element | null { return ; } } diff --git a/src/layout/GenericComponent.tsx b/src/layout/GenericComponent.tsx index 279687d57a..0e8aa314b6 100644 --- a/src/layout/GenericComponent.tsx +++ b/src/layout/GenericComponent.tsx @@ -13,7 +13,7 @@ import { FormDataActions } from 'src/features/form/data/formDataSlice'; import { FormLayoutActions } from 'src/features/form/layout/formLayoutSlice'; import components, { FormComponentContext } from 'src/layout/index'; import { makeGetFocus, makeGetHidden } from 'src/selectors/getLayoutData'; -import { LayoutStyle, Triggers } from 'src/types'; +import { Triggers } from 'src/types'; import { componentHasValidationMessages, componentValidationsHandledByGenericComponent, @@ -36,11 +36,10 @@ import type { ILayoutComponent, } from 'src/layout/layout'; import type { LayoutComponent } from 'src/layout/LayoutComponent'; -import type { IComponentValidations, ILabelSettings } from 'src/types'; +import type { ILabelSettings, LayoutStyle } from 'src/types'; import type { ILanguage } from 'src/types/shared'; export interface IGenericComponentProps { - componentValidations?: IComponentValidations; labelSettings?: ILabelSettings; layout?: LayoutStyle; groupContainerId?: string; @@ -204,25 +203,6 @@ export function GenericComponent( ); }; - const getValidationsForInternalHandling = () => { - if ( - props.type === 'AddressComponent' || - props.type === 'Datepicker' || - props.type === 'FileUpload' || - props.type === 'FileUploadWithTag' || - (props.type === 'Likert' && props.layout === LayoutStyle.Table) - ) { - return componentValidations; - } - return null; - }; - - // some components handle their validations internally (i.e merge with internal validation state) - const internalComponentValidations = getValidationsForInternalHandling(); - if (internalComponentValidations !== null) { - passThroughProps.componentValidations = internalComponentValidations; - } - const layoutComponent = components[props.type as keyof typeof components] as LayoutComponent; if (!layoutComponent) { return ( @@ -257,7 +237,6 @@ export function GenericComponent( key={`description-${props.id}`} description={texts.description} id={id} - {...passThroughProps} /> ); }; @@ -270,8 +249,10 @@ export function GenericComponent( descriptionText={texts.description} helpText={texts.help} language={language} - {...props} - {...passThroughProps} + id={props.id} + required={props.required} + labelSettings={props.labelSettings} + layout={props.layout} /> ); }; @@ -296,26 +277,10 @@ export function GenericComponent( text: texts.title, label: RenderLabel, legend: RenderLegend, + componentValidations, ...passThroughProps, } as unknown as PropsFromGenericComponent; - const noLabelComponents: ComponentTypes[] = [ - 'Header', - 'Paragraph', - 'Image', - 'NavigationButtons', - 'Custom', - 'AddressComponent', - 'Button', - 'Checkboxes', - 'RadioButtons', - 'AttachmentList', - 'InstantiationButton', - 'NavigationBar', - 'Likert', - 'Panel', - ]; - const showValidationMessages = componentValidationsHandledByGenericComponent(props.dataModelBindings, props.type) && hasValidationMessages; @@ -343,7 +308,7 @@ export function GenericComponent( )} alignItems='baseline' > - {!noLabelComponents.includes(props.type) && ( + {layoutComponent.renderWithLabel() && ( { - public render(props: PropsFromGenericComponent<'Header'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Header'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Image/index.tsx b/src/layout/Image/index.tsx index ddc72c00fd..34835d135e 100644 --- a/src/layout/Image/index.tsx +++ b/src/layout/Image/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Image extends LayoutComponent<'Image'> { - public render(props: PropsFromGenericComponent<'Image'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Image'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Input/index.tsx b/src/layout/Input/index.tsx index ce2327902b..c2ed32a26e 100644 --- a/src/layout/Input/index.tsx +++ b/src/layout/Input/index.tsx @@ -5,7 +5,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Input extends LayoutComponent<'Input'> { - public render(props: PropsFromGenericComponent<'Input'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Input'>): JSX.Element | null { return ; } } diff --git a/src/layout/InstanceInformation/index.tsx b/src/layout/InstanceInformation/index.tsx index 54913f1bfb..d4502d3a19 100644 --- a/src/layout/InstanceInformation/index.tsx +++ b/src/layout/InstanceInformation/index.tsx @@ -5,7 +5,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class InstanceInformation extends LayoutComponent<'InstanceInformation'> { - public render(props: PropsFromGenericComponent<'InstanceInformation'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'InstanceInformation'>): JSX.Element | null { return ; } } diff --git a/src/layout/InstantiationButton/index.tsx b/src/layout/InstantiationButton/index.tsx index baf4e0d3f1..24c6085fe4 100644 --- a/src/layout/InstantiationButton/index.tsx +++ b/src/layout/InstantiationButton/index.tsx @@ -5,7 +5,11 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class InstantiationButton extends LayoutComponent<'InstantiationButton'> { - public render(props: PropsFromGenericComponent<'InstantiationButton'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'InstantiationButton'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/LayoutComponent.ts b/src/layout/LayoutComponent.ts index 0fb834bc46..2fe985e812 100644 --- a/src/layout/LayoutComponent.ts +++ b/src/layout/LayoutComponent.ts @@ -5,13 +5,20 @@ export abstract class LayoutComponent): JSX.Element | null; + abstract render(props: PropsFromGenericComponent): JSX.Element | null; /** * Direct render? Override this and return true if you want GenericComponent to omit rendering grid, * validation messages, etc. */ - public directRender(_props: PropsFromGenericComponent): boolean { + directRender(_props: PropsFromGenericComponent): boolean { return false; } + + /** + * Return false to render this component without the label (in GenericComponent.tsx) + */ + renderWithLabel(): boolean { + return true; + } } diff --git a/src/layout/Likert/index.tsx b/src/layout/Likert/index.tsx index a2e6e503d7..f724a611ef 100644 --- a/src/layout/Likert/index.tsx +++ b/src/layout/Likert/index.tsx @@ -6,11 +6,15 @@ import { LayoutStyle } from 'src/types'; import type { PropsFromGenericComponent } from 'src/layout'; export class Likert extends LayoutComponent<'Likert'> { - public render(props: PropsFromGenericComponent<'Likert'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Likert'>): JSX.Element | null { return ; } - public directRender(props: PropsFromGenericComponent<'Likert'>): boolean { + directRender(props: PropsFromGenericComponent<'Likert'>): boolean { return props.layout === LayoutStyle.Table; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/List/index.tsx b/src/layout/List/index.tsx index 7b294a3565..776631e62b 100644 --- a/src/layout/List/index.tsx +++ b/src/layout/List/index.tsx @@ -5,7 +5,7 @@ import { ListComponent } from 'src/layout/List/ListComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class List extends LayoutComponent<'List'> { - public render(props: PropsFromGenericComponent<'List'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'List'>): JSX.Element | null { return ; } } diff --git a/src/layout/Map/index.tsx b/src/layout/Map/index.tsx index 492e360cdc..2b05063d45 100644 --- a/src/layout/Map/index.tsx +++ b/src/layout/Map/index.tsx @@ -5,7 +5,7 @@ import { MapComponent } from 'src/layout/Map/MapComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Map extends LayoutComponent<'Map'> { - public render(props: PropsFromGenericComponent<'Map'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Map'>): JSX.Element | null { return ; } } diff --git a/src/layout/MultipleSelect/index.tsx b/src/layout/MultipleSelect/index.tsx index 423fa034e8..b805ae6d8c 100644 --- a/src/layout/MultipleSelect/index.tsx +++ b/src/layout/MultipleSelect/index.tsx @@ -5,7 +5,7 @@ import { MultipleSelectComponent } from 'src/layout/MultipleSelect/MultipleSelec import type { PropsFromGenericComponent } from 'src/layout'; export class MultipleSelect extends LayoutComponent<'MultipleSelect'> { - public render(props: PropsFromGenericComponent<'MultipleSelect'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'MultipleSelect'>): JSX.Element | null { return ; } } diff --git a/src/layout/NavigationBar/index.tsx b/src/layout/NavigationBar/index.tsx index 39f4f2e160..57858ff5b3 100644 --- a/src/layout/NavigationBar/index.tsx +++ b/src/layout/NavigationBar/index.tsx @@ -5,7 +5,11 @@ import { NavigationBarComponent } from 'src/layout/NavigationBar/NavigationBarCo import type { PropsFromGenericComponent } from 'src/layout'; export class NavigationBar extends LayoutComponent<'NavigationBar'> { - public render(props: PropsFromGenericComponent<'NavigationBar'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'NavigationBar'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/NavigationButtons/index.tsx b/src/layout/NavigationButtons/index.tsx index edffda6562..a6e8d12fea 100644 --- a/src/layout/NavigationButtons/index.tsx +++ b/src/layout/NavigationButtons/index.tsx @@ -5,7 +5,11 @@ import { NavigationButtonsComponent } from 'src/layout/NavigationButtons/Navigat import type { PropsFromGenericComponent } from 'src/layout'; export class NavigationButtons extends LayoutComponent<'NavigationButtons'> { - public render(props: PropsFromGenericComponent<'NavigationButtons'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'NavigationButtons'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Panel/index.tsx b/src/layout/Panel/index.tsx index baa2acb2c5..b5bcea8f43 100644 --- a/src/layout/Panel/index.tsx +++ b/src/layout/Panel/index.tsx @@ -5,7 +5,11 @@ import { PanelComponent } from 'src/layout/Panel/PanelComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Panel extends LayoutComponent<'Panel'> { - public render(props: PropsFromGenericComponent<'Panel'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Panel'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/Paragraph/index.tsx b/src/layout/Paragraph/index.tsx index 37dc271ce4..dfc75381d0 100644 --- a/src/layout/Paragraph/index.tsx +++ b/src/layout/Paragraph/index.tsx @@ -5,7 +5,11 @@ import { ParagraphComponent } from 'src/layout/Paragraph/ParagraphComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class Paragraph extends LayoutComponent<'Paragraph'> { - public render(props: PropsFromGenericComponent<'Paragraph'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'Paragraph'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/PrintButton/index.tsx b/src/layout/PrintButton/index.tsx index 493f52edc8..7afea4c10e 100644 --- a/src/layout/PrintButton/index.tsx +++ b/src/layout/PrintButton/index.tsx @@ -4,7 +4,7 @@ import { LayoutComponent } from 'src/layout/LayoutComponent'; import { PrintButtonComponent } from 'src/layout/PrintButton/PrintButtonComponent'; export class PrintButton extends LayoutComponent<'PrintButton'> { - public render(): JSX.Element | null { + render(): JSX.Element | null { return ; } } diff --git a/src/layout/RadioButtons/index.tsx b/src/layout/RadioButtons/index.tsx index b2f94a4154..7cc14da22f 100644 --- a/src/layout/RadioButtons/index.tsx +++ b/src/layout/RadioButtons/index.tsx @@ -5,7 +5,11 @@ import { RadioButtonContainerComponent } from 'src/layout/RadioButtons/RadioButt import type { PropsFromGenericComponent } from 'src/layout'; export class RadioButtons extends LayoutComponent<'RadioButtons'> { - public render(props: PropsFromGenericComponent<'RadioButtons'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'RadioButtons'>): JSX.Element | null { return ; } + + renderWithLabel(): boolean { + return false; + } } diff --git a/src/layout/TextArea/index.tsx b/src/layout/TextArea/index.tsx index 7953c55904..06b07d6f07 100644 --- a/src/layout/TextArea/index.tsx +++ b/src/layout/TextArea/index.tsx @@ -5,7 +5,7 @@ import { TextAreaComponent } from 'src/layout/TextArea/TextAreaComponent'; import type { PropsFromGenericComponent } from 'src/layout'; export class TextArea extends LayoutComponent<'TextArea'> { - public render(props: PropsFromGenericComponent<'TextArea'>): JSX.Element | null { + render(props: PropsFromGenericComponent<'TextArea'>): JSX.Element | null { return ; } } diff --git a/src/layout/index.ts b/src/layout/index.ts index 5d2fa1ba03..fb64dfb1a4 100644 --- a/src/layout/index.ts +++ b/src/layout/index.ts @@ -30,6 +30,7 @@ import type { ExprResolved } from 'src/features/expressions/types'; import type { IGenericComponentProps } from 'src/layout/GenericComponent'; import type { ComponentExceptGroup, ComponentExceptGroupAndSummary, IGrid, ILayoutComponent } from 'src/layout/layout'; import type { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { IComponentValidations } from 'src/types'; import type { ILanguage } from 'src/types/shared'; import type { IComponentFormData } from 'src/utils/formComponentUtils'; @@ -80,6 +81,7 @@ export interface IComponentProps extends IGenericComponentProps { legend: () => JSX.Element; formData: IComponentFormData; isValid?: boolean; + componentValidations?: IComponentValidations; } export type PropsFromGenericComponent = IComponentProps & From 9a833d8ca4a6d748ae40892c3b12fb1c0e7578b5 Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Fri, 30 Dec 2022 11:52:11 +0100 Subject: [PATCH 6/9] Moving functionality for componentValidationsHandledByGenericComponent() into LayoutComponent.ts --- src/layout/FileUpload/index.tsx | 4 ++++ src/layout/FileUploadWithTag/index.tsx | 4 ++++ src/layout/GenericComponent.tsx | 4 +--- src/layout/LayoutComponent.ts | 10 ++++++++++ src/utils/formComponentUtils.test.ts | 18 ------------------ src/utils/formComponentUtils.ts | 15 +-------------- 6 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/layout/FileUpload/index.tsx b/src/layout/FileUpload/index.tsx index 4c4c14acc5..ab2dddbd80 100644 --- a/src/layout/FileUpload/index.tsx +++ b/src/layout/FileUpload/index.tsx @@ -8,4 +8,8 @@ export class FileUpload extends LayoutComponent<'FileUpload'> { render(props: PropsFromGenericComponent<'FileUpload'>): JSX.Element | null { return ; } + + renderDefaultValidations(): boolean { + return false; + } } diff --git a/src/layout/FileUploadWithTag/index.tsx b/src/layout/FileUploadWithTag/index.tsx index 6b29ecf2d9..f73828bf87 100644 --- a/src/layout/FileUploadWithTag/index.tsx +++ b/src/layout/FileUploadWithTag/index.tsx @@ -8,4 +8,8 @@ export class FileUploadWithTag extends LayoutComponent<'FileUploadWithTag'> { render(props: PropsFromGenericComponent<'FileUploadWithTag'>): JSX.Element | null { return ; } + + renderDefaultValidations(): boolean { + return false; + } } diff --git a/src/layout/GenericComponent.tsx b/src/layout/GenericComponent.tsx index 0e8aa314b6..001bfb6011 100644 --- a/src/layout/GenericComponent.tsx +++ b/src/layout/GenericComponent.tsx @@ -16,7 +16,6 @@ import { makeGetFocus, makeGetHidden } from 'src/selectors/getLayoutData'; import { Triggers } from 'src/types'; import { componentHasValidationMessages, - componentValidationsHandledByGenericComponent, getFormDataForComponent, getTextResource, gridBreakpoints, @@ -281,8 +280,7 @@ export function GenericComponent( ...passThroughProps, } as unknown as PropsFromGenericComponent; - const showValidationMessages = - componentValidationsHandledByGenericComponent(props.dataModelBindings, props.type) && hasValidationMessages; + const showValidationMessages = hasValidationMessages && layoutComponent.renderDefaultValidations(); if (layoutComponent.directRender(componentProps)) { return ( diff --git a/src/layout/LayoutComponent.ts b/src/layout/LayoutComponent.ts index 2fe985e812..923a365691 100644 --- a/src/layout/LayoutComponent.ts +++ b/src/layout/LayoutComponent.ts @@ -21,4 +21,14 @@ export abstract class LayoutComponent { }); }); - describe('componentValidationsHandledByGenericComponent', () => { - it('should return false when dataModelBinding is undefined', () => { - const result = componentValidationsHandledByGenericComponent(undefined, 'FileUpload'); - expect(result).toEqual(false); - }); - - it('should return true when component type is Datepicker', () => { - const result = componentValidationsHandledByGenericComponent({ simpleBinding: 'group.superdate' }, 'Datepicker'); - expect(result).toEqual(true); - }); - - it('should return true when component type is Input', () => { - const result = componentValidationsHandledByGenericComponent({ simpleBinding: 'group.secretnumber' }, 'Input'); - expect(result).toEqual(true); - }); - }); - describe('selectComponentTexts', () => { it('should return value of mapped textResourceBinding', () => { const textResourceBindings = { diff --git a/src/utils/formComponentUtils.ts b/src/utils/formComponentUtils.ts index 95bcc36095..ed8b07ed98 100644 --- a/src/utils/formComponentUtils.ts +++ b/src/utils/formComponentUtils.ts @@ -15,13 +15,7 @@ import { import { getTextFromAppOrDefault } from 'src/utils/textResource'; import type { IFormData } from 'src/features/form/data'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { - IDataModelBindings, - IGridStyling, - ILayoutComponent, - ILayoutEntry, - ISelectionComponentProps, -} from 'src/layout/layout'; +import type { IDataModelBindings, IGridStyling, ILayoutComponent, ISelectionComponentProps } from 'src/layout/layout'; import type { IAttachment, IAttachments } from 'src/shared/resources/attachments'; import type { IComponentValidations, @@ -34,13 +28,6 @@ import type { } from 'src/types'; import type { ILanguage } from 'src/types/shared'; -export const componentValidationsHandledByGenericComponent = ( - dataModelBindings: IDataModelBindings | undefined, - type: ILayoutEntry['type'], -): boolean => { - return !!dataModelBindings?.simpleBinding && type !== 'FileUpload' && type !== 'FileUploadWithTag'; -}; - export const componentHasValidationMessages = (componentValidations: IComponentValidations | undefined) => { if (!componentValidations) { return false; From eb9803946947989ab844b49df238811138500452 Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Fri, 30 Dec 2022 12:22:53 +0100 Subject: [PATCH 7/9] Some more cleanup in GenericComponent --- src/features/form/components/Label.tsx | 6 +-- src/layout/GenericComponent.tsx | 64 ++++++++------------------ src/layout/LayoutComponent.ts | 7 +++ 3 files changed, 30 insertions(+), 47 deletions(-) diff --git a/src/features/form/components/Label.tsx b/src/features/form/components/Label.tsx index a02fe35dbc..7fc6e368a5 100644 --- a/src/features/form/components/Label.tsx +++ b/src/features/form/components/Label.tsx @@ -12,10 +12,10 @@ export interface IFormLabelProps { labelText: any; id: string; language: ILanguage; - required: boolean; - readOnly: boolean; + required?: boolean; + readOnly?: boolean; labelSettings?: ILabelSettings; - helpText: string; + helpText: string | number | boolean | React.ReactNode | undefined | null; } export default function Label(props: IFormLabelProps) { diff --git a/src/layout/GenericComponent.tsx b/src/layout/GenericComponent.tsx index 001bfb6011..414cb6707d 100644 --- a/src/layout/GenericComponent.tsx +++ b/src/layout/GenericComponent.tsx @@ -12,6 +12,7 @@ import Legend from 'src/features/form/components/Legend'; import { FormDataActions } from 'src/features/form/data/formDataSlice'; import { FormLayoutActions } from 'src/features/form/layout/formLayoutSlice'; import components, { FormComponentContext } from 'src/layout/index'; +import { getLayoutComponentObject } from 'src/layout/LayoutComponent'; import { makeGetFocus, makeGetHidden } from 'src/selectors/getLayoutData'; import { Triggers } from 'src/types'; import { @@ -34,9 +35,7 @@ import type { ILayoutCompBase, ILayoutComponent, } from 'src/layout/layout'; -import type { LayoutComponent } from 'src/layout/LayoutComponent'; import type { ILabelSettings, LayoutStyle } from 'src/types'; -import type { ILanguage } from 'src/types/shared'; export interface IGenericComponentProps { labelSettings?: ILabelSettings; @@ -202,7 +201,7 @@ export function GenericComponent( ); }; - const layoutComponent = components[props.type as keyof typeof components] as LayoutComponent; + const layoutComponent = getLayoutComponentObject(_props.type); if (!layoutComponent) { return (
@@ -213,18 +212,20 @@ export function GenericComponent( ); } - const RenderComponent = layoutComponent.render as LayoutComponent['render']; + const RenderComponent = layoutComponent.render; - const RenderLabel = () => { - return ( - - ); - }; + const RenderLabel = () => ( +